diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 87daebd..b515bb0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.27.1 + rev: 0.27.2 hooks: - id: check-github-workflows @@ -52,7 +52,7 @@ repos: args: ["-L", "sur,nd"] - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v1.6.1" + rev: "v1.7.1" hooks: - id: mypy files: "^jupyter_events" @@ -77,7 +77,7 @@ repos: - id: rst-inline-touching-normal - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.5 + rev: v0.1.6 hooks: - id: ruff types_or: [python, jupyter] @@ -86,7 +86,8 @@ repos: types_or: [python, jupyter] - repo: https://github.com/scientific-python/cookie - rev: "2023.10.27" + rev: "2023.11.17" hooks: - id: sp-repo-review additional_dependencies: ["repo-review[cli]"] + args: ["--ignore", "GH102"] diff --git a/docs/conf.py b/docs/conf.py index 81435a3..f145aa8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,11 +16,10 @@ # -- Project information ----------------------------------------------------- - -from typing import List +from __future__ import annotations project = "jupyter_events" -copyright = "2019, Project Jupyter" # noqa +copyright = "2019, Project Jupyter" author = "Project Jupyter" @@ -29,10 +28,10 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions: List = ["myst_parser", "jupyterlite_sphinx"] +extensions: list = ["myst_parser", "jupyterlite_sphinx"] try: - import enchant # type:ignore # noqa + import enchant # noqa: F401 extensions += ["sphinxcontrib.spelling"] except ImportError: diff --git a/docs/demo/demo-notebook.ipynb b/docs/demo/demo-notebook.ipynb index 74ccade..cbf8e30 100644 --- a/docs/demo/demo-notebook.ipynb +++ b/docs/demo/demo-notebook.ipynb @@ -13,6 +13,8 @@ "metadata": {}, "outputs": [], "source": [ + "from __future__ import annotations\n", + "\n", "import piplite\n", "\n", "await piplite.install(\"jupyter_events\")" diff --git a/jupyter_events/_version.py b/jupyter_events/_version.py index d320623..f64016f 100644 --- a/jupyter_events/_version.py +++ b/jupyter_events/_version.py @@ -1,8 +1,9 @@ """ store the current version info of jupyter-events. """ +from __future__ import annotations + import re -from typing import List # Version string must appear intact for hatch versioning __version__ = "0.9.0" @@ -10,8 +11,8 @@ # Build up version_info tuple for backwards compatibility pattern = r"(?P\d+).(?P\d+).(?P\d+)(?P.*)" match = re.match(pattern, __version__) -assert match is not None # noqa -parts: List[object] = [int(match[part]) for part in ["major", "minor", "patch"]] +assert match is not None +parts: list[object] = [int(match[part]) for part in ["major", "minor", "patch"]] if match["rest"]: parts.append(match["rest"]) version_info = tuple(parts) diff --git a/jupyter_events/cli.py b/jupyter_events/cli.py index 13b119b..911d091 100644 --- a/jupyter_events/cli.py +++ b/jupyter_events/cli.py @@ -1,4 +1,6 @@ """The cli for jupyter events.""" +from __future__ import annotations + import json import pathlib import platform @@ -46,7 +48,6 @@ def main() -> None: https://raw.githubusercontent.com/jupyter/jupyter_events/main/jupyter_events/schemas/event-metaschema.yml """ - pass @click.command() diff --git a/jupyter_events/logger.py b/jupyter_events/logger.py index 2cf657b..79b4fba 100644 --- a/jupyter_events/logger.py +++ b/jupyter_events/logger.py @@ -91,9 +91,7 @@ class EventLogger(LoggingConfigurable): async def gather_listeners(self) -> list[t.Any]: """Gather all of the active listeners.""" - return await asyncio.gather( # type:ignore[no-any-return] - *self._active_listeners, return_exceptions=True - ) + return await asyncio.gather(*self._active_listeners, return_exceptions=True) @default("schemas") def _default_schemas(self) -> SchemaRegistry: @@ -120,8 +118,8 @@ def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: def _load_config( self, cfg: Config, - section_names: list[str] | None = None, - traits: list[str] | None = None, # type:ignore[override] + section_names: list[str] | None = None, # noqa: ARG002 + traits: list[str] | None = None, # type:ignore[override] # noqa: ARG002 ) -> None: """Load EventLogger traits from a Config object, patching the handlers trait in the Config object to avoid deepcopy errors. diff --git a/jupyter_events/schema.py b/jupyter_events/schema.py index e658875..49420e1 100644 --- a/jupyter_events/schema.py +++ b/jupyter_events/schema.py @@ -3,7 +3,7 @@ import json from pathlib import Path, PurePath -from typing import Any, Union +from typing import Any, Dict, Union from jsonschema import FormatChecker, validators from referencing import Registry @@ -18,25 +18,19 @@ from .validators import draft7_format_checker, validate_schema -class EventSchemaUnrecognized(Exception): # noqa +class EventSchemaUnrecognized(Exception): """An error for an unrecognized event schema.""" - pass - class EventSchemaLoadingError(Exception): """An error for an event schema loading error.""" - pass - -class EventSchemaFileAbsent(Exception): # noqa +class EventSchemaFileAbsent(Exception): """An error for an absent event schema file.""" - pass - -SchemaType = Union[dict, str, PurePath] +SchemaType = Union[Dict[str, Any], str, PurePath] class EventSchema: @@ -141,7 +135,7 @@ def _load_schema(schema: SchemaType) -> dict[str, Any]: raise EventSchemaUnrecognized(msg) @property - def id(self) -> str: # noqa + def id(self) -> str: """Schema $id field.""" return self._schema["$id"] # type:ignore[no-any-return] diff --git a/jupyter_events/schema_registry.py b/jupyter_events/schema_registry.py index 00d32ee..52defd3 100644 --- a/jupyter_events/schema_registry.py +++ b/jupyter_events/schema_registry.py @@ -6,7 +6,7 @@ from .schema import EventSchema -class SchemaRegistryException(Exception): # noqa: N818 +class SchemaRegistryException(Exception): """Exception class for Jupyter Events Schema Registry Errors.""" diff --git a/jupyter_events/traits.py b/jupyter_events/traits.py index 118d53e..d7b38c0 100644 --- a/jupyter_events/traits.py +++ b/jupyter_events/traits.py @@ -44,8 +44,8 @@ def validate(self, obj: t.Any, value: t.Any) -> t.Any: return out # If a list, check it's elements to verify # that each element is a logging handler instance. - elif isinstance(value, list): + if isinstance(value, list): self.validate_elements(obj, value) return value - else: - self.error(obj, value) + self.error(obj, value) + return None # type:ignore[unreachable] diff --git a/pyproject.toml b/pyproject.toml index 77977ea..c07baca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -152,7 +152,6 @@ source = ["jupyter_events"] files = "jupyter_events" python_version = "3.8" strict = true -show_error_codes = true enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] warn_unreachable = true @@ -160,20 +159,34 @@ warn_unreachable = true line-length = 100 [tool.ruff.lint] -select = [ - "A", "B", "C", "DTZ", "E", "EM", "F", "FBT", "I", "ICN", "N", - "PLC", "PLE", "PLR", "PLW", "Q", "RUF", "S", "SIM", "T", "TID", "UP", - "W", "YTT", +extend-select = [ + "B", # flake8-bugbear + "I", # isort + "ARG", # flake8-unused-arguments + "C4", # flake8-comprehensions + "EM", # flake8-errmsg + "ICN", # flake8-import-conventions + "G", # flake8-logging-format + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PL", # pylint + "PTH", # flake8-use-pathlib + "PT", # flake8-pytest-style + "RET", # flake8-return + "RUF", # Ruff-specific + "SIM", # flake8-simplify + "T20", # flake8-print + "UP", # pyupgrade + "YTT", # flake8-2020 + "EXE", # flake8-executable + "PYI", # flake8-pyi + "S", # flake8-bandit ] ignore = [ - # Q000 Single quotes found but double quotes preferred - "Q000", - # FBT001 Boolean positional arg in function definition - "FBT001", "FBT002", "FBT003", - # E501 Line too long (158 > 100 characters) - "E501", - # SIM105 Use `contextlib.suppress(...)` - "SIM105", + "E501", # E501 Line too long (158 > 100 characters) + "SIM105", # SIM105 Use `contextlib.suppress(...)` + "PLR", # Design related pylint codes + "S101", # Use of `assert` detected ] unfixable = [ # Don't touch print statements @@ -181,6 +194,7 @@ unfixable = [ # Don't touch noqa lines "RUF100", ] +isort.required-imports = ["from __future__ import annotations"] [tool.ruff.lint.per-file-ignores] # B011 Do not call assert False since python -O removes these calls @@ -192,11 +206,16 @@ unfixable = [ # N802 Function name `assertIn` should be lowercase # F841 Local variable `t` is assigned to but never used # S101 Use of `assert` detected -"tests/*" = ["B011", "F841", "C408", "E402", "T201", "B007", "N802", "F841", "S101"] +"tests/*" = ["B011", "F841", "C408", "E402", "T201", "B007", "N802", "F841", "S101", "ARG", "PGH"] # C901 Function is too complex "jupyter_events/logger.py" = ["C901"] # `emit` is too complex (12 > 10) "docs/demo/demo-notebook.ipynb" = ["PLE1142", "E402", "T201"] +[tool.ruff.lint.flake8-pytest-style] +fixture-parentheses = false +mark-parentheses = false +parametrize-names-type = "csv" + [tool.interrogate] ignore-init-module=true ignore-private=true @@ -206,6 +225,3 @@ ignore-nested-functions=true ignore-nested-classes=true fail-under=100 exclude = ["docs", "tests"] - -[tool.repo-review] -ignore = ["PY007", "GH102"] diff --git a/tests/conftest.py b/tests/conftest.py index c83f78d..e4ad08f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1 +1,3 @@ +from __future__ import annotations + pytest_plugins = ["jupyter_events.pytest_plugin"] diff --git a/tests/test_cli.py b/tests/test_cli.py index 5722257..f15bc19 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import pytest diff --git a/tests/test_listeners.py b/tests/test_listeners.py index 861d5bb..2e167a1 100644 --- a/tests/test_listeners.py +++ b/tests/test_listeners.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import io import logging @@ -42,7 +44,7 @@ async def test_listener_function_str_annotations(jp_event_logger, schema): event_logger = jp_event_logger listener_was_called = False - async def my_listener(logger: "EventLogger", schema_id: "str", data: "dict") -> "None": + async def my_listener(logger: EventLogger, schema_id: str, data: dict) -> None: nonlocal listener_was_called listener_was_called = True diff --git a/tests/test_logger.py b/tests/test_logger.py index 96cef1b..8a0a499 100644 --- a/tests/test_logger.py +++ b/tests/test_logger.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import io import json import logging @@ -40,8 +42,7 @@ def get_config_from_file(path, content): # Load written file. loader = PyFileConfigLoader(filename, path=str(path)) - cfg = loader.load_config() - return cfg + return loader.load_config() def test_good_config_file(tmp_path): diff --git a/tests/test_modifiers.py b/tests/test_modifiers.py index 4da7f89..dd45d6b 100644 --- a/tests/test_modifiers.py +++ b/tests/test_modifiers.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from jupyter_events.schema import EventSchema diff --git a/tests/test_schema.py b/tests/test_schema.py index cf71520..2793b7e 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from pathlib import Path @@ -31,7 +33,7 @@ def test_bad_validations(schema_file, validation_error_msg): a redactionPolicies field. """ # Read the schema file - with open(SCHEMA_PATH / "bad" / schema_file) as f: + with Path.open(SCHEMA_PATH / "bad" / schema_file) as f: schema = yaml.loads(f) # Assert that the schema files for a known reason. with pytest.raises(ValidationError) as err: @@ -49,7 +51,7 @@ def test_string_intended_as_path(): """Ensure EventSchema returns a helpful error message if user passes a string intended as a Path.""" expected_msg_contents = "Paths to schema files must be explicitly wrapped in a Pathlib object." - str_path = os.path.join(SCHEMA_PATH, "good", "some_schema.yaml") + str_path = os.path.join(SCHEMA_PATH, "good", "some_schema.yaml") # noqa: PTH118 with pytest.raises(EventSchemaLoadingError) as e: EventSchema(str_path) @@ -79,7 +81,7 @@ def test_valid_json(): def test_good_validations(schema_file): """Ensure validation passes for good schemas.""" # Read the schema file - with open(SCHEMA_PATH / "good" / schema_file) as f: + with Path.open(SCHEMA_PATH / "good" / schema_file) as f: schema = yaml.loads(f) # assert that no exception gets raised validate_schema(schema) diff --git a/tests/test_traits.py b/tests/test_traits.py index a6878f2..201a1bc 100644 --- a/tests/test_traits.py +++ b/tests/test_traits.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import pytest diff --git a/tests/utils.py b/tests/utils.py index 61a2967..dc88c30 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import io import json import logging