Skip to content

Commit

Permalink
#17 add pathdep remover (#19)
Browse files Browse the repository at this point in the history
* #17 - Partial complete work to add removing of path dependencies invocations

* #17 - Adding back original commands so they stay intact

* #17 - modifying export command behavior

* #17 - modifying export command intercept logic

* #17 - modifying export intercept debug mode

* #17 - removing unneeded built in export command imports

* #17 - correcting spelling in README and adding export to enable verbiage
  • Loading branch information
JeffreyRoss authored Mar 13, 2024
1 parent d63b869 commit 534db6a
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 10 deletions.
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ building and publishing archives with local path dependencies to other Poetry pr
poetry self add poetry-monorepo-dependency-plugin
```

If you want to activate `poetry-monorepo-dependency-plugin` for all [build][poetry-build] and
[publish][poetry-publish] command invocations, add the following to your project's `pyproject.toml`
If you want to activate `poetry-monorepo-dependency-plugin` for all [build][poetry-build],
[publish][poetry-publish], and [export][poetry-export] command invocations, add the following to your project's `pyproject.toml`
that has path dependencies to other Poetry projects:

```toml
Expand Down Expand Up @@ -57,6 +57,11 @@ When generating `wheel` or `sdist` archives for the `spam` project through Poetr
`ham` project were declared as `ham = "1.2.3"`. As a result, package metadata in archives for `spam` will shift from
`Requires-Dist: ham @ ../ham` to `Requires-Dist: ham (==1.2.3)`

Additionally, to address [path dependencies](https://python-poetry.org/docs/dependency-specification/#path-dependencies) not being portable, this plugin provides
the ability to extend Poetry's [export][poetry-export] command using the `export-without-path-deps` command. This command will exclude path dependencies from being written to intermediate `requirements.txt` export. When installing the exported
`requirements.txt` on another machine and/or Docker container local Paths dependencies can not be resolved and therefore can not
be installed so they are removed.

### Command line mode

If you need greater control over when `poetry-monorepo-dependency-plugin` is activated, this plugin exposes new `build-rewrite-path-deps`
Expand All @@ -68,6 +73,11 @@ any configuration defined in the project's `pyproject.toml` `[tool.poetry-monore
poetry build-rewrite-path-deps --version-pinning-strategy=semver
```

To generate a `requirements.txt` without path dependencies use the `export-without-path-deps`. For example:
```commandline
poetry export-without-path-deps -f requirements.txt --output requirements.txt
```

### Configuration

The following configuration options are supported within your project's `pyproject.toml` configuration:
Expand Down Expand Up @@ -113,5 +123,6 @@ the package to PyPI.

[poetry]: https://python-poetry.org/
[poetry-build]: https://python-poetry.org/docs/cli/#build
[poetry-export]: https://python-poetry.org/docs/cli/#export
[poetry-publish]: https://python-poetry.org/docs/cli/#publish
[mit_licence]: http://dan.mit-license.org/
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ repository = "https://github.com/TechnologyBrewery/poetry-monorepo-dependency-pl
[tool.poetry.dependencies]
python = "^3.8"
poetry = ">=1.6"
poetry-plugin-export = ">=1.5.0"
cleo = ">=2.0.1"

[tool.poetry.group.dev.dependencies]
Expand Down
53 changes: 53 additions & 0 deletions src/poetry_monorepo_dependency_plugin/path_dependency_remover.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import typing

from cleo.io.io import IO as cleoIO
import cleo.io.outputs.output
from poetry.core.pyproject.toml import PyProjectTOML
from poetry.core.constraints.version import Version
from poetry.core.packages.dependency import Dependency
from poetry.core.packages.directory_dependency import DirectoryDependency
from poetry.core.packages.dependency_group import DependencyGroup


class PathDependencyRemover:
"""
Exposes core functionality for gathering a pyproject.toml's path dependencies,
determining if they are Poetry projects, and if so, extracting the corresponding
dependency.
"""

def update_dependency_group(
self,
io: cleoIO,
pyproject: PyProjectTOML,
dependency_group: DependencyGroup,
) -> None:
"""
Removes all path dependencies to Poetry projects.
:param io: instance of Cleo IO that may be used for logging diagnostic output during
plugin execution
:param pyproject: encapsulates the pyproject.toml of the current project for which
path dependencies will be rewritten
:param dependency_group: specifies the dependency group from which to pin path
dependencies, this will usually be "main"
:return: none
"""
io.write_line(
"Updating dependency constraints...",
verbosity=cleo.io.outputs.output.Verbosity.DEBUG,
)

for dependency in dependency_group.dependencies:
if not isinstance(
dependency,
DirectoryDependency,
):
continue

io.write_line(
f" • Removing {dependency.name} path dependency)",
verbosity=cleo.io.outputs.output.Verbosity.DEBUG,
)

dependency_group.remove_dependency(dependency.name)
49 changes: 42 additions & 7 deletions src/poetry_monorepo_dependency_plugin/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
from cleo.helpers import option
from poetry.console.commands.build import BuildCommand
from poetry.console.commands.publish import PublishCommand
from poetry_plugin_export.command import ExportCommand

from .path_dependency_rewriter import PathDependencyRewriter
from .path_dependency_remover import PathDependencyRemover

_version_pinning_strategy = option(
"version-pinning-strategy",
Expand Down Expand Up @@ -75,10 +77,26 @@ def handle(self) -> int:
return super().handle()


class ExportWithoutPathDepsCommand(ExportCommand):
name = "export-without-path-deps"
description = (
"Extends the 'export' command to generate exports in which path dependencies to "
"other Poetry projects are removed from package dependencies."
)

def handle(self) -> int:
path_dependency_remover = PathDependencyRemover()
path_dependency_remover.update_dependency_group(
self.io, self.poetry.pyproject, self.poetry.package.dependency_group("main")
)
return super().handle()


class MonorepoDependencyPlugin(poetry.plugins.application_plugin.ApplicationPlugin):
COMMANDS = (
BuildCommand,
PublishCommand,
ExportCommand,
)

def __init__(self):
Expand All @@ -92,6 +110,9 @@ def activate(self, application: poetry.console.application.Application):
application.command_loader.register_factory(
"publish-rewrite-path-deps", lambda: PublishWithVersionedPathDepsCommand()
)
application.command_loader.register_factory(
"export-without-path-deps", lambda: ExportWithoutPathDepsCommand()
)

try:
local_poetry_proj_config = application.poetry.pyproject.data
Expand Down Expand Up @@ -121,18 +142,32 @@ def event_listener(
event_name: str,
dispatcher: cleo.events.event_dispatcher.EventDispatcher,
) -> None:

if not isinstance(event.command, self.COMMANDS):
return

path_dependency_writer = PathDependencyRewriter(
self.plugin_config["version-pinning-strategy"]
)
path_dependency_writer.update_dependency_group(
event.io,
self.poetry.pyproject,
self.poetry.package.dependency_group("main"),
event.io.write_line(
"Intercepting the command: " + str(event.command.__class__),
verbosity=cleo.io.outputs.output.Verbosity.DEBUG,
)

if isinstance(event.command, ExportCommand):
path_dependency_remover = PathDependencyRemover()
path_dependency_remover.update_dependency_group(
event.io,
self.poetry.pyproject,
self.poetry.package.dependency_group("main"),
)
else:
path_dependency_writer = PathDependencyRewriter(
self.plugin_config["version-pinning-strategy"]
)
path_dependency_writer.update_dependency_group(
event.io,
self.poetry.pyproject,
self.poetry.package.dependency_group("main"),
)


def _default_plugin_config() -> Mapping:
"""
Expand Down
15 changes: 15 additions & 0 deletions tests/features/remove-path-dependencies.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Feature: Remove path dependencies from Poetry projects

Scenario Outline: Remove project dependencies with paths
Given a project with a local path dependencies to other Poetry projects
When the project is exported using the plugin's command-line mode
Then the path dependencies for "<dependency name>" are removed from poetry dependencies
Examples:
| dependency name
| spam
| spam
| spam
| ham
| ham
| ham
| eggs
2 changes: 1 addition & 1 deletion tests/features/rewrite-path-dependencies.feature
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ Feature: Re-write path dependencies to Poetry projects as versioned package depe
| ham | mixed | 4.5.6 |
| ham | exact | 4.5.6 |
| ham | semver | ^4.5.6 |
| eggs | mixed | >=1.0rc4,<1.0.1 |
| eggs | mixed | >=1.0rc4,<1.0.1 |
38 changes: 38 additions & 0 deletions tests/features/steps/remove_path_dependencies_steps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import unittest.mock
import unittest
from pathlib import Path

import cleo.io.io
import poetry.core.factory
from behave import *
import nose.tools as nt

from poetry_monorepo_dependency_plugin.path_dependency_remover import (
PathDependencyRemover,
)


@when("the project is exported using the plugin's command-line mode")
def step_impl(context):
path_dependency_remover = PathDependencyRemover()
mock_io = unittest.mock.create_autospec(cleo.io.io.IO)
path_dependency_remover.update_dependency_group(
mock_io,
context.project_with_local_deps.pyproject,
context.project_with_local_deps.package.dependency_group("main"),
)


@then(
'the path dependencies for "{dependency_name}" are removed from poetry dependencies'
)
def step_impl(context, dependency_name):
mydependencies = context.project_with_local_deps.package.dependency_group(
"main"
).dependencies

nt.assert_not_in(
dependency_name,
mydependencies,
f"Found the path dependency {dependency_name}",
)

0 comments on commit 534db6a

Please sign in to comment.