From ee3f30d0055620e8c1b8de3587efca0e82526c19 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 6 Dec 2022 20:56:43 -0600 Subject: [PATCH 1/4] adopt ruff and address lint --- .github/dependabot.yml | 6 +- .github/workflows/ci.yml | 12 ++- .pre-commit-config.yaml | 84 +++------------ docs/conf.py | 2 +- examples/embedding/inprocess_qtconsole.py | 5 +- examples/embedding/inprocess_terminal.py | 5 +- examples/embedding/internal_ipkernel.py | 2 +- examples/embedding/ipkernel_wxapp.py | 1 + ipykernel/_eventloop_macos.py | 2 +- ipykernel/comm/__init__.py | 2 +- ipykernel/connect.py | 2 +- ipykernel/embed.py | 1 + ipykernel/eventloops.py | 2 +- ipykernel/gui/gtk3embed.py | 1 + ipykernel/gui/gtkembed.py | 1 + ipykernel/heartbeat.py | 3 +- ipykernel/inprocess/channels.py | 1 + ipykernel/inprocess/client.py | 1 + ipykernel/inprocess/ipkernel.py | 1 + ipykernel/inprocess/socket.py | 1 + ipykernel/inprocess/tests/test_kernel.py | 8 +- .../inprocess/tests/test_kernelmanager.py | 1 + ipykernel/ipkernel.py | 13 +-- ipykernel/kernelapp.py | 7 +- ipykernel/serialize.py | 9 +- ipykernel/tests/__init__.py | 4 +- ipykernel/tests/conftest.py | 17 +-- ipykernel/tests/test_async.py | 4 + ipykernel/tests/test_comm.py | 2 +- ipykernel/tests/test_connect.py | 6 +- ipykernel/tests/test_debugger.py | 10 +- ipykernel/tests/test_embed_kernel.py | 4 +- ipykernel/tests/test_eventloop.py | 6 +- ipykernel/tests/test_heartbeat.py | 2 + ipykernel/tests/test_io.py | 6 +- ipykernel/tests/test_ipkernel_direct.py | 41 +++---- ipykernel/tests/test_jsonutil.py | 4 +- ipykernel/tests/test_kernelapp.py | 4 +- ipykernel/tests/test_kernelspec.py | 8 +- ipykernel/tests/test_message_spec.py | 25 ++--- ipykernel/tests/test_zmq_shell.py | 4 +- ipykernel/tests/utils.py | 9 +- ipykernel/zmqshell.py | 1 + pyproject.toml | 100 ++++++++++++++---- 44 files changed, 222 insertions(+), 208 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f10a5bc1f..cbd920f6b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,8 +1,10 @@ version: 2 updates: - # Set update schedule for GitHub Actions - package-ecosystem: "github-actions" directory: "/" schedule: - # Check for updates to GitHub Actions every weekday + interval: "weekly" + - package-ecosystem: "pip" + directory: "/" + schedule: interval: "weekly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf533e2da..759a13cc8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,12 +66,18 @@ jobs: cd $HOME python -m ipykernel_launcher --help - pre_commit: + test_lint: + name: Test Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - - uses: jupyterlab/maintainer-tools/.github/actions/pre-commit@v1 + - name: Run Linters + run: | + hatch run typing:test + hatch run lint:style + pipx run 'validate-pyproject[all]' pyproject.toml + pipx run doc8 --max-line-length=200 check_release: runs-on: ubuntu-latest @@ -177,7 +183,7 @@ jobs: - test_docs - test_without_debugpy - test_miniumum_versions - - pre_commit + - test_lint - test_prereleases - check_release - link_check diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1091dafa5..d048f624f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,6 @@ +ci: + autoupdate_schedule: monthly + repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 @@ -10,88 +13,27 @@ repos: - id: check-case-conflict - id: check-toml - id: check-yaml - - id: debug-statements - exclude: ipykernel/kernelapp.py - id: forbid-new-submodules - id: check-builtin-literals - id: trailing-whitespace - - repo: https://github.com/psf/black - rev: 22.10.0 - hooks: - - id: black - args: ["--line-length", "100"] - - - repo: https://github.com/Carreau/velin - rev: 0.0.12 - hooks: - - id: velin - args: ["ipykernel"] - - - repo: https://github.com/PyCQA/isort - rev: 5.10.1 - hooks: - - id: isort - files: \.py$ - args: [--profile=black] - - - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.10.1 + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.19.2 hooks: - - id: validate-pyproject - stages: [manual] + - id: check-github-workflows - repo: https://github.com/executablebooks/mdformat rev: 0.7.16 hooks: - id: mdformat - additional_dependencies: - [mdformat-gfm, mdformat-frontmatter, mdformat-footnote] - - repo: https://github.com/asottile/pyupgrade - rev: v3.3.0 - hooks: - - id: pyupgrade - args: [--py38-plus] - - - repo: https://github.com/PyCQA/doc8 - rev: v1.0.0 - hooks: - - id: doc8 - args: [--max-line-length=200] - stages: [manual] - - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.991 - hooks: - - id: mypy - exclude: "ipykernel.*tests" - args: ["--config-file", "pyproject.toml"] - additional_dependencies: - [tornado, jupyter_client, pytest, traitlets, jupyter_core] - stages: [manual] - - - repo: https://github.com/john-hen/Flake8-pyproject - rev: 1.2.2 - hooks: - - id: Flake8-pyproject - alias: flake8 - additional_dependencies: - ["flake8-bugbear==22.6.22", "flake8-implicit-str-concat==0.2.0"] - stages: [manual] - - - repo: https://github.com/pre-commit/mirrors-eslint - rev: v8.29.0 + - repo: https://github.com/psf/black + rev: 22.10.0 hooks: - - id: eslint - stages: [manual] + - id: black - - repo: https://github.com/sirosen/check-jsonschema - rev: 0.19.2 + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: v0.0.165 hooks: - - id: check-jsonschema - name: "Check GitHub Workflows" - files: ^\.github/workflows/ - types: [yaml] - args: ["--schemafile", "https://json.schemastore.org/github-workflow"] - stages: [manual] + - id: ruff + args: ["--fix"] diff --git a/docs/conf.py b/docs/conf.py index 4a1a6fa5f..9a47e1769 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -66,7 +66,7 @@ here = os.path.dirname(__file__) version_py = os.path.join(here, os.pardir, "ipykernel", "_version.py") with open(version_py) as f: - exec(compile(f.read(), version_py, "exec"), version_ns) + exec(compile(f.read(), version_py, "exec"), version_ns) # noqa # The short X.Y version. version = "%i.%i" % version_ns["version_info"][:2] diff --git a/examples/embedding/inprocess_qtconsole.py b/examples/embedding/inprocess_qtconsole.py index 55e514343..3ac4ae485 100644 --- a/examples/embedding/inprocess_qtconsole.py +++ b/examples/embedding/inprocess_qtconsole.py @@ -30,10 +30,7 @@ def init_asyncio_patch(): import asyncio try: - from asyncio import ( - WindowsProactorEventLoopPolicy, - WindowsSelectorEventLoopPolicy, - ) + from asyncio import WindowsProactorEventLoopPolicy, WindowsSelectorEventLoopPolicy except ImportError: pass # not affected diff --git a/examples/embedding/inprocess_terminal.py b/examples/embedding/inprocess_terminal.py index 4121619d0..2f8d5453d 100644 --- a/examples/embedding/inprocess_terminal.py +++ b/examples/embedding/inprocess_terminal.py @@ -30,10 +30,7 @@ def init_asyncio_patch(): import asyncio try: - from asyncio import ( - WindowsProactorEventLoopPolicy, - WindowsSelectorEventLoopPolicy, - ) + from asyncio import WindowsProactorEventLoopPolicy, WindowsSelectorEventLoopPolicy except ImportError: pass # not affected diff --git a/examples/embedding/internal_ipkernel.py b/examples/embedding/internal_ipkernel.py index 7d126c17f..9aa2783a0 100644 --- a/examples/embedding/internal_ipkernel.py +++ b/examples/embedding/internal_ipkernel.py @@ -19,7 +19,7 @@ def mpl_kernel(gui): [ "python", "--matplotlib=%s" % gui, - #'--log-level=10' # noqa + #'--log-level=10' ] ) return kernel diff --git a/examples/embedding/ipkernel_wxapp.py b/examples/embedding/ipkernel_wxapp.py index 2caad5be2..fce802b64 100755 --- a/examples/embedding/ipkernel_wxapp.py +++ b/examples/embedding/ipkernel_wxapp.py @@ -24,6 +24,7 @@ import wx from internal_ipkernel import InternalIPKernel + # ----------------------------------------------------------------------------- # Functions and classes # ----------------------------------------------------------------------------- diff --git a/ipykernel/_eventloop_macos.py b/ipykernel/_eventloop_macos.py index 3a6692fce..f07ce6ffc 100644 --- a/ipykernel/_eventloop_macos.py +++ b/ipykernel/_eventloop_macos.py @@ -75,7 +75,7 @@ def C(classname): CFRunLoopAddTimer.restype = None CFRunLoopAddTimer.argtypes = [void_p, void_p, void_p] -kCFRunLoopCommonModes = void_p.in_dll(CoreFoundation, "kCFRunLoopCommonModes") +kCFRunLoopCommonModes = void_p.in_dll(CoreFoundation, "kCFRunLoopCommonModes") # noqa def _NSApp(): diff --git a/ipykernel/comm/__init__.py b/ipykernel/comm/__init__.py index 36b419d5e..d56cbe308 100644 --- a/ipykernel/comm/__init__.py +++ b/ipykernel/comm/__init__.py @@ -1,4 +1,4 @@ __all__ = ["Comm", "CommManager"] -from .comm import Comm # noqa +from .comm import Comm from .manager import CommManager diff --git a/ipykernel/connect.py b/ipykernel/connect.py index 6baae872a..f76091674 100644 --- a/ipykernel/connect.py +++ b/ipykernel/connect.py @@ -120,7 +120,7 @@ def connect_qtconsole(connection_file=None, argv=None): stdout=PIPE, stderr=PIPE, close_fds=(sys.platform != "win32"), - **kwargs + **kwargs, ) diff --git a/ipykernel/embed.py b/ipykernel/embed.py index 53c11119b..72f5d6c1b 100644 --- a/ipykernel/embed.py +++ b/ipykernel/embed.py @@ -10,6 +10,7 @@ from .kernelapp import IPKernelApp + # ----------------------------------------------------------------------------- # Code # ----------------------------------------------------------------------------- diff --git a/ipykernel/eventloops.py b/ipykernel/eventloops.py index 0329926a3..b2a312d90 100644 --- a/ipykernel/eventloops.py +++ b/ipykernel/eventloops.py @@ -9,7 +9,7 @@ from functools import partial import zmq -from packaging.version import Version as V +from packaging.version import Version as V # noqa from traitlets.config.application import Application diff --git a/ipykernel/gui/gtk3embed.py b/ipykernel/gui/gtk3embed.py index 42c7e181f..eb190c845 100644 --- a/ipykernel/gui/gtk3embed.py +++ b/ipykernel/gui/gtk3embed.py @@ -23,6 +23,7 @@ gi.require_version("Gtk", "3.0") from gi.repository import GObject, Gtk + # ----------------------------------------------------------------------------- # Classes and functions # ----------------------------------------------------------------------------- diff --git a/ipykernel/gui/gtkembed.py b/ipykernel/gui/gtkembed.py index a97a62a06..e7a19c3b8 100644 --- a/ipykernel/gui/gtkembed.py +++ b/ipykernel/gui/gtkembed.py @@ -20,6 +20,7 @@ import gobject import gtk + # ----------------------------------------------------------------------------- # Classes and functions # ----------------------------------------------------------------------------- diff --git a/ipykernel/heartbeat.py b/ipykernel/heartbeat.py index 091cdd372..1835c0d67 100644 --- a/ipykernel/heartbeat.py +++ b/ipykernel/heartbeat.py @@ -20,13 +20,14 @@ import zmq from jupyter_client.localinterfaces import localhost + # ----------------------------------------------------------------------------- # Code # ----------------------------------------------------------------------------- class Heartbeat(Thread): - "A simple ping-pong style heartbeat that runs in a thread." + """A simple ping-pong style heartbeat that runs in a thread.""" def __init__(self, context, addr=None): if addr is None: diff --git a/ipykernel/inprocess/channels.py b/ipykernel/inprocess/channels.py index 84629ff5d..6fd8ede62 100644 --- a/ipykernel/inprocess/channels.py +++ b/ipykernel/inprocess/channels.py @@ -7,6 +7,7 @@ from jupyter_client.channelsabc import HBChannelABC + # ----------------------------------------------------------------------------- # Channel classes # ----------------------------------------------------------------------------- diff --git a/ipykernel/inprocess/client.py b/ipykernel/inprocess/client.py index d6bfc9a5f..9377fd1d3 100644 --- a/ipykernel/inprocess/client.py +++ b/ipykernel/inprocess/client.py @@ -27,6 +27,7 @@ # Local imports from .channels import InProcessChannel, InProcessHBChannel + # ----------------------------------------------------------------------------- # Main kernel Client class # ----------------------------------------------------------------------------- diff --git a/ipykernel/inprocess/ipkernel.py b/ipykernel/inprocess/ipkernel.py index f185ddc35..074a2588e 100644 --- a/ipykernel/inprocess/ipkernel.py +++ b/ipykernel/inprocess/ipkernel.py @@ -18,6 +18,7 @@ from .constants import INPROCESS_KEY from .socket import DummySocket + # ----------------------------------------------------------------------------- # Main kernel class # ----------------------------------------------------------------------------- diff --git a/ipykernel/inprocess/socket.py b/ipykernel/inprocess/socket.py index 477c36a47..e33e1d251 100644 --- a/ipykernel/inprocess/socket.py +++ b/ipykernel/inprocess/socket.py @@ -8,6 +8,7 @@ import zmq from traitlets import HasTraits, Instance, Int + # ----------------------------------------------------------------------------- # Dummy socket class # ----------------------------------------------------------------------------- diff --git a/ipykernel/inprocess/tests/test_kernel.py b/ipykernel/inprocess/tests/test_kernel.py index 47dab33d2..947b5823e 100644 --- a/ipykernel/inprocess/tests/test_kernel.py +++ b/ipykernel/inprocess/tests/test_kernel.py @@ -31,10 +31,10 @@ def _inject_cell_id(_self, *args, **kwargs): @contextmanager def patch_cell_id(): try: - Session.msg = _inject_cell_id + Session.msg = _inject_cell_id # type:ignore yield finally: - Session.msg = orig_msg + Session.msg = orig_msg # type:ignore @pytest.fixture() @@ -107,10 +107,10 @@ def test_capfd(kc): def test_getpass_stream(kc): - "Tests that kernel getpass accept the stream parameter" + """Tests that kernel getpass accept the stream parameter""" kernel = InProcessKernel() kernel._allow_stdin = True - kernel._input_request = lambda *args, **kwargs: None + kernel._input_request = lambda *args, **kwargs: None # type:ignore kernel.getpass(stream="non empty") diff --git a/ipykernel/inprocess/tests/test_kernelmanager.py b/ipykernel/inprocess/tests/test_kernelmanager.py index 850f543ce..fb34c76b0 100644 --- a/ipykernel/inprocess/tests/test_kernelmanager.py +++ b/ipykernel/inprocess/tests/test_kernelmanager.py @@ -5,6 +5,7 @@ from ipykernel.inprocess.manager import InProcessKernelManager + # ----------------------------------------------------------------------------- # Test case # ----------------------------------------------------------------------------- diff --git a/ipykernel/ipkernel.py b/ipykernel/ipkernel.py index 3bacf0a3b..f86f30451 100644 --- a/ipykernel/ipkernel.py +++ b/ipykernel/ipkernel.py @@ -12,16 +12,7 @@ import comm from IPython.core import release from IPython.utils.tokenutil import line_at_cursor, token_at_cursor -from traitlets import ( - Any, - Bool, - HasTraits, - Instance, - List, - Type, - observe, - observe_compat, -) +from traitlets import Any, Bool, HasTraits, Instance, List, Type, observe, observe_compat from zmq.eventloop.zmqstream import ZMQStream from .comm.comm import BaseComm @@ -629,7 +620,7 @@ def do_apply(self, content, bufs, msg_id, reply_metadata): working.update(ns) code = f"{resultname} = {fname}(*{argname},**{kwargname})" try: - exec(code, shell.user_global_ns, shell.user_ns) + exec(code, shell.user_global_ns, shell.user_ns) # noqa result = working.get(resultname) finally: for key in ns: diff --git a/ipykernel/kernelapp.py b/ipykernel/kernelapp.py index 8b4a5ca55..3f4aa708a 100644 --- a/ipykernel/kernelapp.py +++ b/ipykernel/kernelapp.py @@ -625,10 +625,7 @@ def _init_asyncio_patch(self): import asyncio try: - from asyncio import ( - WindowsProactorEventLoopPolicy, - WindowsSelectorEventLoopPolicy, - ) + from asyncio import WindowsProactorEventLoopPolicy, WindowsSelectorEventLoopPolicy except ImportError: pass # not affected @@ -644,7 +641,7 @@ def init_pdb(self): With the non-interruptible version, stopping pdb() locks up the kernel in a non-recoverable state. """ - import pdb + import pdb # noqa from IPython.core import debugger diff --git a/ipykernel/serialize.py b/ipykernel/serialize.py index 6b77d8536..cd7fa51ac 100644 --- a/ipykernel/serialize.py +++ b/ipykernel/serialize.py @@ -29,18 +29,19 @@ except ImportError: # Deprecated since ipykernel 4.3.0 from ipykernel.pickleutil import ( + PICKLE_PROTOCOL, + CannedObject, can, - uncan, can_sequence, - uncan_sequence, - CannedObject, istype, sequence_types, - PICKLE_PROTOCOL, + uncan, + uncan_sequence, ) from jupyter_client.session import MAX_BYTES, MAX_ITEMS + # ----------------------------------------------------------------------------- # Serialization Functions # ----------------------------------------------------------------------------- diff --git a/ipykernel/tests/__init__.py b/ipykernel/tests/__init__.py index d2606e108..013114bd1 100644 --- a/ipykernel/tests/__init__.py +++ b/ipykernel/tests/__init__.py @@ -12,7 +12,7 @@ pjoin = os.path.join tmp = None -patchers = [] +patchers: list = [] def setup(): @@ -41,7 +41,7 @@ def teardown(): p.stop() try: - shutil.rmtree(tmp) + shutil.rmtree(tmp) # type:ignore except OSError: # no such file pass diff --git a/ipykernel/tests/conftest.py b/ipykernel/tests/conftest.py index 5e1e192ad..45341cef6 100644 --- a/ipykernel/tests/conftest.py +++ b/ipykernel/tests/conftest.py @@ -1,6 +1,8 @@ import asyncio import logging import os +from typing import no_type_check +from unittest.mock import MagicMock import pytest import zmq @@ -16,7 +18,7 @@ import resource except ImportError: # Windows - resource = None + resource = None # type:ignore # Handle resource limit @@ -36,7 +38,7 @@ # Enforce selector event loop on Windows. if os.name == "nt": - asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # type:ignore class KernelMixin: @@ -68,12 +70,14 @@ def destroy(self): socket.close() self.context.destroy() + @no_type_check async def test_shell_message(self, *args, **kwargs): msg_list = self._prep_msg(*args, **kwargs) await self.dispatch_shell(msg_list) self.shell_stream.flush() return await self._wait_for_msg() + @no_type_check async def test_control_message(self, *args, **kwargs): msg_list = self._prep_msg(*args, **kwargs) await self.process_control(msg_list) @@ -85,8 +89,8 @@ def _on_send(self, msg, *args, **kwargs): def _prep_msg(self, *args, **kwargs): self._reply = None - msg = self.session.msg(*args, **kwargs) - msg = self.session.serialize(msg) + raw_msg = self.session.msg(*args, **kwargs) + msg = self.session.serialize(raw_msg) return [zmq.Message(m) for m in msg] async def _wait_for_msg(self): @@ -100,7 +104,7 @@ def _send_interupt_children(self): pass -class MockKernel(KernelMixin, Kernel): +class MockKernel(KernelMixin, Kernel): # type:ignore implementation = "test" implementation_version = "1.0" language = "no-op" @@ -114,6 +118,7 @@ class MockKernel(KernelMixin, Kernel): def __init__(self, *args, **kwargs): self._initialize() + self.shell = MagicMock() super().__init__(*args, **kwargs) def do_execute( @@ -132,7 +137,7 @@ def do_execute( } -class MockIPyKernel(KernelMixin, IPythonKernel): +class MockIPyKernel(KernelMixin, IPythonKernel): # type:ignore def __init__(self, *args, **kwargs): self._initialize() super().__init__(*args, **kwargs) diff --git a/ipykernel/tests/test_async.py b/ipykernel/tests/test_async.py index 81a8e3d95..c58a24d9d 100644 --- a/ipykernel/tests/test_async.py +++ b/ipykernel/tests/test_async.py @@ -16,6 +16,8 @@ def setup_function(): def teardown_function(): + assert KC is not None + assert KM is not None KC.stop_channels() KM.shutdown_kernel(now=True) @@ -28,6 +30,8 @@ def test_async_await(): @pytest.mark.parametrize("asynclib", ["asyncio", "trio", "curio"]) def test_async_interrupt(asynclib, request): + assert KC is not None + assert KM is not None try: __import__(asynclib) except ImportError: diff --git a/ipykernel/tests/test_comm.py b/ipykernel/tests/test_comm.py index 0fc5991bc..3ab1e5c24 100644 --- a/ipykernel/tests/test_comm.py +++ b/ipykernel/tests/test_comm.py @@ -3,5 +3,5 @@ async def test_comm(kernel): c = Comm() - c.kernel = kernel + c.kernel = kernel # type:ignore c.publish_msg("foo") diff --git a/ipykernel/tests/test_connect.py b/ipykernel/tests/test_connect.py index 4bb998ae1..b4b739d97 100644 --- a/ipykernel/tests/test_connect.py +++ b/ipykernel/tests/test_connect.py @@ -7,18 +7,19 @@ import json import os from tempfile import TemporaryDirectory +from typing import no_type_check from unittest.mock import patch import pytest import zmq -from traitlets.config import Config +from traitlets.config.loader import Config from ipykernel import connect from ipykernel.kernelapp import IPKernelApp from .utils import TemporaryWorkingDirectory -sample_info = { +sample_info: dict = { "ip": "1.2.3.4", "transport": "ipc", "shell_port": 1, @@ -91,6 +92,7 @@ def test_port_bind_failure_raises(request): assert mock_try_bind.call_count == 1 +@no_type_check def test_port_bind_failure_recovery(request): try: errno.WSAEADDRINUSE diff --git a/ipykernel/tests/test_debugger.py b/ipykernel/tests/test_debugger.py index 6b9c817cd..200154cfc 100644 --- a/ipykernel/tests/test_debugger.py +++ b/ipykernel/tests/test_debugger.py @@ -121,7 +121,7 @@ def test_set_breakpoints(kernel_with_debug): assert reply["body"]["breakpoints"][0]["source"]["path"] == source r = wait_for_debug_request(kernel_with_debug, "debugInfo") - assert source in map(lambda b: b["source"], r["body"]["breakpoints"]) + assert source in map(lambda b: b["source"], r["body"]["breakpoints"]) # type:ignore # noqa r = wait_for_debug_request(kernel_with_debug, "configurationDone") assert r["success"] @@ -154,7 +154,7 @@ def test_stop_on_breakpoint(kernel_with_debug): kernel_with_debug.execute(code) # Wait for stop on breakpoint - msg = {"msg_type": "", "content": {}} + msg: dict = {"msg_type": "", "content": {}} while msg.get("msg_type") != "debug_event" or msg["content"].get("event") != "stopped": msg = kernel_with_debug.get_iopub_msg(timeout=TIMEOUT) @@ -190,7 +190,7 @@ def f(a, b): kernel_with_debug.execute(code) # Wait for stop on breakpoint - msg = {"msg_type": "", "content": {}} + msg: dict = {"msg_type": "", "content": {}} while msg.get("msg_type") != "debug_event" or msg["content"].get("event") != "stopped": msg = kernel_with_debug.get_iopub_msg(timeout=TIMEOUT) @@ -208,7 +208,7 @@ def test_rich_inspect_not_at_breakpoint(kernel_with_debug): get_reply(kernel_with_debug, msg_id) r = wait_for_debug_request(kernel_with_debug, "inspectVariables") - assert var_name in list(map(lambda v: v["name"], r["body"]["variables"])) + assert var_name in list(map(lambda v: v["name"], r["body"]["variables"])) # type:ignore # noqa reply = wait_for_debug_request( kernel_with_debug, @@ -246,7 +246,7 @@ def test_rich_inspect_at_breakpoint(kernel_with_debug): kernel_with_debug.execute(code) # Wait for stop on breakpoint - msg = {"msg_type": "", "content": {}} + msg: dict = {"msg_type": "", "content": {}} while msg.get("msg_type") != "debug_event" or msg["content"].get("event") != "stopped": msg = kernel_with_debug.get_iopub_msg(timeout=TIMEOUT) diff --git a/ipykernel/tests/test_embed_kernel.py b/ipykernel/tests/test_embed_kernel.py index 6ce306cd2..ff97edfa5 100644 --- a/ipykernel/tests/test_embed_kernel.py +++ b/ipykernel/tests/test_embed_kernel.py @@ -13,10 +13,10 @@ import pytest from flaky import flaky -from jupyter_client import BlockingKernelClient +from jupyter_client.blocking.client import BlockingKernelClient from jupyter_core import paths -from ipykernel.embed import IPKernelApp, embed_kernel +from ipykernel.embed import IPKernelApp, embed_kernel # type:ignore[attr-defined] SETUP_TIMEOUT = 60 TIMEOUT = 15 diff --git a/ipykernel/tests/test_eventloop.py b/ipykernel/tests/test_eventloop.py index 13a536ed0..7d0063e61 100644 --- a/ipykernel/tests/test_eventloop.py +++ b/ipykernel/tests/test_eventloop.py @@ -5,7 +5,6 @@ import sys import threading import time -from unittest.mock import MagicMock import pytest import tornado @@ -25,6 +24,8 @@ def setup(): def teardown(): + assert KM is not None + assert KC is not None KC.stop_channels() KM.shutdown_kernel(now=True) @@ -37,6 +38,8 @@ def teardown(): @pytest.mark.skipif(tornado.version_info < (5,), reason="only relevant on tornado 5") def test_asyncio_interrupt(): + assert KM is not None + assert KC is not None flush_channels(KC) msg_id, content = execute("%gui asyncio", KC) assert content["status"] == "ok", content @@ -93,5 +96,4 @@ def test_enable_gui(kernel): @pytest.mark.skipif(sys.platform != "darwin", reason="MacOS-only") def test_cocoa_loop(kernel): - kernel.shell = MagicMock() loop_cocoa(kernel) diff --git a/ipykernel/tests/test_heartbeat.py b/ipykernel/tests/test_heartbeat.py index 7847595e8..e093d5eb2 100644 --- a/ipykernel/tests/test_heartbeat.py +++ b/ipykernel/tests/test_heartbeat.py @@ -4,6 +4,7 @@ # Distributed under the terms of the Modified BSD License. import errno +from typing import no_type_check from unittest.mock import patch import pytest @@ -28,6 +29,7 @@ def test_port_bind_success(): assert mock_try_bind.call_count == 1 +@no_type_check def test_port_bind_failure_recovery(): try: errno.WSAEADDRINUSE diff --git a/ipykernel/tests/test_io.py b/ipykernel/tests/test_io.py index d0e70e10f..f7fc8b3f4 100644 --- a/ipykernel/tests/test_io.py +++ b/ipykernel/tests/test_io.py @@ -40,7 +40,7 @@ def test_io_api(): with pytest.raises(io.UnsupportedOperation): stream.tell() with pytest.raises(TypeError): - stream.write(b"") + stream.write(b"") # type:ignore def test_io_isatty(): @@ -64,7 +64,7 @@ def test_io_thread(): ctx1, pipe = thread._setup_pipe_out() pipe.close() thread._pipe_in.close() - thread._check_mp_mode = lambda: MASTER + thread._check_mp_mode = lambda: MASTER # type:ignore thread._really_send([b"hi"]) ctx1.destroy() thread.close() @@ -105,4 +105,4 @@ def test_outstream(): stream.flush() stream.write("hi") stream.writelines(["ab", "cd"]) - assert stream.writable + assert stream.writable() diff --git a/ipykernel/tests/test_ipkernel_direct.py b/ipykernel/tests/test_ipkernel_direct.py index 7e42a35b5..0e65c9a8a 100644 --- a/ipykernel/tests/test_ipkernel_direct.py +++ b/ipykernel/tests/test_ipkernel_direct.py @@ -7,7 +7,8 @@ import zmq from IPython.core.history import DummyDB -from ipykernel.ipkernel import BaseComm, IPythonKernel, _create_comm +from ipykernel.comm.comm import BaseComm +from ipykernel.ipkernel import IPythonKernel, _create_comm from .conftest import MockIPyKernel @@ -19,7 +20,7 @@ class user_mod: __dict__ = {} -async def test_properities(ipkernel: IPythonKernel): +async def test_properities(ipkernel: IPythonKernel) -> None: ipkernel.user_module = user_mod() ipkernel.user_ns = {} @@ -29,7 +30,7 @@ async def test_direct_kernel_info_request(ipkernel): assert reply["header"]["msg_type"] == "kernel_info_reply" -async def test_direct_execute_request(ipkernel: MockIPyKernel): +async def test_direct_execute_request(ipkernel: MockIPyKernel) -> None: reply = await ipkernel.test_shell_message("execute_request", dict(code="hello", silent=False)) assert reply["header"]["msg_type"] == "execute_reply" reply = await ipkernel.test_shell_message( @@ -106,7 +107,7 @@ async def test_direct_interrupt_request(ipkernel): # assert reply['header']['msg_type'] == 'usage_reply' -async def test_is_complete_request(ipkernel: MockIPyKernel): +async def test_is_complete_request(ipkernel: MockIPyKernel) -> None: reply = await ipkernel.test_shell_message("is_complete_request", dict(code="hello")) assert reply["header"]["msg_type"] == "is_complete_reply" setattr(ipkernel, "shell.input_transformer_manager", None) @@ -114,7 +115,7 @@ async def test_is_complete_request(ipkernel: MockIPyKernel): assert reply["header"]["msg_type"] == "is_complete_reply" -def test_do_apply(ipkernel: MockIPyKernel): +def test_do_apply(ipkernel: MockIPyKernel) -> None: from ipyparallel import pack_apply_message def hello(): @@ -134,22 +135,22 @@ async def test_direct_clear(ipkernel): ipkernel.do_clear() -async def test_cancel_on_sigint(ipkernel: IPythonKernel): - future = asyncio.Future() +async def test_cancel_on_sigint(ipkernel: IPythonKernel) -> None: + future: asyncio.Future = asyncio.Future() with ipkernel._cancel_on_sigint(future): pass future.set_result(None) -def test_dispatch_debugpy(ipkernel: IPythonKernel): +def test_dispatch_debugpy(ipkernel: IPythonKernel) -> None: msg = ipkernel.session.msg("debug_request", {}) msg_list = ipkernel.session.serialize(msg) ipkernel.dispatch_debugpy([zmq.Message(m) for m in msg_list]) -async def test_start(ipkernel: IPythonKernel): - shell_future = asyncio.Future() - control_future = asyncio.Future() +async def test_start(ipkernel: IPythonKernel) -> None: + shell_future: asyncio.Future = asyncio.Future() + control_future: asyncio.Future = asyncio.Future() async def fake_dispatch_queue(): shell_future.set_result(None) @@ -157,8 +158,8 @@ async def fake_dispatch_queue(): async def fake_poll_control_queue(): control_future.set_result(None) - ipkernel.dispatch_queue = fake_dispatch_queue - ipkernel.poll_control_queue = fake_poll_control_queue + ipkernel.dispatch_queue = fake_dispatch_queue # type:ignore + ipkernel.poll_control_queue = fake_poll_control_queue # type:ignore ipkernel.start() ipkernel.debugpy_stream = None ipkernel.start() @@ -167,9 +168,9 @@ async def fake_poll_control_queue(): await control_future -async def test_start_no_debugpy(ipkernel: IPythonKernel): - shell_future = asyncio.Future() - control_future = asyncio.Future() +async def test_start_no_debugpy(ipkernel: IPythonKernel) -> None: + shell_future: asyncio.Future = asyncio.Future() + control_future: asyncio.Future = asyncio.Future() async def fake_dispatch_queue(): shell_future.set_result(None) @@ -177,8 +178,8 @@ async def fake_dispatch_queue(): async def fake_poll_control_queue(): control_future.set_result(None) - ipkernel.dispatch_queue = fake_dispatch_queue - ipkernel.poll_control_queue = fake_poll_control_queue + ipkernel.dispatch_queue = fake_dispatch_queue # type:ignore + ipkernel.poll_control_queue = fake_poll_control_queue # type:ignore ipkernel.debugpy_stream = None ipkernel.start() @@ -190,13 +191,13 @@ def test_create_comm(): assert isinstance(_create_comm(), BaseComm) -def test_finish_metadata(ipkernel: IPythonKernel): +def test_finish_metadata(ipkernel: IPythonKernel) -> None: reply_content = dict(status="error", ename="UnmetDependency") metadata = ipkernel.finish_metadata({}, {}, reply_content) assert metadata["dependencies_met"] is False -async def test_do_debug_request(ipkernel: IPythonKernel): +async def test_do_debug_request(ipkernel: IPythonKernel) -> None: msg = ipkernel.session.msg("debug_request", {}) msg_list = ipkernel.session.serialize(msg) await ipkernel.do_debug_request(msg) diff --git a/ipykernel/tests/test_jsonutil.py b/ipykernel/tests/test_jsonutil.py index 0d8edbb71..8fdb35495 100644 --- a/ipykernel/tests/test_jsonutil.py +++ b/ipykernel/tests/test_jsonutil.py @@ -14,7 +14,7 @@ from .. import jsonutil from ..jsonutil import encode_images, json_clean -JUPYTER_CLIENT_MAJOR_VERSION = jupyter_client_version[0] +JUPYTER_CLIENT_MAJOR_VERSION: int = jupyter_client_version[0] # type:ignore class MyInt: @@ -60,7 +60,7 @@ def test(): for val, jval in pairs: if jval is None: - jval = val + jval = val # type:ignore out = json_clean(val) # validate our cleanup assert out == jval diff --git a/ipykernel/tests/test_kernelapp.py b/ipykernel/tests/test_kernelapp.py index 319672709..9a7b1f92e 100644 --- a/ipykernel/tests/test_kernelapp.py +++ b/ipykernel/tests/test_kernelapp.py @@ -1,7 +1,7 @@ import os import threading import time -from unittest.mock import MagicMock, patch +from unittest.mock import patch import pytest @@ -33,7 +33,6 @@ def test_blackhole(): def test_start_app(): app = IPKernelApp() app.kernel = MockKernel() - app.kernel.shell = MagicMock() def trigger_stop(): time.sleep(1) @@ -52,7 +51,6 @@ def trigger_stop(): def test_trio_loop(): app = IPKernelApp(trio_loop=True) app.kernel = MockKernel() - app.kernel.shell = MagicMock() app.init_sockets() with patch("ipykernel.trio_runner.TrioRunner.run", lambda _: None): app.start() diff --git a/ipykernel/tests/test_kernelspec.py b/ipykernel/tests/test_kernelspec.py index 31edff5bc..24771bc1d 100644 --- a/ipykernel/tests/test_kernelspec.py +++ b/ipykernel/tests/test_kernelspec.py @@ -110,8 +110,8 @@ def test_install_profile(): with mock.patch("jupyter_client.kernelspec.SYSTEM_JUPYTER_PATH", [system_jupyter_dir]): install(profile="Test") - spec = os.path.join(system_jupyter_dir, "kernels", KERNEL_NAME, "kernel.json") - with open(spec) as f: + spec_file = os.path.join(system_jupyter_dir, "kernels", KERNEL_NAME, "kernel.json") + with open(spec_file) as f: spec = json.load(f) assert spec["display_name"].endswith(" [profile=Test]") assert spec["argv"][-2:] == ["--profile", "Test"] @@ -123,8 +123,8 @@ def test_install_display_name_overrides_profile(): with mock.patch("jupyter_client.kernelspec.SYSTEM_JUPYTER_PATH", [system_jupyter_dir]): install(display_name="Display", profile="Test") - spec = os.path.join(system_jupyter_dir, "kernels", KERNEL_NAME, "kernel.json") - with open(spec) as f: + spec_file = os.path.join(system_jupyter_dir, "kernels", KERNEL_NAME, "kernel.json") + with open(spec_file) as f: spec = json.load(f) assert spec["display_name"] == "Display" diff --git a/ipykernel/tests/test_message_spec.py b/ipykernel/tests/test_message_spec.py index 9ab41ae0f..b6774ace2 100644 --- a/ipykernel/tests/test_message_spec.py +++ b/ipykernel/tests/test_message_spec.py @@ -7,27 +7,18 @@ import sys from queue import Empty -import jupyter_client import pytest -from packaging.version import Version as V -from traitlets import ( - Bool, - Dict, - Enum, - HasTraits, - Integer, - List, - TraitError, - Unicode, - observe, -) +from jupyter_client._version import version_info +from jupyter_client.blocking.client import BlockingKernelClient +from packaging.version import Version as V # noqa +from traitlets import Bool, Dict, Enum, HasTraits, Integer, List, TraitError, Unicode, observe from .utils import TIMEOUT, execute, flush_channels, get_reply, start_global_kernel # ----------------------------------------------------------------------------- # Globals # ----------------------------------------------------------------------------- -KC = None +KC: BlockingKernelClient def setup(): @@ -168,7 +159,7 @@ class CompleteReply(Reply): matches = List(Unicode()) cursor_start = Integer() cursor_end = Integer() - status = Unicode() + status = Unicode() # type:ignore class LanguageInfo(Reference): @@ -224,7 +215,7 @@ class ExecuteInput(Reference): class Error(ExecuteReplyError): """Errors are the same as ExecuteReply, but without status""" - status = None # no status field + status = None # type:ignore # no status field class Stream(Reference): @@ -515,7 +506,7 @@ def test_connect_request(): @pytest.mark.skipif( - jupyter_client.version_info < (5, 0), + version_info < (5, 0), reason="earlier Jupyter Client don't have comm_info", ) def test_comm_info_request(): diff --git a/ipykernel/tests/test_zmq_shell.py b/ipykernel/tests/test_zmq_shell.py index 8d6780ba6..c0cc50516 100644 --- a/ipykernel/tests/test_zmq_shell.py +++ b/ipykernel/tests/test_zmq_shell.py @@ -15,7 +15,7 @@ from jupyter_client.session import Session from traitlets import Int -from ipykernel.zmqshell import ( +from ipykernel.zmqshell import ( # type:ignore InteractiveShell, KernelMagics, ZMQDisplayPublisher, @@ -111,7 +111,7 @@ def hook(msg): self.disp_pub.register_hook(hook) assert self.disp_pub._hooks == [hook] - q = Queue() + q: Queue = Queue() def set_thread_hooks(): q.put(self.disp_pub._hooks) diff --git a/ipykernel/tests/utils.py b/ipykernel/tests/utils.py index 8ca55b1e7..67556d88f 100644 --- a/ipykernel/tests/utils.py +++ b/ipykernel/tests/utils.py @@ -13,12 +13,13 @@ from time import time from jupyter_client import manager +from jupyter_client.blocking.client import BlockingKernelClient STARTUP_TIMEOUT = 60 TIMEOUT = 100 -KM = None -KC = None +KM: manager.KernelManager +KC: BlockingKernelClient def start_new_kernel(**kwargs): @@ -132,11 +133,11 @@ def stop_global_kernel(): """Stop the global shared kernel instance, if it exists""" global KM, KC KC.stop_channels() - KC = None + KC = None # type:ignore if KM is None: return KM.shutdown_kernel(now=True) - KM = None + KM = None # type:ignore def new_kernel(argv=None): diff --git a/ipykernel/zmqshell.py b/ipykernel/zmqshell.py index 23458d304..acc1c95d3 100644 --- a/ipykernel/zmqshell.py +++ b/ipykernel/zmqshell.py @@ -38,6 +38,7 @@ from ipykernel.displayhook import ZMQShellDisplayHook from ipykernel.jsonutil import encode_images, json_clean + # ----------------------------------------------------------------------------- # Functions and classes # ----------------------------------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 0a65a8a97..33c05466e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,6 +62,8 @@ cov = [ "curio", "trio", ] +lint = ["black>=22.6.0", "mdformat>0.7", "ruff>=0.0.156"] +typing = ["mypy>=0.990"] [tool.hatch.version] path = "ipykernel/_version.py" @@ -92,9 +94,28 @@ features = ["test", "cov"] test = "python -m pytest -vv --cov ipykernel --cov-branch --cov-report term-missing:skip-covered {args}" nowarn = "test -W default {args}" +[tool.hatch.envs.typing] +features = ["test", "typing"] +dependencies = ["mypy>=0.990"] +[tool.hatch.envs.typing.scripts] +test = "mypy --install-types --non-interactive {args:.}" + +[tool.hatch.envs.lint] +features = ["lint"] +[tool.hatch.envs.lint.scripts] +style = [ + "ruff {args:.}", + "black --check --diff {args:.}", + "mdformat --check {args:docs *.md}" +] +fmt = [ + "black {args:.}", + "ruff --fix {args:.}", + "mdformat {args:docs *.md}" +] + [tool.mypy] check_untyped_defs = true -disallow_any_generics = true disallow_incomplete_defs = true disallow_untyped_decorators = true follow_imports = "normal" @@ -167,23 +188,66 @@ omit = [ "ipykernel/pylab/*", ] -[tool.flake8] -ignore = "E501, W503, E402" -builtins = "c, get_config" -exclude = [ - ".cache", - ".github", - "docs", - "setup.py", +[tool.black] +line-length = 100 +skip-string-normalization = true +target-version = ["py37"] + +[tool.ruff] +target-version = "py37" +line-length = 100 +select = [ + "A", "B", "C", "E", "F", "FBT", "I", "N", "Q", "RUF", "S", "T", + "UP", "W", "YTT", ] -enable-extensions = "G" -extend-ignore = [ - "G001", "G002", "G004", "G200", "G201", "G202", - # black adds spaces around ':' - "E203", +ignore = [ + # Allow non-abstract empty methods in abstract base classes + "B027", + # Ignore McCabe complexity + "C901", + # Allow boolean positional values in function calls, like `dict.get(... True)` + "FBT003", + # Use of `assert` detected + "S101", + # Line too long + "E501", + # Relative imports are banned + "I252", + # Boolean ... in function definition + "FBT001", "FBT002", + # Module level import not at top of file + "E402", + # A001/A002/A003 .. is shadowing a python builtin + "A001", "A002", "A003", + # Possible hardcoded password + "S105", "S106", + # Q000 Single quotes found but double quotes preferred + "Q000", + # N806 Variable `B` in function should be lowercase + "N806", + # T201 `print` found + "T201", + # N802 Function name `CreateWellKnownSid` should be lowercase + "N802", "N803", + # C408 Unnecessary `dict` call (rewrite as a literal) + "C408", + # N801 Class name `directional_link` should use CapWords convention + "N801", ] -per-file-ignores = [ - # B011: Do not call assert False since python -O removes these calls - # F841 local variable 'foo' is assigned to but never used - "ipykernel/tests/*: B011", "F841", +unfixable = [ + # Don't touch print statements + "T201", + # Don't touch noqa lines + "RUF100", ] + +[tool.ruff.per-file-ignores] +# B011 Do not call assert False since python -O removes these calls +# F841 local variable 'foo' is assigned to but never used +# C408 Unnecessary `dict` call +# E402 Module level import not at top of file +# T201 `print` found +# B007 Loop control variable `i` not used within the loop body. +# N802 Function name `assertIn` should be lowercase +# F841 Local variable `t` is assigned to but never used +"ipykernel/tests/*" = ["B011", "F841", "C408", "E402", "T201", "B007", "N802", "F841"] From 2461eca947f11248c7a895d0c52d4f63dcd95907 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 6 Dec 2022 21:01:24 -0600 Subject: [PATCH 2/4] fix test --- ipykernel/tests/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipykernel/tests/utils.py b/ipykernel/tests/utils.py index 67556d88f..b1b4119f0 100644 --- a/ipykernel/tests/utils.py +++ b/ipykernel/tests/utils.py @@ -18,8 +18,8 @@ STARTUP_TIMEOUT = 60 TIMEOUT = 100 -KM: manager.KernelManager -KC: BlockingKernelClient +KM: manager.KernelManager = None # type:ignore +KC: BlockingKernelClient = None # type:ignore def start_new_kernel(**kwargs): From 1c7200da621962c5845c82d7e60aafa313e02f79 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 6 Dec 2022 21:01:37 -0600 Subject: [PATCH 3/4] fix test --- ipykernel/tests/test_message_spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipykernel/tests/test_message_spec.py b/ipykernel/tests/test_message_spec.py index b6774ace2..a12d126bf 100644 --- a/ipykernel/tests/test_message_spec.py +++ b/ipykernel/tests/test_message_spec.py @@ -18,7 +18,7 @@ # ----------------------------------------------------------------------------- # Globals # ----------------------------------------------------------------------------- -KC: BlockingKernelClient +KC: BlockingKernelClient = None def setup(): From ac1b26ff88f36bbc102ea13055e0ee55e5075a30 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 7 Dec 2022 07:38:54 -0600 Subject: [PATCH 4/4] fix lint --- ipykernel/tests/test_message_spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipykernel/tests/test_message_spec.py b/ipykernel/tests/test_message_spec.py index a12d126bf..1ba5f7651 100644 --- a/ipykernel/tests/test_message_spec.py +++ b/ipykernel/tests/test_message_spec.py @@ -18,7 +18,7 @@ # ----------------------------------------------------------------------------- # Globals # ----------------------------------------------------------------------------- -KC: BlockingKernelClient = None +KC: BlockingKernelClient = None # type:ignore def setup():