diff --git a/examples/service-fastapi/app/security.py b/examples/service-fastapi/app/security.py index 63fd2a5e..14c66d2b 100644 --- a/examples/service-fastapi/app/security.py +++ b/examples/service-fastapi/app/security.py @@ -32,6 +32,7 @@ if os.environ.get("JUPYTERHUB_OAUTH_SCOPES"): else: access_scopes = ["access:services"] + ### For consideration: optimize performance with a cache instead of ### always hitting the Hub api? async def get_current_user( diff --git a/jupyterhub/_memoize.py b/jupyterhub/_memoize.py index 21907b1d..7266340f 100644 --- a/jupyterhub/_memoize.py +++ b/jupyterhub/_memoize.py @@ -83,6 +83,7 @@ def lru_cache_key(key_func, maxsize=1024): def cache_func(func): cache = LRUCache(maxsize=maxsize) + # the actual decorated function: @wraps(func) def cached(*args, **kwargs): diff --git a/jupyterhub/app.py b/jupyterhub/app.py index bfd436ff..29822429 100644 --- a/jupyterhub/app.py +++ b/jupyterhub/app.py @@ -2929,7 +2929,6 @@ class JupyterHub(Application): init_spawners_future.add_done_callback(log_init_time) try: - # don't allow a zero timeout because we still need to be sure # that the Spawner objects are defined and pending await gen.with_timeout( diff --git a/jupyterhub/auth.py b/jupyterhub/auth.py index 7798e0ef..3e2b117d 100644 --- a/jupyterhub/auth.py +++ b/jupyterhub/auth.py @@ -357,6 +357,7 @@ class Authenticator(LoggingConfigurable): ), DeprecationWarning, ) + # use old name instead of new # if old name is overridden in subclass def _new_calls_old(old_name, *args, **kwargs): diff --git a/jupyterhub/handlers/base.py b/jupyterhub/handlers/base.py index aa71c9e8..0a32292c 100644 --- a/jupyterhub/handlers/base.py +++ b/jupyterhub/handlers/base.py @@ -664,7 +664,12 @@ class BaseHandler(RequestHandler): next_url = "//" + next_url.lstrip("/") parsed_next_url = urlparse(next_url) - if (next_url + '/').startswith((f'{proto}://{host}/', f'//{host}/',)) or ( + if (next_url + '/').startswith( + ( + f'{proto}://{host}/', + f'//{host}/', + ) + ) or ( self.subdomain_host and parsed_next_url.netloc and ("." + parsed_next_url.netloc).endswith( diff --git a/jupyterhub/scopes.py b/jupyterhub/scopes.py index fb345f3d..5d21d5be 100644 --- a/jupyterhub/scopes.py +++ b/jupyterhub/scopes.py @@ -685,7 +685,7 @@ def _check_scope_access(api_handler, req_scope, **kwargs): req_scope, ) return True - for (filter_, filter_value) in kwargs.items(): + for filter_, filter_value in kwargs.items(): if filter_ in sub_scope and filter_value in sub_scope[filter_]: app_log.debug("Argument-based access to %s via %s", api_name, req_scope) return True diff --git a/jupyterhub/services/auth.py b/jupyterhub/services/auth.py index 24106ee2..f7a6c87e 100644 --- a/jupyterhub/services/auth.py +++ b/jupyterhub/services/auth.py @@ -1146,6 +1146,7 @@ class HubAuthenticated: except UserNotAllowed as e: # cache None, in case get_user is called again while processing the error self._hub_auth_user_cache = None + # Override redirect so if/when tornado @web.authenticated # tries to redirect to login URL, 403 will be raised instead. # This is not the best, but avoids problems that can be caused diff --git a/jupyterhub/singleuser/mixins.py b/jupyterhub/singleuser/mixins.py index dfad82ca..27868cb1 100755 --- a/jupyterhub/singleuser/mixins.py +++ b/jupyterhub/singleuser/mixins.py @@ -957,6 +957,7 @@ def make_singleuser_app(App): merged_flags = {} merged_flags.update(empty_parent_app.flags or {}) merged_flags.update(flags) + # create mixed-in App class, bringing it all together class SingleUserNotebookApp(SingleUserNotebookAppMixin, App): aliases = merged_aliases diff --git a/jupyterhub/tests/conftest.py b/jupyterhub/tests/conftest.py index d0480362..7d3b9a0e 100644 --- a/jupyterhub/tests/conftest.py +++ b/jupyterhub/tests/conftest.py @@ -290,7 +290,6 @@ async def _mockservice(request, app, external=False, url=False): spec['url'] = 'http://127.0.0.1:%i' % random_port() if external: - spec['oauth_redirect_uri'] = 'http://127.0.0.1:%i' % random_port() event_loop = asyncio.get_running_loop() diff --git a/jupyterhub/tests/selenium/test_browser.py b/jupyterhub/tests/selenium/test_browser.py index d86e8500..e42dcfb7 100644 --- a/jupyterhub/tests/selenium/test_browser.py +++ b/jupyterhub/tests/selenium/test_browser.py @@ -168,7 +168,6 @@ async def test_open_url_login( form_action, user, ): - await open_url(app, browser, path=url) url_new = url_path_join(public_host(app), app.hub.base_url, url_concat(url, params)) await in_thread(browser.get, url_new) diff --git a/jupyterhub/tests/test_app.py b/jupyterhub/tests/test_app.py index a26d191f..cae40a60 100644 --- a/jupyterhub/tests/test_app.py +++ b/jupyterhub/tests/test_app.py @@ -233,7 +233,6 @@ def test_cookie_secret_string_(): async def test_load_groups(tmpdir, request): - to_load = { 'blue': { 'users': ['cyclops', 'rogue', 'wolverine'], diff --git a/jupyterhub/tests/test_auth.py b/jupyterhub/tests/test_auth.py index db4fd8bc..8bd88fd8 100644 --- a/jupyterhub/tests/test_auth.py +++ b/jupyterhub/tests/test_auth.py @@ -559,7 +559,6 @@ class MockGroupsAuthenticator(auth.Authenticator): async def test_auth_managed_groups( app, user, group, authenticated_groups, refresh_groups ): - authenticator = MockGroupsAuthenticator( parent=app, authenticated_groups=authenticated_groups, diff --git a/jupyterhub/tests/test_memoize.py b/jupyterhub/tests/test_memoize.py index c37942a8..3ae120ff 100644 --- a/jupyterhub/tests/test_memoize.py +++ b/jupyterhub/tests/test_memoize.py @@ -25,7 +25,6 @@ def test_lru_cache(): def test_lru_cache_key(): - call_count = 0 @lru_cache_key(frozenset) @@ -47,7 +46,6 @@ def test_lru_cache_key(): def test_do_not_cache(): - call_count = 0 @lru_cache_key(lambda arg: arg) diff --git a/jupyterhub/tests/test_roles.py b/jupyterhub/tests/test_roles.py index 3198e59a..cec5c0df 100644 --- a/jupyterhub/tests/test_roles.py +++ b/jupyterhub/tests/test_roles.py @@ -1243,7 +1243,6 @@ async def test_admin_role_respects_config(): ], ) async def test_admin_role_membership(in_db, role_users, admin_users, expected_members): - load_roles = [] if role_users is not None: load_roles.append({"name": "admin", "users": role_users}) diff --git a/jupyterhub/tests/test_scopes.py b/jupyterhub/tests/test_scopes.py index 5facb172..8ceef6da 100644 --- a/jupyterhub/tests/test_scopes.py +++ b/jupyterhub/tests/test_scopes.py @@ -971,7 +971,6 @@ def test_intersect_groups(request, db, left, right, expected, groups): async def test_list_users_filter( app, group, create_service_with_scopes, scopes, expected ): - # create users: for i in (1, 2): user = add_user(app.db, app, name=f'in-{i}') @@ -1028,7 +1027,6 @@ async def test_list_users_filter( async def test_list_groups_filter( request, app, create_service_with_scopes, scopes, expected ): - # create groups: groups = [] for i in (1, 2, 3): diff --git a/jupyterhub/utils.py b/jupyterhub/utils.py index 291c412b..36789698 100644 --- a/jupyterhub/utils.py +++ b/jupyterhub/utils.py @@ -710,7 +710,7 @@ def get_accepted_mimetype(accept_header, choices=None): Return `None` if choices is given and no match is found, or nothing is specified. """ - for (mime, params, q) in _parse_accept_header(accept_header): + for mime, params, q in _parse_accept_header(accept_header): if choices: if mime in choices: return mime diff --git a/setup.py b/setup.py index 5670167e..955669c9 100755 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ def get_data_files(): """Get data files in share/jupyter""" data_files = [] - for (d, dirs, filenames) in os.walk(share_jupyterhub): + for d, dirs, filenames in os.walk(share_jupyterhub): rel_d = os.path.relpath(d, here) data_files.append((rel_d, [os.path.join(rel_d, f) for f in filenames])) return data_files @@ -237,7 +237,7 @@ class CSS(BaseCommand): earliest_target = sorted(mtime(t) for t in targets)[0] # check if any .less files are newer than the generated targets - for (dirpath, dirnames, filenames) in os.walk(static): + for dirpath, dirnames, filenames in os.walk(static): for f in filenames: if f.endswith('.less'): path = pjoin(static, dirpath, f)