Skip to content

Commit

Permalink
Handle newer jupyter_events wants string version, drop 3.8 (#1481)
Browse files Browse the repository at this point in the history
  • Loading branch information
Carreau authored Dec 19, 2024
1 parent e544fa1 commit 111e104
Show file tree
Hide file tree
Showing 31 changed files with 80 additions and 79 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
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
Expand Down Expand Up @@ -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"]
steps:
- uses: actions/checkout@v4
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
Expand All @@ -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

Expand Down
4 changes: 2 additions & 2 deletions examples/simple/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ build-backend = "hatchling.build"
name = "jupyter-server-example"
description = "Jupyter Server Example"
readme = "README.md"
license = ""
requires-python = ">=3.8"
license = "MIT"
requires-python = ">=3.9"
dependencies = [
"jinja2",
"jupyter_server",
Expand Down
3 changes: 1 addition & 2 deletions jupyter_server/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"""

import re
from typing import List

# Version string must appear intact for automatic versioning
__version__ = "2.15.0.dev0"
Expand All @@ -13,7 +12,7 @@
pattern = r"(?P<major>\d+).(?P<minor>\d+).(?P<patch>\d+)(?P<rest>.*)"
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)
4 changes: 3 additions & 1 deletion jupyter_server/auth/authorizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@
# Distributed under the terms of the Modified BSD License.
from __future__ import annotations

from typing import TYPE_CHECKING, Awaitable
from typing import TYPE_CHECKING

from traitlets import Instance
from traitlets.config import LoggingConfigurable

from .identity import IdentityProvider, User

if TYPE_CHECKING:
from collections.abc import Awaitable

from jupyter_server.base.handlers import JupyterHandler


Expand Down
8 changes: 4 additions & 4 deletions jupyter_server/base/call_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion jupyter_server/base/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion jupyter_server/config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion jupyter_server/event_schemas/contents_service/v1.yaml
Original file line number Diff line number Diff line change
@@ -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: |
Expand Down
2 changes: 1 addition & 1 deletion jupyter_server/event_schemas/gateway_client/v1.yaml
Original file line number Diff line number Diff line change
@@ -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: |
Expand Down
2 changes: 1 addition & 1 deletion jupyter_server/event_schemas/kernel_actions/v1.yaml
Original file line number Diff line number Diff line change
@@ -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: |
Expand Down
5 changes: 4 additions & 1 deletion jupyter_server/files/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@

import mimetypes
from base64 import decodebytes
from typing import Awaitable
from typing import TYPE_CHECKING

from jupyter_core.utils import ensure_async
from tornado import web

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"


Expand Down
4 changes: 1 addition & 3 deletions jupyter_server/serverapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down Expand Up @@ -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:
Expand Down
6 changes: 3 additions & 3 deletions jupyter_server/services/api/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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():
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion jupyter_server/services/config/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions jupyter_server/services/contents/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
"""
Expand Down
8 changes: 4 additions & 4 deletions jupyter_server/services/events/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)


Expand Down Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions jupyter_server/services/kernels/connection/abc.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from abc import ABC, abstractmethod
from typing import Any, List
from typing import Any


class KernelWebsocketConnectionABC(ABC):
Expand All @@ -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."""
6 changes: 3 additions & 3 deletions jupyter_server/services/kernels/connection/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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

Expand Down
10 changes: 5 additions & 5 deletions jupyter_server/services/sessions/sessionmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
19 changes: 7 additions & 12 deletions jupyter_server/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from _frozen_importlib_external import _NamespacePath
from contextlib import contextmanager
from pathlib import Path
from typing import Any, Generator, NewType, Sequence
from typing import TYPE_CHECKING, Any, NewType
from urllib.parse import (
SplitResult,
quote,
Expand All @@ -32,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
Expand Down Expand Up @@ -378,17 +381,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():
Expand Down
Loading

0 comments on commit 111e104

Please sign in to comment.