diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f10a5bc1..cbd920f6 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/tests.yml b/.github/workflows/tests.yml index a0d879b9..8a47563e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -50,12 +50,18 @@ jobs: codecov - run: hatch version 100.100.100 - pre_commit: - runs-on: ubuntu-20.04 + 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 docs: runs-on: ubuntu-latest @@ -142,7 +148,7 @@ jobs: if: always() needs: - tests - - pre_commit + - test_lint - docs - test_minimum_versions - test_prereleases diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 99467558..ff708b29 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 @@ -15,59 +18,23 @@ repos: - id: check-builtin-literals - id: trailing-whitespace - - repo: https://github.com/psf/black - rev: 22.10.0 - hooks: - - id: black - - - repo: https://github.com/PyCQA/isort - rev: 5.10.1 - hooks: - - id: isort - files: \.py$ - - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.991 - hooks: - - id: mypy - additional_dependencies: [types-requests, traitlets, jupyter_core] - stages: [manual] - - - 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 - - repo: https://github.com/asottile/pyupgrade - rev: v3.3.0 - hooks: - - id: pyupgrade - args: [--py37-plus] - - - repo: https://github.com/PyCQA/doc8 - rev: v1.0.0 - hooks: - - id: doc8 - args: [--max-line-length=200] - exclude: docs/source/other/full-config.rst - stages: [manual] - - - repo: https://github.com/john-hen/Flake8-pyproject - rev: 1.2.2 + - repo: https://github.com/psf/black + rev: 22.10.0 hooks: - - id: Flake8-pyproject - alias: flake8 - additional_dependencies: - ["flake8-bugbear==22.6.22", "flake8-implicit-str-concat==0.2.0"] - stages: [manual] + - id: black - - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.19.2 + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: v0.0.165 hooks: - - id: check-github-workflows + - id: ruff + args: ["--fix"] diff --git a/nbformat/_imports.py b/nbformat/_imports.py index 1c6c97ac..59fe2f0e 100644 --- a/nbformat/_imports.py +++ b/nbformat/_imports.py @@ -33,7 +33,7 @@ def import_item(name): try: pak = getattr(module, obj) except AttributeError: - raise ImportError("No module named %s" % obj) + raise ImportError("No module named %s" % obj) from None return pak else: # called with un-dotted string diff --git a/nbformat/_struct.py b/nbformat/_struct.py index f1ab4e47..cb70211e 100644 --- a/nbformat/_struct.py +++ b/nbformat/_struct.py @@ -102,7 +102,7 @@ def __setattr__(self, key, value): try: self.__setitem__(key, value) except KeyError as e: - raise AttributeError(e) + raise AttributeError(e) from None def __getattr__(self, key): """Get an attr by calling :meth:`dict.__getitem__`. @@ -127,7 +127,7 @@ def __getattr__(self, key): try: result = self[key] except KeyError: - raise AttributeError(key) + raise AttributeError(key) from None else: return result diff --git a/nbformat/converter.py b/nbformat/converter.py index d49a197e..e6969926 100644 --- a/nbformat/converter.py +++ b/nbformat/converter.py @@ -62,7 +62,7 @@ def convert(nb, to_version): except AttributeError as e: raise ValidationError( f"Notebook could not be converted from version {version} to version {step_version} because it's missing a key: {e}" - ) + ) from None # Recursively convert until target version is reached. return convert(converted, to_version) diff --git a/nbformat/json_compat.py b/nbformat/json_compat.py index 737a9111..7cd87a03 100644 --- a/nbformat/json_compat.py +++ b/nbformat/json_compat.py @@ -47,7 +47,7 @@ def validate(self, data): try: self._validator(data) except _JsonSchemaException as error: - raise ValidationError(str(error), schema_path=error.path) + raise ValidationError(str(error), schema_path=error.path) from error def iter_errors(self, data, schema=None): if schema is not None: diff --git a/nbformat/reader.py b/nbformat/reader.py index 54d91110..47759b45 100644 --- a/nbformat/reader.py +++ b/nbformat/reader.py @@ -74,7 +74,9 @@ def reads(s, **kwargs): try: return versions[major].to_notebook_json(nb_dict, minor=minor) except AttributeError as e: - raise ValidationError(f"The notebook is invalid and is missing an expected key: {e}") + raise ValidationError( + f"The notebook is invalid and is missing an expected key: {e}" + ) from None else: raise NBFormatError("Unsupported nbformat version %s" % major) diff --git a/nbformat/sign.py b/nbformat/sign.py index 6009e9ba..6011ef37 100644 --- a/nbformat/sign.py +++ b/nbformat/sign.py @@ -23,18 +23,7 @@ from base64 import encodebytes from jupyter_core.application import JupyterApp, base_flags -from traitlets import ( - Any, - Bool, - Bytes, - Callable, - Enum, - Instance, - Integer, - Unicode, - default, - observe, -) +from traitlets import Any, Bool, Bytes, Callable, Enum, Instance, Integer, Unicode, default, observe from traitlets.config import LoggingConfigurable, MultipleInstanceError from . import NO_CONVERT, __version__, read, reads @@ -139,9 +128,9 @@ def close(self): self.db.close() def _connect_db(self, db_file): - kwargs: t.Dict[str, t.Any] = dict( - detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES - ) + kwargs: t.Dict[str, t.Any] = { + "detect_types": sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES + } db = None try: db = sqlite3.connect(db_file, **kwargs) diff --git a/nbformat/v1/nbbase.py b/nbformat/v1/nbbase.py index 441f6fa9..5b1c0c41 100644 --- a/nbformat/v1/nbbase.py +++ b/nbformat/v1/nbbase.py @@ -18,6 +18,7 @@ from .._struct import Struct + # ----------------------------------------------------------------------------- # Code # ----------------------------------------------------------------------------- diff --git a/nbformat/v1/nbjson.py b/nbformat/v1/nbjson.py index aeb7b349..d319e60d 100644 --- a/nbformat/v1/nbjson.py +++ b/nbformat/v1/nbjson.py @@ -21,6 +21,7 @@ from .nbbase import from_dict from .rwbase import NotebookReader, NotebookWriter + # ----------------------------------------------------------------------------- # Code # ----------------------------------------------------------------------------- diff --git a/nbformat/v2/convert.py b/nbformat/v2/convert.py index 501b90f6..dd3ab798 100644 --- a/nbformat/v2/convert.py +++ b/nbformat/v2/convert.py @@ -19,6 +19,7 @@ from .nbbase import new_code_cell, new_notebook, new_text_cell, new_worksheet + # ----------------------------------------------------------------------------- # Code # ----------------------------------------------------------------------------- diff --git a/nbformat/v2/nbbase.py b/nbformat/v2/nbbase.py index bc9f3ab4..72cf751f 100644 --- a/nbformat/v2/nbbase.py +++ b/nbformat/v2/nbbase.py @@ -23,6 +23,7 @@ from .._struct import Struct + # ----------------------------------------------------------------------------- # Code # ----------------------------------------------------------------------------- diff --git a/nbformat/v2/nbjson.py b/nbformat/v2/nbjson.py index 02eff5ba..59397333 100644 --- a/nbformat/v2/nbjson.py +++ b/nbformat/v2/nbjson.py @@ -20,13 +20,8 @@ import json from .nbbase import from_dict -from .rwbase import ( - NotebookReader, - NotebookWriter, - rejoin_lines, - restore_bytes, - split_lines, -) +from .rwbase import NotebookReader, NotebookWriter, rejoin_lines, restore_bytes, split_lines + # ----------------------------------------------------------------------------- # Code diff --git a/nbformat/v2/rwbase.py b/nbformat/v2/rwbase.py index edfc8deb..af23ade5 100644 --- a/nbformat/v2/rwbase.py +++ b/nbformat/v2/rwbase.py @@ -18,6 +18,7 @@ from base64 import decodebytes, encodebytes + # ----------------------------------------------------------------------------- # Code # ----------------------------------------------------------------------------- diff --git a/nbformat/v3/nbjson.py b/nbformat/v3/nbjson.py index 341e3e1c..f6efe8bb 100644 --- a/nbformat/v3/nbjson.py +++ b/nbformat/v3/nbjson.py @@ -7,13 +7,7 @@ import json from .nbbase import from_dict -from .rwbase import ( - NotebookReader, - NotebookWriter, - rejoin_lines, - split_lines, - strip_transient, -) +from .rwbase import NotebookReader, NotebookWriter, rejoin_lines, split_lines, strip_transient class BytesEncoder(json.JSONEncoder): diff --git a/nbformat/v4/nbjson.py b/nbformat/v4/nbjson.py index 4b5c5642..70566749 100644 --- a/nbformat/v4/nbjson.py +++ b/nbformat/v4/nbjson.py @@ -7,13 +7,7 @@ import json from ..notebooknode import from_dict -from .rwbase import ( - NotebookReader, - NotebookWriter, - rejoin_lines, - split_lines, - strip_transient, -) +from .rwbase import NotebookReader, NotebookWriter, rejoin_lines, split_lines, strip_transient class BytesEncoder(json.JSONEncoder): diff --git a/nbformat/validator.py b/nbformat/validator.py index 901d4fd0..ede37484 100644 --- a/nbformat/validator.py +++ b/nbformat/validator.py @@ -417,14 +417,14 @@ def validate( version : int version_minor : int relax_add_props : bool - Deprecated since 5.5.0 – will be removed in the future. + Deprecated since 5.5.0 - will be removed in the future. Wether to allow extra property in the Json schema validating the notebook. nbjson repair_duplicate_cell_ids : boolny - Deprecated since 5.5.0 – will be removed in the future. + Deprecated since 5.5.0 - will be removed in the future. strip_invalid_metadata : bool - Deprecated since 5.5.0 – will be removed in the future. + Deprecated since 5.5.0 - will be removed in the future. Returns ------- diff --git a/pyproject.toml b/pyproject.toml index 702917cd..fedd5e69 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,12 +49,13 @@ docs = [ "sphinxcontrib_github_alt" ] test = [ - "check-manifest", "testpath", "pytest", "pre-commit", "pep440" ] +lint = ["black[jupyter]>=22.6.0", "mdformat>0.7", "ruff>=0.0.156"] +typing = ["mypy>=0.990"] [project.scripts] jupyter-trust = "nbformat.sign:TrustNotebookApp.launch_instance" @@ -80,11 +81,24 @@ dependencies = ["coverage", "pytest-cov"] test = "python -m pytest -vv --cov nbformat --cov-branch --cov-report term-missing:skip-covered {args}" nowarn = "test -W default {args}" -[tool.black] -line_length = 100 - -[tool.isort] -profile = "black" +[tool.hatch.envs.typing] +features = ["typing", "test"] +[tool.hatch.envs.typing.scripts] +test = "mypy --install-types --non-interactive {args:nbformat tests}" + +[tool.hatch.envs.lint] +features = ["lint"] +[tool.hatch.envs.lint.scripts] +style = [ + "ruff {args:.}", + "black --check --diff {args:.}", + "mdformat --check {args:*.md}" +] +fmt = [ + "black {args:.}", + "ruff --fix {args:.}", + "mdformat {args:*.md}" +] [tool.pytest.ini_options] addopts = "-raXs --durations 10 --color=yes --doctest-modules" @@ -133,26 +147,65 @@ module = [ ] ignore_missing_imports = true -[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 = ["py38"] +extend-exclude = "^/tests.*ipynb$" + +[tool.ruff] +target-version = "py38" +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" ] -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 - # B007 Loop control variable 'foo' not used within the loop body - "tests/*: B011", "F841", "B007", - # F401 '.foo' imported but unused - "nbformat/*/__init__.py: F401", +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 +# RUF001 contains ambiguous unicode character '–' (did you mean '-'?) +"tests/*" = ["B011", "F841", "C408", "E402", "T201", "B007", "N802", "RUF001", "RUF002"] +# F401 `nbxml.to_notebook` imported but unused +"nbformat/*__init__.py" = ["F401"] diff --git a/tests/invalid.ipynb b/tests/invalid.ipynb index e53a2df0..2435c06e 100644 --- a/tests/invalid.ipynb +++ b/tests/invalid.ipynb @@ -72,12 +72,15 @@ ], "source": [ "from IPython.display import HTML\n", - "HTML(\"\"\"\n", + "\n", + "HTML(\n", + " \"\"\"\n", "<script>\n", "console.log(\"hello\");\n", "</script>\n", "<b>HTML</b>\n", - "\"\"\")" + "\"\"\"\n", + ")" ] }, { @@ -296,6 +299,7 @@ ], "source": [ "from IPython.display import Image\n", + "\n", "Image(\"http://ipython.org/_static/IPy_header.png\")" ] } diff --git a/tests/test4.5.ipynb b/tests/test4.5.ipynb index ba09ecbd..076be786 100644 --- a/tests/test4.5.ipynb +++ b/tests/test4.5.ipynb @@ -76,12 +76,15 @@ ], "source": [ "from IPython.display import HTML\n", - "HTML(\"\"\"\n", + "\n", + "HTML(\n", + " \"\"\"\n", "<script>\n", "console.log(\"hello\");\n", "</script>\n", "<b>HTML</b>\n", - "\"\"\")" + "\"\"\"\n", + ")" ] }, { @@ -136,6 +139,7 @@ ], "source": [ "from IPython.display import Image\n", + "\n", "Image(\"http://ipython.org/_static/IPy_header.png\")" ] } diff --git a/tests/test4.ipynb b/tests/test4.ipynb index 20afe94d..7231e3a0 100644 --- a/tests/test4.ipynb +++ b/tests/test4.ipynb @@ -74,12 +74,15 @@ ], "source": [ "from IPython.display import HTML\n", - "HTML(\"\"\"\n", + "\n", + "HTML(\n", + " \"\"\"\n", "<script>\n", "console.log(\"hello\");\n", "</script>\n", "<b>HTML</b>\n", - "\"\"\")" + "\"\"\"\n", + ")" ] }, { @@ -298,6 +301,7 @@ ], "source": [ "from IPython.display import Image\n", + "\n", "Image(\"http://ipython.org/_static/IPy_header.png\")" ] } diff --git a/tests/test4custom.ipynb b/tests/test4custom.ipynb index 31fa9b11..68a0da91 100644 --- a/tests/test4custom.ipynb +++ b/tests/test4custom.ipynb @@ -26,11 +26,7 @@ "import IPython\n", "\n", "bundle = {}\n", - "bundle['application/vnd.raw.v1+json'] = {\n", - " 'apples': ['🍎', '🍏'],\n", - " 'bananas': 2,\n", - " 'oranges': 'apples'\n", - "}\n", + "bundle['application/vnd.raw.v1+json'] = {'apples': ['🍎', '🍏'], 'bananas': 2, 'oranges': 'apples'}\n", "\n", "IPython.display.display(bundle, raw=True)" ] @@ -45,8 +41,7 @@ "source": [] } ], - "metadata": { - }, + "metadata": {}, "nbformat": 4, "nbformat_minor": 2 } diff --git a/tests/test4docinfo.ipynb b/tests/test4docinfo.ipynb index 7c308921..e0bb9f3d 100644 --- a/tests/test4docinfo.ipynb +++ b/tests/test4docinfo.ipynb @@ -74,12 +74,15 @@ ], "source": [ "from IPython.display import HTML\n", - "HTML(\"\"\"\n", + "\n", + "HTML(\n", + " \"\"\"\n", "<script>\n", "console.log(\"hello\");\n", "</script>\n", "<b>HTML</b>\n", - "\"\"\")" + "\"\"\"\n", + ")" ] }, { @@ -298,13 +301,18 @@ ], "source": [ "from IPython.display import Image\n", + "\n", "Image(\"http://ipython.org/_static/IPy_header.png\")" ] } ], "metadata": { - "title": "Test Notebook", - "authors": [{"name": "Jean Tester"}] + "title": "Test Notebook", + "authors": [ + { + "name": "Jean Tester" + } + ] }, "nbformat": 4, "nbformat_minor": 0 diff --git a/tests/test4jupyter_metadata_timings.ipynb b/tests/test4jupyter_metadata_timings.ipynb index 2e724a55..96b01e7b 100644 --- a/tests/test4jupyter_metadata_timings.ipynb +++ b/tests/test4jupyter_metadata_timings.ipynb @@ -25,7 +25,7 @@ } ], "source": [ - "1+1" + "1 + 1" ] }, { diff --git a/tests/test4plus.ipynb b/tests/test4plus.ipynb index 522df071..2b61a123 100644 --- a/tests/test4plus.ipynb +++ b/tests/test4plus.ipynb @@ -84,12 +84,15 @@ ], "source": [ "from IPython.display import HTML\n", - "HTML(\"\"\"\n", + "\n", + "HTML(\n", + " \"\"\"\n", "<script>\n", "console.log(\"hello\");\n", "</script>\n", "<b>HTML</b>\n", - "\"\"\")" + "\"\"\"\n", + ")" ] }, { @@ -313,6 +316,7 @@ ], "source": [ "from IPython.display import Image\n", + "\n", "Image(\"http://ipython.org/_static/IPy_header.png\")" ] }, diff --git a/tests/test_reader.py b/tests/test_reader.py index 4f3e2f5f..fbdf99f6 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -17,6 +17,7 @@ from .base import TestsBase + # ----------------------------------------------------------------------------- # Classes and functions # -----------------------------------------------------------------------------