internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: jupyterhub/jupyterhub

Issue:
Replace forward slashes in CILogon oauth id for cookie
Fixes https://github.com/jupyterhub/oauthenticator/issues/132

I mostly implemented this to check it really worked...

If you have a suggestion on a more general way to fix this, I can improve this patch.


Discussion:
I think `/` shouldn't have been allowed in the username at all. These usernames should have failed at `.validate_username`.

https://github.com/jupyterhub/oauthenticator/issues/133 is open to address CILogon problems with the new OAuth 2 setup. I think we are using the wrong field for username in CILogon right now. Email is probably the best choice, but another option is possible, if you have an idea.

Resolving patch:
diff --git a/jupyterhub/auth.py b/jupyterhub/auth.py
--- a/jupyterhub/auth.py
+++ b/jupyterhub/auth.py
@@ -144,6 +144,12 @@ def validate_username(self, username):
 
         Return True if username is valid, False otherwise.
         """
+        if '/' in username:
+            # / is not allowed in usernames
+            return False
+        if not username:
+            # empty usernames are not allowed
+            return False
         if not self.username_regex:
             return True
         return bool(self.username_regex.match(username))


Test patch:
Repository: jupyterhub/jupyterhub

Issue:
Jupyterhub 0.8.0 radio buttons unclickable or ugly due to form-control class
```
jupyterhub --version
0.8.0
```

I have some radio buttons in my spawner's `_option_form_default`:

```
return """<label for="type">Which type of instance do you want to launch?</label>
        <table>
        <tr>
        <td><input type="radio" name="type" value="c4.8xlarge" checked="checked"></td>
        <td>&nbsp;c4.8xlarge (36 CPU, 60GB RAM, $1.591/h)</td>
        </tr>
        <tr>
        <td><input type="radio" name="type" value="r4.8xlarge"></td>
        <td>&nbsp;r4.8xlarge (32 CPU, 244GB RAM, $2.341/h)</td>
        </tr>
        </table><br>
        """
```

In `0.8.0` version these are unclickable. Removing `form-control` class introduced [here](https://github.com/jupyterhub/jupyterhub/blob/master/share/jupyter/hub/templates/spawn.html) fixes the issue for me. 

I also tried buttons like this:

```
        <tr>
        <td><label>
        <input type="radio" name="type" value="c4.8xlarge">
        &nbsp;c4.8xlarge (36 CPU, 60GB RAM, $1.591/h)
        </label></td>
        </tr>
```

These are clickable but look ugly with the `form-control` class. 

Removing the `form-control` class makes them both clickable and pretty :) 


Discussion:

Resolving patch:
diff --git a/jupyterhub/_version.py b/jupyterhub/_version.py
--- a/jupyterhub/_version.py
+++ b/jupyterhub/_version.py
@@ -6,8 +6,8 @@
 version_info = (
     0,
     8,
-    1,
-    # 'dev',
+    2,
+    'dev',
 )
 
 __version__ = '.'.join(map(str, version_info))


Test patch:
Repository: jupyterhub/jupyterhub

Issue:
Remove `redirect=` from the URL on error pages
We now track redirect count to get a better error in the event of redirect loops. One problem is that we leave the `?redirect=5` in the URL when we render the error page. This results in refreshing the page after a failure *appearing* to have a redirect loop, but it's reusing the value from the previous failure.

When rendering the error page, we should remove the `redirect=` part from the URL so that refreshing the page doesn't preserve the value.

This would probably be best done in [`share/jupyter/hub/templates/page.html`](https://github.com/jupyterhub/jupyterhub/blob/master/share/jupyter/hub/templates/page.html)

We have similar query removal in jupyter/notebook to remove `token=` [here](https://github.com/jupyter/notebook/blob/5.0.0/notebook/templates/page.html#L160-L182).


Discussion:
Hi! I'm at PyCon Canada and thinking about looking into this issue for the sprints
Is this change likely to fixed my issue (#1551)? If so, when is it likely to be released?
@minrk @Carreau @willingc any thoughts on @Andy-Doddington 's question?

Resolving patch:
diff --git a/jupyterhub/singleuser.py b/jupyterhub/singleuser.py
--- a/jupyterhub/singleuser.py
+++ b/jupyterhub/singleuser.py
@@ -152,9 +152,41 @@ def hub_auth(self):
     </a>
 </span>
 {% endblock %}
+
 {% block logo %}
 <img src='{{logo_url}}' alt='Jupyter Notebook'/>
 {% endblock logo %}
+
+{% block script %}
+{{ super() }}
+<script type='text/javascript'>
+  function _remove_redirects_param() {
+    // remove ?redirects= param from URL so that
+    // successful page loads don't increment the redirect loop counter
+    if (window.location.search.length <= 1) {
+      return;
+    }
+    var search_parameters = window.location.search.slice(1).split('&');
+    for (var i = 0; i < search_parameters.length; i++) {
+      if (search_parameters[i].split('=')[0] === 'redirects') {
+        // remote token from search parameters
+        search_parameters.splice(i, 1);
+        var new_search = '';
+        if (search_parameters.length) {
+          new_search = '?' + search_parameters.join('&');
+        }
+        var new_url = window.location.origin +
+                      window.location.pathname +
+                      new_search +
+                      window.location.hash;
+        window.history.replaceState({}, "", new_url);
+        return;
+      }
+    }
+  }
+  _remove_redirects_param();
+</script>
+{% endblock script %}
 """
 
 


Test patch:
Repository: jupyterhub/jupyterhub

Issue:
It would be helpful to be able to add configurable metadata to services
In my use case, we have services by their design have a specific URL, but I'd like to pass along additional information to present a more friendly interface to the end user. In our specific case we have a tool which is associated with a database. It is more efficient at the low level to use an shortened form to refer to the database. However, I want to present to the end user a descriptive name.

My propose solution is to add a "info" trait to the service object (I initially called it metadata and found out that it collides with a property).

So to deploy a service my YAML section of my config.yaml looks like:
```
  services:
    whoami:
      apiToken: < blah>
      url: http://whoami.kubernetes:8080
      admin: false
      info:
        databases:
          my_db: "My Database"
          your_db: "Your Database as saved last January"
```
I've already made the change to jupyterhub in my local copy and will be pushing a pull request shortly.

Thought I'd open an issue in the event there are some greater issues at play here.




Discussion:

Resolving patch:
diff --git a/jupyterhub/apihandlers/services.py b/jupyterhub/apihandlers/services.py
--- a/jupyterhub/apihandlers/services.py
+++ b/jupyterhub/apihandlers/services.py
@@ -23,6 +23,7 @@ def service_model(service):
         'prefix': service.server.base_url if service.server else '',
         'command': service.command,
         'pid': service.proc.pid if service.proc else 0,
+        'info': service.info
     }
 
 class ServiceListAPIHandler(APIHandler):
diff --git a/jupyterhub/services/service.py b/jupyterhub/services/service.py
--- a/jupyterhub/services/service.py
+++ b/jupyterhub/services/service.py
@@ -175,6 +175,13 @@ class Service(LoggingConfigurable):
         If unspecified, an API token will be generated for managed services.
         """
     ).tag(input=True)
+
+    info = Dict(
+        help="""Provide a place to include miscellaneous information about the service,
+        provided through the configuration
+        """
+    ).tag(input=True)
+
     # Managed service API:
     spawner = Any()
 


Test patch:
diff --git a/jupyterhub/tests/test_api.py b/jupyterhub/tests/test_api.py
--- a/jupyterhub/tests/test_api.py
+++ b/jupyterhub/tests/test_api.py
@@ -1500,6 +1500,7 @@ def test_get_services(app, mockservice_url):
             'pid': mockservice.proc.pid,
             'prefix': mockservice.server.base_url,
             'url': mockservice.url,
+            'info': {},
         }
     }
 
@@ -1526,6 +1527,7 @@ def test_get_service(app, mockservice_url):
         'pid': mockservice.proc.pid,
         'prefix': mockservice.server.base_url,
         'url': mockservice.url,
+        'info': {},
     }
 
     r = yield api_request(app, 'services/%s' % mockservice.name,
Repository: jupyterhub/jupyterhub

Issue:
Update README.md
The default localhost does not work for docker. `0.0.0.0` is necessary to forward docker inside to outside.


Discussion:
Hi @aisensiy, Thanks for the PR. Instead of adding the ip for a specific use case, it would be better to leave the command as is, and add a sentence after that directs readers to the documentation http://jupyterhub.readthedocs.io/en/latest/getting-started/networking-basics.html#set-the-proxy-s-ip-address-and-port and http://jupyterhub.readthedocs.io/en/latest/quickstart-docker.html#starting-jupyterhub-with-docker

Thanks.

Resolving patch:
diff --git a/jupyterhub/app.py b/jupyterhub/app.py
--- a/jupyterhub/app.py
+++ b/jupyterhub/app.py
@@ -358,7 +358,7 @@ def _url_part_changed(self, change):
             self.bind_url = bind_url
 
     bind_url = Unicode(
-        "http://127.0.0.1:8000",
+        "http://:8000",
         help="""The public facing URL of the whole JupyterHub application.
 
         This is the address on which the proxy will bind.


Test patch:
diff --git a/jupyterhub/tests/test_app.py b/jupyterhub/tests/test_app.py
--- a/jupyterhub/tests/test_app.py
+++ b/jupyterhub/tests/test_app.py
@@ -242,6 +242,16 @@ def new_hub():
             {'bind_url': 'http://0.0.0.0:12345/sub'},
             {'base_url': '/sub/'},
         ),
+        (
+            # no config, test defaults
+            {},
+            {
+                'base_url': '/',
+                'bind_url': 'http://:8000',
+                'ip': '',
+                'port': 8000,
+                },
+        ),
     ]
 )
 def test_url_config(hub_config, expected):
Repository: jupyterhub/jupyterhub

Issue:
npm local install suggestion
Hi,

This is not a bug, it's a suggestion for occasional python users like me, who are not familiar with node.js and npm related commands.

Every single time when I start a new virtualenv, and install jupyterhub, runnung `jupyterhub` tells me: 

    Failed to find proxy ['configurable-http-proxy']
    The proxy can be installed with `npm install -g configurable-http-proxy`
 
Which is fair enough, except since many of us, non node.js users, don't have a local copy of node in our environment, and npm is installed globally. This means running `npm install -g configurable-http-proxy` returns errors complaining about file/folder access permissions, which is again, fair enough since the npm is installed globally.

This means every time I need to search for the error online, and try to fix it somehow. After a few times doing it, it's not too hard to find the answer, but still annoying, since what I really expect, is to install my python package (jupyterhub in this case) and use it. And I'm certain many other users encounter the same problem.

So my suggestion is to include something along the lines:

    You need to set these two environment variables to install npm packages locally:
    $ PATH="$HOME/.node_modules/bin:$PATH"
    $ export npm_config_prefix=~/.node_modules

Thanks for the greate work BTW.


Discussion:
@adrinjalali Thanks for the suggestion. We'll see what we can do to make this more helpful. Thanks for the kind words too.
Like Python, there are a dozen different ways to install npm packages, with different permissions requirements.

We can link to [npm's own docs](https://docs.npmjs.com/getting-started/fixing-npm-permissions#option-2-change-npms-default-directory-to-another-directory) on this topic, which include something similar to the above.
That would help, since for a non-npm user finding that page is not trivial.
it certainly is!
@minrk @adrinjalali Would you prefer updating the message or docs?
I'd update the message if I had to choose.
Next action: Update the message when displayed when node is not installed.

Resolving patch:
diff --git a/jupyterhub/proxy.py b/jupyterhub/proxy.py
--- a/jupyterhub/proxy.py
+++ b/jupyterhub/proxy.py
@@ -488,7 +488,10 @@ async def start(self):
         except FileNotFoundError as e:
             self.log.error(
                 "Failed to find proxy %r\n"
-                "The proxy can be installed with `npm install -g configurable-http-proxy`"
+                "The proxy can be installed with `npm install -g configurable-http-proxy`."
+                "To install `npm`, install nodejs which includes `npm`."
+                "If you see an `EACCES` error or permissions error, refer to the `npm` "
+                "documentation on How To Prevent Permissions Errors."
                 % self.command
             )
             raise


Test patch:
Repository: jupyterhub/jupyterhub

Issue:
Polish docs for 0.9 release
Open issues that we would like docs included (@minrk Please add to this list)
- [x] Merge and polish PR #1967 (Configuring user environments)

Additional 'to do' items
- [x] Update changelog
- [x] Update contributors list

Not strictly documentation
- [x] Update metadata in setup.py for pypi.org's additional display of docs now



Discussion:
What is the additional display of docs on PyPI? I can't find any information about it.
Sorry, I wasn't clear. There are some additional fields that are now displayed with the new PyPI UI. Just a reminder to myself to look through them.

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -83,6 +83,10 @@ def get_package_data():
     if os.path.exists(pjoin(d, '__init__.py')):
         packages.append(d.replace(os.path.sep, '.'))
 
+with open('README.md', encoding="utf8") as f:
+    readme = f.read()
+
+
 setup_args = dict(
     name                = 'jupyterhub',
     scripts             = glob(pjoin('scripts', '*')),
@@ -93,10 +97,11 @@ def get_package_data():
     package_data        = get_package_data(),
     version             = ns['__version__'],
     description         = "JupyterHub: A multi-user server for Jupyter notebooks",
-    long_description    = "See https://jupyterhub.readthedocs.io for more info.",
+    long_description    = readme,
+    long_description_content_type = 'text/markdown',
     author              = "Jupyter Development Team",
     author_email        = "jupyter@googlegroups.com",
-    url                 = "http://jupyter.org",
+    url                 = "https://jupyter.org",
     license             = "BSD",
     platforms           = "Linux, Mac OS X",
     keywords            = ['Interactive', 'Interpreter', 'Shell', 'Web'],
@@ -109,6 +114,12 @@ def get_package_data():
         'Programming Language :: Python',
         'Programming Language :: Python :: 3',
     ],
+    project_urls        = {
+        'Documentation': 'https://jupyterhub.readthedocs.io',
+        'Funding': 'https://jupyter.org/about',
+        'Source': 'https://github.com/jupyterhub/jupyterhub/',
+        'Tracker': 'https://github.com/jupyterhub/jupyterhub/issues',
+    },
 )
 
 #---------------------------------------------------------------------------


Test patch:
Repository: jupyterhub/jupyterhub

Issue:
Redirect to login page broken when using baseUrl
Freshly deployed vanilla hub using the zero2jupyterhub chart (v0.7-8b9cb79) on google cloud. None standard parts are that this uses an nginx-ingress to route traffic to the hub which is at example.com/foobar.

http://example.com/foobar redirects to http://example.com/foobar/hub/foobar but http://example.com/foobar/ redirects to http://example.com/foobar/hub/login.

Via @minrk and @chicocvenancio

> Yeah, I'm guessing it's not redirecting correctly without the trailing slash. I think there's a check for if not startswith(prefix) and it probably incorrectly returns False on the edge case where the trailing slash is missing
> I think it's PrefixRedirectHandler 
>
> `base_url` is defined as having the trailing slash https://github.com/jupyterhub/jupyterhub/blob/master/jupyterhub/handlers/base.py#L63


Discussion:

Resolving patch:
diff --git a/jupyterhub/handlers/base.py b/jupyterhub/handlers/base.py
--- a/jupyterhub/handlers/base.py
+++ b/jupyterhub/handlers/base.py
@@ -901,6 +901,11 @@ class PrefixRedirectHandler(BaseHandler):
     """
     def get(self):
         uri = self.request.uri
+        # Since self.base_url will end with trailing slash.
+        # Ensure uri will end with trailing slash when matching
+        # with self.base_url.
+        if not uri.endswith('/'):
+            uri += '/'
         if uri.startswith(self.base_url):
             path = self.request.uri[len(self.base_url):]
         else:


Test patch:
Repository: jupyterhub/jupyterhub

Issue:
Create an async version of options_from_form
I would like to see an async version of `Spawner.options_from_form` much like what `Spawner.get_options_form` is to `Spawner.options_form`.

It looks doable, create a similar function to `get_options_form` (`process_options_form`?), then modify [L150 of pages.py](https://github.com/jupyterhub/jupyterhub/blob/bd16299ffbf58614a789ae047aab551ac056e38d/jupyterhub/handlers/pages.py#L150)

I can get the PR warmed up unless there is a reason this was not done.


Discussion:
Yeah, that makes sense! Unlike `get_options_form`, I think `options_from_form` can be made async with the appropriate addition of `utils.maybe_future`, instead of needing a new method.

Resolving patch:
diff --git a/jupyterhub/handlers/pages.py b/jupyterhub/handlers/pages.py
--- a/jupyterhub/handlers/pages.py
+++ b/jupyterhub/handlers/pages.py
@@ -12,7 +12,7 @@
 from tornado.httputil import url_concat
 
 from .. import orm
-from ..utils import admin_only, url_path_join
+from ..utils import admin_only, url_path_join, maybe_future
 from .base import BaseHandler
 
 
@@ -147,7 +147,7 @@ async def post(self, for_user=None):
         for key, byte_list in self.request.files.items():
             form_options["%s_file"%key] = byte_list
         try:
-            options = user.spawner.options_from_form(form_options)
+            options = await maybe_future(user.spawner.options_from_form(form_options))
             await self.spawn_single_user(user, options=options)
         except Exception as e:
             self.log.error("Failed to spawn single-user server with form", exc_info=True)


Test patch:
Repository: jupyterhub/jupyterhub

Issue:
Username with upper-case character causes 500 error
**Describe the bug**
I am on Ubuntu 16.04. If I create a username with an upper-case character, say userA1, I get a 500 Internal Server Error when I log in to JupyterHub. The stack trace:

    Traceback (most recent call last):
      File "/anaconda/envs/py35/lib/python3.5/site-packages/tornado/web.py", line 1543, in _execute
        result = yield result
      File "/anaconda/envs/py35/lib/python3.5/asyncio/futures.py", line 294, in result
        raise self._exception
      File "/anaconda/envs/py35/lib/python3.5/asyncio/tasks.py", line 242, in _step
        result = coro.throw(exc)
      File "/anaconda/envs/py35/lib/python3.5/site-packages/jupyterhub/handlers/login.py", line 81, in post
        user = await self.login_user(data)
      File "/anaconda/envs/py35/lib/python3.5/site-packages/jupyterhub/handlers/base.py", line 483, in login_user
        await maybe_future(self.authenticator.add_user(user))
      File "/anaconda/envs/py35/lib/python3.5/asyncio/futures.py", line 381, in __iter__
        yield self  # This tells Task to wait for completion.
      File "/anaconda/envs/py35/lib/python3.5/asyncio/tasks.py", line 310, in _wakeup
        future.result()
      File "/anaconda/envs/py35/lib/python3.5/asyncio/futures.py", line 294, in result
        raise self._exception
      File "/anaconda/envs/py35/lib/python3.5/asyncio/tasks.py", line 240, in _step
        result = coro.send(None)
      File "/anaconda/envs/py35/lib/python3.5/site-packages/jupyterhub/auth.py", line 525, in add_user
        raise KeyError("User %s does not exist." % user.name)
    KeyError: 'User usera1 does not exist.'





Discussion:

Resolving patch:
diff --git a/jupyterhub/auth.py b/jupyterhub/auth.py
--- a/jupyterhub/auth.py
+++ b/jupyterhub/auth.py
@@ -554,7 +554,12 @@ async def add_user(self, user):
             if self.create_system_users:
                 await maybe_future(self.add_system_user(user))
             else:
-                raise KeyError("User %s does not exist." % user.name)
+                raise KeyError(
+                    "User {} does not exist on the system."
+                    " Set LocalAuthenticator.create_system_users=True"
+                    " to automatically create system users from jupyterhub users."
+                    .format(user.name)
+                )
 
         await maybe_future(super().add_user(user))
 


Test patch:
Repository: jupyterhub/jupyterhub

Issue:
Is the /hub to /hub/ redirect useful?
I get this from my hub pod of my z2jh deployment when I access `/` and have configured so I don't need to click on the "login with ..." button in order to login using `c.Authenticator.auto_login = True`.

Is the `/hub` to `/hub/` doing something for us in some way or is it a mistake to do it?

```
[I 2019-01-22 13:43:13.282 JupyterHub log:158] 302 GET / -> /hub (@194.218.xxx.xxx) 0.69ms
[I 2019-01-22 13:43:13.313 JupyterHub log:158] 302 GET /hub -> /hub/ (@194.218.xxx.xxx) 0.61ms
[I 2019-01-22 13:43:13.344 JupyterHub log:158] 302 GET /hub/ -> /hub/login (@194.218.xxx.xxx) 0.65m
```


Discussion:

Resolving patch:
diff --git a/jupyterhub/handlers/base.py b/jupyterhub/handlers/base.py
--- a/jupyterhub/handlers/base.py
+++ b/jupyterhub/handlers/base.py
@@ -1068,6 +1068,10 @@ def get(self):
             path = self.request.uri[len(self.base_url):]
         else:
             path = self.request.path
+        if not path:
+            # default / -> /hub/ redirect
+            # avoiding extra hop through /hub
+            path = '/'
         self.redirect(url_path_join(
             self.hub.base_url, path,
         ), permanent=False)


Test patch: