From 3d11ee52e245e4ff458aadadca78fe9e901ad395 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 9 Sep 2023 10:14:44 -0500 Subject: [PATCH 1/8] cleanup --- .pre-commit-config.yaml | 36 ++++++++++++++++++++++++++++++++++-- pyproject.toml | 28 ++++++++++++++++------------ 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b365818..b646360 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,6 @@ ci: autoupdate_schedule: monthly + autoupdate_commit_msg: "chore: update pre-commit hooks" repos: - repo: https://github.com/pre-commit/pre-commit-hooks @@ -29,13 +30,44 @@ repos: hooks: - id: mdformat - - repo: https://github.com/psf/black + - repo: https://github.com/pre-commit/mirrors-prettier + rev: "v3.0.2" + hooks: + - id: prettier + types_or: [yaml, html, json] + + - repo: https://github.com/adamchainz/blacken-docs + rev: "1.16.0" + hooks: + - id: blacken-docs + additional_dependencies: [black==23.7.0] + + - repo: https://github.com/psf/black-pre-commit-mirror rev: 23.7.0 hooks: - id: black + - repo: https://github.com/codespell-project/codespell + rev: "v2.2.5" + hooks: + - id: codespell + args: ["-L", "sur,nd"] + + - repo: https://github.com/pre-commit/pygrep-hooks + rev: "v1.10.0" + hooks: + - id: rst-backticks + - id: rst-directive-colons + - id: rst-inline-touching-normal + - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.0.287 hooks: - id: ruff - args: ["--fix"] + args: ["--fix", "--show-fixes"] + + - repo: https://github.com/scientific-python/cookie + rev: "2023.08.23" + hooks: + - id: sp-repo-review + additional_dependencies: ["repo-review[cli]"] diff --git a/pyproject.toml b/pyproject.toml index 498a499..68235eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,7 +72,7 @@ test = "python -m pytest -n auto -vv --migration-tests True" [tool.hatch.envs.typing] features = ["test"] -dependencies = ["mypy>=0.990"] +dependencies = ["mypy>=1.5.1"] [tool.hatch.envs.typing.scripts] test = "mypy --install-types --non-interactive {args:hatch_jupyter_builder/**/*.py tests/*.py}" @@ -95,7 +95,13 @@ fmt = [ ] [tool.pytest.ini_options] -addopts = "-raXs --durations 10 --color=yes --doctest-modules" +minversion = "6.0" +xfail_strict = true +log_cli_level = "info" +addopts = [ + "-raXs", "--durations=10", "--color=yes", "--doctest-modules", + "--showlocals", "--strict-markers", "--strict-config" +] testpaths = [ "tests/" ] @@ -114,17 +120,12 @@ relative_files = true source = ["hatch_jupyter_builder"] [tool.mypy] -check_untyped_defs = true -disallow_incomplete_defs = true -no_implicit_optional = true -pretty = true -show_error_context = true +python_version = "3.8" +strict = true show_error_codes = true -strict_equality = true -warn_unused_configs = true -warn_unused_ignores = true -warn_redundant_casts = true -exclude = ["tests/data/**/*.*"] +enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] +warn_unreachable = true +files = ["hatch_jupyter_builder"] [tool.black] line-length = 100 @@ -204,3 +205,6 @@ ignore-nested-functions=true ignore-nested-classes=true fail-under=100 exclude = ["docs", "tests"] + +[tool.repo-review] +ignore = ["PY007", "PP308", "GH102", "PC140"] From 73eb4637bb8c74664b21f5f46b3bf7a0da9299a3 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 9 Sep 2023 10:16:26 -0500 Subject: [PATCH 2/8] cleanup --- .github/workflows/tests.yml | 4 ++-- .readthedocs.yml | 3 +-- README.md | 2 +- docs/index.rst | 2 +- docs/source/background/index.rst | 2 +- docs/source/get_started/index.rst | 5 +++-- hatch_jupyter_builder/migrate/_migrate.py | 2 +- .../create_cmdclass/myproject/docs/source/installing.rst | 2 +- .../create_cmdclass/myproject/src/__tests__/index.spec.ts | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3537f45..e2fd23d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -98,8 +98,8 @@ jobs: steps: - uses: actions/checkout@v3 - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - - name: Run Linters - run: | + - name: Run Linters + run: | hatch run typing:test hatch run lint:style pipx run interrogate -v . diff --git a/.readthedocs.yml b/.readthedocs.yml index 84c4c3e..86d0aea 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,4 +1,3 @@ - version: 2 sphinx: configuration: docs/conf.py @@ -12,4 +11,4 @@ python: build: os: ubuntu-22.04 tools: - python: "3.11" + python: "3.11" diff --git a/README.md b/README.md index 662adb8..b4633eb 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ This provides a [build hook](https://hatch.pypa.io/latest/config/build/#build-ho ## Documentation -The full documenation is available on [Read The Docs](https://hatch-jupyter-builder.readthedocs.io/en/latest/). +The full documentation is available on [Read The Docs](https://hatch-jupyter-builder.readthedocs.io/en/latest/). ## Installation diff --git a/docs/index.rst b/docs/index.rst index 2869297..2973047 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,7 +1,7 @@ .. hatch-jupyter-builder documentation master file, created by sphinx-quickstart on Mon Jul 4 07:51:40 2022. You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. + contain the root ``toctree`` directive. Welcome to hatch-jupyter-builder's documentation! ================================================= diff --git a/docs/source/background/index.rst b/docs/source/background/index.rst index 7d34a79..bb5b51e 100644 --- a/docs/source/background/index.rst +++ b/docs/source/background/index.rst @@ -2,7 +2,7 @@ Background ========== Explanation. Clarification and discussion of key topics. -Hatch Jupyter Builder is a plugin for the `hatchling` Python build backend. +Hatch Jupyter Builder is a plugin for the ``hatchling`` Python build backend. It is primarily targeted for package authors who are providing JavaScript as part of their Python packages. diff --git a/docs/source/get_started/index.rst b/docs/source/get_started/index.rst index 8db1f6b..ffe8e2a 100644 --- a/docs/source/get_started/index.rst +++ b/docs/source/get_started/index.rst @@ -8,7 +8,8 @@ Tutorials. A hands-on introduction to Hatch Jupyter Builder for maintainers. config -If starting from a new project, use `hatch new "my project name"`, and follow -along with the prompts. Then add the appropriate [configuration](./config) for `hatch_jupyter_builder` in your `pyproject.toml` flag. +If starting from a new project, use ``hatch new "my project name"``, and follow +along with the prompts. Then add the appropriate [configuration](./config) for +``hatch_jupyter_builder`` in your ``pyproject.toml`` flag. If migrating from existing project, find the appropriate [how to guides](../how_to_guides). diff --git a/hatch_jupyter_builder/migrate/_migrate.py b/hatch_jupyter_builder/migrate/_migrate.py index 6f553f0..866abe4 100644 --- a/hatch_jupyter_builder/migrate/_migrate.py +++ b/hatch_jupyter_builder/migrate/_migrate.py @@ -57,7 +57,7 @@ ) else: warnings.append("Fill in '[project][version]' in 'pyproject.toml'") - current_version = "!!UNKONWN!!" + current_version = "!!UNKNOWN!!" # Run the hatch migration script. logger.info("Running hatch migration") diff --git a/tests/data/create_cmdclass/myproject/docs/source/installing.rst b/tests/data/create_cmdclass/myproject/docs/source/installing.rst index 6a56475..d714b5b 100644 --- a/tests/data/create_cmdclass/myproject/docs/source/installing.rst +++ b/tests/data/create_cmdclass/myproject/docs/source/installing.rst @@ -29,7 +29,7 @@ with:: If you are installing using conda, these commands should be unnecessary, but If you need to run them the commands should be the same (just make sure you choose the -`--sys-prefix` flag). +``--sys-prefix`` flag). .. links diff --git a/tests/data/create_cmdclass/myproject/src/__tests__/index.spec.ts b/tests/data/create_cmdclass/myproject/src/__tests__/index.spec.ts index 407f930..56444c1 100644 --- a/tests/data/create_cmdclass/myproject/src/__tests__/index.spec.ts +++ b/tests/data/create_cmdclass/myproject/src/__tests__/index.spec.ts @@ -16,7 +16,7 @@ describe('Example', () => { expect(model.get('value')).toEqual('Hello World'); }); - it('should be createable with a value', () => { + it('should be creatable with a value', () => { const state = { value: 'Foo Bar!' }; const model = createTestModel(ExampleModel, state); expect(model).toBeInstanceOf(ExampleModel); From 64c799bb447c3191bb55d77d3e3821039aeb793d Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 9 Sep 2023 10:35:30 -0500 Subject: [PATCH 3/8] Add strict typing --- .github/dependabot.yml | 7 +++ hatch_jupyter_builder/compare_migrated/cli.py | 25 +++++---- hatch_jupyter_builder/hooks.py | 4 +- hatch_jupyter_builder/migrate/_migrate.py | 2 +- hatch_jupyter_builder/migrate/cli.py | 2 +- .../migrate/jupyter_packaging.py | 56 ++++++++++++------- hatch_jupyter_builder/plugin.py | 21 ++++--- hatch_jupyter_builder/py.typed | 0 hatch_jupyter_builder/utils.py | 40 ++++++------- pyproject.toml | 2 +- 10 files changed, 95 insertions(+), 64 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 hatch_jupyter_builder/py.typed diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..6fddca0 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/hatch_jupyter_builder/compare_migrated/cli.py b/hatch_jupyter_builder/compare_migrated/cli.py index 8e4e385..2da5b59 100644 --- a/hatch_jupyter_builder/compare_migrated/cli.py +++ b/hatch_jupyter_builder/compare_migrated/cli.py @@ -1,4 +1,6 @@ """Compare the dist file created by a migrated package to one created by the original.""" +from __future__ import annotations + import argparse import glob import logging @@ -8,10 +10,9 @@ import sys import tarfile import zipfile -from typing import Optional -def build_file(dirname, dist_name): +def build_file(dirname: str, dist_name: str) -> None: """Build a dist file in a directory.""" orig_dir = os.getcwd() os.chdir(dirname) @@ -21,21 +22,21 @@ def build_file(dirname, dist_name): os.chdir(orig_dir) -def get_tar_names(dirname): +def get_tar_names(dirname: str) -> set[str]: """Get the tarball names in a directory.""" dist_file = glob.glob(f"{dirname}/dist/*.tar.gz")[0] tarf = tarfile.open(dist_file, "r:gz") return set(tarf.getnames()) -def get_zip_names(dirname): +def get_zip_names(dirname: str) -> set[str]: """Get the zip (wheel) file names in a directory.""" wheel_file = glob.glob(f"{dirname}/dist/*.whl")[0] with zipfile.ZipFile(wheel_file, "r") as f: return set(f.namelist()) -def filter_file(path): +def filter_file(path: str) -> bool: """Filter a file path for interesting files.""" if "egg-info" in path: return True @@ -47,7 +48,7 @@ def filter_file(path): return False -def main(source_dir, target_dir, dist_name): +def main(source_dir: str, target_dir: str, dist_name: str) -> dict[str, list[str]]: """The main script.""" subprocess.check_call([sys.executable, "-m", "pip", "install", "build"]) @@ -64,17 +65,17 @@ def main(source_dir, target_dir, dist_name): source_names = get_zip_names(source_dir) target_names = get_zip_names(target_dir) - removed = source_names - target_names + removed = list(source_names - target_names) removed = [r for r in removed if not filter_file(r)] if removed: logger.info("\nRemoved_files:") - [logger.info(f) for f in removed] # type:ignore + [logger.info(f) for f in removed] # type:ignore[func-returns-value] - added = target_names - source_names + added = list(target_names - source_names) added = [a for a in added if not filter_file(a)] if added: logger.info("\nAdded files:") - [logger.info(f) for f in added] # type:ignore + [logger.info(f) for f in added] # type:ignore[func-returns-value] logger.info("") @@ -82,7 +83,7 @@ def main(source_dir, target_dir, dist_name): def make_parser( - parser: Optional[argparse.ArgumentParser] = None, prog: Optional[str] = None + parser: argparse.ArgumentParser | None = None, prog: str | None = None ) -> argparse.ArgumentParser: """Make an arg parser.""" if parser is None: @@ -93,7 +94,7 @@ def make_parser( return parser -def run(args: Optional[argparse.Namespace] = None) -> None: +def run(args: argparse.Namespace | None = None) -> None: """Run the cli.""" if args is None: parser = make_parser(prog=f"{sys.executable} -m hatch_jupyter_builder.compare_migrated") diff --git a/hatch_jupyter_builder/hooks.py b/hatch_jupyter_builder/hooks.py index be851d9..97e27ce 100644 --- a/hatch_jupyter_builder/hooks.py +++ b/hatch_jupyter_builder/hooks.py @@ -1,10 +1,12 @@ """Register hooks for the plugin.""" +from typing import Type + from hatchling.plugin import hookimpl from .plugin import JupyterBuildHook @hookimpl -def hatch_register_build_hook(): +def hatch_register_build_hook() -> Type[JupyterBuildHook]: """Get the hook implementation.""" return JupyterBuildHook diff --git a/hatch_jupyter_builder/migrate/_migrate.py b/hatch_jupyter_builder/migrate/_migrate.py index 866abe4..97553fe 100644 --- a/hatch_jupyter_builder/migrate/_migrate.py +++ b/hatch_jupyter_builder/migrate/_migrate.py @@ -91,7 +91,7 @@ if not matches: continue - if matches and line.startswith("["): + if line.startswith("["): break flake8.append(line) diff --git a/hatch_jupyter_builder/migrate/cli.py b/hatch_jupyter_builder/migrate/cli.py index f9c1b64..a7b5253 100644 --- a/hatch_jupyter_builder/migrate/cli.py +++ b/hatch_jupyter_builder/migrate/cli.py @@ -13,7 +13,7 @@ from hatch_jupyter_builder import __version__ as builder_version -def main(td, target_dir): +def main(td: str, target_dir: str) -> None: """Main script.""" logger = logging.getLogger(__name__) logging.basicConfig() diff --git a/hatch_jupyter_builder/migrate/jupyter_packaging.py b/hatch_jupyter_builder/migrate/jupyter_packaging.py index b2072e6..3a3b5f5 100644 --- a/hatch_jupyter_builder/migrate/jupyter_packaging.py +++ b/hatch_jupyter_builder/migrate/jupyter_packaging.py @@ -1,7 +1,10 @@ """Shim for jupyter packaging migration.""" +from __future__ import annotations + import os import sys from pathlib import Path +from typing import Any import tomli import tomli_w @@ -9,13 +12,13 @@ __this_shim = sys.modules.pop("jupyter_packaging") __current_directory = sys.path.pop(0) -import jupyter_packaging as __real_jupyter_packaging # type:ignore +import jupyter_packaging as __real_jupyter_packaging # type:ignore[import] sys.path.insert(0, __current_directory) sys.modules["jupyter_packaging"] = __this_shim -def _write_config(path, data): +def _write_config(path: Any, data: Any) -> None: pyproject = Path("pyproject.toml") top = tomli.loads(pyproject.read_text(encoding="utf-8")) current = top @@ -36,7 +39,7 @@ def _write_config(path, data): _npm_kwargs = ["path", "build_dir", "source_dir", "build_cmd", "npm"] -def _normalize_path(path): +def _normalize_path(path: str) -> str: path = str(path) cwd = os.getcwd() if path.startswith(cwd): @@ -44,7 +47,7 @@ def _normalize_path(path): return path -def _get_build_kwargs(**kwargs): +def _get_build_kwargs(**kwargs: Any) -> dict[str, Any]: build_kwargs = {} for name in _npm_kwargs: value = kwargs[name] @@ -57,7 +60,7 @@ def _get_build_kwargs(**kwargs): return build_kwargs -def skip_if_exists(paths, *args): +def skip_if_exists(paths: list[str], *args: str) -> Any: """Shim for skip if exists""" if paths: data = {"skip-if-exists": [_normalize_path(p) for p in paths]} @@ -65,7 +68,7 @@ def skip_if_exists(paths, *args): return __real_jupyter_packaging.skip_if_exists(paths, *args) -def ensure_targets(targets): +def ensure_targets(targets: list[str]) -> Any: """Shim for ensure targets""" if targets: data = {"ensured-targets": [_normalize_path(t) for t in targets]} @@ -74,13 +77,13 @@ def ensure_targets(targets): def wrap_installers( - pre_develop=None, - pre_dist=None, - post_develop=None, - post_dist=None, - ensured_targets=None, - skip_if_exists=None, -): + pre_develop: Any = None, + pre_dist: Any = None, + post_develop: Any = None, + post_dist: Any = None, + ensured_targets: Any = None, + skip_if_exists: Any = None, +) -> Any: """Shim for wrap_installers.""" if pre_develop or post_develop: func = pre_develop or post_develop @@ -107,8 +110,11 @@ def wrap_installers( def create_cmdclass( - prerelease_cmd=None, package_data_spec=None, data_files_spec=None, exclude=None -): + prerelease_cmd: Any = None, + package_data_spec: Any = None, + data_files_spec: Any = None, + exclude: Any = None, +) -> Any: """Shim for create_cmdclass.""" shared_data = {} if data_files_spec is not None: @@ -131,8 +137,13 @@ def create_cmdclass( def install_npm( - path=None, build_dir=None, source_dir=None, build_cmd="build", force=False, npm=None -): + path: Any = None, + build_dir: Any = None, + source_dir: Any = None, + build_cmd: str = "build", + force: bool = False, + npm: Any = None, +) -> Any: """Shim for install_npm.""" build_kwargs = _get_build_kwargs(**locals()) if build_kwargs: @@ -149,8 +160,13 @@ def install_npm( def npm_builder( - path=None, build_dir=None, source_dir=None, build_cmd="build", force=False, npm=None -): + path: Any = None, + build_dir: Any = None, + source_dir: Any = None, + build_cmd: str = "build", + force: bool = False, + npm: Any = None, +) -> Any: """Shim for npm_builder.""" func = __real_jupyter_packaging.npm_builder( path=path, @@ -166,7 +182,7 @@ def npm_builder( return func -def __getattr__(name): +def __getattr__(name: str) -> Any: """Defer to the original for all others.""" return getattr(__real_jupyter_packaging, name) diff --git a/hatch_jupyter_builder/plugin.py b/hatch_jupyter_builder/plugin.py index b1202ea..a0ed247 100644 --- a/hatch_jupyter_builder/plugin.py +++ b/hatch_jupyter_builder/plugin.py @@ -1,9 +1,12 @@ """The main plugin for hatch_jupyter_builder.""" +from __future__ import annotations + import os import typing as t import warnings from dataclasses import dataclass, field, fields +from hatchling.builders.config import BuilderConfig from hatchling.builders.hooks.plugin.interface import BuildHookInterface from .utils import ( @@ -17,34 +20,34 @@ @dataclass -class JupyterBuildConfig: +class JupyterBuildConfig(BuilderConfig): """Build config values for Hatch Jupyter Builder.""" install_pre_commit_hook: str = "" - build_function: t.Optional[str] = None + build_function: str | None = None build_kwargs: t.Mapping[str, str] = field(default_factory=dict) editable_build_kwargs: t.Mapping[str, str] = field(default_factory=dict) - ensured_targets: t.List[str] = field(default_factory=list) - skip_if_exists: t.List[str] = field(default_factory=list) + ensured_targets: list[str] = field(default_factory=list) + skip_if_exists: list[str] = field(default_factory=list) optional_editable_build: str = "" -class JupyterBuildHook(BuildHookInterface): +class JupyterBuildHook(BuildHookInterface[JupyterBuildConfig]): """The hatch jupyter builder build hook.""" PLUGIN_NAME = "jupyter-builder" - def initialize(self, version, build_data): + def initialize(self, version: str, build_data: dict[str, t.Any]) -> None: """Initialize the plugin.""" log = _get_log() log.info("Running jupyter-builder") if self.target_name not in ["wheel", "sdist"]: log.info(f"ignoring target name {self.target_name}") - return False + return if os.getenv("SKIP_JUPYTER_BUILDER"): log.info("Skipping the build hook since SKIP_JUPYTER_BUILDER was set") - return False + return kwargs = normalize_kwargs(self.config) available_fields = [f.name for f in fields(JupyterBuildConfig)] @@ -93,4 +96,4 @@ def initialize(self, version, build_data): ensure_targets(config.ensured_targets) log.info("Finished running jupyter-builder") - return True + return diff --git a/hatch_jupyter_builder/py.typed b/hatch_jupyter_builder/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/hatch_jupyter_builder/utils.py b/hatch_jupyter_builder/utils.py index 6bd4972..7dc1703 100644 --- a/hatch_jupyter_builder/utils.py +++ b/hatch_jupyter_builder/utils.py @@ -1,4 +1,6 @@ """Utilities for hatch_jupyter_builder.""" +from __future__ import annotations + import importlib import logging import os @@ -7,13 +9,13 @@ import sys from pathlib import Path from shutil import which -from typing import Any, Callable, Dict, List, Mapping, Optional, Union +from typing import Any, Callable, Mapping, cast if sys.platform == "win32": # pragma: no cover from subprocess import list2cmdline else: - def list2cmdline(cmd_list): + def list2cmdline(cmd_list: Any) -> str: """Implementation of list2cmdline for posix systems.""" return " ".join(map(shlex.quote, cmd_list)) @@ -24,7 +26,7 @@ def list2cmdline(cmd_list): def _get_log() -> logging.Logger: global _logger # noqa if _logger: - return _logger + return _logger # type:ignore[unreachable] _logger = logging.getLogger(__name__) _logger.setLevel(logging.INFO) logging.basicConfig(level=logging.INFO) @@ -35,12 +37,12 @@ def npm_builder( target_name: str, version: str, path: str = ".", - build_dir: Optional[str] = None, - source_dir: Optional[str] = None, - build_cmd: Optional[str] = "build", + build_dir: str | None = None, + source_dir: str | None = None, + build_cmd: str | None = "build", force: bool = False, - npm: Optional[Union[str, List]] = None, - editable_build_cmd: Optional[str] = None, + npm: str | list[Any] | None = None, + editable_build_cmd: str | None = None, ) -> None: """Build function for managing an npm installation. @@ -117,7 +119,7 @@ def npm_builder( log.info("No build required") -def is_stale(target: Union[str, Path], source: Union[str, Path]) -> bool: +def is_stale(target: str | Path, source: str | Path) -> bool: """Test whether the target file/directory is stale based on the source file/directory. """ @@ -129,7 +131,7 @@ def is_stale(target: Union[str, Path], source: Union[str, Path]) -> bool: return compare_recursive_mtime(source, cutoff=target_mtime) -def compare_recursive_mtime(path: Union[str, Path], cutoff: float, newest: bool = True) -> bool: +def compare_recursive_mtime(path: str | Path, cutoff: float, newest: bool = True) -> bool: """Compare the newest/oldest mtime for all files in a directory. Cutoff should be another mtime to be compared against. If an mtime that is newer/older than the cutoff is found it will return True. @@ -155,7 +157,7 @@ def compare_recursive_mtime(path: Union[str, Path], cutoff: float, newest: bool return False -def recursive_mtime(path: Union[str, Path], newest: bool = True) -> float: +def recursive_mtime(path: str | Path, newest: bool = True) -> float: """Gets the newest/oldest mtime for all files in a directory.""" path = Path(path) if path.is_file(): @@ -172,7 +174,7 @@ def recursive_mtime(path: Union[str, Path], newest: bool = True) -> float: return current_extreme -def mtime(path: Union[str, Path]) -> float: +def mtime(path: str | Path) -> float: """shorthand for mtime""" return Path(path).stat().st_mtime @@ -189,10 +191,10 @@ def get_build_func(build_func_str: str) -> Callable[..., None]: finally: sys.path.pop(0) - return getattr(mod, func_name) + return cast(Callable[..., None], getattr(mod, func_name)) -def normalize_cmd(cmd: Union[str, list]) -> List[str]: +def normalize_cmd(cmd: str | list[Any]) -> list[str]: """Normalize a subprocess command.""" if not isinstance(cmd, (list, tuple)): cmd = shlex.split(cmd, posix=os.name != "nt") @@ -210,7 +212,7 @@ def normalize_cmd(cmd: Union[str, list]) -> List[str]: return cmd -def normalize_kwargs(kwargs: Mapping[str, str]) -> Dict[str, Any]: +def normalize_kwargs(kwargs: Mapping[str, Any]) -> dict[str, Any]: """Normalize the key names in a kwargs input dictionary""" result = {} for key, value in kwargs.items(): @@ -220,7 +222,7 @@ def normalize_kwargs(kwargs: Mapping[str, str]) -> Dict[str, Any]: return result -def run(cmd: Union[str, list], **kwargs: Any) -> int: +def run(cmd: str | list[Any], **kwargs: Any) -> int: """Echo a command before running it.""" kwargs.setdefault("shell", os.name == "nt") cmd = normalize_cmd(cmd) @@ -229,7 +231,7 @@ def run(cmd: Union[str, list], **kwargs: Any) -> int: return subprocess.check_call(cmd, **kwargs) -def ensure_targets(ensured_targets: List[str]) -> None: +def ensure_targets(ensured_targets: list[str]) -> None: """Ensure that target files are available""" for target in ensured_targets: if not Path(target).exists(): @@ -238,14 +240,14 @@ def ensure_targets(ensured_targets: List[str]) -> None: _get_log().info("Ensured target(s) exist!") -def should_skip(skip_if_exists): +def should_skip(skip_if_exists: Any) -> bool: """Detect whether all the paths in skip_if_exists exist""" if not isinstance(skip_if_exists, list) or not len(skip_if_exists): return False return all(os.path.exists(p) for p in skip_if_exists) -def install_pre_commit_hook(): +def install_pre_commit_hook() -> None: """Install a pre-commit hook.""" data = f"""#!/usr/bin/env bash INSTALL_PYTHON={sys.executable} diff --git a/pyproject.toml b/pyproject.toml index 68235eb..7fc1341 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ test = "python -m pytest -n auto -vv --migration-tests True" features = ["test"] dependencies = ["mypy>=1.5.1"] [tool.hatch.envs.typing.scripts] -test = "mypy --install-types --non-interactive {args:hatch_jupyter_builder/**/*.py tests/*.py}" +test = "mypy --install-types --non-interactive {args}" [tool.hatch.envs.lint] dependencies = [ From 8d4c842eea15e74dd00069fd68a8f8b4e2231cf7 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 9 Sep 2023 10:41:09 -0500 Subject: [PATCH 4/8] fix tests --- hatch_jupyter_builder/plugin.py | 6 +++++- tests/test_plugin.py | 35 ++++++++++++++++++++++----------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/hatch_jupyter_builder/plugin.py b/hatch_jupyter_builder/plugin.py index a0ed247..995bc8f 100644 --- a/hatch_jupyter_builder/plugin.py +++ b/hatch_jupyter_builder/plugin.py @@ -36,17 +36,21 @@ class JupyterBuildHook(BuildHookInterface[JupyterBuildConfig]): """The hatch jupyter builder build hook.""" PLUGIN_NAME = "jupyter-builder" + _skipped = False - def initialize(self, version: str, build_data: dict[str, t.Any]) -> None: + def initialize(self, version: str, build_data: dict[str, t.Any]) -> bool: """Initialize the plugin.""" + self._skipped = False log = _get_log() log.info("Running jupyter-builder") if self.target_name not in ["wheel", "sdist"]: log.info(f"ignoring target name {self.target_name}") + self._skipped = True return if os.getenv("SKIP_JUPYTER_BUILDER"): log.info("Skipping the build hook since SKIP_JUPYTER_BUILDER was set") + self._skipped = True return kwargs = normalize_kwargs(self.config) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 04e1dc2..80146b3 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -13,7 +13,7 @@ from hatch_jupyter_builder.plugin import JupyterBuildHook -def test_build_hook(tmp_path): +def test_build_hook(tmp_path): # noqa:PLR0915 manager = PluginManager() meta = ProjectMetadata(".", manager, {}) @@ -36,25 +36,33 @@ def foo(target_name, version, foo_bar=None, fizz_buzz=None): os.makedirs(".git/hooks") hook = JupyterBuildHook(tmp_path, config, {}, meta, tmp_path, "wheel") - assert hook.initialize("standard", {}) - assert hook.initialize("editable", {}) + hook.initialize("standard", {}) + assert not hook._skipped + hook.initialize("editable", {}) + assert not hook._skipped hook = JupyterBuildHook(tmp_path, config, {}, meta, tmp_path, "sdist") - assert hook.initialize("standard", {}) + hook.initialize("standard", {}) + assert not hook._skipped hook = JupyterBuildHook(tmp_path, {}, {}, meta, tmp_path, "wheel") - assert hook.initialize("standard", {}) - assert hook.initialize("editable", {}) + hook.initialize("standard", {}) + assert not hook._skipped + hook.initialize("editable", {}) + assert not hook._skipped config["skip-if-exists"] = ["foo", "bar"] - assert hook.initialize("standard", {}) + hook.initialize("standard", {}) + assert not hook._skipped del config["skip-if-exists"] config["editable-build-kwargs"] = {"foo-bar": "2", "fizz_buzz": "3"} - assert hook.initialize("editable", {}) + hook.initialize("editable", {}) + assert not hook._skipped hook = JupyterBuildHook(tmp_path, config, {}, meta, tmp_path, "foo") - assert not hook.initialize("standard", {}) + hook.initialize("standard", {}) + assert hook._skipped text = """ def foo(target_name, version, foo_bar=None, fizz_buzz=None): @@ -69,20 +77,23 @@ def foo(target_name, version, foo_bar=None, fizz_buzz=None): hook.initialize("editable", {}) os.environ["SKIP_JUPYTER_BUILDER"] = "1" - assert not hook.initialize("standard", {}) + hook.initialize("standard", {}) + assert hook._skipped del os.environ["SKIP_JUPYTER_BUILDER"] config["optional-editable-build"] = "true" hook = JupyterBuildHook(tmp_path, config, {}, meta, tmp_path, "wheel") with warnings.catch_warnings(): warnings.simplefilter("ignore") - assert hook.initialize("editable", {}) + hook.initialize("editable", {}) + assert not hook._skipped config["optional-editable-build"] = True hook = JupyterBuildHook(tmp_path, config, {}, meta, tmp_path, "wheel") with warnings.catch_warnings(): warnings.simplefilter("ignore") - assert hook.initialize("editable", {}) + hook.initialize("editable", {}) + assert not hook._skipped del sys.modules["test"] From 8498450c6bf8d01a6578a033c3468ea829fcf321 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 9 Sep 2023 10:46:54 -0500 Subject: [PATCH 5/8] fixups --- hatch_jupyter_builder/plugin.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hatch_jupyter_builder/plugin.py b/hatch_jupyter_builder/plugin.py index 995bc8f..931e502 100644 --- a/hatch_jupyter_builder/plugin.py +++ b/hatch_jupyter_builder/plugin.py @@ -7,7 +7,9 @@ from dataclasses import dataclass, field, fields from hatchling.builders.config import BuilderConfig -from hatchling.builders.hooks.plugin.interface import BuildHookInterface + +if t.TYPE_CHECKING: + from hatchling.builders.hooks.plugin.interface import BuildHookInterface # noqa:F401 from .utils import ( _get_log, @@ -32,13 +34,13 @@ class JupyterBuildConfig(BuilderConfig): optional_editable_build: str = "" -class JupyterBuildHook(BuildHookInterface[JupyterBuildConfig]): +class JupyterBuildHook("BuildHookInterface[JupyterBuildConfig]"): """The hatch jupyter builder build hook.""" PLUGIN_NAME = "jupyter-builder" _skipped = False - def initialize(self, version: str, build_data: dict[str, t.Any]) -> bool: + def initialize(self, version: str, build_data: dict[str, t.Any]) -> None: """Initialize the plugin.""" self._skipped = False log = _get_log() From d5b3e92d58a9e7c672e403ce08268445d9792d63 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 9 Sep 2023 10:49:10 -0500 Subject: [PATCH 6/8] cleanup --- hatch_jupyter_builder/plugin.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hatch_jupyter_builder/plugin.py b/hatch_jupyter_builder/plugin.py index 931e502..f2cf689 100644 --- a/hatch_jupyter_builder/plugin.py +++ b/hatch_jupyter_builder/plugin.py @@ -7,9 +7,7 @@ from dataclasses import dataclass, field, fields from hatchling.builders.config import BuilderConfig - -if t.TYPE_CHECKING: - from hatchling.builders.hooks.plugin.interface import BuildHookInterface # noqa:F401 +from hatchling.builders.hooks.plugin.interface import BuildHookInterface from .utils import ( _get_log, @@ -34,7 +32,10 @@ class JupyterBuildConfig(BuilderConfig): optional_editable_build: str = "" -class JupyterBuildHook("BuildHookInterface[JupyterBuildConfig]"): +kls = BuildHookInterface[JupyterBuildConfig] if t.TYPE_CHECKING else BuildHookInterface + + +class JupyterBuildHook(kls): """The hatch jupyter builder build hook.""" PLUGIN_NAME = "jupyter-builder" From e497b1c23636ad0c79c42ee564e084498b5de978 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 9 Sep 2023 12:28:00 -0500 Subject: [PATCH 7/8] bump min version --- hatch_jupyter_builder/plugin.py | 5 +---- pyproject.toml | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/hatch_jupyter_builder/plugin.py b/hatch_jupyter_builder/plugin.py index f2cf689..efdc5b1 100644 --- a/hatch_jupyter_builder/plugin.py +++ b/hatch_jupyter_builder/plugin.py @@ -32,10 +32,7 @@ class JupyterBuildConfig(BuilderConfig): optional_editable_build: str = "" -kls = BuildHookInterface[JupyterBuildConfig] if t.TYPE_CHECKING else BuildHookInterface - - -class JupyterBuildHook(kls): +class JupyterBuildHook(BuildHookInterface[JupyterBuildConfig]): """The hatch jupyter builder build hook.""" PLUGIN_NAME = "jupyter-builder" diff --git a/pyproject.toml b/pyproject.toml index 7fc1341..c446fec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["hatchling>=1.5"] +requires = ["hatchling>=1.7.0"] build-backend = "hatchling.build" [project] @@ -24,7 +24,7 @@ classifiers = [ "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", ] -dependencies = ["hatchling>=1.5"] +dependencies = ["hatchling>=1.7.0"] [project.urls] Documentation = "https://github.com/jupyterlab/hatch-jupyter-builder#readme" From aa8fa26a9629eee25671e0d3cd4e3ed528d75a76 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 9 Sep 2023 12:30:37 -0500 Subject: [PATCH 8/8] bump min version --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c446fec..3259d23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["hatchling>=1.7.0"] +requires = ["hatchling>=1.17.0"] build-backend = "hatchling.build" [project] @@ -24,7 +24,7 @@ classifiers = [ "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", ] -dependencies = ["hatchling>=1.7.0"] +dependencies = ["hatchling>=1.17.0"] [project.urls] Documentation = "https://github.com/jupyterlab/hatch-jupyter-builder#readme"