Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ repos:
["traitlets>=5.13", "jupyter_core>=5.5", "jupyter_client>=8.5"]

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.0
# keep the revision in sync with the ruff version in pyproject.toml
rev: v0.14.6
hooks:
- id: ruff
types_or: [python, jupyter]
Expand Down
4 changes: 1 addition & 3 deletions examples/authorization/jupyter_nbclassic_readonly_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ class ReadOnly(Authorizer):

def is_authorized(self, handler, user, action, resource):
"""Only allows `read` operations."""
if action != "read":
return False
return True
return action == "read"


c.ServerApp.authorizer_class = ReadOnly # type:ignore[name-defined]
4 changes: 1 addition & 3 deletions examples/authorization/jupyter_nbclassic_rw_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ class ReadWriteOnly(Authorizer):

def is_authorized(self, handler, user, action, resource):
"""Only allows `read` and `write` operations."""
if action not in {"read", "write"}:
return False
return True
return action in {"read", "write"}


c.ServerApp.authorizer_class = ReadWriteOnly # type:ignore[name-defined]
4 changes: 1 addition & 3 deletions examples/authorization/jupyter_temporary_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ class TemporaryServerPersonality(Authorizer):

def is_authorized(self, handler, user, action, resource):
"""Allow everything but write on contents"""
if action == "write" and resource == "contents":
return False
return True
return not (action == "write" and resource == "contents")


c.ServerApp.authorizer_class = TemporaryServerPersonality # type:ignore[name-defined]
6 changes: 3 additions & 3 deletions jupyter_server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@
from .base.call_context import CallContext

__all__ = [
"DEFAULT_EVENTS_SCHEMA_PATH",
"DEFAULT_JUPYTER_SERVER_PORT",
"DEFAULT_STATIC_FILES_PATH",
"DEFAULT_TEMPLATE_PATH_LIST",
"DEFAULT_JUPYTER_SERVER_PORT",
"JUPYTER_SERVER_EVENTS_URI",
"DEFAULT_EVENTS_SCHEMA_PATH",
"CallContext",
"__version__",
"version_info",
"CallContext",
]
8 changes: 4 additions & 4 deletions jupyter_server/auth/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ async def inner(self, *args, **kwargs):
method = action
action = None
# no-arguments `@authorized` decorator called
return cast(FuncT, wrapper(method))
return cast("FuncT", wrapper(method))

return cast(FuncT, wrapper)
return cast("FuncT", wrapper)


def allow_unauthenticated(method: FuncT) -> FuncT:
Expand All @@ -111,7 +111,7 @@ def wrapper(self, *args, **kwargs):

setattr(wrapper, "__allow_unauthenticated", True)

return cast(FuncT, wrapper)
return cast("FuncT", wrapper)


def ws_authenticated(method: FuncT) -> FuncT:
Expand Down Expand Up @@ -139,4 +139,4 @@ def wrapper(self, *args, **kwargs):

setattr(wrapper, "__allow_unauthenticated", False)

return cast(FuncT, wrapper)
return cast("FuncT", wrapper)
6 changes: 3 additions & 3 deletions jupyter_server/auth/identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ async def _get_user(self, handler: web.RequestHandler) -> User | None:
"""Get the user."""
if getattr(handler, "_jupyter_current_user", None):
# already authenticated
return t.cast(User, handler._jupyter_current_user) # type:ignore[attr-defined]
return t.cast("User", handler._jupyter_current_user) # type:ignore[attr-defined]
_token_user: User | None | t.Awaitable[User | None] = self.get_user_token(handler)
if isinstance(_token_user, t.Awaitable):
_token_user = await _token_user
Expand Down Expand Up @@ -298,7 +298,7 @@ def update_user(
) -> User:
"""Update user information and persist the user model."""
self.check_update(user_data)
current_user = t.cast(User, handler.current_user)
current_user = t.cast("User", handler.current_user)
updated_user = self.update_user_model(current_user, user_data)
self.persist_user_model(handler)
return updated_user
Expand Down Expand Up @@ -585,7 +585,7 @@ def process_login_form(self, handler: web.RequestHandler) -> User | None:
return self.generate_anonymous_user(handler)

if self.token and self.token == typed_password:
return t.cast(User, self.user_for_token(typed_password)) # type:ignore[attr-defined]
return t.cast("User", self.user_for_token(typed_password)) # type:ignore[attr-defined]

return user

Expand Down
35 changes: 18 additions & 17 deletions jupyter_server/base/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import warnings
from collections.abc import Awaitable, Coroutine, Sequence
from http.client import responses
from logging import Logger
from typing import TYPE_CHECKING, Any, cast
from urllib.parse import urlparse

Expand Down Expand Up @@ -44,6 +43,8 @@
)

if TYPE_CHECKING:
from logging import Logger

from jupyter_client.kernelspec import KernelSpecManager
from jupyter_events import EventLogger
from jupyter_server_terminals.terminalmanager import TerminalManager
Expand Down Expand Up @@ -75,7 +76,7 @@ def json_sys_info():
def log() -> Logger:
"""Get the application log."""
if Application.initialized():
return cast(Logger, Application.instance().log)
return cast("Logger", Application.instance().log)
else:
return app_log

Expand All @@ -85,7 +86,7 @@ class AuthenticatedHandler(web.RequestHandler):

@property
def base_url(self) -> str:
return cast(str, self.settings.get("base_url", "/"))
return cast("str", self.settings.get("base_url", "/"))

@property
def content_security_policy(self) -> str:
Expand All @@ -95,7 +96,7 @@ def content_security_policy(self) -> str:
"""
if "Content-Security-Policy" in self.settings.get("headers", {}):
# user-specified, don't override
return cast(str, self.settings["headers"]["Content-Security-Policy"])
return cast("str", self.settings["headers"]["Content-Security-Policy"])

return "; ".join(
[
Expand Down Expand Up @@ -173,7 +174,7 @@ def get_current_user(self) -> str:
DeprecationWarning,
stacklevel=2,
)
return cast(str, self._jupyter_current_user)
return cast("str", self._jupyter_current_user)
# haven't called get_user in prepare, raise
raise RuntimeError(msg)

Expand Down Expand Up @@ -224,7 +225,7 @@ def login_available(self) -> bool:
whether the user is already logged in or not.

"""
return cast(bool, self.identity_provider.login_available)
return cast("bool", self.identity_provider.login_available)

@property
def authorizer(self) -> Authorizer:
Expand Down Expand Up @@ -302,34 +303,34 @@ def serverapp(self) -> ServerApp | None:
@property
def version_hash(self) -> str:
"""The version hash to use for cache hints for static files"""
return cast(str, self.settings.get("version_hash", ""))
return cast("str", self.settings.get("version_hash", ""))

@property
def mathjax_url(self) -> str:
url = cast(str, self.settings.get("mathjax_url", ""))
url = cast("str", self.settings.get("mathjax_url", ""))
if not url or url_is_absolute(url):
return url
return url_path_join(self.base_url, url)

@property
def mathjax_config(self) -> str:
return cast(str, self.settings.get("mathjax_config", "TeX-AMS-MML_HTMLorMML-full,Safe"))
return cast("str", self.settings.get("mathjax_config", "TeX-AMS-MML_HTMLorMML-full,Safe"))

@property
def default_url(self) -> str:
return cast(str, self.settings.get("default_url", ""))
return cast("str", self.settings.get("default_url", ""))

@property
def ws_url(self) -> str:
return cast(str, self.settings.get("websocket_url", ""))
return cast("str", self.settings.get("websocket_url", ""))

@property
def contents_js_source(self) -> str:
self.log.debug(
"Using contents: %s",
self.settings.get("contents_js_source", "services/contents"),
)
return cast(str, self.settings.get("contents_js_source", "services/contents"))
return cast("str", self.settings.get("contents_js_source", "services/contents"))

# ---------------------------------------------------------------
# Manager objects
Expand Down Expand Up @@ -370,7 +371,7 @@ def event_logger(self) -> EventLogger:
@property
def allow_origin(self) -> str:
"""Normal Access-Control-Allow-Origin"""
return cast(str, self.settings.get("allow_origin", ""))
return cast("str", self.settings.get("allow_origin", ""))

@property
def allow_origin_pat(self) -> str | None:
Expand All @@ -380,7 +381,7 @@ def allow_origin_pat(self) -> str | None:
@property
def allow_credentials(self) -> bool:
"""Whether to set Access-Control-Allow-Credentials"""
return cast(bool, self.settings.get("allow_credentials", False))
return cast("bool", self.settings.get("allow_credentials", False))

def set_default_headers(self) -> None:
"""Add CORS headers, if defined"""
Expand Down Expand Up @@ -774,7 +775,7 @@ def write_error(self, status_code: int, **kwargs: Any) -> None:
# backward-compatibility: traceback field is present,
# but always empty
reply["traceback"] = ""
self.log.warning("wrote error: %r", reply["message"], exc_info=True)
self.log.warning("wrote error: %r", reply["message"])
self.finish(json.dumps(reply))

def get_login_url(self) -> str:
Expand Down Expand Up @@ -1060,7 +1061,7 @@ def validate_absolute_path(self, root: str, absolute_path: str) -> str | None:
if not absolute_path:
raise web.HTTPError(404)

for root in self.root:
for root in self.root: # noqa: PLR1704
if (absolute_path + os.sep).startswith(root):
break

Expand Down Expand Up @@ -1115,7 +1116,7 @@ class FilesRedirectHandler(JupyterHandler):
"""Handler for redirecting relative URLs to the /files/ handler"""

@staticmethod
async def redirect_to_files(self: Any, path: str) -> None:
async def redirect_to_files(self: Any, path: str) -> None: # noqa: PLW0211
"""make redirect logic a reusable static method

so it can be called from other handlers.
Expand Down
25 changes: 13 additions & 12 deletions jupyter_server/extension/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

from __future__ import annotations

from logging import Logger
from typing import TYPE_CHECKING, Any, cast

from jinja2 import Template
from jinja2.exceptions import TemplateNotFound

from jupyter_server.base.handlers import FileFindHandler

if TYPE_CHECKING:
from logging import Logger

from jinja2 import Template
from traitlets.config import Config

from jupyter_server.extension.application import ExtensionApp
Expand All @@ -26,10 +27,10 @@ def get_template(self, name: str) -> Template:
"""Return the jinja template object for a given name"""
try:
env = f"{self.name}_jinja2_env" # type:ignore[attr-defined]
template = cast(Template, self.settings[env].get_template(name)) # type:ignore[attr-defined]
template = cast("Template", self.settings[env].get_template(name)) # type:ignore[attr-defined]
return template
except TemplateNotFound:
return cast(Template, super().get_template(name)) # type:ignore[misc]
return cast("Template", super().get_template(name)) # type:ignore[misc]


class ExtensionHandlerMixin:
Expand Down Expand Up @@ -64,12 +65,12 @@ def serverapp(self) -> ServerApp:
@property
def log(self) -> Logger:
if not hasattr(self, "name"):
return cast(Logger, super().log) # type:ignore[misc]
return cast("Logger", super().log) # type:ignore[misc]
# Attempt to pull the ExtensionApp's log, otherwise fall back to ServerApp.
try:
return cast(Logger, self.extensionapp.log)
return cast("Logger", self.extensionapp.log)
except AttributeError:
return cast(Logger, self.serverapp.log)
return cast("Logger", self.serverapp.log)

@property
def config(self) -> Config:
Expand All @@ -81,7 +82,7 @@ def server_config(self) -> Config:

@property
def base_url(self) -> str:
return cast(str, self.settings.get("base_url", "/"))
return cast("str", self.settings.get("base_url", "/"))

def render_template(self, name: str, **ns) -> str:
"""Override render template to handle static_paths
Expand All @@ -90,20 +91,20 @@ def render_template(self, name: str, **ns) -> str:
(e.g. default error pages)
make sure our extension-specific static_url is _not_ used.
"""
template = cast(Template, self.get_template(name)) # type:ignore[attr-defined]
template = cast("Template", self.get_template(name)) # type:ignore[attr-defined]
ns.update(self.template_namespace) # type:ignore[attr-defined]
if template.environment is self.settings["jinja2_env"]:
# default template environment, use default static_url
ns["static_url"] = super().static_url # type:ignore[misc]
return cast(str, template.render(**ns))
return cast("str", template.render(**ns))

@property
def static_url_prefix(self) -> str:
return self.extensionapp.static_url_prefix

@property
def static_path(self) -> str:
return cast(str, self.settings[f"{self.name}_static_paths"])
return cast("str", self.settings[f"{self.name}_static_paths"])

def static_url(self, path: str, include_host: bool | None = None, **kwargs: Any) -> str:
"""Returns a static URL for the given relative static file path.
Expand Down Expand Up @@ -151,4 +152,4 @@ def static_url(self, path: str, include_host: bool | None = None, **kwargs: Any)
"static_url_prefix": self.static_url_prefix,
}

return base + cast(str, get_url(settings, path, **kwargs))
return base + cast("str", get_url(settings, path, **kwargs))
2 changes: 1 addition & 1 deletion jupyter_server/gateway/gateway_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ def gateway_enabled(self):
return bool(self.url is not None and len(self.url) > 0)

# Ensure KERNEL_LAUNCH_TIMEOUT has a default value.
KERNEL_LAUNCH_TIMEOUT = int(os.environ.get("KERNEL_LAUNCH_TIMEOUT", 40))
KERNEL_LAUNCH_TIMEOUT = int(os.environ.get("KERNEL_LAUNCH_TIMEOUT", "40"))

_connection_args: dict[str, ty.Any] # initialized on first use

Expand Down
2 changes: 1 addition & 1 deletion jupyter_server/gateway/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ def stop(self) -> None:
msgs.append(msg["msg_type"])
if self.channel_name == "iopub" and "shutdown_reply" in msgs:
return
if len(msgs):
if msgs:
self.log.warning(
f"Stopping channel '{self.channel_name}' with {len(msgs)} unprocessed non-status messages: {msgs}."
)
Expand Down
2 changes: 1 addition & 1 deletion jupyter_server/kernelspecs/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ async def get(self, kernel_name, path, include_body=True):
"""Get a kernelspec resource."""
ksm = self.kernel_spec_manager
if path.lower().endswith(".png"):
self.set_header("Cache-Control", f"max-age={60*60*24*30}")
self.set_header("Cache-Control", f"max-age={60 * 60 * 24 * 30}")
ksm = self.kernel_spec_manager
if hasattr(ksm, "get_kernel_spec_resource"):
# If the kernel spec manager defines a method to get kernelspec resources,
Expand Down
2 changes: 1 addition & 1 deletion jupyter_server/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def _scrub_uri(uri: str, extra_param_keys=None) -> str:
parts = parsed.query.split("&")
changed = False
for i, s in enumerate(parts):
key, sep, value = s.partition("=")
key, sep, _value = s.partition("=")
for substring in scrub_param_keys:
if substring in key:
parts[i] = f"{key}{sep}[secret]"
Expand Down
2 changes: 1 addition & 1 deletion jupyter_server/nbconvert/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ async def get(self, format, path):
# give its path to nbconvert.
if hasattr(self.contents_manager, "_get_os_path"):
os_path = self.contents_manager._get_os_path(path)
ext_resources_dir, basename = os.path.split(os_path)
ext_resources_dir, _basename = os.path.split(os_path)
else:
ext_resources_dir = None

Expand Down
Loading
Loading