Skip to content

Commit

Permalink
factory: merge legacy dependencies and groups (python-poetry#305)
Browse files Browse the repository at this point in the history
* factory: merge legacy dependencies and groups

This change ensures that existence of legacy "dev-dependency" section
does not break when "dev" group is added for new dependencies.

* package: remove redundant filtering

* fix typing for `ProjectPackage.python_versions`
  • Loading branch information
abn authored and bostonrwalker committed Aug 29, 2022
1 parent 34d1518 commit 405869d
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 76 deletions.
124 changes: 56 additions & 68 deletions src/poetry/core/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Any
from typing import Dict
from typing import List
from typing import Mapping
from typing import Optional
from typing import Set
from typing import Union
Expand All @@ -15,12 +16,19 @@


if TYPE_CHECKING:
from poetry.core.packages.dependency_group import DependencyGroup
from poetry.core.packages.project_package import ProjectPackage
from poetry.core.packages.types import DependencyTypes
from poetry.core.poetry import Poetry
from poetry.core.spdx.license import License
from poetry.core.version.markers import BaseMarker

DependencyConstraint = Union[str, Dict[str, Any]]
DependencyConfig = Mapping[
str, Union[List[DependencyConstraint], DependencyConstraint]
]


logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -63,6 +71,42 @@ def get_package(cls, name: str, version: str) -> "ProjectPackage":

return ProjectPackage(name, version, version)

@classmethod
def _add_package_group_dependencies(
cls,
package: "ProjectPackage",
group: Union[str, "DependencyGroup"],
dependencies: "DependencyConfig",
) -> None:
if isinstance(group, str):
if package.has_dependency_group(group):
group = package.dependency_group(group)
else:
from poetry.core.packages.dependency_group import DependencyGroup

group = DependencyGroup(group)

for name, constraints in dependencies.items():
_constraints = (
constraints if isinstance(constraints, list) else [constraints]
)
for _constraint in _constraints:
if name.lower() == "python":
if group.name == "default" and isinstance(_constraint, str):
package.python_versions = _constraint
continue

group.add_dependency(
cls.create_dependency(
name,
_constraint,
groups=[group.name],
root_dir=package.root_dir,
)
)

package.add_dependency_group(group)

@classmethod
def configure_package(
cls,
Expand Down Expand Up @@ -106,81 +150,25 @@ def configure_package(
package.platform = config["platform"]

if "dependencies" in config:
group = DependencyGroup("default")
for name, constraint in config["dependencies"].items():
if name.lower() == "python":
package.python_versions = constraint
continue

if isinstance(constraint, list):
for _constraint in constraint:
group.add_dependency(
cls.create_dependency(
name, _constraint, root_dir=package.root_dir
)
)

continue

group.add_dependency(
cls.create_dependency(name, constraint, root_dir=package.root_dir)
)

package.add_dependency_group(group)
cls._add_package_group_dependencies(
package=package, group="default", dependencies=config["dependencies"]
)

if with_groups and "group" in config:
for group_name, group_config in config["group"].items():
group = DependencyGroup(
group_name, optional=group_config.get("optional", False)
)
for name, constraint in group_config["dependencies"].items():
if isinstance(constraint, list):
for _constraint in constraint:
group.add_dependency(
cls.create_dependency(
name,
_constraint,
groups=[group_name],
root_dir=package.root_dir,
)
)

continue

group.add_dependency(
cls.create_dependency(
name,
constraint,
groups=[group_name],
root_dir=package.root_dir,
)
)

package.add_dependency_group(group)

if with_groups and "dev-dependencies" in config:
group = DependencyGroup("dev")
for name, constraint in config["dev-dependencies"].items():
if isinstance(constraint, list):
for _constraint in constraint:
group.add_dependency(
cls.create_dependency(
name,
_constraint,
groups=["dev"],
root_dir=package.root_dir,
)
)

continue

group.add_dependency(
cls.create_dependency(
name, constraint, groups=["dev"], root_dir=package.root_dir
)
cls._add_package_group_dependencies(
package=package,
group=group,
dependencies=group_config["dependencies"],
)

package.add_dependency_group(group)
if with_groups and "dev-dependencies" in config:
cls._add_package_group_dependencies(
package=package, group="dev", dependencies=config["dev-dependencies"]
)

extras = config.get("extras", {})
for extra_name, requirements in extras.items():
Expand Down Expand Up @@ -254,7 +242,7 @@ def configure_package(
def create_dependency(
cls,
name: str,
constraint: Union[str, Dict[str, Any]],
constraint: "DependencyConstraint",
groups: Optional[List[str]] = None,
root_dir: Optional[Path] = None,
) -> "DependencyTypes":
Expand Down
8 changes: 5 additions & 3 deletions src/poetry/core/packages/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,10 @@ def all_requires(
"""
Returns the default dependencies and group dependencies.
"""
return self.requires + [
return [
dependency
for group in self._dependency_groups.values()
for dependency in group.dependencies
if group.name != "default"
]

def _get_author(self) -> Dict[str, Optional[str]]:
Expand Down Expand Up @@ -373,8 +372,11 @@ def is_root(self) -> bool:
def add_dependency_group(self, group: "DependencyGroup") -> None:
self._dependency_groups[group.name] = group

def has_dependency_group(self, name: str) -> bool:
return name in self._dependency_groups

def dependency_group(self, name: str) -> "DependencyGroup":
if name not in self._dependency_groups:
if not self.has_dependency_group(name):
raise ValueError(f'The dependency group "{name}" does not exist.')

return self._dependency_groups[name]
Expand Down
8 changes: 3 additions & 5 deletions src/poetry/core/packages/project_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,14 @@ def to_dependency(self) -> Union["DependencyTypes"]:
return dependency

@property
def python_versions(self) -> Union[str, "VersionConstraint"]:
def python_versions(self) -> str:
return self._python_versions

@python_versions.setter
def python_versions(self, value: Union[str, "VersionConstraint"]) -> None:
from poetry.core.semver.version_range import VersionRange

def python_versions(self, value: str) -> None:
self._python_versions = value

if value == "*" or value == VersionRange():
if value == "*":
value = "~2.7 || >=3.4"

self._python_constraint = parse_constraint(value)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
My Package
==========
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[tool.poetry]
name = "simple-project"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]

[tool.poetry.dependencies]
python = "^3.7"

[tool.poetry.group.default.dependencies]
aiohttp = "^2.17.0"

[tools.poetry]

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
Empty file.
2 changes: 2 additions & 0 deletions tests/fixtures/project_with_groups_and_legacy_dev/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
My Package
==========
20 changes: 20 additions & 0 deletions tests/fixtures/project_with_groups_and_legacy_dev/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[tool.poetry]
name = "simple-project"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]

[tool.poetry.dependencies]
python = "^3.7"

[tool.poetry.group.dev.dependencies]
pre-commit = "^2.17.0"

[tool.poetry.dev-dependencies]
pytest = "^5.2"

[tools.poetry]

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
Empty file.
26 changes: 26 additions & 0 deletions tests/test_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,29 @@ def test_create_poetry_fails_with_invalid_dev_dependencies_iff_with_dev_is_true(
Factory().create_poetry(
fixtures_dir / "project_with_invalid_dev_deps", with_groups=False
)


def test_create_poetry_with_groups_and_legacy_dev():
poetry = Factory().create_poetry(
fixtures_dir / "project_with_groups_and_legacy_dev"
)

package = poetry.package
dependencies = package.all_requires

assert len(dependencies) == 2
assert {dependency.name for dependency in dependencies} == {"pytest", "pre-commit"}


def test_create_poetry_with_groups_and_explicit_default():
poetry = Factory().create_poetry(
fixtures_dir / "project_with_groups_and_explicit_default"
)

package = poetry.package
dependencies = package.requires

assert len(dependencies) == 1
assert {dependency.name for dependency in dependencies} == {
"aiohttp",
}

0 comments on commit 405869d

Please sign in to comment.