Skip to content

Commit

Permalink
Merge pull request #183 from python-poetry/dependency-groups
Browse files Browse the repository at this point in the history
Add support for dependency groups
  • Loading branch information
sdispater committed Jul 23, 2021
2 parents 58138c5 + f3215b7 commit afaa690
Show file tree
Hide file tree
Showing 15 changed files with 349 additions and 70 deletions.
76 changes: 58 additions & 18 deletions poetry/core/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Factory(object):
"""

def create_poetry(
self, cwd: Optional[Path] = None, with_dev: bool = True
self, cwd: Optional[Path] = None, with_groups: bool = True
) -> "Poetry":
from .poetry import Poetry
from .pyproject.toml import PyProjectTOML
Expand All @@ -49,7 +49,7 @@ def create_poetry(
version = local_config["version"]
package = self.get_package(name, version)
package = self.configure_package(
package, local_config, poetry_file.parent, with_dev=with_dev
package, local_config, poetry_file.parent, with_groups=with_groups
)

return Poetry(poetry_file, local_config, package)
Expand All @@ -66,9 +66,10 @@ def configure_package(
package: "ProjectPackage",
config: Dict[str, Any],
root: Path,
with_dev: bool = True,
with_groups: bool = True,
) -> "ProjectPackage":
from .packages.dependency import Dependency
from .packages.dependency_group import DependencyGroup
from .spdx.helpers import license_by_id

package.root_dir = root
Expand Down Expand Up @@ -99,46 +100,82 @@ 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:
package.add_dependency(
group.add_dependency(
cls.create_dependency(
name, _constraint, root_dir=package.root_dir
)
)

continue

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

if with_dev and "dev-dependencies" in config:
package.add_dependency_group(group)

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:
package.add_dependency(
group.add_dependency(
cls.create_dependency(
name,
_constraint,
category="dev",
groups=["dev"],
root_dir=package.root_dir,
)
)

continue

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

package.add_dependency_group(group)

extras = config.get("extras", {})
for extra_name, requirements in extras.items():
package.extras[extra_name] = []
Expand Down Expand Up @@ -191,7 +228,7 @@ def create_dependency(
cls,
name: str,
constraint: Union[str, Dict[str, Any]],
category: str = "main",
groups: Optional[List[str]] = None,
root_dir: Optional[Path] = None,
) -> "DependencyTypes":
from .packages.constraints import parse_constraint as parse_generic_constraint
Expand All @@ -204,6 +241,9 @@ def create_dependency(
from .version.markers import AnyMarker
from .version.markers import parse_marker

if groups is None:
groups = ["default"]

if constraint is None:
constraint = "*"

Expand Down Expand Up @@ -234,7 +274,7 @@ def create_dependency(
branch=constraint.get("branch", None),
tag=constraint.get("tag", None),
rev=constraint.get("rev", None),
category=category,
groups=groups,
optional=optional,
develop=constraint.get("develop", False),
extras=constraint.get("extras", []),
Expand All @@ -245,7 +285,7 @@ def create_dependency(
dependency = FileDependency(
name,
file_path,
category=category,
groups=groups,
base=root_dir,
extras=constraint.get("extras", []),
)
Expand All @@ -261,7 +301,7 @@ def create_dependency(
dependency = FileDependency(
name,
path,
category=category,
groups=groups,
optional=optional,
base=root_dir,
extras=constraint.get("extras", []),
Expand All @@ -270,7 +310,7 @@ def create_dependency(
dependency = DirectoryDependency(
name,
path,
category=category,
groups=groups,
optional=optional,
base=root_dir,
develop=constraint.get("develop", False),
Expand All @@ -280,7 +320,7 @@ def create_dependency(
dependency = URLDependency(
name,
constraint["url"],
category=category,
groups=groups,
optional=optional,
extras=constraint.get("extras", []),
)
Expand All @@ -291,7 +331,7 @@ def create_dependency(
name,
version,
optional=optional,
category=category,
groups=groups,
allows_prereleases=allows_prereleases,
extras=constraint.get("extras", []),
)
Expand Down Expand Up @@ -324,7 +364,7 @@ def create_dependency(

dependency.source_name = constraint.get("source")
else:
dependency = Dependency(name, constraint, category=category)
dependency = Dependency(name, constraint, groups=groups)

return dependency

Expand Down
26 changes: 26 additions & 0 deletions poetry/core/json/schemas/poetry-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,32 @@
}
}
},
"group": {
"type": "object",
"description": "This represents groups of dependencies",
"patternProperties": {
"^[a-zA-Z-_.0-9]+$": {
"type": "object",
"description": "This represents a single dependency group",
"required": [
"dependencies"
],
"properties": {
"optional": {
"type": "boolean",
"description": "Whether the dependency group is optional or not"
},
"dependencies": {
"type": "object",
"description": "The dependencies of this dependency group",
"$ref": "#/definitions/dependencies",
"additionalProperties": false
}
},
"additionalProperties": false
}
}
},
"build": {
"$ref": "#/definitions/build-section"
},
Expand Down
6 changes: 3 additions & 3 deletions poetry/core/masonry/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def get_requires_for_build_wheel(
def prepare_metadata_for_build_wheel(
metadata_directory: str, config_settings: Optional[Dict[str, Any]] = None
) -> str:
poetry = Factory().create_poetry(Path(".").resolve(), with_dev=False)
poetry = Factory().create_poetry(Path(".").resolve(), with_groups=False)
builder = WheelBuilder(poetry)

dist_info = Path(metadata_directory, builder.dist_info)
Expand All @@ -64,7 +64,7 @@ def build_wheel(
metadata_directory: Optional[str] = None,
) -> str:
"""Builds a wheel, places it in wheel_directory"""
poetry = Factory().create_poetry(Path(".").resolve(), with_dev=False)
poetry = Factory().create_poetry(Path(".").resolve(), with_groups=False)

return WheelBuilder.make_in(poetry, Path(wheel_directory))

Expand All @@ -73,7 +73,7 @@ def build_sdist(
sdist_directory: str, config_settings: Optional[Dict[str, Any]] = None
) -> str:
"""Builds an sdist, places it in sdist_directory"""
poetry = Factory().create_poetry(Path(".").resolve(), with_dev=False)
poetry = Factory().create_poetry(Path(".").resolve(), with_groups=False)

path = SdistBuilder(poetry).build(Path(sdist_directory))

Expand Down
14 changes: 9 additions & 5 deletions poetry/core/packages/dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def __init__(
name: str,
constraint: Union[str, "VersionTypes"],
optional: bool = False,
category: str = "main",
groups: Optional[List[str]] = None,
allows_prereleases: bool = False,
extras: Union[List[str], FrozenSet[str]] = None,
source_type: Optional[str] = None,
Expand All @@ -58,7 +58,11 @@ def __init__(

self._pretty_constraint = str(constraint)
self._optional = optional
self._category = category

if not groups:
groups = ["default"]

self._groups = frozenset(groups)

if (
isinstance(self._constraint, VersionRangeConstraint)
Expand Down Expand Up @@ -113,8 +117,8 @@ def pretty_name(self) -> str:
return self._pretty_name

@property
def category(self) -> str:
return self._category
def groups(self) -> FrozenSet[str]:
return self._groups

@property
def python_versions(self) -> str:
Expand Down Expand Up @@ -387,7 +391,7 @@ def with_constraint(self, constraint: Union[str, "VersionTypes"]) -> "Dependency
self.pretty_name,
constraint,
optional=self.is_optional(),
category=self.category,
groups=list(self._groups),
allows_prereleases=self.allows_prereleases(),
extras=self._extras,
source_type=self._source_type,
Expand Down
54 changes: 54 additions & 0 deletions poetry/core/packages/dependency_group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from typing import TYPE_CHECKING
from typing import List


if TYPE_CHECKING:
from .types import DependencyTypes


class DependencyGroup:
def __init__(self, name: str, optional: bool = False) -> None:
self._name: str = name
self._optional: bool = optional
self._dependencies: List["DependencyTypes"] = []

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

@property
def dependencies(self) -> List["DependencyTypes"]:
return self._dependencies

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

def add_dependency(self, dependency: "DependencyTypes") -> None:
self._dependencies.append(dependency)

def remove_dependency(self, name: "str") -> None:
from poetry.core.utils.helpers import canonicalize_name

name = canonicalize_name(name)

dependencies = []
for dependency in dependencies:
if dependency.name == name:
continue

dependencies.append(dependency)

self._dependencies = dependencies

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

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

def __repr__(self) -> str:
return "{}({}, optional={})".format(
self.__class__.__name__, self._name, self._optional
)
6 changes: 3 additions & 3 deletions poetry/core/packages/directory_dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def __init__(
self,
name: str,
path: Path,
category: str = "main",
groups: Optional[List[str]] = None,
optional: bool = False,
base: Optional[Path] = None,
develop: bool = False,
Expand Down Expand Up @@ -61,7 +61,7 @@ def __init__(
super(DirectoryDependency, self).__init__(
name,
"*",
category=category,
groups=groups,
optional=optional,
allows_prereleases=True,
source_type="directory",
Expand Down Expand Up @@ -97,7 +97,7 @@ def with_constraint(self, constraint: "BaseConstraint") -> "DirectoryDependency"
path=self.path,
base=self.base,
optional=self.is_optional(),
category=self.category,
groups=list(self._groups),
develop=self._develop,
extras=self._extras,
)
Expand Down
Loading

0 comments on commit afaa690

Please sign in to comment.