Skip to content

Commit

Permalink
#117: Add 'enable' subcommand
Browse files Browse the repository at this point in the history
  • Loading branch information
mtkennerly committed Jun 12, 2023
1 parent 9427e87 commit 85b06d9
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 48 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
## Unreleased

* Added:
* CLI `enable` subcommand to enable the plugin in pyproject.toml.
* Support for `POETRY_DYNAMIC_VERSIONING_OVERRIDE` environment variable.
* Changed:
* CLI: `poetry dynamic-versioning` now outputs a summary of the changes,
the same way that `poetry-dynamic-versioning` already did.

## v0.22.0 (2023-05-19)

Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@ If you've previously installed the deprecated `poetry-dynamic-versioning-plugin`
be sure to uninstall it before proceeding.

* Run: `poetry self add "poetry-dynamic-versioning[plugin]"`
* Add this section to your pyproject.toml:
* Run in your project: `poetry dynamic-versioning enable`

Or you can update your pyproject.toml manually:

```toml
[tool.poetry-dynamic-versioning]
enable = true
```
* Include the plugin in the `build-system` section of pyproject.toml

Include the plugin in the `build-system` section of pyproject.toml
for interoperability with PEP 517 build frontends:

```toml
Expand Down
38 changes: 6 additions & 32 deletions poetry_dynamic_versioning/__main__.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,20 @@
import argparse
import sys

from poetry_dynamic_versioning import (
_get_and_apply_version,
_state,
_validate_config,
cli,
)


def _parse_args(argv=None) -> None:
parser = argparse.ArgumentParser(
description=(
"Apply the dynamic version to all relevant files and leave the changes in-place."
" This allows you to activate the plugin behavior on demand and inspect the result."
" Your configuration will be detected from pyproject.toml as normal."
)
)
parser.parse_args(argv)


def main() -> None:
try:
_state.cli_mode = True
_parse_args()

errors = _validate_config()
if errors:
print("Configuration issues:", file=sys.stderr)
for error in errors:
print(" - {}".format(error), file=sys.stderr)

name = _get_and_apply_version(retain=True, force=True)
if not name:
raise RuntimeError("Unable to determine a dynamic version")
args = cli.parse_args()

print("Version: {}".format(_state.projects[name].version), file=sys.stderr)
if _state.projects[name].substitutions:
print("Files with substitutions:", file=sys.stderr)
for file_name in _state.projects[name].substitutions:
print(" - {}".format(file_name), file=sys.stderr)
else:
print("Files with substitutions: none", file=sys.stderr)
if args.cmd is None:
cli.apply(standalone=True)
elif args.cmd == cli.Command.enable:
cli.enable()
except Exception as e:
print("Error: {}".format(e), file=sys.stderr)
sys.exit(1)
120 changes: 120 additions & 0 deletions poetry_dynamic_versioning/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import argparse
import sys
from typing import (
Mapping,
Optional,
)

import tomlkit

from poetry_dynamic_versioning import (
_get_and_apply_version,
_get_pyproject_path,
_state,
_validate_config,
)

_DEFAULT_REQUIRES = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"]
_DEFAULT_BUILD_BACKEND = "poetry_dynamic_versioning.backend"


class Key:
tool = "tool"
pdv = "poetry-dynamic-versioning"
enable = "enable"
build_system = "build-system"
requires = "requires"
build_backend = "build-backend"


class Command:
dv = "dynamic-versioning"
enable = "enable"
dv_enable = "{} {}".format(dv, enable)


class Help:
main = (
"Apply the dynamic version to all relevant files and leave the changes in-place."
" This allows you to activate the plugin behavior on demand and inspect the result."
" Your configuration will be detected from pyproject.toml as normal."
)
enable = (
"Update pyproject.toml to enable the plugin using a typical configuration."
" The output may not be suitable for more complex use cases."
)


def parse_args(argv=None) -> argparse.Namespace:
parser = argparse.ArgumentParser(description=Help.main)

subparsers = parser.add_subparsers(dest="cmd", title="subcommands")
subparsers.add_parser(Command.enable, help=Help.enable)

return parser.parse_args(argv)


def validate(*, standalone: bool, config: Optional[Mapping] = None) -> None:
errors = _validate_config(config)
if errors:
if standalone:
print("Configuration issues:", file=sys.stderr)
else:
print("poetry-dynamic-versioning configuration issues:", file=sys.stderr)
for error in errors:
print(" - {}".format(error), file=sys.stderr)


def apply(*, standalone: bool) -> None:
validate(standalone=standalone)

name = _get_and_apply_version(retain=True, force=True)
if not name:
raise RuntimeError("Unable to determine a dynamic version")

report_apply(name)


def report_apply(name: str) -> None:
print("Version: {}".format(_state.projects[name].version), file=sys.stderr)
if _state.projects[name].substitutions:
print("Files with substitutions:", file=sys.stderr)
for file_name in _state.projects[name].substitutions:
print(" - {}".format(file_name), file=sys.stderr)
else:
print("Files with substitutions: none", file=sys.stderr)


def enable() -> None:
pyproject_path = _get_pyproject_path()
if pyproject_path is None:
raise RuntimeError("Unable to find pyproject.toml")
config = tomlkit.parse(pyproject_path.read_text(encoding="utf-8"))

config = _enable_in_doc(config)
pyproject_path.write_bytes(tomlkit.dumps(config).encode("utf-8"))


def _enable_in_doc(doc: tomlkit.TOMLDocument) -> tomlkit.TOMLDocument:
pdv_table = tomlkit.table().add(Key.enable, True)
tool_table = tomlkit.table().add(Key.pdv, pdv_table)

if doc.get(Key.tool) is None:
doc.add(Key.tool, tool_table)
elif doc[Key.tool].get(Key.pdv) is None: # type: ignore
doc[Key.tool].add(Key.pdv, pdv_table) # type: ignore
else:
doc[Key.tool][Key.pdv].update(pdv_table) # type: ignore

build_system_table = (
tomlkit.table()
.add(Key.requires, _DEFAULT_REQUIRES)
.add(Key.build_backend, _DEFAULT_BUILD_BACKEND)
)

if doc.get(Key.build_system) is None:
doc.add(Key.build_system, build_system_table)
else:
doc[Key.build_system].update(build_system_table) # type: ignore

return doc
39 changes: 25 additions & 14 deletions poetry_dynamic_versioning/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import functools
import os
import sys

from cleo.commands.command import Command
from cleo.events.console_command_event import ConsoleCommandEvent
Expand All @@ -25,12 +24,12 @@


from poetry_dynamic_versioning import (
cli,
_get_config,
_get_and_apply_version,
_get_pyproject_path_from_poetry,
_state,
_revert_version,
_validate_config,
)

_COMMAND_ENV = "POETRY_DYNAMIC_VERSIONING_COMMANDS"
Expand Down Expand Up @@ -61,7 +60,7 @@ def _should_apply(command: str) -> bool:
if override is not None:
return command in override.split(",")
else:
return command not in ["run", "shell", "dynamic-versioning"]
return command not in ["run", "shell", cli.Command.dv, cli.Command.dv_enable]


def _apply_version_via_plugin(poetry: Poetry, retain: bool = False, force: bool = False) -> None:
Expand All @@ -81,13 +80,12 @@ def _apply_version_via_plugin(poetry: Poetry, retain: bool = False, force: bool
poetry._package._version = PoetryCoreVersion.parse(version)
poetry._package._pretty_version = version

cli.report_apply(name)


class DynamicVersioningCommand(Command):
name = "dynamic-versioning"
description = (
"Apply the dynamic version to all relevant files and leave the changes in-place."
" This allows you to activate the plugin behavior on demand and inspect the result."
)
name = cli.Command.dv
description = cli.Help.main

def __init__(self, application: Application):
super().__init__()
Expand All @@ -99,6 +97,20 @@ def handle(self) -> int:
return 0


class DynamicVersioningEnableCommand(Command):
name = cli.Command.dv_enable
description = cli.Help.enable

def __init__(self, application: Application):
super().__init__()
self._application = application

def handle(self) -> int:
_state.cli_mode = True
cli.enable()
return 0


class DynamicVersioningPlugin(ApplicationPlugin):
def __init__(self):
self._application = None
Expand All @@ -107,7 +119,10 @@ def activate(self, application: Application) -> None:
self._application = application

application.command_loader.register_factory(
"dynamic-versioning", lambda: DynamicVersioningCommand(application)
cli.Command.dv, lambda: DynamicVersioningCommand(application)
)
application.command_loader.register_factory(
cli.Command.dv_enable, lambda: DynamicVersioningEnableCommand(application)
)

try:
Expand All @@ -116,11 +131,7 @@ def activate(self, application: Application) -> None:
# We're not in a Poetry project directory
return

errors = _validate_config(local)
if errors:
print("poetry-dynamic-versioning configuration issues:", file=sys.stderr)
for error in errors:
print(" - {}".format(error), file=sys.stderr)
cli.validate(standalone=False, config=local)

config = _get_config(local)
if not config["enable"]:
Expand Down
85 changes: 85 additions & 0 deletions tests/test_unit.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import os
import textwrap
from pathlib import Path

import pytest
import tomlkit
from dunamai import Version

import poetry_dynamic_versioning as plugin
from poetry_dynamic_versioning import cli

root = Path(__file__).parents[1]

Expand Down Expand Up @@ -105,3 +108,85 @@ def test__get_override_version__combined():
env = {plugin._BYPASS_ENV: "0.0.0", plugin._OVERRIDE_ENV: "foo = 0.1.0, bar = 0.2.0"}
assert plugin._get_override_version(None, env) == "0.0.0"
assert plugin._get_override_version("foo", env) == "0.1.0"


def test__enable_in_doc__empty():
doc = tomlkit.parse("")
updated = cli._enable_in_doc(doc)
assert updated[cli.Key.tool][cli.Key.pdv][cli.Key.enable] is True
assert updated[cli.Key.build_system][cli.Key.requires] == cli._DEFAULT_REQUIRES
assert updated[cli.Key.build_system][cli.Key.build_backend] == cli._DEFAULT_BUILD_BACKEND
assert (
tomlkit.dumps(updated)
== textwrap.dedent(
"""
[tool]
[tool.poetry-dynamic-versioning]
enable = true
[build-system]
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"]
build-backend = "poetry_dynamic_versioning.backend"
"""
).lstrip()
)


def test__enable_in_doc__added_pdv():
doc = tomlkit.parse(
textwrap.dedent(
"""
[tool]
foo = 1
"""
)
)
updated = cli._enable_in_doc(doc)
assert updated[cli.Key.tool][cli.Key.pdv][cli.Key.enable] is True
assert updated[cli.Key.build_system][cli.Key.requires] == cli._DEFAULT_REQUIRES
assert updated[cli.Key.build_system][cli.Key.build_backend] == cli._DEFAULT_BUILD_BACKEND


def test__enable_in_doc__updated_enable():
doc = tomlkit.parse(
textwrap.dedent(
"""
[tool.poetry-dynamic-versioning]
enable = false
"""
)
)
updated = cli._enable_in_doc(doc)
assert updated[cli.Key.tool][cli.Key.pdv][cli.Key.enable] is True
assert updated[cli.Key.build_system][cli.Key.requires] == cli._DEFAULT_REQUIRES
assert updated[cli.Key.build_system][cli.Key.build_backend] == cli._DEFAULT_BUILD_BACKEND


def test__enable_in_doc__updated_requires():
doc = tomlkit.parse(
textwrap.dedent(
"""
[build-system]
requires = ["foo"]
"""
)
)
updated = cli._enable_in_doc(doc)
assert updated[cli.Key.tool][cli.Key.pdv][cli.Key.enable] is True
assert updated[cli.Key.build_system][cli.Key.requires] == cli._DEFAULT_REQUIRES
assert updated[cli.Key.build_system][cli.Key.build_backend] == cli._DEFAULT_BUILD_BACKEND


def test__enable_in_doc__updated_build_backend():
doc = tomlkit.parse(
textwrap.dedent(
"""
[build-system]
build-backend = ""
"""
)
)
updated = cli._enable_in_doc(doc)
assert updated[cli.Key.tool][cli.Key.pdv][cli.Key.enable] is True
assert updated[cli.Key.build_system][cli.Key.requires] == cli._DEFAULT_REQUIRES
assert updated[cli.Key.build_system][cli.Key.build_backend] == cli._DEFAULT_BUILD_BACKEND

0 comments on commit 85b06d9

Please sign in to comment.