Skip to content

Commit

Permalink
Refactor dependencies handling to support groups
Browse files Browse the repository at this point in the history
  • Loading branch information
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.