Skip to content

Commit

Permalink
add support for PEP 621: enrich [project] dependencies with informati…
Browse files Browse the repository at this point in the history
…on from [tool.poetry] dependencies for locking (python-poetry#708)
  • Loading branch information
radoering committed Sep 1, 2024
1 parent 1de43b7 commit 06f0e3b
Show file tree
Hide file tree
Showing 8 changed files with 403 additions and 41 deletions.
13 changes: 4 additions & 9 deletions src/poetry/core/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def _add_package_group_dependencies(
package.python_versions = _constraint
continue

group.add_dependency(
group.add_poetry_dependency(
cls.create_dependency(
name,
_constraint,
Expand Down Expand Up @@ -260,14 +260,7 @@ def _configure_package_dependencies(

package.extras = package_extras

# TODO: ignore dependencies in [tool.poetry] for now if dependencies or
# optional-dependencies are declared in [project],
# later we have to merge them (abstract vs. concrete dependencies)
if (
not dependencies
and not optional_dependencies
and "dependencies" in tool_poetry
):
if "dependencies" in tool_poetry:
cls._add_package_group_dependencies(
package=package,
group=MAIN_GROUP,
Expand Down Expand Up @@ -456,6 +449,8 @@ def create_dependency(
allows_prereleases=allows_prereleases,
extras=constraint.get("extras", []),
)
# Normally not valid, but required for enriching [project] dependencies
dependency._develop = constraint.get("develop", False)

marker = parse_marker(markers) if markers else AnyMarker()

Expand Down
2 changes: 2 additions & 0 deletions src/poetry/core/packages/dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ def __init__(

self._groups = frozenset(groups)
self._allows_prereleases = allows_prereleases
# "_develop" is only required for enriching [project] dependencies
self._develop = False

self._python_versions = "*"
self._python_constraint = parse_constraint("*")
Expand Down
91 changes: 85 additions & 6 deletions src/poetry/core/packages/dependency_group.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from __future__ import annotations

from collections import defaultdict
from typing import TYPE_CHECKING


if TYPE_CHECKING:
from poetry.core.packages.dependency import Dependency
from poetry.core.version.markers import BaseMarker


MAIN_GROUP = "main"
Expand All @@ -15,20 +17,54 @@ def __init__(self, name: str, optional: bool = False) -> None:
self._name: str = name
self._optional: bool = optional
self._dependencies: list[Dependency] = []
self._poetry_dependencies: list[Dependency] = []

@property
def name(self) -> str:
return self._name

@property
def dependencies(self) -> list[Dependency]:
return self._dependencies
return self._dependencies or self._poetry_dependencies

@property
def dependencies_for_locking(self) -> list[Dependency]:
if not self._poetry_dependencies:
return self._dependencies
if not self._dependencies:
return self._poetry_dependencies

poetry_dependencies_by_name = defaultdict(list)
for dep in self._poetry_dependencies:
poetry_dependencies_by_name[dep.name].append(dep)

dependencies = []
for dep in self._dependencies:
if dep.name in poetry_dependencies_by_name:
enriched = False
for poetry_dep in poetry_dependencies_by_name[dep.name]:
marker = dep.marker.intersect(poetry_dep.marker)
if not marker.is_empty():
enriched = True
dependencies.append(_enrich_dependency(dep, poetry_dep, marker))
if not enriched:
dependencies.append(dep)
else:
dependencies.append(dep)

return dependencies

def is_optional(self) -> bool:
return self._optional

def add_dependency(self, dependency: Dependency) -> None:
self._dependencies.append(dependency)
if not self._dependencies and self._poetry_dependencies:
self._poetry_dependencies.append(dependency)
else:
self._dependencies.append(dependency)

def add_poetry_dependency(self, dependency: Dependency) -> None:
self._poetry_dependencies.append(dependency)

def remove_dependency(self, name: str) -> None:
from packaging.utils import canonicalize_name
Expand All @@ -39,19 +75,62 @@ def remove_dependency(self, name: str) -> None:
for dependency in self.dependencies:
if dependency.name == name:
continue

dependencies.append(dependency)

self._dependencies = dependencies

dependencies = []
for dependency in self._poetry_dependencies:
if dependency.name == name:
continue
dependencies.append(dependency)
self._poetry_dependencies = dependencies

def __eq__(self, other: object) -> bool:
if not isinstance(other, DependencyGroup):
return NotImplemented

return self._name == other.name and set(self._dependencies) == set(
other.dependencies
return (
self._name == other.name
and set(self._dependencies) == set(other.dependencies)
and set(self._poetry_dependencies) == set(other._poetry_dependencies)
)

def __repr__(self) -> str:
cls = self.__class__.__name__
return f"{cls}({self._name}, optional={self._optional})"


def _enrich_dependency(
project_dependency: Dependency, poetry_dependency: Dependency, marker: BaseMarker
) -> Dependency:
if (
project_dependency.source_type is not None
and poetry_dependency.source_type is not None
and not poetry_dependency.is_same_source_as(project_dependency)
):
raise ValueError(
"Cannot enrich dependency with different sources: "
f"{project_dependency} and {poetry_dependency}"
)

constraint = project_dependency.constraint.intersect(poetry_dependency.constraint)
if constraint.is_empty():
raise ValueError(
"Cannot enrich dependency with incompatible constraints: "
f"{project_dependency} and {poetry_dependency}"
)

if project_dependency.source_type is not None:
from poetry.core.packages.directory_dependency import DirectoryDependency
from poetry.core.packages.vcs_dependency import VCSDependency

dependency = project_dependency.clone()
if isinstance(project_dependency, (DirectoryDependency, VCSDependency)):
dependency._develop = poetry_dependency._develop # type: ignore[has-type]
else:
dependency = poetry_dependency.with_features(project_dependency.features)

dependency.constraint = constraint
dependency.marker = marker

return dependency
11 changes: 5 additions & 6 deletions src/poetry/core/packages/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,24 +203,23 @@ def maintainer_email(self) -> str | None:
@property
def requires(self) -> list[Dependency]:
"""
Returns the main dependencies
Returns the main dependencies.
"""
if not self._dependency_groups or MAIN_GROUP not in self._dependency_groups:
return []

return self._dependency_groups[MAIN_GROUP].dependencies

@property
def all_requires(
self,
) -> list[Dependency]:
def all_requires(self) -> list[Dependency]:
"""
Returns the main dependencies and group dependencies.
Returns the main dependencies and group dependencies
enriched with Poetry-specific information for locking.
"""
return [
dependency
for group in self._dependency_groups.values()
for dependency in group.dependencies
for dependency in group.dependencies_for_locking
]

def _set_version(self, version: str | Version) -> None:
Expand Down
2 changes: 1 addition & 1 deletion src/poetry/core/packages/vcs_dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ def __init__(
self._tag = tag
self._rev = rev
self._directory = directory
self._develop = develop

super().__init__(
name,
Expand All @@ -54,6 +53,7 @@ def __init__(
)

self._source = self.source_url or source
self._develop = develop

@property
def vcs(self) -> str:
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/sample_project/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ classifiers = [
python = ">=3.6"
cleo = "^0.6"
pendulum = { git = "https://github.com/sdispater/pendulum.git", branch = "2.0" }
tomlkit = { git = "https://github.com/sdispater/tomlkit.git", rev = "3bff550", develop = false }
tomlkit = { git = "https://github.com/sdispater/tomlkit.git", rev = "3bff550", develop = true }
requests = { version = "^2.18", optional = true, extras = [ "security" ] }
pathlib2 = { version = "^2.2", python = "~2.7" }

Expand Down
Loading

0 comments on commit 06f0e3b

Please sign in to comment.