We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Alembic causes failures on fresh install when postgres db username has '@' in it
I'm using a postgres db where usernames are of the form 'something@something.com'. This causes alembic to fail with:

```
    configparser.InterpolationSyntaxError: '%' must be followed by '%' or '(', found: '%40datahub-fa17-db:<my-db-password>@datahub-fa17-db.postgres.database.azure.com/datahub-fa17-staging'
```

Full traceback below. Note that it was also interleaved with other log lines, not sure why.

```
[D 2017-08-11 23:29:39.187 JupyterHub application:555] Looking for /srv/jupyterhub_config in /srv/jupyterhub
[D 2017-08-11 23:29:39.221 JupyterHub application:577] Loaded config file: /srv/jupyterhub_config.py
[I 2017-08-11 23:29:39.437 JupyterHub app:839] Loading cookie_secret from env[JPY_COOKIE_SECRET]
[D 2017-08-11 23:29:39.437 JupyterHub app:894] Connecting to db: postgresql+psycopg2://datahub-fa17-staging@datahub-fa17-db:<my-password>@datahub-fa17-db.postgres.database.azure.com/datahub-fa17-staging
[D 2017-08-11 23:29:40.808 JupyterHub orm:474] Stamping empty database with alembic revision 3ec6993fe20c
[E 2017-08-11 23:29:40.813 JupyterHub app:1626]
    Traceback (most recent call last):
      File "/usr/local/lib/python3.5/dist-packages/jupyterhub/app.py", line 1623, in launch_instance_async
        yield self.initialize(argv)
      File "/usr/lib/python3.5/types.py", line 199, in __next__
        return next(self.__wrapped)
      File "/usr/local/lib/python3.5/dist-packages/jupyterhub/app.py", line 1376, in initialize
        self.init_db()
      File "/usr/local/lib/python3.5/dist-packages/jupyterhub/app.py", line 900, in init_db
        **self.db_kwargs
      File "/usr/local/lib/python3.5/dist-packages/jupyterhub/orm.py", line 530, in new_session_factory
        check_db_revision(engine)
      File "/usr/local/lib/python3.5/dist-packages/jupyterhub/orm.py", line 475, in check_db_revision
singleuser.uid 1000
singleuser.fs-gid 1000
singleuser.storage.type hostPath
singleuser.storage.extra-volumes [{'hostPath': {'path': '/data/homes/{username}'}, 'name': 'home'}]
singleuser.storage.extra-volume-mounts [{'name': 'home', 'mountPath': '/home/jovyan'}]
singleuser.memory.limit 1G
singleuser.memory.guarantee 1G
auth.type google
auth.google.client-id <more-secret>
auth.google.client-secret <some-secret>
auth.google.callback-url http://datahub-staging.yuvi.in/hub/oauth_callback
auth.google.hosted-domain berkeley.edu
        alembic.command.stamp(cfg, head)
      File "/usr/local/lib/python3.5/dist-packages/alembic/command.py", line 496, in stamp
        script.run_env()
      File "/usr/local/lib/python3.5/dist-packages/alembic/script/base.py", line 425, in run_env
        util.load_python_file(self.dir, 'env.py')
      File "/usr/local/lib/python3.5/dist-packages/alembic/util/pyfiles.py", line 93, in load_python_file
        module = load_module_py(module_id, path)
      File "/usr/local/lib/python3.5/dist-packages/alembic/util/compat.py", line 64, in load_module_py
        module_id, path).load_module(module_id)
      File "<frozen importlib._bootstrap_external>", line 396, in _check_name_wrapper
      File "<frozen importlib._bootstrap_external>", line 817, in load_module
      File "<frozen importlib._bootstrap_external>", line 676, in load_module
      File "<frozen importlib._bootstrap>", line 268, in _load_module_shim
      File "<frozen importlib._bootstrap>", line 693, in _load
      File "<frozen importlib._bootstrap>", line 673, in _load_unlocked
auth.google.login-service Calnet ID
auth.google.hosted-domain berkeley.edu
admin.access True
cull.enabled True
cull.timeout 3600
cull.every 600
hub.base_url /
hub.db_url postgresql+psycopg2://datahub-fa17-staging@datahub-fa17-db:<my-db-password>@datahub-fa17-db.postgres.database.azure.com/datahub-fa17-staging
singleuser.cmd jupyterhub-singleuser
      File "<frozen importlib._bootstrap_external>", line 673, in exec_module
      File "<frozen importlib._bootstrap>", line 222, in _call_with_frames_removed
      File "/usr/local/lib/python3.5/dist-packages/jupyterhub/alembic/env.py", line 82, in <module>
        run_migrations_online()
      File "/usr/local/lib/python3.5/dist-packages/jupyterhub/alembic/env.py", line 66, in run_migrations_online
        config.get_section(config.config_ini_section),
      File "/usr/local/lib/python3.5/dist-packages/alembic/config.py", line 201, in get_section
        return dict(self.file_config.items(name))
      File "/usr/lib/python3.5/configparser.py", line 855, in items
        return [(option, value_getter(option)) for option in d.keys()]
      File "/usr/lib/python3.5/configparser.py", line 855, in <listcomp>
        return [(option, value_getter(option)) for option in d.keys()]
      File "/usr/lib/python3.5/configparser.py", line 852, in <lambda>
        section, option, d[option], d)
      File "/usr/lib/python3.5/configparser.py", line 393, in before_get
        self._interpolate_some(parser, option, L, value, section, defaults, 1)
      File "/usr/lib/python3.5/configparser.py", line 443, in _interpolate_some
        "found: %r" % (rest,))
    configparser.InterpolationSyntaxError: '%' must be followed by '%' or '(', found: '%40datahub-fa17-db:<my-db-password>@datahub-fa17-db.postgres.database.azure.com/datahub-fa17-staging'
    
[D 2017-08-11 23:29:40.816 JupyterHub application:647] Exiting application: jupyterhub
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `jupyterhub/dbutil.py`
Content:
```
1 """Database utilities for JupyterHub"""
2 # Copyright (c) Jupyter Development Team.
3 # Distributed under the terms of the Modified BSD License.
4 
5 # Based on pgcontents.utils.migrate, used under the Apache license.
6 
7 from contextlib import contextmanager
8 import os
9 from subprocess import check_call
10 import sys
11 from tempfile import TemporaryDirectory
12 
13 _here = os.path.abspath(os.path.dirname(__file__))
14 
15 ALEMBIC_INI_TEMPLATE_PATH = os.path.join(_here, 'alembic.ini')
16 ALEMBIC_DIR = os.path.join(_here, 'alembic')
17 
18 
19 def write_alembic_ini(alembic_ini='alembic.ini', db_url='sqlite:///jupyterhub.sqlite'):
20     """Write a complete alembic.ini from our template.
21 
22     Parameters
23     ----------
24 
25     alembic_ini: str
26         path to the alembic.ini file that should be written.
27     db_url: str
28         The SQLAlchemy database url, e.g. `sqlite:///jupyterhub.sqlite`.
29     """
30     with open(ALEMBIC_INI_TEMPLATE_PATH) as f:
31         alembic_ini_tpl = f.read()
32 
33     with open(alembic_ini, 'w') as f:
34         f.write(
35             alembic_ini_tpl.format(
36                 alembic_dir=ALEMBIC_DIR,
37                 db_url=db_url,
38             )
39         )
40 
41 
42 @contextmanager
43 def _temp_alembic_ini(db_url):
44     """Context manager for temporary JupyterHub alembic directory
45 
46     Temporarily write an alembic.ini file for use with alembic migration scripts.
47 
48     Context manager yields alembic.ini path.
49 
50     Parameters
51     ----------
52 
53     db_url: str
54         The SQLAlchemy database url, e.g. `sqlite:///jupyterhub.sqlite`.
55 
56     Returns
57     -------
58 
59     alembic_ini: str
60         The path to the temporary alembic.ini that we have created.
61         This file will be cleaned up on exit from the context manager.
62     """
63     with TemporaryDirectory() as td:
64         alembic_ini = os.path.join(td, 'alembic.ini')
65         write_alembic_ini(alembic_ini, db_url)
66         yield alembic_ini
67 
68 
69 def upgrade(db_url, revision='head'):
70     """Upgrade the given database to revision.
71 
72     db_url: str
73         The SQLAlchemy database url, e.g. `sqlite:///jupyterhub.sqlite`.
74     revision: str [default: head]
75         The alembic revision to upgrade to.
76     """
77     with _temp_alembic_ini(db_url) as alembic_ini:
78         check_call(
79             ['alembic', '-c', alembic_ini, 'upgrade', revision]
80         )
81 
82 
83 def _alembic(*args):
84     """Run an alembic command with a temporary alembic.ini"""
85     with _temp_alembic_ini('sqlite:///jupyterhub.sqlite') as alembic_ini:
86         check_call(
87             ['alembic', '-c', alembic_ini] + list(args)
88         )
89 
90 
91 if __name__ == '__main__':
92     _alembic(*sys.argv[1:])
93
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/jupyterhub/dbutil.py b/jupyterhub/dbutil.py
--- a/jupyterhub/dbutil.py
+++ b/jupyterhub/dbutil.py
@@ -34,7 +34,11 @@
         f.write(
             alembic_ini_tpl.format(
                 alembic_dir=ALEMBIC_DIR,
-                db_url=db_url,
+                # If there are any %s in the URL, they should be replaced with %%, since ConfigParser
+                # by default uses %() for substitution. You'll get %s in your URL when you have usernames
+                # with special chars (such as '@') that need to be URL encoded. URL Encoding is done with %s.
+                # YAY for nested templates?
+                db_url=str(db_url).replace('%', '%%'),
             )
         )

{"golden_diff": "diff --git a/jupyterhub/dbutil.py b/jupyterhub/dbutil.py\n--- a/jupyterhub/dbutil.py\n+++ b/jupyterhub/dbutil.py\n@@ -34,7 +34,11 @@\n         f.write(\n             alembic_ini_tpl.format(\n                 alembic_dir=ALEMBIC_DIR,\n-                db_url=db_url,\n+                # If there are any %s in the URL, they should be replaced with %%, since ConfigParser\n+                # by default uses %() for substitution. You'll get %s in your URL when you have usernames\n+                # with special chars (such as '@') that need to be URL encoded. URL Encoding is done with %s.\n+                # YAY for nested templates?\n+                db_url=str(db_url).replace('%', '%%'),\n             )\n         )\n", "issue": "Alembic causes failures on fresh install when postgres db username has '@' in it\nI'm using a postgres db where usernames are of the form 'something@something.com'. This causes alembic to fail with:\r\n\r\n```\r\n    configparser.InterpolationSyntaxError: '%' must be followed by '%' or '(', found: '%40datahub-fa17-db:<my-db-password>@datahub-fa17-db.postgres.database.azure.com/datahub-fa17-staging'\r\n```\r\n\r\nFull traceback below. Note that it was also interleaved with other log lines, not sure why.\r\n\r\n```\r\n[D 2017-08-11 23:29:39.187 JupyterHub application:555] Looking for /srv/jupyterhub_config in /srv/jupyterhub\r\n[D 2017-08-11 23:29:39.221 JupyterHub application:577] Loaded config file: /srv/jupyterhub_config.py\r\n[I 2017-08-11 23:29:39.437 JupyterHub app:839] Loading cookie_secret from env[JPY_COOKIE_SECRET]\r\n[D 2017-08-11 23:29:39.437 JupyterHub app:894] Connecting to db: postgresql+psycopg2://datahub-fa17-staging@datahub-fa17-db:<my-password>@datahub-fa17-db.postgres.database.azure.com/datahub-fa17-staging\r\n[D 2017-08-11 23:29:40.808 JupyterHub orm:474] Stamping empty database with alembic revision 3ec6993fe20c\r\n[E 2017-08-11 23:29:40.813 JupyterHub app:1626]\r\n    Traceback (most recent call last):\r\n      File \"/usr/local/lib/python3.5/dist-packages/jupyterhub/app.py\", line 1623, in launch_instance_async\r\n        yield self.initialize(argv)\r\n      File \"/usr/lib/python3.5/types.py\", line 199, in __next__\r\n        return next(self.__wrapped)\r\n      File \"/usr/local/lib/python3.5/dist-packages/jupyterhub/app.py\", line 1376, in initialize\r\n        self.init_db()\r\n      File \"/usr/local/lib/python3.5/dist-packages/jupyterhub/app.py\", line 900, in init_db\r\n        **self.db_kwargs\r\n      File \"/usr/local/lib/python3.5/dist-packages/jupyterhub/orm.py\", line 530, in new_session_factory\r\n        check_db_revision(engine)\r\n      File \"/usr/local/lib/python3.5/dist-packages/jupyterhub/orm.py\", line 475, in check_db_revision\r\nsingleuser.uid 1000\r\nsingleuser.fs-gid 1000\r\nsingleuser.storage.type hostPath\r\nsingleuser.storage.extra-volumes [{'hostPath': {'path': '/data/homes/{username}'}, 'name': 'home'}]\r\nsingleuser.storage.extra-volume-mounts [{'name': 'home', 'mountPath': '/home/jovyan'}]\r\nsingleuser.memory.limit 1G\r\nsingleuser.memory.guarantee 1G\r\nauth.type google\r\nauth.google.client-id <more-secret>\r\nauth.google.client-secret <some-secret>\r\nauth.google.callback-url http://datahub-staging.yuvi.in/hub/oauth_callback\r\nauth.google.hosted-domain berkeley.edu\r\n        alembic.command.stamp(cfg, head)\r\n      File \"/usr/local/lib/python3.5/dist-packages/alembic/command.py\", line 496, in stamp\r\n        script.run_env()\r\n      File \"/usr/local/lib/python3.5/dist-packages/alembic/script/base.py\", line 425, in run_env\r\n        util.load_python_file(self.dir, 'env.py')\r\n      File \"/usr/local/lib/python3.5/dist-packages/alembic/util/pyfiles.py\", line 93, in load_python_file\r\n        module = load_module_py(module_id, path)\r\n      File \"/usr/local/lib/python3.5/dist-packages/alembic/util/compat.py\", line 64, in load_module_py\r\n        module_id, path).load_module(module_id)\r\n      File \"<frozen importlib._bootstrap_external>\", line 396, in _check_name_wrapper\r\n      File \"<frozen importlib._bootstrap_external>\", line 817, in load_module\r\n      File \"<frozen importlib._bootstrap_external>\", line 676, in load_module\r\n      File \"<frozen importlib._bootstrap>\", line 268, in _load_module_shim\r\n      File \"<frozen importlib._bootstrap>\", line 693, in _load\r\n      File \"<frozen importlib._bootstrap>\", line 673, in _load_unlocked\r\nauth.google.login-service Calnet ID\r\nauth.google.hosted-domain berkeley.edu\r\nadmin.access True\r\ncull.enabled True\r\ncull.timeout 3600\r\ncull.every 600\r\nhub.base_url /\r\nhub.db_url postgresql+psycopg2://datahub-fa17-staging@datahub-fa17-db:<my-db-password>@datahub-fa17-db.postgres.database.azure.com/datahub-fa17-staging\r\nsingleuser.cmd jupyterhub-singleuser\r\n      File \"<frozen importlib._bootstrap_external>\", line 673, in exec_module\r\n      File \"<frozen importlib._bootstrap>\", line 222, in _call_with_frames_removed\r\n      File \"/usr/local/lib/python3.5/dist-packages/jupyterhub/alembic/env.py\", line 82, in <module>\r\n        run_migrations_online()\r\n      File \"/usr/local/lib/python3.5/dist-packages/jupyterhub/alembic/env.py\", line 66, in run_migrations_online\r\n        config.get_section(config.config_ini_section),\r\n      File \"/usr/local/lib/python3.5/dist-packages/alembic/config.py\", line 201, in get_section\r\n        return dict(self.file_config.items(name))\r\n      File \"/usr/lib/python3.5/configparser.py\", line 855, in items\r\n        return [(option, value_getter(option)) for option in d.keys()]\r\n      File \"/usr/lib/python3.5/configparser.py\", line 855, in <listcomp>\r\n        return [(option, value_getter(option)) for option in d.keys()]\r\n      File \"/usr/lib/python3.5/configparser.py\", line 852, in <lambda>\r\n        section, option, d[option], d)\r\n      File \"/usr/lib/python3.5/configparser.py\", line 393, in before_get\r\n        self._interpolate_some(parser, option, L, value, section, defaults, 1)\r\n      File \"/usr/lib/python3.5/configparser.py\", line 443, in _interpolate_some\r\n        \"found: %r\" % (rest,))\r\n    configparser.InterpolationSyntaxError: '%' must be followed by '%' or '(', found: '%40datahub-fa17-db:<my-db-password>@datahub-fa17-db.postgres.database.azure.com/datahub-fa17-staging'\r\n    \r\n[D 2017-08-11 23:29:40.816 JupyterHub application:647] Exiting application: jupyterhub\r\n```\n", "before_files": [{"content": "\"\"\"Database utilities for JupyterHub\"\"\"\n# Copyright (c) Jupyter Development Team.\n# Distributed under the terms of the Modified BSD License.\n\n# Based on pgcontents.utils.migrate, used under the Apache license.\n\nfrom contextlib import contextmanager\nimport os\nfrom subprocess import check_call\nimport sys\nfrom tempfile import TemporaryDirectory\n\n_here = os.path.abspath(os.path.dirname(__file__))\n\nALEMBIC_INI_TEMPLATE_PATH = os.path.join(_here, 'alembic.ini')\nALEMBIC_DIR = os.path.join(_here, 'alembic')\n\n\ndef write_alembic_ini(alembic_ini='alembic.ini', db_url='sqlite:///jupyterhub.sqlite'):\n    \"\"\"Write a complete alembic.ini from our template.\n\n    Parameters\n    ----------\n\n    alembic_ini: str\n        path to the alembic.ini file that should be written.\n    db_url: str\n        The SQLAlchemy database url, e.g. `sqlite:///jupyterhub.sqlite`.\n    \"\"\"\n    with open(ALEMBIC_INI_TEMPLATE_PATH) as f:\n        alembic_ini_tpl = f.read()\n\n    with open(alembic_ini, 'w') as f:\n        f.write(\n            alembic_ini_tpl.format(\n                alembic_dir=ALEMBIC_DIR,\n                db_url=db_url,\n            )\n        )\n\n\n@contextmanager\ndef _temp_alembic_ini(db_url):\n    \"\"\"Context manager for temporary JupyterHub alembic directory\n\n    Temporarily write an alembic.ini file for use with alembic migration scripts.\n\n    Context manager yields alembic.ini path.\n\n    Parameters\n    ----------\n\n    db_url: str\n        The SQLAlchemy database url, e.g. `sqlite:///jupyterhub.sqlite`.\n\n    Returns\n    -------\n\n    alembic_ini: str\n        The path to the temporary alembic.ini that we have created.\n        This file will be cleaned up on exit from the context manager.\n    \"\"\"\n    with TemporaryDirectory() as td:\n        alembic_ini = os.path.join(td, 'alembic.ini')\n        write_alembic_ini(alembic_ini, db_url)\n        yield alembic_ini\n\n\ndef upgrade(db_url, revision='head'):\n    \"\"\"Upgrade the given database to revision.\n\n    db_url: str\n        The SQLAlchemy database url, e.g. `sqlite:///jupyterhub.sqlite`.\n    revision: str [default: head]\n        The alembic revision to upgrade to.\n    \"\"\"\n    with _temp_alembic_ini(db_url) as alembic_ini:\n        check_call(\n            ['alembic', '-c', alembic_ini, 'upgrade', revision]\n        )\n\n\ndef _alembic(*args):\n    \"\"\"Run an alembic command with a temporary alembic.ini\"\"\"\n    with _temp_alembic_ini('sqlite:///jupyterhub.sqlite') as alembic_ini:\n        check_call(\n            ['alembic', '-c', alembic_ini] + list(args)\n        )\n\n\nif __name__ == '__main__':\n    _alembic(*sys.argv[1:])\n", "path": "jupyterhub/dbutil.py"}], "after_files": [{"content": "\"\"\"Database utilities for JupyterHub\"\"\"\n# Copyright (c) Jupyter Development Team.\n# Distributed under the terms of the Modified BSD License.\n\n# Based on pgcontents.utils.migrate, used under the Apache license.\n\nfrom contextlib import contextmanager\nimport os\nfrom subprocess import check_call\nimport sys\nfrom tempfile import TemporaryDirectory\n\n_here = os.path.abspath(os.path.dirname(__file__))\n\nALEMBIC_INI_TEMPLATE_PATH = os.path.join(_here, 'alembic.ini')\nALEMBIC_DIR = os.path.join(_here, 'alembic')\n\n\ndef write_alembic_ini(alembic_ini='alembic.ini', db_url='sqlite:///jupyterhub.sqlite'):\n    \"\"\"Write a complete alembic.ini from our template.\n\n    Parameters\n    ----------\n\n    alembic_ini: str\n        path to the alembic.ini file that should be written.\n    db_url: str\n        The SQLAlchemy database url, e.g. `sqlite:///jupyterhub.sqlite`.\n    \"\"\"\n    with open(ALEMBIC_INI_TEMPLATE_PATH) as f:\n        alembic_ini_tpl = f.read()\n\n    with open(alembic_ini, 'w') as f:\n        f.write(\n            alembic_ini_tpl.format(\n                alembic_dir=ALEMBIC_DIR,\n                # If there are any %s in the URL, they should be replaced with %%, since ConfigParser\n                # by default uses %() for substitution. You'll get %s in your URL when you have usernames\n                # with special chars (such as '@') that need to be URL encoded. URL Encoding is done with %s.\n                # YAY for nested templates?\n                db_url=str(db_url).replace('%', '%%'),\n            )\n        )\n\n\n@contextmanager\ndef _temp_alembic_ini(db_url):\n    \"\"\"Context manager for temporary JupyterHub alembic directory\n\n    Temporarily write an alembic.ini file for use with alembic migration scripts.\n\n    Context manager yields alembic.ini path.\n\n    Parameters\n    ----------\n\n    db_url: str\n        The SQLAlchemy database url, e.g. `sqlite:///jupyterhub.sqlite`.\n\n    Returns\n    -------\n\n    alembic_ini: str\n        The path to the temporary alembic.ini that we have created.\n        This file will be cleaned up on exit from the context manager.\n    \"\"\"\n    with TemporaryDirectory() as td:\n        alembic_ini = os.path.join(td, 'alembic.ini')\n        write_alembic_ini(alembic_ini, db_url)\n        yield alembic_ini\n\n\ndef upgrade(db_url, revision='head'):\n    \"\"\"Upgrade the given database to revision.\n\n    db_url: str\n        The SQLAlchemy database url, e.g. `sqlite:///jupyterhub.sqlite`.\n    revision: str [default: head]\n        The alembic revision to upgrade to.\n    \"\"\"\n    with _temp_alembic_ini(db_url) as alembic_ini:\n        check_call(\n            ['alembic', '-c', alembic_ini, 'upgrade', revision]\n        )\n\n\ndef _alembic(*args):\n    \"\"\"Run an alembic command with a temporary alembic.ini\"\"\"\n    with _temp_alembic_ini('sqlite:///jupyterhub.sqlite') as alembic_ini:\n        check_call(\n            ['alembic', '-c', alembic_ini] + list(args)\n        )\n\n\nif __name__ == '__main__':\n    _alembic(*sys.argv[1:])\n", "path": "jupyterhub/dbutil.py"}]}