From 0983b715cce2e15932dffb87f0956338234a169e Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sun, 19 Nov 2023 07:27:17 -0600 Subject: [PATCH] Update ruff and typings (#1365) --- .pre-commit-config.yaml | 8 +- docs/source/conf.py | 6 +- examples/simple/simple_ext1/application.py | 6 +- examples/simple/simple_ext1/handlers.py | 2 - examples/simple/simple_ext2/application.py | 2 +- examples/simple/simple_ext2/handlers.py | 2 - examples/simple/tests/test_handlers.py | 4 +- jupyter_server/_sysinfo.py | 2 +- jupyter_server/_tz.py | 2 +- jupyter_server/auth/__init__.py | 8 +- jupyter_server/auth/__main__.py | 2 +- jupyter_server/auth/identity.py | 2 +- jupyter_server/auth/login.py | 4 +- jupyter_server/auth/security.py | 3 +- jupyter_server/auth/utils.py | 2 +- jupyter_server/base/handlers.py | 12 +-- jupyter_server/base/websocket.py | 1 - jupyter_server/extension/application.py | 11 +-- jupyter_server/extension/manager.py | 8 +- jupyter_server/extension/utils.py | 12 +-- jupyter_server/gateway/connections.py | 2 +- jupyter_server/gateway/gateway_client.py | 7 +- jupyter_server/gateway/handlers.py | 3 +- jupyter_server/gateway/managers.py | 12 +-- jupyter_server/i18n/__init__.py | 2 +- jupyter_server/kernelspecs/handlers.py | 2 +- jupyter_server/log.py | 10 +- jupyter_server/nbconvert/handlers.py | 2 +- jupyter_server/pytest_plugin.py | 7 +- jupyter_server/serverapp.py | 17 ++-- jupyter_server/services/contents/fileio.py | 12 +-- .../services/kernels/connection/abc.py | 4 - .../services/kernels/connection/channels.py | 6 +- .../services/kernels/kernelmanager.py | 4 +- jupyter_server/services/kernels/websocket.py | 1 - .../services/sessions/sessionmanager.py | 8 +- jupyter_server/terminal/__init__.py | 6 +- jupyter_server/terminal/handlers.py | 2 +- jupyter_server/terminal/terminalmanager.py | 2 +- jupyter_server/traittypes.py | 18 ++-- jupyter_server/utils.py | 4 +- pyproject.toml | 91 +++++++------------ tests/auth/test_authorizer.py | 4 +- tests/auth/test_identity.py | 2 +- tests/auth/test_legacy_login.py | 10 +- tests/auth/test_login.py | 10 +- tests/base/test_websocket.py | 2 +- tests/conftest.py | 12 +-- tests/extension/mockextensions/app.py | 1 - tests/extension/test_app.py | 4 +- tests/extension/test_config.py | 6 +- tests/extension/test_handler.py | 2 +- tests/extension/test_launch.py | 16 ++-- tests/extension/test_manager.py | 2 +- tests/nbconvert/test_handlers.py | 2 +- tests/services/api/test_api.py | 2 +- tests/services/contents/test_api.py | 2 +- tests/services/contents/test_fileio.py | 4 +- tests/services/contents/test_manager.py | 4 +- .../services/events/mockextension/__init__.py | 2 +- tests/services/events/test_api.py | 4 +- tests/services/events/test_extension.py | 2 +- tests/services/kernels/test_api.py | 2 +- tests/services/kernels/test_config.py | 2 +- tests/services/kernels/test_cull.py | 2 +- tests/services/kernels/test_events.py | 3 +- tests/services/sessions/test_api.py | 4 +- tests/services/sessions/test_manager.py | 2 +- tests/test_gateway.py | 4 +- tests/test_serverapp.py | 2 +- tests/test_terminal.py | 16 ++-- tests/test_utils.py | 2 +- tests/unix_sockets/conftest.py | 6 +- tests/unix_sockets/test_api.py | 6 +- .../test_serverapp_integration.py | 14 +-- 75 files changed, 207 insertions(+), 272 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6ea5ed59ba..ff6b9662b6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -33,7 +33,7 @@ repos: [mdformat-gfm, mdformat-frontmatter, mdformat-footnote] - repo: https://github.com/pre-commit/mirrors-prettier - rev: "v3.0.3" + rev: "v3.1.0" hooks: - id: prettier types_or: [yaml, html, json] @@ -58,7 +58,7 @@ repos: - id: rst-inline-touching-normal - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v1.6.1" + rev: "v1.7.0" hooks: - id: mypy files: jupyter_server @@ -67,7 +67,7 @@ repos: ["traitlets>=5.13", "jupyter_core>=5.5", "jupyter_client>=8.5"] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.5 + rev: v0.1.6 hooks: - id: ruff types_or: [python, jupyter] @@ -76,7 +76,7 @@ repos: types_or: [python, jupyter] - repo: https://github.com/scientific-python/cookie - rev: "2023.10.27" + rev: "2023.11.17" hooks: - id: sp-repo-review additional_dependencies: ["repo-review[cli]"] diff --git a/docs/source/conf.py b/docs/source/conf.py index 4cb0a01b01..7f59cb956b 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# # Jupyter Server documentation build configuration file, created by # sphinx-quickstart on Mon Apr 13 09:51:11 2015. # @@ -44,7 +42,7 @@ ] try: - import enchant # type:ignore[import-not-found] # noqa + import enchant # type:ignore[import-not-found] extensions += ["sphinxcontrib.spelling"] except ImportError: @@ -338,7 +336,7 @@ spelling_word_list_filename = "spelling_wordlist.txt" # import before any doc is built, so _ is guaranteed to be injected -import jupyter_server.transutils # noqa: F401 +import jupyter_server.transutils CONFIG_HEADER = """\ .. _other-full-config: diff --git a/examples/simple/simple_ext1/application.py b/examples/simple/simple_ext1/application.py index b28d8f8781..b77e57e4a8 100644 --- a/examples/simple/simple_ext1/application.py +++ b/examples/simple/simple_ext1/application.py @@ -36,11 +36,11 @@ class SimpleApp1(ExtensionAppJinjaMixin, ExtensionApp): # Local path to templates directory. template_paths = [DEFAULT_TEMPLATE_FILES_PATH] # type:ignore[assignment] - configA = Unicode("", config=True, help="Config A example.") # noqa + configA = Unicode("", config=True, help="Config A example.") - configB = Unicode("", config=True, help="Config B example.") # noqa + configB = Unicode("", config=True, help="Config B example.") - configC = Unicode("", config=True, help="Config C example.") # noqa + configC = Unicode("", config=True, help="Config C example.") def initialize_handlers(self): """Initialize handlers.""" diff --git a/examples/simple/simple_ext1/handlers.py b/examples/simple/simple_ext1/handlers.py index fefbdf610b..9d25057bc3 100644 --- a/examples/simple/simple_ext1/handlers.py +++ b/examples/simple/simple_ext1/handlers.py @@ -47,8 +47,6 @@ def get(self, matched_part=None, *args, **kwargs): class BaseTemplateHandler(ExtensionHandlerJinjaMixin, ExtensionHandlerMixin, JupyterHandler): """The base template handler.""" - pass - class TypescriptHandler(BaseTemplateHandler): """A typescript handler.""" diff --git a/examples/simple/simple_ext2/application.py b/examples/simple/simple_ext2/application.py index 6f8498407d..b9da358131 100644 --- a/examples/simple/simple_ext2/application.py +++ b/examples/simple/simple_ext2/application.py @@ -29,7 +29,7 @@ class SimpleApp2(ExtensionAppJinjaMixin, ExtensionApp): # Local path to templates directory. template_paths = [DEFAULT_TEMPLATE_FILES_PATH] # type:ignore[assignment] - configD = Unicode("", config=True, help="Config D example.") # noqa + configD = Unicode("", config=True, help="Config D example.") def initialize_handlers(self): """Initialize handlers.""" diff --git a/examples/simple/simple_ext2/handlers.py b/examples/simple/simple_ext2/handlers.py index ea649b68d2..4f52e6f061 100644 --- a/examples/simple/simple_ext2/handlers.py +++ b/examples/simple/simple_ext2/handlers.py @@ -20,8 +20,6 @@ def get(self, matched_part=None, *args, **kwargs): class BaseTemplateHandler(ExtensionHandlerJinjaMixin, ExtensionHandlerMixin, JupyterHandler): """A base template handler.""" - pass - class IndexHandler(BaseTemplateHandler): """The root API handler.""" diff --git a/examples/simple/tests/test_handlers.py b/examples/simple/tests/test_handlers.py index efa302d5d5..59b9d045ae 100644 --- a/examples/simple/tests/test_handlers.py +++ b/examples/simple/tests/test_handlers.py @@ -2,7 +2,7 @@ import pytest -@pytest.fixture +@pytest.fixture() def jp_server_auth_resources(jp_server_auth_core_resources): """The server auth resources.""" for url_regex in [ @@ -12,7 +12,7 @@ def jp_server_auth_resources(jp_server_auth_core_resources): return jp_server_auth_core_resources -@pytest.fixture +@pytest.fixture() def jp_server_config(jp_template_dir, jp_server_authorizer): """The server config.""" return { diff --git a/jupyter_server/_sysinfo.py b/jupyter_server/_sysinfo.py index 5e17215890..f167c4e92a 100644 --- a/jupyter_server/_sysinfo.py +++ b/jupyter_server/_sysinfo.py @@ -41,7 +41,7 @@ def pkg_commit_hash(pkg_path): if p.exists(p.join(cur_path, ".git")): try: proc = subprocess.Popen( - ["git", "rev-parse", "--short", "HEAD"], # noqa + ["git", "rev-parse", "--short", "HEAD"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=pkg_path, diff --git a/jupyter_server/_tz.py b/jupyter_server/_tz.py index 7027d80124..a7a495de85 100644 --- a/jupyter_server/_tz.py +++ b/jupyter_server/_tz.py @@ -13,7 +13,7 @@ ZERO = timedelta(0) -class tzUTC(tzinfo): # noqa: N801 +class tzUTC(tzinfo): """tzinfo object for UTC (zero offset)""" def utcoffset(self, d: datetime | None) -> timedelta: diff --git a/jupyter_server/auth/__init__.py b/jupyter_server/auth/__init__.py index bb7d345be2..36418f214b 100644 --- a/jupyter_server/auth/__init__.py +++ b/jupyter_server/auth/__init__.py @@ -1,4 +1,4 @@ -from .authorizer import * # noqa: F403 -from .decorator import authorized # noqa: F401 -from .identity import * # noqa: F403 -from .security import passwd # noqa: F401 +from .authorizer import * +from .decorator import authorized +from .identity import * +from .security import passwd diff --git a/jupyter_server/auth/__main__.py b/jupyter_server/auth/__main__.py index a564a55864..d1573f11a1 100644 --- a/jupyter_server/auth/__main__.py +++ b/jupyter_server/auth/__main__.py @@ -20,7 +20,7 @@ def set_password(args): password_repeat = getpass("" if args.quiet else "Repeat password: ") if password1 != password_repeat: warnings.warn("Passwords do not match, try again", stacklevel=2) - elif len(password1) < 4: # noqa + elif len(password1) < 4: warnings.warn("Please provide at least 4 characters", stacklevel=2) else: password = password1 diff --git a/jupyter_server/auth/identity.py b/jupyter_server/auth/identity.py index 1374bc7430..adeb567b5b 100644 --- a/jupyter_server/auth/identity.py +++ b/jupyter_server/auth/identity.py @@ -496,7 +496,7 @@ def is_token_authenticated(self, handler: web.RequestHandler) -> bool: - skip origin-checks for scripts """ # ensure get_user has been called, so we know if we're token-authenticated - handler.current_user # noqa + handler.current_user # noqa: B018 return getattr(handler, "_token_authenticated", False) def validate_security( diff --git a/jupyter_server/auth/login.py b/jupyter_server/auth/login.py index b9eda58e08..22832df341 100644 --- a/jupyter_server/auth/login.py +++ b/jupyter_server/auth/login.py @@ -179,7 +179,7 @@ def is_token_authenticated(cls, handler): """DEPRECATED in 2.0, use IdentityProvider API""" if getattr(handler, "_user_id", None) is None: # ensure get_user has been called, so we know if we're token-authenticated - handler.current_user # noqa + handler.current_user # noqa: B018 return getattr(handler, "_token_authenticated", False) @classmethod @@ -233,7 +233,7 @@ def get_user_token(cls, handler): """DEPRECATED in 2.0, use IdentityProvider API""" token = handler.token if not token: - return + return None # check login token from URL argument or Authorization header user_token = cls.get_token(handler) authenticated = False diff --git a/jupyter_server/auth/security.py b/jupyter_server/auth/security.py index 1a7107eb11..a5ae185f1e 100644 --- a/jupyter_server/auth/security.py +++ b/jupyter_server/auth/security.py @@ -52,8 +52,7 @@ def passwd(passphrase=None, algorithm="argon2"): if p0 == p1: passphrase = p0 break - else: - warnings.warn("Passwords do not match.", stacklevel=2) + warnings.warn("Passwords do not match.", stacklevel=2) else: msg = "No matching passwords found. Giving up." raise ValueError(msg) diff --git a/jupyter_server/auth/utils.py b/jupyter_server/auth/utils.py index 3c73f6f794..b0f790be1f 100644 --- a/jupyter_server/auth/utils.py +++ b/jupyter_server/auth/utils.py @@ -166,4 +166,4 @@ def get_anonymous_username() -> str: Get a random user-name based on the moons of Jupyter. This function returns names like "Anonymous Io" or "Anonymous Metis". """ - return moons_of_jupyter[random.randint(0, len(moons_of_jupyter) - 1)] # noqa + return moons_of_jupyter[random.randint(0, len(moons_of_jupyter) - 1)] diff --git a/jupyter_server/base/handlers.py b/jupyter_server/base/handlers.py index a45443619d..e6e6b3246e 100644 --- a/jupyter_server/base/handlers.py +++ b/jupyter_server/base/handlers.py @@ -64,7 +64,7 @@ def json_sys_info(): """Get sys info as json.""" - global _sys_info_cache # noqa + global _sys_info_cache # noqa: PLW0603 if _sys_info_cache is None: _sys_info_cache = json.dumps(get_sys_info()) return _sys_info_cache @@ -527,11 +527,11 @@ def check_xsrf_cookie(self) -> None: """Bypass xsrf cookie checks when token-authenticated""" if not hasattr(self, "_jupyter_current_user"): # Called too early, will be checked later - return + return None if self.token_authenticated or self.settings.get("disable_check_xsrf", False): # Token-authenticated requests do not need additional XSRF-check # Servers without authentication are vulnerable to XSRF - return + return None try: return super().check_xsrf_cookie() except web.HTTPError as e: @@ -608,11 +608,11 @@ async def prepare(self) -> Awaitable[None] | None: # type:ignore[override] # check for overridden get_current_user + default IdentityProvider # deprecated way to override auth (e.g. JupyterHub < 3.0) # allow deprecated, overridden get_current_user - warnings.warn( # noqa + warnings.warn( "Overriding JupyterHandler.get_current_user is deprecated in jupyter-server 2.0." " Use an IdentityProvider class.", DeprecationWarning, - # stacklevel not useful here + stacklevel=1, ) user = User(self.get_current_user()) else: @@ -695,7 +695,7 @@ def write_error(self, status_code: int, **kwargs: Any) -> None: # get the custom message, if defined try: message = exception.log_message % exception.args - except Exception: # noqa + except Exception: pass # construct the custom reason, if defined diff --git a/jupyter_server/base/websocket.py b/jupyter_server/base/websocket.py index 82082299e6..a27b7a72a7 100644 --- a/jupyter_server/base/websocket.py +++ b/jupyter_server/base/websocket.py @@ -81,7 +81,6 @@ def check_origin(self, origin: Optional[str] = None) -> bool: def clear_cookie(self, *args, **kwargs): """meaningless for websockets""" - pass @no_type_check def open(self, *args, **kwargs): diff --git a/jupyter_server/extension/application.py b/jupyter_server/extension/application.py index 0bd4e8b018..aeeab5a94d 100644 --- a/jupyter_server/extension/application.py +++ b/jupyter_server/extension/application.py @@ -28,7 +28,7 @@ def _preparse_for_subcommand(application_klass, argv): """Preparse command line to look for subcommands.""" # Read in arguments from command line. if len(argv) == 0: - return + return None # Find any subcommands. if application_klass.subcommands and len(argv) > 0: @@ -218,7 +218,7 @@ def _default_serverapp(self): if ServerApp.initialized(): try: return ServerApp.instance() - except Exception: # noqa + except Exception: # error retrieving instance, e.g. MultipleInstanceError pass @@ -271,7 +271,7 @@ def _default_static_url_prefix(self): handlers: List[tuple[t.Any, ...]] = List( help=_i18n("""Handlers appended to the server.""") - ).tag(config=True) # type:ignore[assignment] + ).tag(config=True) def _config_file_name_default(self): """The default config file name.""" @@ -281,15 +281,12 @@ def _config_file_name_default(self): def initialize_settings(self): """Override this method to add handling of settings.""" - pass def initialize_handlers(self): """Override this method to append handlers to a Jupyter Server.""" - pass def initialize_templates(self): """Override this method to add handling of template files.""" - pass def _prepare_config(self): """Builds a Config object from the extension's traits and passes @@ -599,7 +596,7 @@ def launch_instance(cls, argv=None, **kwargs): extension's landing page. """ # Handle arguments. - if argv is None: # noqa + if argv is None: # noqa: SIM108 args = sys.argv[1:] # slice out extension config. else: args = argv diff --git a/jupyter_server/extension/manager.py b/jupyter_server/extension/manager.py index 156eba825a..3509e2e9f6 100644 --- a/jupyter_server/extension/manager.py +++ b/jupyter_server/extension/manager.py @@ -2,6 +2,7 @@ from __future__ import annotations import importlib +from itertools import starmap from tornado.gen import multi from traitlets import Any, Bool, Dict, HasTraits, Instance, List, Unicode, default, observe @@ -392,12 +393,7 @@ def load_all_extensions(self): async def stop_all_extensions(self): """Call the shutdown hooks in all extensions.""" - await multi( - [ - self.stop_extension(name, apps) - for name, apps in sorted(dict(self.extension_apps).items()) - ] - ) + await multi(list(starmap(self.stop_extension, sorted(dict(self.extension_apps).items())))) def any_activity(self): """Check for any activity currently happening across all extension applications.""" diff --git a/jupyter_server/extension/utils.py b/jupyter_server/extension/utils.py index 69af455b7c..5d18939ab2 100644 --- a/jupyter_server/extension/utils.py +++ b/jupyter_server/extension/utils.py @@ -7,26 +7,18 @@ class ExtensionLoadingError(Exception): """An extension loading error.""" - pass - class ExtensionMetadataError(Exception): """An extension metadata error.""" - pass - class ExtensionModuleNotFound(Exception): """An extension module not found error.""" - pass - class NotAnExtensionApp(Exception): """An error raised when a module is not an extension.""" - pass - def get_loader(obj, logger=None): """Looks for _load_jupyter_server_extension as an attribute @@ -36,12 +28,12 @@ def get_loader(obj, logger=None): underscore prefix. """ try: - return getattr(obj, "_load_jupyter_server_extension") # noqa B009 + return obj._load_jupyter_server_extension except AttributeError: pass try: - func = getattr(obj, "load_jupyter_server_extension") # noqa B009 + func = obj.load_jupyter_server_extension except AttributeError: msg = "_load_jupyter_server_extension function was not found." raise ExtensionLoadingError(msg) from None diff --git a/jupyter_server/gateway/connections.py b/jupyter_server/gateway/connections.py index 9926644859..028a0f8f4e 100644 --- a/jupyter_server/gateway/connections.py +++ b/jupyter_server/gateway/connections.py @@ -104,7 +104,7 @@ async def _read_messages(self): # NOTE(esevan): if websocket is not disconnected by client, try to reconnect. if not self.disconnected and self.retry < GatewayClient.instance().gateway_retry_max: - jitter = random.randint(10, 100) * 0.01 # noqa + jitter = random.randint(10, 100) * 0.01 retry_interval = ( min( GatewayClient.instance().gateway_retry_interval * (2**self.retry), diff --git a/jupyter_server/gateway/gateway_client.py b/jupyter_server/gateway/gateway_client.py index fb0562032b..437d54d227 100644 --- a/jupyter_server/gateway/gateway_client.py +++ b/jupyter_server/gateway/gateway_client.py @@ -46,8 +46,6 @@ class GatewayTokenRenewerMeta(ABCMeta, type(LoggingConfigurable)): # type: ignore[misc] """The metaclass necessary for proper ABC behavior in a Configurable.""" - pass - class GatewayTokenRenewerBase( # type:ignore[misc] ABC, LoggingConfigurable, metaclass=GatewayTokenRenewerMeta @@ -71,7 +69,6 @@ def get_token( Given the current authorization header key, scheme, and token, this method returns a (potentially renewed) token for use against the Gateway server. """ - pass class NoOpTokenRenewer(GatewayTokenRenewerBase): # type:ignore[misc] @@ -632,7 +629,7 @@ def load_connection_args(self, **kwargs): return kwargs - def update_cookies(self, cookie: SimpleCookie[ty.Any]) -> None: + def update_cookies(self, cookie: SimpleCookie) -> None: """Update cookies from existing requests for load balancers""" if not self.accept_cookies: return @@ -822,7 +819,7 @@ async def gateway_request(endpoint: str, **kwargs: ty.Any) -> HTTPResponse: # Update cookies on GatewayClient from server if configured. cookie_values = response.headers.get("Set-Cookie") if cookie_values: - cookie: SimpleCookie[ty.Any] = SimpleCookie() + cookie: SimpleCookie = SimpleCookie() cookie.load(cookie_values) GatewayClient.instance().update_cookies(cookie) return response diff --git a/jupyter_server/gateway/handlers.py b/jupyter_server/gateway/handlers.py index 2dbbf3edfc..dcde4cd5ca 100644 --- a/jupyter_server/gateway/handlers.py +++ b/jupyter_server/gateway/handlers.py @@ -49,7 +49,6 @@ def check_origin(self, origin=None): def set_default_headers(self): """Undo the set_default_headers in JupyterHandler which doesn't make sense for websockets""" - pass def get_compression_options(self): """Get the compression options for the socket.""" @@ -240,7 +239,7 @@ async def _read_messages(self, callback): # NOTE(esevan): if websocket is not disconnected by client, try to reconnect. if not self.disconnected and self.retry < GatewayClient.instance().gateway_retry_max: - jitter = random.randint(10, 100) * 0.01 # noqa + jitter = random.randint(10, 100) * 0.01 retry_interval = ( min( GatewayClient.instance().gateway_retry_interval * (2**self.retry), diff --git a/jupyter_server/gateway/managers.py b/jupyter_server/gateway/managers.py index 21aaefb86d..cd0b27b50d 100644 --- a/jupyter_server/gateway/managers.py +++ b/jupyter_server/gateway/managers.py @@ -313,7 +313,7 @@ async def get_kernel_spec(self, kernel_name, **kwargs): try: response = await gateway_request(kernel_spec_url, method="GET") except web.HTTPError as error: - if error.status_code == 404: # noqa: PLR2004 + if error.status_code == 404: # Convert not found to KeyError since that's what the Notebook handler expects # message is not used, but might as well make it useful for troubleshooting msg = f"kernelspec {kernel_name} not found on Gateway server at: {GatewayClient.instance().url}" @@ -342,7 +342,7 @@ async def get_kernel_spec_resource(self, kernel_name, path): try: response = await gateway_request(kernel_spec_resource_url, method="GET") except web.HTTPError as error: - if error.status_code == 404: # noqa: PLR2004 + if error.status_code == 404: kernel_spec_resource = None else: raise @@ -368,7 +368,7 @@ async def kernel_culled(self, kernel_id: str) -> bool: # typing: ignore # Note that should the redundant polling be consolidated, or replaced with an event-based # notification model, this will need to be revisited. km = self.kernel_manager.get_kernel(kernel_id) - except Exception: # noqa + except Exception: # Let exceptions here reflect culled kernel pass return km is None @@ -439,7 +439,7 @@ async def refresh_model(self, model=None): response = await gateway_request(self.kernel_url, method="GET") except web.HTTPError as error: - if error.status_code == 404: # noqa: PLR2004 + if error.status_code == 404: self.log.warning("Kernel not found at: %s" % self.kernel_url) model = None else: @@ -533,7 +533,7 @@ async def shutdown_kernel(self, now=False, restart=False): response = await gateway_request(self.kernel_url, method="DELETE") self.log.debug("Shutdown kernel response: %d %s", response.code, response.reason) except web.HTTPError as error: - if error.status_code == 404: # noqa: PLR2004 + if error.status_code == 404: self.log.debug("Shutdown kernel response: kernel not found (ignored)") else: raise @@ -585,7 +585,6 @@ async def is_alive(self): def cleanup_resources(self, restart=False): """Clean up resources when the kernel is shut down""" - pass KernelManagerABC.register(GatewayKernelManager) @@ -656,7 +655,6 @@ def serialize_datetime(dt): def start(self) -> None: """Start the queue.""" - pass def stop(self) -> None: """Stop the queue.""" diff --git a/jupyter_server/i18n/__init__.py b/jupyter_server/i18n/__init__.py index f71bb916dd..896f41c57c 100644 --- a/jupyter_server/i18n/__init__.py +++ b/jupyter_server/i18n/__init__.py @@ -59,7 +59,7 @@ def parse_accept_lang_header(accept_lang): def load(language, domain="nbjs"): """Load translations from an nbjs.json file""" try: - f = open(pjoin(I18N_DIR, language, "LC_MESSAGES", "nbjs.json"), encoding="utf-8") # noqa + f = open(pjoin(I18N_DIR, language, "LC_MESSAGES", "nbjs.json"), encoding="utf-8") # noqa: SIM115 except OSError as e: if e.errno != errno.ENOENT: raise diff --git a/jupyter_server/kernelspecs/handlers.py b/jupyter_server/kernelspecs/handlers.py index aa44cb086f..c7cb141459 100644 --- a/jupyter_server/kernelspecs/handlers.py +++ b/jupyter_server/kernelspecs/handlers.py @@ -41,7 +41,7 @@ async def get(self, kernel_name, path, include_body=True): mimetype: str = mimetypes.guess_type(path)[0] or "text/plain" self.set_header("Content-Type", mimetype) self.finish(kernel_spec_res) - return + return None else: self.log.warning( "Kernelspec resource '{}' for '{}' not found. Kernel spec manager may" diff --git a/jupyter_server/log.py b/jupyter_server/log.py index 52eadadea8..705eaaf44c 100644 --- a/jupyter_server/log.py +++ b/jupyter_server/log.py @@ -55,12 +55,12 @@ def log_request(handler): except AttributeError: logger = access_log - if status < 300 or status == 304: # noqa: PLR2004 + if status < 300 or status == 304: # Successes (or 304 FOUND) are debug-level log_method = logger.debug - elif status < 400: # noqa: PLR2004 + elif status < 400: log_method = logger.info - elif status < 500: # noqa: PLR2004 + elif status < 500: log_method = logger.warning else: log_method = logger.error @@ -84,11 +84,11 @@ def log_request(handler): ns["username"] = username msg = "{status} {method} {uri} ({username}@{ip}) {request_time:.2f}ms" - if status >= 400: # noqa: PLR2004 + if status >= 400: # log bad referrers ns["referer"] = _scrub_uri(request.headers.get("Referer", "None")) msg = msg + " referer={referer}" - if status >= 500 and status != 502: # noqa: PLR2004 + if status >= 500 and status != 502: # Log a subset of the headers if it caused an error. headers = {} for header in ["Host", "Accept", "Referer", "User-Agent"]: diff --git a/jupyter_server/nbconvert/handlers.py b/jupyter_server/nbconvert/handlers.py index 4a3f68ff2b..b7a39d0c8b 100644 --- a/jupyter_server/nbconvert/handlers.py +++ b/jupyter_server/nbconvert/handlers.py @@ -139,7 +139,7 @@ async def get(self, format, path): raise web.HTTPError(500, "nbconvert failed: %s" % e) from e if respond_zip(self, name, output, resources): - return + return None # Force download if requested if self.get_argument("download", "false").lower() == "true": diff --git a/jupyter_server/pytest_plugin.py b/jupyter_server/pytest_plugin.py index 505c46e823..f77448f866 100644 --- a/jupyter_server/pytest_plugin.py +++ b/jupyter_server/pytest_plugin.py @@ -2,6 +2,7 @@ # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. import json +from pathlib import Path import pytest @@ -17,8 +18,8 @@ } -@pytest.fixture -def jp_kernelspecs(jp_data_dir): +@pytest.fixture() # type:ignore[misc] +def jp_kernelspecs(jp_data_dir: Path) -> None: # noqa: PT004 """Configures some sample kernelspecs in the Jupyter data directory.""" spec_names = ["sample", "sample2", "bad"] for name in spec_names: @@ -41,7 +42,7 @@ def jp_contents_manager(request, tmp_path): return AsyncFileContentsManager(root_dir=str(tmp_path), use_atomic_writing=request.param) -@pytest.fixture +@pytest.fixture() def jp_large_contents_manager(tmp_path): """Returns an AsyncLargeFileManager instance.""" return AsyncLargeFileManager(root_dir=str(tmp_path)) diff --git a/jupyter_server/serverapp.py b/jupyter_server/serverapp.py index 084ce32604..9e4a57375d 100644 --- a/jupyter_server/serverapp.py +++ b/jupyter_server/serverapp.py @@ -203,7 +203,7 @@ def random_ports(port: int, n: int) -> t.Generator[int, None, None]: for i in range(min(5, n)): yield port + i for _ in range(n - 5): - yield max(1, port + random.randint(-2 * n, 2 * n)) # noqa + yield max(1, port + random.randint(-2 * n, 2 * n)) def load_handlers(name: str) -> t.Any: @@ -314,7 +314,7 @@ def init_settings( jenv_opt: dict[str, t.Any] = {"autoescape": True} jenv_opt.update(jinja_env_options if jinja_env_options else {}) - env = Environment( # noqa: S701 + env = Environment( loader=FileSystemLoader(template_path), extensions=["jinja2.ext.i18n"], **jenv_opt ) sys_info = get_sys_info() @@ -1703,7 +1703,6 @@ def _root_dir_changed(self, change: t.Any) -> None: # record that root_dir is set, # which affects loading of deprecated notebook_dir self._root_dir_set = True - pass preferred_dir = Unicode( config=True, @@ -1934,7 +1933,7 @@ def init_configurables(self) -> None: ) # Trigger a default/validation here explicitly while we still support the # deprecated trait on ServerApp (FIXME remove when deprecation finalized) - self.contents_manager.preferred_dir # noqa + self.contents_manager.preferred_dir # noqa: B018 self.session_manager = self.session_manager_class( parent=self, log=self.log, @@ -2164,7 +2163,7 @@ def _get_urlparts( if not self.ip: ip = "localhost" # Handle nonexplicit hostname. - elif self.ip in ("0.0.0.0", "::"): # noqa + elif self.ip in ("0.0.0.0", "::"): ip = "%s" % socket.gethostname() else: ip = f"[{self.ip}]" if ":" in self.ip else self.ip @@ -2305,7 +2304,6 @@ def _signal_info(self, sig: t.Any, frame: t.Any) -> None: def init_components(self) -> None: """Check the components submodule, and warn if it's unclean""" # TODO: this should still check, but now we use bower, not git submodule - pass def find_server_extensions(self) -> None: """ @@ -2491,14 +2489,13 @@ def _find_http_port(self) -> None: else: self.log.info(_i18n("The port %i is already in use.") % port) continue - elif e.errno in ( + if e.errno in ( errno.EACCES, getattr(errno, "WSAEACCES", errno.EACCES), ): self.log.warning(_i18n("Permission to listen on port %i denied.") % port) continue - else: - raise + raise else: success = True self.port = port @@ -2796,7 +2793,7 @@ def _prepare_browser_open(self) -> tuple[str, t.Optional[str]]: if self.identity_provider.token: uri = url_concat(uri, {"token": self.identity_provider.token}) - if self.file_to_run: # noqa + if self.file_to_run: # noqa: SIM108 # Create a separate, temporary open-browser-file # pointing at a specific file. open_file = self.browser_open_file_to_run diff --git a/jupyter_server/services/contents/fileio.py b/jupyter_server/services/contents/fileio.py index ba84a3733b..5f0aa4a8bf 100644 --- a/jupyter_server/services/contents/fileio.py +++ b/jupyter_server/services/contents/fileio.py @@ -106,9 +106,9 @@ def atomic_writing(path, text=True, encoding="utf-8", log=None, **kwargs): if text: # Make sure that text files have Unix linefeeds by default kwargs.setdefault("newline", "\n") - fileobj = open(path, "w", encoding=encoding, **kwargs) # noqa + fileobj = open(path, "w", encoding=encoding, **kwargs) # noqa: SIM115 else: - fileobj = open(path, "wb", **kwargs) # noqa + fileobj = open(path, "wb", **kwargs) # noqa: SIM115 try: yield fileobj @@ -154,9 +154,9 @@ def _simple_writing(path, text=True, encoding="utf-8", log=None, **kwargs): if text: # Make sure that text files have Unix linefeeds by default kwargs.setdefault("newline", "\n") - fileobj = open(path, "w", encoding=encoding, **kwargs) # noqa + fileobj = open(path, "w", encoding=encoding, **kwargs) # noqa: SIM115 else: - fileobj = open(path, "wb", **kwargs) # noqa + fileobj = open(path, "wb", **kwargs) # noqa: SIM115 try: yield fileobj @@ -359,7 +359,7 @@ def _save_file(self, os_path, content, format): def _get_md5(self, os_path): c, _ = self._read_file(os_path, "byte") - md5 = hashlib.md5() # noqa: S324 + md5 = hashlib.md5() md5.update(c) return md5.hexdigest() @@ -477,6 +477,6 @@ async def _save_file(self, os_path, content, format): async def _get_md5(self, os_path): c, _ = await self._read_file(os_path, "byte") - md5 = hashlib.md5() # noqa: S324 + md5 = hashlib.md5() await run_sync(md5.update, c) return md5.hexdigest() diff --git a/jupyter_server/services/kernels/connection/abc.py b/jupyter_server/services/kernels/connection/abc.py index bc98233a23..71f9e8254f 100644 --- a/jupyter_server/services/kernels/connection/abc.py +++ b/jupyter_server/services/kernels/connection/abc.py @@ -15,19 +15,15 @@ class KernelWebsocketConnectionABC(ABC): @abstractmethod async def connect(self): """Connect the kernel websocket to the kernel ZMQ connections""" - ... @abstractmethod async def disconnect(self): """Disconnect the kernel websocket from the kernel ZMQ connections""" - ... @abstractmethod def handle_incoming_message(self, incoming_msg: str) -> None: """Broker the incoming websocket message to the appropriate ZMQ channel.""" - ... @abstractmethod def handle_outgoing_message(self, stream: str, outgoing_msg: List[Any]) -> None: """Broker outgoing ZMQ messages to the kernel websocket.""" - ... diff --git a/jupyter_server/services/kernels/connection/channels.py b/jupyter_server/services/kernels/connection/channels.py index c103fe456d..05b9f6954e 100644 --- a/jupyter_server/services/kernels/connection/channels.py +++ b/jupyter_server/services/kernels/connection/channels.py @@ -154,7 +154,7 @@ def create_stream(self): self.channels[channel] = stream = meth(identity=identity) stream.channel = channel - def nudge(self): # noqa + def nudge(self): """Nudge the zmq connections with kernel_info_requests Returns a Future that will resolve when we have received a shell or control reply and at least one iopub message, @@ -376,7 +376,7 @@ def replay(value): if not stream.closed(): stream.close() self.disconnect() - return + return None self.multi_kernel_manager.add_restart_callback(self.kernel_id, self.on_kernel_restarted) self.multi_kernel_manager.add_restart_callback( @@ -438,7 +438,7 @@ def disconnect(self): try: ZMQChannelsWebsocketConnection._open_sockets.remove(self) self._close_future.set_result(None) - except Exception: # noqa + except Exception: pass def handle_incoming_message(self, incoming_msg: str) -> None: diff --git a/jupyter_server/services/kernels/kernelmanager.py b/jupyter_server/services/kernels/kernelmanager.py index d0ed803b74..451a279a4e 100644 --- a/jupyter_server/services/kernels/kernelmanager.py +++ b/jupyter_server/services/kernels/kernelmanager.py @@ -373,7 +373,7 @@ def get_buffer(self, kernel_id, session_key): """ self.log.debug("Getting buffer for %s", kernel_id) if kernel_id not in self._kernel_buffers: - return + return None buffer_info = self._kernel_buffers[kernel_id] if buffer_info["session_key"] == session_key: @@ -802,7 +802,7 @@ def core_event_schema_paths(self) -> list[pathlib.Path]: # This trait is intended for subclasses to override and define # custom event schemas. - extra_event_schema_paths: List[str] = List( # type:ignore[assignment] + extra_event_schema_paths: List[str] = List( default_value=[], help=""" A list of pathlib.Path objects pointing at to register with diff --git a/jupyter_server/services/kernels/websocket.py b/jupyter_server/services/kernels/websocket.py index 753ab01e26..7473e2f320 100644 --- a/jupyter_server/services/kernels/websocket.py +++ b/jupyter_server/services/kernels/websocket.py @@ -26,7 +26,6 @@ def set_default_headers(self): which doesn't make sense for websockets """ - pass def get_compression_options(self): """Get the socket connection options.""" diff --git a/jupyter_server/services/sessions/sessionmanager.py b/jupyter_server/services/sessions/sessionmanager.py index 6e4ebd3cac..5f3a19c71d 100644 --- a/jupyter_server/services/sessions/sessionmanager.py +++ b/jupyter_server/services/sessions/sessionmanager.py @@ -31,8 +31,6 @@ class KernelSessionRecordConflict(Exception): merge because of conflicting data. """ - pass - @dataclass class KernelSessionRecord: @@ -410,7 +408,7 @@ async def get_session(self, **kwargs): raise TypeError(msg) conditions.append("%s=?" % column) - query = "SELECT * FROM session WHERE %s" % (" AND ".join(conditions)) # noqa + query = "SELECT * FROM session WHERE %s" % (" AND ".join(conditions)) self.cursor.execute(query, list(kwargs.values())) try: @@ -458,7 +456,7 @@ async def update_session(self, session_id, **kwargs): if column not in self._columns: raise TypeError("No such column: %r" % column) sets.append("%s=?" % column) - query = "UPDATE session SET %s WHERE session_id=?" % (", ".join(sets)) # noqa + query = "UPDATE session SET %s WHERE session_id=?" % (", ".join(sets)) self.cursor.execute(query, [*list(kwargs.values()), session_id]) if hasattr(self.kernel_manager, "update_env"): @@ -492,7 +490,7 @@ async def row_to_model(self, row, tolerate_culled=False): ) if tolerate_culled: self.log.warning(f"{msg} Continuing...") - return + return None raise KeyError(msg) kernel_model = await ensure_async(self.kernel_manager.kernel_model(row["kernel_id"])) diff --git a/jupyter_server/terminal/__init__.py b/jupyter_server/terminal/__init__.py index 641bad66b7..0dd1533c6a 100644 --- a/jupyter_server/terminal/__init__.py +++ b/jupyter_server/terminal/__init__.py @@ -2,9 +2,9 @@ import warnings # Shims -from jupyter_server_terminals import api_handlers # noqa -from jupyter_server_terminals.handlers import TermSocket # noqa -from jupyter_server_terminals.terminalmanager import TerminalManager # noqa +from jupyter_server_terminals import api_handlers +from jupyter_server_terminals.handlers import TermSocket +from jupyter_server_terminals.terminalmanager import TerminalManager warnings.warn( "Terminals support has moved to `jupyter_server_terminals`", diff --git a/jupyter_server/terminal/handlers.py b/jupyter_server/terminal/handlers.py index 23e19ee355..f3da8aa91c 100644 --- a/jupyter_server/terminal/handlers.py +++ b/jupyter_server/terminal/handlers.py @@ -1,4 +1,4 @@ """Tornado handlers for the terminal emulator.""" # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. -from jupyter_server_terminals.handlers import TermSocket # noqa +from jupyter_server_terminals.handlers import TermSocket diff --git a/jupyter_server/terminal/terminalmanager.py b/jupyter_server/terminal/terminalmanager.py index d2bef723f8..6f9ada1b71 100644 --- a/jupyter_server/terminal/terminalmanager.py +++ b/jupyter_server/terminal/terminalmanager.py @@ -4,4 +4,4 @@ """ # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. -from jupyter_server_terminals.terminalmanager import TerminalManager # noqa +from jupyter_server_terminals.terminalmanager import TerminalManager diff --git a/jupyter_server/traittypes.py b/jupyter_server/traittypes.py index cfa3a8720e..f17f3a0a24 100644 --- a/jupyter_server/traittypes.py +++ b/jupyter_server/traittypes.py @@ -76,7 +76,7 @@ def validate(self, obj, value): try: if self.subclass_from_klasses(value): return value - except Exception: # noqa + except Exception: pass self.error(obj, value) @@ -86,10 +86,10 @@ def info(self): result = "a subclass of " for klass in self.klasses: if not isinstance(klass, str): - klass = klass.__module__ + "." + klass.__name__ # noqa + klass = klass.__module__ + "." + klass.__name__ # noqa: PLW2901 result += f"{klass} or " # Strip the last "or" - result = result.strip(" or ") # noqa + result = result.strip(" or ") # noqa: B005 if self.allow_none: return result + " or None" return result @@ -106,9 +106,9 @@ def _resolve_classes(self): if isinstance(klass, str): # Try importing the classes to compare. Silently, ignore if not importable. try: - klass = self._resolve_string(klass) # noqa + klass = self._resolve_string(klass) # noqa: PLW2901 self.importable_klasses.append(klass) - except Exception: # noqa + except Exception: pass else: self.importable_klasses.append(klass) @@ -156,7 +156,7 @@ class or its subclasses. Our implementation is quite different None, the None is replaced by ``()`` or ``{}``, respectively. """ # If class - if klasses is None: # noqa + if klasses is None: # noqa: SIM114 self.klasses = klasses # Verify all elements are either classes or strings. elif all(inspect.isclass(k) or isinstance(k, str) for k in klasses): @@ -200,7 +200,7 @@ def info(self): else: result += describe("a", klass) result += " or " - result = result.strip(" or ") # noqa + result = result.strip(" or ") # noqa: B005 if self.allow_none: result += " or None" return result @@ -218,9 +218,9 @@ def _resolve_classes(self): if isinstance(klass, str): # Try importing the classes to compare. Silently, ignore if not importable. try: - klass = self._resolve_string(klass) # noqa + klass = self._resolve_string(klass) # noqa: PLW2901 self.importable_klasses.append(klass) - except Exception: # noqa + except Exception: pass else: self.importable_klasses.append(klass) diff --git a/jupyter_server/utils.py b/jupyter_server/utils.py index 1ff4979cc3..2a4c185d97 100644 --- a/jupyter_server/utils.py +++ b/jupyter_server/utils.py @@ -382,7 +382,7 @@ def filefind(filename: str, path_dirs: Sequence[str] | str | None = None) -> str for path in path_dirs: if path == ".": - path = os.getcwd() # noqa + path = os.getcwd() # noqa: PLW2901 testname = expand_path(os.path.join(path, filename)) if os.path.isfile(testname): return os.path.abspath(testname) @@ -427,7 +427,7 @@ def import_item(name: str) -> Any: """ parts = name.rsplit(".", 1) - if len(parts) == 2: # noqa + if len(parts) == 2: # called with 'foo.bar....' package, obj = parts module = __import__(package, fromlist=[obj]) diff --git a/pyproject.toml b/pyproject.toml index 1d82870cb2..68b10b04d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -144,66 +144,45 @@ line-length = 100 [tool.ruff.lint] select = [ - "A", - "B", - "C", - "DTZ", - "E", - "EM", - "F", - "FBT", - "I", - "ICN", - "N", - "PLC", - "PLE", - "PLR", - "PLW", - "Q", - "RUF", - "S", - "SIM", - "T", - "TID", - "UP", - "W", - "YTT", + "B", # flake8-bugbear + "I", # isort + "C4", # flake8-comprehensions + "EM", # flake8-errmsg + "ICN", # flake8-import-conventions + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PL", # pylint + "PT", # flake8-pytest-style + "RET", # flake8-return + "RUF", # Ruff-specific + "SIM", # flake8-simplify + "T20", # flake8-print + "UP", # pyupgrade + "YTT", # flake8-2020 + "EXE", # flake8-executable + "NPY", # NumPy specific rules + "PD", # pandas-vet + "PYI", # flake8-pyi ] ignore = [ # Allow non-abstract empty methods in abstract base classes "B027", - # Ignore McCabe complexity - "C901", - # Allow boolean positional values in function calls, like `dict.get(... True)` - "FBT003", # Use of `assert` detected "S101", - # Line too long - "E501", - # Relative imports are banned - "TID252", - # Boolean ... in function definition - "FBT001", - "FBT002", - # Module level import not at top of file - "E402", - # A001/A002/A003 .. is shadowing a python builtin - "A001", - "A002", - "A003", + # Use `contextlib.suppress(SchemaRegistryException)` instead of `try`-`except`-`pass` + "SIM105", + # Missing explicit `return` at the end of function able to return non-`None` value + "RET503" , + # Unnecessary assignment to + "RET504", + # Unnecessary `else` after `return` statement + "RET505", + # Unnecessary `elif` after `raise` statement + "RET506", # Possible hardcoded password "S105", "S106", - # Variable `xxx` in function should be lowercase - "N806", - # Exception name `KernelSessionRecordConflict` should be named with an Error suffix - "N818", - # SIM105 Use `contextlib.suppress(...)` - "SIM105", - # PLR0913 Too many arguments to function call - "PLR0913", - # PLR0912 Too many branches - "PLR0912", + "PLR", # Design related pylint codes # RUF012 Mutable class attributes should be annotated with `typing.ClassVar` "RUF012", # Use `X | Y` for type annotations @@ -222,13 +201,10 @@ unfixable = [ # B011 Do not call assert False since python -O removes these calls # F841 local variable 'foo' is assigned to but never used # C408 Unnecessary `dict` call -# E402 Module level import not at top of file -# T201 `print` found -# EM101 Exception must not use a string literal -# PLR2004 Magic value used in comparison # S108 Probable insecure usage of temporary file or directory # PLC1901 `ext_pkg.version == ""` can be simplified to `not ext_pkg.version` as an empty string is falsey -"tests/*" = ["B011", "F841", "C408", "E402", "T201", "EM101", "EM102", "EM103", "PLR2004", "S108", "PLC1901"] +# B018 Found useless expression +"tests/*" = ["B011", "F841", "EM", "C", "T201", "S108", "PLC1901", "PTH", "ARG", "PT", "RET", "G", "PLW", "B018"] # print should be used in applications "**/*app.py" = ["T201"] # Ignore flake 8 errors from shimmed imports @@ -300,7 +276,6 @@ python_version = "3.8" explicit_package_bases = true strict = true pretty = true -show_error_codes = true warn_unreachable = true disable_error_code = ["no-untyped-def", "no-untyped-call"] enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] @@ -319,4 +294,4 @@ exclude = ["docs", "test"] ignore = ["W002"] [tool.repo-review] -ignore = ["PY007", "GH102"] +ignore = ["GH102"] diff --git a/tests/auth/test_authorizer.py b/tests/auth/test_authorizer.py index 86e2184307..08c49eadf0 100644 --- a/tests/auth/test_authorizer.py +++ b/tests/auth/test_authorizer.py @@ -10,7 +10,7 @@ from jupyter_server.services.security import csp_report_uri -@pytest.fixture +@pytest.fixture() def jp_server_config(jp_server_authorizer): return { "ServerApp": {"authorizer_class": jp_server_authorizer}, @@ -18,7 +18,7 @@ def jp_server_config(jp_server_authorizer): } -@pytest.fixture +@pytest.fixture() def jp_server_auth_resources(jp_server_auth_core_resources): # terminal plugin doesn't have importable url patterns # get these from terminal/__init__.py diff --git a/tests/auth/test_identity.py b/tests/auth/test_identity.py index 9c4010f445..6f3af07060 100644 --- a/tests/auth/test_identity.py +++ b/tests/auth/test_identity.py @@ -113,7 +113,7 @@ def test_user_defaults(fields, expected): assert value is None or isinstance(value, str) -@pytest.fixture +@pytest.fixture() def identity_provider_class(): """Allow override in other test modules""" return PasswordIdentityProvider diff --git a/tests/auth/test_legacy_login.py b/tests/auth/test_legacy_login.py index ba49ff53f2..be139fe707 100644 --- a/tests/auth/test_legacy_login.py +++ b/tests/auth/test_legacy_login.py @@ -13,8 +13,8 @@ from jupyter_server.serverapp import ServerApp # re-run some login tests with legacy login config -from .test_identity import test_password_required, test_validate_security # noqa -from .test_login import login, test_change_password, test_login_cookie, test_logout # noqa +from .test_identity import test_password_required, test_validate_security +from .test_login import login, test_change_password, test_login_cookie, test_logout # Don't raise on deprecation warnings in this module testing deprecated behavior pytestmark = pytest.mark.filterwarnings("ignore::DeprecationWarning") @@ -32,19 +32,19 @@ def get_user(cls, handler): return None -@pytest.fixture +@pytest.fixture() def login_headers(): return {"test-user": "super"} -@pytest.fixture +@pytest.fixture() def jp_server_config(): cfg = Config() cfg.ServerApp.login_handler_class = CustomLoginHandler return cfg -@pytest.fixture +@pytest.fixture() def identity_provider_class(): # for tests imported from test_identity.py return LegacyIdentityProvider diff --git a/tests/auth/test_login.py b/tests/auth/test_login.py index d91ec5ad06..7aad3129ca 100644 --- a/tests/auth/test_login.py +++ b/tests/auth/test_login.py @@ -11,12 +11,12 @@ # override default config to ensure a non-empty base url is used -@pytest.fixture +@pytest.fixture() def jp_base_url(): return "/a%40b/" -@pytest.fixture +@pytest.fixture() def jp_server_config(jp_base_url): return { "ServerApp": { @@ -66,7 +66,7 @@ async def _login( return resp -@pytest.fixture +@pytest.fixture() def login_headers(): """Extra headers to pass to login @@ -75,13 +75,13 @@ def login_headers(): return {} -@pytest.fixture +@pytest.fixture() def login(jp_serverapp, http_server_client, jp_base_url, login_headers): """Fixture to return a function to login to a Jupyter server by submitting the login page form """ - yield partial(_login, jp_serverapp, http_server_client, jp_base_url, login_headers) + return partial(_login, jp_serverapp, http_server_client, jp_base_url, login_headers) @pytest.mark.parametrize( diff --git a/tests/base/test_websocket.py b/tests/base/test_websocket.py index 22751c059a..ee6ee3ee62 100644 --- a/tests/base/test_websocket.py +++ b/tests/base/test_websocket.py @@ -18,7 +18,7 @@ class MockHandler(WebSocketMixin, WebSocketHandler): log = logging.getLogger() -@pytest.fixture +@pytest.fixture() def mixin(jp_serverapp): app: ServerApp = jp_serverapp headers = HTTPHeaders({"Host": "foo"}) diff --git a/tests/conftest.py b/tests/conftest.py index 9b43e0f532..f50aa797db 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -62,18 +62,18 @@ def pytest_runtest_setup(item): """ -@pytest.fixture +@pytest.fixture() def mock_template(jp_template_dir): index = jp_template_dir.joinpath("index.html") index.write_text(mock_html) -@pytest.fixture +@pytest.fixture() def extension_manager(jp_serverapp): return jp_serverapp.extension_manager -@pytest.fixture +@pytest.fixture() def config_file(jp_config_dir): """""" f = jp_config_dir.joinpath("jupyter_mockextension_config.py") @@ -87,7 +87,7 @@ def jp_mockextension_cleanup(): MockExtensionApp.clear_instance() -@pytest.fixture +@pytest.fixture() def contents_dir(tmp_path, jp_serverapp): return tmp_path / jp_serverapp.root_dir @@ -108,7 +108,7 @@ def contents_dir(tmp_path, jp_serverapp): ] -@pytest.fixture +@pytest.fixture() def contents(contents_dir): # Create files in temporary directory paths: dict = {"notebooks": [], "textfiles": [], "blobs": [], "contents_dir": contents_dir} @@ -137,6 +137,6 @@ def contents(contents_dir): return paths -@pytest.fixture +@pytest.fixture() def folders(): return list({item[0] for item in dirs}) diff --git a/tests/extension/mockextensions/app.py b/tests/extension/mockextensions/app.py index 0cd45b26d3..26f38464cd 100644 --- a/tests/extension/mockextensions/app.py +++ b/tests/extension/mockextensions/app.py @@ -65,7 +65,6 @@ def initialize_settings(self): elogger.register_event_schema(EVENT_SCHEMA) except SchemaRegistryException as err: self.log.error(err) - pass def initialize_handlers(self): self.handlers.append(("/mock", MockExtensionHandler)) diff --git a/tests/extension/test_app.py b/tests/extension/test_app.py index 6c7af22fe2..de52924df3 100644 --- a/tests/extension/test_app.py +++ b/tests/extension/test_app.py @@ -11,7 +11,7 @@ from .mockextensions.app import MockExtensionApp -@pytest.fixture +@pytest.fixture() def jp_server_config(jp_template_dir): config = { "ServerApp": { @@ -25,7 +25,7 @@ def jp_server_config(jp_template_dir): return config -@pytest.fixture +@pytest.fixture() def mock_extension(extension_manager): name = "tests.extension.mockextensions" pkg = extension_manager.extensions[name] diff --git a/tests/extension/test_config.py b/tests/extension/test_config.py index 5667c3efae..ac7a75aedc 100644 --- a/tests/extension/test_config.py +++ b/tests/extension/test_config.py @@ -9,7 +9,7 @@ pytestmark = pytest.mark.usefixtures("jp_environ") -@pytest.fixture +@pytest.fixture() def configd(jp_env_config_path): """A pathlib.Path object that acts like a jupyter_server_config.d folder.""" configd = jp_env_config_path.joinpath("jupyter_server_config.d") @@ -28,7 +28,7 @@ def configd(jp_env_config_path): """ -@pytest.fixture +@pytest.fixture() def ext1_config(configd): config = configd.joinpath("ext1_config.json") config.write_text(ext1_json_config) @@ -45,7 +45,7 @@ def ext1_config(configd): """ -@pytest.fixture +@pytest.fixture() def ext2_config(configd): config = configd.joinpath("ext2_config.json") config.write_text(ext2_json_config) diff --git a/tests/extension/test_handler.py b/tests/extension/test_handler.py index 870e311b4a..3151cf2b4d 100644 --- a/tests/extension/test_handler.py +++ b/tests/extension/test_handler.py @@ -1,7 +1,7 @@ import pytest -@pytest.fixture +@pytest.fixture() def jp_server_config(jp_template_dir): return { "ServerApp": {"jpserver_extensions": {"tests.extension.mockextensions": True}}, diff --git a/tests/extension/test_launch.py b/tests/extension/test_launch.py index 86336efe21..de94607187 100644 --- a/tests/extension/test_launch.py +++ b/tests/extension/test_launch.py @@ -14,17 +14,17 @@ HERE = os.path.dirname(os.path.abspath(__file__)) -@pytest.fixture +@pytest.fixture() def port(): return 9999 -@pytest.fixture +@pytest.fixture() def token(): return hexlify(os.urandom(4)).decode("ascii") -@pytest.fixture +@pytest.fixture() def auth_header(token): return {"Authorization": "token %s" % token} @@ -32,7 +32,7 @@ def auth_header(token): def wait_up(url, interval=0.1, check=None): while True: try: - r = requests.get(url) # noqa + r = requests.get(url) except Exception: if check: assert check() @@ -42,7 +42,7 @@ def wait_up(url, interval=0.1, check=None): break -@pytest.fixture +@pytest.fixture() def launch_instance(request, port, token): def _run_in_subprocess(argv=None, add_token=True): argv = argv or [] @@ -66,7 +66,7 @@ def _kill_extension_app(): root = Path(HERE).parent.parent process = subprocess.Popen( - [ # noqa + [ sys.executable, "-m", "tests.extension.mockextensions.app", @@ -86,11 +86,11 @@ def _kill_extension_app(): return _run_in_subprocess -@pytest.fixture +@pytest.fixture() def fetch(port, auth_header): def _get(endpoint): url = f"http://127.0.0.1:{port}" + endpoint - return requests.get(url, headers=auth_header) # noqa + return requests.get(url, headers=auth_header) return _get diff --git a/tests/extension/test_manager.py b/tests/extension/test_manager.py index 9ca42d95ef..6e48b65df4 100644 --- a/tests/extension/test_manager.py +++ b/tests/extension/test_manager.py @@ -62,7 +62,7 @@ def test_extension_package_api(): app = path1["app"] e = ExtensionPackage(name="tests.extension.mockextensions", enabled=True) - e.extension_points # noqa + e.extension_points assert hasattr(e, "extension_points") assert len(e.extension_points) == len(metadata_list) assert app.name in e.extension_points diff --git a/tests/nbconvert/test_handlers.py b/tests/nbconvert/test_handlers.py index f14fde35a2..1805902e76 100644 --- a/tests/nbconvert/test_handlers.py +++ b/tests/nbconvert/test_handlers.py @@ -16,7 +16,7 @@ ).decode("ascii") -@pytest.fixture +@pytest.fixture() def notebook(jp_root_dir): # Build sub directory. subdir = jp_root_dir / "foo" diff --git a/tests/services/api/test_api.py b/tests/services/api/test_api.py index 900280f67d..f013dcfcd8 100644 --- a/tests/services/api/test_api.py +++ b/tests/services/api/test_api.py @@ -59,7 +59,7 @@ def is_authorized(self, handler, user, action, resource): return action in actions -@pytest.fixture +@pytest.fixture() def identity_provider(jp_serverapp): idp = MockIdentityProvider(parent=jp_serverapp) authorizer = MockAuthorizer(parent=jp_serverapp) diff --git a/tests/services/contents/test_api.py b/tests/services/contents/test_api.py index 8733b02ae7..eb93fd7526 100644 --- a/tests/services/contents/test_api.py +++ b/tests/services/contents/test_api.py @@ -295,7 +295,7 @@ async def test_get_bad_type(jp_fetch, contents): assert expected_http_error(e, 400, "%s is not a directory" % path) -@pytest.fixture +@pytest.fixture() def _check_created(jp_base_url): def _inner(r, contents_dir, path, name, type="notebook"): fpath = path + "/" + name diff --git a/tests/services/contents/test_fileio.py b/tests/services/contents/test_fileio.py index a72acfa429..19060db94a 100644 --- a/tests/services/contents/test_fileio.py +++ b/tests/services/contents/test_fileio.py @@ -67,9 +67,9 @@ class CustomExc(Exception): assert f.read() == "written from symlink" -@pytest.fixture +@pytest.fixture() def handle_umask(): - global umask # noqa + global umask umask = os.umask(0) os.umask(umask) yield diff --git a/tests/services/contents/test_manager.py b/tests/services/contents/test_manager.py index 6e3ac01945..3d11a43ad0 100644 --- a/tests/services/contents/test_manager.py +++ b/tests/services/contents/test_manager.py @@ -296,7 +296,7 @@ async def test_403(jp_file_contents_manager_class, tmp_path): assert e.status_code == 403 -async def test_400(jp_file_contents_manager_class, tmp_path): # noqa +async def test_400(jp_file_contents_manager_class, tmp_path): # Test Delete behavior # Test delete of file in hidden directory td = str(tmp_path) @@ -547,7 +547,7 @@ async def test_modified_date(jp_contents_manager): assert renamed["last_modified"] >= saved["last_modified"] -async def test_get(jp_contents_manager): # noqa +async def test_get(jp_contents_manager): cm = jp_contents_manager # Create a notebook model = await ensure_async(cm.new_untitled(type="notebook")) diff --git a/tests/services/events/mockextension/__init__.py b/tests/services/events/mockextension/__init__.py index b19cb18a2e..ed7c0e9d37 100644 --- a/tests/services/events/mockextension/__init__.py +++ b/tests/services/events/mockextension/__init__.py @@ -1,4 +1,4 @@ -from .mock_extension import _load_jupyter_server_extension # noqa: F401 +from .mock_extension import _load_jupyter_server_extension # Function that makes these extensions discoverable # by the test functions. diff --git a/tests/services/events/test_api.py b/tests/services/events/test_api.py index 5311f0860b..d84b112240 100644 --- a/tests/services/events/test_api.py +++ b/tests/services/events/test_api.py @@ -9,7 +9,7 @@ from tests.utils import expected_http_error -@pytest.fixture +@pytest.fixture() def event_logger_sink(jp_serverapp): event_logger = jp_serverapp.event_logger # Register the event schema defined in this directory. @@ -21,7 +21,7 @@ def event_logger_sink(jp_serverapp): return event_logger, sink -@pytest.fixture +@pytest.fixture() def event_logger(event_logger_sink): event_logger, sink = event_logger_sink return event_logger diff --git a/tests/services/events/test_extension.py b/tests/services/events/test_extension.py index fafb34497f..c20e6f79b6 100644 --- a/tests/services/events/test_extension.py +++ b/tests/services/events/test_extension.py @@ -3,7 +3,7 @@ import pytest -@pytest.fixture +@pytest.fixture() def jp_server_config(): config = { "ServerApp": { diff --git a/tests/services/kernels/test_api.py b/tests/services/kernels/test_api.py index 60009e8978..c1b98e7269 100644 --- a/tests/services/kernels/test_api.py +++ b/tests/services/kernels/test_api.py @@ -29,7 +29,7 @@ def suppress_deprecation_warnings(): yield -@pytest.fixture +@pytest.fixture() def pending_kernel_is_ready(jp_serverapp): async def _(kernel_id, ready=None): km = jp_serverapp.kernel_manager diff --git a/tests/services/kernels/test_config.py b/tests/services/kernels/test_config.py index 1db2e11b1f..8f779bb1dd 100644 --- a/tests/services/kernels/test_config.py +++ b/tests/services/kernels/test_config.py @@ -4,7 +4,7 @@ from jupyter_server.services.kernels.kernelmanager import AsyncMappingKernelManager -@pytest.fixture +@pytest.fixture() def jp_server_config(): return Config( {"ServerApp": {"MappingKernelManager": {"allowed_message_types": ["kernel_info_request"]}}} diff --git a/tests/services/kernels/test_cull.py b/tests/services/kernels/test_cull.py index 50ecbf2b96..f370f9c5ef 100644 --- a/tests/services/kernels/test_cull.py +++ b/tests/services/kernels/test_cull.py @@ -30,7 +30,7 @@ def suppress_deprecation_warnings(): yield -@pytest.fixture +@pytest.fixture() def jp_kernelspec_with_metadata(jp_data_dir): """Configures some sample kernelspecs in the Jupyter data directory.""" kenrel_spec_name = "sample_with_metadata" diff --git a/tests/services/kernels/test_events.py b/tests/services/kernels/test_events.py index de2e976b1b..9bdfb03672 100644 --- a/tests/services/kernels/test_events.py +++ b/tests/services/kernels/test_events.py @@ -16,7 +16,6 @@ async def test_kernel_action_success_event( async def mock_method(self, *args, **kwargs): self.kernel_id = "x-x-x-x-x" - ... monkeypatch.setattr(AsyncKernelManager, f"{action}_kernel", mock_method) @@ -42,7 +41,7 @@ async def mock_method(self, *args, **kwargs): monkeypatch.setattr(AsyncKernelManager, f"{action}_kernel", mock_method) - with pytest.raises(Exception): # noqa + with pytest.raises(Exception): # noqa: B017 await getattr(manager, f"{action}_kernel")() output = jp_read_emitted_events()[0] diff --git a/tests/services/sessions/test_api.py b/tests/services/sessions/test_api.py index 8c46de9470..a4aa0a73e5 100644 --- a/tests/services/sessions/test_api.py +++ b/tests/services/sessions/test_api.py @@ -164,7 +164,7 @@ async def cleanup(self): time.sleep(0.1) -@pytest.fixture +@pytest.fixture() def session_is_ready(jp_serverapp): """Wait for the kernel started by a session to be ready. @@ -187,7 +187,7 @@ async def _(session_id): return _ -@pytest.fixture +@pytest.fixture() def session_client(jp_root_dir, jp_fetch): subdir = jp_root_dir.joinpath("foo") subdir.mkdir() diff --git a/tests/services/sessions/test_manager.py b/tests/services/sessions/test_manager.py index bd092259e0..9af04f2268 100644 --- a/tests/services/sessions/test_manager.py +++ b/tests/services/sessions/test_manager.py @@ -66,7 +66,7 @@ async def shutdown_kernel(self, kernel_id, now=False): await super().shutdown_kernel(kernel_id, now=now) -@pytest.fixture +@pytest.fixture() def session_manager(): return SessionManager(kernel_manager=MockMKM(), contents_manager=ContentsManager()) diff --git a/tests/test_gateway.py b/tests/test_gateway.py index c178c7f801..585650e2f0 100644 --- a/tests/test_gateway.py +++ b/tests/test_gateway.py @@ -90,7 +90,7 @@ def generate_model(name): return model -async def mock_gateway_request(url, **kwargs): # noqa +async def mock_gateway_request(url, **kwargs): method = "GET" if kwargs["method"]: method = kwargs["method"] @@ -234,7 +234,7 @@ def jp_server_config(): ) -@pytest.fixture +@pytest.fixture() def init_gateway(monkeypatch): """Initializes the server for use as a gateway client.""" # Clear the singleton first since previous tests may not have used a gateway. diff --git a/tests/test_serverapp.py b/tests/test_serverapp.py index 9ea6e569f7..df703f550c 100644 --- a/tests/test_serverapp.py +++ b/tests/test_serverapp.py @@ -168,7 +168,7 @@ def test_list_running_servers(jp_serverapp, jp_web_app): assert len(servers) >= 1 -@pytest.fixture +@pytest.fixture() def prefix_path(jp_root_dir, tmp_path): """If a given path is prefixed with the literal strings `/jp_root_dir` or `/tmp_path`, replace those diff --git a/tests/test_terminal.py b/tests/test_terminal.py index 5e45b01dee..27fd69fadb 100644 --- a/tests/test_terminal.py +++ b/tests/test_terminal.py @@ -14,7 +14,7 @@ from jupyter_server._tz import isoformat -@pytest.fixture +@pytest.fixture() def terminal_path(tmp_path): subdir = tmp_path.joinpath("terminal_path") subdir.mkdir() @@ -24,7 +24,7 @@ def terminal_path(tmp_path): shutil.rmtree(str(subdir), ignore_errors=True) -@pytest.fixture +@pytest.fixture() def terminal_root_dir(jp_root_dir): subdir = jp_root_dir.joinpath("terminal_path") subdir.mkdir() @@ -38,7 +38,7 @@ def terminal_root_dir(jp_root_dir): CULL_INTERVAL = 3 -@pytest.fixture +@pytest.fixture() def jp_server_config(): return Config( { @@ -52,7 +52,7 @@ def jp_server_config(): ) -@pytest.fixture +@pytest.fixture() def jp_argv(): """Allows tests to setup specific argv values.""" return ["--ServerApp.jpserver_extensions", "jupyter_server_terminals=True"] @@ -300,7 +300,7 @@ def test_shell_command_override( def test_importing_shims(): with warnings.catch_warnings(): warnings.simplefilter("ignore") - from jupyter_server.terminal import initialize # noqa - from jupyter_server.terminal.api_handlers import TerminalRootHandler # noqa - from jupyter_server.terminal.handlers import TermSocket # noqa - from jupyter_server.terminal.terminalmanager import TerminalManager # noqa + from jupyter_server.terminal import initialize + from jupyter_server.terminal.api_handlers import TerminalRootHandler + from jupyter_server.terminal.handlers import TermSocket + from jupyter_server.terminal.terminalmanager import TerminalManager diff --git a/tests/test_utils.py b/tests/test_utils.py index 5a3f33138b..83d2a1d926 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -99,7 +99,7 @@ def test_check_version(): def test_check_pid(): - proc = subprocess.Popen([sys.executable]) # noqa + proc = subprocess.Popen([sys.executable]) proc.kill() proc.wait() check_pid(proc.pid) diff --git a/tests/unix_sockets/conftest.py b/tests/unix_sockets/conftest.py index c9324d226d..6eb4daa03a 100644 --- a/tests/unix_sockets/conftest.py +++ b/tests/unix_sockets/conftest.py @@ -6,13 +6,13 @@ from jupyter_server import DEFAULT_JUPYTER_SERVER_PORT -@pytest.fixture +@pytest.fixture() def jp_process_id(): """Choose a random unused process ID.""" return os.getpid() -@pytest.fixture +@pytest.fixture() def jp_unix_socket_file(jp_process_id): """Define a temporary socket connection""" # Rely on `/tmp` to avoid any Linux socket length max buffer @@ -26,7 +26,7 @@ def jp_unix_socket_file(jp_process_id): jp_unix_socket_file.unlink() -@pytest.fixture +@pytest.fixture() def jp_http_port(): """Set the port to the default value, since sock and port cannot both be configured at the same time. diff --git a/tests/unix_sockets/test_api.py b/tests/unix_sockets/test_api.py index 85714f4a52..fb6358860f 100644 --- a/tests/unix_sockets/test_api.py +++ b/tests/unix_sockets/test_api.py @@ -16,13 +16,13 @@ from jupyter_server.utils import async_fetch, url_path_join, urlencode_unix_socket -@pytest.fixture +@pytest.fixture() def jp_server_config(jp_unix_socket_file): """Configure the serverapp fixture with the unix socket.""" return {"ServerApp": {"sock": jp_unix_socket_file, "allow_remote_access": True}} -@pytest.fixture +@pytest.fixture() def http_server_port(jp_unix_socket_file, jp_process_id): """Unix socket and process ID used by tornado's HTTP Server. @@ -32,7 +32,7 @@ def http_server_port(jp_unix_socket_file, jp_process_id): return (bind_unix_socket(jp_unix_socket_file), jp_process_id) -@pytest.fixture +@pytest.fixture() def jp_unix_socket_fetch(jp_unix_socket_file, jp_auth_header, jp_base_url, http_server, io_loop): """A fetch fixture for Jupyter Server tests that use the unix_serverapp fixture""" diff --git a/tests/unix_sockets/test_serverapp_integration.py b/tests/unix_sockets/test_serverapp_integration.py index f60c99b1bc..392fd7a61a 100644 --- a/tests/unix_sockets/test_serverapp_integration.py +++ b/tests/unix_sockets/test_serverapp_integration.py @@ -42,7 +42,7 @@ def _cleanup_process(proc): fid.close() -@pytest.mark.integration_test +@pytest.mark.integration_test() def test_shutdown_sock_server_integration(jp_unix_socket_file): url = urlencode_unix_socket(jp_unix_socket_file).encode() encoded_sock_path = urlencode_unix_socket_path(jp_unix_socket_file) @@ -89,7 +89,7 @@ def test_shutdown_sock_server_integration(jp_unix_socket_file): _cleanup_process(p) -@pytest.mark.integration_test +@pytest.mark.integration_test() def test_sock_server_validate_sockmode_type(): try: _check_output(["jupyter-server", "--sock=/tmp/nonexistent", "--sock-mode=badbadbad"]) @@ -99,7 +99,7 @@ def test_sock_server_validate_sockmode_type(): raise AssertionError("expected execution to fail due to validation of --sock-mode param") -@pytest.mark.integration_test +@pytest.mark.integration_test() def test_sock_server_validate_sockmode_accessible(): try: _check_output( @@ -120,7 +120,7 @@ def _ensure_stopped(check_msg="There are no running servers"): raise AssertionError("expected all servers to be stopped") -@pytest.mark.integration_test +@pytest.mark.integration_test() def test_stop_multi_integration(jp_unix_socket_file, jp_http_port): """Tests lifecycle behavior for mixed-mode server types w/ default ports. @@ -158,7 +158,7 @@ def test_stop_multi_integration(jp_unix_socket_file, jp_http_port): [_cleanup_process(p) for p in [p1, p2, p3]] -@pytest.mark.integration_test +@pytest.mark.integration_test() def test_launch_socket_collision(jp_unix_socket_file): """Tests UNIX socket in-use detection for lifecycle correctness.""" sock = jp_unix_socket_file @@ -189,7 +189,7 @@ def test_launch_socket_collision(jp_unix_socket_file): _cleanup_process(p1) -@pytest.mark.integration_test +@pytest.mark.integration_test() def test_shutdown_server(jp_environ): # Start a server in another process # Stop that server @@ -214,7 +214,7 @@ def test_shutdown_server(jp_environ): _cleanup_process(p) -@pytest.mark.integration_test +@pytest.mark.integration_test() def test_jupyter_server_apps(jp_environ): # Start a server in another process # Stop that server