From bfff6780ae5ccac7d02af2575d60bd282cb0568b Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 21 Sep 2023 22:21:45 +0200 Subject: [PATCH 01/13] ensure self-init accepts setup.py only config fixes #938 --- src/setuptools_scm/_config.py | 1 + .../_integration/pyproject_reading.py | 12 +++++- src/setuptools_scm/_integration/setuptools.py | 19 +++++---- testing/test_integration.py | 40 +++++++++++++++++++ 4 files changed, 63 insertions(+), 9 deletions(-) diff --git a/src/setuptools_scm/_config.py b/src/setuptools_scm/_config.py index aaf0440d..2fd92c7d 100644 --- a/src/setuptools_scm/_config.py +++ b/src/setuptools_scm/_config.py @@ -115,6 +115,7 @@ def from_file( name: str | os.PathLike[str] = "pyproject.toml", dist_name: str | None = None, _load_toml: Callable[[str], dict[str, Any]] | None = None, + _require_section: bool = True, **kwargs: Any, ) -> Configuration: """ diff --git a/src/setuptools_scm/_integration/pyproject_reading.py b/src/setuptools_scm/_integration/pyproject_reading.py index db880a82..3510038a 100644 --- a/src/setuptools_scm/_integration/pyproject_reading.py +++ b/src/setuptools_scm/_integration/pyproject_reading.py @@ -9,11 +9,14 @@ from typing import NamedTuple from typing import TYPE_CHECKING +from .. import _log from .setuptools import read_dist_name_from_setup_cfg if TYPE_CHECKING: from typing_extensions import TypeAlias +log = _log.log.getChild("pyproject_reading") + _ROOT = "root" TOML_RESULT: TypeAlias = Dict[str, Any] TOML_LOADER: TypeAlias = Callable[[str], TOML_RESULT] @@ -43,6 +46,7 @@ def read_pyproject( name: str | os.PathLike[str] = "pyproject.toml", tool_name: str = "setuptools_scm", _load_toml: TOML_LOADER | None = None, + require_secion: bool = True, ) -> PyProjectData: if _load_toml is None: _load_toml = lazy_toml_load @@ -52,7 +56,13 @@ def read_pyproject( try: section = defn.get("tool", {})[tool_name] except LookupError as e: - raise LookupError(f"{name} does not contain a tool.{tool_name} section") from e + error = f"{name} does not contain a tool.{tool_name} section" + if require_secion: + raise LookupError(error) from e + else: + log.warn(error) + section = {} + project = defn.get("project", {}) return PyProjectData(name, tool_name, project, section) diff --git a/src/setuptools_scm/_integration/setuptools.py b/src/setuptools_scm/_integration/setuptools.py index 259b2d43..fd20c7dd 100644 --- a/src/setuptools_scm/_integration/setuptools.py +++ b/src/setuptools_scm/_integration/setuptools.py @@ -63,21 +63,27 @@ def _assign_version( _warn_on_old_setuptools() +def _log_hookstart(hook: str, dist: setuptools.Distribution) -> None: + log.debug("%s %r", hook, vars(dist.metadata)) + + def version_keyword( dist: setuptools.Distribution, keyword: str, value: bool | dict[str, Any] | Callable[[], dict[str, Any]], ) -> None: - if not value: - return - elif value is True: + if value is True: value = {} elif callable(value): value = value() + + assert isinstance(value, dict), "version_keyword expects a dict or True" assert ( "dist_name" not in value ), "dist_name may not be specified in the setup keyword " dist_name: str | None = dist.metadata.name + _log_hookstart("version_keyword", dist) + if dist.metadata.version is not None: warnings.warn(f"version of {dist_name} already set") return @@ -103,10 +109,7 @@ def version_keyword( def infer_version(dist: setuptools.Distribution) -> None: - log.debug( - "finalize hook %r", - vars(dist.metadata), - ) + _log_hookstart("infer_version", dist) log.debug("dist %s %s", id(dist), id(dist.metadata)) if dist.metadata.version is not None: return # metadata already added by hook @@ -120,6 +123,6 @@ def infer_version(dist: setuptools.Distribution) -> None: try: config = _config.Configuration.from_file(dist_name=dist_name) except LookupError as e: - log.exception(e) + log.warning(e) else: _assign_version(dist, config) diff --git a/testing/test_integration.py b/testing/test_integration.py index bcdd6184..e875a92f 100644 --- a/testing/test_integration.py +++ b/testing/test_integration.py @@ -1,6 +1,8 @@ from __future__ import annotations import importlib.metadata +import os +import subprocess import sys import textwrap from pathlib import Path @@ -99,6 +101,44 @@ def test_pyproject_support_with_git(wd: WorkDir, metadata_in: str) -> None: assert res.endswith("0.1.dev0+d20090213") +@pytest.mark.parametrize("use_scm_version", ["True", "{}", "lambda: {}"]) +def test_pyproject_missing_setup_hook_works(wd: WorkDir, use_scm_version: str) -> None: + wd.write( + "setup.py", + f"""__import__('setuptools').setup( + name="example-scm-unique", + use_scm_version={use_scm_version}, + )""", + ) + wd.write( + "pyproject.toml", + """[build-system]\nrequires=["setuptools", "setuptools_scm"]\n\n[tool]""", + ) + + res = subprocess.run( + [sys.executable, "setup.py", "--version"], + cwd=wd.cwd, + check=True, + stdout=subprocess.PIPE, + encoding="utf-8", + ) + stripped = res.stdout.strip() + assert stripped.endswith("0.1.dev0+d20090213") + + res_build = subprocess.run( + [sys.executable, "-m", "build", "-nxw"], + env={k: v for k, v in os.environ.items() if k != "SETUPTOOLS_SCM_DEBUG"}, + capture_output=True, + text=True, + cwd=wd.cwd, + ) + import pprint + + pprint.pprint(res_build) + wheel: Path = next(wd.cwd.joinpath("dist").iterdir()) + assert "0.1.dev0+d20090213" in str(wheel) + + def test_pretend_version(monkeypatch: pytest.MonkeyPatch, wd: WorkDir) -> None: monkeypatch.setenv(PRETEND_KEY, "1.0.0") From cd8c6d0fcd35abeeaec1b46ed439e7ce3aca447b Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 30 Sep 2023 23:24:30 +0200 Subject: [PATCH 02/13] correctly consider pyproject.toml data in version keyword --- src/setuptools_scm/_config.py | 7 +++++-- .../_integration/pyproject_reading.py | 20 +++++++++++++------ src/setuptools_scm/_integration/setuptools.py | 11 ++-------- tox.ini | 1 + 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/setuptools_scm/_config.py b/src/setuptools_scm/_config.py index 2fd92c7d..92400b6e 100644 --- a/src/setuptools_scm/_config.py +++ b/src/setuptools_scm/_config.py @@ -125,11 +125,14 @@ def from_file( not contain the [tool.setuptools_scm] section. """ - pyproject_data = _read_pyproject(name, _load_toml=_load_toml) + pyproject_data = _read_pyproject( + name, _load_toml=_load_toml, require_section=_require_section + ) args = _get_args_for_pyproject(pyproject_data, dist_name, kwargs) args.update(read_toml_overrides(args["dist_name"])) - return cls.from_data(relative_to=name, data=args) + relative_to = args.pop("relative_to", name) + return cls.from_data(relative_to=relative_to, data=args) @classmethod def from_data( diff --git a/src/setuptools_scm/_integration/pyproject_reading.py b/src/setuptools_scm/_integration/pyproject_reading.py index 3510038a..a0c711b8 100644 --- a/src/setuptools_scm/_integration/pyproject_reading.py +++ b/src/setuptools_scm/_integration/pyproject_reading.py @@ -46,21 +46,29 @@ def read_pyproject( name: str | os.PathLike[str] = "pyproject.toml", tool_name: str = "setuptools_scm", _load_toml: TOML_LOADER | None = None, - require_secion: bool = True, + require_section: bool = True, ) -> PyProjectData: if _load_toml is None: _load_toml = lazy_toml_load - with open(name, encoding="UTF-8") as strm: - data = strm.read() - defn = _load_toml(data) + try: + with open(name, encoding="UTF-8") as strm: + data = strm.read() + except FileNotFoundError: + if require_section: + raise + else: + log.debug("%s missing, presuming emptry as section is not required", name) + defn = {} + else: + defn = _load_toml(data) try: section = defn.get("tool", {})[tool_name] except LookupError as e: error = f"{name} does not contain a tool.{tool_name} section" - if require_secion: + if require_section: raise LookupError(error) from e else: - log.warn(error) + log.warning(error) section = {} project = defn.get("project", {}) diff --git a/src/setuptools_scm/_integration/setuptools.py b/src/setuptools_scm/_integration/setuptools.py index fd20c7dd..44da9e6f 100644 --- a/src/setuptools_scm/_integration/setuptools.py +++ b/src/setuptools_scm/_integration/setuptools.py @@ -9,7 +9,6 @@ import setuptools from .. import _config -from .._version_cls import _validate_version_cls log = logging.getLogger(__name__) @@ -95,15 +94,9 @@ def version_keyword( if dist_name is None: dist_name = read_dist_name_from_setup_cfg() - version_cls = value.pop("version_cls", None) - normalize = value.pop("normalize", True) - tag_regex = _config._check_tag_regex( - value.pop("tag_regex", _config.DEFAULT_TAG_REGEX) - ) - final_version = _validate_version_cls(version_cls, normalize) - config = _config.Configuration( - dist_name=dist_name, version_cls=final_version, tag_regex=tag_regex, **value + config = _config.Configuration.from_file( + dist_name=dist_name, _require_section=False, **value ) _assign_version(dist, config) diff --git a/tox.ini b/tox.ini index a309f9da..e2da4b97 100644 --- a/tox.ini +++ b/tox.ini @@ -13,6 +13,7 @@ deps= pytest setuptools >= 45 rich + build commands= pytest {posargs} From bb3be207525858edad306bb718a1dd7aa9d67766 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 2 Oct 2023 14:16:32 +0200 Subject: [PATCH 03/13] chore: move toml handling to own module --- src/setuptools_scm/_config.py | 7 +-- .../_integration/pyproject_reading.py | 51 ++++-------------- src/setuptools_scm/_integration/toml.py | 52 +++++++++++++++++++ src/setuptools_scm/_overrides.py | 21 +------- 4 files changed, 66 insertions(+), 65 deletions(-) create mode 100644 src/setuptools_scm/_integration/toml.py diff --git a/src/setuptools_scm/_config.py b/src/setuptools_scm/_config.py index 92400b6e..5e5feb17 100644 --- a/src/setuptools_scm/_config.py +++ b/src/setuptools_scm/_config.py @@ -5,8 +5,8 @@ import os import re import warnings +from pathlib import Path from typing import Any -from typing import Callable from typing import Pattern from typing import Protocol @@ -114,7 +114,6 @@ def from_file( cls, name: str | os.PathLike[str] = "pyproject.toml", dist_name: str | None = None, - _load_toml: Callable[[str], dict[str, Any]] | None = None, _require_section: bool = True, **kwargs: Any, ) -> Configuration: @@ -125,9 +124,7 @@ def from_file( not contain the [tool.setuptools_scm] section. """ - pyproject_data = _read_pyproject( - name, _load_toml=_load_toml, require_section=_require_section - ) + pyproject_data = _read_pyproject(Path(name), require_section=_require_section) args = _get_args_for_pyproject(pyproject_data, dist_name, kwargs) args.update(read_toml_overrides(args["dist_name"])) diff --git a/src/setuptools_scm/_integration/pyproject_reading.py b/src/setuptools_scm/_integration/pyproject_reading.py index a0c711b8..c9818a29 100644 --- a/src/setuptools_scm/_integration/pyproject_reading.py +++ b/src/setuptools_scm/_integration/pyproject_reading.py @@ -1,29 +1,22 @@ from __future__ import annotations -import os -import sys import warnings -from typing import Any -from typing import Callable -from typing import Dict +from pathlib import Path from typing import NamedTuple -from typing import TYPE_CHECKING from .. import _log from .setuptools import read_dist_name_from_setup_cfg +from .toml import read_toml_content +from .toml import TOML_RESULT -if TYPE_CHECKING: - from typing_extensions import TypeAlias log = _log.log.getChild("pyproject_reading") _ROOT = "root" -TOML_RESULT: TypeAlias = Dict[str, Any] -TOML_LOADER: TypeAlias = Callable[[str], TOML_RESULT] class PyProjectData(NamedTuple): - name: str | os.PathLike[str] + path: Path tool_name: str project: TOML_RESULT section: TOML_RESULT @@ -33,46 +26,24 @@ def project_name(self) -> str | None: return self.project.get("name") -def lazy_toml_load(data: str) -> TOML_RESULT: - if sys.version_info >= (3, 11): - from tomllib import loads - else: - from tomli import loads - - return loads(data) - - def read_pyproject( - name: str | os.PathLike[str] = "pyproject.toml", + path: Path = Path("pyproject.toml"), tool_name: str = "setuptools_scm", - _load_toml: TOML_LOADER | None = None, require_section: bool = True, ) -> PyProjectData: - if _load_toml is None: - _load_toml = lazy_toml_load - try: - with open(name, encoding="UTF-8") as strm: - data = strm.read() - except FileNotFoundError: - if require_section: - raise - else: - log.debug("%s missing, presuming emptry as section is not required", name) - defn = {} - else: - defn = _load_toml(data) + defn = read_toml_content(path, None if require_section else {}) try: section = defn.get("tool", {})[tool_name] except LookupError as e: - error = f"{name} does not contain a tool.{tool_name} section" + error = f"{path} does not contain a tool.{tool_name} section" if require_section: raise LookupError(error) from e else: - log.warning(error) + log.warning("toml section missing %r", error) section = {} project = defn.get("project", {}) - return PyProjectData(name, tool_name, project, section) + return PyProjectData(path, tool_name, project, section) def get_args_for_pyproject( @@ -86,7 +57,7 @@ def get_args_for_pyproject( if "relative_to" in section: relative = section.pop("relative_to") warnings.warn( - f"{pyproject.name}: at [tool.{pyproject.tool_name}]\n" + f"{pyproject.path}: at [tool.{pyproject.tool_name}]\n" f"ignoring value relative_to={relative!r}" " as its always relative to the config file" ) @@ -110,5 +81,5 @@ def get_args_for_pyproject( f"root {section[_ROOT]} is overridden" f" by the cli arg {kwargs[_ROOT]}" ) - section.pop("root", None) + section.pop(_ROOT, None) return {"dist_name": dist_name, **section, **kwargs} diff --git a/src/setuptools_scm/_integration/toml.py b/src/setuptools_scm/_integration/toml.py new file mode 100644 index 00000000..ac1cbe3b --- /dev/null +++ b/src/setuptools_scm/_integration/toml.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +import sys +from pathlib import Path +from typing import Any +from typing import Callable +from typing import cast +from typing import Dict +from typing import TypedDict + +if sys.version_info >= (3, 11): + from tomllib import loads as load_toml +else: + from tomli import loads as load_toml +from typing_extensions import TypeAlias + +from .. import _log + +log = _log.log.getChild("toml") + +TOML_RESULT: TypeAlias = Dict[str, Any] +TOML_LOADER: TypeAlias = Callable[[str], TOML_RESULT] + + +def read_toml_content(path: Path, default: TOML_RESULT | None = None) -> TOML_RESULT: + try: + data = path.read_text() + except FileNotFoundError: + if default is None: + raise + else: + log.debug("%s missing, presuming default %r", path, default) + return default + else: + return load_toml(data) + + +class _CheatTomlData(TypedDict): + cheat: dict[str, Any] + + +def load_toml_or_inline_map(data: str | None) -> dict[str, Any]: + """ + load toml data - with a special hack if only a inline map is given + """ + if not data: + return {} + elif data[0] == "{": + data = "cheat=" + data + loaded: _CheatTomlData = cast(_CheatTomlData, load_toml(data)) + return loaded["cheat"] + return load_toml(data) diff --git a/src/setuptools_scm/_overrides.py b/src/setuptools_scm/_overrides.py index e7efe287..792bfd27 100644 --- a/src/setuptools_scm/_overrides.py +++ b/src/setuptools_scm/_overrides.py @@ -3,13 +3,11 @@ import os import re from typing import Any -from typing import cast -from typing import TypedDict from . import _config from . import _log from . import version -from ._integration.pyproject_reading import lazy_toml_load +from ._integration.toml import load_toml_or_inline_map log = _log.log.getChild("overrides") @@ -51,23 +49,6 @@ def _read_pretended_version_for( return None -class _CheatTomlData(TypedDict): - cheat: dict[str, Any] - - -def load_toml_or_inline_map(data: str | None) -> dict[str, Any]: - """ - load toml data - with a special hack if only a inline map is given - """ - if not data: - return {} - elif data[0] == "{": - data = "cheat=" + data - loaded: _CheatTomlData = cast(_CheatTomlData, lazy_toml_load(data)) - return loaded["cheat"] - return lazy_toml_load(data) - - def read_toml_overrides(dist_name: str | None) -> dict[str, Any]: data = read_named_env(name="OVERRIDES", dist_name=dist_name) return load_toml_or_inline_map(data) From 27e1bc33c6cfc0f2e2ea28a307511abf4117c4b2 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 2 Oct 2023 14:17:36 +0200 Subject: [PATCH 04/13] chore: version_from_entrypoint - use kwonly args --- src/setuptools_scm/_entrypoints.py | 2 +- src/setuptools_scm/_get_version_impl.py | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/setuptools_scm/_entrypoints.py b/src/setuptools_scm/_entrypoints.py index be6fdddd..50c91829 100644 --- a/src/setuptools_scm/_entrypoints.py +++ b/src/setuptools_scm/_entrypoints.py @@ -45,7 +45,7 @@ def entry_points(group: str) -> EntryPoints: def version_from_entrypoint( - config: Configuration, entrypoint: str, root: _t.PathT + config: Configuration, *, entrypoint: str, root: _t.PathT ) -> version.ScmVersion | None: from .discover import iter_matching_entrypoints diff --git a/src/setuptools_scm/_get_version_impl.py b/src/setuptools_scm/_get_version_impl.py index 8be513aa..2d9d9478 100644 --- a/src/setuptools_scm/_get_version_impl.py +++ b/src/setuptools_scm/_get_version_impl.py @@ -32,18 +32,22 @@ def parse_scm_version(config: Configuration) -> ScmVersion | None: ) return parse_result else: - entrypoint = "setuptools_scm.parse_scm" - root = config.absolute_root - return _entrypoints.version_from_entrypoint(config, entrypoint, root) + return _entrypoints.version_from_entrypoint( + config, + entrypoint="setuptools_scm.parse_scm", + root=config.absolute_root, + ) except _run_cmd.CommandNotFoundError as e: _log.exception("command %s not found while parsing the scm, using fallbacks", e) return None def parse_fallback_version(config: Configuration) -> ScmVersion | None: - entrypoint = "setuptools_scm.parse_scm_fallback" - root = config.fallback_root - return _entrypoints.version_from_entrypoint(config, entrypoint, root) + return _entrypoints.version_from_entrypoint( + config, + entrypoint="setuptools_scm.parse_scm_fallback", + root=config.fallback_root, + ) def parse_version(config: Configuration) -> ScmVersion | None: From 27aad55ff400aae92d6e4817cdae768643a041e0 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 2 Oct 2023 14:18:20 +0200 Subject: [PATCH 05/13] chore: steamline logging in _version_cls --- src/setuptools_scm/_version_cls.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/setuptools_scm/_version_cls.py b/src/setuptools_scm/_version_cls.py index 61abad50..3fd4a32e 100644 --- a/src/setuptools_scm/_version_cls.py +++ b/src/setuptools_scm/_version_cls.py @@ -1,6 +1,5 @@ from __future__ import annotations -from logging import getLogger from typing import cast from typing import Type from typing import Union @@ -11,6 +10,9 @@ except ImportError: from setuptools.extern.packaging.version import InvalidVersion # type: ignore from setuptools.extern.packaging.version import Version as Version # type: ignore +from . import _log + +log = _log.log.getChild("version_cls") class NonNormalizedVersion(Version): @@ -41,10 +43,8 @@ def __repr__(self) -> str: def _version_as_tuple(version_str: str) -> tuple[int | str, ...]: try: parsed_version = Version(version_str) - except InvalidVersion: - log = getLogger(__name__).parent - assert log is not None - log.error("failed to parse version %s", version_str) + except InvalidVersion as e: + log.error("failed to parse version %s: %s", e, version_str) return (version_str,) else: version_fields: tuple[int | str, ...] = parsed_version.release From 0af76c06daffefe9e8408be149214bc85e3b4d4d Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 2 Oct 2023 14:27:44 +0200 Subject: [PATCH 06/13] version_keyword: introduce variable name for overrides --- src/setuptools_scm/_integration/setuptools.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/setuptools_scm/_integration/setuptools.py b/src/setuptools_scm/_integration/setuptools.py index 44da9e6f..f574d23d 100644 --- a/src/setuptools_scm/_integration/setuptools.py +++ b/src/setuptools_scm/_integration/setuptools.py @@ -71,14 +71,17 @@ def version_keyword( keyword: str, value: bool | dict[str, Any] | Callable[[], dict[str, Any]], ) -> None: + overrides: dict[str, Any] if value is True: - value = {} + overrides = {} elif callable(value): - value = value() + overrides = value() + else: + assert isinstance(value, dict), "version_keyword expects a dict or True" + overrides = value - assert isinstance(value, dict), "version_keyword expects a dict or True" assert ( - "dist_name" not in value + "dist_name" not in overrides ), "dist_name may not be specified in the setup keyword " dist_name: str | None = dist.metadata.name _log_hookstart("version_keyword", dist) @@ -86,17 +89,14 @@ def version_keyword( if dist.metadata.version is not None: warnings.warn(f"version of {dist_name} already set") return - log.debug( - "version keyword %r", - vars(dist.metadata), - ) - log.debug("dist %s %s", id(dist), id(dist.metadata)) if dist_name is None: dist_name = read_dist_name_from_setup_cfg() config = _config.Configuration.from_file( - dist_name=dist_name, _require_section=False, **value + dist_name=dist_name, + _require_section=False, + **overrides, ) _assign_version(dist, config) From 3c0556aae9898fe21697b639633c8a3aee12da86 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 2 Oct 2023 14:30:38 +0200 Subject: [PATCH 07/13] remove build time need for typing_extensions --- src/setuptools_scm/_integration/toml.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/setuptools_scm/_integration/toml.py b/src/setuptools_scm/_integration/toml.py index ac1cbe3b..795b8363 100644 --- a/src/setuptools_scm/_integration/toml.py +++ b/src/setuptools_scm/_integration/toml.py @@ -6,13 +6,16 @@ from typing import Callable from typing import cast from typing import Dict +from typing import TYPE_CHECKING from typing import TypedDict if sys.version_info >= (3, 11): from tomllib import loads as load_toml else: from tomli import loads as load_toml -from typing_extensions import TypeAlias + +if TYPE_CHECKING: + from typing_extensions import TypeAlias from .. import _log From 8bd16178cd54cbaf8a5c528ab7e78041bb5dd488 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 2 Oct 2023 14:39:21 +0200 Subject: [PATCH 08/13] enforce utf-8 for toml --- src/setuptools_scm/_integration/toml.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setuptools_scm/_integration/toml.py b/src/setuptools_scm/_integration/toml.py index 795b8363..a08b7b88 100644 --- a/src/setuptools_scm/_integration/toml.py +++ b/src/setuptools_scm/_integration/toml.py @@ -27,7 +27,7 @@ def read_toml_content(path: Path, default: TOML_RESULT | None = None) -> TOML_RESULT: try: - data = path.read_text() + data = path.read_text(encoding="utf-8") except FileNotFoundError: if default is None: raise From 41eac35195ba6d47ecff3e66cc163285ef2699f8 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 2 Oct 2023 15:04:50 +0200 Subject: [PATCH 09/13] add build to the test requirements --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 967efd70..46b35bbf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,6 +57,7 @@ rich = [ "rich", ] test = [ + "build", "pytest", "rich", ] From d37eb9186155c48f43b231159025c530437ed22d Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 2 Oct 2023 15:13:36 +0200 Subject: [PATCH 10/13] tests: dont capture output of build --- testing/test_integration.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/testing/test_integration.py b/testing/test_integration.py index e875a92f..aa9d992d 100644 --- a/testing/test_integration.py +++ b/testing/test_integration.py @@ -128,8 +128,6 @@ def test_pyproject_missing_setup_hook_works(wd: WorkDir, use_scm_version: str) - res_build = subprocess.run( [sys.executable, "-m", "build", "-nxw"], env={k: v for k, v in os.environ.items() if k != "SETUPTOOLS_SCM_DEBUG"}, - capture_output=True, - text=True, cwd=wd.cwd, ) import pprint From e90d13fd3a36ff8a5b7693ce3d8ca0136e123f33 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 2 Oct 2023 15:28:59 +0200 Subject: [PATCH 11/13] also add wheel as test requirement --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 46b35bbf..2b34682d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,6 +60,7 @@ test = [ "build", "pytest", "rich", + "wheel", ] toml = [ ] From 8c70e1bdb3c0ddf4505c4c9a161f1d509c74db77 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 2 Oct 2023 15:50:55 +0200 Subject: [PATCH 12/13] configure build backend in integration tests --- testing/conftest.py | 2 +- testing/test_integration.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/testing/conftest.py b/testing/conftest.py index 8baec7cc..05ab5344 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -20,7 +20,7 @@ def pytest_configure() -> None: os.environ["SETUPTOOLS_SCM_DEBUG"] = "1" -VERSION_PKGS = ["setuptools", "setuptools_scm", "packaging"] +VERSION_PKGS = ["setuptools", "setuptools_scm", "packaging", "build", "wheel"] def pytest_report_header() -> list[str]: diff --git a/testing/test_integration.py b/testing/test_integration.py index aa9d992d..45ee295d 100644 --- a/testing/test_integration.py +++ b/testing/test_integration.py @@ -112,7 +112,14 @@ def test_pyproject_missing_setup_hook_works(wd: WorkDir, use_scm_version: str) - ) wd.write( "pyproject.toml", - """[build-system]\nrequires=["setuptools", "setuptools_scm"]\n\n[tool]""", + textwrap.dedent( + """ + [build-system] + requires=["setuptools", "setuptools_scm"] + build-backend = "setuptools.build_meta" + [tool] + """ + ), ) res = subprocess.run( From 8d3560e0e99aa8dc441a4696a553069aa2cebda6 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 2 Oct 2023 16:23:24 +0200 Subject: [PATCH 13/13] changelog --- .../20231002_161552_opensource_fix_938_self_reinit_fixup.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 changelog.d/20231002_161552_opensource_fix_938_self_reinit_fixup.md diff --git a/changelog.d/20231002_161552_opensource_fix_938_self_reinit_fixup.md b/changelog.d/20231002_161552_opensource_fix_938_self_reinit_fixup.md new file mode 100644 index 00000000..294fe4ae --- /dev/null +++ b/changelog.d/20231002_161552_opensource_fix_938_self_reinit_fixup.md @@ -0,0 +1,6 @@ + +### Changed + +- ensure the setuptools version keyword correctly load pyproject.toml configuration +- add build and wheel to the test requirements for regression testing +- move internal toml handling to own module