diff --git a/.flake8 b/.flake8 index 3c729960..41852dd2 100644 --- a/.flake8 +++ b/.flake8 @@ -4,11 +4,8 @@ # W: style warnings # C: complexity # D: docstring warnings (unused pydocstyle extension) -# F401: module imported but unused -# F403: import * -# F811: redefinition of unused `name` from line `N` # F841: local variable assigned but never used -ignore = E, C, W, D, F401, F403, F811, F841 +ignore = E, C, W, D, F841 builtins = c, get_config exclude = .cache, diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6401614b..41a0a3dd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,6 +17,15 @@ repos: args: - --py36-plus + # Autoformat: Python code + - repo: https://github.com/PyCQA/autoflake + rev: v1.7.7 + hooks: + - id: autoflake + # args ref: https://github.com/PyCQA/autoflake#advanced-usage + args: + - --in-place + # Autoformat: Python code - repo: https://github.com/pycqa/isort rev: 5.10.1 diff --git a/ci/check_sdist.py b/ci/check_sdist.py index 5fd008f3..b3ff18de 100755 --- a/ci/check_sdist.py +++ b/ci/check_sdist.py @@ -3,7 +3,6 @@ import sys import tarfile -from tarfile import TarFile expected_files = [ "docs/requirements.txt", diff --git a/docs/generate-metrics.py b/docs/generate-metrics.py index 81304bef..05cc6c35 100644 --- a/docs/generate-metrics.py +++ b/docs/generate-metrics.py @@ -1,5 +1,4 @@ import os -from os.path import join from pytablewriter import RstSimpleTableWriter from pytablewriter.style import Style diff --git a/docs/source/conf.py b/docs/source/conf.py index 03044de1..1a7ae015 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -171,7 +171,7 @@ if on_rtd: # -- Spell checking ------------------------------------------------------- try: - import sphinxcontrib.spelling + import sphinxcontrib.spelling # noqa except ImportError: pass else: diff --git a/examples/service-fastapi/app/__init__.py b/examples/service-fastapi/app/__init__.py index c07c4599..34f275ed 100644 --- a/examples/service-fastapi/app/__init__.py +++ b/examples/service-fastapi/app/__init__.py @@ -1 +1,3 @@ from .app import app + +__all__ = ["app"] diff --git a/examples/service-fastapi/app/service.py b/examples/service-fastapi/app/service.py index 81d7b87d..8aaeef94 100644 --- a/examples/service-fastapi/app/service.py +++ b/examples/service-fastapi/app/service.py @@ -51,7 +51,7 @@ async def me(user: User = Depends(get_current_user)): @router.get("/debug") -async def index(request: Request, user: User = Depends(get_current_user)): +async def debug(request: Request, user: User = Depends(get_current_user)): """ Authenticated function that returns a few pieces of debug * Environ of the service process diff --git a/jupyterhub/__init__.py b/jupyterhub/__init__.py index 948f47a6..4b758c5d 100644 --- a/jupyterhub/__init__.py +++ b/jupyterhub/__init__.py @@ -1 +1,3 @@ from ._version import __version__, version_info + +__all__ = ["__version__", "version_info"] diff --git a/jupyterhub/alembic/versions/19c0846f6344_base_revision_for_0_5.py b/jupyterhub/alembic/versions/19c0846f6344_base_revision_for_0_5.py index 9ee5a24a..c8421da4 100644 --- a/jupyterhub/alembic/versions/19c0846f6344_base_revision_for_0_5.py +++ b/jupyterhub/alembic/versions/19c0846f6344_base_revision_for_0_5.py @@ -11,9 +11,6 @@ down_revision = None branch_labels = None depends_on = None -import sqlalchemy as sa -from alembic import op - def upgrade(): pass diff --git a/jupyterhub/apihandlers/__init__.py b/jupyterhub/apihandlers/__init__.py index 39733829..f72887eb 100644 --- a/jupyterhub/apihandlers/__init__.py +++ b/jupyterhub/apihandlers/__init__.py @@ -1,5 +1,5 @@ from . import auth, groups, hub, proxy, services, users -from .base import * +from .base import * # noqa default_handlers = [] for mod in (auth, hub, proxy, users, groups, services): diff --git a/jupyterhub/apihandlers/hub.py b/jupyterhub/apihandlers/hub.py index 47a4a6e5..4474c821 100644 --- a/jupyterhub/apihandlers/hub.py +++ b/jupyterhub/apihandlers/hub.py @@ -5,7 +5,6 @@ import json import sys from tornado import web -from tornado.ioloop import IOLoop from .._version import __version__ from ..scopes import needs_scope diff --git a/jupyterhub/auth.py b/jupyterhub/auth.py index e31cd395..2795a781 100644 --- a/jupyterhub/auth.py +++ b/jupyterhub/auth.py @@ -776,8 +776,6 @@ def _deprecated_method(old_name, new_name, version): return deprecated -import types - # deprecate white/blacklist method names for _old_name, _new_name, _version in [ ("check_whitelist", "check_allowed", "1.2"), diff --git a/jupyterhub/handlers/__init__.py b/jupyterhub/handlers/__init__.py index e39e5730..79f3ea87 100644 --- a/jupyterhub/handlers/__init__.py +++ b/jupyterhub/handlers/__init__.py @@ -1,6 +1,6 @@ from . import base, login, metrics, pages -from .base import * -from .login import * +from .base import * # noqa +from .login import * # noqa default_handlers = [] for mod in (base, pages, login, metrics): diff --git a/jupyterhub/oauth/provider.py b/jupyterhub/oauth/provider.py index d3e17c46..bb72e338 100644 --- a/jupyterhub/oauth/provider.py +++ b/jupyterhub/oauth/provider.py @@ -13,7 +13,6 @@ from ..scopes import ( _check_scopes_exist, _resolve_requested_scopes, access_scopes, - expand_scopes, identify_scopes, ) from ..utils import compare_token, hash_token diff --git a/jupyterhub/orm.py b/jupyterhub/orm.py index ea8f54d8..0aa1e1ca 100644 --- a/jupyterhub/orm.py +++ b/jupyterhub/orm.py @@ -35,7 +35,6 @@ from sqlalchemy.orm import ( sessionmaker, ) from sqlalchemy.pool import StaticPool -from sqlalchemy.sql.expression import bindparam from sqlalchemy.types import LargeBinary, Text, TypeDecorator from tornado.log import app_log @@ -996,7 +995,6 @@ def check_db_revision(engine): ).first()[0] if alembic_revision == head: app_log.debug("database schema version found: %s", alembic_revision) - pass else: raise DatabaseSchemaMismatch( "Found database schema version {found} != {head}. " diff --git a/jupyterhub/proxy.py b/jupyterhub/proxy.py index dbe0fcdb..87b47a6c 100644 --- a/jupyterhub/proxy.py +++ b/jupyterhub/proxy.py @@ -250,14 +250,12 @@ class Proxy(LoggingConfigurable): The proxy implementation should also have a way to associate the fact that a route came from JupyterHub. """ - pass async def delete_route(self, routespec): """Delete a route with a given routespec if it exists. **Subclasses must define this method** """ - pass async def get_all_routes(self): """Fetch and return all the routes associated by JupyterHub from the @@ -274,7 +272,6 @@ class Proxy(LoggingConfigurable): 'data': the attached data dict for this route (as specified in add_route) } """ - pass async def get_route(self, routespec): """Return the route info for a given routespec. @@ -683,7 +680,6 @@ class ConfigurableHTTPProxy(Proxy): os.remove(self.pid_file) except FileNotFoundError: self.log.debug("PID file %s already removed", self.pid_file) - pass def _get_ssl_options(self): """List of cmd proxy options to use internal SSL""" diff --git a/jupyterhub/services/auth.py b/jupyterhub/services/auth.py index d61239e5..24106ee2 100644 --- a/jupyterhub/services/auth.py +++ b/jupyterhub/services/auth.py @@ -35,7 +35,6 @@ import string import time import uuid import warnings -from functools import partial from http import HTTPStatus from unittest import mock from urllib.parse import urlencode diff --git a/jupyterhub/singleuser/__init__.py b/jupyterhub/singleuser/__init__.py index 60159406..f3188bc7 100644 --- a/jupyterhub/singleuser/__init__.py +++ b/jupyterhub/singleuser/__init__.py @@ -5,6 +5,13 @@ Contains default notebook-app subclass and mixins from .app import SingleUserNotebookApp, main from .mixins import HubAuthenticatedHandler, make_singleuser_app +__all__ = [ + "SingleUserNotebookApp", + "main", + "HubAuthenticatedHandler", + "make_singleuser_app", +] + # backward-compatibility JupyterHubLoginHandler = SingleUserNotebookApp.login_handler_class JupyterHubLogoutHandler = SingleUserNotebookApp.logout_handler_class diff --git a/jupyterhub/singleuser/mixins.py b/jupyterhub/singleuser/mixins.py index 953d3d27..dfad82ca 100755 --- a/jupyterhub/singleuser/mixins.py +++ b/jupyterhub/singleuser/mixins.py @@ -14,7 +14,6 @@ import logging import os import random import secrets -import ssl import sys import warnings from datetime import timezone diff --git a/jupyterhub/spawner.py b/jupyterhub/spawner.py index 39837b08..756fefa7 100644 --- a/jupyterhub/spawner.py +++ b/jupyterhub/spawner.py @@ -896,7 +896,6 @@ class Spawner(LoggingConfigurable): Override in subclasses to restore any extra state that is needed to track the single-user server for that user. Subclasses should call super(). """ - pass def get_state(self): """Save state of spawner into database. @@ -1341,7 +1340,6 @@ class Spawner(LoggingConfigurable): Stopping a server does *not* call this method. """ - pass def add_poll_callback(self, callback, *args, **kwargs): """Add a callback to fire when the single-user server stops""" diff --git a/jupyterhub/tests/conftest.py b/jupyterhub/tests/conftest.py index b200bb44..b880017b 100644 --- a/jupyterhub/tests/conftest.py +++ b/jupyterhub/tests/conftest.py @@ -27,7 +27,6 @@ Fixtures to add functionality or spawning behavior # Distributed under the terms of the Modified BSD License. import asyncio import copy -import inspect import os import sys from functools import partial @@ -36,7 +35,6 @@ from subprocess import TimeoutExpired from unittest import mock from pytest import fixture, raises -from tornado import ioloop from tornado.httpclient import HTTPError from tornado.platform.asyncio import AsyncIOMainLoop diff --git a/jupyterhub/tests/mocking.py b/jupyterhub/tests/mocking.py index 5066ddde..8aed2bfb 100644 --- a/jupyterhub/tests/mocking.py +++ b/jupyterhub/tests/mocking.py @@ -36,7 +36,6 @@ from unittest import mock from urllib.parse import urlparse from pamela import PAMError -from tornado.ioloop import IOLoop from traitlets import Bool, Dict, default from .. import metrics, orm, roles @@ -45,7 +44,7 @@ from ..auth import PAMAuthenticator from ..singleuser import SingleUserNotebookApp from ..spawner import SimpleLocalProcessSpawner from ..utils import random_port, utcnow -from .utils import async_requests, public_host, public_url, ssl_setup +from .utils import async_requests, public_url, ssl_setup def mock_authenticate(username, password, service, encoding): diff --git a/jupyterhub/tests/populate_db.py b/jupyterhub/tests/populate_db.py index c85e8c57..33b5247b 100644 --- a/jupyterhub/tests/populate_db.py +++ b/jupyterhub/tests/populate_db.py @@ -4,7 +4,6 @@ Run with old versions of jupyterhub to test upgrade/downgrade used in test_db.py """ -import os from datetime import datetime from functools import partial diff --git a/jupyterhub/tests/selenium/conftest.py b/jupyterhub/tests/selenium/conftest.py index 06de2029..a68ce6e3 100644 --- a/jupyterhub/tests/selenium/conftest.py +++ b/jupyterhub/tests/selenium/conftest.py @@ -1,6 +1,5 @@ import pytest from selenium import webdriver -from selenium.webdriver.firefox.options import Options as FirefoxOptions @pytest.fixture() diff --git a/jupyterhub/tests/selenium/test_browser.py b/jupyterhub/tests/selenium/test_browser.py index 4e7ba00c..9c52878c 100644 --- a/jupyterhub/tests/selenium/test_browser.py +++ b/jupyterhub/tests/selenium/test_browser.py @@ -1,21 +1,14 @@ import asyncio -import time from functools import partial import pytest -from selenium import webdriver -from selenium.common.exceptions import NoSuchElementException, TimeoutException -from selenium.webdriver.common.by import By +from selenium.common.exceptions import NoSuchElementException from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait from tornado.escape import url_escape from tornado.httputil import url_concat -from jupyterhub.tests.selenium.locators import ( - HomePageLocators, - LoginPageLocators, - TokenPageLocators, -) +from jupyterhub.tests.selenium.locators import LoginPageLocators from jupyterhub.utils import exponential_backoff from ...utils import url_path_join diff --git a/jupyterhub/tests/test_api.py b/jupyterhub/tests/test_api.py index fa96b22d..2bd8c811 100644 --- a/jupyterhub/tests/test_api.py +++ b/jupyterhub/tests/test_api.py @@ -19,8 +19,15 @@ from ..objects import Server from ..utils import url_path_join as ujoin from ..utils import utcnow from .conftest import new_username -from .mocking import public_host, public_url -from .utils import add_user, api_request, async_requests, auth_header, find_user +from .utils import ( + add_user, + api_request, + async_requests, + auth_header, + find_user, + public_host, + public_url, +) # -------------------- # Authentication tests diff --git a/jupyterhub/tests/test_auth_expiry.py b/jupyterhub/tests/test_auth_expiry.py index 5b750e79..876f85b8 100644 --- a/jupyterhub/tests/test_auth_expiry.py +++ b/jupyterhub/tests/test_auth_expiry.py @@ -7,7 +7,6 @@ authentication can expire in a number of ways: - doesn't need refresh - needs refresh and cannot be refreshed without new login """ -from contextlib import contextmanager from unittest import mock from urllib.parse import parse_qs, urlparse diff --git a/jupyterhub/tests/test_eventlog.py b/jupyterhub/tests/test_eventlog.py index d8dcd285..5b6649cd 100644 --- a/jupyterhub/tests/test_eventlog.py +++ b/jupyterhub/tests/test_eventlog.py @@ -14,8 +14,6 @@ import jsonschema import pytest from traitlets.config import Config -from .mocking import MockHub - # To test new schemas, add them to the `valid_events` # and `invalid_events` dictionary below. diff --git a/jupyterhub/tests/test_named_servers.py b/jupyterhub/tests/test_named_servers.py index 0c1c9d69..d5f03054 100644 --- a/jupyterhub/tests/test_named_servers.py +++ b/jupyterhub/tests/test_named_servers.py @@ -11,9 +11,9 @@ from tornado.httputil import url_concat from .. import orm from ..utils import url_escape_path, url_path_join -from .mocking import FormSpawner, public_url +from .mocking import FormSpawner from .test_api import TIMESTAMP, add_user, api_request, fill_user, normalize_user -from .utils import async_requests, get_page +from .utils import async_requests, get_page, public_url @pytest.fixture diff --git a/jupyterhub/tests/test_orm.py b/jupyterhub/tests/test_orm.py index 7a7f0310..3009a11d 100644 --- a/jupyterhub/tests/test_orm.py +++ b/jupyterhub/tests/test_orm.py @@ -7,7 +7,6 @@ from datetime import datetime, timedelta from unittest import mock import pytest -from tornado import gen from .. import crypto, objects, orm, roles from ..emptyclass import EmptyClass diff --git a/jupyterhub/tests/test_services.py b/jupyterhub/tests/test_services.py index a5d50571..b1e9fe9f 100644 --- a/jupyterhub/tests/test_services.py +++ b/jupyterhub/tests/test_services.py @@ -6,8 +6,6 @@ from subprocess import Popen from async_generator import asynccontextmanager -from .. import orm -from ..roles import roles_to_scopes from ..utils import ( exponential_backoff, maybe_future, diff --git a/jupyterhub/tests/test_singleuser.py b/jupyterhub/tests/test_singleuser.py index c45ade95..e139b41f 100644 --- a/jupyterhub/tests/test_singleuser.py +++ b/jupyterhub/tests/test_singleuser.py @@ -1,7 +1,7 @@ """Tests for jupyterhub.singleuser""" import os import sys -from contextlib import contextmanager, nullcontext +from contextlib import nullcontext from subprocess import CalledProcessError, check_output from unittest import mock from urllib.parse import urlencode, urlparse diff --git a/jupyterhub/utils.py b/jupyterhub/utils.py index 61fc6c7c..291c412b 100644 --- a/jupyterhub/utils.py +++ b/jupyterhub/utils.py @@ -527,7 +527,6 @@ def print_stacks(file=sys.stderr): """ # local imports because these will not be used often, # no need to add them to startup - import asyncio import traceback from .log import coroutine_frames diff --git a/pyproject.toml b/pyproject.toml index b018fc66..054c6f1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,26 @@ +# autoflake is used for autoformatting Python code +# +# ref: https://github.com/PyCQA/autoflake#readme +# +[tool.autoflake] +ignore-init-module-imports = true +remove-all-unused-imports = true +remove-duplicate-keys = true +#remove-unused-variables = true + + +# isort is used for autoformatting Python code +# +# ref: https://pycqa.github.io/isort/ +# [tool.isort] profile = "black" + +# black is used for autoformatting Python code +# +# ref: https://black.readthedocs.io/en/stable/ +# [tool.black] skip-string-normalization = true # target-version should be all supported versions, see @@ -12,6 +32,12 @@ target_version = [ "py310", ] + +# tbump is used to simplify and standardize the release process when updating +# the version, making a git commit and tag, and pushing changes. +# +# ref: https://github.com/your-tools/tbump#readme +# [tool.tbump] # Uncomment this if your project is hosted on GitHub: github_url = "https://github.com/jupyterhub/jupyterhub"