From d25a4ac6e949a9383b629bba43a826c8891904e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez-Mondrag=C3=B3n?= Date: Sun, 19 May 2024 12:31:29 -0600 Subject: [PATCH 01/11] Support the `[project]` section (PEP 621) --- .github/workflows/main.yaml | 5 ++++- poetry_dynamic_versioning/__init__.py | 6 ++---- poetry_dynamic_versioning/plugin.py | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index ea5a5b2..9536e03 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -43,6 +43,9 @@ jobs: poetry-version: '1.7.1' - python-version: '3.7' poetry-version: '1.8.1' + include: + - python-version: '3.11' + poetry-version: 'git+https://github.com/radoering/poetry.git@pep621-support' steps: - uses: actions/checkout@v4 with: @@ -52,7 +55,7 @@ jobs: python-version: ${{ matrix.python-version }} - run: | pip install pipx - pipx install poetry==${{ matrix.poetry-version }} + pipx install ${{ startsWith(matrix.poetry-version, 'git') && matrix.poetry-version || format('poetry=={0}', matrix.poetry-version) }} pipx install invoke poetry install --extras plugin diff --git a/poetry_dynamic_versioning/__init__.py b/poetry_dynamic_versioning/__init__.py index 699fc76..c0a3d30 100644 --- a/poetry_dynamic_versioning/__init__.py +++ b/poetry_dynamic_versioning/__init__.py @@ -590,7 +590,7 @@ def _substitute_version_in_text(version: str, content: str, patterns: Sequence[_ def _apply_version( - version: str, instance: Version, config: _Config, pyproject_path: Path, retain: bool = False + name: str, version: str, instance: Version, config: _Config, pyproject_path: Path, retain: bool = False ) -> None: pyproject = tomlkit.parse(pyproject_path.read_bytes().decode("utf-8")) @@ -604,8 +604,6 @@ def _apply_version( pyproject_path.write_bytes(tomlkit.dumps(pyproject).encode("utf-8")) - name = pyproject["tool"]["poetry"]["name"] # type: ignore - for file_name, file_info in config["files"].items(): full_file = pyproject_path.parent.joinpath(file_name) @@ -678,7 +676,7 @@ def _get_and_apply_version( if name is not None and original is not None: _state.projects[name] = _ProjectState(pyproject_path, original, version) if io: - _apply_version(version, instance, config, pyproject_path, retain) + _apply_version(name, version, instance, config, pyproject_path, retain) return name diff --git a/poetry_dynamic_versioning/plugin.py b/poetry_dynamic_versioning/plugin.py index 55c1077..44f94c6 100644 --- a/poetry_dynamic_versioning/plugin.py +++ b/poetry_dynamic_versioning/plugin.py @@ -81,8 +81,9 @@ def _apply_version_via_plugin( io: bool = True # fmt: on ) -> None: + project_section = poetry.pyproject.data.get("project", {}) name = _get_and_apply_version( - name=poetry.local_config["name"], + name=project_section.get("name") or poetry.local_config("name"), original=poetry.local_config["version"], pyproject=poetry.pyproject.data, pyproject_path=_get_pyproject_path_from_poetry(poetry.pyproject), From 837d3d05144dcece21293b2d68e97a7a3303d075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez-Mondrag=C3=B3n?= Date: Mon, 20 May 2024 09:41:51 -0600 Subject: [PATCH 02/11] Fix missing `.get(...)` --- poetry_dynamic_versioning/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poetry_dynamic_versioning/plugin.py b/poetry_dynamic_versioning/plugin.py index 44f94c6..0a0aa6a 100644 --- a/poetry_dynamic_versioning/plugin.py +++ b/poetry_dynamic_versioning/plugin.py @@ -83,7 +83,7 @@ def _apply_version_via_plugin( ) -> None: project_section = poetry.pyproject.data.get("project", {}) name = _get_and_apply_version( - name=project_section.get("name") or poetry.local_config("name"), + name=poetry.local_config.get("name") or project_section.get("name"), original=poetry.local_config["version"], pyproject=poetry.pyproject.data, pyproject_path=_get_pyproject_path_from_poetry(poetry.pyproject), From 512ab9132c703260e4dbe183d9a2f4751dc2c5b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez-Mondrag=C3=B3n?= Date: Wed, 22 May 2024 20:02:01 -0600 Subject: [PATCH 03/11] Get name and version from `pyproject` in `_get_and_apply_version` --- poetry_dynamic_versioning/__init__.py | 13 ++++++++++--- poetry_dynamic_versioning/plugin.py | 3 --- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/poetry_dynamic_versioning/__init__.py b/poetry_dynamic_versioning/__init__.py index c0a3d30..860aaf2 100644 --- a/poetry_dynamic_versioning/__init__.py +++ b/poetry_dynamic_versioning/__init__.py @@ -633,9 +633,7 @@ def _apply_version( def _get_and_apply_version( - name: Optional[str] = None, - original: Optional[str] = None, - pyproject: Optional[Mapping] = None, + pyproject: Mapping, pyproject_path: Optional[Path] = None, retain: bool = False, force: bool = False, @@ -643,6 +641,15 @@ def _get_and_apply_version( io: bool = True # fmt: on ) -> Optional[str]: + poetry_section = pyproject.get("tool", {}).get("poetry", {}) + project_section = pyproject.get("project", {}) + + name = poetry_section.get("name") or project_section.get("name") + original = poetry_section.get("version") + + if not original: + raise RuntimeError("No version found in section 'tool.poetry'") + if name is not None and name in _state.projects: return name diff --git a/poetry_dynamic_versioning/plugin.py b/poetry_dynamic_versioning/plugin.py index 0a0aa6a..a46ad4c 100644 --- a/poetry_dynamic_versioning/plugin.py +++ b/poetry_dynamic_versioning/plugin.py @@ -81,10 +81,7 @@ def _apply_version_via_plugin( io: bool = True # fmt: on ) -> None: - project_section = poetry.pyproject.data.get("project", {}) name = _get_and_apply_version( - name=poetry.local_config.get("name") or project_section.get("name"), - original=poetry.local_config["version"], pyproject=poetry.pyproject.data, pyproject_path=_get_pyproject_path_from_poetry(poetry.pyproject), retain=retain, From 296669f5ab49836bd695a36d9b6a08f3261ed8c6 Mon Sep 17 00:00:00 2001 From: mtkennerly Date: Mon, 17 Jun 2024 14:35:02 -0400 Subject: [PATCH 04/11] Update reversion and tests --- .github/workflows/main.yaml | 3 + poetry_dynamic_versioning/__init__.py | 73 ++++++++++++------- poetry_dynamic_versioning/patch.py | 2 - .../project-pep621/project_pep621/__init__.py | 1 + tests/project-pep621/pyproject.toml | 10 +++ tests/test_integration.py | 27 +++++++ 6 files changed, 89 insertions(+), 27 deletions(-) create mode 100644 tests/project-pep621/project_pep621/__init__.py create mode 100644 tests/project-pep621/pyproject.toml diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 9536e03..4f5ea86 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -53,6 +53,9 @@ jobs: - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - if: ${{ startsWith(matrix.poetry-version, 'git') }} + run: | + echo "USE_PEP621=1" >> $GITHUB_ENV - run: | pip install pipx pipx install ${{ startsWith(matrix.poetry-version, 'git') && matrix.poetry-version || format('poetry=={0}', matrix.poetry-version) }} diff --git a/poetry_dynamic_versioning/__init__.py b/poetry_dynamic_versioning/__init__.py index 860aaf2..caeaa82 100644 --- a/poetry_dynamic_versioning/__init__.py +++ b/poetry_dynamic_versioning/__init__.py @@ -8,6 +8,7 @@ import subprocess import sys import textwrap +from enum import Enum from importlib import import_module from pathlib import Path from typing import Mapping, MutableMapping, Optional, Sequence, Tuple, Union @@ -118,17 +119,24 @@ class _Config(Mapping): pass +class _Mode(Enum): + Classic = "classic" + Pep621 = "pep621" + + class _ProjectState: def __init__( self, path: Path, - original_version: str, + original_version: Optional[str], version: str, + mode: _Mode, substitutions: Optional[MutableMapping[Path, str]] = None, ) -> None: self.path = path self.original_version = original_version self.version = version + self.mode = mode self.substitutions = ( {} if substitutions is None else substitutions ) # type: MutableMapping[Path, str] @@ -590,7 +598,12 @@ def _substitute_version_in_text(version: str, content: str, patterns: Sequence[_ def _apply_version( - name: str, version: str, instance: Version, config: _Config, pyproject_path: Path, retain: bool = False + name: str, + version: str, + instance: Version, + config: _Config, + pyproject_path: Path, + retain: bool = False, ) -> None: pyproject = tomlkit.parse(pyproject_path.read_bytes().decode("utf-8")) @@ -633,26 +646,12 @@ def _apply_version( def _get_and_apply_version( - pyproject: Mapping, + pyproject: Optional[Mapping] = None, pyproject_path: Optional[Path] = None, retain: bool = False, force: bool = False, - # fmt: off - io: bool = True - # fmt: on + io: bool = True, ) -> Optional[str]: - poetry_section = pyproject.get("tool", {}).get("poetry", {}) - project_section = pyproject.get("project", {}) - - name = poetry_section.get("name") or project_section.get("name") - original = poetry_section.get("version") - - if not original: - raise RuntimeError("No version found in section 'tool.poetry'") - - if name is not None and name in _state.projects: - return name - if pyproject_path is None: pyproject_path = _get_pyproject_path() if pyproject_path is None: @@ -661,11 +660,26 @@ def _get_and_apply_version( if pyproject is None: pyproject = tomlkit.parse(pyproject_path.read_bytes().decode("utf-8")) - if name is None or original is None: + classic = ( + "tool" in pyproject + and "poetry" in pyproject["tool"] + and "name" in pyproject["tool"]["poetry"] + ) + pep621 = "project" in pyproject and "name" in pyproject["project"] + + if classic: name = pyproject["tool"]["poetry"]["name"] original = pyproject["tool"]["poetry"]["version"] - if name in _state.projects: - return name + elif pep621: + name = pyproject["project"]["name"] + original = pyproject["project"].get("version") + if "version" not in pyproject["project"].get("dynamic", []): + return name if name in _state.projects else None + else: + return name if name in _state.projects else None + + if name in _state.projects: + return name config = _get_config(pyproject) if not config["enable"] and not force: @@ -679,9 +693,12 @@ def _get_and_apply_version( finally: os.chdir(str(initial_dir)) - # Condition will always be true, but it makes Mypy happy. - if name is not None and original is not None: - _state.projects[name] = _ProjectState(pyproject_path, original, version) + if classic and name is not None and original is not None: + _state.projects[name] = _ProjectState(pyproject_path, original, version, _Mode.Classic) + if io: + _apply_version(name, version, instance, config, pyproject_path, retain) + elif pep621 and name is not None: + _state.projects[name] = _ProjectState(pyproject_path, original, version, _Mode.Pep621) if io: _apply_version(name, version, instance, config, pyproject_path, retain) @@ -709,7 +726,13 @@ def _revert_version(retain: bool = False) -> None: # Reread pyproject.toml in case the substitutions affected it. pyproject = tomlkit.parse(state.path.read_bytes().decode("utf-8")) - pyproject["tool"]["poetry"]["version"] = state.original_version # type: ignore + if state.mode == _Mode.Classic: + pyproject["tool"]["poetry"]["version"] = state.original_version # type: ignore + elif state.mode == _Mode.Pep621: + if state.original_version is not None: + pyproject["project"]["version"] = state.original_version # type: ignore + elif "version" in pyproject["project"]: # type: ignore + pyproject["project"].pop("version") # type: ignore if not retain and not _state.cli_mode: pyproject["tool"]["poetry-dynamic-versioning"]["enable"] = True # type: ignore diff --git a/poetry_dynamic_versioning/patch.py b/poetry_dynamic_versioning/patch.py index 648adb4..9ad6c90 100644 --- a/poetry_dynamic_versioning/patch.py +++ b/poetry_dynamic_versioning/patch.py @@ -24,8 +24,6 @@ def alt_poetry_create(cls, *args, **kwargs): if not _state.cli_mode: name = _get_and_apply_version( - name=instance.local_config["name"], - original=instance.local_config["version"], pyproject=instance.pyproject.data, pyproject_path=_get_pyproject_path_from_poetry(instance.pyproject), ) diff --git a/tests/project-pep621/project_pep621/__init__.py b/tests/project-pep621/project_pep621/__init__.py new file mode 100644 index 0000000..6c8e6b9 --- /dev/null +++ b/tests/project-pep621/project_pep621/__init__.py @@ -0,0 +1 @@ +__version__ = "0.0.0" diff --git a/tests/project-pep621/pyproject.toml b/tests/project-pep621/pyproject.toml new file mode 100644 index 0000000..201aa6c --- /dev/null +++ b/tests/project-pep621/pyproject.toml @@ -0,0 +1,10 @@ +[project] +name = "project-pep621" +dynamic = ["version"] + +[tool.poetry-dynamic-versioning] +enable = true + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/tests/test_integration.py b/tests/test_integration.py index 0030cb5..ec7dab4 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -7,6 +7,7 @@ from pathlib import Path from typing import Optional, Sequence, Tuple +import dunamai import pytest import tomlkit @@ -267,3 +268,29 @@ def test_plugin_show(): # Just skip it for now. if "CI" not in os.environ: assert "poetry-dynamic-versioning" in out + + +@pytest.mark.skipif("USE_PEP621" not in os.environ, reason="Requires Poetry with PEP-621 support") +def test_pep621_with_dynamic_version(): + dummy = ROOT / "tests" / "project-pep621" + dummy_pyproject = dummy / "pyproject.toml" + + version = dunamai.Version.from_git().serialize() + + run("poetry-dynamic-versioning", where=dummy) + assert f'version = "{version}"' in dummy_pyproject.read_bytes().decode("utf-8") + assert f'__version__ = "{version}"' in (dummy / "project" / "__init__.py").read_bytes().decode( + "utf-8" + ) + + +@pytest.mark.skipif("USE_PEP621" not in os.environ, reason="Requires Poetry with PEP-621 support") +def test_pep621_without_dynamic_version(): + dummy = ROOT / "tests" / "project-pep621" + dummy_pyproject = dummy / "pyproject.toml" + + run("poetry-dynamic-versioning", where=dummy) + assert "version =" not in dummy_pyproject.read_bytes().decode("utf-8") + assert '__version__ = "0.0.0"' in (dummy / "project" / "__init__.py").read_bytes().decode( + "utf-8" + ) From 978a9a1bcb505ecc9977fe5c7ff35c08478bb7a6 Mon Sep 17 00:00:00 2001 From: mtkennerly Date: Mon, 17 Jun 2024 14:44:27 -0400 Subject: [PATCH 05/11] Fix some unconditional references to tool.poetry --- poetry_dynamic_versioning/__init__.py | 16 +++++++++++----- tests/test_integration.py | 5 ++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/poetry_dynamic_versioning/__init__.py b/poetry_dynamic_versioning/__init__.py index caeaa82..95509e1 100644 --- a/poetry_dynamic_versioning/__init__.py +++ b/poetry_dynamic_versioning/__init__.py @@ -603,11 +603,15 @@ def _apply_version( instance: Version, config: _Config, pyproject_path: Path, + mode: _Mode, retain: bool = False, ) -> None: pyproject = tomlkit.parse(pyproject_path.read_bytes().decode("utf-8")) - pyproject["tool"]["poetry"]["version"] = version # type: ignore + if mode == _Mode.Classic: + pyproject["tool"]["poetry"]["version"] = version # type: ignore + elif mode == _Mode.Pep621: + pyproject["project"]["version"] = version # type: ignore # Disable the plugin in case we're building a source distribution, # which won't have access to the VCS info at install time. @@ -694,13 +698,15 @@ def _get_and_apply_version( os.chdir(str(initial_dir)) if classic and name is not None and original is not None: - _state.projects[name] = _ProjectState(pyproject_path, original, version, _Mode.Classic) + mode = _Mode.Classic + _state.projects[name] = _ProjectState(pyproject_path, original, version, mode) if io: - _apply_version(name, version, instance, config, pyproject_path, retain) + _apply_version(name, version, instance, config, pyproject_path, mode, retain) elif pep621 and name is not None: - _state.projects[name] = _ProjectState(pyproject_path, original, version, _Mode.Pep621) + mode = _Mode.Pep621 + _state.projects[name] = _ProjectState(pyproject_path, original, version, mode) if io: - _apply_version(name, version, instance, config, pyproject_path, retain) + _apply_version(name, version, instance, config, pyproject_path, mode, retain) return name diff --git a/tests/test_integration.py b/tests/test_integration.py index ec7dab4..ac7bdcc 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -124,7 +124,10 @@ def test_invalid_config_for_vcs(): def test_keep_pyproject_modifications(): package = "cachy" # Using --optional to avoid actually installing the package - run(f"poetry add --optional {package}", where=DUMMY) + if "USE_PEP621" in os.environ: + run(f"poetry add --optional main {package}", where=DUMMY) + else: + run(f"poetry add --optional {package}", where=DUMMY) # Make sure pyproject.toml contains the new package dependency data = DUMMY_PYPROJECT.read_bytes().decode("utf-8") assert package in data From 33e68b434e31e14a02ae8bd469774c3628f29d28 Mon Sep 17 00:00:00 2001 From: mtkennerly Date: Mon, 17 Jun 2024 14:58:43 -0400 Subject: [PATCH 06/11] Fix test setup --- tests/test_integration.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/test_integration.py b/tests/test_integration.py index ac7bdcc..059728e 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -282,9 +282,9 @@ def test_pep621_with_dynamic_version(): run("poetry-dynamic-versioning", where=dummy) assert f'version = "{version}"' in dummy_pyproject.read_bytes().decode("utf-8") - assert f'__version__ = "{version}"' in (dummy / "project" / "__init__.py").read_bytes().decode( - "utf-8" - ) + assert f'__version__ = "{version}"' in ( + dummy / "project_pep621" / "__init__.py" + ).read_bytes().decode("utf-8") @pytest.mark.skipif("USE_PEP621" not in os.environ, reason="Requires Poetry with PEP-621 support") @@ -292,8 +292,12 @@ def test_pep621_without_dynamic_version(): dummy = ROOT / "tests" / "project-pep621" dummy_pyproject = dummy / "pyproject.toml" + data = dummy_pyproject.read_bytes().decode("utf-8") + data = re.sub(r"dynamic = .*", "dynamic = []", data) + dummy_pyproject.write_bytes(data.encode("utf-8")) + run("poetry-dynamic-versioning", where=dummy) assert "version =" not in dummy_pyproject.read_bytes().decode("utf-8") - assert '__version__ = "0.0.0"' in (dummy / "project" / "__init__.py").read_bytes().decode( - "utf-8" - ) + assert '__version__ = "0.0.0"' in ( + dummy / "project_pep621" / "__init__.py" + ).read_bytes().decode("utf-8") From 41d454b2826ef91eb97d5fa1fcf1869610046a27 Mon Sep 17 00:00:00 2001 From: mtkennerly Date: Mon, 17 Jun 2024 15:05:34 -0400 Subject: [PATCH 07/11] Expect failure in PEP 621 mode without dynamic version requested --- tests/test_integration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_integration.py b/tests/test_integration.py index 059728e..7a08a06 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -296,7 +296,7 @@ def test_pep621_without_dynamic_version(): data = re.sub(r"dynamic = .*", "dynamic = []", data) dummy_pyproject.write_bytes(data.encode("utf-8")) - run("poetry-dynamic-versioning", where=dummy) + run("poetry-dynamic-versioning", codes=[1], where=dummy) assert "version =" not in dummy_pyproject.read_bytes().decode("utf-8") assert '__version__ = "0.0.0"' in ( dummy / "project_pep621" / "__init__.py" From decb8b3b45f5ab954cb692637208dd921d7363e0 Mon Sep 17 00:00:00 2001 From: mtkennerly Date: Mon, 17 Jun 2024 15:47:42 -0400 Subject: [PATCH 08/11] Fix test cleanup --- tests/test_integration.py | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/tests/test_integration.py b/tests/test_integration.py index 7a08a06..36f8b3d 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -17,6 +17,9 @@ DUMMY_DIST = DUMMY / "dist" DUMMY_PYPROJECT = DUMMY / "pyproject.toml" +DUMMY_PEP621 = ROOT / "tests" / "project-pep621" +DUMMY_PEP621_PYPROJECT = DUMMY_PEP621 / "pyproject.toml" + DUMMY_VERSION = "0.0.999" DEPENDENCY_DYNAMIC_VERSION = "0.0.888" @@ -75,11 +78,12 @@ def before_all(): @pytest.fixture(autouse=True) def before_each(): - run(f"git checkout -- {DUMMY.as_posix()}") - delete(DUMMY / "dist") - delete(DUMMY / "poetry.lock") - for file in DUMMY.glob("*.whl"): - delete(file) + for project in [DUMMY, DUMMY_PEP621]: + run(f"git checkout -- {project.as_posix()}") + delete(project / "dist") + delete(project / "poetry.lock") + for file in project.glob("*.whl"): + delete(file) def test_plugin_enabled(): @@ -275,29 +279,23 @@ def test_plugin_show(): @pytest.mark.skipif("USE_PEP621" not in os.environ, reason="Requires Poetry with PEP-621 support") def test_pep621_with_dynamic_version(): - dummy = ROOT / "tests" / "project-pep621" - dummy_pyproject = dummy / "pyproject.toml" - version = dunamai.Version.from_git().serialize() - run("poetry-dynamic-versioning", where=dummy) - assert f'version = "{version}"' in dummy_pyproject.read_bytes().decode("utf-8") + run("poetry-dynamic-versioning", where=DUMMY_PEP621) + assert f'version = "{version}"' in DUMMY_PEP621_PYPROJECT.read_bytes().decode("utf-8") assert f'__version__ = "{version}"' in ( - dummy / "project_pep621" / "__init__.py" + DUMMY_PEP621 / "project_pep621" / "__init__.py" ).read_bytes().decode("utf-8") @pytest.mark.skipif("USE_PEP621" not in os.environ, reason="Requires Poetry with PEP-621 support") def test_pep621_without_dynamic_version(): - dummy = ROOT / "tests" / "project-pep621" - dummy_pyproject = dummy / "pyproject.toml" - - data = dummy_pyproject.read_bytes().decode("utf-8") + data = DUMMY_PEP621_PYPROJECT.read_bytes().decode("utf-8") data = re.sub(r"dynamic = .*", "dynamic = []", data) - dummy_pyproject.write_bytes(data.encode("utf-8")) + DUMMY_PEP621_PYPROJECT.write_bytes(data.encode("utf-8")) - run("poetry-dynamic-versioning", codes=[1], where=dummy) - assert "version =" not in dummy_pyproject.read_bytes().decode("utf-8") + run("poetry-dynamic-versioning", codes=[1], where=DUMMY_PEP621) + assert "version =" not in DUMMY_PEP621_PYPROJECT.read_bytes().decode("utf-8") assert '__version__ = "0.0.0"' in ( - dummy / "project_pep621" / "__init__.py" + DUMMY_PEP621 / "project_pep621" / "__init__.py" ).read_bytes().decode("utf-8") From 160e8a99dd521ebeed707804611b3b34cc179a30 Mon Sep 17 00:00:00 2001 From: mtkennerly Date: Mon, 17 Jun 2024 16:47:15 -0400 Subject: [PATCH 09/11] Add test case for 'poetry build' --- tests/project-pep621/pyproject.toml | 5 +++++ tests/test_integration.py | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/tests/project-pep621/pyproject.toml b/tests/project-pep621/pyproject.toml index 201aa6c..eec1826 100644 --- a/tests/project-pep621/pyproject.toml +++ b/tests/project-pep621/pyproject.toml @@ -2,6 +2,11 @@ name = "project-pep621" dynamic = ["version"] +[tool.poetry] +# The plugin itself doesn't need this, but Poetry does: +# https://github.com/python-poetry/poetry-core/blob/c80dcc53793316104862d2c3ac888dde3c263b08/tests/test_factory.py#L39-L42 +version = "0.0.0" + [tool.poetry-dynamic-versioning] enable = true diff --git a/tests/test_integration.py b/tests/test_integration.py index 36f8b3d..e2d073b 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -18,6 +18,7 @@ DUMMY_PYPROJECT = DUMMY / "pyproject.toml" DUMMY_PEP621 = ROOT / "tests" / "project-pep621" +DUMMY_PEP621_DIST = DUMMY_PEP621 / "dist" DUMMY_PEP621_PYPROJECT = DUMMY_PEP621 / "pyproject.toml" DUMMY_VERSION = "0.0.999" @@ -288,6 +289,20 @@ def test_pep621_with_dynamic_version(): ).read_bytes().decode("utf-8") +@pytest.mark.skipif("USE_PEP621" not in os.environ, reason="Requires Poetry with PEP-621 support") +def test_pep621_with_dynamic_version_and_cleanup(): + version = dunamai.Version.from_git().serialize() + + run("poetry build", where=DUMMY_PEP621) + assert 'version = "0.0.0"' in DUMMY_PEP621_PYPROJECT.read_bytes().decode("utf-8") + assert '__version__ = "0.0.0"' in ( + DUMMY_PEP621 / "project_pep621" / "__init__.py" + ).read_bytes().decode("utf-8") + + artifact = next(DUMMY_PEP621_DIST.glob("*.whl")) + assert f"-{version}-" in artifact.name + + @pytest.mark.skipif("USE_PEP621" not in os.environ, reason="Requires Poetry with PEP-621 support") def test_pep621_without_dynamic_version(): data = DUMMY_PEP621_PYPROJECT.read_bytes().decode("utf-8") From 4a126ca19ce0f52876e4d157bc4b9cac48e53a07 Mon Sep 17 00:00:00 2001 From: mtkennerly Date: Mon, 17 Jun 2024 16:57:18 -0400 Subject: [PATCH 10/11] Fix test pyproject updates --- tests/test_integration.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/test_integration.py b/tests/test_integration.py index e2d073b..177932e 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -283,7 +283,8 @@ def test_pep621_with_dynamic_version(): version = dunamai.Version.from_git().serialize() run("poetry-dynamic-versioning", where=DUMMY_PEP621) - assert f'version = "{version}"' in DUMMY_PEP621_PYPROJECT.read_bytes().decode("utf-8") + pyproject = tomlkit.parse(DUMMY_PEP621_PYPROJECT.read_bytes().decode("utf-8")) + assert pyproject["project"]["version"] == version assert f'__version__ = "{version}"' in ( DUMMY_PEP621 / "project_pep621" / "__init__.py" ).read_bytes().decode("utf-8") @@ -294,7 +295,8 @@ def test_pep621_with_dynamic_version_and_cleanup(): version = dunamai.Version.from_git().serialize() run("poetry build", where=DUMMY_PEP621) - assert 'version = "0.0.0"' in DUMMY_PEP621_PYPROJECT.read_bytes().decode("utf-8") + pyproject = tomlkit.parse(DUMMY_PEP621_PYPROJECT.read_bytes().decode("utf-8")) + assert "version" not in pyproject["project"] assert '__version__ = "0.0.0"' in ( DUMMY_PEP621 / "project_pep621" / "__init__.py" ).read_bytes().decode("utf-8") @@ -305,12 +307,13 @@ def test_pep621_with_dynamic_version_and_cleanup(): @pytest.mark.skipif("USE_PEP621" not in os.environ, reason="Requires Poetry with PEP-621 support") def test_pep621_without_dynamic_version(): - data = DUMMY_PEP621_PYPROJECT.read_bytes().decode("utf-8") - data = re.sub(r"dynamic = .*", "dynamic = []", data) - DUMMY_PEP621_PYPROJECT.write_bytes(data.encode("utf-8")) + pyproject = tomlkit.parse(DUMMY_PEP621_PYPROJECT.read_bytes().decode("utf-8")) + pyproject["project"]["dynamic"] = [] + DUMMY_PEP621_PYPROJECT.write_bytes(tomlkit.dumps(pyproject).encode("utf-8")) run("poetry-dynamic-versioning", codes=[1], where=DUMMY_PEP621) - assert "version =" not in DUMMY_PEP621_PYPROJECT.read_bytes().decode("utf-8") + pyproject = tomlkit.parse(DUMMY_PEP621_PYPROJECT.read_bytes().decode("utf-8")) + assert "version" not in pyproject["project"] assert '__version__ = "0.0.0"' in ( DUMMY_PEP621 / "project_pep621" / "__init__.py" ).read_bytes().decode("utf-8") From fb3994104f1bb88139b13cee55561a3ed17f63de Mon Sep 17 00:00:00 2001 From: mtkennerly Date: Mon, 17 Jun 2024 17:25:22 -0400 Subject: [PATCH 11/11] Remove 'version' from 'dynamic' list as needed --- poetry_dynamic_versioning/__init__.py | 33 +++++++++++++++++++-------- tests/test_integration.py | 2 ++ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/poetry_dynamic_versioning/__init__.py b/poetry_dynamic_versioning/__init__.py index 95509e1..604a799 100644 --- a/poetry_dynamic_versioning/__init__.py +++ b/poetry_dynamic_versioning/__init__.py @@ -131,12 +131,14 @@ def __init__( original_version: Optional[str], version: str, mode: _Mode, + dynamic_index: Optional[int], substitutions: Optional[MutableMapping[Path, str]] = None, ) -> None: self.path = path self.original_version = original_version self.version = version self.mode = mode + self.dynamic_index = dynamic_index self.substitutions = ( {} if substitutions is None else substitutions ) # type: MutableMapping[Path, str] @@ -611,6 +613,7 @@ def _apply_version( if mode == _Mode.Classic: pyproject["tool"]["poetry"]["version"] = version # type: ignore elif mode == _Mode.Pep621: + pyproject["project"]["dynamic"].remove("version") # type: ignore pyproject["project"]["version"] = version # type: ignore # Disable the plugin in case we're building a source distribution, @@ -669,16 +672,22 @@ def _get_and_apply_version( and "poetry" in pyproject["tool"] and "name" in pyproject["tool"]["poetry"] ) - pep621 = "project" in pyproject and "name" in pyproject["project"] + pep621 = ( + "project" in pyproject + and "name" in pyproject["project"] + and "dynamic" in pyproject["project"] + and "version" in pyproject["project"]["dynamic"] + and "version" not in pyproject["project"] + ) if classic: name = pyproject["tool"]["poetry"]["name"] original = pyproject["tool"]["poetry"]["version"] + dynamic_index = None elif pep621: name = pyproject["project"]["name"] original = pyproject["project"].get("version") - if "version" not in pyproject["project"].get("dynamic", []): - return name if name in _state.projects else None + dynamic_index = pyproject["project"]["dynamic"].index("version") else: return name if name in _state.projects else None @@ -699,12 +708,16 @@ def _get_and_apply_version( if classic and name is not None and original is not None: mode = _Mode.Classic - _state.projects[name] = _ProjectState(pyproject_path, original, version, mode) + _state.projects[name] = _ProjectState( + pyproject_path, original, version, mode, dynamic_index + ) if io: _apply_version(name, version, instance, config, pyproject_path, mode, retain) elif pep621 and name is not None: mode = _Mode.Pep621 - _state.projects[name] = _ProjectState(pyproject_path, original, version, mode) + _state.projects[name] = _ProjectState( + pyproject_path, original, version, mode, dynamic_index + ) if io: _apply_version(name, version, instance, config, pyproject_path, mode, retain) @@ -733,11 +746,13 @@ def _revert_version(retain: bool = False) -> None: pyproject = tomlkit.parse(state.path.read_bytes().decode("utf-8")) if state.mode == _Mode.Classic: - pyproject["tool"]["poetry"]["version"] = state.original_version # type: ignore - elif state.mode == _Mode.Pep621: if state.original_version is not None: - pyproject["project"]["version"] = state.original_version # type: ignore - elif "version" in pyproject["project"]: # type: ignore + pyproject["tool"]["poetry"]["version"] = state.original_version # type: ignore + elif state.mode == _Mode.Pep621: + if state.dynamic_index is not None: + index = state.dynamic_index + pyproject["project"]["dynamic"].insert(index, "version") # type: ignore + if "version" in pyproject["project"]: # type: ignore pyproject["project"].pop("version") # type: ignore if not retain and not _state.cli_mode: diff --git a/tests/test_integration.py b/tests/test_integration.py index 177932e..bdc0886 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -285,6 +285,7 @@ def test_pep621_with_dynamic_version(): run("poetry-dynamic-versioning", where=DUMMY_PEP621) pyproject = tomlkit.parse(DUMMY_PEP621_PYPROJECT.read_bytes().decode("utf-8")) assert pyproject["project"]["version"] == version + assert "version" not in pyproject["project"]["dynamic"] assert f'__version__ = "{version}"' in ( DUMMY_PEP621 / "project_pep621" / "__init__.py" ).read_bytes().decode("utf-8") @@ -297,6 +298,7 @@ def test_pep621_with_dynamic_version_and_cleanup(): run("poetry build", where=DUMMY_PEP621) pyproject = tomlkit.parse(DUMMY_PEP621_PYPROJECT.read_bytes().decode("utf-8")) assert "version" not in pyproject["project"] + assert "version" in pyproject["project"]["dynamic"] assert '__version__ = "0.0.0"' in ( DUMMY_PEP621 / "project_pep621" / "__init__.py" ).read_bytes().decode("utf-8")