prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  CI: jupyter-server build fails since late september
  The `test_singleuser_auth` step fails with the following error ([example failure](https://travis-ci.org/github/jupyterhub/jupyterhub/jobs/729518444))
  
  ```
  404 Client Error: Not Found for url: http://127.0.0.1:59471/@/space%20word/user/nandy/api/spec.yaml?redirects=2
  ```
  
  Has something change with regards to `@` symbols or spaces in words like `space word`? Yes it has, in `jupyter-server` it seems, because there have been releases in this time span.
  
  ![image](https://user-images.githubusercontent.com/3837114/94790428-9275a400-03d6-11eb-807f-1a174e1b3721.png)
  
  ## References
  - [jupyter-server changelog](https://github.com/jupyter/jupyter_server/blob/master/CHANGELOG.md)
  - [The only PR that I saw in the changelog with clear potential to cause our CI error](https://github.com/jupyter/jupyter_server/pull/304)
  - [A seemingly related PR by, @minrk](https://github.com/jupyterhub/jupyterhub/pull/3168)
  - [Another seemingly related PR, by @danlester](https://github.com/jupyterhub/jupyterhub/pull/3167)
  
  --- 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/traitlets.py`
  Content:
  ```
  1 """
  2 Traitlets that are used in JupyterHub
  3 """
  4 # Copyright (c) Jupyter Development Team.
  5 # Distributed under the terms of the Modified BSD License.
  6 import entrypoints
  7 from traitlets import Integer
  8 from traitlets import List
  9 from traitlets import TraitError
  10 from traitlets import TraitType
  11 from traitlets import Type
  12 from traitlets import Unicode
  13 
  14 
  15 class URLPrefix(Unicode):
  16     def validate(self, obj, value):
  17         u = super().validate(obj, value)
  18         if not u.startswith('/'):
  19             u = '/' + u
  20         if not u.endswith('/'):
  21             u = u + '/'
  22         return u
  23 
  24 
  25 class Command(List):
  26     """Traitlet for a command that should be a list of strings,
  27     but allows it to be specified as a single string.
  28     """
  29 
  30     def __init__(self, default_value=None, **kwargs):
  31         kwargs.setdefault('minlen', 1)
  32         if isinstance(default_value, str):
  33             default_value = [default_value]
  34         super().__init__(Unicode(), default_value, **kwargs)
  35 
  36     def validate(self, obj, value):
  37         if isinstance(value, str):
  38             value = [value]
  39         return super().validate(obj, value)
  40 
  41 
  42 class ByteSpecification(Integer):
  43     """
  44     Allow easily specifying bytes in units of 1024 with suffixes
  45 
  46     Suffixes allowed are:
  47       - K -> Kilobyte
  48       - M -> Megabyte
  49       - G -> Gigabyte
  50       - T -> Terabyte
  51     """
  52 
  53     UNIT_SUFFIXES = {
  54         'K': 1024,
  55         'M': 1024 * 1024,
  56         'G': 1024 * 1024 * 1024,
  57         'T': 1024 * 1024 * 1024 * 1024,
  58     }
  59 
  60     # Default to allowing None as a value
  61     allow_none = True
  62 
  63     def validate(self, obj, value):
  64         """
  65         Validate that the passed in value is a valid memory specification
  66 
  67         It could either be a pure int, when it is taken as a byte value.
  68         If it has one of the suffixes, it is converted into the appropriate
  69         pure byte value.
  70         """
  71         if isinstance(value, (int, float)):
  72             return int(value)
  73 
  74         try:
  75             num = float(value[:-1])
  76         except ValueError:
  77             raise TraitError(
  78                 '{val} is not a valid memory specification. Must be an int or a string with suffix K, M, G, T'.format(
  79                     val=value
  80                 )
  81             )
  82         suffix = value[-1]
  83         if suffix not in self.UNIT_SUFFIXES:
  84             raise TraitError(
  85                 '{val} is not a valid memory specification. Must be an int or a string with suffix K, M, G, T'.format(
  86                     val=value
  87                 )
  88             )
  89         else:
  90             return int(float(num) * self.UNIT_SUFFIXES[suffix])
  91 
  92 
  93 class Callable(TraitType):
  94     """
  95     A trait which is callable.
  96 
  97     Classes are callable, as are instances
  98     with a __call__() method.
  99     """
  100 
  101     info_text = 'a callable'
  102 
  103     def validate(self, obj, value):
  104         if callable(value):
  105             return value
  106         else:
  107             self.error(obj, value)
  108 
  109 
  110 class EntryPointType(Type):
  111     """Entry point-extended Type
  112 
  113     classes can be registered via entry points
  114     in addition to standard 'mypackage.MyClass' strings
  115     """
  116 
  117     _original_help = ''
  118 
  119     def __init__(self, *args, entry_point_group, **kwargs):
  120         self.entry_point_group = entry_point_group
  121         super().__init__(*args, **kwargs)
  122 
  123     @property
  124     def help(self):
  125         """Extend help by listing currently installed choices"""
  126         chunks = [self._original_help]
  127         chunks.append("Currently installed: ")
  128         for key, entry_point in self.load_entry_points().items():
  129             chunks.append(
  130                 "  - {}: {}.{}".format(
  131                     key, entry_point.module_name, entry_point.object_name
  132                 )
  133             )
  134         return '\n'.join(chunks)
  135 
  136     @help.setter
  137     def help(self, value):
  138         self._original_help = value
  139 
  140     def load_entry_points(self):
  141         """Load my entry point group"""
  142         # load the group
  143         group = entrypoints.get_group_named(self.entry_point_group)
  144         # make it case-insensitive
  145         return {key.lower(): value for key, value in group.items()}
  146 
  147     def validate(self, obj, value):
  148         if isinstance(value, str):
  149             # first, look up in entry point registry
  150             registry = self.load_entry_points()
  151             key = value.lower()
  152             if key in registry:
  153                 value = registry[key].load()
  154         return super().validate(obj, value)
  155
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/jupyterhub/traitlets.py b/jupyterhub/traitlets.py
  --- a/jupyterhub/traitlets.py
  +++ b/jupyterhub/traitlets.py
  @@ -9,6 +9,7 @@
   from traitlets import TraitError
   from traitlets import TraitType
   from traitlets import Type
  +from traitlets import Undefined
   from traitlets import Unicode
   
   
  @@ -27,11 +28,15 @@
       but allows it to be specified as a single string.
       """
   
  -    def __init__(self, default_value=None, **kwargs):
  +    def __init__(self, default_value=Undefined, **kwargs):
           kwargs.setdefault('minlen', 1)
           if isinstance(default_value, str):
               default_value = [default_value]
  -        super().__init__(Unicode(), default_value, **kwargs)
  +        if default_value is not Undefined and (
  +            not (default_value is None and not kwargs.get("allow_none", False))
  +        ):
  +            kwargs["default_value"] = default_value
  +        super().__init__(Unicode(), **kwargs)
   
       def validate(self, obj, value):
           if isinstance(value, str):
  
verification_info: {"golden_diff": "diff --git a/jupyterhub/traitlets.py b/jupyterhub/traitlets.py\n--- a/jupyterhub/traitlets.py\n+++ b/jupyterhub/traitlets.py\n@@ -9,6 +9,7 @@\n from traitlets import TraitError\n from traitlets import TraitType\n from traitlets import Type\n+from traitlets import Undefined\n from traitlets import Unicode\n \n \n@@ -27,11 +28,15 @@\n     but allows it to be specified as a single string.\n     \"\"\"\n \n-    def __init__(self, default_value=None, **kwargs):\n+    def __init__(self, default_value=Undefined, **kwargs):\n         kwargs.setdefault('minlen', 1)\n         if isinstance(default_value, str):\n             default_value = [default_value]\n-        super().__init__(Unicode(), default_value, **kwargs)\n+        if default_value is not Undefined and (\n+            not (default_value is None and not kwargs.get(\"allow_none\", False))\n+        ):\n+            kwargs[\"default_value\"] = default_value\n+        super().__init__(Unicode(), **kwargs)\n \n     def validate(self, obj, value):\n         if isinstance(value, str):\n", "issue": "CI: jupyter-server build fails since late september\nThe `test_singleuser_auth` step fails with the following error ([example failure](https://travis-ci.org/github/jupyterhub/jupyterhub/jobs/729518444))\r\n\r\n```\r\n404 Client Error: Not Found for url: http://127.0.0.1:59471/@/space%20word/user/nandy/api/spec.yaml?redirects=2\r\n```\r\n\r\nHas something change with regards to `@` symbols or spaces in words like `space word`? Yes it has, in `jupyter-server` it seems, because there have been releases in this time span.\r\n\r\n![image](https://user-images.githubusercontent.com/3837114/94790428-9275a400-03d6-11eb-807f-1a174e1b3721.png)\r\n\r\n## References\r\n- [jupyter-server changelog](https://github.com/jupyter/jupyter_server/blob/master/CHANGELOG.md)\r\n- [The only PR that I saw in the changelog with clear potential to cause our CI error](https://github.com/jupyter/jupyter_server/pull/304)\r\n- [A seemingly related PR by, @minrk](https://github.com/jupyterhub/jupyterhub/pull/3168)\r\n- [Another seemingly related PR, by @danlester](https://github.com/jupyterhub/jupyterhub/pull/3167)\n", "before_files": [{"content": "\"\"\"\nTraitlets that are used in JupyterHub\n\"\"\"\n# Copyright (c) Jupyter Development Team.\n# Distributed under the terms of the Modified BSD License.\nimport entrypoints\nfrom traitlets import Integer\nfrom traitlets import List\nfrom traitlets import TraitError\nfrom traitlets import TraitType\nfrom traitlets import Type\nfrom traitlets import Unicode\n\n\nclass URLPrefix(Unicode):\n    def validate(self, obj, value):\n        u = super().validate(obj, value)\n        if not u.startswith('/'):\n            u = '/' + u\n        if not u.endswith('/'):\n            u = u + '/'\n        return u\n\n\nclass Command(List):\n    \"\"\"Traitlet for a command that should be a list of strings,\n    but allows it to be specified as a single string.\n    \"\"\"\n\n    def __init__(self, default_value=None, **kwargs):\n        kwargs.setdefault('minlen', 1)\n        if isinstance(default_value, str):\n            default_value = [default_value]\n        super().__init__(Unicode(), default_value, **kwargs)\n\n    def validate(self, obj, value):\n        if isinstance(value, str):\n            value = [value]\n        return super().validate(obj, value)\n\n\nclass ByteSpecification(Integer):\n    \"\"\"\n    Allow easily specifying bytes in units of 1024 with suffixes\n\n    Suffixes allowed are:\n      - K -> Kilobyte\n      - M -> Megabyte\n      - G -> Gigabyte\n      - T -> Terabyte\n    \"\"\"\n\n    UNIT_SUFFIXES = {\n        'K': 1024,\n        'M': 1024 * 1024,\n        'G': 1024 * 1024 * 1024,\n        'T': 1024 * 1024 * 1024 * 1024,\n    }\n\n    # Default to allowing None as a value\n    allow_none = True\n\n    def validate(self, obj, value):\n        \"\"\"\n        Validate that the passed in value is a valid memory specification\n\n        It could either be a pure int, when it is taken as a byte value.\n        If it has one of the suffixes, it is converted into the appropriate\n        pure byte value.\n        \"\"\"\n        if isinstance(value, (int, float)):\n            return int(value)\n\n        try:\n            num = float(value[:-1])\n        except ValueError:\n            raise TraitError(\n                '{val} is not a valid memory specification. Must be an int or a string with suffix K, M, G, T'.format(\n                    val=value\n                )\n            )\n        suffix = value[-1]\n        if suffix not in self.UNIT_SUFFIXES:\n            raise TraitError(\n                '{val} is not a valid memory specification. Must be an int or a string with suffix K, M, G, T'.format(\n                    val=value\n                )\n            )\n        else:\n            return int(float(num) * self.UNIT_SUFFIXES[suffix])\n\n\nclass Callable(TraitType):\n    \"\"\"\n    A trait which is callable.\n\n    Classes are callable, as are instances\n    with a __call__() method.\n    \"\"\"\n\n    info_text = 'a callable'\n\n    def validate(self, obj, value):\n        if callable(value):\n            return value\n        else:\n            self.error(obj, value)\n\n\nclass EntryPointType(Type):\n    \"\"\"Entry point-extended Type\n\n    classes can be registered via entry points\n    in addition to standard 'mypackage.MyClass' strings\n    \"\"\"\n\n    _original_help = ''\n\n    def __init__(self, *args, entry_point_group, **kwargs):\n        self.entry_point_group = entry_point_group\n        super().__init__(*args, **kwargs)\n\n    @property\n    def help(self):\n        \"\"\"Extend help by listing currently installed choices\"\"\"\n        chunks = [self._original_help]\n        chunks.append(\"Currently installed: \")\n        for key, entry_point in self.load_entry_points().items():\n            chunks.append(\n                \"  - {}: {}.{}\".format(\n                    key, entry_point.module_name, entry_point.object_name\n                )\n            )\n        return '\\n'.join(chunks)\n\n    @help.setter\n    def help(self, value):\n        self._original_help = value\n\n    def load_entry_points(self):\n        \"\"\"Load my entry point group\"\"\"\n        # load the group\n        group = entrypoints.get_group_named(self.entry_point_group)\n        # make it case-insensitive\n        return {key.lower(): value for key, value in group.items()}\n\n    def validate(self, obj, value):\n        if isinstance(value, str):\n            # first, look up in entry point registry\n            registry = self.load_entry_points()\n            key = value.lower()\n            if key in registry:\n                value = registry[key].load()\n        return super().validate(obj, value)\n", "path": "jupyterhub/traitlets.py"}], "after_files": [{"content": "\"\"\"\nTraitlets that are used in JupyterHub\n\"\"\"\n# Copyright (c) Jupyter Development Team.\n# Distributed under the terms of the Modified BSD License.\nimport entrypoints\nfrom traitlets import Integer\nfrom traitlets import List\nfrom traitlets import TraitError\nfrom traitlets import TraitType\nfrom traitlets import Type\nfrom traitlets import Undefined\nfrom traitlets import Unicode\n\n\nclass URLPrefix(Unicode):\n    def validate(self, obj, value):\n        u = super().validate(obj, value)\n        if not u.startswith('/'):\n            u = '/' + u\n        if not u.endswith('/'):\n            u = u + '/'\n        return u\n\n\nclass Command(List):\n    \"\"\"Traitlet for a command that should be a list of strings,\n    but allows it to be specified as a single string.\n    \"\"\"\n\n    def __init__(self, default_value=Undefined, **kwargs):\n        kwargs.setdefault('minlen', 1)\n        if isinstance(default_value, str):\n            default_value = [default_value]\n        if default_value is not Undefined and (\n            not (default_value is None and not kwargs.get(\"allow_none\", False))\n        ):\n            kwargs[\"default_value\"] = default_value\n        super().__init__(Unicode(), **kwargs)\n\n    def validate(self, obj, value):\n        if isinstance(value, str):\n            value = [value]\n        return super().validate(obj, value)\n\n\nclass ByteSpecification(Integer):\n    \"\"\"\n    Allow easily specifying bytes in units of 1024 with suffixes\n\n    Suffixes allowed are:\n      - K -> Kilobyte\n      - M -> Megabyte\n      - G -> Gigabyte\n      - T -> Terabyte\n    \"\"\"\n\n    UNIT_SUFFIXES = {\n        'K': 1024,\n        'M': 1024 * 1024,\n        'G': 1024 * 1024 * 1024,\n        'T': 1024 * 1024 * 1024 * 1024,\n    }\n\n    # Default to allowing None as a value\n    allow_none = True\n\n    def validate(self, obj, value):\n        \"\"\"\n        Validate that the passed in value is a valid memory specification\n\n        It could either be a pure int, when it is taken as a byte value.\n        If it has one of the suffixes, it is converted into the appropriate\n        pure byte value.\n        \"\"\"\n        if isinstance(value, (int, float)):\n            return int(value)\n\n        try:\n            num = float(value[:-1])\n        except ValueError:\n            raise TraitError(\n                '{val} is not a valid memory specification. Must be an int or a string with suffix K, M, G, T'.format(\n                    val=value\n                )\n            )\n        suffix = value[-1]\n        if suffix not in self.UNIT_SUFFIXES:\n            raise TraitError(\n                '{val} is not a valid memory specification. Must be an int or a string with suffix K, M, G, T'.format(\n                    val=value\n                )\n            )\n        else:\n            return int(float(num) * self.UNIT_SUFFIXES[suffix])\n\n\nclass Callable(TraitType):\n    \"\"\"\n    A trait which is callable.\n\n    Classes are callable, as are instances\n    with a __call__() method.\n    \"\"\"\n\n    info_text = 'a callable'\n\n    def validate(self, obj, value):\n        if callable(value):\n            return value\n        else:\n            self.error(obj, value)\n\n\nclass EntryPointType(Type):\n    \"\"\"Entry point-extended Type\n\n    classes can be registered via entry points\n    in addition to standard 'mypackage.MyClass' strings\n    \"\"\"\n\n    _original_help = ''\n\n    def __init__(self, *args, entry_point_group, **kwargs):\n        self.entry_point_group = entry_point_group\n        super().__init__(*args, **kwargs)\n\n    @property\n    def help(self):\n        \"\"\"Extend help by listing currently installed choices\"\"\"\n        chunks = [self._original_help]\n        chunks.append(\"Currently installed: \")\n        for key, entry_point in self.load_entry_points().items():\n            chunks.append(\n                \"  - {}: {}.{}\".format(\n                    key, entry_point.module_name, entry_point.object_name\n                )\n            )\n        return '\\n'.join(chunks)\n\n    @help.setter\n    def help(self, value):\n        self._original_help = value\n\n    def load_entry_points(self):\n        \"\"\"Load my entry point group\"\"\"\n        # load the group\n        group = entrypoints.get_group_named(self.entry_point_group)\n        # make it case-insensitive\n        return {key.lower(): value for key, value in group.items()}\n\n    def validate(self, obj, value):\n        if isinstance(value, str):\n            # first, look up in entry point registry\n            registry = self.load_entry_points()\n            key = value.lower()\n            if key in registry:\n                value = registry[key].load()\n        return super().validate(obj, value)\n", "path": "jupyterhub/traitlets.py"}]}