From 62fb515f46f9c5d027db0326af67f7042542bfa7 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Fri, 7 Apr 2023 11:02:36 +0100 Subject: [PATCH 01/19] Add changelog section for next release --- docs/changelog.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 607ac05..7c08eea 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,11 @@ Changelog ========= +v1.1 +---- + +No changes. + v1.0 ---- From cb566f5ec133a2231174ca09f57a34f7f0fb664b Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Tue, 4 Apr 2023 18:55:03 +0100 Subject: [PATCH 02/19] Type annotate the public API This makes it easier for tools to provide completion and enhances the documentation of the API. --- .pre-commit-config.yaml | 6 ++ src/pyproject_hooks/_impl.py | 113 +++++++++++++++++++++++++---------- 2 files changed, 89 insertions(+), 30 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ce2a162..2873424 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,6 +9,12 @@ repos: hooks: - id: black + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.1.1 + hooks: + - id: mypy + exclude: tests/samples + - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: diff --git a/src/pyproject_hooks/_impl.py b/src/pyproject_hooks/_impl.py index 68db11f..19a5e6e 100644 --- a/src/pyproject_hooks/_impl.py +++ b/src/pyproject_hooks/_impl.py @@ -6,16 +6,31 @@ from os.path import abspath from os.path import join as pjoin from subprocess import STDOUT, check_call, check_output +from typing import TYPE_CHECKING, Any, Dict, Iterator, List, Optional from ._in_process import _in_proc_script_path +if TYPE_CHECKING: + from typing import Protocol -def write_json(obj, path, **kwargs): + class SubprocessRunner(Protocol): + """A protocol for the subprocess runner.""" + + def __call__( + self, + cmd: List[str], + cwd: Optional[str] = None, + extra_environ: Optional[Dict[str, str]] = None, + ) -> None: + ... + + +def write_json(obj: Dict[str, Any], path: str, **kwargs) -> None: with open(path, "w", encoding="utf-8") as f: json.dump(obj, f, **kwargs) -def read_json(path): +def read_json(path: str) -> Dict[str, Any]: with open(path, encoding="utf-8") as f: return json.load(f) @@ -23,14 +38,19 @@ def read_json(path): class BackendUnavailable(Exception): """Will be raised if the backend cannot be imported in the hook process.""" - def __init__(self, traceback): + def __init__(self, traceback: str) -> None: self.traceback = traceback class BackendInvalid(Exception): """Will be raised if the backend is invalid.""" - def __init__(self, backend_name, backend_path, message): + def __init__( + self, + backend_name: str, + backend_path: Optional[List[str]], + message: str, + ) -> None: super().__init__(message) self.backend_name = backend_name self.backend_path = backend_path @@ -39,7 +59,7 @@ def __init__(self, backend_name, backend_path, message): class HookMissing(Exception): """Will be raised on missing hooks (if a fallback can't be used).""" - def __init__(self, hook_name): + def __init__(self, hook_name: str) -> None: super().__init__(hook_name) self.hook_name = hook_name @@ -47,11 +67,15 @@ def __init__(self, hook_name): class UnsupportedOperation(Exception): """May be raised by build_sdist if the backend indicates that it can't.""" - def __init__(self, traceback): + def __init__(self, traceback: str) -> None: self.traceback = traceback -def default_subprocess_runner(cmd, cwd=None, extra_environ=None): +def default_subprocess_runner( + cmd: List[str], + cwd: Optional[str] = None, + extra_environ: Optional[Dict[str, str]] = None, +) -> None: """The default method of calling the wrapper subprocess. This uses :func:`subprocess.check_call` under the hood. @@ -63,7 +87,11 @@ def default_subprocess_runner(cmd, cwd=None, extra_environ=None): check_call(cmd, cwd=cwd, env=env) -def quiet_subprocess_runner(cmd, cwd=None, extra_environ=None): +def quiet_subprocess_runner( + cmd: List[str], + cwd: Optional[str] = None, + extra_environ: Optional[Dict[str, str]] = None, +) -> None: """Call the subprocess while suppressing output. This uses :func:`subprocess.check_output` under the hood. @@ -75,7 +103,7 @@ def quiet_subprocess_runner(cmd, cwd=None, extra_environ=None): check_output(cmd, cwd=cwd, env=env, stderr=STDOUT) -def norm_and_check(source_tree, requested): +def norm_and_check(source_tree: str, requested: str) -> str: """Normalise and check a backend path. Ensure that the requested backend path is specified as a relative path, @@ -104,12 +132,12 @@ class BuildBackendHookCaller: def __init__( self, - source_dir, - build_backend, - backend_path=None, - runner=None, - python_executable=None, - ): + source_dir: str, + build_backend: str, + backend_path: Optional[List[str]] = None, + runner: Optional["SubprocessRunner"] = None, + python_executable: Optional[str] = None, + ) -> None: """ :param source_dir: The source directory to invoke the build backend for :param build_backend: The build backend spec @@ -132,7 +160,7 @@ def __init__( self.python_executable = python_executable @contextmanager - def subprocess_runner(self, runner): + def subprocess_runner(self, runner: "SubprocessRunner") -> Iterator[None]: """A context manager for temporarily overriding the default :ref:`subprocess runner `. @@ -149,11 +177,14 @@ def subprocess_runner(self, runner): finally: self._subprocess_runner = prev - def _supported_features(self): + def _supported_features(self) -> List[str]: """Return the list of optional features supported by the backend.""" return self._call_hook("_supported_features", {}) - def get_requires_for_build_wheel(self, config_settings=None): + def get_requires_for_build_wheel( + self, + config_settings: Optional[Dict[str, Any]] = None, + ) -> List[str]: """Get additional dependencies required for building a wheel. :returns: A list of :pep:`dependency specifiers <508>`. @@ -169,8 +200,11 @@ def get_requires_for_build_wheel(self, config_settings=None): ) def prepare_metadata_for_build_wheel( - self, metadata_directory, config_settings=None, _allow_fallback=True - ): + self, + metadata_directory: str, + config_settings: Optional[Dict[str, Any]] = None, + _allow_fallback: bool = True, + ) -> Optional[str]: """Prepare a ``*.dist-info`` folder with metadata for this project. :returns: Name of the newly created subfolder within @@ -194,8 +228,11 @@ def prepare_metadata_for_build_wheel( ) def build_wheel( - self, wheel_directory, config_settings=None, metadata_directory=None - ): + self, + wheel_directory: str, + config_settings: Optional[Dict[str, Any]] = None, + metadata_directory: Optional[str] = None, + ) -> str: """Build a wheel from this project. :returns: @@ -219,7 +256,10 @@ def build_wheel( }, ) - def get_requires_for_build_editable(self, config_settings=None): + def get_requires_for_build_editable( + self, + config_settings: Optional[Dict[str, Any]] = None, + ) -> List[str]: """Get additional dependencies required for building an editable wheel. :returns: A list of :pep:`dependency specifiers <508>`. @@ -235,8 +275,11 @@ def get_requires_for_build_editable(self, config_settings=None): ) def prepare_metadata_for_build_editable( - self, metadata_directory, config_settings=None, _allow_fallback=True - ): + self, + metadata_directory: str, + config_settings: Optional[Dict[str, Any]] = None, + _allow_fallback: bool = True, + ) -> Optional[str]: """Prepare a ``*.dist-info`` folder with metadata for this project. :returns: Name of the newly created subfolder within @@ -260,8 +303,11 @@ def prepare_metadata_for_build_editable( ) def build_editable( - self, wheel_directory, config_settings=None, metadata_directory=None - ): + self, + wheel_directory: str, + config_settings: Optional[Dict[str, Any]] = None, + metadata_directory: Optional[str] = None, + ) -> str: """Build an editable wheel from this project. :returns: @@ -286,7 +332,10 @@ def build_editable( }, ) - def get_requires_for_build_sdist(self, config_settings=None): + def get_requires_for_build_sdist( + self, + config_settings: Optional[Dict[str, Any]] = None, + ) -> List[str]: """Get additional dependencies required for building an sdist. :returns: A list of :pep:`dependency specifiers <508>`. @@ -296,7 +345,11 @@ def get_requires_for_build_sdist(self, config_settings=None): "get_requires_for_build_sdist", {"config_settings": config_settings} ) - def build_sdist(self, sdist_directory, config_settings=None): + def build_sdist( + self, + sdist_directory: str, + config_settings: Optional[Dict[str, Any]] = None, + ) -> str: """Build an sdist from this project. :returns: @@ -310,7 +363,7 @@ def build_sdist(self, sdist_directory, config_settings=None): }, ) - def _call_hook(self, hook_name, kwargs): + def _call_hook(self, hook_name: str, kwargs: Dict[str, Any]) -> Any: extra_environ = {"PEP517_BUILD_BACKEND": self.build_backend} if self.backend_path: From 88906ab623d221f050481e9ac800da977adb643b Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Wed, 5 Apr 2023 21:30:54 +0100 Subject: [PATCH 03/19] Update configuration for `ruff` This makes it behave better with newer ruff versions, and removes the unused isort configuration. --- pyproject.toml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ad93ea0..8e58eb3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,5 +22,8 @@ Source = "https://github.com/pypa/pyproject-hooks" Documentation = "https://pyproject-hooks.readthedocs.io/" Changelog = "https://pyproject-hooks.readthedocs.io/en/latest/changelog.html" -[tool.isort] -profile = "black" +[tool.ruff] +src = ["src", "tests"] + +[tool.ruff.isort] +known-first-party = ["pyproject_hooks", "tests"] From 69ed8b1dac92e33d6a2d18c1b3a3cfad57020d90 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Wed, 5 Apr 2023 21:30:10 +0100 Subject: [PATCH 04/19] Drop support for Python 3.7 This enables the use of newer Python typing functionality without compatibility shims. --- .github/workflows/ci.yml | 2 +- docs/changelog.rst | 2 +- noxfile.py | 2 +- pyproject.toml | 2 +- src/pyproject_hooks/_impl.py | 28 +++++++++++++--------------- 5 files changed, 17 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 422b44a..1f2e48e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11"] os: [Ubuntu, macOS, Windows] steps: diff --git a/docs/changelog.rst b/docs/changelog.rst index 7c08eea..0fe02a3 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,7 +4,7 @@ Changelog v1.1 ---- -No changes. +- Drop support for Python 3.7. v1.0 ---- diff --git a/noxfile.py b/noxfile.py index 66ff09f..7bde1e9 100644 --- a/noxfile.py +++ b/noxfile.py @@ -6,7 +6,7 @@ nox.options.reuse_existing_virtualenvs = True -@nox.session(python=["3.7", "3.8", "3.9", "3.10", "3.11", "pypy3"]) +@nox.session(python=["3.8", "3.9", "3.10", "3.11", "pypy3"]) def test(session: nox.Session) -> None: session.install("-r", "dev-requirements.txt") session.install(".") diff --git a/pyproject.toml b/pyproject.toml index 8e58eb3..e88812b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ authors = [ {name = "Thomas Kluyver", email = "thomas@kluyver.me.uk"}, ] readme = "README.rst" -requires-python = ">=3.7" +requires-python = ">=3.8" dependencies = [] classifiers = [ "License :: OSI Approved :: MIT License", diff --git a/src/pyproject_hooks/_impl.py b/src/pyproject_hooks/_impl.py index 19a5e6e..ed173eb 100644 --- a/src/pyproject_hooks/_impl.py +++ b/src/pyproject_hooks/_impl.py @@ -6,24 +6,10 @@ from os.path import abspath from os.path import join as pjoin from subprocess import STDOUT, check_call, check_output -from typing import TYPE_CHECKING, Any, Dict, Iterator, List, Optional +from typing import Any, Dict, Iterator, List, Optional, Protocol from ._in_process import _in_proc_script_path -if TYPE_CHECKING: - from typing import Protocol - - class SubprocessRunner(Protocol): - """A protocol for the subprocess runner.""" - - def __call__( - self, - cmd: List[str], - cwd: Optional[str] = None, - extra_environ: Optional[Dict[str, str]] = None, - ) -> None: - ... - def write_json(obj: Dict[str, Any], path: str, **kwargs) -> None: with open(path, "w", encoding="utf-8") as f: @@ -71,6 +57,18 @@ def __init__(self, traceback: str) -> None: self.traceback = traceback +class SubprocessRunner(Protocol): + """A protocol for the subprocess runner.""" + + def __call__( + self, + cmd: List[str], + cwd: Optional[str] = None, + extra_environ: Optional[Dict[str, str]] = None, + ) -> None: + ... + + def default_subprocess_runner( cmd: List[str], cwd: Optional[str] = None, From a2cd373a66926d8d5e472a2aca14200b520efbc9 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Fri, 7 Apr 2023 11:03:08 +0100 Subject: [PATCH 05/19] Add support for Python 3.12 --- .github/workflows/ci.yml | 2 +- noxfile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f2e48e..690f643 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] os: [Ubuntu, macOS, Windows] steps: diff --git a/noxfile.py b/noxfile.py index 7bde1e9..92eb395 100644 --- a/noxfile.py +++ b/noxfile.py @@ -6,7 +6,7 @@ nox.options.reuse_existing_virtualenvs = True -@nox.session(python=["3.8", "3.9", "3.10", "3.11", "pypy3"]) +@nox.session(python=["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3"]) def test(session: nox.Session) -> None: session.install("-r", "dev-requirements.txt") session.install(".") From 87c779b41f033329181a39ea8a29485a4d9aa24a Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Fri, 7 Apr 2023 11:10:39 +0100 Subject: [PATCH 06/19] Enforce a Python version in Read the Docs builds This ensures that the documentation build uses a well-defined Python version, rather than whatever happens to be the default for the environment. --- .readthedocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.readthedocs.yml b/.readthedocs.yml index 1881693..ffff8cf 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -4,6 +4,7 @@ sphinx: configuration: docs/conf.py python: + version: 3.8 install: - requirements: docs/requirements.txt - method: pip From 8a2adbf217a2c1f03e41210e38c075780c2bc534 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Fri, 7 Apr 2023 11:17:35 +0100 Subject: [PATCH 07/19] Add changelog entry for type annotations --- docs/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 0fe02a3..f74fd92 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog v1.1 ---- +- Add type annotations to the public API. - Drop support for Python 3.7. v1.0 From 9cf1649a071f4268578b0e7417eb017637c4a486 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sat, 8 Apr 2023 08:09:13 -0500 Subject: [PATCH 08/19] Revert "Drop support for Python 3.7" This reverts commit 69ed8b1dac92e33d6a2d18c1b3a3cfad57020d90. --- .github/workflows/ci.yml | 2 +- docs/changelog.rst | 1 - noxfile.py | 2 +- pyproject.toml | 2 +- src/pyproject_hooks/_impl.py | 28 +++++++++++++++------------- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 690f643..4518be2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] os: [Ubuntu, macOS, Windows] steps: diff --git a/docs/changelog.rst b/docs/changelog.rst index f74fd92..7f88cfa 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,7 +5,6 @@ v1.1 ---- - Add type annotations to the public API. -- Drop support for Python 3.7. v1.0 ---- diff --git a/noxfile.py b/noxfile.py index 92eb395..45417a9 100644 --- a/noxfile.py +++ b/noxfile.py @@ -6,7 +6,7 @@ nox.options.reuse_existing_virtualenvs = True -@nox.session(python=["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3"]) +@nox.session(python=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "pypy3"]) def test(session: nox.Session) -> None: session.install("-r", "dev-requirements.txt") session.install(".") diff --git a/pyproject.toml b/pyproject.toml index e88812b..8e58eb3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ authors = [ {name = "Thomas Kluyver", email = "thomas@kluyver.me.uk"}, ] readme = "README.rst" -requires-python = ">=3.8" +requires-python = ">=3.7" dependencies = [] classifiers = [ "License :: OSI Approved :: MIT License", diff --git a/src/pyproject_hooks/_impl.py b/src/pyproject_hooks/_impl.py index ed173eb..19a5e6e 100644 --- a/src/pyproject_hooks/_impl.py +++ b/src/pyproject_hooks/_impl.py @@ -6,10 +6,24 @@ from os.path import abspath from os.path import join as pjoin from subprocess import STDOUT, check_call, check_output -from typing import Any, Dict, Iterator, List, Optional, Protocol +from typing import TYPE_CHECKING, Any, Dict, Iterator, List, Optional from ._in_process import _in_proc_script_path +if TYPE_CHECKING: + from typing import Protocol + + class SubprocessRunner(Protocol): + """A protocol for the subprocess runner.""" + + def __call__( + self, + cmd: List[str], + cwd: Optional[str] = None, + extra_environ: Optional[Dict[str, str]] = None, + ) -> None: + ... + def write_json(obj: Dict[str, Any], path: str, **kwargs) -> None: with open(path, "w", encoding="utf-8") as f: @@ -57,18 +71,6 @@ def __init__(self, traceback: str) -> None: self.traceback = traceback -class SubprocessRunner(Protocol): - """A protocol for the subprocess runner.""" - - def __call__( - self, - cmd: List[str], - cwd: Optional[str] = None, - extra_environ: Optional[Dict[str, str]] = None, - ) -> None: - ... - - def default_subprocess_runner( cmd: List[str], cwd: Optional[str] = None, From 26460ba7a825320f620867e966708bb8d4ac7826 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sat, 8 Apr 2023 08:09:58 -0500 Subject: [PATCH 09/19] Add a `py.typed` file to denote type annotated sources --- src/pyproject_hooks/py.typed | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/pyproject_hooks/py.typed diff --git a/src/pyproject_hooks/py.typed b/src/pyproject_hooks/py.typed new file mode 100644 index 0000000..e69de29 From 58e28143329c878cecd397c281543f1b3630267b Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sat, 8 Apr 2023 08:12:54 -0500 Subject: [PATCH 10/19] Use more generic types in type annotations --- src/pyproject_hooks/_impl.py | 51 +++++++++++++++++------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/src/pyproject_hooks/_impl.py b/src/pyproject_hooks/_impl.py index 19a5e6e..06f7382 100644 --- a/src/pyproject_hooks/_impl.py +++ b/src/pyproject_hooks/_impl.py @@ -6,7 +6,7 @@ from os.path import abspath from os.path import join as pjoin from subprocess import STDOUT, check_call, check_output -from typing import TYPE_CHECKING, Any, Dict, Iterator, List, Optional +from typing import TYPE_CHECKING, Any, Iterator, Mapping, Optional, Sequence from ._in_process import _in_proc_script_path @@ -18,19 +18,19 @@ class SubprocessRunner(Protocol): def __call__( self, - cmd: List[str], + cmd: Sequence[str], cwd: Optional[str] = None, - extra_environ: Optional[Dict[str, str]] = None, + extra_environ: Optional[Mapping[str, str]] = None, ) -> None: ... -def write_json(obj: Dict[str, Any], path: str, **kwargs) -> None: +def write_json(obj: Mapping[str, Any], path: str, **kwargs) -> None: with open(path, "w", encoding="utf-8") as f: json.dump(obj, f, **kwargs) -def read_json(path: str) -> Dict[str, Any]: +def read_json(path: str) -> Mapping[str, Any]: with open(path, encoding="utf-8") as f: return json.load(f) @@ -48,7 +48,7 @@ class BackendInvalid(Exception): def __init__( self, backend_name: str, - backend_path: Optional[List[str]], + backend_path: Optional[Sequence[str]], message: str, ) -> None: super().__init__(message) @@ -72,9 +72,9 @@ def __init__(self, traceback: str) -> None: def default_subprocess_runner( - cmd: List[str], + cmd: Sequence[str], cwd: Optional[str] = None, - extra_environ: Optional[Dict[str, str]] = None, + extra_environ: Optional[Mapping[str, str]] = None, ) -> None: """The default method of calling the wrapper subprocess. @@ -88,9 +88,9 @@ def default_subprocess_runner( def quiet_subprocess_runner( - cmd: List[str], + cmd: Sequence[str], cwd: Optional[str] = None, - extra_environ: Optional[Dict[str, str]] = None, + extra_environ: Optional[Mapping[str, str]] = None, ) -> None: """Call the subprocess while suppressing output. @@ -134,7 +134,7 @@ def __init__( self, source_dir: str, build_backend: str, - backend_path: Optional[List[str]] = None, + backend_path: Optional[Sequence[str]] = None, runner: Optional["SubprocessRunner"] = None, python_executable: Optional[str] = None, ) -> None: @@ -177,18 +177,17 @@ def subprocess_runner(self, runner: "SubprocessRunner") -> Iterator[None]: finally: self._subprocess_runner = prev - def _supported_features(self) -> List[str]: + def _supported_features(self) -> Sequence[str]: """Return the list of optional features supported by the backend.""" return self._call_hook("_supported_features", {}) def get_requires_for_build_wheel( self, - config_settings: Optional[Dict[str, Any]] = None, - ) -> List[str]: + config_settings: Optional[Mapping[str, Any]] = None, + ) -> Sequence[str]: """Get additional dependencies required for building a wheel. :returns: A list of :pep:`dependency specifiers <508>`. - :rtype: list[str] .. admonition:: Fallback @@ -202,7 +201,7 @@ def get_requires_for_build_wheel( def prepare_metadata_for_build_wheel( self, metadata_directory: str, - config_settings: Optional[Dict[str, Any]] = None, + config_settings: Optional[Mapping[str, Any]] = None, _allow_fallback: bool = True, ) -> Optional[str]: """Prepare a ``*.dist-info`` folder with metadata for this project. @@ -230,7 +229,7 @@ def prepare_metadata_for_build_wheel( def build_wheel( self, wheel_directory: str, - config_settings: Optional[Dict[str, Any]] = None, + config_settings: Optional[Mapping[str, Any]] = None, metadata_directory: Optional[str] = None, ) -> str: """Build a wheel from this project. @@ -258,12 +257,11 @@ def build_wheel( def get_requires_for_build_editable( self, - config_settings: Optional[Dict[str, Any]] = None, - ) -> List[str]: + config_settings: Optional[Mapping[str, Any]] = None, + ) -> Sequence[str]: """Get additional dependencies required for building an editable wheel. :returns: A list of :pep:`dependency specifiers <508>`. - :rtype: list[str] .. admonition:: Fallback @@ -277,7 +275,7 @@ def get_requires_for_build_editable( def prepare_metadata_for_build_editable( self, metadata_directory: str, - config_settings: Optional[Dict[str, Any]] = None, + config_settings: Optional[Mapping[str, Any]] = None, _allow_fallback: bool = True, ) -> Optional[str]: """Prepare a ``*.dist-info`` folder with metadata for this project. @@ -305,7 +303,7 @@ def prepare_metadata_for_build_editable( def build_editable( self, wheel_directory: str, - config_settings: Optional[Dict[str, Any]] = None, + config_settings: Optional[Mapping[str, Any]] = None, metadata_directory: Optional[str] = None, ) -> str: """Build an editable wheel from this project. @@ -334,12 +332,11 @@ def build_editable( def get_requires_for_build_sdist( self, - config_settings: Optional[Dict[str, Any]] = None, - ) -> List[str]: + config_settings: Optional[Mapping[str, Any]] = None, + ) -> Sequence[str]: """Get additional dependencies required for building an sdist. :returns: A list of :pep:`dependency specifiers <508>`. - :rtype: list[str] """ return self._call_hook( "get_requires_for_build_sdist", {"config_settings": config_settings} @@ -348,7 +345,7 @@ def get_requires_for_build_sdist( def build_sdist( self, sdist_directory: str, - config_settings: Optional[Dict[str, Any]] = None, + config_settings: Optional[Mapping[str, Any]] = None, ) -> str: """Build an sdist from this project. @@ -363,7 +360,7 @@ def build_sdist( }, ) - def _call_hook(self, hook_name: str, kwargs: Dict[str, Any]) -> Any: + def _call_hook(self, hook_name: str, kwargs: Mapping[str, Any]) -> Any: extra_environ = {"PEP517_BUILD_BACKEND": self.build_backend} if self.backend_path: From 96a6691e8e19c5a6c24e4ac152e4839a0a9350aa Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sat, 8 Apr 2023 08:14:40 -0500 Subject: [PATCH 11/19] Correct the type of `prepare_metadata_for_build_wheel` --- src/pyproject_hooks/_impl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyproject_hooks/_impl.py b/src/pyproject_hooks/_impl.py index 06f7382..a657ab1 100644 --- a/src/pyproject_hooks/_impl.py +++ b/src/pyproject_hooks/_impl.py @@ -203,7 +203,7 @@ def prepare_metadata_for_build_wheel( metadata_directory: str, config_settings: Optional[Mapping[str, Any]] = None, _allow_fallback: bool = True, - ) -> Optional[str]: + ) -> str: """Prepare a ``*.dist-info`` folder with metadata for this project. :returns: Name of the newly created subfolder within From 5b264f595634e9156f4779ac5c96d2bb06213b5f Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sat, 8 Apr 2023 08:18:34 -0500 Subject: [PATCH 12/19] Use the correct directory for documentation gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 88cd89d..9f359b8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ __pycache__/ /dist/ .nox .pytest_cache -doc/_build/ +docs/_build/ *.egg-info/ .coverage htmlcov/ From 1017004c9aea20c61cffa5e454bb454fb4930f28 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sat, 8 Apr 2023 08:21:18 -0500 Subject: [PATCH 13/19] Drop the return types from docstrings This information is now coming from the source code directly. --- src/pyproject_hooks/_impl.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pyproject_hooks/_impl.py b/src/pyproject_hooks/_impl.py index a657ab1..3b773f4 100644 --- a/src/pyproject_hooks/_impl.py +++ b/src/pyproject_hooks/_impl.py @@ -208,7 +208,6 @@ def prepare_metadata_for_build_wheel( :returns: Name of the newly created subfolder within ``metadata_directory``, containing the metadata. - :rtype: str .. admonition:: Fallback @@ -282,7 +281,6 @@ def prepare_metadata_for_build_editable( :returns: Name of the newly created subfolder within ``metadata_directory``, containing the metadata. - :rtype: str .. admonition:: Fallback From cffda1f8bca50ffab5ed0d9d71cd48640a828dc7 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sat, 8 Apr 2023 08:22:05 -0500 Subject: [PATCH 14/19] Don't set an incorrect name on `nox -s release` --- noxfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index 45417a9..4efa446 100644 --- a/noxfile.py +++ b/noxfile.py @@ -40,7 +40,7 @@ def lint(session: nox.Session) -> None: session.run("pre-commit", "run", *args) -@nox.session(name="docs-live") +@nox.session def release(session: nox.Session) -> None: session.install("flit") session.run("flit", "publish") From 12401f6951d11fce011945aca73cbdbe562fa663 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sat, 8 Apr 2023 08:44:16 -0500 Subject: [PATCH 15/19] Fully document all parameters in the public API --- docs/conf.py | 3 +++ docs/pyproject_hooks.rst | 8 ++++---- src/pyproject_hooks/_impl.py | 21 +++++++++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 2865b9c..222b257 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -30,8 +30,11 @@ # -- Options for autodoc ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#configuration +autodoc_class_signature = "separated" autodoc_member_order = "bysource" +autodoc_preserve_defaults = True autodoc_typehints = "description" +autodoc_typehints_description_target = "documented_params" # -- Options for intersphinx ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html#configuration diff --git a/docs/pyproject_hooks.rst b/docs/pyproject_hooks.rst index 2e4b682..d779243 100644 --- a/docs/pyproject_hooks.rst +++ b/docs/pyproject_hooks.rst @@ -22,15 +22,15 @@ Custom Subprocess Runners It is possible to provide a custom subprocess runner, that behaves differently. The expected protocol for subprocess runners is as follows: -.. function:: subprocess_runner_protocol(cmd, cwd, extra_environ) +.. function:: subprocess_runner_protocol(cmd, cwd=None, extra_environ=None) :noindex: :param cmd: The command and arguments to execute, as would be passed to :func:`subprocess.run`. - :type cmd: list[str] + :type cmd: typing.Sequence[str] :param cwd: The working directory that must be used for the subprocess. - :type cwd: str + :type cwd: typing.Optional[str] :param extra_environ: Mapping of environment variables (name to value) which must be set for the subprocess execution. - :type extra_environ: dict[str, str] + :type extra_environ: typing.Optional[typing.Mapping[str, str]] :rtype: None diff --git a/src/pyproject_hooks/_impl.py b/src/pyproject_hooks/_impl.py index 3b773f4..f00e0d3 100644 --- a/src/pyproject_hooks/_impl.py +++ b/src/pyproject_hooks/_impl.py @@ -164,6 +164,8 @@ def subprocess_runner(self, runner: "SubprocessRunner") -> Iterator[None]: """A context manager for temporarily overriding the default :ref:`subprocess runner `. + :param runner: The new subprocess runner to use within the context. + .. code-block:: python hook_caller = BuildBackendHookCaller(...) @@ -187,6 +189,7 @@ def get_requires_for_build_wheel( ) -> Sequence[str]: """Get additional dependencies required for building a wheel. + :param config_settings: The configuration settings for the build backend :returns: A list of :pep:`dependency specifiers <508>`. .. admonition:: Fallback @@ -206,6 +209,12 @@ def prepare_metadata_for_build_wheel( ) -> str: """Prepare a ``*.dist-info`` folder with metadata for this project. + :param metadata_directory: The directory to write the metadata to + :param config_settings: The configuration settings for the build backend + :param _allow_fallback: + Whether to allow the fallback to building a wheel and extracting + the metadata from it. Should be passed as a keyword argument only. + :returns: Name of the newly created subfolder within ``metadata_directory``, containing the metadata. @@ -233,6 +242,9 @@ def build_wheel( ) -> str: """Build a wheel from this project. + :param wheel_directory: The directory to write the wheel to + :param config_settings: The configuration settings for the build backend + :param metadata_directory: The directory to reuse existing metadata from :returns: The name of the newly created wheel within ``wheel_directory``. @@ -260,6 +272,7 @@ def get_requires_for_build_editable( ) -> Sequence[str]: """Get additional dependencies required for building an editable wheel. + :param config_settings: The configuration settings for the build backend :returns: A list of :pep:`dependency specifiers <508>`. .. admonition:: Fallback @@ -279,6 +292,11 @@ def prepare_metadata_for_build_editable( ) -> Optional[str]: """Prepare a ``*.dist-info`` folder with metadata for this project. + :param metadata_directory: The directory to write the metadata to + :param config_settings: The configuration settings for the build backend + :param _allow_fallback: + Whether to allow the fallback to building a wheel and extracting + the metadata from it. Should be passed as a keyword argument only. :returns: Name of the newly created subfolder within ``metadata_directory``, containing the metadata. @@ -306,6 +324,9 @@ def build_editable( ) -> str: """Build an editable wheel from this project. + :param wheel_directory: The directory to write the wheel to + :param config_settings: The configuration settings for the build backend + :param metadata_directory: The directory to reuse existing metadata from :returns: The name of the newly created wheel within ``wheel_directory``. From 5c83df5dc968748a7975067bca97934b215362dc Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sat, 8 Apr 2023 08:48:39 -0500 Subject: [PATCH 16/19] Expose `SubprocessRunner` for type checking This makes it possible to annotate using this type, instead of relying on redefining it. --- src/pyproject_hooks/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/pyproject_hooks/__init__.py b/src/pyproject_hooks/__init__.py index 9ae51a8..6b5ec51 100644 --- a/src/pyproject_hooks/__init__.py +++ b/src/pyproject_hooks/__init__.py @@ -1,6 +1,8 @@ """Wrappers to call pyproject.toml-based build backend hooks. """ +from typing import TYPE_CHECKING + from ._impl import ( BackendInvalid, BackendUnavailable, @@ -21,3 +23,8 @@ "quiet_subprocess_runner", "BuildBackendHookCaller", ] + +if TYPE_CHECKING: + from ._impl import SubprocessRunner # noqa: F401 + + __all__.append("SubprocessRunner") From 375b8fc4606e1cf404017a267ffa89677db7a6f2 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sat, 8 Apr 2023 08:50:51 -0500 Subject: [PATCH 17/19] Don't meddle with `__all__` --- src/pyproject_hooks/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pyproject_hooks/__init__.py b/src/pyproject_hooks/__init__.py index 6b5ec51..48736a9 100644 --- a/src/pyproject_hooks/__init__.py +++ b/src/pyproject_hooks/__init__.py @@ -26,5 +26,3 @@ if TYPE_CHECKING: from ._impl import SubprocessRunner # noqa: F401 - - __all__.append("SubprocessRunner") From 228aef1c51a3afb4a582ee005e972a01ea31ef39 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sat, 8 Apr 2023 08:53:56 -0500 Subject: [PATCH 18/19] Document the usage of the subprocess protocol for type checking --- docs/pyproject_hooks.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/pyproject_hooks.rst b/docs/pyproject_hooks.rst index d779243..2c0d4e0 100644 --- a/docs/pyproject_hooks.rst +++ b/docs/pyproject_hooks.rst @@ -34,6 +34,19 @@ It is possible to provide a custom subprocess runner, that behaves differently. :rtype: None +Since this codebase is currently Python 3.7-compatible, the type annotation for this protocol is only available to type checkers. To annotate a variable as a subprocess runner, you can do something along the lines of: + +.. code-block:: python + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from pyproject_hooks import SubprocessRunner + + # Example usage + def build(awesome_runner: "SubprocessRunner") -> None: + ... + Exceptions ---------- From 1eea736db62fbc95a9b36881141886064672fda3 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Wed, 1 Nov 2023 06:17:16 +0000 Subject: [PATCH 19/19] Remove whitespace that was accidentally added in a merge commit --- src/pyproject_hooks/_impl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyproject_hooks/_impl.py b/src/pyproject_hooks/_impl.py index 036924c..d1e9d7b 100644 --- a/src/pyproject_hooks/_impl.py +++ b/src/pyproject_hooks/_impl.py @@ -37,7 +37,7 @@ def read_json(path: str) -> Mapping[str, Any]: class BackendUnavailable(Exception): """Will be raised if the backend cannot be imported in the hook process.""" - + def __init__( self, traceback: str,