From 048f16fd8e4ae0cd8ed434fe43ad1b111ea05be2 Mon Sep 17 00:00:00 2001 From: M Bussonnier Date: Tue, 17 Dec 2024 16:30:25 +0100 Subject: [PATCH 1/8] Handle newer jupyter_events wants string version --- .../event_schemas/contents_service/v1.yaml | 2 +- .../event_schemas/gateway_client/v1.yaml | 2 +- .../event_schemas/kernel_actions/v1.yaml | 2 +- jupyter_server/services/events/handlers.py | 4 ++-- pyproject.toml | 2 +- tests/services/events/mock_event.yaml | 2 +- tests/services/events/test_api.py | 14 +++++++------- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/jupyter_server/event_schemas/contents_service/v1.yaml b/jupyter_server/event_schemas/contents_service/v1.yaml index a787f9b2b0..d049005e01 100644 --- a/jupyter_server/event_schemas/contents_service/v1.yaml +++ b/jupyter_server/event_schemas/contents_service/v1.yaml @@ -1,5 +1,5 @@ "$id": https://events.jupyter.org/jupyter_server/contents_service/v1 -version: 1 +version: "1" title: Contents Manager activities personal-data: true description: | diff --git a/jupyter_server/event_schemas/gateway_client/v1.yaml b/jupyter_server/event_schemas/gateway_client/v1.yaml index 0a35d2464d..0257ce071a 100644 --- a/jupyter_server/event_schemas/gateway_client/v1.yaml +++ b/jupyter_server/event_schemas/gateway_client/v1.yaml @@ -1,5 +1,5 @@ "$id": https://events.jupyter.org/jupyter_server/gateway_client/v1 -version: 1 +version: "1" title: Gateway Client activities. personal-data: true description: | diff --git a/jupyter_server/event_schemas/kernel_actions/v1.yaml b/jupyter_server/event_schemas/kernel_actions/v1.yaml index e0375e5aaa..66c13802c2 100644 --- a/jupyter_server/event_schemas/kernel_actions/v1.yaml +++ b/jupyter_server/event_schemas/kernel_actions/v1.yaml @@ -1,5 +1,5 @@ "$id": https://events.jupyter.org/jupyter_server/kernel_actions/v1 -version: 1 +version: "1" title: Kernel Manager activities personal-data: true description: | diff --git a/jupyter_server/services/events/handlers.py b/jupyter_server/services/events/handlers.py index 41e3d0d53f..fc899c8771 100644 --- a/jupyter_server/services/events/handlers.py +++ b/jupyter_server/services/events/handlers.py @@ -86,9 +86,9 @@ def validate_model( # jupyter_events raises a useful error, so there's no need to # handle that case here. schema = registry.get(schema_id) - version = int(cast(int, data.get("version"))) + version = str(cast(str, data.get("version"))) if schema.version != version: - message = f"Unregistered version: {version}≠{schema.version} for `{schema_id}`" + message = f"Unregistered version: {version!r}≠{schema.version!r} for `{schema_id}`" raise Exception(message) diff --git a/pyproject.toml b/pyproject.toml index 9bfcc74eef..4f83091f25 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ dependencies = [ "tornado>=6.2.0", "traitlets>=5.6.0", "websocket-client>=1.7", - "jupyter_events>=0.9.0", + "jupyter_events>=0.11.0", "overrides>=5.0" ] diff --git a/tests/services/events/mock_event.yaml b/tests/services/events/mock_event.yaml index dabaa23db5..bf73915fb0 100644 --- a/tests/services/events/mock_event.yaml +++ b/tests/services/events/mock_event.yaml @@ -1,5 +1,5 @@ $id: http://event.mock.jupyter.org/message -version: 1 +version: "1" title: Message description: | Emit a message diff --git a/tests/services/events/test_api.py b/tests/services/events/test_api.py index 49599e8380..40ad8b137b 100644 --- a/tests/services/events/test_api.py +++ b/tests/services/events/test_api.py @@ -45,7 +45,7 @@ async def test_subscribe_websocket(event_logger, jp_ws_fetch): payload_1 = """\ { "schema_id": "http://event.mock.jupyter.org/message", - "version": 1, + "version": "1", "data": { "event_message": "Hello, world!" }, @@ -56,7 +56,7 @@ async def test_subscribe_websocket(event_logger, jp_ws_fetch): payload_2 = """\ { "schema_id": "http://event.mock.jupyter.org/message", - "version": 1, + "version": "1", "data": { "event_message": "Hello, world!" } @@ -92,7 +92,7 @@ async def test_post_event(jp_fetch, event_logger_sink, payload): payload_4 = """\ { - "version": 1, + "version": "1", "data": { "event_message": "Hello, world!" } @@ -102,14 +102,14 @@ async def test_post_event(jp_fetch, event_logger_sink, payload): payload_5 = """\ { "schema_id": "http://event.mock.jupyter.org/message", - "version": 1 + "version": "1" } """ payload_6 = """\ { "schema_id": "event.mock.jupyter.org/message", - "version": 1, + "version": "1", "data": { "event_message": "Hello, world!" }, @@ -120,7 +120,7 @@ async def test_post_event(jp_fetch, event_logger_sink, payload): payload_7 = """\ { "schema_id": "http://event.mock.jupyter.org/UNREGISTERED-SCHEMA", - "version": 1, + "version": "1", "data": { "event_message": "Hello, world!" } @@ -130,7 +130,7 @@ async def test_post_event(jp_fetch, event_logger_sink, payload): payload_8 = """\ { "schema_id": "http://event.mock.jupyter.org/message", - "version": 1, + "version": "1", "data": { "message": "Hello, world!" } From 811e2c52cfbc11ba63eac30fda0d43a772427f16 Mon Sep 17 00:00:00 2001 From: M Bussonnier Date: Tue, 17 Dec 2024 16:39:45 +0100 Subject: [PATCH 2/8] Drop 3.8 not compatible with latest jupyter_events --- .github/workflows/python-tests.yml | 8 ++++---- examples/simple/pyproject.toml | 2 +- jupyter_server/serverapp.py | 4 +--- jupyter_server/utils.py | 17 +++++------------ pyproject.toml | 4 ++-- 5 files changed, 13 insertions(+), 22 deletions(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 83c949e6ca..2d08a1ea31 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -18,12 +18,12 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ["3.8", "3.11"] + python-version: ["3.9", "3.11", "3.12", "3.13"] include: - os: windows-latest python-version: "3.9" - os: ubuntu-latest - python-version: "pypy-3.8" + python-version: "pypy-3.9" - os: macos-latest python-version: "3.10" - os: ubuntu-latest @@ -180,7 +180,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 @@ -194,7 +194,7 @@ jobs: - uses: actions/checkout@v4 - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 with: - python_version: "pypy-3.8" + python_version: "pypy-3.9" - name: Run the tests run: hatch -v run test:nowarn --integration_tests=true diff --git a/examples/simple/pyproject.toml b/examples/simple/pyproject.toml index 38ae8e71a7..cb73a5e70c 100644 --- a/examples/simple/pyproject.toml +++ b/examples/simple/pyproject.toml @@ -7,7 +7,7 @@ name = "jupyter-server-example" description = "Jupyter Server Example" readme = "README.md" license = "" -requires-python = ">=3.8" +requires-python = ">=3.9" dependencies = [ "jinja2", "jupyter_server", diff --git a/jupyter_server/serverapp.py b/jupyter_server/serverapp.py index 13fa256397..8aa3dbd082 100644 --- a/jupyter_server/serverapp.py +++ b/jupyter_server/serverapp.py @@ -2552,8 +2552,6 @@ def init_mime_overrides(self) -> None: # ensure css, js are correct, which are required for pages to function mimetypes.add_type("text/css", ".css") mimetypes.add_type("application/javascript", ".js") - # for python <3.8 - mimetypes.add_type("application/wasm", ".wasm") def shutdown_no_activity(self) -> None: """Shutdown server on timeout when there are no kernels or terminals.""" @@ -2718,7 +2716,7 @@ def _init_asyncio_patch() -> None: at least until asyncio adds *_reader methods to proactor. """ - if sys.platform.startswith("win") and sys.version_info >= (3, 8): + if sys.platform.startswith("win"): import asyncio try: diff --git a/jupyter_server/utils.py b/jupyter_server/utils.py index 0c987bff25..382d174912 100644 --- a/jupyter_server/utils.py +++ b/jupyter_server/utils.py @@ -11,9 +11,10 @@ import sys import warnings from _frozen_importlib_external import _NamespacePath +from collections.abc import Generator, Sequence from contextlib import contextmanager from pathlib import Path -from typing import Any, Generator, NewType, Sequence +from typing import Any, NewType from urllib.parse import ( SplitResult, quote, @@ -378,17 +379,9 @@ def filefind(filename: str, path_dirs: Sequence[str]) -> str: # os.path.abspath resolves '..', but Path.absolute() doesn't # Path.resolve() does, but traverses symlinks, which we don't want test_path = Path(os.path.abspath(test_path)) - if sys.version_info >= (3, 9): - if not test_path.is_relative_to(path): - # points outside root, e.g. via `filename='../foo'` - continue - else: - # is_relative_to is new in 3.9 - try: - test_path.relative_to(path) - except ValueError: - # points outside root, e.g. via `filename='../foo'` - continue + if not test_path.is_relative_to(path): + # points outside root, e.g. via `filename='../foo'` + continue # make sure we don't call is_file before we know it's a file within a prefix # GHSA-hrw6-wg82-cm62 - can leak password hash on windows. if test_path.is_file(): diff --git a/pyproject.toml b/pyproject.toml index 4f83091f25..21f701a220 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ classifiers = [ "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", ] -requires-python = ">=3.8" +requires-python = ">=3.9" dependencies = [ "anyio>=3.1.0", "argon2-cffi>=21.1", @@ -208,7 +208,7 @@ pydist_resource_paths = ["jupyter_server/static/style/bootstrap.min.css", "jupyt post-version-spec = "dev" [tool.mypy] -python_version = "3.8" +python_version = "3.9" explicit_package_bases = true strict = true pretty = true From 82c53cc0fab3ec03e83d88124eaacd3fb211bf41 Mon Sep 17 00:00:00 2001 From: M Bussonnier Date: Tue, 17 Dec 2024 16:51:58 +0100 Subject: [PATCH 3/8] more fixes with initialize --- tests/extension/mockextensions/app.py | 2 +- tests/extension/test_app.py | 1 + tests/services/events/mockextension/mock_extension_event.yaml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/extension/mockextensions/app.py b/tests/extension/mockextensions/app.py index 26f38464cd..c4f7af099c 100644 --- a/tests/extension/mockextensions/app.py +++ b/tests/extension/mockextensions/app.py @@ -14,7 +14,7 @@ EVENT_SCHEMA = """\ $id: https://events.jupyter.org/mockapp/v1/test -version: 1 +version: '1' properties: msg: type: string diff --git a/tests/extension/test_app.py b/tests/extension/test_app.py index d1add54344..965fe2ca16 100644 --- a/tests/extension/test_app.py +++ b/tests/extension/test_app.py @@ -32,6 +32,7 @@ def mock_extension(extension_manager): pkg = extension_manager.extensions[name] point = pkg.extension_points["mockextension"] app = point.app + app.initialize() return app diff --git a/tests/services/events/mockextension/mock_extension_event.yaml b/tests/services/events/mockextension/mock_extension_event.yaml index b7c03d1a48..7354d6a094 100644 --- a/tests/services/events/mockextension/mock_extension_event.yaml +++ b/tests/services/events/mockextension/mock_extension_event.yaml @@ -1,5 +1,5 @@ $id: http://event.mockextension.jupyter.org/message -version: 1 +version: "1" title: Message description: | Emit a message From ff6a4e2aad8c0a6ed4b0dea9558907511734f672 Mon Sep 17 00:00:00 2001 From: M Bussonnier Date: Tue, 17 Dec 2024 16:54:09 +0100 Subject: [PATCH 4/8] pre-commit --- jupyter_server/_version.py | 3 +-- jupyter_server/auth/authorizer.py | 3 ++- jupyter_server/base/call_context.py | 8 ++++---- jupyter_server/base/handlers.py | 3 ++- jupyter_server/config_manager.py | 2 +- jupyter_server/files/handlers.py | 2 +- jupyter_server/services/api/handlers.py | 6 +++--- jupyter_server/services/config/manager.py | 2 +- jupyter_server/services/contents/handlers.py | 4 ++-- jupyter_server/services/events/handlers.py | 4 ++-- jupyter_server/services/kernels/connection/abc.py | 4 ++-- jupyter_server/services/kernels/connection/base.py | 6 +++--- jupyter_server/services/sessions/sessionmanager.py | 10 +++++----- tests/auth/test_authorizer.py | 2 +- tests/services/api/test_api.py | 4 ++-- tests/services/contents/test_manager.py | 11 ++++++----- tests/test_gateway.py | 4 ++-- tests/test_serverapp.py | 5 +++-- 18 files changed, 43 insertions(+), 40 deletions(-) diff --git a/jupyter_server/_version.py b/jupyter_server/_version.py index fa814fbb2f..20f829fe92 100644 --- a/jupyter_server/_version.py +++ b/jupyter_server/_version.py @@ -4,7 +4,6 @@ """ import re -from typing import List # Version string must appear intact for automatic versioning __version__ = "2.15.0.dev0" @@ -13,7 +12,7 @@ pattern = r"(?P\d+).(?P\d+).(?P\d+)(?P.*)" match = re.match(pattern, __version__) assert match is not None -parts: List[object] = [int(match[part]) for part in ["major", "minor", "patch"]] +parts: list[object] = [int(match[part]) for part in ["major", "minor", "patch"]] if match["rest"]: parts.append(match["rest"]) version_info = tuple(parts) diff --git a/jupyter_server/auth/authorizer.py b/jupyter_server/auth/authorizer.py index 10414e2c39..56d26db3a7 100644 --- a/jupyter_server/auth/authorizer.py +++ b/jupyter_server/auth/authorizer.py @@ -10,7 +10,8 @@ # Distributed under the terms of the Modified BSD License. from __future__ import annotations -from typing import TYPE_CHECKING, Awaitable +from collections.abc import Awaitable +from typing import TYPE_CHECKING from traitlets import Instance from traitlets.config import LoggingConfigurable diff --git a/jupyter_server/base/call_context.py b/jupyter_server/base/call_context.py index cf71256235..4e80be8a7d 100644 --- a/jupyter_server/base/call_context.py +++ b/jupyter_server/base/call_context.py @@ -3,7 +3,7 @@ # Distributed under the terms of the Modified BSD License. from contextvars import Context, ContextVar, copy_context -from typing import Any, Dict, List +from typing import Any class CallContext: @@ -22,7 +22,7 @@ class CallContext: # easier management over maintaining a set of ContextVar instances, since the Context is a # map of ContextVar instances to their values, and the "name" is no longer a lookup key. _NAME_VALUE_MAP = "_name_value_map" - _name_value_map: ContextVar[Dict[str, Any]] = ContextVar(_NAME_VALUE_MAP) + _name_value_map: ContextVar[dict[str, Any]] = ContextVar(_NAME_VALUE_MAP) @classmethod def get(cls, name: str) -> Any: @@ -65,7 +65,7 @@ def set(cls, name: str, value: Any) -> None: name_value_map[name] = value @classmethod - def context_variable_names(cls) -> List[str]: + def context_variable_names(cls) -> list[str]: """Returns a list of variable names set for this call context. Returns @@ -77,7 +77,7 @@ def context_variable_names(cls) -> List[str]: return list(name_value_map.keys()) @classmethod - def _get_map(cls) -> Dict[str, Any]: + def _get_map(cls) -> dict[str, Any]: """Get the map of names to their values from the _NAME_VALUE_MAP context var. If the map does not exist in the current context, an empty map is created and returned. diff --git a/jupyter_server/base/handlers.py b/jupyter_server/base/handlers.py index 770fff1866..6f4977145d 100644 --- a/jupyter_server/base/handlers.py +++ b/jupyter_server/base/handlers.py @@ -13,9 +13,10 @@ import re import types import warnings +from collections.abc import Awaitable, Coroutine, Sequence from http.client import responses from logging import Logger -from typing import TYPE_CHECKING, Any, Awaitable, Coroutine, Sequence, cast +from typing import TYPE_CHECKING, Any, cast from urllib.parse import urlparse import prometheus_client diff --git a/jupyter_server/config_manager.py b/jupyter_server/config_manager.py index 4a0bff4015..8f49cb7bd6 100644 --- a/jupyter_server/config_manager.py +++ b/jupyter_server/config_manager.py @@ -14,7 +14,7 @@ from traitlets.config import LoggingConfigurable from traitlets.traitlets import Bool, Unicode -StrDict = t.Dict[str, t.Any] +StrDict = dict[str, t.Any] def recursive_update(target: StrDict, new: StrDict) -> None: diff --git a/jupyter_server/files/handlers.py b/jupyter_server/files/handlers.py index 2c1dc5adf6..f6ebae64f1 100644 --- a/jupyter_server/files/handlers.py +++ b/jupyter_server/files/handlers.py @@ -6,7 +6,7 @@ import mimetypes from base64 import decodebytes -from typing import Awaitable +from collections.abc import Awaitable from jupyter_core.utils import ensure_async from tornado import web diff --git a/jupyter_server/services/api/handlers.py b/jupyter_server/services/api/handlers.py index 22904fdb07..f61d9dd10f 100644 --- a/jupyter_server/services/api/handlers.py +++ b/jupyter_server/services/api/handlers.py @@ -4,7 +4,7 @@ # Distributed under the terms of the Modified BSD License. import json import os -from typing import Any, Dict, List +from typing import Any from jupyter_core.utils import ensure_async from tornado import web @@ -87,7 +87,7 @@ async def get(self): else: permissions_to_check = {} - permissions: Dict[str, List[str]] = {} + permissions: dict[str, list[str]] = {} user = self.current_user for resource, actions in permissions_to_check.items(): @@ -106,7 +106,7 @@ async def get(self): if authorized: allowed.append(action) - identity: Dict[str, Any] = self.identity_provider.identity_model(user) + identity: dict[str, Any] = self.identity_provider.identity_model(user) model = { "identity": identity, "permissions": permissions, diff --git a/jupyter_server/services/config/manager.py b/jupyter_server/services/config/manager.py index d4e207e247..223d4fed2c 100644 --- a/jupyter_server/services/config/manager.py +++ b/jupyter_server/services/config/manager.py @@ -23,7 +23,7 @@ class ConfigManager(LoggingConfigurable): def get(self, section_name): """Get the config from all config sections.""" - config: t.Dict[str, t.Any] = {} + config: dict[str, t.Any] = {} # step through back to front, to ensure front of the list is top priority for p in self.read_config_path[::-1]: cm = BaseJSONConfigManager(config_dir=p) diff --git a/jupyter_server/services/contents/handlers.py b/jupyter_server/services/contents/handlers.py index 13e987809b..0a6110ee3d 100644 --- a/jupyter_server/services/contents/handlers.py +++ b/jupyter_server/services/contents/handlers.py @@ -7,7 +7,7 @@ # Distributed under the terms of the Modified BSD License. import json from http import HTTPStatus -from typing import Any, Dict, List +from typing import Any try: from jupyter_client.jsonutil import json_default @@ -24,7 +24,7 @@ AUTH_RESOURCE = "contents" -def _validate_keys(expect_defined: bool, model: Dict[str, Any], keys: List[str]): +def _validate_keys(expect_defined: bool, model: dict[str, Any], keys: list[str]): """ Validate that the keys are defined (i.e. not None) or not (i.e. None) """ diff --git a/jupyter_server/services/events/handlers.py b/jupyter_server/services/events/handlers.py index fc899c8771..fbc007341d 100644 --- a/jupyter_server/services/events/handlers.py +++ b/jupyter_server/services/events/handlers.py @@ -7,7 +7,7 @@ import json from datetime import datetime -from typing import TYPE_CHECKING, Any, Dict, Optional, cast +from typing import TYPE_CHECKING, Any, Optional, cast from jupyter_core.utils import ensure_async from tornado import web, websocket @@ -127,7 +127,7 @@ async def post(self): validate_model(payload, self.event_logger.schemas) self.event_logger.emit( schema_id=cast(str, payload.get("schema_id")), - data=cast("Dict[str, Any]", payload.get("data")), + data=cast("dict[str, Any]", payload.get("data")), timestamp_override=get_timestamp(payload), ) self.set_status(204) diff --git a/jupyter_server/services/kernels/connection/abc.py b/jupyter_server/services/kernels/connection/abc.py index 71f9e8254f..61e11a948e 100644 --- a/jupyter_server/services/kernels/connection/abc.py +++ b/jupyter_server/services/kernels/connection/abc.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import Any, List +from typing import Any class KernelWebsocketConnectionABC(ABC): @@ -25,5 +25,5 @@ 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: + 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/base.py b/jupyter_server/services/kernels/connection/base.py index a0e0bae8b8..6af10444b5 100644 --- a/jupyter_server/services/kernels/connection/base.py +++ b/jupyter_server/services/kernels/connection/base.py @@ -2,7 +2,7 @@ import json import struct -from typing import Any, List +from typing import Any from jupyter_client.session import Session from tornado.websocket import WebSocketHandler @@ -89,7 +89,7 @@ def serialize_msg_to_ws_v1(msg_or_list, channel, pack=None): else: msg_list = msg_or_list channel = channel.encode("utf-8") - offsets: List[Any] = [] + offsets: list[Any] = [] offsets.append(8 * (1 + 1 + len(msg_list) + 1)) offsets.append(len(channel) + offsets[-1]) for msg in msg_list: @@ -173,7 +173,7 @@ def handle_incoming_message(self, incoming_msg: str) -> None: """Handle an incoming message.""" raise NotImplementedError - def handle_outgoing_message(self, stream: str, outgoing_msg: List[Any]) -> None: + def handle_outgoing_message(self, stream: str, outgoing_msg: list[Any]) -> None: """Handle an outgoing message.""" raise NotImplementedError diff --git a/jupyter_server/services/sessions/sessionmanager.py b/jupyter_server/services/sessions/sessionmanager.py index 8b392b4e1b..3aac78a0a9 100644 --- a/jupyter_server/services/sessions/sessionmanager.py +++ b/jupyter_server/services/sessions/sessionmanager.py @@ -5,7 +5,7 @@ import os import pathlib import uuid -from typing import Any, Dict, List, NewType, Optional, Union, cast +from typing import Any, NewType, Optional, Union, cast KernelName = NewType("KernelName", str) ModelName = NewType("ModelName", str) @@ -100,7 +100,7 @@ class KernelSessionRecordList: it will be appended. """ - _records: List[KernelSessionRecord] + _records: list[KernelSessionRecord] def __init__(self, *records: KernelSessionRecord): """Initialize a record list.""" @@ -267,7 +267,7 @@ async def create_session( type: Optional[str] = None, kernel_name: Optional[KernelName] = None, kernel_id: Optional[str] = None, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Creates a session and returns its model Parameters @@ -291,11 +291,11 @@ async def create_session( session_id, path=path, name=name, type=type, kernel_id=kernel_id ) self._pending_sessions.remove(record) - return cast(Dict[str, Any], result) + return cast(dict[str, Any], result) def get_kernel_env( self, path: Optional[str], name: Optional[ModelName] = None - ) -> Dict[str, str]: + ) -> dict[str, str]: """Return the environment variables that need to be set in the kernel Parameters diff --git a/tests/auth/test_authorizer.py b/tests/auth/test_authorizer.py index 3ba6bef9f1..59a6aa7428 100644 --- a/tests/auth/test_authorizer.py +++ b/tests/auth/test_authorizer.py @@ -3,7 +3,7 @@ import asyncio import json import os -from typing import Awaitable +from collections.abc import Awaitable import pytest from jupyter_client.kernelspec import NATIVE_KERNEL_NAME diff --git a/tests/services/api/test_api.py b/tests/services/api/test_api.py index 900280f67d..8339f6e4af 100644 --- a/tests/services/api/test_api.py +++ b/tests/services/api/test_api.py @@ -1,5 +1,5 @@ import json -from typing import Awaitable, Dict, List +from collections.abc import Awaitable from unittest import mock import pytest @@ -31,7 +31,7 @@ async def test_get_status(jp_fetch): class MockUser(User): - permissions: Dict[str, List[str]] + permissions: dict[str, list[str]] class MockIdentityProvider(IdentityProvider): diff --git a/tests/services/contents/test_manager.py b/tests/services/contents/test_manager.py index be79011be8..5c3e2eca50 100644 --- a/tests/services/contents/test_manager.py +++ b/tests/services/contents/test_manager.py @@ -3,7 +3,7 @@ import sys import time from itertools import combinations -from typing import Dict, Optional, Tuple +from typing import Optional from unittest.mock import patch import pytest @@ -112,7 +112,7 @@ def add_invalid_cell(notebook): async def prepare_notebook( jp_contents_manager: FileContentsManager, make_invalid: Optional[bool] = False -) -> Tuple[Dict, str]: +) -> tuple[dict, str]: cm = jp_contents_manager model = await ensure_async(cm.new_untitled(type="notebook")) name = model["name"] @@ -983,9 +983,10 @@ async def test_nb_validation(jp_contents_manager): # successful methods and ensure that calls to the aliased "validate_nb" are # zero. Note that since patching side-effects the validation error case, we'll # skip call-count assertions for that portion of the test. - with patch("nbformat.validate") as mock_validate, patch( - "jupyter_server.services.contents.manager.validate_nb" - ) as mock_validate_nb: + with ( + patch("nbformat.validate") as mock_validate, + patch("jupyter_server.services.contents.manager.validate_nb") as mock_validate_nb, + ): # Valid notebook, save, then get model = await ensure_async(cm.save(model, path)) assert "message" not in model diff --git a/tests/test_gateway.py b/tests/test_gateway.py index 569268d833..00aa64f111 100644 --- a/tests/test_gateway.py +++ b/tests/test_gateway.py @@ -10,7 +10,7 @@ from http.cookies import SimpleCookie from io import BytesIO from queue import Empty -from typing import Any, Dict, Union +from typing import Any, Union from unittest.mock import MagicMock, patch import pytest @@ -74,7 +74,7 @@ def generate_kernelspec(name): # # This is used to simulate inconsistency in list results from the Gateway server # due to issues like race conditions, bugs, etc. -omitted_kernels: Dict[str, bool] = {} +omitted_kernels: dict[str, bool] = {} def generate_model(name): diff --git a/tests/test_serverapp.py b/tests/test_serverapp.py index 91fa33230c..f836d1b2f8 100644 --- a/tests/test_serverapp.py +++ b/tests/test_serverapp.py @@ -154,8 +154,9 @@ async def test_generate_config(tmp_path, jp_configurable_serverapp): def test_server_password(tmp_path, jp_configurable_serverapp): password = "secret" - with patch.dict("os.environ", {"JUPYTER_CONFIG_DIR": str(tmp_path)}), patch.object( - getpass, "getpass", return_value=password + with ( + patch.dict("os.environ", {"JUPYTER_CONFIG_DIR": str(tmp_path)}), + patch.object(getpass, "getpass", return_value=password), ): app = JupyterPasswordApp(log_level=logging.ERROR) app.initialize([]) From 1f210b22d43462d063b3f2df822b74d8fb438ccb Mon Sep 17 00:00:00 2001 From: M Bussonnier Date: Tue, 17 Dec 2024 17:06:24 +0100 Subject: [PATCH 5/8] actually dont test on 3.13 yet --- .github/workflows/python-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 2d08a1ea31..709a190ac6 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -18,7 +18,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ["3.9", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.11", "3.12"] include: - os: windows-latest python-version: "3.9" @@ -180,7 +180,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v4 - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 From 90e22857006916eed5b82683f369aadbdec572a1 Mon Sep 17 00:00:00 2001 From: M Bussonnier Date: Tue, 17 Dec 2024 17:08:11 +0100 Subject: [PATCH 6/8] unsafe ruff fixes --- jupyter_server/auth/authorizer.py | 3 ++- jupyter_server/files/handlers.py | 5 ++++- jupyter_server/utils.py | 6 ++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/jupyter_server/auth/authorizer.py b/jupyter_server/auth/authorizer.py index 56d26db3a7..fcebc3404b 100644 --- a/jupyter_server/auth/authorizer.py +++ b/jupyter_server/auth/authorizer.py @@ -10,7 +10,6 @@ # Distributed under the terms of the Modified BSD License. from __future__ import annotations -from collections.abc import Awaitable from typing import TYPE_CHECKING from traitlets import Instance @@ -19,6 +18,8 @@ from .identity import IdentityProvider, User if TYPE_CHECKING: + from collections.abc import Awaitable + from jupyter_server.base.handlers import JupyterHandler diff --git a/jupyter_server/files/handlers.py b/jupyter_server/files/handlers.py index f6ebae64f1..749328438e 100644 --- a/jupyter_server/files/handlers.py +++ b/jupyter_server/files/handlers.py @@ -6,7 +6,7 @@ import mimetypes from base64 import decodebytes -from collections.abc import Awaitable +from typing import TYPE_CHECKING from jupyter_core.utils import ensure_async from tornado import web @@ -14,6 +14,9 @@ from jupyter_server.auth.decorator import authorized from jupyter_server.base.handlers import JupyterHandler +if TYPE_CHECKING: + from collections.abc import Awaitable + AUTH_RESOURCE = "contents" diff --git a/jupyter_server/utils.py b/jupyter_server/utils.py index 382d174912..d83e1be880 100644 --- a/jupyter_server/utils.py +++ b/jupyter_server/utils.py @@ -11,10 +11,9 @@ import sys import warnings from _frozen_importlib_external import _NamespacePath -from collections.abc import Generator, Sequence from contextlib import contextmanager from pathlib import Path -from typing import Any, NewType +from typing import TYPE_CHECKING, Any, NewType from urllib.parse import ( SplitResult, quote, @@ -33,6 +32,9 @@ from tornado.httpclient import AsyncHTTPClient, HTTPClient, HTTPRequest, HTTPResponse from tornado.netutil import Resolver +if TYPE_CHECKING: + from collections.abc import Generator, Sequence + ApiPath = NewType("ApiPath", str) # Re-export From fbda08cabdc0d87ad7c4ec093ef3958f21daef2c Mon Sep 17 00:00:00 2001 From: M Bussonnier Date: Tue, 17 Dec 2024 17:08:54 +0100 Subject: [PATCH 7/8] fix examples --- examples/simple/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/simple/pyproject.toml b/examples/simple/pyproject.toml index cb73a5e70c..d4b880b87d 100644 --- a/examples/simple/pyproject.toml +++ b/examples/simple/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" name = "jupyter-server-example" description = "Jupyter Server Example" readme = "README.md" -license = "" +license = "BSD" requires-python = ">=3.9" dependencies = [ "jinja2", From 259ba7fcbe1f153be6fc70e7482b6e278f21631a Mon Sep 17 00:00:00 2001 From: M Bussonnier Date: Tue, 17 Dec 2024 17:31:26 +0100 Subject: [PATCH 8/8] try another license --- examples/simple/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/simple/pyproject.toml b/examples/simple/pyproject.toml index d4b880b87d..9dec3e55c7 100644 --- a/examples/simple/pyproject.toml +++ b/examples/simple/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" name = "jupyter-server-example" description = "Jupyter Server Example" readme = "README.md" -license = "BSD" +license = "MIT" requires-python = ">=3.9" dependencies = [ "jinja2",