Skip to content

Commit

Permalink
Refactor dependencies handling to support groups
Browse files Browse the repository at this point in the history
sdispater committed Jul 23, 2021

Verified

This commit was signed with the committer’s verified signature.
sdispater Sébastien Eustace
1 parent 5278823 commit 26f13f7
Showing 21 changed files with 178 additions and 154 deletions.
16 changes: 10 additions & 6 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion poetry/console/commands/debug/resolve.py
Original file line number Diff line number Diff line change
@@ -99,7 +99,7 @@ def handle(self) -> Optional[int]:
packages = [op.package for op in ops]
repo = Repository(packages)

requires = package.requires + package.dev_requires
requires = package.all_requires
for pkg in repo.packages:
for require in requires:
if pkg.name == require.name:
22 changes: 14 additions & 8 deletions poetry/console/commands/show.py
Original file line number Diff line number Diff line change
@@ -68,14 +68,17 @@ def handle(self) -> Optional[int]:

# Show tree view if requested
if self.option("tree") and not package:
requires = self.poetry.package.requires
groups = ["default"]
if include_dev:
requires += self.poetry.package.dev_requires
groups.append("dev")

package = self.poetry.package.with_dependency_groups(groups, only=True)
requires = package.all_requires
packages = locked_repo.packages
for package in packages:
for pkg in packages:
for require in requires:
if package.name == require.name:
self.display_package_tree(self._io, package, locked_repo)
if pkg.name == require.name:
self.display_package_tree(self._io, pkg, locked_repo)
break

return 0
@@ -386,13 +389,16 @@ def find_latest_package(

# find the latest version allowed in this pool
if package.source_type in ("git", "file", "directory"):
requires = self.poetry.package.requires
groups = ["default"]
if include_dev:
requires = requires + self.poetry.package.dev_requires
groups.append("dev")

root = self.poetry.package.with_dependency_groups(groups, only=True)
requires = root.all_requires

for dep in requires:
if dep.name == package.name:
provider = Provider(self.poetry.package, self.poetry.pool, NullIO())
provider = Provider(root, self.poetry.pool, NullIO())

if dep.is_vcs():
return provider.search_for_vcs(dep)[0]
4 changes: 3 additions & 1 deletion poetry/console/commands/update.py
Original file line number Diff line number Diff line change
@@ -37,7 +37,9 @@ def handle(self) -> int:
if packages:
self._installer.whitelist({name: "*" for name in packages})

self._installer.dev_mode(not self.option("no-dev"))
if self.option("no-dev"):
self._installer.with_groups(["dev"])

self._installer.dry_run(self.option("dry-run"))
self._installer.execute_operations(not self.option("lock"))

6 changes: 4 additions & 2 deletions poetry/inspection/info.py
Original file line number Diff line number Diff line change
@@ -157,7 +157,9 @@ def to_package(
poetry_package = self._get_poetry_package(path=root_dir or self._source_url)
if poetry_package:
package.extras = poetry_package.extras
package.requires = poetry_package.requires
for dependency in poetry_package.requires:
package.add_dependency(dependency)

return package

seen_requirements = set()
@@ -191,7 +193,7 @@ def to_package(
req = dependency.to_pep_508(with_extras=True)

if req not in seen_requirements:
package.requires.append(dependency)
package.add_dependency(dependency)
seen_requirements.add(req)

return package
53 changes: 27 additions & 26 deletions poetry/installation/installer.py
Original file line number Diff line number Diff line change
@@ -54,8 +54,10 @@ def __init__(
self._update = False
self._verbose = False
self._write_lock = True
self._dev_mode = True
self._dev_only = False
self._without_groups = None
self._with_groups = None
self._only_groups = None

self._execute_operations = True
self._lock = False

@@ -137,21 +139,20 @@ def verbose(self, verbose: bool = True) -> "Installer":
def is_verbose(self) -> bool:
return self._verbose

def dev_mode(self, dev_mode: bool = True) -> "Installer":
self._dev_mode = dev_mode
def without_groups(self, groups: List[str]) -> "Installer":
self._without_groups = groups

return self

def is_dev_mode(self) -> bool:
return self._dev_mode

def dev_only(self, dev_only: bool = False) -> "Installer":
self._dev_only = dev_only
def with_groups(self, groups: List[str]) -> "Installer":
self._with_groups = groups

return self

def is_dev_only(self) -> bool:
return self._dev_only
def only_groups(self, groups: List[str]) -> "Installer":
self._only_groups = groups

return self

def update(self, update: bool = True) -> "Installer":
self._update = update
@@ -283,13 +284,20 @@ def _do_install(self, local_repo: Repository) -> int:
# If we are only in lock mode, no need to go any further
return 0

root = self._package
if not self.is_dev_mode():
root = root.clone()
del root.dev_requires[:]
elif self.is_dev_only():
root = root.clone()
del root.requires[:]
if self._without_groups or self._with_groups or self._only_groups:
if self._with_groups:
# Default dependencies and opt-in optional dependencies
root = self._package.without_dependency_groups(self._with_groups)
elif self._without_groups:
# Default dependencies without elected groups
root = self._package.without_dependency_groups(self._without_groups)
else:
# Only selected groups
root = self._package.with_dependency_groups(
self._only_groups, only=True
)
else:
root = self._package.without_optional_dependency_groups()

if self._io.is_verbose():
self._io.write_line("")
@@ -502,9 +510,7 @@ def _get_operations_from_lock(
for installed in installed_repo.packages:
if locked.name == installed.name:
is_installed = True
if locked.category == "dev" and not self.is_dev_mode():
ops.append(Uninstall(locked))
elif locked.optional and locked.name not in extra_packages:
if locked.optional and locked.name not in extra_packages:
# Installed but optional and not requested in extras
ops.append(Uninstall(locked))
elif locked.version != installed.version:
@@ -553,11 +559,6 @@ def _filter_operations(self, ops: List[Operation], repo: Repository) -> None:
if package.name not in extra_packages:
op.skip("Not required")

# If the package is a dev package and dev packages
# are not requested, we skip it
if package.category == "dev" and not self.is_dev_mode():
op.skip("Dev dependencies not requested")

def _get_extra_packages(self, repo: Repository) -> List[str]:
"""
Returns all package names required by extras.
5 changes: 3 additions & 2 deletions poetry/packages/locker.py
Original file line number Diff line number Diff line change
@@ -47,7 +47,7 @@ class Locker:

_VERSION = "1.1"

_relevant_keys = ["dependencies", "dev-dependencies", "source", "extras"]
_relevant_keys = ["dependencies", "group", "source", "extras"]

def __init__(self, lock: Union[str, Path], local_config: dict) -> None:
self._lock = TOMLFile(lock)
@@ -128,7 +128,8 @@ def locked_repository(
source_resolved_reference=source.get("resolved_reference"),
)
package.description = info.get("description", "")
package.category = info["category"]
package.category = info.get("category", "main")
package.groups = info.get("groups", ["default"])
package.optional = info["optional"]
if "hashes" in lock_data["metadata"]:
# Old lock so we create dummy files from the hashes
2 changes: 2 additions & 0 deletions poetry/packages/project_package.py
Original file line number Diff line number Diff line change
@@ -21,3 +21,5 @@ def set_version(
else:
self._version = version
self._pretty_version = pretty_version or version.text

return self
16 changes: 11 additions & 5 deletions poetry/puzzle/provider.py
Original file line number Diff line number Diff line change
@@ -330,7 +330,8 @@ def search_for_url(self, dependency: URLDependency) -> List[Package]:
for dep in package.extras[extra]:
dep.activate()

package.requires += package.extras[extra]
for extra_dep in package.extras[extra]:
package.add_dependency(extra_dep)

dependency._constraint = package.version
dependency._pretty_constraint = package.version.text
@@ -535,10 +536,10 @@ def complete_package(self, package: DependencyPackage) -> DependencyPackage:
# - pypiwin32 (219); sys_platform == "win32" and python_version < "3.6"
duplicates = dict()
for dep in dependencies:
if dep.name not in duplicates:
duplicates[dep.name] = []
if dep.complete_name not in duplicates:
duplicates[dep.complete_name] = []

duplicates[dep.name].append(dep)
duplicates[dep.complete_name].append(dep)

dependencies = []
for dep_name, deps in duplicates.items():
@@ -699,7 +700,12 @@ def complete_package(self, package: DependencyPackage) -> DependencyPackage:

clean_dependencies.append(dep)

package.requires = clean_dependencies
package = DependencyPackage(
package.dependency, package.with_dependency_groups([], only=True)
)

for dep in clean_dependencies:
package.add_dependency(dep)

return package

25 changes: 18 additions & 7 deletions poetry/puzzle/solver.py
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
from typing import TYPE_CHECKING
from typing import Callable
from typing import Dict
from typing import FrozenSet
from typing import List
from typing import Optional
from typing import Tuple
@@ -254,7 +255,7 @@ def solve_in_compatibility_mode(

for dep in package.requires:
if dep not in pkg.requires:
pkg.requires.append(dep)
pkg.add_dependency(dep)

return packages, depths

@@ -300,7 +301,7 @@ def _solve(self, use_latest: List[str] = None) -> Tuple[List[Package], List[int]
continue

if dep not in _package.requires:
_package.requires.append(dep)
_package.add_dependency(dep)

continue

@@ -312,7 +313,9 @@ def _solve(self, use_latest: List[str] = None) -> Tuple[List[Package], List[int]


class DFSNode:
def __init__(self, id: Tuple[str, str, bool], name: str, base_name: str) -> None:
def __init__(
self, id: Tuple[str, FrozenSet[str], bool], name: str, base_name: str
) -> None:
self.id = id
self.name = name
self.base_name = base_name
@@ -423,13 +426,15 @@ def __init__(

if not previous:
self.category = "dev"
self.groups = frozenset()
self.optional = True
else:
self.category = dep.category
self.category = "main" if "default" in dep.groups else "dev"
self.groups = dep.groups
self.optional = dep.is_optional()

super().__init__(
(package.complete_name, self.category, self.optional),
(package.complete_name, self.groups, self.optional),
package.complete_name,
package.name,
)
@@ -470,7 +475,7 @@ def reachable(self) -> List["PackageNode"]:
# we merge the requirements
if any(
child.package.name == pkg.name
and child.category == dependency.category
and child.groups == dependency.groups
for child in children
):
continue
@@ -505,14 +510,20 @@ def aggregate_package_nodes(
) -> Tuple[Package, int]:
package = nodes[0].package
depth = max(node.depth for node in nodes)
groups = []
for node in nodes:
groups.extend(node.groups)

category = (
"main" if any(node.category == "main" for node in children + nodes) else "dev"
"main" if any("default" in node.groups for node in children + nodes) else "dev"
)
optional = all(node.optional for node in children + nodes)
for node in nodes:
node.depth = depth
node.category = category
node.optional = optional

package.category = category
package.optional = optional

return package, depth
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ generate-setup-file = false
[tool.poetry.dependencies]
python = "^3.6"

poetry-core = "~1.1.0a5"
poetry-core = { git = "https://github.com/python-poetry/poetry-core.git", branch = "master" }
cleo = "^1.0.0a1"
crashtest = "^0.3.0"
requests = "^2.18"
Loading

0 comments on commit 26f13f7

Please sign in to comment.