File: plugin.py

package info (click to toggle)
python-poetry-dynamic-versioning 1.9.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 460 kB
  • sloc: python: 1,528; makefile: 4
file content (197 lines) | stat: -rw-r--r-- 6,176 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
__all__ = [
    "DynamicVersioningCommand",
    "DynamicVersioningPlugin",
]

import functools
import os

from cleo.commands.command import Command
from cleo.events.console_command_event import ConsoleCommandEvent
from cleo.events.event_dispatcher import EventDispatcher
from cleo.events.console_events import COMMAND, SIGNAL, TERMINATE, ERROR
from packaging.version import Version as PackagingVersion
from poetry.core import __version__ as poetry_core_version
from poetry.core.poetry import Poetry
from poetry.core.factory import Factory
from poetry.console.application import Application
from poetry.plugins.application_plugin import ApplicationPlugin

if PackagingVersion(poetry_core_version) >= PackagingVersion("1.3.0"):
    from poetry.core.constraints.version import Version as PoetryCoreVersion
else:
    from poetry.core.semver.version import Version as PoetryCoreVersion


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

_COMMAND_ENV = "POETRY_DYNAMIC_VERSIONING_COMMANDS"
_COMMAND_NO_IO_ENV = "POETRY_DYNAMIC_VERSIONING_COMMANDS_NO_IO"


def _patch_dependency_versions(io: bool) -> None:
    """
    The plugin system doesn't seem to expose a way to change dependency
    versions, so we patch `Factory.create_poetry()` to do the work there.
    """
    if _state.patched_core_poetry_create:
        return

    original_create_poetry = Factory.create_poetry

    @functools.wraps(Factory.create_poetry)
    def patched_create_poetry(*args, **kwargs):
        instance = original_create_poetry(*args, **kwargs)
        _apply_version_via_plugin(instance, io=io)
        return instance

    Factory.create_poetry = patched_create_poetry
    _state.patched_core_poetry_create = True


def _should_apply(command: str) -> bool:
    override = os.environ.get(_COMMAND_ENV)
    if override is not None:
        return command in override.split(",")
    else:
        return command not in ["run", "shell", cli.Command.dv, cli.Command.dv_enable, cli.Command.dv_show]


def _should_apply_with_io(command: str) -> bool:
    override = os.environ.get(_COMMAND_NO_IO_ENV)
    if override is not None:
        return command not in override.split(",")
    else:
        return command not in ["version"]


def _apply_version_via_plugin(
    poetry: Poetry,
    retain: bool = False,
    force: bool = False,
    standalone: bool = False,
    # fmt: off
    io: bool = True
    # fmt: on
) -> None:
    name = _get_and_apply_version(
        pyproject_path=_get_pyproject_path_from_poetry(poetry.pyproject),
        retain=retain,
        force=force,
        io=io,
    )
    if name:
        version = _state.projects[name].version

        # Would be nice to use `.set_version()`, but it's only available on
        # Poetry's `ProjectPackage`, not poetry-core's `ProjectPackage`.
        poetry._package._version = PoetryCoreVersion.parse(version)
        poetry._package._pretty_version = version

        if standalone:
            cli.report_apply(name)


class DynamicVersioningCommand(Command):
    name = cli.Command.dv
    description = cli.Help.main

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

    def handle(self) -> int:
        _state.cli_mode = True
        _apply_version_via_plugin(self._application.poetry, retain=True, force=True, standalone=True)
        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 DynamicVersioningShowCommand(Command):
    name = cli.Command.dv_show
    description = cli.Help.show

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

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


class DynamicVersioningPlugin(ApplicationPlugin):
    def __init__(self):
        self._application = None

    def activate(self, application: Application) -> None:
        self._application = application

        application.command_loader.register_factory(cli.Command.dv, lambda: DynamicVersioningCommand(application))
        application.command_loader.register_factory(
            cli.Command.dv_enable, lambda: DynamicVersioningEnableCommand(application)
        )
        application.command_loader.register_factory(
            cli.Command.dv_show, lambda: DynamicVersioningShowCommand(application)
        )

        try:
            local = self._application.poetry.pyproject.data
        except RuntimeError:
            # We're not in a Poetry project directory
            return

        cli.validate(standalone=False, config=local)

        config = _get_config(local)
        if not config["enable"]:
            return

        application.event_dispatcher.add_listener(COMMAND, self._apply_version)
        application.event_dispatcher.add_listener(SIGNAL, self._revert_version)
        application.event_dispatcher.add_listener(TERMINATE, self._revert_version)
        application.event_dispatcher.add_listener(ERROR, self._revert_version)

    def _apply_version(self, event: ConsoleCommandEvent, kind: str, dispatcher: EventDispatcher) -> None:
        if not _should_apply(event.command.name):
            return

        io = _should_apply_with_io(event.command.name)

        if hasattr(event.command, "poetry"):
            poetry_instance = event.command.poetry
        else:
            poetry_instance = self._application.poetry

        _apply_version_via_plugin(poetry_instance, io=io)
        _patch_dependency_versions(io)

    def _revert_version(self, event: ConsoleCommandEvent, kind: str, dispatcher: EventDispatcher) -> None:
        if not _should_apply(event.command.name):
            return

        if not _should_apply_with_io(event.command.name):
            return

        _revert_version()