Skip to content

Commit 25b8011

Browse files
authored
Backport PR #779 on branch 1.x (Add mypy check) (#831)
(cherry picked from commit 3e64fa5)
1 parent 999b056 commit 25b8011

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+262
-135
lines changed

.pre-commit-config.yaml

+7
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ repos:
2828
files: \.py$
2929
args: [--profile=black]
3030

31+
- repo: https://github.com/pre-commit/mirrors-mypy
32+
rev: v0.942
33+
hooks:
34+
- id: mypy
35+
exclude: examples/simple/setup.py
36+
additional_dependencies: [types-requests]
37+
3138
- repo: https://github.com/pre-commit/mirrors-prettier
3239
rev: v2.6.2
3340
hooks:

docs/source/conf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import shutil
1717
import sys
1818

19-
from pkg_resources import parse_version
19+
from packaging.version import parse as parse_version
2020

2121
HERE = osp.abspath(osp.dirname(__file__))
2222

examples/authorization/jupyter_nbclassic_readonly_config.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ def is_authorized(self, handler, user, action, resource):
1111
return True
1212

1313

14-
c.ServerApp.authorizer_class = ReadOnly
14+
c.ServerApp.authorizer_class = ReadOnly # type:ignore[name-defined]

examples/authorization/jupyter_nbclassic_rw_config.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ def is_authorized(self, handler, user, action, resource):
1111
return True
1212

1313

14-
c.ServerApp.authorizer_class = ReadWriteOnly
14+
c.ServerApp.authorizer_class = ReadWriteOnly # type:ignore[name-defined]

examples/authorization/jupyter_temporary_config.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ def is_authorized(self, handler, user, action, resource):
1111
return True
1212

1313

14-
c.ServerApp.authorizer_class = TemporaryServerPersonality
14+
c.ServerApp.authorizer_class = TemporaryServerPersonality # type:ignore[name-defined]

examples/simple/jupyter_server_config.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@
33
# Application(SingletonConfigurable) configuration
44
# ------------------------------------------------------------------------------
55
# The date format used by logging formatters for %(asctime)s
6-
c.Application.log_datefmt = "%Y-%m-%d %H:%M:%S Simple_Extensions_Example"
6+
c.Application.log_datefmt = ( # type:ignore[name-defined]
7+
"%Y-%m-%d %H:%M:%S Simple_Extensions_Example"
8+
)
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
c.SimpleApp11.ignore_js = True
1+
c.SimpleApp11.ignore_js = True # type:ignore[name-defined]
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
c.SimpleApp1.configA = "ConfigA from file"
2-
c.SimpleApp1.configB = "ConfigB from file"
3-
c.SimpleApp1.configC = "ConfigC from file"
4-
c.SimpleApp1.configD = "ConfigD from file"
1+
c.SimpleApp1.configA = "ConfigA from file" # type:ignore[name-defined]
2+
c.SimpleApp1.configB = "ConfigB from file" # type:ignore[name-defined]
3+
c.SimpleApp1.configC = "ConfigC from file" # type:ignore[name-defined]
4+
c.SimpleApp1.configD = "ConfigD from file" # type:ignore[name-defined]
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
c.SimpleApp2.configD = "ConfigD from file"
1+
c.SimpleApp2.configD = "ConfigD from file" # type:ignore[name-defined]

jupyter_server/auth/login.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def post(self):
8383
elif self.token and self.token == typed_password:
8484
self.set_login_cookie(self, uuid.uuid4().hex)
8585
if new_password and self.settings.get("allow_password_change"):
86-
config_dir = self.settings.get("config_dir")
86+
config_dir = self.settings.get("config_dir", "")
8787
config_file = os.path.join(config_dir, "jupyter_server_config.json")
8888
set_password(new_password, config_file=config_file)
8989
self.log.info("Wrote hashed password to %s" % config_file)

jupyter_server/auth/security.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ def passwd(passphrase=None, algorithm="argon2"):
6464
time_cost=10,
6565
parallelism=8,
6666
)
67-
h = ph.hash(passphrase)
67+
h_ph = ph.hash(passphrase)
6868

69-
return ":".join((algorithm, h))
69+
return ":".join((algorithm, h_ph))
7070

7171
h = hashlib.new(algorithm)
7272
salt = ("%0" + str(salt_len) + "x") % random.getrandbits(4 * salt_len)

jupyter_server/auth/utils.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ def get_regex_to_resource_map():
3939
from jupyter_server.serverapp import JUPYTER_SERVICE_HANDLERS
4040

4141
modules = []
42-
for mod in JUPYTER_SERVICE_HANDLERS.values():
43-
if mod:
44-
modules.extend(mod)
42+
for mod_name in JUPYTER_SERVICE_HANDLERS.values():
43+
if mod_name:
44+
modules.extend(mod_name)
4545
resource_map = {}
4646
for handler_module in modules:
4747
mod = importlib.import_module(handler_module)

jupyter_server/base/handlers.py

+16-7
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def force_clear_cookie(self, name, path="/", domain=None):
114114
name = escape.native_str(name)
115115
expires = datetime.datetime.utcnow() - datetime.timedelta(days=365)
116116

117-
morsel = Morsel()
117+
morsel: Morsel = Morsel()
118118
morsel.set(name, "", '""')
119119
morsel["expires"] = httputil.format_timestamp(expires)
120120
morsel["path"] = path
@@ -257,7 +257,7 @@ def mathjax_config(self):
257257
return self.settings.get("mathjax_config", "TeX-AMS-MML_HTMLorMML-full,Safe")
258258

259259
@property
260-
def base_url(self):
260+
def base_url(self) -> str:
261261
return self.settings.get("base_url", "/")
262262

263263
@property
@@ -492,7 +492,9 @@ def check_host(self):
492492
return True
493493

494494
# Remove port (e.g. ':8888') from host
495-
host = re.match(r"^(.*?)(:\d+)?$", self.request.host).group(1)
495+
match = re.match(r"^(.*?)(:\d+)?$", self.request.host)
496+
assert match is not None
497+
host = match.group(1)
496498

497499
# Browsers format IPv6 addresses like [::1]; we need to remove the []
498500
if host.startswith("[") and host.endswith("]"):
@@ -583,7 +585,7 @@ def write_error(self, status_code, **kwargs):
583585
exc_info = kwargs.get("exc_info")
584586
message = ""
585587
status_message = responses.get(status_code, "Unknown HTTP Error")
586-
exception = "(unknown)"
588+
587589
if exc_info:
588590
exception = exc_info[1]
589591
# get the custom message, if defined
@@ -596,6 +598,8 @@ def write_error(self, status_code, **kwargs):
596598
reason = getattr(exception, "reason", "")
597599
if reason:
598600
status_message = reason
601+
else:
602+
exception = "(unknown)"
599603

600604
# build template namespace
601605
ns = dict(
@@ -618,6 +622,8 @@ def write_error(self, status_code, **kwargs):
618622
class APIHandler(JupyterHandler):
619623
"""Base class for API handlers"""
620624

625+
_user_cache: dict
626+
621627
def prepare(self):
622628
if not self.check_origin():
623629
raise web.HTTPError(404)
@@ -627,7 +633,7 @@ def write_error(self, status_code, **kwargs):
627633
"""APIHandler errors are JSON, not human pages"""
628634
self.set_header("Content-Type", "application/json")
629635
message = responses.get(status_code, "Unknown HTTP Error")
630-
reply = {
636+
reply: dict = {
631637
"message": message,
632638
}
633639
exc_info = kwargs.get("exc_info")
@@ -749,13 +755,14 @@ def head(self, path):
749755

750756
@web.authenticated
751757
def get(self, path):
752-
if os.path.splitext(path)[1] == ".ipynb" or self.get_argument("download", False):
758+
if os.path.splitext(path)[1] == ".ipynb" or self.get_argument("download", None):
753759
name = path.rsplit("/", 1)[-1]
754760
self.set_attachment_header(name)
755761

756762
return web.StaticFileHandler.get(self, path)
757763

758764
def get_content_type(self):
765+
assert self.absolute_path is not None
759766
path = self.absolute_path.strip("/")
760767
if "/" in path:
761768
_, name = path.rsplit("/", 1)
@@ -834,7 +841,8 @@ class FileFindHandler(JupyterHandler, web.StaticFileHandler):
834841
"""subclass of StaticFileHandler for serving files from a search path"""
835842

836843
# cache search results, don't search for files more than once
837-
_static_paths = {}
844+
_static_paths: dict = {}
845+
root: tuple
838846

839847
def set_headers(self):
840848
super().set_headers()
@@ -898,6 +906,7 @@ class TrailingSlashHandler(web.RequestHandler):
898906
"""
899907

900908
def get(self):
909+
assert self.request.uri is not None
901910
path, *rest = self.request.uri.partition("?")
902911
# trim trailing *and* leading /
903912
# to avoid misinterpreting repeated '//'

jupyter_server/base/zmqhandlers.py

+15-8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import re
66
import struct
77
import sys
8+
from typing import Optional, no_type_check
89
from urllib.parse import urlparse
910

1011
import tornado
@@ -17,6 +18,7 @@
1718
from jupyter_client.jsonutil import extract_dates
1819
from jupyter_client.session import Session
1920
from tornado import ioloop, web
21+
from tornado.iostream import IOStream
2022
from tornado.websocket import WebSocketHandler
2123

2224
from .handlers import JupyterHandler
@@ -91,7 +93,7 @@ def serialize_msg_to_ws_v1(msg_or_list, channel, pack=None):
9193
else:
9294
msg_list = msg_or_list
9395
channel = channel.encode("utf-8")
94-
offsets = []
96+
offsets: list = []
9597
offsets.append(8 * (1 + 1 + len(msg_list) + 1))
9698
offsets.append(len(channel) + offsets[-1])
9799
for msg in msg_list:
@@ -120,27 +122,30 @@ class WebSocketMixin:
120122
"""Mixin for common websocket options"""
121123

122124
ping_callback = None
123-
last_ping = 0
124-
last_pong = 0
125-
stream = None
125+
last_ping = 0.0
126+
last_pong = 0.0
127+
stream = None # type: Optional[IOStream]
126128

127129
@property
128130
def ping_interval(self):
129131
"""The interval for websocket keep-alive pings.
130132
131133
Set ws_ping_interval = 0 to disable pings.
132134
"""
133-
return self.settings.get("ws_ping_interval", WS_PING_INTERVAL)
135+
return self.settings.get("ws_ping_interval", WS_PING_INTERVAL) # type:ignore[attr-defined]
134136

135137
@property
136138
def ping_timeout(self):
137139
"""If no ping is received in this many milliseconds,
138140
close the websocket connection (VPNs, etc. can fail to cleanly close ws connections).
139141
Default is max of 3 pings or 30 seconds.
140142
"""
141-
return self.settings.get("ws_ping_timeout", max(3 * self.ping_interval, WS_PING_INTERVAL))
143+
return self.settings.get( # type:ignore[attr-defined]
144+
"ws_ping_timeout", max(3 * self.ping_interval, WS_PING_INTERVAL)
145+
)
142146

143-
def check_origin(self, origin=None):
147+
@no_type_check
148+
def check_origin(self, origin: Optional[str] = None) -> bool:
144149
"""Check Origin == Host or Access-Control-Allow-Origin.
145150
146151
Tornado >= 4 calls this method automatically, raising 403 if it returns False.
@@ -186,6 +191,7 @@ def clear_cookie(self, *args, **kwargs):
186191
"""meaningless for websockets"""
187192
pass
188193

194+
@no_type_check
189195
def open(self, *args, **kwargs):
190196
self.log.debug("Opening websocket %s", self.request.path)
191197

@@ -201,6 +207,7 @@ def open(self, *args, **kwargs):
201207
self.ping_callback.start()
202208
return super().open(*args, **kwargs)
203209

210+
@no_type_check
204211
def send_ping(self):
205212
"""send a ping to keep the websocket alive"""
206213
if self.ws_connection is None and self.ping_callback is not None:
@@ -322,7 +329,7 @@ def pre_get(self):
322329
if not self.authorizer.is_authorized(self, user, "execute", "kernels"):
323330
raise web.HTTPError(403)
324331

325-
if self.get_argument("session_id", False):
332+
if self.get_argument("session_id", None):
326333
self.session.session = self.get_argument("session_id")
327334
else:
328335
self.log.warning("No session ID specified")

jupyter_server/config_manager.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def get(self, section_name, include_root=True):
9595
section_name,
9696
"\n\t".join(paths),
9797
)
98-
data = {}
98+
data: dict = {}
9999
for path in paths:
100100
if os.path.isfile(path):
101101
with open(path, encoding="utf-8") as f:

jupyter_server/extension/application.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ class method. This method can be set as a entry_point in
137137
# A useful class property that subclasses can override to
138138
# configure the underlying Jupyter Server when this extension
139139
# is launched directly (using its `launch_instance` method).
140-
serverapp_config = {}
140+
serverapp_config: dict = {}
141141

142142
# Some subclasses will likely override this trait to flip
143143
# the default value to False if they don't offer a browser
@@ -165,7 +165,7 @@ def config_file_paths(self):
165165
# file, jupyter_{name}_config.
166166
# This should also match the jupyter subcommand used to launch
167167
# this extension from the CLI, e.g. `jupyter {name}`.
168-
name = None
168+
name = "ExtensionApp"
169169

170170
@classmethod
171171
def get_extension_package(cls):
@@ -318,7 +318,7 @@ def _prepare_handlers(self):
318318
handler = handler_items[1]
319319

320320
# Get handler kwargs, if given
321-
kwargs = {}
321+
kwargs: dict = {}
322322
if issubclass(handler, ExtensionHandlerMixin):
323323
kwargs["name"] = self.name
324324

0 commit comments

Comments
 (0)