Repository: gratipay/gratipay.com

Issue:
give better error when DATABASE_URL is empty
@jamieforrest hit this and I just did as well. Here's the gnarly Traceback we currently have:

```
pid-39707 thread-140735224484224 (MainThread) Oh no! Aspen crashed!
pid-39707 thread-140735224484224 (MainThread) Traceback (most recent call last):
pid-39707 thread-140735224484224 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/aspen/server.py", line 125, in main
pid-39707 thread-140735224484224 (MainThread)     _main(argv)
pid-39707 thread-140735224484224 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/aspen/server.py", line 51, in _main
pid-39707 thread-140735224484224 (MainThread)     website = Website(argv)
pid-39707 thread-140735224484224 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/aspen/website.py", line 31, in __init__
pid-39707 thread-140735224484224 (MainThread)     self.configure(argv)
pid-39707 thread-140735224484224 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/aspen/configuration/__init__.py", line 343, in configure
pid-39707 thread-140735224484224 (MainThread)     self.run_config_scripts()
pid-39707 thread-140735224484224 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/aspen/configuration/__init__.py", line 386, in run_config_scripts
pid-39707 thread-140735224484224 (MainThread)     execfile(filepath, {'website': self})
pid-39707 thread-140735224484224 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/configure-aspen.py", line 7, in <module>
pid-39707 thread-140735224484224 (MainThread)     import gittip.authentication
pid-39707 thread-140735224484224 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/gittip/authentication.py", line 10, in <module>
pid-39707 thread-140735224484224 (MainThread)     from gittip.orm import db
pid-39707 thread-140735224484224 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/gittip/orm/__init__.py", line 73, in <module>
pid-39707 thread-140735224484224 (MainThread)     db = SQLAlchemy()
pid-39707 thread-140735224484224 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/gittip/orm/__init__.py", line 31, in __init__
pid-39707 thread-140735224484224 (MainThread)     self.session = self.create_session()
pid-39707 thread-140735224484224 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/gittip/orm/__init__.py", line 46, in create_session
pid-39707 thread-140735224484224 (MainThread)     session.configure(bind=self.engine)
pid-39707 thread-140735224484224 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/gittip/orm/__init__.py", line 38, in engine
pid-39707 thread-140735224484224 (MainThread)     return create_engine(dburl, pool_size=maxconn, max_overflow=0)
pid-39707 thread-140735224484224 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/sqlalchemy/engine/__init__.py", line 331, in create_engine
pid-39707 thread-140735224484224 (MainThread)     return strategy.create(*args, **kwargs)
pid-39707 thread-140735224484224 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/sqlalchemy/engine/strategies.py", line 48, in create
pid-39707 thread-140735224484224 (MainThread)     u = url.make_url(name_or_url)
pid-39707 thread-140735224484224 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/sqlalchemy/engine/url.py", line 154, in make_url
pid-39707 thread-140735224484224 (MainThread)     return _parse_rfc1738_args(name_or_url)
pid-39707 thread-140735224484224 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/sqlalchemy/engine/url.py", line 196, in _parse_rfc1738_args
pid-39707 thread-140735224484224 (MainThread)     "Could not parse rfc1738 URL from string '%s'" % name)
pid-39707 thread-140735224484224 (MainThread) ArgumentError: Could not parse rfc1738 URL from string ''
```



Discussion:
L196 of www.gittip.com/env/lib/python2.7/site-packages/sqlalchemy/engine/url.py can be manually changed to "Database URL error!". I'm not sure if it's appropriate to go changing files in the virtualenv /env/ directory. Is there a better solution? 

This traceback is outdated. SQLalchemy has been removed some time ago.

> L196 of www.gittip.com/env/lib/python2.7/site-packages/sqlalchemy/engine/url.py can be manually changed to "Database URL error!". I'm not sure if it's appropriate to go changing files in the virtualenv /env/ directory. Is there a better solution?

Yes! It is not appropriate to change files in the `env/` directory. If you're just starting out you shouldn't need to change any dependencies (such as SQLAlchemy, which as @zwn mentions we're not actually depending on anymore). The only files you should be changing are in these directories:
- `gittip/`
- `templates/`
- `www/`

Here's the traceback I get if I have Postgres running but no `DATABASE_URL` configured:

```
pid-331 thread-140735190806912 (MainThread) Traceback (most recent call last):
pid-331 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/aspen/server.py", line 125, in main
pid-331 thread-140735190806912 (MainThread)     _main(argv)
pid-331 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/aspen/server.py", line 51, in _main
pid-331 thread-140735190806912 (MainThread)     website = Website(argv)
pid-331 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/aspen/website.py", line 31, in __init__
pid-331 thread-140735190806912 (MainThread)     self.configure(argv)
pid-331 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/aspen/configuration/__init__.py", line 343, in configure
pid-331 thread-140735190806912 (MainThread)     self.run_config_scripts()
pid-331 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/aspen/configuration/__init__.py", line 386, in run_config_scripts
pid-331 thread-140735190806912 (MainThread)     execfile(filepath, {'website': self})
pid-331 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/configure-aspen.py", line 22, in <module>
pid-331 thread-140735190806912 (MainThread)     website.db = gittip.wireup.db()
pid-331 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/gittip/wireup.py", line 31, in db
pid-331 thread-140735190806912 (MainThread)     psycopg2.extras.register_hstore(cursor, globally=True, unicode=True)
pid-331 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/psycopg2/extras.py", line 775, in register_hstore
pid-331 thread-140735190806912 (MainThread)     "hstore type not found in the database. "
pid-331 thread-140735190806912 (MainThread) ProgrammingError: hstore type not found in the database. please install it from your 'contrib/hstore.sql' file
```

Presumably it is trying to use `postgres://localhost/whit537`, and that database exists but doesn't have the right schema nor the hstore extension installed.

Here's what I get if Postgres is not running:

```
pid-355 thread-140735190806912 (MainThread) Traceback (most recent call last):
pid-355 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/aspen/server.py", line 125, in main
pid-355 thread-140735190806912 (MainThread)     _main(argv)
pid-355 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/aspen/server.py", line 51, in _main
pid-355 thread-140735190806912 (MainThread)     website = Website(argv)
pid-355 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/aspen/website.py", line 31, in __init__
pid-355 thread-140735190806912 (MainThread)     self.configure(argv)
pid-355 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/aspen/configuration/__init__.py", line 343, in configure
pid-355 thread-140735190806912 (MainThread)     self.run_config_scripts()
pid-355 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/aspen/configuration/__init__.py", line 386, in run_config_scripts
pid-355 thread-140735190806912 (MainThread)     execfile(filepath, {'website': self})
pid-355 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/configure-aspen.py", line 22, in <module>
pid-355 thread-140735190806912 (MainThread)     website.db = gittip.wireup.db()
pid-355 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/gittip/wireup.py", line 27, in db
pid-355 thread-140735190806912 (MainThread)     db = gittip.db = Postgres(dburl, maxconn=maxconn)
pid-355 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/postgres/__init__.py", line 321, in __init__
pid-355 thread-140735190806912 (MainThread)     , connection_factory=Connection
pid-355 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/psycopg2/pool.py", line 156, in __init__
pid-355 thread-140735190806912 (MainThread)     self, minconn, maxconn, *args, **kwargs)
pid-355 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/psycopg2/pool.py", line 58, in __init__
pid-355 thread-140735190806912 (MainThread)     self._connect()
pid-355 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/psycopg2/pool.py", line 62, in _connect
pid-355 thread-140735190806912 (MainThread)     conn = psycopg2.connect(*self._args, **self._kwargs)
pid-355 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/psycopg2/__init__.py", line 164, in connect
pid-355 thread-140735190806912 (MainThread)     conn = _connect(dsn, connection_factory=connection_factory, async=async)
pid-355 thread-140735190806912 (MainThread)   File "/Users/whit537/personal/gittip/www.gittip.com/env/lib/python2.7/site-packages/postgres/__init__.py", line 720, in __init__
pid-355 thread-140735190806912 (MainThread)     psycopg2.extensions.connection.__init__(self, *a, **kw)
pid-355 thread-140735190806912 (MainThread) OperationalError: could not connect to server: No such file or directory
pid-355 thread-140735190806912 (MainThread)     Is the server running locally and accepting
pid-355 thread-140735190806912 (MainThread)     connections on Unix domain socket "/var/pgsql_socket/.s.PGSQL.5432"?
```

Again, it's trying to connect to "something" but in this case is failing because Postgres isn't running at all.

What about testing for empty value in https://github.com/gittip/www.gittip.com/blob/master/gittip/wireup.py#L25 and raising an exception?

@zwn Sounds about right, yes.

@whit537 Ok. If no one gets to it before weekend, I'll do it.

Now I think it is better to make the default setup work with an empty DATABASE_URL. If the user does

```
sudo -u postgres createuser --superuser $USER
createdb $USER
```

it _just works_ &trade;. On Linux there is also no need to modify trust levels since without _hostname_ it defaults to using unix sockets that are accessible without password by default.

:clap: 


Resolving patch:
diff --git a/gittip/utils/swaddle.py b/gittip/utils/swaddle.py
deleted file mode 100644
--- a/gittip/utils/swaddle.py
+++ /dev/null
@@ -1,65 +0,0 @@
-"""Run a command with an environment specified in a file.
-"""
-import os
-import sys
-if sys.platform.startswith('win'):
-    import ctypes
-
-
-def configure(envdef):
-    """Given a filepath or -, return a tuple of bytestrings.
-    """
-    if envdef == '-':               # Heroku-style
-
-        # Heroku used to use =>, which was weird. Now they use :, and we may as
-        # well too, but I had already started using =, and now changing would
-        # break people's local.env.
-
-        m = "[SWADDLE] reading environment from stdin."
-        print >> sys.stderr, m
-        fp = sys.stdin
-        splitter = ": "
-    elif not os.path.isfile(envdef):
-        m = "[SWADDLE] %s is not a file; environment unchanged." % envdef
-        print >> sys.stderr, m
-        envdef = ""
-    else:                           # Gittip-style
-        fp = open(envdef)
-        splitter = "="
-
-    args = sys.argv[2:]
-    if not args:
-        m ="[SWADDLE] No command specified; exiting."
-        raise SystemExit(m)
-    if sys.platform == 'win32' and not os.path.isfile(args[0]):
-        # Try with an '.exe' extension on Windows if the command doesn't
-        # already have an extension.
-        if os.path.splitext(args[0])[-1] == '':
-            args[0] += '.exe'
-    if not os.path.isfile(args[0]):
-        m ="[SWADDLE] Command %s does not exist; exiting." % args[0]
-        raise SystemExit(m)
-
-    if envdef:
-        for line in fp:
-            line = line.split('#')[0].strip()
-            if splitter not in line:
-                m = "[SWADDLE] Skipping line: %s." % line
-                print >> sys.stderr, m
-                continue
-            key, val = line.split(splitter, 1)
-            if sys.platform.startswith('win'):
-                ctypes.windll.kernel32.SetEnvironmentVariableA(key.strip(), val.strip())
-            else:
-                os.environ[key.strip()] = val.strip()
-
-    return args
-
-
-def main():
-    if len(sys.argv) < 1:
-        m ="[SWADDLE] Usage: %s {definition.env} {command} {args}" % sys.argv[0]
-        raise SystemExit(m)
-    envdef = sys.argv[1]
-    args = configure(envdef)
-    os.execv(args[0], args)
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -14,7 +14,6 @@ def get_version():
      , packages=find_packages()
      , entry_points = { 'console_scripts'
                       : [ 'payday=gittip.cli:payday'
-                        , 'swaddle=gittip.utils.swaddle:main'
                         , 'fake_data=gittip.utils.fake_data:main'
                          ]
                        }


Test patch:
diff --git a/default_tests.env b/tests/defaults.env
similarity index 98%
rename from default_tests.env
rename to tests/defaults.env
--- a/default_tests.env
+++ b/tests/defaults.env
@@ -1,4 +1,4 @@
-DATABASE_URL=
+DATABASE_URL="dbname=gittip-test"
 
 PYTHONDONTWRITEBYTECODE=true
 CANONICAL_HOST=