Skip to content

Commit 111e104

Browse files
authored
Handle newer jupyter_events wants string version, drop 3.8 (#1481)
1 parent e544fa1 commit 111e104

File tree

31 files changed

+80
-79
lines changed

31 files changed

+80
-79
lines changed

.github/workflows/python-tests.yml

+4-4
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ jobs:
1818
fail-fast: false
1919
matrix:
2020
os: [ubuntu-latest, windows-latest, macos-latest]
21-
python-version: ["3.8", "3.11"]
21+
python-version: ["3.9", "3.11", "3.12"]
2222
include:
2323
- os: windows-latest
2424
python-version: "3.9"
2525
- os: ubuntu-latest
26-
python-version: "pypy-3.8"
26+
python-version: "pypy-3.9"
2727
- os: macos-latest
2828
python-version: "3.10"
2929
- os: ubuntu-latest
@@ -180,7 +180,7 @@ jobs:
180180
fail-fast: false
181181
matrix:
182182
os: [ubuntu-latest]
183-
python-version: ["3.8", "3.9", "3.10", "3.11"]
183+
python-version: ["3.9", "3.10", "3.11", "3.12"]
184184
steps:
185185
- uses: actions/checkout@v4
186186
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
@@ -194,7 +194,7 @@ jobs:
194194
- uses: actions/checkout@v4
195195
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
196196
with:
197-
python_version: "pypy-3.8"
197+
python_version: "pypy-3.9"
198198
- name: Run the tests
199199
run: hatch -v run test:nowarn --integration_tests=true
200200

examples/simple/pyproject.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ build-backend = "hatchling.build"
66
name = "jupyter-server-example"
77
description = "Jupyter Server Example"
88
readme = "README.md"
9-
license = ""
10-
requires-python = ">=3.8"
9+
license = "MIT"
10+
requires-python = ">=3.9"
1111
dependencies = [
1212
"jinja2",
1313
"jupyter_server",

jupyter_server/_version.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"""
55

66
import re
7-
from typing import List
87

98
# Version string must appear intact for automatic versioning
109
__version__ = "2.15.0.dev0"
@@ -13,7 +12,7 @@
1312
pattern = r"(?P<major>\d+).(?P<minor>\d+).(?P<patch>\d+)(?P<rest>.*)"
1413
match = re.match(pattern, __version__)
1514
assert match is not None
16-
parts: List[object] = [int(match[part]) for part in ["major", "minor", "patch"]]
15+
parts: list[object] = [int(match[part]) for part in ["major", "minor", "patch"]]
1716
if match["rest"]:
1817
parts.append(match["rest"])
1918
version_info = tuple(parts)

jupyter_server/auth/authorizer.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@
1010
# Distributed under the terms of the Modified BSD License.
1111
from __future__ import annotations
1212

13-
from typing import TYPE_CHECKING, Awaitable
13+
from typing import TYPE_CHECKING
1414

1515
from traitlets import Instance
1616
from traitlets.config import LoggingConfigurable
1717

1818
from .identity import IdentityProvider, User
1919

2020
if TYPE_CHECKING:
21+
from collections.abc import Awaitable
22+
2123
from jupyter_server.base.handlers import JupyterHandler
2224

2325

jupyter_server/base/call_context.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Distributed under the terms of the Modified BSD License.
44

55
from contextvars import Context, ContextVar, copy_context
6-
from typing import Any, Dict, List
6+
from typing import Any
77

88

99
class CallContext:
@@ -22,7 +22,7 @@ class CallContext:
2222
# easier management over maintaining a set of ContextVar instances, since the Context is a
2323
# map of ContextVar instances to their values, and the "name" is no longer a lookup key.
2424
_NAME_VALUE_MAP = "_name_value_map"
25-
_name_value_map: ContextVar[Dict[str, Any]] = ContextVar(_NAME_VALUE_MAP)
25+
_name_value_map: ContextVar[dict[str, Any]] = ContextVar(_NAME_VALUE_MAP)
2626

2727
@classmethod
2828
def get(cls, name: str) -> Any:
@@ -65,7 +65,7 @@ def set(cls, name: str, value: Any) -> None:
6565
name_value_map[name] = value
6666

6767
@classmethod
68-
def context_variable_names(cls) -> List[str]:
68+
def context_variable_names(cls) -> list[str]:
6969
"""Returns a list of variable names set for this call context.
7070
7171
Returns
@@ -77,7 +77,7 @@ def context_variable_names(cls) -> List[str]:
7777
return list(name_value_map.keys())
7878

7979
@classmethod
80-
def _get_map(cls) -> Dict[str, Any]:
80+
def _get_map(cls) -> dict[str, Any]:
8181
"""Get the map of names to their values from the _NAME_VALUE_MAP context var.
8282
8383
If the map does not exist in the current context, an empty map is created and returned.

jupyter_server/base/handlers.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313
import re
1414
import types
1515
import warnings
16+
from collections.abc import Awaitable, Coroutine, Sequence
1617
from http.client import responses
1718
from logging import Logger
18-
from typing import TYPE_CHECKING, Any, Awaitable, Coroutine, Sequence, cast
19+
from typing import TYPE_CHECKING, Any, cast
1920
from urllib.parse import urlparse
2021

2122
import prometheus_client

jupyter_server/config_manager.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from traitlets.config import LoggingConfigurable
1515
from traitlets.traitlets import Bool, Unicode
1616

17-
StrDict = t.Dict[str, t.Any]
17+
StrDict = dict[str, t.Any]
1818

1919

2020
def recursive_update(target: StrDict, new: StrDict) -> None:

jupyter_server/event_schemas/contents_service/v1.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"$id": https://events.jupyter.org/jupyter_server/contents_service/v1
2-
version: 1
2+
version: "1"
33
title: Contents Manager activities
44
personal-data: true
55
description: |

jupyter_server/event_schemas/gateway_client/v1.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"$id": https://events.jupyter.org/jupyter_server/gateway_client/v1
2-
version: 1
2+
version: "1"
33
title: Gateway Client activities.
44
personal-data: true
55
description: |

jupyter_server/event_schemas/kernel_actions/v1.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"$id": https://events.jupyter.org/jupyter_server/kernel_actions/v1
2-
version: 1
2+
version: "1"
33
title: Kernel Manager activities
44
personal-data: true
55
description: |

jupyter_server/files/handlers.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@
66

77
import mimetypes
88
from base64 import decodebytes
9-
from typing import Awaitable
9+
from typing import TYPE_CHECKING
1010

1111
from jupyter_core.utils import ensure_async
1212
from tornado import web
1313

1414
from jupyter_server.auth.decorator import authorized
1515
from jupyter_server.base.handlers import JupyterHandler
1616

17+
if TYPE_CHECKING:
18+
from collections.abc import Awaitable
19+
1720
AUTH_RESOURCE = "contents"
1821

1922

jupyter_server/serverapp.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -2552,8 +2552,6 @@ def init_mime_overrides(self) -> None:
25522552
# ensure css, js are correct, which are required for pages to function
25532553
mimetypes.add_type("text/css", ".css")
25542554
mimetypes.add_type("application/javascript", ".js")
2555-
# for python <3.8
2556-
mimetypes.add_type("application/wasm", ".wasm")
25572555

25582556
def shutdown_no_activity(self) -> None:
25592557
"""Shutdown server on timeout when there are no kernels or terminals."""
@@ -2718,7 +2716,7 @@ def _init_asyncio_patch() -> None:
27182716
at least until asyncio adds *_reader methods
27192717
to proactor.
27202718
"""
2721-
if sys.platform.startswith("win") and sys.version_info >= (3, 8):
2719+
if sys.platform.startswith("win"):
27222720
import asyncio
27232721

27242722
try:

jupyter_server/services/api/handlers.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Distributed under the terms of the Modified BSD License.
55
import json
66
import os
7-
from typing import Any, Dict, List
7+
from typing import Any
88

99
from jupyter_core.utils import ensure_async
1010
from tornado import web
@@ -87,7 +87,7 @@ async def get(self):
8787
else:
8888
permissions_to_check = {}
8989

90-
permissions: Dict[str, List[str]] = {}
90+
permissions: dict[str, list[str]] = {}
9191
user = self.current_user
9292

9393
for resource, actions in permissions_to_check.items():
@@ -106,7 +106,7 @@ async def get(self):
106106
if authorized:
107107
allowed.append(action)
108108

109-
identity: Dict[str, Any] = self.identity_provider.identity_model(user)
109+
identity: dict[str, Any] = self.identity_provider.identity_model(user)
110110
model = {
111111
"identity": identity,
112112
"permissions": permissions,

jupyter_server/services/config/manager.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class ConfigManager(LoggingConfigurable):
2323

2424
def get(self, section_name):
2525
"""Get the config from all config sections."""
26-
config: t.Dict[str, t.Any] = {}
26+
config: dict[str, t.Any] = {}
2727
# step through back to front, to ensure front of the list is top priority
2828
for p in self.read_config_path[::-1]:
2929
cm = BaseJSONConfigManager(config_dir=p)

jupyter_server/services/contents/handlers.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# Distributed under the terms of the Modified BSD License.
88
import json
99
from http import HTTPStatus
10-
from typing import Any, Dict, List
10+
from typing import Any
1111

1212
try:
1313
from jupyter_client.jsonutil import json_default
@@ -24,7 +24,7 @@
2424
AUTH_RESOURCE = "contents"
2525

2626

27-
def _validate_keys(expect_defined: bool, model: Dict[str, Any], keys: List[str]):
27+
def _validate_keys(expect_defined: bool, model: dict[str, Any], keys: list[str]):
2828
"""
2929
Validate that the keys are defined (i.e. not None) or not (i.e. None)
3030
"""

jupyter_server/services/events/handlers.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import json
99
from datetime import datetime
10-
from typing import TYPE_CHECKING, Any, Dict, Optional, cast
10+
from typing import TYPE_CHECKING, Any, Optional, cast
1111

1212
from jupyter_core.utils import ensure_async
1313
from tornado import web, websocket
@@ -86,9 +86,9 @@ def validate_model(
8686
# jupyter_events raises a useful error, so there's no need to
8787
# handle that case here.
8888
schema = registry.get(schema_id)
89-
version = int(cast(int, data.get("version")))
89+
version = str(cast(str, data.get("version")))
9090
if schema.version != version:
91-
message = f"Unregistered version: {version}{schema.version} for `{schema_id}`"
91+
message = f"Unregistered version: {version!r}{schema.version!r} for `{schema_id}`"
9292
raise Exception(message)
9393

9494

@@ -127,7 +127,7 @@ async def post(self):
127127
validate_model(payload, self.event_logger.schemas)
128128
self.event_logger.emit(
129129
schema_id=cast(str, payload.get("schema_id")),
130-
data=cast("Dict[str, Any]", payload.get("data")),
130+
data=cast("dict[str, Any]", payload.get("data")),
131131
timestamp_override=get_timestamp(payload),
132132
)
133133
self.set_status(204)

jupyter_server/services/kernels/connection/abc.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from abc import ABC, abstractmethod
2-
from typing import Any, List
2+
from typing import Any
33

44

55
class KernelWebsocketConnectionABC(ABC):
@@ -25,5 +25,5 @@ def handle_incoming_message(self, incoming_msg: str) -> None:
2525
"""Broker the incoming websocket message to the appropriate ZMQ channel."""
2626

2727
@abstractmethod
28-
def handle_outgoing_message(self, stream: str, outgoing_msg: List[Any]) -> None:
28+
def handle_outgoing_message(self, stream: str, outgoing_msg: list[Any]) -> None:
2929
"""Broker outgoing ZMQ messages to the kernel websocket."""

jupyter_server/services/kernels/connection/base.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import json
44
import struct
5-
from typing import Any, List
5+
from typing import Any
66

77
from jupyter_client.session import Session
88
from tornado.websocket import WebSocketHandler
@@ -89,7 +89,7 @@ def serialize_msg_to_ws_v1(msg_or_list, channel, pack=None):
8989
else:
9090
msg_list = msg_or_list
9191
channel = channel.encode("utf-8")
92-
offsets: List[Any] = []
92+
offsets: list[Any] = []
9393
offsets.append(8 * (1 + 1 + len(msg_list) + 1))
9494
offsets.append(len(channel) + offsets[-1])
9595
for msg in msg_list:
@@ -173,7 +173,7 @@ def handle_incoming_message(self, incoming_msg: str) -> None:
173173
"""Handle an incoming message."""
174174
raise NotImplementedError
175175

176-
def handle_outgoing_message(self, stream: str, outgoing_msg: List[Any]) -> None:
176+
def handle_outgoing_message(self, stream: str, outgoing_msg: list[Any]) -> None:
177177
"""Handle an outgoing message."""
178178
raise NotImplementedError
179179

jupyter_server/services/sessions/sessionmanager.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import os
66
import pathlib
77
import uuid
8-
from typing import Any, Dict, List, NewType, Optional, Union, cast
8+
from typing import Any, NewType, Optional, Union, cast
99

1010
KernelName = NewType("KernelName", str)
1111
ModelName = NewType("ModelName", str)
@@ -100,7 +100,7 @@ class KernelSessionRecordList:
100100
it will be appended.
101101
"""
102102

103-
_records: List[KernelSessionRecord]
103+
_records: list[KernelSessionRecord]
104104

105105
def __init__(self, *records: KernelSessionRecord):
106106
"""Initialize a record list."""
@@ -267,7 +267,7 @@ async def create_session(
267267
type: Optional[str] = None,
268268
kernel_name: Optional[KernelName] = None,
269269
kernel_id: Optional[str] = None,
270-
) -> Dict[str, Any]:
270+
) -> dict[str, Any]:
271271
"""Creates a session and returns its model
272272
273273
Parameters
@@ -291,11 +291,11 @@ async def create_session(
291291
session_id, path=path, name=name, type=type, kernel_id=kernel_id
292292
)
293293
self._pending_sessions.remove(record)
294-
return cast(Dict[str, Any], result)
294+
return cast(dict[str, Any], result)
295295

296296
def get_kernel_env(
297297
self, path: Optional[str], name: Optional[ModelName] = None
298-
) -> Dict[str, str]:
298+
) -> dict[str, str]:
299299
"""Return the environment variables that need to be set in the kernel
300300
301301
Parameters

jupyter_server/utils.py

+7-12
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from _frozen_importlib_external import _NamespacePath
1414
from contextlib import contextmanager
1515
from pathlib import Path
16-
from typing import Any, Generator, NewType, Sequence
16+
from typing import TYPE_CHECKING, Any, NewType
1717
from urllib.parse import (
1818
SplitResult,
1919
quote,
@@ -32,6 +32,9 @@
3232
from tornado.httpclient import AsyncHTTPClient, HTTPClient, HTTPRequest, HTTPResponse
3333
from tornado.netutil import Resolver
3434

35+
if TYPE_CHECKING:
36+
from collections.abc import Generator, Sequence
37+
3538
ApiPath = NewType("ApiPath", str)
3639

3740
# Re-export
@@ -378,17 +381,9 @@ def filefind(filename: str, path_dirs: Sequence[str]) -> str:
378381
# os.path.abspath resolves '..', but Path.absolute() doesn't
379382
# Path.resolve() does, but traverses symlinks, which we don't want
380383
test_path = Path(os.path.abspath(test_path))
381-
if sys.version_info >= (3, 9):
382-
if not test_path.is_relative_to(path):
383-
# points outside root, e.g. via `filename='../foo'`
384-
continue
385-
else:
386-
# is_relative_to is new in 3.9
387-
try:
388-
test_path.relative_to(path)
389-
except ValueError:
390-
# points outside root, e.g. via `filename='../foo'`
391-
continue
384+
if not test_path.is_relative_to(path):
385+
# points outside root, e.g. via `filename='../foo'`
386+
continue
392387
# make sure we don't call is_file before we know it's a file within a prefix
393388
# GHSA-hrw6-wg82-cm62 - can leak password hash on windows.
394389
if test_path.is_file():

0 commit comments

Comments
 (0)