From 55ebb9cde0205ccf057f9923f79479b2df92ac83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 4 Sep 2020 10:22:42 +0200 Subject: [PATCH] Fix and improve handling of extras while resolving dependencies --- poetry.lock | 19 +- poetry/console/commands/debug/resolve.py | 9 +- poetry/console/commands/init.py | 2 +- poetry/console/commands/show.py | 37 +- poetry/factory.py | 0 poetry/inspection/info.py | 56 +- poetry/installation/executor.py | 4 +- poetry/installation/pip_installer.py | 4 +- poetry/mixology/incompatibility.py | 12 +- poetry/mixology/partial_solution.py | 24 +- poetry/mixology/term.py | 17 +- poetry/mixology/version_solver.py | 42 +- poetry/packages/dependency_package.py | 18 +- poetry/packages/locker.py | 116 ++- poetry/packages/package_collection.py | 6 +- poetry/puzzle/provider.py | 120 ++- poetry/puzzle/solver.py | 118 +-- poetry/repositories/base_repository.py | 4 +- poetry/repositories/installed_repository.py | 14 +- poetry/repositories/legacy_repository.py | 34 +- poetry/repositories/pool.py | 18 +- poetry/repositories/pypi_repository.py | 26 +- poetry/repositories/repository.py | 39 +- poetry/utils/exporter.py | 25 +- poetry/utils/helpers.py | 13 + poetry/version/version_selector.py | 22 +- pyproject.toml | 2 +- tests/console/commands/debug/test_resolve.py | 5 +- tests/console/commands/test_add.py | 74 +- tests/console/commands/test_show.py | 5 +- tests/console/conftest.py | 11 +- ...irectory-dependency-poetry-transitive.test | 17 +- .../with-directory-dependency-poetry.test | 3 +- .../with-directory-dependency-setuptools.test | 3 +- .../with-file-dependency-transitive.test | 7 +- .../fixtures/with-file-dependency.test | 1 - .../fixtures/with-url-dependency.test | 1 - ...ith-wheel-dependency-no-requires-dist.test | 1 - tests/installation/test_chooser.py | 54 +- tests/installation/test_executor.py | 33 +- tests/installation/test_installer.py | 289 ++++--- tests/installation/test_installer_old.py | 283 +++--- tests/installation/test_pip_installer.py | 77 +- tests/mixology/helpers.py | 3 +- .../version_solver/test_backtracking.py | 28 +- .../version_solver/test_basic_graph.py | 16 +- .../version_solver/test_python_constraint.py | 4 +- .../version_solver/test_unsolvable.py | 20 +- .../mixology/version_solver/test_with_lock.py | 16 +- tests/packages/test_locker.py | 128 +-- tests/puzzle/test_provider.py | 170 +++- tests/puzzle/test_solver.py | 803 ++++++++++++------ .../repositories/test_installed_repository.py | 4 +- tests/repositories/test_legacy_repository.py | 27 +- tests/repositories/test_pypi_repository.py | 24 +- tests/test_factory.py | 2 +- tests/utils/test_env.py | 1 - tests/utils/test_exporter.py | 2 + tests/utils/test_extras.py | 7 +- 59 files changed, 1835 insertions(+), 1085 deletions(-) mode change 100644 => 100755 poetry/factory.py mode change 100644 => 100755 poetry/mixology/partial_solution.py mode change 100644 => 100755 poetry/mixology/term.py mode change 100644 => 100755 poetry/mixology/version_solver.py mode change 100644 => 100755 poetry/puzzle/provider.py mode change 100644 => 100755 poetry/repositories/legacy_repository.py mode change 100644 => 100755 poetry/repositories/pool.py mode change 100644 => 100755 poetry/repositories/pypi_repository.py mode change 100644 => 100755 poetry/repositories/repository.py diff --git a/poetry.lock b/poetry.lock index 2cd8f04059f..45351aecdab 100644 --- a/poetry.lock +++ b/poetry.lock @@ -461,7 +461,7 @@ six = ">=1.0.0,<2.0.0" [[package]] name = "more-itertools" -version = "8.3.0" +version = "8.4.0" description = "More routines for operating on iterables, beyond itertools" category = "dev" optional = false @@ -469,7 +469,7 @@ python-versions = ">=3.5" [[package]] name = "more-itertools" -version = "8.4.0" +version = "8.5.0" description = "More routines for operating on iterables, beyond itertools" category = "dev" optional = false @@ -561,8 +561,8 @@ importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [[package]] name = "poetry-core" -version = "1.0.0a9" -description = "Core utilities for Poetry" +version = "1.0.0b1" +description = "Poetry PEP 517 Build Backend" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" @@ -984,7 +984,7 @@ contextlib2 = {version = "*", markers = "python_version < \"3.4\""} [metadata] lock-version = "1.1" python-versions = "~2.7 || ^3.5" -content-hash = "9528141e0eb24000c6f245065820732b3977d1c7d14c14fbcedf77e91c4a3e68" +content-hash = "062aef66c1c37dce14fcb2a66841afe97937ce9150136ff8dfa45e43d796821f" [metadata.files] appdirs = [ @@ -1217,10 +1217,10 @@ more-itertools = [ {file = "more-itertools-5.0.0.tar.gz", hash = "sha256:38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4"}, {file = "more_itertools-5.0.0-py2-none-any.whl", hash = "sha256:c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc"}, {file = "more_itertools-5.0.0-py3-none-any.whl", hash = "sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9"}, - {file = "more-itertools-8.3.0.tar.gz", hash = "sha256:558bb897a2232f5e4f8e2399089e35aecb746e1f9191b6584a151647e89267be"}, - {file = "more_itertools-8.3.0-py3-none-any.whl", hash = "sha256:7818f596b1e87be009031c7653d01acc46ed422e6656b394b0f765ce66ed4982"}, {file = "more-itertools-8.4.0.tar.gz", hash = "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5"}, {file = "more_itertools-8.4.0-py3-none-any.whl", hash = "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"}, + {file = "more-itertools-8.5.0.tar.gz", hash = "sha256:6f83822ae94818eae2612063a5101a7311e68ae8002005b5e05f03fd74a86a20"}, + {file = "more_itertools-8.5.0-py3-none-any.whl", hash = "sha256:9b30f12df9393f0d28af9210ff8efe48d10c94f73e5daf886f10c4b0b0b4f03c"}, ] msgpack = [ {file = "msgpack-1.0.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:cec8bf10981ed70998d98431cd814db0ecf3384e6b113366e7f36af71a0fca08"}, @@ -1270,8 +1270,8 @@ pluggy = [ {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] poetry-core = [ - {file = "poetry-core-1.0.0a9.tar.gz", hash = "sha256:f08e9829fd06609ca5615faa91739b589eb71e025a6aaf7ddffb698676eb7c8c"}, - {file = "poetry_core-1.0.0a9-py2.py3-none-any.whl", hash = "sha256:79a63629ae44533ba9aa828e0eff0002c61b3af5fc9bec212e006cc643f4eb19"}, + {file = "poetry-core-1.0.0b1.tar.gz", hash = "sha256:c7a64770780f6a4998eee1e260fc3b7e22fa1c9b94b05c0faa13aa512f95eaa1"}, + {file = "poetry_core-1.0.0b1-py2.py3-none-any.whl", hash = "sha256:92d2a33c27c733e746425c6506fdf583909e3ce5de4591deb23a4efb13f1a72c"}, ] pre-commit = [ {file = "pre_commit-2.6.0-py2.py3-none-any.whl", hash = "sha256:e8b1315c585052e729ab7e99dcca5698266bedce9067d21dc909c23e3ceed626"}, @@ -1371,6 +1371,7 @@ six = [ ] subprocess32 = [ {file = "subprocess32-3.5.4-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:88e37c1aac5388df41cc8a8456bb49ebffd321a3ad4d70358e3518176de3a56b"}, + {file = "subprocess32-3.5.4-cp27-cp27mu-manylinux2014_x86_64.whl", hash = "sha256:e45d985aef903c5b7444d34350b05da91a9e0ea015415ab45a21212786c649d0"}, {file = "subprocess32-3.5.4.tar.gz", hash = "sha256:eb2937c80497978d181efa1b839ec2d9622cf9600a039a79d0e108d1f9aec79d"}, ] termcolor = [ diff --git a/poetry/console/commands/debug/resolve.py b/poetry/console/commands/debug/resolve.py index 3f19de2b4b0..52ae1951b21 100644 --- a/poetry/console/commands/debug/resolve.py +++ b/poetry/console/commands/debug/resolve.py @@ -29,6 +29,7 @@ class DebugResolveCommand(InitCommand): def handle(self): from poetry.core.packages.project_package import ProjectPackage + from poetry.factory import Factory from poetry.io.null_io import NullIO from poetry.puzzle import Solver from poetry.repositories.pool import Pool @@ -59,7 +60,6 @@ def handle(self): for constraint in requirements: name = constraint.pop("name") - dep = package.add_dependency(name, constraint) extras = [] for extra in self.option("extras"): if " " in extra: @@ -67,8 +67,9 @@ def handle(self): else: extras.append(extra) - for ex in extras: - dep.extras.append(ex) + constraint["extras"] = extras + + package.add_dependency(Factory.create_dependency(name, constraint)) package.python_versions = self.option("python") or ( self.poetry.package.python_versions @@ -122,7 +123,7 @@ def handle(self): pkg = op.package row = [ - "{}".format(pkg.name), + "{}".format(pkg.complete_name), "{}".format(pkg.version), "", ] diff --git a/poetry/console/commands/init.py b/poetry/console/commands/init.py index c843a15212f..ab23da95c4e 100644 --- a/poetry/console/commands/init.py +++ b/poetry/console/commands/init.py @@ -388,7 +388,7 @@ def _parse_requirements( pair["extras"] = extras package = Provider.get_package_from_vcs( - "git", url.url, reference=pair.get("rev") + "git", url.url, rev=pair.get("rev") ) pair["name"] = package.name result.append(pair) diff --git a/poetry/console/commands/show.py b/poetry/console/commands/show.py index 8f596e52962..a91f8da6404 100644 --- a/poetry/console/commands/show.py +++ b/poetry/console/commands/show.py @@ -37,6 +37,7 @@ def handle(self): from poetry.core.semver import Version from poetry.repositories.installed_repository import InstalledRepository + from poetry.utils.helpers import get_package_version_display_string package = self.argument("package") @@ -144,12 +145,31 @@ def handle(self): if not self.option("outdated") or update_status != "up-to-date": name_length = max(name_length, current_length) version_length = max( - version_length, len(locked.full_pretty_version) + version_length, + len( + get_package_version_display_string( + locked, root=self.poetry.file.parent + ) + ), + ) + latest_length = max( + latest_length, + len( + get_package_version_display_string( + latest, root=self.poetry.file.parent + ) + ), ) - latest_length = max(latest_length, len(latest.full_pretty_version)) else: name_length = max(name_length, current_length) - version_length = max(version_length, len(locked.full_pretty_version)) + version_length = max( + version_length, + len( + get_package_version_display_string( + locked, root=self.poetry.file.parent + ) + ), + ) write_version = name_length + version_length + 3 <= width write_latest = name_length + version_length + latest_length + 3 <= width @@ -185,7 +205,10 @@ def handle(self): ) if write_version: line += " {:{}}".format( - locked.full_pretty_version, version_length + get_package_version_display_string( + locked, root=self.poetry.file.parent + ), + version_length, ) if show_latest: latest = latest_packages[locked.pretty_name] @@ -199,7 +222,11 @@ def handle(self): color = "yellow" line += " {:{}}".format( - color, latest.full_pretty_version, latest_length + color, + get_package_version_display_string( + latest, root=self.poetry.file.parent + ), + latest_length, ) if write_description: diff --git a/poetry/factory.py b/poetry/factory.py old mode 100644 new mode 100755 diff --git a/poetry/inspection/info.py b/poetry/inspection/info.py index 2d3e0820f4f..1f0bfd7e966 100644 --- a/poetry/inspection/info.py +++ b/poetry/inspection/info.py @@ -69,6 +69,9 @@ def __init__( self.requires_python = requires_python self.files = files or [] self._cache_version = cache_version + self._source_type = None + self._source_url = None + self._source_reference = None @property def cache_version(self): # type: () -> Optional[str] @@ -136,7 +139,13 @@ def to_package( "Unable to retrieve the package version for {}".format(name) ) - package = Package(name=name, version=self.version) + package = Package( + name=name, + version=self.version, + source_type=self._source_type, + source_url=self._source_url, + source_reference=self._source_reference, + ) package.description = self.summary package.root_dir = root_dir package.python_versions = self.requires_python or "*" @@ -166,14 +175,9 @@ def to_package( # this is the first time we encounter this extra for this package package.extras[extra] = [] - # Activate extra dependencies if specified - if extras and extra in extras: - dependency.activate() - package.extras[extra].append(dependency) - if not dependency.is_optional() or dependency.is_activated(): - # we skip add only if the dependency is option and was not activated as part of an extra + if dependency not in package.requires: package.requires.append(dependency) return package @@ -197,7 +201,7 @@ def _from_distribution( with requires.open(encoding="utf-8") as f: requirements = parse_requires(f.read()) - return cls( + info = cls( name=dist.name, version=dist.version, summary=dist.summary, @@ -206,6 +210,11 @@ def _from_distribution( requires_python=dist.requires_python, ) + info._source_type = "file" + info._source_url = Path(dist.filename).resolve().as_posix() + + return info + @classmethod def _from_sdist_file(cls, path): # type: (Path) -> PackageInfo """ @@ -501,23 +510,26 @@ def from_directory( """ project_package = cls._get_poetry_package(path) if project_package: - return cls.from_package(project_package) + info = cls.from_package(project_package) + else: + info = cls.from_metadata(path) - info = cls.from_metadata(path) + if not info or info.requires_dist is None: + try: + if disable_build: + info = cls.from_setup_files(path) + else: + info = cls._pep517_metadata(path) + except PackageInfoError: + if not info: + raise - if info and info.requires_dist is not None: - # return only if requirements are discovered - return info + # we discovered PkgInfo but no requirements were listed - try: - if disable_build: - return cls.from_setup_files(path) - return cls._pep517_metadata(path) - except PackageInfoError as e: - if info: - # we discovered PkgInfo but no requirements were listed - return info - raise e + info._source_type = "directory" + info._source_url = path.as_posix() + + return info @classmethod def from_sdist(cls, path): # type: (Path) -> PackageInfo diff --git a/poetry/installation/executor.py b/poetry/installation/executor.py index 0283dcf79af..1e63394985f 100644 --- a/poetry/installation/executor.py +++ b/poetry/installation/executor.py @@ -556,7 +556,7 @@ def _install_git(self, operation): git.checkout(package.source_reference, src_dir) # Now we just need to install from the source directory - package.source_url = str(src_dir) + package._source_url = str(src_dir) return self._install_directory(operation) @@ -599,7 +599,7 @@ def _download_link(self, operation, link): def _download_archive(self, operation, link): # type: (Operation, Link) -> Path response = self._authenticator.request( - "get", link.url, stream=True, io=self._sections.get(id(operation)) + "get", link.url, stream=True, io=self._sections.get(id(operation), self._io) ) wheel_size = response.headers.get("content-length") operation_message = self.get_operation_message(operation) diff --git a/poetry/installation/pip_installer.py b/poetry/installation/pip_installer.py index d4702565c2e..bc2a4042769 100644 --- a/poetry/installation/pip_installer.py +++ b/poetry/installation/pip_installer.py @@ -261,8 +261,8 @@ def install_git(self, package): # Now we just need to install from the source directory pkg = Package(package.name, package.version) - pkg.source_type = "directory" - pkg.source_url = str(src_dir) + pkg._source_type = "directory" + pkg._source_url = str(src_dir) pkg.develop = package.develop self.install_directory(pkg) diff --git a/poetry/mixology/incompatibility.py b/poetry/mixology/incompatibility.py index e731b743cb4..3cbc867874a 100644 --- a/poetry/mixology/incompatibility.py +++ b/poetry/mixology/incompatibility.py @@ -36,18 +36,18 @@ def __init__( # Short-circuit in the common case of a two-term incompatibility with # two different packages (for example, a dependency). or len(terms) == 2 - and terms[0].dependency.name != terms[-1].dependency.name + and terms[0].dependency.complete_name != terms[-1].dependency.complete_name ): pass else: # Coalesce multiple terms about the same package if possible. by_name = {} # type: Dict[str, Dict[str, Term]] for term in terms: - if term.dependency.name not in by_name: - by_name[term.dependency.name] = {} + if term.dependency.complete_name not in by_name: + by_name[term.dependency.complete_name] = {} - by_ref = by_name[term.dependency.name] - ref = term.dependency.name + by_ref = by_name[term.dependency.complete_name] + ref = term.dependency.complete_name if ref in by_ref: by_ref[ref] = by_ref[ref].intersect(term) @@ -432,7 +432,7 @@ def _try_requires_forbidden( def _terse(self, term, allow_every=False): if allow_every and term.constraint.is_any(): - return "every version of {}".format(term.dependency.name) + return "every version of {}".format(term.dependency.complete_name) return str(term.dependency) diff --git a/poetry/mixology/partial_solution.py b/poetry/mixology/partial_solution.py old mode 100644 new mode 100755 index 34c3231e2bc..df17f7184b2 --- a/poetry/mixology/partial_solution.py +++ b/poetry/mixology/partial_solution.py @@ -65,7 +65,7 @@ def unsatisfied(self): # type: () -> List[Dependency] return [ term.dependency for term in self._positive.values() - if term.dependency.name not in self._decisions + if term.dependency.complete_name not in self._decisions ] def decide(self, package): # type: (Package) -> None @@ -81,7 +81,7 @@ def decide(self, package): # type: (Package) -> None self._attempted_solutions += 1 self._backtracking = False - self._decisions[package.name] = package + self._decisions[package.complete_name] = package self._assign( Assignment.decision(package, self.decision_level, len(self._assignments)) @@ -120,9 +120,9 @@ def backtrack(self, decision_level): # type: (int) -> None packages = set() while self._assignments[-1].decision_level > decision_level: removed = self._assignments.pop(-1) - packages.add(removed.dependency.name) + packages.add(removed.dependency.complete_name) if removed.is_decision(): - del self._decisions[removed.dependency.name] + del self._decisions[removed.dependency.complete_name] # Re-compute _positive and _negative for the packages that were removed. for package in packages: @@ -133,21 +133,21 @@ def backtrack(self, decision_level): # type: (int) -> None del self._negative[package] for assignment in self._assignments: - if assignment.dependency.name in packages: + if assignment.dependency.complete_name in packages: self._register(assignment) def _register(self, assignment): # type: (Assignment) -> None """ Registers an Assignment in _positive or _negative. """ - name = assignment.dependency.name + name = assignment.dependency.complete_name old_positive = self._positive.get(name) if old_positive is not None: self._positive[name] = old_positive.intersect(assignment) return - ref = assignment.dependency.name + ref = assignment.dependency.complete_name negative_by_ref = self._negative.get(name) old_negative = None if negative_by_ref is None else negative_by_ref.get(ref) if old_negative is None: @@ -174,12 +174,12 @@ def satisfier(self, term): # type: (Term) -> Assignment assigned_term = None # type: Term for assignment in self._assignments: - if assignment.dependency.name != term.dependency.name: + if assignment.dependency.complete_name != term.dependency.complete_name: continue if ( not assignment.dependency.is_root - and not assignment.dependency.name == term.dependency.name + and not assignment.dependency.is_same_package_as(term.dependency) ): if not assignment.is_positive(): continue @@ -203,15 +203,15 @@ def satisfies(self, term): # type: (Term) -> bool return self.relation(term) == SetRelation.SUBSET def relation(self, term): # type: (Term) -> int - positive = self._positive.get(term.dependency.name) + positive = self._positive.get(term.dependency.complete_name) if positive is not None: return positive.relation(term) - by_ref = self._negative.get(term.dependency.name) + by_ref = self._negative.get(term.dependency.complete_name) if by_ref is None: return SetRelation.OVERLAPPING - negative = by_ref[term.dependency.name] + negative = by_ref[term.dependency.complete_name] if negative is None: return SetRelation.OVERLAPPING diff --git a/poetry/mixology/term.py b/poetry/mixology/term.py old mode 100644 new mode 100755 index 346c9431abb..0889e984a3b --- a/poetry/mixology/term.py +++ b/poetry/mixology/term.py @@ -38,7 +38,7 @@ def satisfies(self, other): # type: (Term) -> bool Returns whether this term satisfies another. """ return ( - self.dependency.name == other.dependency.name + self.dependency.complete_name == other.dependency.complete_name and self.relation(other) == SetRelation.SUBSET ) @@ -47,9 +47,9 @@ def relation(self, other): # type: (Term) -> int Returns the relationship between the package versions allowed by this term and another. """ - if self.dependency.name != other.dependency.name: + if self.dependency.complete_name != other.dependency.complete_name: raise ValueError( - "{} should refer to {}".format(other, self.dependency.name) + "{} should refer to {}".format(other, self.dependency.complete_name) ) other_constraint = other.constraint @@ -111,9 +111,9 @@ def intersect(self, other): # type: (Term) -> Union[Term, None] Returns a Term that represents the packages allowed by both this term and another """ - if self.dependency.name != other.dependency.name: + if self.dependency.complete_name != other.dependency.complete_name: raise ValueError( - "{} should refer to {}".format(other, self.dependency.name) + "{} should refer to {}".format(other, self.dependency.complete_name) ) if self._compatible_dependency(other.dependency): @@ -151,17 +151,14 @@ def _compatible_dependency(self, other): return ( self.dependency.is_root or other.is_root - or other.name == self.dependency.name + or other.is_same_package_as(self.dependency) ) def _non_empty_term(self, constraint, is_positive): if constraint.is_empty(): return - dep = Dependency(self.dependency.name, constraint) - dep.python_versions = str(self.dependency.python_versions) - - return Term(dep, is_positive) + return Term(self.dependency.with_constraint(constraint), is_positive) def __str__(self): return "{}{}".format("not " if not self.is_positive() else "", self._dependency) diff --git a/poetry/mixology/version_solver.py b/poetry/mixology/version_solver.py old mode 100644 new mode 100755 index 372b837a8b5..f7c0a1e5a3c --- a/poetry/mixology/version_solver.py +++ b/poetry/mixology/version_solver.py @@ -183,7 +183,7 @@ def _propagate_incompatibility( unsatisfied.dependency, not unsatisfied.is_positive(), incompatibility ) - return unsatisfied.dependency.name + return unsatisfied.dependency.complete_name def _resolve_conflict( self, incompatibility @@ -371,7 +371,7 @@ def _get_min(dependency): self._add_incompatibility( Incompatibility([Term(dependency, True)], PackageNotFoundCause(e)) ) - return dependency.name + return dependency.complete_name try: version = packages[0] @@ -387,7 +387,7 @@ def _get_min(dependency): Incompatibility([Term(dependency, True)], NoVersionsCause()) ) - return dependency.name + return dependency.complete_name version = self._provider.complete_package(version) @@ -402,7 +402,7 @@ def _get_min(dependency): # unit propagation which will guide us to choose a better version. conflict = conflict or all( [ - term.dependency.name == dependency.name + term.dependency.complete_name == dependency.complete_name or self._solution.satisfies(term) for term in incompatibility.terms ] @@ -411,10 +411,12 @@ def _get_min(dependency): if not conflict: self._solution.decide(version) self._log( - "selecting {} ({})".format(version.name, version.full_pretty_version) + "selecting {} ({})".format( + version.complete_name, version.full_pretty_version + ) ) - return dependency.name + return dependency.complete_name def _excludes_single_version(self, constraint): # type: (Any) -> bool return isinstance(VersionRange().difference(constraint), Version) @@ -435,31 +437,29 @@ def _add_incompatibility(self, incompatibility): # type: (Incompatibility) -> N self._log("fact: {}".format(incompatibility)) for term in incompatibility.terms: - if term.dependency.name not in self._incompatibilities: - self._incompatibilities[term.dependency.name] = [] + if term.dependency.complete_name not in self._incompatibilities: + self._incompatibilities[term.dependency.complete_name] = [] - if incompatibility in self._incompatibilities[term.dependency.name]: + if ( + incompatibility + in self._incompatibilities[term.dependency.complete_name] + ): continue - self._incompatibilities[term.dependency.name].append(incompatibility) + self._incompatibilities[term.dependency.complete_name].append( + incompatibility + ) def _get_locked(self, dependency): # type: (Dependency) -> Union[Package, None] - if dependency.name in self._use_latest: + if dependency.complete_name in self._use_latest: return - locked = self._locked.get(dependency.name) + locked = self._locked.get(dependency.complete_name) if not locked: return - if dependency.extras: - locked.requires_extras = dependency.extras - - if not dependency.transitive_marker.without_extras().is_any(): - marker_intersection = dependency.transitive_marker.without_extras().intersect( - locked.dependency.marker.without_extras() - ) - if not marker_intersection.is_empty(): - locked.dependency.transitive_marker = marker_intersection + if not dependency.is_same_package_as(locked): + return return locked diff --git a/poetry/packages/dependency_package.py b/poetry/packages/dependency_package.py index f38892488d9..60c51007ed8 100644 --- a/poetry/packages/dependency_package.py +++ b/poetry/packages/dependency_package.py @@ -1,19 +1,31 @@ +from typing import List + +from poetry.core.packages.dependency import Dependency +from poetry.core.packages.package import Package + + class DependencyPackage(object): - def __init__(self, dependency, package): + def __init__(self, dependency, package): # type: (Dependency, Package) -> None self._dependency = dependency self._package = package @property - def dependency(self): + def dependency(self): # type: () -> Dependency return self._dependency @property - def package(self): + def package(self): # type: () -> Package return self._package def clone(self): # type: () -> DependencyPackage return self.__class__(self._dependency, self._package.clone()) + def with_features(self, features): # type: (List[str]) -> "DependencyPackage" + return self.__class__(self._dependency, self._package.with_features(features)) + + def without_features(self): # type: () -> "DependencyPackage" + return self.with_features([]) + def __getattr__(self, name): return getattr(self._package, name) diff --git a/poetry/packages/locker.py b/poetry/packages/locker.py index 4cb6c67c72e..054cd049de3 100644 --- a/poetry/packages/locker.py +++ b/poetry/packages/locker.py @@ -1,10 +1,12 @@ import json import logging +import os import re from hashlib import sha256 from typing import List +from tomlkit import array from tomlkit import document from tomlkit import inline_table from tomlkit import item @@ -18,6 +20,7 @@ from poetry.core.semver import parse_constraint from poetry.core.semver.version import Version from poetry.core.version.markers import parse_marker +from poetry.utils._compat import OrderedDict from poetry.utils._compat import Path from poetry.utils.toml_file import TomlFile @@ -75,6 +78,8 @@ def locked_repository( """ Searches and returns a repository of locked packages. """ + from poetry.factory import Factory + if not self.is_locked(): return poetry.repositories.Repository() @@ -92,7 +97,21 @@ def locked_repository( return packages for info in locked_packages: - package = Package(info["name"], info["version"], info["version"]) + source = info.get("source", {}) + source_type = source.get("type") + url = source.get("url") + if source_type in ["directory", "file"]: + url = self._lock.path.parent.joinpath(url).resolve().as_posix() + + package = Package( + info["name"], + info["version"], + info["version"], + source_type=source_type, + source_url=url, + source_reference=source.get("reference"), + source_resolved_reference=source.get("resolved_reference"), + ) package.description = info.get("description", "") package.category = info["category"] package.optional = info["optional"] @@ -137,20 +156,23 @@ def locked_repository( for dep_name, constraint in info.get("dependencies", {}).items(): if isinstance(constraint, list): for c in constraint: - package.add_dependency(dep_name, c) + package.add_dependency( + Factory.create_dependency( + dep_name, c, root_dir=self._lock.path.parent + ) + ) continue - package.add_dependency(dep_name, constraint) + package.add_dependency( + Factory.create_dependency( + dep_name, constraint, root_dir=self._lock.path.parent + ) + ) if "develop" in info: package.develop = info["develop"] - if "source" in info: - package.source_type = info["source"].get("type", "") - package.source_url = info["source"]["url"] - package.source_reference = info["source"]["reference"] - packages.add_package(package) return packages @@ -181,15 +203,17 @@ def set_lock_data(self, root, packages): # type: (...) -> bool if root.extras: lock["extras"] = { extra: [dep.pretty_name for dep in deps] - for extra, deps in root.extras.items() + for extra, deps in sorted(root.extras.items()) } - lock["metadata"] = { - "lock-version": self._VERSION, - "python-versions": root.python_versions, - "content-hash": self._content_hash, - "files": files, - } + lock["metadata"] = OrderedDict( + [ + ("lock-version", self._VERSION), + ("python-versions", root.python_versions), + ("content-hash", self._content_hash), + ("files", files), + ] + ) if not self.is_locked() or lock != self.lock_data: self._write_lock_data(lock) @@ -270,9 +294,6 @@ def _lock_packages( def _dump_package(self, package): # type: (Package) -> dict dependencies = {} for dependency in sorted(package.requires, key=lambda d: d.name): - if dependency.is_optional() and not dependency.is_activated(): - continue - if dependency.pretty_name not in dependencies: dependencies[dependency.pretty_name] = [] @@ -298,15 +319,27 @@ def _dump_package(self, package): # type: (Package) -> dict constraint["version"] for constraint in constraints ] - data = { - "name": package.pretty_name, - "version": package.pretty_version, - "description": package.description or "", - "category": package.category, - "optional": package.optional, - "python-versions": package.python_versions, - "files": sorted(package.files, key=lambda x: x["file"]), - } + data = OrderedDict( + [ + ("name", package.pretty_name), + ("version", package.pretty_version), + ("description", package.description or ""), + ("category", package.category), + ("optional", package.optional), + ("python-versions", package.python_versions), + ("files", sorted(package.files, key=lambda x: x["file"])), + ] + ) + + if dependencies: + data["dependencies"] = table() + for k, constraints in dependencies.items(): + if len(constraints) == 1: + data["dependencies"][k] = constraints[0] + else: + data["dependencies"][k] = array().multiline(True) + for constraint in constraints: + data["dependencies"][k].append(constraint) if package.extras: extras = {} @@ -318,20 +351,29 @@ def _dump_package(self, package): # type: (Package) -> dict data["extras"] = extras - if dependencies: - for k, constraints in dependencies.items(): - if len(constraints) == 1: - dependencies[k] = constraints[0] + if package.source_url: + url = package.source_url + if package.source_type in ["file", "directory"]: + # The lock file should only store paths relative to the root project + url = Path( + os.path.relpath( + Path(url).as_posix(), self._lock.path.parent.as_posix() + ) + ).as_posix() - data["dependencies"] = dependencies + data["source"] = OrderedDict() - if package.source_url: - data["source"] = { - "url": package.source_url, - "reference": package.source_reference, - } if package.source_type: data["source"]["type"] = package.source_type + + data["source"]["url"] = url + + if package.source_reference: + data["source"]["reference"] = package.source_reference + + if package.source_resolved_reference: + data["source"]["resolved_reference"] = package.source_resolved_reference + if package.source_type == "directory": data["develop"] = package.develop diff --git a/poetry/packages/package_collection.py b/poetry/packages/package_collection.py index bee1674fbef..e10ea635bca 100644 --- a/poetry/packages/package_collection.py +++ b/poetry/packages/package_collection.py @@ -14,7 +14,9 @@ def __init__(self, dependency, packages=None): self.append(package) def append(self, package): - if not isinstance(package, DependencyPackage): - package = DependencyPackage(self._dependency, package) + if isinstance(package, DependencyPackage): + package = package.package + + package = DependencyPackage(self._dependency, package) return super(PackageCollection, self).append(package) diff --git a/poetry/puzzle/provider.py b/poetry/puzzle/provider.py old mode 100644 new mode 100755 index 30cb8f7537e..73f8284bef0 --- a/poetry/puzzle/provider.py +++ b/poetry/puzzle/provider.py @@ -103,7 +103,7 @@ def search_for(self, dependency): # type: (Dependency) -> List[Package] for constraint in self._search_for.keys(): if ( - constraint.name == dependency.name + constraint.is_same_package_as(dependency) and constraint.constraint.intersect(dependency.constraint) == dependency.constraint ): @@ -132,15 +132,7 @@ def search_for(self, dependency): # type: (Dependency) -> List[Package] elif dependency.is_url(): packages = self.search_for_url(dependency) else: - constraint = dependency.constraint - - packages = self._pool.find_packages( - dependency.name, - constraint, - extras=dependency.extras, - allow_prereleases=dependency.allows_prereleases(), - repository=dependency.source_name, - ) + packages = self._pool.find_packages(dependency) packages.sort( key=lambda p: ( @@ -167,17 +159,12 @@ def search_for_vcs(self, dependency): # type: (VCSDependency) -> List[Package] package = self.get_package_from_vcs( dependency.vcs, dependency.source, - dependency.reference, + branch=dependency.branch, + tag=dependency.tag, + rev=dependency.rev, name=dependency.name, ) - for extra in dependency.extras: - if extra in package.extras: - for dep in package.extras[extra]: - dep.activate() - - package.requires += package.extras[extra] - dependency._constraint = package.version dependency._pretty_constraint = package.version.text @@ -187,7 +174,7 @@ def search_for_vcs(self, dependency): # type: (VCSDependency) -> List[Package] @classmethod def get_package_from_vcs( - cls, vcs, url, reference=None, name=None + cls, vcs, url, branch=None, tag=None, rev=None, name=None ): # type: (str, str, Optional[str], Optional[str]) -> Package if vcs != "git": raise ValueError("Unsupported VCS dependency {}".format(vcs)) @@ -199,6 +186,7 @@ def get_package_from_vcs( try: git = Git() git.clone(url, tmp_dir) + reference = branch or tag or rev if reference is not None: git.checkout(reference, tmp_dir) else: @@ -207,10 +195,10 @@ def get_package_from_vcs( revision = git.rev_parse(reference, tmp_dir).strip() package = cls.get_package_from_directory(tmp_dir, name=name) - - package.source_type = "git" - package.source_url = url - package.source_reference = revision + package._source_type = "git" + package._source_url = url + package._source_reference = reference + package._source_resolved_reference = revision except Exception: raise finally: @@ -242,18 +230,10 @@ def search_for_file(self, dependency): # type: (FileDependency) -> List[Package if dependency.base is not None: package.root_dir = dependency.base - package.source_url = dependency.path.as_posix() package.files = [ {"file": dependency.path.name, "hash": "sha256:" + dependency.hash()} ] - for extra in dependency.extras: - if extra in package.extras: - for dep in package.extras[extra]: - dep.activate() - - package.requires += package.extras[extra] - return [package] @classmethod @@ -267,9 +247,6 @@ def get_package_from_file(cls, file_path): # type: (Path) -> Package "Unable to determine package info from path: {}".format(file_path) ) - package.source_type = "file" - package.source_url = file_path.as_posix() - return package def search_for_directory( @@ -289,19 +266,11 @@ def search_for_directory( self._deferred_cache[dependency] = (dependency, package) - package.source_url = dependency.path.as_posix() package.develop = dependency.develop if dependency.base is not None: package.root_dir = dependency.base - for extra in dependency.extras: - if extra in package.extras: - for dep in package.extras[extra]: - dep.activate() - - package.requires += package.extras[extra] - return [package] @classmethod @@ -320,9 +289,6 @@ def get_package_from_directory( ) ) - package.source_type = "directory" - package.source_url = directory.as_posix() - return package def search_for_url(self, dependency): # type: (URLDependency) -> List[Package] @@ -362,8 +328,8 @@ def get_package_from_url(cls, url): # type: (str) -> Package package = cls.get_package_from_file(temp_dir / file_name) - package.source_type = "url" - package.source_url = url + package._source_type = "url" + package._source_url = url return package @@ -447,6 +413,7 @@ def incompatibilities_for( def complete_package( self, package ): # type: (DependencyPackage) -> DependencyPackage + if package.is_root(): package = package.clone() requires = package.all_requires @@ -461,7 +428,7 @@ def complete_package( self._pool.package( package.name, package.version.text, - extras=package.requires_extras, + extras=package.dependency.extras, repository=package.dependency.source_name, ), ) @@ -480,13 +447,44 @@ def complete_package( elif r.is_url(): self.search_for_url(r) - _dependencies = [ - r - for r in requires - if self._python_constraint.allows_any(r.python_constraint) - and r.name not in self.UNSAFE_PACKAGES - and (not self._env or r.marker.validate(self._env.marker_env)) - ] + optional_dependencies = [] + activated_extras = [] + for extra in package.dependency.extras: + if extra not in package.extras: + continue + + activated_extras.append(extra) + optional_dependencies += [d.name for d in package.extras[extra]] + + _dependencies = [] + + # If some extras/features were required, we need to + # add a special dependency representing the base package + # to the current package + if package.dependency.extras: + if activated_extras: + package = package.with_features(activated_extras) + + _dependencies.append(package.without_features().to_dependency()) + + for dep in requires: + if not self._python_constraint.allows_any(dep.python_constraint): + continue + + if dep.name in self.UNSAFE_PACKAGES: + continue + + if self._env and not dep.marker.validate(self._env.marker_env): + continue + + if ( + dep.is_optional() + and dep.name not in optional_dependencies + and not package.is_root() + ): + continue + + _dependencies.append(dep) overrides = self._overrides.get(package, {}) dependencies = [] @@ -683,18 +681,6 @@ def complete_package( continue dep.transitive_python_versions = str(python_constraint_intersection) - if (package.dependency.is_directory() or package.dependency.is_file()) and ( - dep.is_directory() or dep.is_file() - ): - relative_path = Path( - os.path.relpath( - dep.full_path.as_posix(), package.root_dir.as_posix() - ) - ) - - # TODO: Improve the way we set the correct relative path for dependencies - dep._path = relative_path - clean_dependencies.append(dep) package.requires = clean_dependencies diff --git a/poetry/puzzle/solver.py b/poetry/puzzle/solver.py index 8dd4fafaaaa..afd3a1850eb 100644 --- a/poetry/puzzle/solver.py +++ b/poetry/puzzle/solver.py @@ -34,7 +34,7 @@ def __init__( installed, # type: Repository locked, # type: Repository io, # type: ConsoleIO - remove_untracked=False, # type: bool, + remove_untracked=False, # type: bool provider=None, # type: Optional[Provider] ): self._package = package @@ -100,18 +100,37 @@ def solve(self, use_latest=None): # type: (...) -> List[Operation] and locked.source_type == pkg.source_type and locked_source_url == pkg_source_url and locked.source_reference == pkg.source_reference + and locked.source_resolved_reference + == pkg.source_resolved_reference ): - pkg = Package(pkg.name, locked.version) - pkg.source_type = "git" - pkg.source_url = locked.source_url - pkg.source_reference = locked.source_reference + pkg = Package( + pkg.name, + locked.version, + source_type="git", + source_url=locked.source_url, + source_reference=locked.source_reference, + source_resolved_reference=locked.source_resolved_reference, + ) break if pkg_source_url != package_source_url or ( - pkg.source_reference != package.source_reference + ( + not pkg.source_resolved_reference + or not package.source_resolved_reference + ) + and pkg.source_reference != package.source_reference and not pkg.source_reference.startswith( package.source_reference ) + or ( + pkg.source_resolved_reference + and package.source_resolved_reference + and pkg.source_resolved_reference + != package.source_resolved_reference + and not pkg.source_resolved_reference.startswith( + package.source_resolved_reference + ) + ) ): operations.append(Update(pkg, package, priority=depths[i])) else: @@ -226,17 +245,39 @@ def _solve(self, use_latest=None): PackageNode(self._package, packages), aggregate_package_nodes ) ) - # Return the packages in their original order with associated depths - final_packages = packages - depths = [results[package] for package in packages] + # Merging feature packages with base packages + final_packages = [] + depths = [] + for package in packages: + if package.features: + for _package in packages: + if ( + _package.name == package.name + and not _package.is_same_package_as(package) + and _package.version == package.version + ): + for dep in package.requires: + if dep.is_same_package_as(_package): + continue + + if dep not in _package.requires: + _package.requires.append(dep) + + continue + + final_packages.append(package) + depths.append(results[package]) + + # Return the packages in their original order with associated depths return final_packages, depths class DFSNode(object): - def __init__(self, id, name): + def __init__(self, id, name, base_name): self.id = id self.name = name + self.base_name = base_name def reachable(self): return [] @@ -302,13 +343,7 @@ def dfs_visit(node, back_edges, visited, sorted_nodes): class PackageNode(DFSNode): def __init__( - self, - package, - packages, - previous=None, - previous_dep=None, - dep=None, - is_activated=True, + self, package, packages, previous=None, previous_dep=None, dep=None, ): self.package = package self.packages = packages @@ -323,11 +358,12 @@ def __init__( self.optional = True else: self.category = dep.category - self.optional = dep.is_optional() and not dep.is_activated() - if not is_activated: - self.optional = True + self.optional = dep.is_optional() + super(PackageNode, self).__init__( - (package.name, self.category, self.optional), package.name + (package.complete_name, self.category, self.optional), + package.complete_name, + package.name, ) def reachable(self): @@ -341,29 +377,7 @@ def reachable(self): return [] for dependency in self.package.all_requires: - is_activated = True - if dependency.is_optional(): - if not self.package.is_root() and ( - not self.previous_dep or not self.previous_dep.extras - ): - continue - - is_activated = False - for group, extra_deps in self.package.extras.items(): - if self.dep: - extras = self.previous_dep.extras - elif self.package.is_root(): - extras = self.package.extras - else: - extras = [] - - if group in extras and dependency.name in ( - d.name for d in self.package.extras[group] - ): - is_activated = True - break - - if self.previous and self.previous.package.name == dependency.name: + if self.previous and self.previous.name == dependency.name: # We have a circular dependency. # Since the dependencies are resolved we can # simply skip it because we already have it @@ -372,8 +386,9 @@ def reachable(self): continue for pkg in self.packages: - if pkg.name == dependency.name and dependency.constraint.allows( - pkg.version + if ( + pkg.complete_name == dependency.complete_name + and dependency.constraint.allows(pkg.version) ): # If there is already a child with this name # we merge the requirements @@ -383,6 +398,7 @@ def reachable(self): for child in children ): continue + children.append( PackageNode( pkg, @@ -390,15 +406,21 @@ def reachable(self): self, dependency, self.dep or dependency, - is_activated=is_activated, ) ) + return children def visit(self, parents): # The root package, which has no parents, is defined as having depth -1 # So that the root package's top-level dependencies have depth 0. - self.depth = 1 + max([parent.depth for parent in parents] + [-2]) + self.depth = 1 + max( + [ + parent.depth if parent.base_name != self.base_name else parent.depth - 1 + for parent in parents + ] + + [-2] + ) def aggregate_package_nodes(nodes, children): diff --git a/poetry/repositories/base_repository.py b/poetry/repositories/base_repository.py index 049494766b4..46422ca0edc 100644 --- a/poetry/repositories/base_repository.py +++ b/poetry/repositories/base_repository.py @@ -12,9 +12,7 @@ def has_package(self, package): def package(self, name, version, extras=None): raise NotImplementedError() - def find_packages( - self, name, constraint=None, extras=None, allow_prereleases=False - ): + def find_packages(self, dependency): raise NotImplementedError() def search(self, query): diff --git a/poetry/repositories/installed_repository.py b/poetry/repositories/installed_repository.py index 1b6fed488f7..a0630116f53 100644 --- a/poetry/repositories/installed_repository.py +++ b/poetry/repositories/installed_repository.py @@ -76,9 +76,9 @@ def set_package_vcs_properties_from_path( revision = git.rev_parse("HEAD", src).strip() url = git.remote_url(src) - package.source_type = "git" - package.source_url = url - package.source_reference = revision + package._source_type = "git" + package._source_url = url + package._source_reference = revision @classmethod def set_package_vcs_properties(cls, package, env): # type: (Package, Env) -> None @@ -144,15 +144,15 @@ def load(cls, env): # type: (Env) -> InstalledRepository break else: # TODO: handle multiple source directories? - package.source_type = "directory" - package.source_url = paths.pop().as_posix() + package._source_type = "directory" + package._source_url = paths.pop().as_posix() continue if cls.is_vcs_package(path, env): cls.set_package_vcs_properties(package, env) else: # If not, it's a path dependency - package.source_type = "directory" - package.source_url = str(path.parent) + package._source_type = "directory" + package._source_url = str(path.parent) return repo diff --git a/poetry/repositories/legacy_repository.py b/poetry/repositories/legacy_repository.py old mode 100644 new mode 100755 index fed921e5f77..c2eed7c4d06 --- a/poetry/repositories/legacy_repository.py +++ b/poetry/repositories/legacy_repository.py @@ -222,17 +222,17 @@ def authenticated_url(self): # type: () -> str path=parsed.path, ) - def find_packages( - self, name, constraint=None, extras=None, allow_prereleases=False - ): + def find_packages(self, dependency): packages = [] + constraint = dependency.constraint if constraint is None: constraint = "*" if not isinstance(constraint, VersionConstraint): constraint = parse_constraint(constraint) + allow_prereleases = dependency.allows_prereleases() if isinstance(constraint, VersionRange): if ( constraint.max is not None @@ -242,7 +242,7 @@ def find_packages( ): allow_prereleases = True - key = name + key = dependency.name if not constraint.is_any(): key = "{}:{}".format(key, str(constraint)) @@ -251,7 +251,7 @@ def find_packages( if self._cache.store("matches").has(key): versions = self._cache.store("matches").get(key) else: - page = self._get("/{}/".format(canonicalize_name(name).replace(".", "-"))) + page = self._get("/{}/".format(dependency.name.replace(".", "-"))) if page is None: return [] @@ -270,19 +270,19 @@ def find_packages( for package_versions in (versions, ignored_pre_release_versions): for version in package_versions: - package = Package(name, version) - package.source_type = "legacy" - package.source_reference = self.name - package.source_url = self._url - - if extras is not None: - package.requires_extras = extras + package = Package( + dependency.name, + version, + source_type="legacy", + source_reference=self.name, + source_url=self._url, + ) packages.append(package) self._log( "{} packages found for {} {}".format( - len(packages), name, str(constraint) + len(packages), dependency.name, str(constraint) ), level="debug", ) @@ -302,7 +302,7 @@ def package(self, name, version, extras=None): # type: (...) -> Package We also need to download every file matching this release to get the various hashes. - Note that, this will be cached so the subsequent operations + Note that this will be cached so the subsequent operations should be much faster. """ try: @@ -311,9 +311,9 @@ def package(self, name, version, extras=None): # type: (...) -> Package return self._packages[index] except ValueError: package = super(LegacyRepository, self).package(name, version, extras) - package.source_type = "legacy" - package.source_url = self._url - package.source_reference = self.name + package._source_type = "legacy" + package._source_url = self._url + package._source_reference = self.name return package diff --git a/poetry/repositories/pool.py b/poetry/repositories/pool.py old mode 100644 new mode 100755 index 2992410279e..ac712831d3a --- a/poetry/repositories/pool.py +++ b/poetry/repositories/pool.py @@ -144,13 +144,9 @@ def package( raise PackageNotFound("Package {} ({}) not found.".format(name, version)) def find_packages( - self, - name, - constraint=None, - extras=None, - allow_prereleases=False, - repository=None, + self, dependency, ): + repository = dependency.source_name if repository is not None: repository = repository.lower() @@ -162,15 +158,11 @@ def find_packages( raise ValueError('Repository "{}" does not exist.'.format(repository)) if repository is not None and not self._ignore_repository_names: - return self.repository(repository).find_packages( - name, constraint, extras=extras, allow_prereleases=allow_prereleases - ) + return self.repository(repository).find_packages(dependency) packages = [] - for idx, repo in enumerate(self._repositories): - packages += repo.find_packages( - name, constraint, extras=extras, allow_prereleases=allow_prereleases - ) + for repo in self._repositories: + packages += repo.find_packages(dependency) return packages diff --git a/poetry/repositories/pypi_repository.py b/poetry/repositories/pypi_repository.py old mode 100644 new mode 100755 index 6ea50bd84dc..16105992a1c --- a/poetry/repositories/pypi_repository.py +++ b/poetry/repositories/pypi_repository.py @@ -14,6 +14,7 @@ from cachy import CacheManager from html5lib.html5parser import parse +from poetry.core.packages import Dependency from poetry.core.packages import Package from poetry.core.packages import dependency_from_pep_508 from poetry.core.packages.utils.link import Link @@ -79,22 +80,18 @@ def __init__(self, url="https://pypi.org/", disable_cache=False, fallback=True): def session(self): return self._session - def find_packages( - self, - name, # type: str - constraint=None, # type: Union[VersionConstraint, str, None] - extras=None, # type: Union[list, None] - allow_prereleases=False, # type: bool - ): # type: (...) -> List[Package] + def find_packages(self, dependency): # type: (Dependency) -> List[Package] """ Find packages on the remote server. """ + constraint = dependency.constraint if constraint is None: constraint = "*" if not isinstance(constraint, VersionConstraint): constraint = parse_constraint(constraint) + allow_prereleases = dependency.allows_prereleases() if isinstance(constraint, VersionRange): if ( constraint.max is not None @@ -105,10 +102,10 @@ def find_packages( allow_prereleases = True try: - info = self.get_package_info(name) + info = self.get_package_info(dependency.name) except PackageNotFound: self._log( - "No packages found for {} {}".format(name, str(constraint)), + "No packages found for {} {}".format(dependency.name, str(constraint)), level="debug", ) return [] @@ -121,7 +118,7 @@ def find_packages( # Bad release self._log( "No release information found for {}-{}, skipping".format( - name, version + dependency.name, version ), level="debug", ) @@ -132,7 +129,7 @@ def find_packages( except ParseVersionError: self._log( 'Unable to parse version "{}" for the {} package, skipping'.format( - version, name + version, dependency.name ), level="debug", ) @@ -145,13 +142,12 @@ def find_packages( continue if not constraint or (constraint and constraint.allows(package.version)): - if extras is not None: - package.requires_extras = extras - packages.append(package) self._log( - "{} packages found for {} {}".format(len(packages), name, str(constraint)), + "{} packages found for {} {}".format( + len(packages), dependency.name, str(constraint) + ), level="debug", ) diff --git a/poetry/repositories/repository.py b/poetry/repositories/repository.py old mode 100644 new mode 100755 index 85aac6ddabc..b57cbbc63c7 --- a/poetry/repositories/repository.py +++ b/poetry/repositories/repository.py @@ -29,32 +29,22 @@ def package(self, name, version, extras=None): for package in self.packages: if name == package.name and package.version.text == version: - # Activate extra dependencies - for extra in extras: - if extra in package.extras: - for extra_dep in package.extras[extra]: - for dep in package.requires: - if dep.name == extra_dep.name: - dep.activate() - - return package.clone() - - def find_packages( - self, name, constraint=None, extras=None, allow_prereleases=False - ): - name = name.lower() + package = package.with_features(extras) + + return package + + def find_packages(self, dependency): + constraint = dependency.constraint packages = [] ignored_pre_release_packages = [] - if extras is None: - extras = [] - if constraint is None: constraint = "*" if not isinstance(constraint, VersionConstraint): constraint = parse_constraint(constraint) + allow_prereleases = dependency.allows_prereleases if isinstance(constraint, VersionRange): if ( constraint.max is not None @@ -65,7 +55,7 @@ def find_packages( allow_prereleases = True for package in self.packages: - if name == package.name: + if dependency.name == package.name: if ( package.is_prerelease() and not allow_prereleases @@ -79,19 +69,6 @@ def find_packages( continue if constraint.allows(package.version): - for dep in package.requires: - for extra in extras: - if extra not in package.extras: - continue - - reqs = package.extras[extra] - for req in reqs: - if req.name == dep.name: - dep.activate() - - if extras: - package.requires_extras = extras - packages.append(package) return packages or ignored_pre_release_packages diff --git a/poetry/utils/exporter.py b/poetry/utils/exporter.py index 116e20c8804..e8c91e66e0b 100644 --- a/poetry/utils/exporter.py +++ b/poetry/utils/exporter.py @@ -1,3 +1,5 @@ +import os + from typing import Union from clikit.api.io import IO @@ -84,18 +86,35 @@ def _export_requirements_txt( package.source_url, package.source_reference, package.name ) elif package.source_type in ["directory", "file", "url"]: + url = package.source_url if package.source_type == "file": - dependency = FileDependency(package.name, Path(package.source_url)) + dependency = FileDependency( + package.name, + Path(package.source_url), + base=self._poetry.locker.lock.path.parent, + ) + url = Path( + os.path.relpath( + url, self._poetry.locker.lock.path.parent.as_posix() + ) + ).as_posix() elif package.source_type == "directory": dependency = DirectoryDependency( - package.name, Path(package.source_url) + package.name, + Path(package.source_url), + base=self._poetry.locker.lock.path.parent, ) + url = Path( + os.path.relpath( + url, self._poetry.locker.lock.path.parent.as_posix() + ) + ).as_posix() else: dependency = URLDependency(package.name, package.source_url) dependency.marker = package.marker - line = "{}".format(package.source_url) + line = "{}".format(url) if package.develop and package.source_type == "directory": line = "-e " + line else: diff --git a/poetry/utils/helpers.py b/poetry/utils/helpers.py index ac00c53bec0..180a90d50f3 100644 --- a/poetry/utils/helpers.py +++ b/poetry/utils/helpers.py @@ -10,6 +10,7 @@ import requests from poetry.config.config import Config +from poetry.core.packages.package import Package from poetry.core.version import Version from poetry.utils._compat import Path @@ -100,3 +101,15 @@ def download_file( for chunk in response.iter_content(chunk_size=chunk_size): if chunk: f.write(chunk) + + +def get_package_version_display_string( + package, root=None +): # type: (Package, Optional[Path]) -> str + if package.source_type in ["file", "directory"] and root: + return "{} {}".format( + package.version, + Path(os.path.relpath(package.source_url, root.as_posix())).as_posix(), + ) + + return package.full_pretty_version diff --git a/poetry/version/version_selector.py b/poetry/version/version_selector.py index 8cf02c7cbed..ea002860938 100644 --- a/poetry/version/version_selector.py +++ b/poetry/version/version_selector.py @@ -1,9 +1,7 @@ from typing import Union -from poetry.core.packages import Dependency from poetry.core.packages import Package from poetry.core.semver import Version -from poetry.core.semver import parse_constraint class VersionSelector(object): @@ -21,27 +19,27 @@ def find_best_candidate( Given a package name and optional version, returns the latest Package that matches """ - if target_package_version: - constraint = parse_constraint(target_package_version) - else: - constraint = parse_constraint("*") - - candidates = self._pool.find_packages( - package_name, constraint, allow_prereleases=True, repository=source + from poetry.factory import Factory + + dependency = Factory.create_dependency( + package_name, + { + "version": target_package_version or "*", + "allow_prereleases": allow_prereleases, + "source": source, + }, ) + candidates = self._pool.find_packages(dependency) only_prereleases = all([c.version.is_prerelease() for c in candidates]) if not candidates: return False - dependency = Dependency(package_name, constraint) - package = None for candidate in candidates: if ( candidate.is_prerelease() and not dependency.allows_prereleases() - and not allow_prereleases and not only_prereleases ): continue diff --git a/pyproject.toml b/pyproject.toml index e2296a3c2a5..9b31778e231 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ [tool.poetry.dependencies] python = "~2.7 || ^3.5" -poetry-core = "^1.0.0a9" +poetry-core = "^1.0.0b1" cleo = "^0.8.1" clikit = "^0.6.2" crashtest = { version = "^0.3.0", python = "^3.6" } diff --git a/tests/console/commands/debug/test_resolve.py b/tests/console/commands/debug/test_resolve.py index 2be60959a66..f96c36fc062 100644 --- a/tests/console/commands/debug/test_resolve.py +++ b/tests/console/commands/debug/test_resolve.py @@ -1,5 +1,6 @@ from cleo.testers import CommandTester +from poetry.factory import Factory from tests.helpers import get_package @@ -8,7 +9,7 @@ def test_debug_resolve_gives_resolution_results(app, repo): tester = CommandTester(command) cachy2 = get_package("cachy", "0.2.0") - cachy2.add_dependency("msgpack-python", ">=0.5 <0.6") + cachy2.add_dependency(Factory.create_dependency("msgpack-python", ">=0.5 <0.6")) repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(cachy2) @@ -33,7 +34,7 @@ def test_debug_resolve_tree_option_gives_the_dependency_tree(app, repo): tester = CommandTester(command) cachy2 = get_package("cachy", "0.2.0") - cachy2.add_dependency("msgpack-python", ">=0.5 <0.6") + cachy2.add_dependency(Factory.create_dependency("msgpack-python", ">=0.5 <0.6")) repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(cachy2) diff --git a/tests/console/commands/test_add.py b/tests/console/commands/test_add.py index 14b99842231..3a0f3400403 100644 --- a/tests/console/commands/test_add.py +++ b/tests/console/commands/test_add.py @@ -300,12 +300,13 @@ def test_add_git_ssh_constraint(app, repo, tester, tmp_venv): def test_add_directory_constraint(app, repo, tester, mocker): p = mocker.patch("poetry.utils._compat.Path.cwd") - p.return_value = Path(__file__) / ".." + p.return_value = Path(__file__).parent repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("cleo", "0.6.5")) - tester.execute("../git/github.com/demo/demo") + path = "../git/github.com/demo/demo" + tester.execute("{}".format(path)) expected = """\ @@ -317,8 +318,10 @@ def test_add_directory_constraint(app, repo, tester, mocker): Package operations: 2 installs, 0 updates, 0 removals • Installing pendulum (1.4.4) - • Installing demo (0.1.2 ../git/github.com/demo/demo) -""" + • Installing demo (0.1.2 {}) +""".format( + app.poetry.file.parent.joinpath(path).resolve().as_posix() + ) assert expected == tester.io.fetch_output() assert 2 == tester._command.installer.executor.installations_count @@ -335,7 +338,8 @@ def test_add_directory_with_poetry(app, repo, tester, mocker): repo.add_package(get_package("pendulum", "1.4.4")) - tester.execute("../git/github.com/demo/pyproject-demo") + path = "../git/github.com/demo/pyproject-demo" + tester.execute("{}".format(path)) expected = """\ @@ -347,8 +351,10 @@ def test_add_directory_with_poetry(app, repo, tester, mocker): Package operations: 2 installs, 0 updates, 0 removals • Installing pendulum (1.4.4) - • Installing demo (0.1.2 ../git/github.com/demo/pyproject-demo) -""" + • Installing demo (0.1.2 {}) +""".format( + app.poetry.file.parent.joinpath(path).resolve().as_posix() + ) assert expected == tester.io.fetch_output() assert 2 == tester._command.installer.executor.installations_count @@ -360,7 +366,8 @@ def test_add_file_constraint_wheel(app, repo, tester, mocker, poetry): repo.add_package(get_package("pendulum", "1.4.4")) - tester.execute("../distributions/demo-0.1.0-py2.py3-none-any.whl") + path = "../distributions/demo-0.1.0-py2.py3-none-any.whl" + tester.execute("{}".format(path)) expected = """\ @@ -372,8 +379,10 @@ def test_add_file_constraint_wheel(app, repo, tester, mocker, poetry): Package operations: 2 installs, 0 updates, 0 removals • Installing pendulum (1.4.4) - • Installing demo (0.1.0 ../distributions/demo-0.1.0-py2.py3-none-any.whl) -""" + • Installing demo (0.1.0 {}) +""".format( + app.poetry.file.parent.joinpath(path).resolve().as_posix() + ) assert expected == tester.io.fetch_output() assert 2 == tester._command.installer.executor.installations_count @@ -392,7 +401,8 @@ def test_add_file_constraint_sdist(app, repo, tester, mocker): repo.add_package(get_package("pendulum", "1.4.4")) - tester.execute("../distributions/demo-0.1.0.tar.gz") + path = "../distributions/demo-0.1.0.tar.gz" + tester.execute("{}".format(path)) expected = """\ @@ -404,8 +414,10 @@ def test_add_file_constraint_sdist(app, repo, tester, mocker): Package operations: 2 installs, 0 updates, 0 removals • Installing pendulum (1.4.4) - • Installing demo (0.1.0 ../distributions/demo-0.1.0.tar.gz) -""" + • Installing demo (0.1.0 {}) +""".format( + app.poetry.file.parent.joinpath(path).resolve().as_posix() + ) assert expected == tester.io.fetch_output() assert 2 == tester._command.installer.executor.installations_count @@ -1076,7 +1088,8 @@ def test_add_directory_constraint_old_installer( repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("cleo", "0.6.5")) - old_tester.execute("../git/github.com/demo/demo") + path = "../git/github.com/demo/demo" + old_tester.execute("{}".format(path)) expected = """\ @@ -1088,8 +1101,10 @@ def test_add_directory_constraint_old_installer( Package operations: 2 installs, 0 updates, 0 removals - Installing pendulum (1.4.4) - - Installing demo (0.1.2 ../git/github.com/demo/demo) -""" + - Installing demo (0.1.2 {}) +""".format( + app.poetry.file.parent.joinpath(path).resolve().as_posix() + ) assert expected == old_tester.io.fetch_output() @@ -1109,7 +1124,8 @@ def test_add_directory_with_poetry_old_installer( repo.add_package(get_package("pendulum", "1.4.4")) - old_tester.execute("../git/github.com/demo/pyproject-demo") + path = "../git/github.com/demo/pyproject-demo" + old_tester.execute("{}".format(path)) expected = """\ @@ -1121,8 +1137,10 @@ def test_add_directory_with_poetry_old_installer( Package operations: 2 installs, 0 updates, 0 removals - Installing pendulum (1.4.4) - - Installing demo (0.1.2 ../git/github.com/demo/pyproject-demo) -""" + - Installing demo (0.1.2 {}) +""".format( + app.poetry.file.parent.joinpath(path).resolve().as_posix() + ) assert expected == old_tester.io.fetch_output() @@ -1137,7 +1155,8 @@ def test_add_file_constraint_wheel_old_installer( repo.add_package(get_package("pendulum", "1.4.4")) - old_tester.execute("../distributions/demo-0.1.0-py2.py3-none-any.whl") + path = "../distributions/demo-0.1.0-py2.py3-none-any.whl" + old_tester.execute("{}".format(path)) expected = """\ @@ -1149,8 +1168,10 @@ def test_add_file_constraint_wheel_old_installer( Package operations: 2 installs, 0 updates, 0 removals - Installing pendulum (1.4.4) - - Installing demo (0.1.0 ../distributions/demo-0.1.0-py2.py3-none-any.whl) -""" + - Installing demo (0.1.0 {}) +""".format( + app.poetry.file.parent.joinpath(path).resolve().as_posix() + ) assert expected == old_tester.io.fetch_output() @@ -1172,7 +1193,8 @@ def test_add_file_constraint_sdist_old_installer( repo.add_package(get_package("pendulum", "1.4.4")) - old_tester.execute("../distributions/demo-0.1.0.tar.gz") + path = "../distributions/demo-0.1.0.tar.gz" + old_tester.execute("{}".format(path)) expected = """\ @@ -1184,8 +1206,10 @@ def test_add_file_constraint_sdist_old_installer( Package operations: 2 installs, 0 updates, 0 removals - Installing pendulum (1.4.4) - - Installing demo (0.1.0 ../distributions/demo-0.1.0.tar.gz) -""" + - Installing demo (0.1.0 {}) +""".format( + app.poetry.file.parent.joinpath(path).resolve().as_posix() + ) assert expected == old_tester.io.fetch_output() diff --git a/tests/console/commands/test_show.py b/tests/console/commands/test_show.py index 8ac6b4db7e4..c7f9716e6ac 100644 --- a/tests/console/commands/test_show.py +++ b/tests/console/commands/test_show.py @@ -3,6 +3,7 @@ from cleo.testers import CommandTester from clikit.formatter.ansi_formatter import AnsiFormatter +from poetry.factory import Factory from tests.helpers import get_package @@ -1067,10 +1068,10 @@ def test_show_tree(app, poetry, installed): command = app.find("show") tester = CommandTester(command) - poetry.package.add_dependency("cachy", "^0.2.0") + poetry.package.add_dependency(Factory.create_dependency("cachy", "^0.2.0")) cachy2 = get_package("cachy", "0.2.0") - cachy2.add_dependency("msgpack-python", ">=0.5 <0.6") + cachy2.add_dependency(Factory.create_dependency("msgpack-python", ">=0.5 <0.6")) installed.add_package(cachy2) diff --git a/tests/console/conftest.py b/tests/console/conftest.py index 034e47207f5..76417e382ab 100644 --- a/tests/console/conftest.py +++ b/tests/console/conftest.py @@ -181,14 +181,11 @@ def __init__(self, file, local_config, package, locker, config): class Repository(BaseRepository): - def find_packages( - self, name, constraint=None, extras=None, allow_prereleases=False - ): - packages = super(Repository, self).find_packages( - name, constraint, extras, allow_prereleases - ) + def find_packages(self, dependency): + packages = super(Repository, self).find_packages(dependency) if len(packages) == 0: - raise PackageNotFound("Package [{}] not found.".format(name)) + raise PackageNotFound("Package [{}] not found.".format(dependency.name)) + return packages def find_links_for_package(self, package): diff --git a/tests/installation/fixtures/with-directory-dependency-poetry-transitive.test b/tests/installation/fixtures/with-directory-dependency-poetry-transitive.test index 33836fb0b13..7177191e2c8 100644 --- a/tests/installation/fixtures/with-directory-dependency-poetry-transitive.test +++ b/tests/installation/fixtures/with-directory-dependency-poetry-transitive.test @@ -14,23 +14,21 @@ bar = ["tomlkit"] foo = ["cleo"] [package.source] -reference = "" type = "file" -url = "../../distributions/demo-0.1.0-py2.py3-none-any.whl" +url = "../distributions/demo-0.1.0-py2.py3-none-any.whl" [[package]] category = "main" description = "This is a description" -develop = true +develop = false name = "inner-directory-project" optional = false python-versions = "*" version = "1.2.4" [package.source] -reference = "" type = "directory" -url = "../project_with_transitive_file_dependencies/inner-directory-project" +url = "project_with_transitive_file_dependencies/inner-directory-project" [[package]] category = "main" @@ -43,7 +41,7 @@ version = "1.4.4" [[package]] category = "main" description = "This is a description" -develop = true +develop = false name = "project-with-extras" optional = false python-versions = "*" @@ -54,14 +52,13 @@ extras_a = ["pendulum (>=1.4.4)"] extras_b = ["cachy (>=0.2.0)"] [package.source] -reference = "" type = "directory" url = "../project_with_extras" [[package]] category = "main" description = "This is a description" -develop = true +develop = false name = "project-with-transitive-directory-dependencies" optional = false python-versions = "*" @@ -72,14 +69,13 @@ project-with-extras = "1.2.3" project-with-transitive-file-dependencies = "1.2.3" [package.source] -reference = "" type = "directory" url = "project_with_transitive_directory_dependencies" [[package]] category = "main" description = "This is a description" -develop = true +develop = false name = "project-with-transitive-file-dependencies" optional = false python-versions = "*" @@ -90,7 +86,6 @@ demo = "0.1.0" inner-directory-project = "1.2.4" [package.source] -reference = "" type = "directory" url = "project_with_transitive_file_dependencies" diff --git a/tests/installation/fixtures/with-directory-dependency-poetry.test b/tests/installation/fixtures/with-directory-dependency-poetry.test index 4a3fd542a45..32530cb97f0 100644 --- a/tests/installation/fixtures/with-directory-dependency-poetry.test +++ b/tests/installation/fixtures/with-directory-dependency-poetry.test @@ -9,7 +9,7 @@ version = "1.4.4" [[package]] category = "main" description = "This is a description" -develop = true +develop = false name = "project-with-extras" optional = false python-versions = "*" @@ -23,7 +23,6 @@ extras_a = ["pendulum (>=1.4.4)"] extras_b = ["cachy (>=0.2.0)"] [package.source] -reference = "" type = "directory" url = "tests/fixtures/project_with_extras" diff --git a/tests/installation/fixtures/with-directory-dependency-setuptools.test b/tests/installation/fixtures/with-directory-dependency-setuptools.test index ffa790f3305..879faecb22f 100644 --- a/tests/installation/fixtures/with-directory-dependency-setuptools.test +++ b/tests/installation/fixtures/with-directory-dependency-setuptools.test @@ -9,7 +9,7 @@ python-versions = "*" [[package]] name = "my-package" version = "0.1.2" -develop = true +develop = false description = "Demo project." category = "main" optional = false @@ -17,7 +17,6 @@ python-versions = "*" [package.source] type = "directory" -reference = "" url = "tests/fixtures/project_with_setup" [package.dependencies] diff --git a/tests/installation/fixtures/with-file-dependency-transitive.test b/tests/installation/fixtures/with-file-dependency-transitive.test index 42ffd18ea41..6e5d92d711a 100644 --- a/tests/installation/fixtures/with-file-dependency-transitive.test +++ b/tests/installation/fixtures/with-file-dependency-transitive.test @@ -14,21 +14,19 @@ bar = ["tomlkit"] foo = ["cleo"] [package.source] -reference = "" type = "file" url = "../distributions/demo-0.1.0-py2.py3-none-any.whl" [[package]] category = "main" description = "This is a description" -develop = true +develop = false name = "inner-directory-project" optional = false python-versions = "*" version = "1.2.4" [package.source] -reference = "" type = "directory" url = "project_with_transitive_file_dependencies/inner-directory-project" @@ -43,7 +41,7 @@ version = "1.4.4" [[package]] category = "main" description = "This is a description" -develop = true +develop = false name = "project-with-transitive-file-dependencies" optional = false python-versions = "*" @@ -54,7 +52,6 @@ demo = "0.1.0" inner-directory-project = "1.2.4" [package.source] -reference = "" type = "directory" url = "project_with_transitive_file_dependencies" diff --git a/tests/installation/fixtures/with-file-dependency.test b/tests/installation/fixtures/with-file-dependency.test index cb3c3d432e0..06ad019c0f4 100644 --- a/tests/installation/fixtures/with-file-dependency.test +++ b/tests/installation/fixtures/with-file-dependency.test @@ -8,7 +8,6 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.source] type = "file" -reference = "" url = "tests/fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl" [package.dependencies] diff --git a/tests/installation/fixtures/with-url-dependency.test b/tests/installation/fixtures/with-url-dependency.test index ee3ac5cb2ed..2d23f7981aa 100644 --- a/tests/installation/fixtures/with-url-dependency.test +++ b/tests/installation/fixtures/with-url-dependency.test @@ -8,7 +8,6 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.source] type = "url" -reference = "" url = "https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl" [package.dependencies] diff --git a/tests/installation/fixtures/with-wheel-dependency-no-requires-dist.test b/tests/installation/fixtures/with-wheel-dependency-no-requires-dist.test index a4b5fd40b87..383e43803eb 100644 --- a/tests/installation/fixtures/with-wheel-dependency-no-requires-dist.test +++ b/tests/installation/fixtures/with-wheel-dependency-no-requires-dist.test @@ -8,7 +8,6 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.source] type = "file" -reference = "" url = "tests/fixtures/wheel_with_no_requires_dist/demo-0.1.0-py2.py3-none-any.whl" [metadata] diff --git a/tests/installation/test_chooser.py b/tests/installation/test_chooser.py index 5a48202b288..cf3f931b942 100644 --- a/tests/installation/test_chooser.py +++ b/tests/installation/test_chooser.py @@ -89,9 +89,13 @@ def test_chooser_chooses_universal_wheel_link_if_available( package = Package("pytest", "3.5.0") if source_type == "legacy": - package.source_type = "legacy" - package.source_reference = "foo" - package.source_url = "https://foo.bar/simple/" + package = Package( + package.name, + package.version.text, + source_type="legacy", + source_reference="foo", + source_url="https://foo.bar/simple/", + ) link = chooser.choose_for(package) @@ -106,9 +110,13 @@ def test_chooser_chooses_specific_python_universal_wheel_link_if_available( package = Package("isort", "4.3.4") if source_type == "legacy": - package.source_type = "legacy" - package.source_reference = "foo" - package.source_url = "https://foo.bar/simple/" + package = Package( + package.name, + package.version.text, + source_type="legacy", + source_reference="foo", + source_url="https://foo.bar/simple/", + ) link = chooser.choose_for(package) @@ -126,9 +134,13 @@ def test_chooser_chooses_system_specific_wheel_link_if_available( package = Package("pyyaml", "3.13.0") if source_type == "legacy": - package.source_type = "legacy" - package.source_reference = "foo" - package.source_url = "https://foo.bar/simple/" + package = Package( + package.name, + package.version.text, + source_type="legacy", + source_reference="foo", + source_url="https://foo.bar/simple/", + ) link = chooser.choose_for(package) @@ -143,9 +155,13 @@ def test_chooser_chooses_sdist_if_no_compatible_wheel_link_is_available( package = Package("pyyaml", "3.13.0") if source_type == "legacy": - package.source_type = "legacy" - package.source_reference = "foo" - package.source_url = "https://foo.bar/simple/" + package = Package( + package.name, + package.version.text, + source_type="legacy", + source_reference="foo", + source_url="https://foo.bar/simple/", + ) link = chooser.choose_for(package) @@ -159,16 +175,22 @@ def test_chooser_chooses_distributions_that_match_the_package_hashes( chooser = Chooser(pool, env) package = Package("isort", "4.3.4") - package.files = [ + files = [ { "hash": "sha256:b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8", "filename": "isort-4.3.4.tar.gz", } ] if source_type == "legacy": - package.source_type = "legacy" - package.source_reference = "foo" - package.source_url = "https://foo.bar/simple/" + package = Package( + package.name, + package.version.text, + source_type="legacy", + source_reference="foo", + source_url="https://foo.bar/simple/", + ) + + package.files = files link = chooser.choose_for(package) diff --git a/tests/installation/test_executor.py b/tests/installation/test_executor.py index dfc813c9f87..bb659321d0f 100644 --- a/tests/installation/test_executor.py +++ b/tests/installation/test_executor.py @@ -65,26 +65,35 @@ def test_execute_executes_a_batch_of_operations( env = MockEnv(path=Path(tmp_dir)) executor = Executor(env, pool, config, io) - file_package = Package("demo", "0.1.0") - file_package.source_type = "file" - file_package.source_url = str( - Path(__file__) + file_package = Package( + "demo", + "0.1.0", + source_type="file", + source_url=Path(__file__) .parent.parent.joinpath( "fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl" ) .resolve() + .as_posix(), ) - directory_package = Package("simple-project", "1.2.3") - directory_package.source_type = "directory" - directory_package.source_url = str( - Path(__file__).parent.parent.joinpath("fixtures/simple_project").resolve() + directory_package = Package( + "simple-project", + "1.2.3", + source_type="directory", + source_url=Path(__file__) + .parent.parent.joinpath("fixtures/simple_project") + .resolve() + .as_posix(), ) - git_package = Package("demo", "0.1.0") - git_package.source_type = "git" - git_package.source_reference = "master" - git_package.source_url = "https://github.com/demo/demo.git" + git_package = Package( + "demo", + "0.1.0", + source_type="git", + source_reference="master", + source_url="https://github.com/demo/demo.git", + ) assert 0 == executor.execute( [ diff --git a/tests/installation/test_installer.py b/tests/installation/test_installer.py index 88103e7c323..25f3a97d88d 100644 --- a/tests/installation/test_installer.py +++ b/tests/installation/test_installer.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals +import json import sys import pytest @@ -7,6 +8,7 @@ from clikit.io import NullIO from poetry.core.packages import ProjectPackage +from poetry.factory import Factory from poetry.installation import Installer as BaseInstaller from poetry.installation.executor import Executor as BaseExecutor from poetry.installation.noop_installer import NoopInstaller @@ -79,6 +81,7 @@ def load(cls, env): class Locker(BaseLocker): def __init__(self): + self._lock = TomlFile(Path.cwd().joinpath("poetry.lock")) self._written_data = None self._locked = False self._content_hash = self._get_content_hash() @@ -87,6 +90,11 @@ def __init__(self): def written_data(self): return self._written_data + def set_lock_path(self, lock): + self._lock = TomlFile(Path(lock).joinpath("poetry.lock")) + + return self + def locked(self, is_locked=True): self._locked = is_locked @@ -118,7 +126,7 @@ def _write_lock_data(self, data): package["python-versions"] = python_versions - self._written_data = data + self._written_data = json.loads(json.dumps(data)) self._lock_data = data @@ -178,7 +186,7 @@ def installer(package, pool, locker, env, installed, config): def fixture(name): file = TomlFile(Path(__file__).parent / "fixtures" / "{}.test".format(name)) - return file.read() + return json.loads(json.dumps(file.read())) def test_run_no_dependencies(installer, locker): @@ -194,8 +202,8 @@ def test_run_with_dependencies(installer, locker, repo, package): repo.add_package(package_a) repo.add_package(package_b) - package.add_dependency("A", "~1.0") - package.add_dependency("B", "^1.0") + package.add_dependency(Factory.create_dependency("A", "~1.0")) + package.add_dependency(Factory.create_dependency("B", "^1.0")) installer.run() expected = fixture("with-dependencies") @@ -257,8 +265,8 @@ def test_run_update_after_removing_dependencies( installed.add_package(package_b) installed.add_package(package_c) - package.add_dependency("A", "~1.0") - package.add_dependency("B", "~1.1") + package.add_dependency(Factory.create_dependency("A", "~1.0")) + package.add_dependency(Factory.create_dependency("B", "~1.1")) installer.update(True) installer.run() @@ -323,9 +331,9 @@ def test_run_install_no_dev(installer, locker, repo, package, installed): installed.add_package(package_b) installed.add_package(package_c) - package.add_dependency("A", "~1.0") - package.add_dependency("B", "~1.1") - package.add_dependency("C", "~1.2", category="dev") + package.add_dependency(Factory.create_dependency("A", "~1.0")) + package.add_dependency(Factory.create_dependency("B", "~1.1")) + package.add_dependency(Factory.create_dependency("C", "~1.2", category="dev")) installer.dev_mode(False) installer.run() @@ -373,7 +381,7 @@ def test_run_install_remove_untracked(installer, locker, repo, package, installe installed.add_package(package_pip) # Always required and never removed. installed.add_package(package) # Root package never removed. - package.add_dependency("A", "~1.0") + package.add_dependency(Factory.create_dependency("A", "~1.0")) installer.dev_mode(True).remove_untracked(True) installer.run() @@ -414,8 +422,8 @@ def test_run_whitelist_add(installer, locker, repo, package): repo.add_package(package_a_new) repo.add_package(package_b) - package.add_dependency("A", "~1.0") - package.add_dependency("B", "^1.0") + package.add_dependency(Factory.create_dependency("A", "~1.0")) + package.add_dependency(Factory.create_dependency("B", "^1.0")) installer.update(True) installer.whitelist(["B"]) @@ -464,7 +472,7 @@ def test_run_whitelist_remove(installer, locker, repo, package, installed): repo.add_package(package_b) installed.add_package(package_b) - package.add_dependency("A", "~1.0") + package.add_dependency(Factory.create_dependency("A", "~1.0")) installer.update(True) installer.whitelist(["B"]) @@ -488,11 +496,11 @@ def test_add_with_sub_dependencies(installer, locker, repo, package): repo.add_package(package_c) repo.add_package(package_d) - package.add_dependency("A", "~1.0") - package.add_dependency("B", "^1.0") + package.add_dependency(Factory.create_dependency("A", "~1.0")) + package.add_dependency(Factory.create_dependency("B", "^1.0")) - package_a.add_dependency("D", "^1.0") - package_b.add_dependency("C", "~1.2") + package_a.add_dependency(Factory.create_dependency("D", "^1.0")) + package_b.add_dependency(Factory.create_dependency("C", "~1.2")) installer.run() expected = fixture("with-sub-dependencies") @@ -515,9 +523,9 @@ def test_run_with_python_versions(installer, locker, repo, package): repo.add_package(package_c12) repo.add_package(package_c13) - package.add_dependency("A", "~1.0") - package.add_dependency("B", "^1.0") - package.add_dependency("C", "^1.0") + package.add_dependency(Factory.create_dependency("A", "~1.0")) + package.add_dependency(Factory.create_dependency("B", "^1.0")) + package.add_dependency(Factory.create_dependency("C", "^1.0")) installer.run() expected = fixture("with-python-versions") @@ -535,7 +543,7 @@ def test_run_with_optional_and_python_restricted_dependencies( package_c12 = get_package("C", "1.2") package_c13 = get_package("C", "1.3") package_d = get_package("D", "1.4") - package_c13.add_dependency("D", "^1.2") + package_c13.add_dependency(Factory.create_dependency("D", "^1.2")) repo.add_package(package_a) repo.add_package(package_b) @@ -544,9 +552,15 @@ def test_run_with_optional_and_python_restricted_dependencies( repo.add_package(package_d) package.extras = {"foo": [get_dependency("A", "~1.0")]} - package.add_dependency("A", {"version": "~1.0", "optional": True}) - package.add_dependency("B", {"version": "^1.0", "python": "~2.4"}) - package.add_dependency("C", {"version": "^1.0", "python": "~2.7 || ^3.4"}) + package.add_dependency( + Factory.create_dependency("A", {"version": "~1.0", "optional": True}) + ) + package.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "python": "~2.4"}) + ) + package.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "python": "~2.7 || ^3.4"}) + ) installer.run() expected = fixture("with-optional-dependencies") @@ -571,7 +585,7 @@ def test_run_with_optional_and_platform_restricted_dependencies( package_c12 = get_package("C", "1.2") package_c13 = get_package("C", "1.3") package_d = get_package("D", "1.4") - package_c13.add_dependency("D", "^1.2") + package_c13.add_dependency(Factory.create_dependency("D", "^1.2")) repo.add_package(package_a) repo.add_package(package_b) @@ -580,9 +594,15 @@ def test_run_with_optional_and_platform_restricted_dependencies( repo.add_package(package_d) package.extras = {"foo": [get_dependency("A", "~1.0")]} - package.add_dependency("A", {"version": "~1.0", "optional": True}) - package.add_dependency("B", {"version": "^1.0", "platform": "custom"}) - package.add_dependency("C", {"version": "^1.0", "platform": "darwin"}) + package.add_dependency( + Factory.create_dependency("A", {"version": "~1.0", "optional": True}) + ) + package.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "platform": "custom"}) + ) + package.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "platform": "darwin"}) + ) installer.run() expected = fixture("with-platform-dependencies") @@ -603,14 +623,18 @@ def test_run_with_dependencies_extras(installer, locker, repo, package): package_c = get_package("C", "1.0") package_b.extras = {"foo": [get_dependency("C", "^1.0")]} - package_b.add_dependency("C", {"version": "^1.0", "optional": True}) + package_b.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "optional": True}) + ) repo.add_package(package_a) repo.add_package(package_b) repo.add_package(package_c) - package.add_dependency("A", "^1.0") - package.add_dependency("B", {"version": "^1.0", "extras": ["foo"]}) + package.add_dependency(Factory.create_dependency("A", "^1.0")) + package.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "extras": ["foo"]}) + ) installer.run() expected = fixture("with-dependencies-extras") @@ -630,10 +654,12 @@ def test_run_does_not_install_extras_if_not_requested(installer, locker, repo, p repo.add_package(package_c) repo.add_package(package_d) - package.add_dependency("A", "^1.0") - package.add_dependency("B", "^1.0") - package.add_dependency("C", "^1.0") - package.add_dependency("D", {"version": "^1.0", "optional": True}) + package.add_dependency(Factory.create_dependency("A", "^1.0")) + package.add_dependency(Factory.create_dependency("B", "^1.0")) + package.add_dependency(Factory.create_dependency("C", "^1.0")) + package.add_dependency( + Factory.create_dependency("D", {"version": "^1.0", "optional": True}) + ) installer.run() expected = fixture("extras") @@ -657,10 +683,12 @@ def test_run_installs_extras_if_requested(installer, locker, repo, package): repo.add_package(package_c) repo.add_package(package_d) - package.add_dependency("A", "^1.0") - package.add_dependency("B", "^1.0") - package.add_dependency("C", "^1.0") - package.add_dependency("D", {"version": "^1.0", "optional": True}) + package.add_dependency(Factory.create_dependency("A", "^1.0")) + package.add_dependency(Factory.create_dependency("B", "^1.0")) + package.add_dependency(Factory.create_dependency("C", "^1.0")) + package.add_dependency( + Factory.create_dependency("D", {"version": "^1.0", "optional": True}) + ) installer.extras(["foo"]) installer.run() @@ -685,11 +713,13 @@ def test_run_installs_extras_with_deps_if_requested(installer, locker, repo, pac repo.add_package(package_c) repo.add_package(package_d) - package.add_dependency("A", "^1.0") - package.add_dependency("B", "^1.0") - package.add_dependency("C", {"version": "^1.0", "optional": True}) + package.add_dependency(Factory.create_dependency("A", "^1.0")) + package.add_dependency(Factory.create_dependency("B", "^1.0")) + package.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "optional": True}) + ) - package_c.add_dependency("D", "^1.0") + package_c.add_dependency(Factory.create_dependency("D", "^1.0")) installer.extras(["foo"]) installer.run() @@ -718,11 +748,13 @@ def test_run_installs_extras_with_deps_if_requested_locked( repo.add_package(package_c) repo.add_package(package_d) - package.add_dependency("A", "^1.0") - package.add_dependency("B", "^1.0") - package.add_dependency("C", {"version": "^1.0", "optional": True}) + package.add_dependency(Factory.create_dependency("A", "^1.0")) + package.add_dependency(Factory.create_dependency("B", "^1.0")) + package.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "optional": True}) + ) - package_c.add_dependency("D", "^1.0") + package_c.add_dependency(Factory.create_dependency("D", "^1.0")) installer.extras(["foo"]) installer.run() @@ -739,7 +771,7 @@ def test_installer_with_pypi_repository(package, locker, installed, config): NullIO(), NullEnv(), package, locker, pool, config, installed=installed ) - package.add_dependency("pytest", "^3.5", category="dev") + package.add_dependency(Factory.create_dependency("pytest", "^3.5", category="dev")) installer.run() expected = fixture("with-pypi-repository") @@ -749,7 +781,7 @@ def test_installer_with_pypi_repository(package, locker, installed, config): def test_run_installs_with_local_file(installer, locker, repo, package): file_path = fixtures_dir / "distributions/demo-0.1.0-py2.py3-none-any.whl" - package.add_dependency("demo", {"file": str(file_path)}) + package.add_dependency(Factory.create_dependency("demo", {"file": str(file_path)})) repo.add_package(get_package("pendulum", "1.4.4")) @@ -758,7 +790,6 @@ def test_run_installs_with_local_file(installer, locker, repo, package): expected = fixture("with-file-dependency") assert locker.written_data == expected - assert 2 == installer.executor.installations_count @@ -766,7 +797,7 @@ def test_run_installs_wheel_with_no_requires_dist(installer, locker, repo, packa file_path = ( fixtures_dir / "wheel_with_no_requires_dist/demo-0.1.0-py2.py3-none-any.whl" ) - package.add_dependency("demo", {"file": str(file_path)}) + package.add_dependency(Factory.create_dependency("demo", {"file": str(file_path)})) installer.run() @@ -782,8 +813,11 @@ def test_run_installs_with_local_poetry_directory_and_extras( ): file_path = fixtures_dir / "project_with_extras" package.add_dependency( - "project-with-extras", {"path": str(file_path), "extras": ["extras_a"]} + Factory.create_dependency( + "project-with-extras", {"path": str(file_path), "extras": ["extras_a"]} + ) ) + print(package.requires[0].develop) repo.add_package(get_package("pendulum", "1.4.4")) @@ -799,13 +833,16 @@ def test_run_installs_with_local_poetry_directory_and_extras( def test_run_installs_with_local_poetry_directory_transitive( installer, locker, repo, package, tmpdir ): - package.root_dir = fixtures_dir.joinpath("directory") - directory = fixtures_dir.joinpath("directory").joinpath( - "project_with_transitive_directory_dependencies" - ) + root_dir = fixtures_dir.joinpath("directory") + package.root_dir = root_dir + locker.set_lock_path(root_dir) + directory = root_dir.joinpath("project_with_transitive_directory_dependencies") package.add_dependency( - "project-with-transitive-directory-dependencies", - {"path": str(directory.relative_to(fixtures_dir.joinpath("directory")))}, + Factory.create_dependency( + "project-with-transitive-directory-dependencies", + {"path": str(directory.relative_to(root_dir))}, + root_dir=root_dir, + ) ) repo.add_package(get_package("pendulum", "1.4.4")) @@ -823,13 +860,18 @@ def test_run_installs_with_local_poetry_directory_transitive( def test_run_installs_with_local_poetry_file_transitive( installer, locker, repo, package, tmpdir ): - package.root_dir = fixtures_dir.joinpath("directory") + root_dir = fixtures_dir.joinpath("directory") + package.root_dir = root_dir + locker.set_lock_path(root_dir) directory = fixtures_dir.joinpath("directory").joinpath( "project_with_transitive_file_dependencies" ) package.add_dependency( - "project-with-transitive-file-dependencies", - {"path": str(directory.relative_to(fixtures_dir.joinpath("directory")))}, + Factory.create_dependency( + "project-with-transitive-file-dependencies", + {"path": str(directory.relative_to(root_dir))}, + root_dir=root_dir, + ) ) repo.add_package(get_package("pendulum", "1.4.4")) @@ -848,7 +890,9 @@ def test_run_installs_with_local_setuptools_directory( installer, locker, repo, package, tmpdir ): file_path = fixtures_dir / "project_with_setup/" - package.add_dependency("my-package", {"path": str(file_path)}) + package.add_dependency( + Factory.create_dependency("my-package", {"path": str(file_path)}) + ) repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("cachy", "0.2.0")) @@ -858,7 +902,6 @@ def test_run_installs_with_local_setuptools_directory( expected = fixture("with-directory-dependency-setuptools") assert locker.written_data == expected - assert 3 == installer.executor.installations_count @@ -890,8 +933,10 @@ def test_run_with_prereleases(installer, locker, repo, package): repo.add_package(package_a) repo.add_package(package_b) - package.add_dependency("A", {"version": "*", "allow-prereleases": True}) - package.add_dependency("B", "^1.1") + package.add_dependency( + Factory.create_dependency("A", {"version": "*", "allow-prereleases": True}) + ) + package.add_dependency(Factory.create_dependency("B", "^1.1")) installer.update(True) installer.whitelist({"B": "^1.1"}) @@ -927,12 +972,16 @@ def test_run_changes_category_if_needed(installer, locker, repo, package): ) package_a = get_package("A", "1.0") package_b = get_package("B", "1.1") - package_b.add_dependency("A", "^1.0") + package_b.add_dependency(Factory.create_dependency("A", "^1.0")) repo.add_package(package_a) repo.add_package(package_b) - package.add_dependency("A", {"version": "^1.0", "optional": True}, category="dev") - package.add_dependency("B", "^1.1") + package.add_dependency( + Factory.create_dependency( + "A", {"version": "^1.0", "optional": True}, category="dev" + ) + ) + package.add_dependency(Factory.create_dependency("B", "^1.1")) installer.update(True) installer.whitelist(["B"]) @@ -970,7 +1019,7 @@ def test_run_update_all_with_lock(installer, locker, repo, package): repo.add_package(get_package("A", "1.0")) repo.add_package(package_a) - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) installer.update(True) @@ -1037,8 +1086,10 @@ def test_run_update_with_locked_extras(installer, locker, repo, package): repo.add_package(get_package("C", "1.1")) repo.add_package(get_package("D", "1.1")) - package.add_dependency("A", {"version": "^1.0", "extras": ["foo"]}) - package.add_dependency("D", "^1.0") + package.add_dependency( + Factory.create_dependency("A", {"version": "^1.0", "extras": ["foo"]}) + ) + package.add_dependency(Factory.create_dependency("D", "^1.0")) installer.update(True) installer.whitelist("D") @@ -1052,16 +1103,20 @@ def test_run_update_with_locked_extras(installer, locker, repo, package): def test_run_install_duplicate_dependencies_different_constraints( installer, locker, repo, package ): - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") - package_a.add_dependency("B", {"version": "^1.0", "python": "<4.0"}) - package_a.add_dependency("B", {"version": "^2.0", "python": ">=4.0"}) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "python": "<4.0"}) + ) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^2.0", "python": ">=4.0"}) + ) package_b10 = get_package("B", "1.0") package_b20 = get_package("B", "2.0") - package_b10.add_dependency("C", "1.2") - package_b20.add_dependency("C", "1.5") + package_b10.add_dependency(Factory.create_dependency("C", "1.2")) + package_b20.add_dependency(Factory.create_dependency("C", "1.5")) package_c12 = get_package("C", "1.2") package_c15 = get_package("C", "1.5") @@ -1159,16 +1214,20 @@ def test_run_install_duplicate_dependencies_different_constraints_with_lock( }, } ) - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") - package_a.add_dependency("B", {"version": "^1.0", "python": "<4.0"}) - package_a.add_dependency("B", {"version": "^2.0", "python": ">=4.0"}) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "python": "<4.0"}) + ) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^2.0", "python": ">=4.0"}) + ) package_b10 = get_package("B", "1.0") package_b20 = get_package("B", "2.0") - package_b10.add_dependency("C", "1.2") - package_b20.add_dependency("C", "1.5") + package_b10.add_dependency(Factory.create_dependency("C", "1.2")) + package_b20.add_dependency(Factory.create_dependency("C", "1.5")) package_c12 = get_package("C", "1.2") package_c15 = get_package("C", "1.5") @@ -1226,10 +1285,12 @@ def test_run_update_uninstalls_after_removal_transient_dependency( }, } ) - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") - package_a.add_dependency("B", {"version": "^1.0", "python": "<2.0"}) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "python": "<2.0"}) + ) package_b10 = get_package("B", "1.0") @@ -1318,15 +1379,15 @@ def test_run_install_duplicate_dependencies_different_constraints_with_lock_upda }, } ) - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.1") - package_a.add_dependency("B", "^2.0") + package_a.add_dependency(Factory.create_dependency("B", "^2.0")) package_b10 = get_package("B", "1.0") package_b20 = get_package("B", "2.0") - package_b10.add_dependency("C", "1.2") - package_b20.add_dependency("C", "1.5") + package_b10.add_dependency(Factory.create_dependency("C", "1.2")) + package_b20.add_dependency(Factory.create_dependency("C", "1.5")) package_c12 = get_package("C", "1.2") package_c15 = get_package("C", "1.5") @@ -1359,7 +1420,9 @@ def test_installer_test_solver_finds_compatible_package_for_dependency_python_no installer, locker, repo, package, installed ): package.python_versions = "~2.7 || ^3.4" - package.add_dependency("A", {"version": "^1.0", "python": "^3.5"}) + package.add_dependency( + Factory.create_dependency("A", {"version": "^1.0", "python": "^3.5"}) + ) package_a101 = get_package("A", "1.0.1") package_a101.python_versions = ">=3.6" @@ -1384,13 +1447,17 @@ def test_installer_test_solver_finds_compatible_package_for_dependency_python_no def test_installer_required_extras_should_not_be_removed_when_updating_single_dependency( installer, locker, repo, package, installed, env, pool, config ): - package.add_dependency("A", {"version": "^1.0"}) + package.add_dependency(Factory.create_dependency("A", {"version": "^1.0"})) package_a = get_package("A", "1.0.0") - package_a.add_dependency("B", {"version": "^1.0", "extras": ["foo"]}) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "extras": ["foo"]}) + ) package_b = get_package("B", "1.0.0") - package_b.add_dependency("C", {"version": "^1.0", "optional": True}) + package_b.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "optional": True}) + ) package_b.extras = {"foo": [get_dependency("C")]} package_c = get_package("C", "1.0.0") @@ -1408,7 +1475,7 @@ def test_installer_required_extras_should_not_be_removed_when_updating_single_de assert 0 == installer.executor.updates_count assert 0 == installer.executor.removals_count - package.add_dependency("D", "^1.0") + package.add_dependency(Factory.create_dependency("D", "^1.0")) locker.locked(True) locker.mock_lock_data(locker.written_data) @@ -1457,7 +1524,7 @@ def test_installer_required_extras_should_not_be_removed_when_updating_single_de ) installer.use_executor() - package.add_dependency("poetry", {"version": "^0.12.0"}) + package.add_dependency(Factory.create_dependency("poetry", {"version": "^0.12.0"})) installer.update(True) installer.run() @@ -1466,7 +1533,7 @@ def test_installer_required_extras_should_not_be_removed_when_updating_single_de assert 0 == installer.executor.updates_count assert 0 == installer.executor.removals_count - package.add_dependency("pytest", "^3.5") + package.add_dependency(Factory.create_dependency("pytest", "^3.5")) locker.locked(True) locker.mock_lock_data(locker.written_data) @@ -1514,7 +1581,9 @@ def test_installer_required_extras_should_be_installed( installer.use_executor() package.add_dependency( - "cachecontrol", {"version": "^0.12.5", "extras": ["filecache"]} + Factory.create_dependency( + "cachecontrol", {"version": "^0.12.5", "extras": ["filecache"]} + ) ) installer.update(True) @@ -1585,13 +1654,17 @@ def test_update_multiple_times_with_split_dependencies_is_idempotent( ) package.python_versions = "~2.7 || ^3.4" - package.add_dependency("A", "^1.0") + package.add_dependency(Factory.create_dependency("A", "^1.0")) a10 = get_package("A", "1.0") a11 = get_package("A", "1.1") - a11.add_dependency("B", ">=1.0.1") - a11.add_dependency("C", {"version": "^1.0", "python": "~2.7"}) - a11.add_dependency("C", {"version": "^2.0", "python": "^3.4"}) + a11.add_dependency(Factory.create_dependency("B", ">=1.0.1")) + a11.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "python": "~2.7"}) + ) + a11.add_dependency( + Factory.create_dependency("C", {"version": "^2.0", "python": "^3.4"}) + ) b101 = get_package("B", "1.0.1") b110 = get_package("B", "1.1.0") repo.add_package(a10) @@ -1626,7 +1699,9 @@ def test_installer_can_install_dependencies_from_forced_source( locker, package, installed, env, config ): package.python_versions = "^3.7" - package.add_dependency("tomlkit", {"version": "^0.5", "source": "legacy"}) + package.add_dependency( + Factory.create_dependency("tomlkit", {"version": "^0.5", "source": "legacy"}) + ) pool = Pool() pool.add_repository(MockLegacyRepository()) @@ -1654,7 +1729,7 @@ def test_installer_can_install_dependencies_from_forced_source( def test_run_installs_with_url_file(installer, locker, repo, package): url = "https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl" - package.add_dependency("demo", {"url": url}) + package.add_dependency(Factory.create_dependency("demo", {"url": url})) repo.add_package(get_package("pendulum", "1.4.4")) @@ -1672,11 +1747,13 @@ def test_installer_uses_prereleases_if_they_are_compatible( ): package.python_versions = "~2.7 || ^3.4" package.add_dependency( - "prerelease", {"git": "https://github.com/demo/prerelease.git"} + Factory.create_dependency( + "prerelease", {"git": "https://github.com/demo/prerelease.git"} + ) ) package_b = get_package("b", "2.0.0") - package_b.add_dependency("prerelease", ">=0.19") + package_b.add_dependency(Factory.create_dependency("prerelease", ">=0.19")) repo.add_package(package_b) @@ -1686,7 +1763,7 @@ def test_installer_uses_prereleases_if_they_are_compatible( locker.locked(True) locker.mock_lock_data(locker.written_data) - package.add_dependency("b", "^2.0.0") + package.add_dependency(Factory.create_dependency("b", "^2.0.0")) installer.whitelist(["b"]) installer.update(True) @@ -1701,7 +1778,7 @@ def test_installer_can_handle_old_lock_files( pool = Pool() pool.add_repository(MockRepository()) - package.add_dependency("pytest", "^3.5", category="dev") + package.add_dependency(Factory.create_dependency("pytest", "^3.5", category="dev")) locker.locked() locker.mock_lock_data(fixture("old-lock")) @@ -1714,7 +1791,7 @@ def test_installer_can_handle_old_lock_files( pool, config, installed=installed, - executor=Executor(MockEnv(), pool, config, NullIO(),), + executor=Executor(MockEnv(), pool, config, NullIO()), ) installer.use_executor() diff --git a/tests/installation/test_installer_old.py b/tests/installation/test_installer_old.py index 3aab6831526..0af0ee2a587 100644 --- a/tests/installation/test_installer_old.py +++ b/tests/installation/test_installer_old.py @@ -7,6 +7,7 @@ from clikit.io import NullIO from poetry.core.packages import ProjectPackage +from poetry.factory import Factory from poetry.installation import Installer as BaseInstaller from poetry.installation.noop_installer import NoopInstaller from poetry.packages import Locker as BaseLocker @@ -42,6 +43,7 @@ def load(cls, env): class Locker(BaseLocker): def __init__(self): + self._lock = TomlFile(Path.cwd().joinpath("poetry.lock")) self._written_data = None self._locked = False self._content_hash = self._get_content_hash() @@ -50,6 +52,11 @@ def __init__(self): def written_data(self): return self._written_data + def set_lock_path(self, lock): + self._lock = TomlFile(Path(lock).joinpath("poetry.lock")) + + return self + def locked(self, is_locked=True): self._locked = is_locked @@ -145,8 +152,8 @@ def test_run_with_dependencies(installer, locker, repo, package): repo.add_package(package_a) repo.add_package(package_b) - package.add_dependency("A", "~1.0") - package.add_dependency("B", "^1.0") + package.add_dependency(Factory.create_dependency("A", "~1.0")) + package.add_dependency(Factory.create_dependency("B", "^1.0")) installer.run() expected = fixture("with-dependencies") @@ -208,8 +215,8 @@ def test_run_update_after_removing_dependencies( installed.add_package(package_b) installed.add_package(package_c) - package.add_dependency("A", "~1.0") - package.add_dependency("B", "~1.1") + package.add_dependency(Factory.create_dependency("A", "~1.0")) + package.add_dependency(Factory.create_dependency("B", "~1.1")) installer.update(True) installer.run() @@ -279,9 +286,9 @@ def test_run_install_no_dev(installer, locker, repo, package, installed): installed.add_package(package_b) installed.add_package(package_c) - package.add_dependency("A", "~1.0") - package.add_dependency("B", "~1.1") - package.add_dependency("C", "~1.2", category="dev") + package.add_dependency(Factory.create_dependency("A", "~1.0")) + package.add_dependency(Factory.create_dependency("B", "~1.1")) + package.add_dependency(Factory.create_dependency("C", "~1.2", category="dev")) installer.dev_mode(False) installer.run() @@ -334,7 +341,7 @@ def test_run_install_remove_untracked(installer, locker, repo, package, installe installed.add_package(package_pip) # Always required and never removed. installed.add_package(package) # Root package never removed. - package.add_dependency("A", "~1.0") + package.add_dependency(Factory.create_dependency("A", "~1.0")) installer.dev_mode(True).remove_untracked(True) installer.run() @@ -379,8 +386,8 @@ def test_run_whitelist_add(installer, locker, repo, package): repo.add_package(package_a_new) repo.add_package(package_b) - package.add_dependency("A", "~1.0") - package.add_dependency("B", "^1.0") + package.add_dependency(Factory.create_dependency("A", "~1.0")) + package.add_dependency(Factory.create_dependency("B", "^1.0")) installer.update(True) installer.whitelist(["B"]) @@ -429,7 +436,7 @@ def test_run_whitelist_remove(installer, locker, repo, package, installed): repo.add_package(package_b) installed.add_package(package_b) - package.add_dependency("A", "~1.0") + package.add_dependency(Factory.create_dependency("A", "~1.0")) installer.update(True) installer.whitelist(["B"]) @@ -453,11 +460,11 @@ def test_add_with_sub_dependencies(installer, locker, repo, package): repo.add_package(package_c) repo.add_package(package_d) - package.add_dependency("A", "~1.0") - package.add_dependency("B", "^1.0") + package.add_dependency(Factory.create_dependency("A", "~1.0")) + package.add_dependency(Factory.create_dependency("B", "^1.0")) - package_a.add_dependency("D", "^1.0") - package_b.add_dependency("C", "~1.2") + package_a.add_dependency(Factory.create_dependency("D", "^1.0")) + package_b.add_dependency(Factory.create_dependency("C", "~1.2")) installer.run() expected = fixture("with-sub-dependencies") @@ -480,9 +487,9 @@ def test_run_with_python_versions(installer, locker, repo, package): repo.add_package(package_c12) repo.add_package(package_c13) - package.add_dependency("A", "~1.0") - package.add_dependency("B", "^1.0") - package.add_dependency("C", "^1.0") + package.add_dependency(Factory.create_dependency("A", "~1.0")) + package.add_dependency(Factory.create_dependency("B", "^1.0")) + package.add_dependency(Factory.create_dependency("C", "^1.0")) installer.run() expected = fixture("with-python-versions") @@ -500,7 +507,7 @@ def test_run_with_optional_and_python_restricted_dependencies( package_c12 = get_package("C", "1.2") package_c13 = get_package("C", "1.3") package_d = get_package("D", "1.4") - package_c13.add_dependency("D", "^1.2") + package_c13.add_dependency(Factory.create_dependency("D", "^1.2")) repo.add_package(package_a) repo.add_package(package_b) @@ -509,9 +516,15 @@ def test_run_with_optional_and_python_restricted_dependencies( repo.add_package(package_d) package.extras = {"foo": [get_dependency("A", "~1.0")]} - package.add_dependency("A", {"version": "~1.0", "optional": True}) - package.add_dependency("B", {"version": "^1.0", "python": "~2.4"}) - package.add_dependency("C", {"version": "^1.0", "python": "~2.7 || ^3.4"}) + package.add_dependency( + Factory.create_dependency("A", {"version": "~1.0", "optional": True}) + ) + package.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "python": "~2.4"}) + ) + package.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "python": "~2.7 || ^3.4"}) + ) installer.run() expected = fixture("with-optional-dependencies") @@ -537,7 +550,7 @@ def test_run_with_optional_and_platform_restricted_dependencies( package_c12 = get_package("C", "1.2") package_c13 = get_package("C", "1.3") package_d = get_package("D", "1.4") - package_c13.add_dependency("D", "^1.2") + package_c13.add_dependency(Factory.create_dependency("D", "^1.2")) repo.add_package(package_a) repo.add_package(package_b) @@ -546,9 +559,15 @@ def test_run_with_optional_and_platform_restricted_dependencies( repo.add_package(package_d) package.extras = {"foo": [get_dependency("A", "~1.0")]} - package.add_dependency("A", {"version": "~1.0", "optional": True}) - package.add_dependency("B", {"version": "^1.0", "platform": "custom"}) - package.add_dependency("C", {"version": "^1.0", "platform": "darwin"}) + package.add_dependency( + Factory.create_dependency("A", {"version": "~1.0", "optional": True}) + ) + package.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "platform": "custom"}) + ) + package.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "platform": "darwin"}) + ) installer.run() expected = fixture("with-platform-dependencies") @@ -570,14 +589,18 @@ def test_run_with_dependencies_extras(installer, locker, repo, package): package_c = get_package("C", "1.0") package_b.extras = {"foo": [get_dependency("C", "^1.0")]} - package_b.add_dependency("C", {"version": "^1.0", "optional": True}) + package_b.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "optional": True}) + ) repo.add_package(package_a) repo.add_package(package_b) repo.add_package(package_c) - package.add_dependency("A", "^1.0") - package.add_dependency("B", {"version": "^1.0", "extras": ["foo"]}) + package.add_dependency(Factory.create_dependency("A", "^1.0")) + package.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "extras": ["foo"]}) + ) installer.run() expected = fixture("with-dependencies-extras") @@ -597,10 +620,12 @@ def test_run_does_not_install_extras_if_not_requested(installer, locker, repo, p repo.add_package(package_c) repo.add_package(package_d) - package.add_dependency("A", "^1.0") - package.add_dependency("B", "^1.0") - package.add_dependency("C", "^1.0") - package.add_dependency("D", {"version": "^1.0", "optional": True}) + package.add_dependency(Factory.create_dependency("A", "^1.0")) + package.add_dependency(Factory.create_dependency("B", "^1.0")) + package.add_dependency(Factory.create_dependency("C", "^1.0")) + package.add_dependency( + Factory.create_dependency("D", {"version": "^1.0", "optional": True}) + ) installer.run() expected = fixture("extras") @@ -625,10 +650,12 @@ def test_run_installs_extras_if_requested(installer, locker, repo, package): repo.add_package(package_c) repo.add_package(package_d) - package.add_dependency("A", "^1.0") - package.add_dependency("B", "^1.0") - package.add_dependency("C", "^1.0") - package.add_dependency("D", {"version": "^1.0", "optional": True}) + package.add_dependency(Factory.create_dependency("A", "^1.0")) + package.add_dependency(Factory.create_dependency("B", "^1.0")) + package.add_dependency(Factory.create_dependency("C", "^1.0")) + package.add_dependency( + Factory.create_dependency("D", {"version": "^1.0", "optional": True}) + ) installer.extras(["foo"]) installer.run() @@ -654,11 +681,13 @@ def test_run_installs_extras_with_deps_if_requested(installer, locker, repo, pac repo.add_package(package_c) repo.add_package(package_d) - package.add_dependency("A", "^1.0") - package.add_dependency("B", "^1.0") - package.add_dependency("C", {"version": "^1.0", "optional": True}) + package.add_dependency(Factory.create_dependency("A", "^1.0")) + package.add_dependency(Factory.create_dependency("B", "^1.0")) + package.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "optional": True}) + ) - package_c.add_dependency("D", "^1.0") + package_c.add_dependency(Factory.create_dependency("D", "^1.0")) installer.extras(["foo"]) installer.run() @@ -688,11 +717,13 @@ def test_run_installs_extras_with_deps_if_requested_locked( repo.add_package(package_c) repo.add_package(package_d) - package.add_dependency("A", "^1.0") - package.add_dependency("B", "^1.0") - package.add_dependency("C", {"version": "^1.0", "optional": True}) + package.add_dependency(Factory.create_dependency("A", "^1.0")) + package.add_dependency(Factory.create_dependency("B", "^1.0")) + package.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "optional": True}) + ) - package_c.add_dependency("D", "^1.0") + package_c.add_dependency(Factory.create_dependency("D", "^1.0")) installer.extras(["foo"]) installer.run() @@ -710,7 +741,7 @@ def test_installer_with_pypi_repository(package, locker, installed, config): NullIO(), NullEnv(), package, locker, pool, config, installed=installed ) - package.add_dependency("pytest", "^3.5", category="dev") + package.add_dependency(Factory.create_dependency("pytest", "^3.5", category="dev")) installer.run() expected = fixture("with-pypi-repository") @@ -720,7 +751,7 @@ def test_installer_with_pypi_repository(package, locker, installed, config): def test_run_installs_with_local_file(installer, locker, repo, package): file_path = fixtures_dir / "distributions/demo-0.1.0-py2.py3-none-any.whl" - package.add_dependency("demo", {"file": str(file_path)}) + package.add_dependency(Factory.create_dependency("demo", {"file": str(file_path)})) repo.add_package(get_package("pendulum", "1.4.4")) @@ -737,7 +768,7 @@ def test_run_installs_wheel_with_no_requires_dist(installer, locker, repo, packa file_path = ( fixtures_dir / "wheel_with_no_requires_dist/demo-0.1.0-py2.py3-none-any.whl" ) - package.add_dependency("demo", {"file": str(file_path)}) + package.add_dependency(Factory.create_dependency("demo", {"file": str(file_path)})) installer.run() @@ -753,7 +784,9 @@ def test_run_installs_with_local_poetry_directory_and_extras( ): file_path = fixtures_dir / "project_with_extras" package.add_dependency( - "project-with-extras", {"path": str(file_path), "extras": ["extras_a"]} + Factory.create_dependency( + "project-with-extras", {"path": str(file_path), "extras": ["extras_a"]} + ) ) repo.add_package(get_package("pendulum", "1.4.4")) @@ -770,13 +803,16 @@ def test_run_installs_with_local_poetry_directory_and_extras( def test_run_installs_with_local_poetry_directory_transitive( installer, locker, repo, package, tmpdir ): - package.root_dir = fixtures_dir.joinpath("directory") - directory = fixtures_dir.joinpath("directory").joinpath( - "project_with_transitive_directory_dependencies" - ) + root_dir = fixtures_dir.joinpath("directory") + package.root_dir = root_dir + locker.set_lock_path(root_dir) + directory = root_dir.joinpath("project_with_transitive_directory_dependencies") package.add_dependency( - "project-with-transitive-directory-dependencies", - {"path": str(directory.relative_to(fixtures_dir.joinpath("directory")))}, + Factory.create_dependency( + "project-with-transitive-directory-dependencies", + {"path": str(directory.relative_to(root_dir))}, + root_dir=root_dir, + ) ) repo.add_package(get_package("pendulum", "1.4.4")) @@ -794,13 +830,16 @@ def test_run_installs_with_local_poetry_directory_transitive( def test_run_installs_with_local_poetry_file_transitive( installer, locker, repo, package, tmpdir ): - package.root_dir = fixtures_dir.joinpath("directory") - directory = fixtures_dir.joinpath("directory").joinpath( - "project_with_transitive_file_dependencies" - ) + root_dir = fixtures_dir.joinpath("directory") + package.root_dir = root_dir + locker.set_lock_path(root_dir) + directory = root_dir.joinpath("project_with_transitive_file_dependencies") package.add_dependency( - "project-with-transitive-file-dependencies", - {"path": str(directory.relative_to(fixtures_dir.joinpath("directory")))}, + Factory.create_dependency( + "project-with-transitive-file-dependencies", + {"path": str(directory.relative_to(fixtures_dir.joinpath("directory")))}, + root_dir=root_dir, + ) ) repo.add_package(get_package("pendulum", "1.4.4")) @@ -819,7 +858,9 @@ def test_run_installs_with_local_setuptools_directory( installer, locker, repo, package, tmpdir ): file_path = fixtures_dir / "project_with_setup/" - package.add_dependency("my-package", {"path": str(file_path)}) + package.add_dependency( + Factory.create_dependency("my-package", {"path": str(file_path)}) + ) repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("cachy", "0.2.0")) @@ -861,8 +902,10 @@ def test_run_with_prereleases(installer, locker, repo, package): repo.add_package(package_a) repo.add_package(package_b) - package.add_dependency("A", {"version": "*", "allow-prereleases": True}) - package.add_dependency("B", "^1.1") + package.add_dependency( + Factory.create_dependency("A", {"version": "*", "allow-prereleases": True}) + ) + package.add_dependency(Factory.create_dependency("B", "^1.1")) installer.update(True) installer.whitelist({"B": "^1.1"}) @@ -898,12 +941,16 @@ def test_run_changes_category_if_needed(installer, locker, repo, package): ) package_a = get_package("A", "1.0") package_b = get_package("B", "1.1") - package_b.add_dependency("A", "^1.0") + package_b.add_dependency(Factory.create_dependency("A", "^1.0")) repo.add_package(package_a) repo.add_package(package_b) - package.add_dependency("A", {"version": "^1.0", "optional": True}, category="dev") - package.add_dependency("B", "^1.1") + package.add_dependency( + Factory.create_dependency( + "A", {"version": "^1.0", "optional": True}, category="dev" + ) + ) + package.add_dependency(Factory.create_dependency("B", "^1.1")) installer.update(True) installer.whitelist(["B"]) @@ -941,7 +988,7 @@ def test_run_update_all_with_lock(installer, locker, repo, package): repo.add_package(get_package("A", "1.0")) repo.add_package(package_a) - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) installer.update(True) @@ -1008,8 +1055,10 @@ def test_run_update_with_locked_extras(installer, locker, repo, package): repo.add_package(get_package("C", "1.1")) repo.add_package(get_package("D", "1.1")) - package.add_dependency("A", {"version": "^1.0", "extras": ["foo"]}) - package.add_dependency("D", "^1.0") + package.add_dependency( + Factory.create_dependency("A", {"version": "^1.0", "extras": ["foo"]}) + ) + package.add_dependency(Factory.create_dependency("D", "^1.0")) installer.update(True) installer.whitelist("D") @@ -1023,16 +1072,20 @@ def test_run_update_with_locked_extras(installer, locker, repo, package): def test_run_install_duplicate_dependencies_different_constraints( installer, locker, repo, package ): - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") - package_a.add_dependency("B", {"version": "^1.0", "python": "<4.0"}) - package_a.add_dependency("B", {"version": "^2.0", "python": ">=4.0"}) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "python": "<4.0"}) + ) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^2.0", "python": ">=4.0"}) + ) package_b10 = get_package("B", "1.0") package_b20 = get_package("B", "2.0") - package_b10.add_dependency("C", "1.2") - package_b20.add_dependency("C", "1.5") + package_b10.add_dependency(Factory.create_dependency("C", "1.2")) + package_b20.add_dependency(Factory.create_dependency("C", "1.5")) package_c12 = get_package("C", "1.2") package_c15 = get_package("C", "1.5") @@ -1132,16 +1185,20 @@ def test_run_install_duplicate_dependencies_different_constraints_with_lock( }, } ) - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") - package_a.add_dependency("B", {"version": "^1.0", "python": "<4.0"}) - package_a.add_dependency("B", {"version": "^2.0", "python": ">=4.0"}) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "python": "<4.0"}) + ) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^2.0", "python": ">=4.0"}) + ) package_b10 = get_package("B", "1.0") package_b20 = get_package("B", "2.0") - package_b10.add_dependency("C", "1.2") - package_b20.add_dependency("C", "1.5") + package_b10.add_dependency(Factory.create_dependency("C", "1.2")) + package_b20.add_dependency(Factory.create_dependency("C", "1.5")) package_c12 = get_package("C", "1.2") package_c15 = get_package("C", "1.5") @@ -1202,10 +1259,12 @@ def test_run_update_uninstalls_after_removal_transient_dependency( }, } ) - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") - package_a.add_dependency("B", {"version": "^1.0", "python": "<2.0"}) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "python": "<2.0"}) + ) package_b10 = get_package("B", "1.0") @@ -1297,15 +1356,15 @@ def test_run_install_duplicate_dependencies_different_constraints_with_lock_upda }, } ) - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.1") - package_a.add_dependency("B", "^2.0") + package_a.add_dependency(Factory.create_dependency("B", "^2.0")) package_b10 = get_package("B", "1.0") package_b20 = get_package("B", "2.0") - package_b10.add_dependency("C", "1.2") - package_b20.add_dependency("C", "1.5") + package_b10.add_dependency(Factory.create_dependency("C", "1.2")) + package_b20.add_dependency(Factory.create_dependency("C", "1.5")) package_c12 = get_package("C", "1.2") package_c15 = get_package("C", "1.5") @@ -1341,7 +1400,9 @@ def test_installer_test_solver_finds_compatible_package_for_dependency_python_no installer, locker, repo, package, installed ): package.python_versions = "~2.7 || ^3.4" - package.add_dependency("A", {"version": "^1.0", "python": "^3.5"}) + package.add_dependency( + Factory.create_dependency("A", {"version": "^1.0", "python": "^3.5"}) + ) package_a101 = get_package("A", "1.0.1") package_a101.python_versions = ">=3.6" @@ -1368,13 +1429,17 @@ def test_installer_test_solver_finds_compatible_package_for_dependency_python_no def test_installer_required_extras_should_not_be_removed_when_updating_single_dependency( installer, locker, repo, package, installed, env, pool, config ): - package.add_dependency("A", {"version": "^1.0"}) + package.add_dependency(Factory.create_dependency("A", {"version": "^1.0"})) package_a = get_package("A", "1.0.0") - package_a.add_dependency("B", {"version": "^1.0", "extras": ["foo"]}) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "extras": ["foo"]}) + ) package_b = get_package("B", "1.0.0") - package_b.add_dependency("C", {"version": "^1.0", "optional": True}) + package_b.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "optional": True}) + ) package_b.extras = {"foo": [get_dependency("C")]} package_c = get_package("C", "1.0.0") @@ -1392,7 +1457,7 @@ def test_installer_required_extras_should_not_be_removed_when_updating_single_de assert len(installer.installer.updates) == 0 assert len(installer.installer.removals) == 0 - package.add_dependency("D", "^1.0") + package.add_dependency(Factory.create_dependency("D", "^1.0")) locker.locked(True) locker.mock_lock_data(locker.written_data) @@ -1425,7 +1490,7 @@ def test_installer_required_extras_should_not_be_removed_when_updating_single_de NullIO(), env, package, locker, pool, config, installed=installed ) - package.add_dependency("poetry", {"version": "^0.12.0"}) + package.add_dependency(Factory.create_dependency("poetry", {"version": "^0.12.0"})) installer.update(True) installer.run() @@ -1434,7 +1499,7 @@ def test_installer_required_extras_should_not_be_removed_when_updating_single_de assert len(installer.installer.updates) == 0 assert len(installer.installer.removals) == 0 - package.add_dependency("pytest", "^3.5") + package.add_dependency(Factory.create_dependency("pytest", "^3.5")) locker.locked(True) locker.mock_lock_data(locker.written_data) @@ -1466,7 +1531,9 @@ def test_installer_required_extras_should_be_installed( ) package.add_dependency( - "cachecontrol", {"version": "^0.12.5", "extras": ["filecache"]} + Factory.create_dependency( + "cachecontrol", {"version": "^0.12.5", "extras": ["filecache"]} + ) ) installer.update(True) @@ -1529,13 +1596,17 @@ def test_update_multiple_times_with_split_dependencies_is_idempotent( ) package.python_versions = "~2.7 || ^3.4" - package.add_dependency("A", "^1.0") + package.add_dependency(Factory.create_dependency("A", "^1.0")) a10 = get_package("A", "1.0") a11 = get_package("A", "1.1") - a11.add_dependency("B", ">=1.0.1") - a11.add_dependency("C", {"version": "^1.0", "python": "~2.7"}) - a11.add_dependency("C", {"version": "^2.0", "python": "^3.4"}) + a11.add_dependency(Factory.create_dependency("B", ">=1.0.1")) + a11.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "python": "~2.7"}) + ) + a11.add_dependency( + Factory.create_dependency("C", {"version": "^2.0", "python": "^3.4"}) + ) b101 = get_package("B", "1.0.1") b110 = get_package("B", "1.1.0") repo.add_package(a10) @@ -1570,7 +1641,9 @@ def test_installer_can_install_dependencies_from_forced_source( locker, package, installed, env, config ): package.python_versions = "^3.7" - package.add_dependency("tomlkit", {"version": "^0.5", "source": "legacy"}) + package.add_dependency( + Factory.create_dependency("tomlkit", {"version": "^0.5", "source": "legacy"}) + ) pool = Pool() pool.add_repository(MockLegacyRepository()) @@ -1590,7 +1663,7 @@ def test_installer_can_install_dependencies_from_forced_source( def test_run_installs_with_url_file(installer, locker, repo, package): url = "https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl" - package.add_dependency("demo", {"url": url}) + package.add_dependency(Factory.create_dependency("demo", {"url": url})) repo.add_package(get_package("pendulum", "1.4.4")) @@ -1608,11 +1681,13 @@ def test_installer_uses_prereleases_if_they_are_compatible( ): package.python_versions = "~2.7 || ^3.4" package.add_dependency( - "prerelease", {"git": "https://github.com/demo/prerelease.git"} + Factory.create_dependency( + "prerelease", {"git": "https://github.com/demo/prerelease.git"} + ) ) package_b = get_package("b", "2.0.0") - package_b.add_dependency("prerelease", ">=0.19") + package_b.add_dependency(Factory.create_dependency("prerelease", ">=0.19")) repo.add_package(package_b) @@ -1622,7 +1697,7 @@ def test_installer_uses_prereleases_if_they_are_compatible( locker.locked(True) locker.mock_lock_data(locker.written_data) - package.add_dependency("b", "^2.0.0") + package.add_dependency(Factory.create_dependency("b", "^2.0.0")) installer.whitelist(["b"]) installer.update(True) @@ -1637,7 +1712,7 @@ def test_installer_can_handle_old_lock_files( pool = Pool() pool.add_repository(MockRepository()) - package.add_dependency("pytest", "^3.5", category="dev") + package.add_dependency(Factory.create_dependency("pytest", "^3.5", category="dev")) locker.locked() locker.mock_lock_data(fixture("old-lock")) diff --git a/tests/installation/test_pip_installer.py b/tests/installation/test_pip_installer.py index 8f7d3975a61..206bcce59cc 100644 --- a/tests/installation/test_pip_installer.py +++ b/tests/installation/test_pip_installer.py @@ -13,10 +13,14 @@ @pytest.fixture def package_git(): - package = Package("demo", "1.0.0") - package.source_type = "git" - package.source_url = "git@github.com:demo/demo.git" - package.source_reference = "master" + package = Package( + "demo", + "1.0.0", + source_type="git", + source_url="git@github.com:demo/demo.git", + source_reference="master", + ) + return package @@ -54,9 +58,12 @@ def test_requirement(installer): def test_requirement_source_type_url(): installer = PipInstaller(NullEnv(), NullIO(), Pool()) - foo = Package("foo", "0.0.0") - foo.source_type = "url" - foo.source_url = "https://somehwere.com/releases/foo-1.0.0.tar.gz" + foo = Package( + "foo", + "0.0.0", + source_type="url", + source_url="https://somehwere.com/releases/foo-1.0.0.tar.gz", + ) result = installer.requirement(foo, formatted=True) expected = "{}#egg={}".format(foo.source_url, foo.name) @@ -79,14 +86,20 @@ def test_install_with_non_pypi_default_repository(pool, installer): pool.add_repository(default, default=True) pool.add_repository(another) - foo = Package("foo", "0.0.0") - foo.source_type = "legacy" - foo.source_reference = default._name - foo.source_url = default._url - bar = Package("bar", "0.1.0") - bar.source_type = "legacy" - bar.source_reference = another._name - bar.source_url = another._url + foo = Package( + "foo", + "0.0.0", + source_type="legacy", + source_reference=default.name, + source_url=default.url, + ) + bar = Package( + "bar", + "0.1.0", + source_type="legacy", + source_reference=another.name, + source_url=another.url, + ) installer.install(foo) installer.install(bar) @@ -104,10 +117,13 @@ def test_install_with_cert(): installer = PipInstaller(null_env, NullIO(), pool) - foo = Package("foo", "0.0.0") - foo.source_type = "legacy" - foo.source_reference = default._name - foo.source_url = default._url + foo = Package( + "foo", + "0.0.0", + source_type="legacy", + source_reference=default.name, + source_url=default.url, + ) installer.install(foo) @@ -133,10 +149,13 @@ def test_install_with_client_cert(): installer = PipInstaller(null_env, NullIO(), pool) - foo = Package("foo", "0.0.0") - foo.source_type = "legacy" - foo.source_reference = default._name - foo.source_url = default._url + foo = Package( + "foo", + "0.0.0", + source_type="legacy", + source_reference=default.name, + source_url=default.url, + ) installer.install(foo) @@ -161,11 +180,13 @@ def test_uninstall_git_package_nspkg_pth_cleanup(mocker, tmp_venv, pool): installer = PipInstaller(tmp_venv, NullIO(), pool) # use a namepspace package - package = Package("namespace-package-one", "1.0.0") - package.source_type = "git" - package.source_url = "https://github.com/demo/namespace-package-one.git" - package.source_reference = "master" - package.develop = True + package = Package( + "namespace-package-one", + "1.0.0", + source_type="git", + source_url="https://github.com/demo/namespace-package-one.git", + source_reference="master", + ) # we do this here because the virtual env might not be usable if failure case is triggered pth_file_candidate = tmp_venv.site_packages / "{}-nspkg.pth".format(package.name) diff --git a/tests/mixology/helpers.py b/tests/mixology/helpers.py index 05ab493f79a..fcde6701516 100644 --- a/tests/mixology/helpers.py +++ b/tests/mixology/helpers.py @@ -1,4 +1,5 @@ from poetry.core.packages import Package +from poetry.factory import Factory from poetry.mixology.failure import SolveFailure from poetry.mixology.version_solver import VersionSolver from poetry.packages import DependencyPackage @@ -11,7 +12,7 @@ def add_to_repo(repository, name, version, deps=None, python=None): if deps: for dep_name, dep_constraint in deps.items(): - package.add_dependency(dep_name, dep_constraint) + package.add_dependency(Factory.create_dependency(dep_name, dep_constraint)) repository.add_package(package) diff --git a/tests/mixology/version_solver/test_backtracking.py b/tests/mixology/version_solver/test_backtracking.py index b1c695279ea..1716ca35f70 100644 --- a/tests/mixology/version_solver/test_backtracking.py +++ b/tests/mixology/version_solver/test_backtracking.py @@ -1,9 +1,11 @@ +from poetry.factory import Factory + from ..helpers import add_to_repo from ..helpers import check_solver_result def test_circular_dependency_on_older_version(root, provider, repo): - root.add_dependency("a", ">=1.0.0") + root.add_dependency(Factory.create_dependency("a", ">=1.0.0")) add_to_repo(repo, "a", "1.0.0") add_to_repo(repo, "a", "2.0.0", deps={"b": "1.0.0"}) @@ -13,8 +15,8 @@ def test_circular_dependency_on_older_version(root, provider, repo): def test_diamond_dependency_graph(root, provider, repo): - root.add_dependency("a", "*") - root.add_dependency("b", "*") + root.add_dependency(Factory.create_dependency("a", "*")) + root.add_dependency(Factory.create_dependency("b", "*")) add_to_repo(repo, "a", "2.0.0", deps={"c": "^1.0.0"}) add_to_repo(repo, "a", "1.0.0") @@ -33,8 +35,8 @@ def test_backjumps_after_partial_satisfier(root, provider, repo): # c 2.0.0 is incompatible with y 2.0.0 because it requires x 1.0.0, but that # requirement only exists because of both a and b. The solver should be able # to deduce c 2.0.0's incompatibility and select c 1.0.0 instead. - root.add_dependency("c", "*") - root.add_dependency("y", "^2.0.0") + root.add_dependency(Factory.create_dependency("c", "*")) + root.add_dependency(Factory.create_dependency("y", "^2.0.0")) add_to_repo(repo, "a", "1.0.0", deps={"x": ">=1.0.0"}) add_to_repo(repo, "b", "1.0.0", deps={"x": "<2.0.0"}) @@ -56,7 +58,7 @@ def test_rolls_back_leaf_versions_first(root, provider, repo): # The latest versions of a and b disagree on c. An older version of either # will resolve the problem. This test validates that b, which is farther # in the dependency graph from myapp is downgraded first. - root.add_dependency("a", "*") + root.add_dependency(Factory.create_dependency("a", "*")) add_to_repo(repo, "a", "1.0.0", deps={"b": "*"}) add_to_repo(repo, "a", "2.0.0", deps={"b": "*", "c": "2.0.0"}) @@ -71,7 +73,7 @@ def test_rolls_back_leaf_versions_first(root, provider, repo): def test_simple_transitive(root, provider, repo): # Only one version of baz, so foo and bar will have to downgrade # until they reach it - root.add_dependency("foo", "*") + root.add_dependency(Factory.create_dependency("foo", "*")) add_to_repo(repo, "foo", "1.0.0", deps={"bar": "1.0.0"}) add_to_repo(repo, "foo", "2.0.0", deps={"bar": "2.0.0"}) @@ -93,8 +95,8 @@ def test_backjump_to_nearer_unsatisfied_package(root, provider, repo): # a-2.0.0 whose dependency on c-2.0.0-nonexistent led to the problem. We # make sure b has more versions than a so that the solver tries a first # since it sorts sibling dependencies by number of versions. - root.add_dependency("a", "*") - root.add_dependency("b", "*") + root.add_dependency(Factory.create_dependency("a", "*")) + root.add_dependency(Factory.create_dependency("b", "*")) add_to_repo(repo, "a", "1.0.0", deps={"c": "1.0.0"}) add_to_repo(repo, "a", "2.0.0", deps={"c": "2.0.0-nonexistent"}) @@ -114,8 +116,8 @@ def test_traverse_into_package_with_fewer_versions_first(root, provider, repo): # downgraded once). The chosen one depends on which dep is traversed first. # Since b has fewer versions, it will be traversed first, which means a will # come later. Since later selections are revised first, a gets downgraded. - root.add_dependency("a", "*") - root.add_dependency("b", "*") + root.add_dependency(Factory.create_dependency("a", "*")) + root.add_dependency(Factory.create_dependency("b", "*")) add_to_repo(repo, "a", "1.0.0", deps={"c": "*"}) add_to_repo(repo, "a", "2.0.0", deps={"c": "*"}) @@ -133,8 +135,8 @@ def test_traverse_into_package_with_fewer_versions_first(root, provider, repo): def test_backjump_past_failed_package_on_disjoint_constraint(root, provider, repo): - root.add_dependency("a", "*") - root.add_dependency("foo", ">2.0.0") + root.add_dependency(Factory.create_dependency("a", "*")) + root.add_dependency(Factory.create_dependency("foo", ">2.0.0")) add_to_repo(repo, "a", "1.0.0", deps={"foo": "*"}) # ok add_to_repo( diff --git a/tests/mixology/version_solver/test_basic_graph.py b/tests/mixology/version_solver/test_basic_graph.py index 59fe26bcfdd..0e70e938164 100644 --- a/tests/mixology/version_solver/test_basic_graph.py +++ b/tests/mixology/version_solver/test_basic_graph.py @@ -1,10 +1,12 @@ +from poetry.factory import Factory + from ..helpers import add_to_repo from ..helpers import check_solver_result def test_simple_dependencies(root, provider, repo): - root.add_dependency("a", "1.0.0") - root.add_dependency("b", "1.0.0") + root.add_dependency(Factory.create_dependency("a", "1.0.0")) + root.add_dependency(Factory.create_dependency("b", "1.0.0")) add_to_repo(repo, "a", "1.0.0", deps={"aa": "1.0.0", "ab": "1.0.0"}) add_to_repo(repo, "b", "1.0.0", deps={"ba": "1.0.0", "bb": "1.0.0"}) @@ -28,8 +30,8 @@ def test_simple_dependencies(root, provider, repo): def test_shared_dependencies_with_overlapping_constraints(root, provider, repo): - root.add_dependency("a", "1.0.0") - root.add_dependency("b", "1.0.0") + root.add_dependency(Factory.create_dependency("a", "1.0.0")) + root.add_dependency(Factory.create_dependency("b", "1.0.0")) add_to_repo(repo, "a", "1.0.0", deps={"shared": ">=2.0.0 <4.0.0"}) add_to_repo(repo, "b", "1.0.0", deps={"shared": ">=3.0.0 <5.0.0"}) @@ -45,8 +47,8 @@ def test_shared_dependencies_with_overlapping_constraints(root, provider, repo): def test_shared_dependency_where_dependent_version_affects_other_dependencies( root, provider, repo ): - root.add_dependency("foo", "<=1.0.2") - root.add_dependency("bar", "1.0.0") + root.add_dependency(Factory.create_dependency("foo", "<=1.0.2")) + root.add_dependency(Factory.create_dependency("bar", "1.0.0")) add_to_repo(repo, "foo", "1.0.0") add_to_repo(repo, "foo", "1.0.1", deps={"bang": "1.0.0"}) @@ -63,7 +65,7 @@ def test_shared_dependency_where_dependent_version_affects_other_dependencies( def test_circular_dependency(root, provider, repo): - root.add_dependency("foo", "1.0.0") + root.add_dependency(Factory.create_dependency("foo", "1.0.0")) add_to_repo(repo, "foo", "1.0.0", deps={"bar": "1.0.0"}) add_to_repo(repo, "bar", "1.0.0", deps={"foo": "1.0.0"}) diff --git a/tests/mixology/version_solver/test_python_constraint.py b/tests/mixology/version_solver/test_python_constraint.py index 8229079a7a2..739b11500ea 100644 --- a/tests/mixology/version_solver/test_python_constraint.py +++ b/tests/mixology/version_solver/test_python_constraint.py @@ -1,10 +1,12 @@ +from poetry.factory import Factory + from ..helpers import add_to_repo from ..helpers import check_solver_result def test_dependency_does_not_match_root_python_constraint(root, provider, repo): provider.set_package_python_versions("^3.6") - root.add_dependency("foo", "*") + root.add_dependency(Factory.create_dependency("foo", "*")) add_to_repo(repo, "foo", "1.0.0", python="<3.5") diff --git a/tests/mixology/version_solver/test_unsolvable.py b/tests/mixology/version_solver/test_unsolvable.py index e735063cd45..6baa7880490 100644 --- a/tests/mixology/version_solver/test_unsolvable.py +++ b/tests/mixology/version_solver/test_unsolvable.py @@ -1,9 +1,11 @@ +from poetry.factory import Factory + from ..helpers import add_to_repo from ..helpers import check_solver_result def test_no_version_matching_constraint(root, provider, repo): - root.add_dependency("foo", "^1.0") + root.add_dependency(Factory.create_dependency("foo", "^1.0")) add_to_repo(repo, "foo", "2.0.0") add_to_repo(repo, "foo", "2.1.3") @@ -19,8 +21,8 @@ def test_no_version_matching_constraint(root, provider, repo): def test_no_version_that_matches_combined_constraints(root, provider, repo): - root.add_dependency("foo", "1.0.0") - root.add_dependency("bar", "1.0.0") + root.add_dependency(Factory.create_dependency("foo", "1.0.0")) + root.add_dependency(Factory.create_dependency("bar", "1.0.0")) add_to_repo(repo, "foo", "1.0.0", deps={"shared": ">=2.0.0 <3.0.0"}) add_to_repo(repo, "bar", "1.0.0", deps={"shared": ">=2.9.0 <4.0.0"}) @@ -37,8 +39,8 @@ def test_no_version_that_matches_combined_constraints(root, provider, repo): def test_disjoint_constraints(root, provider, repo): - root.add_dependency("foo", "1.0.0") - root.add_dependency("bar", "1.0.0") + root.add_dependency(Factory.create_dependency("foo", "1.0.0")) + root.add_dependency(Factory.create_dependency("bar", "1.0.0")) add_to_repo(repo, "foo", "1.0.0", deps={"shared": "<=2.0.0"}) add_to_repo(repo, "bar", "1.0.0", deps={"shared": ">3.0.0"}) @@ -55,8 +57,8 @@ def test_disjoint_constraints(root, provider, repo): def test_disjoint_root_constraints(root, provider, repo): - root.add_dependency("foo", "1.0.0") - root.add_dependency("foo", "2.0.0") + root.add_dependency(Factory.create_dependency("foo", "1.0.0")) + root.add_dependency(Factory.create_dependency("foo", "2.0.0")) add_to_repo(repo, "foo", "1.0.0") add_to_repo(repo, "foo", "2.0.0") @@ -68,8 +70,8 @@ def test_disjoint_root_constraints(root, provider, repo): def test_no_valid_solution(root, provider, repo): - root.add_dependency("a") - root.add_dependency("b") + root.add_dependency(Factory.create_dependency("a", "*")) + root.add_dependency(Factory.create_dependency("b", "*")) add_to_repo(repo, "a", "1.0.0", deps={"b": "1.0.0"}) add_to_repo(repo, "a", "2.0.0", deps={"b": "2.0.0"}) diff --git a/tests/mixology/version_solver/test_with_lock.py b/tests/mixology/version_solver/test_with_lock.py index db60544f193..ee8535a8496 100644 --- a/tests/mixology/version_solver/test_with_lock.py +++ b/tests/mixology/version_solver/test_with_lock.py @@ -1,10 +1,12 @@ +from poetry.factory import Factory + from ...helpers import get_package from ..helpers import add_to_repo from ..helpers import check_solver_result def test_with_compatible_locked_dependencies(root, provider, repo): - root.add_dependency("foo", "*") + root.add_dependency(Factory.create_dependency("foo", "*")) add_to_repo(repo, "foo", "1.0.0", deps={"bar": "1.0.0"}) add_to_repo(repo, "foo", "1.0.1", deps={"bar": "1.0.1"}) @@ -22,7 +24,7 @@ def test_with_compatible_locked_dependencies(root, provider, repo): def test_with_incompatible_locked_dependencies(root, provider, repo): - root.add_dependency("foo", ">1.0.1") + root.add_dependency(Factory.create_dependency("foo", ">1.0.1")) add_to_repo(repo, "foo", "1.0.0", deps={"bar": "1.0.0"}) add_to_repo(repo, "foo", "1.0.1", deps={"bar": "1.0.1"}) @@ -40,7 +42,7 @@ def test_with_incompatible_locked_dependencies(root, provider, repo): def test_with_unrelated_locked_dependencies(root, provider, repo): - root.add_dependency("foo", "*") + root.add_dependency(Factory.create_dependency("foo", "*")) add_to_repo(repo, "foo", "1.0.0", deps={"bar": "1.0.0"}) add_to_repo(repo, "foo", "1.0.1", deps={"bar": "1.0.1"}) @@ -61,8 +63,8 @@ def test_with_unrelated_locked_dependencies(root, provider, repo): def test_unlocks_dependencies_if_necessary_to_ensure_that_a_new_dependency_is_statisfied( root, provider, repo ): - root.add_dependency("foo") - root.add_dependency("newdep", "2.0.0") + root.add_dependency(Factory.create_dependency("foo", "*")) + root.add_dependency(Factory.create_dependency("newdep", "2.0.0")) add_to_repo(repo, "foo", "1.0.0", deps={"bar": "<2.0.0"}) add_to_repo(repo, "bar", "1.0.0", deps={"baz": "<2.0.0"}) @@ -94,8 +96,8 @@ def test_unlocks_dependencies_if_necessary_to_ensure_that_a_new_dependency_is_st def test_with_compatible_locked_dependencies_use_latest(root, provider, repo): - root.add_dependency("foo", "*") - root.add_dependency("baz", "*") + root.add_dependency(Factory.create_dependency("foo", "*")) + root.add_dependency(Factory.create_dependency("baz", "*")) add_to_repo(repo, "foo", "1.0.0", deps={"bar": "1.0.0"}) add_to_repo(repo, "foo", "1.0.1", deps={"bar": "1.0.1"}) diff --git a/tests/packages/test_locker.py b/tests/packages/test_locker.py index 79362142957..35c584f2767 100644 --- a/tests/packages/test_locker.py +++ b/tests/packages/test_locker.py @@ -4,8 +4,10 @@ import pytest import tomlkit +from poetry.core.packages.package import Package from poetry.core.packages.project_package import ProjectPackage from poetry.core.semver.version import Version +from poetry.factory import Factory from poetry.packages.locker import Locker from ..helpers import get_dependency @@ -28,9 +30,17 @@ def root(): def test_lock_file_data_is_ordered(locker, root): package_a = get_package("A", "1.0.0") - package_a.add_dependency("B", "^1.0") + package_a.add_dependency(Factory.create_dependency("B", "^1.0")) package_a.files = [{"file": "foo", "hash": "456"}, {"file": "bar", "hash": "123"}] - packages = [package_a, get_package("B", "1.2")] + package_git = Package( + "git-package", + "1.2.3", + source_type="git", + source_url="https://github.com/python-poetry/poetry.git", + source_reference="develop", + source_resolved_reference="123456", + ) + packages = [package_a, get_package("B", "1.2"), package_git] locker.set_lock_data(root, packages) @@ -38,28 +48,42 @@ def test_lock_file_data_is_ordered(locker, root): content = f.read() expected = """[[package]] -category = "main" -description = "" name = "A" +version = "1.0.0" +description = "" +category = "main" optional = false python-versions = "*" -version = "1.0.0" [package.dependencies] B = "^1.0" [[package]] +name = "B" +version = "1.2" +description = "" category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "git-package" +version = "1.2.3" description = "" -name = "B" +category = "main" optional = false python-versions = "*" -version = "1.2" + +[package.source] +type = "git" +url = "https://github.com/python-poetry/poetry.git" +reference = "develop" +resolved_reference = "123456" [metadata] -content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" lock-version = "1.1" python-versions = "*" +content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" [metadata.files] A = [ @@ -67,6 +91,7 @@ def test_lock_file_data_is_ordered(locker, root): {file = "foo", hash = "456"}, ] B = [] +git-package = [] """ assert expected == content @@ -75,12 +100,12 @@ def test_lock_file_data_is_ordered(locker, root): def test_locker_properly_loads_extras(locker): content = """\ [[package]] -category = "main" -description = "httplib2 caching for requests" name = "cachecontrol" +version = "0.12.5" +description = "httplib2 caching for requests" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.12.5" [package.dependencies] msgpack = "*" @@ -95,9 +120,9 @@ def test_locker_properly_loads_extras(locker): redis = ["redis (>=2.10.5)"] [metadata] -content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77" lock-version = "1.1" python-versions = "~2.7 || ^3.4" +content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77" [metadata.files] cachecontrol = [] @@ -127,17 +152,17 @@ def test_lock_packages_with_null_description(locker, root): content = f.read() expected = """[[package]] -category = "main" -description = "" name = "A" +version = "1.0.0" +description = "" +category = "main" optional = false python-versions = "*" -version = "1.0.0" [metadata] -content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" lock-version = "1.1" python-versions = "*" +content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" [metadata.files] A = [] @@ -148,36 +173,36 @@ def test_lock_packages_with_null_description(locker, root): def test_lock_file_should_not_have_mixed_types(locker, root): package_a = get_package("A", "1.0.0") - package_a.add_dependency("B", "^1.0.0") - package_a.add_dependency("B", {"version": ">=1.0.0", "optional": True}) + package_a.add_dependency(Factory.create_dependency("B", "^1.0.0")) + package_a.add_dependency( + Factory.create_dependency("B", {"version": ">=1.0.0", "optional": True}) + ) package_a.requires[-1].activate() package_a.extras["foo"] = [get_dependency("B", ">=1.0.0")] locker.set_lock_data(root, [package_a]) expected = """[[package]] -category = "main" -description = "" name = "A" +version = "1.0.0" +description = "" +category = "main" optional = false python-versions = "*" -version = "1.0.0" [package.dependencies] -[[package.dependencies.B]] -version = "^1.0.0" - -[[package.dependencies.B]] -optional = true -version = ">=1.0.0" +B = [ + {version = "^1.0.0"}, + {version = ">=1.0.0", optional = true}, +] [package.extras] foo = ["B (>=1.0.0)"] [metadata] -content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" lock-version = "1.1" python-versions = "*" +content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" [metadata.files] A = [] @@ -191,12 +216,12 @@ def test_lock_file_should_not_have_mixed_types(locker, root): def test_reading_lock_file_should_raise_an_error_on_invalid_data(locker): content = u"""[[package]] -category = "main" -description = "" name = "A" +version = "1.0.0" +description = "" +category = "main" optional = false python-versions = "*" -version = "1.0.0" [package.extras] foo = ["bar"] @@ -205,9 +230,9 @@ def test_reading_lock_file_should_raise_an_error_on_invalid_data(locker): foo = ["bar"] [metadata] -content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" lock-version = "1.1" python-versions = "*" +content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" [metadata.files] A = [] @@ -222,9 +247,13 @@ def test_reading_lock_file_should_raise_an_error_on_invalid_data(locker): def test_locking_legacy_repository_package_should_include_source_section(root, locker): - package_a = get_package("A", "1.0.0") - package_a.source_url = "https://foo.bar" - package_a.source_reference = "legacy" + package_a = Package( + "A", + "1.0.0", + source_type="legacy", + source_url="https://foo.bar", + source_reference="legacy", + ) packages = [package_a] locker.set_lock_data(root, packages) @@ -233,21 +262,22 @@ def test_locking_legacy_repository_package_should_include_source_section(root, l content = f.read() expected = """[[package]] -category = "main" -description = "" name = "A" +version = "1.0.0" +description = "" +category = "main" optional = false python-versions = "*" -version = "1.0.0" [package.source] -reference = "legacy" +type = "legacy" url = "https://foo.bar" +reference = "legacy" [metadata] -content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" lock-version = "1.1" python-versions = "*" +content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" [metadata.files] A = [] @@ -261,9 +291,9 @@ def test_locker_should_emit_warnings_if_lock_version_is_newer_but_allowed( ): content = """\ [metadata] -content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77" lock-version = "{version}" python-versions = "~2.7 || ^3.4" +content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77" [metadata.files] """.format( @@ -293,9 +323,9 @@ def test_locker_should_raise_an_error_if_lock_version_is_newer_and_not_allowed( ): content = """\ [metadata] -content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77" lock-version = "2.0" python-versions = "~2.7 || ^3.4" +content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77" [metadata.files] """ @@ -310,27 +340,29 @@ def test_locker_should_raise_an_error_if_lock_version_is_newer_and_not_allowed( def test_extras_dependencies_are_ordered(locker, root): package_a = get_package("A", "1.0.0") package_a.add_dependency( - "B", {"version": "^1.0.0", "optional": True, "extras": ["c", "a", "b"]} + Factory.create_dependency( + "B", {"version": "^1.0.0", "optional": True, "extras": ["c", "a", "b"]} + ) ) package_a.requires[-1].activate() locker.set_lock_data(root, [package_a]) expected = """[[package]] -category = "main" -description = "" name = "A" +version = "1.0.0" +description = "" +category = "main" optional = false python-versions = "*" -version = "1.0.0" [package.dependencies] B = {version = "^1.0.0", extras = ["a", "b", "c"], optional = true} [metadata] -content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" lock-version = "1.1" python-versions = "*" +content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" [metadata.files] A = [] @@ -351,9 +383,9 @@ def test_locker_should_neither_emit_warnings_nor_raise_error_for_lower_compatibl ) content = """\ [metadata] -content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77" lock-version = "{version}" python-versions = "~2.7 || ^3.4" +content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77" [metadata.files] """.format( diff --git a/tests/puzzle/test_provider.py b/tests/puzzle/test_provider.py index 016f40c9678..693470156f4 100644 --- a/tests/puzzle/test_provider.py +++ b/tests/puzzle/test_provider.py @@ -54,7 +54,11 @@ def test_search_for_vcs_setup_egg_info(provider): assert package.name == "demo" assert package.version.text == "0.1.2" - assert package.requires == [get_dependency("pendulum", ">=1.4.4")] + + required = [r for r in package.requires if not r.is_optional()] + optional = [r for r in package.requires if r.is_optional()] + assert required == [get_dependency("pendulum", ">=1.4.4")] + assert optional == [get_dependency("tomlkit"), get_dependency("cleo")] assert package.extras == { "foo": [get_dependency("cleo")], "bar": [get_dependency("tomlkit")], @@ -62,17 +66,19 @@ def test_search_for_vcs_setup_egg_info(provider): def test_search_for_vcs_setup_egg_info_with_extras(provider): - dependency = VCSDependency("demo", "git", "https://github.com/demo/demo.git") - dependency.extras.append("foo") + dependency = VCSDependency( + "demo", "git", "https://github.com/demo/demo.git", extras=["foo"] + ) package = provider.search_for_vcs(dependency)[0] assert package.name == "demo" assert package.version.text == "0.1.2" - assert package.requires == [ - get_dependency("pendulum", ">=1.4.4"), - get_dependency("cleo", optional=True), - ] + + required = [r for r in package.requires if not r.is_optional()] + optional = [r for r in package.requires if r.is_optional()] + assert required == [get_dependency("pendulum", ">=1.4.4")] + assert optional == [get_dependency("tomlkit"), get_dependency("cleo")] assert package.extras == { "foo": [get_dependency("cleo")], "bar": [get_dependency("tomlkit")], @@ -89,7 +95,11 @@ def test_search_for_vcs_read_setup(provider, mocker): assert package.name == "demo" assert package.version.text == "0.1.2" - assert package.requires == [get_dependency("pendulum", ">=1.4.4")] + + required = [r for r in package.requires if not r.is_optional()] + optional = [r for r in package.requires if r.is_optional()] + assert required == [get_dependency("pendulum", ">=1.4.4")] + assert optional == [get_dependency("tomlkit"), get_dependency("cleo")] assert package.extras == { "foo": [get_dependency("cleo")], "bar": [get_dependency("tomlkit")], @@ -100,21 +110,19 @@ def test_search_for_vcs_read_setup(provider, mocker): def test_search_for_vcs_read_setup_with_extras(provider, mocker): mocker.patch("poetry.utils.env.EnvManager.get", return_value=MockEnv()) - dependency = VCSDependency("demo", "git", "https://github.com/demo/demo.git") - dependency.extras.append("foo") + dependency = VCSDependency( + "demo", "git", "https://github.com/demo/demo.git", extras=["foo"] + ) package = provider.search_for_vcs(dependency)[0] assert package.name == "demo" assert package.version.text == "0.1.2" - assert package.requires == [ - get_dependency("pendulum", ">=1.4.4"), - get_dependency("cleo", optional=True), - ] - assert package.extras == { - "foo": [get_dependency("cleo")], - "bar": [get_dependency("tomlkit")], - } + + required = [r for r in package.requires if not r.is_optional()] + optional = [r for r in package.requires if r.is_optional()] + assert required == [get_dependency("pendulum", ">=1.4.4")] + assert optional == [get_dependency("tomlkit"), get_dependency("cleo")] def test_search_for_vcs_read_setup_raises_error_if_no_version(provider, mocker): @@ -145,7 +153,11 @@ def test_search_for_directory_setup_egg_info(provider, directory): assert package.name == "demo" assert package.version.text == "0.1.2" - assert package.requires == [get_dependency("pendulum", ">=1.4.4")] + + required = [r for r in package.requires if not r.is_optional()] + optional = [r for r in package.requires if r.is_optional()] + assert required == [get_dependency("pendulum", ">=1.4.4")] + assert optional == [get_dependency("tomlkit"), get_dependency("cleo")] assert package.extras == { "foo": [get_dependency("cleo")], "bar": [get_dependency("tomlkit")], @@ -161,17 +173,18 @@ def test_search_for_directory_setup_egg_info_with_extras(provider): / "github.com" / "demo" / "demo", + extras=["foo"], ) - dependency.extras.append("foo") package = provider.search_for_directory(dependency)[0] assert package.name == "demo" assert package.version.text == "0.1.2" - assert package.requires == [ - get_dependency("pendulum", ">=1.4.4"), - get_dependency("cleo", optional=True), - ] + + required = [r for r in package.requires if not r.is_optional()] + optional = [r for r in package.requires if r.is_optional()] + assert required == [get_dependency("pendulum", ">=1.4.4")] + assert optional == [get_dependency("tomlkit"), get_dependency("cleo")] assert package.extras == { "foo": [get_dependency("cleo")], "bar": [get_dependency("tomlkit")], @@ -200,7 +213,11 @@ def test_search_for_directory_setup_with_base(provider, directory): assert package.name == "demo" assert package.version.text == "0.1.2" - assert package.requires == [get_dependency("pendulum", ">=1.4.4")] + + required = [r for r in package.requires if not r.is_optional()] + optional = [r for r in package.requires if r.is_optional()] + assert required == [get_dependency("pendulum", ">=1.4.4")] + assert optional == [get_dependency("tomlkit"), get_dependency("cleo")] assert package.extras == { "foo": [get_dependency("cleo")], "bar": [get_dependency("tomlkit")], @@ -233,7 +250,11 @@ def test_search_for_directory_setup_read_setup(provider, mocker): assert package.name == "demo" assert package.version.text == "0.1.2" - assert package.requires == [get_dependency("pendulum", ">=1.4.4")] + + required = [r for r in package.requires if not r.is_optional()] + optional = [r for r in package.requires if r.is_optional()] + assert required == [get_dependency("pendulum", ">=1.4.4")] + assert optional == [get_dependency("tomlkit"), get_dependency("cleo")] assert package.extras == { "foo": [get_dependency("cleo")], "bar": [get_dependency("tomlkit")], @@ -252,17 +273,18 @@ def test_search_for_directory_setup_read_setup_with_extras(provider, mocker): / "github.com" / "demo" / "demo", + extras=["foo"], ) - dependency.extras.append("foo") package = provider.search_for_directory(dependency)[0] assert package.name == "demo" assert package.version.text == "0.1.2" - assert package.requires == [ - get_dependency("pendulum", ">=1.4.4"), - get_dependency("cleo", optional=True), - ] + + required = [r for r in package.requires if not r.is_optional()] + optional = [r for r in package.requires if r.is_optional()] + assert required == [get_dependency("pendulum", ">=1.4.4")] + assert optional == [get_dependency("tomlkit"), get_dependency("cleo")] assert package.extras == { "foo": [get_dependency("cleo")], "bar": [get_dependency("tomlkit")], @@ -299,7 +321,18 @@ def test_search_for_directory_poetry(provider): assert package.name == "project-with-extras" assert package.version.text == "1.2.3" - assert package.requires == [] + + required = [ + r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional() + ] + optional = [ + r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional() + ] + assert required == [] + assert optional == [ + get_dependency("cachy", ">=0.2.0"), + get_dependency("pendulum", ">=1.4.4"), + ] assert package.extras == { "extras_a": [get_dependency("pendulum", ">=1.4.4")], "extras_b": [get_dependency("cachy", ">=0.2.0")], @@ -310,14 +343,25 @@ def test_search_for_directory_poetry_with_extras(provider): dependency = DirectoryDependency( "project-with-extras", Path(__file__).parent.parent / "fixtures" / "project_with_extras", + extras=["extras_a"], ) - dependency.extras.append("extras_a") package = provider.search_for_directory(dependency)[0] assert package.name == "project-with-extras" assert package.version.text == "1.2.3" - assert package.requires == [get_dependency("pendulum", ">=1.4.4")] + + required = [ + r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional() + ] + optional = [ + r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional() + ] + assert required == [] + assert optional == [ + get_dependency("cachy", ">=0.2.0"), + get_dependency("pendulum", ">=1.4.4"), + ] assert package.extras == { "extras_a": [get_dependency("pendulum", ">=1.4.4")], "extras_b": [get_dependency("cachy", ">=0.2.0")], @@ -337,7 +381,18 @@ def test_search_for_file_sdist(provider): assert package.name == "demo" assert package.version.text == "0.1.0" - assert package.requires == [get_dependency("pendulum", ">=1.4.4")] + + required = [ + r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional() + ] + optional = [ + r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional() + ] + assert required == [get_dependency("pendulum", ">=1.4.4")] + assert optional == [ + get_dependency("cleo"), + get_dependency("tomlkit"), + ] assert package.extras == { "foo": [get_dependency("cleo")], "bar": [get_dependency("tomlkit")], @@ -351,16 +406,24 @@ def test_search_for_file_sdist_with_extras(provider): / "fixtures" / "distributions" / "demo-0.1.0.tar.gz", + extras=["foo"], ) - dependency.extras.append("foo") package = provider.search_for_file(dependency)[0] assert package.name == "demo" assert package.version.text == "0.1.0" - assert package.requires == [ - get_dependency("pendulum", ">=1.4.4"), - get_dependency("cleo", optional=True), + + required = [ + r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional() + ] + optional = [ + r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional() + ] + assert required == [get_dependency("pendulum", ">=1.4.4")] + assert optional == [ + get_dependency("cleo"), + get_dependency("tomlkit"), ] assert package.extras == { "foo": [get_dependency("cleo")], @@ -381,7 +444,18 @@ def test_search_for_file_wheel(provider): assert package.name == "demo" assert package.version.text == "0.1.0" - assert package.requires == [get_dependency("pendulum", ">=1.4.4")] + + required = [ + r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional() + ] + optional = [ + r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional() + ] + assert required == [get_dependency("pendulum", ">=1.4.4")] + assert optional == [ + get_dependency("cleo"), + get_dependency("tomlkit"), + ] assert package.extras == { "foo": [get_dependency("cleo")], "bar": [get_dependency("tomlkit")], @@ -395,16 +469,24 @@ def test_search_for_file_wheel_with_extras(provider): / "fixtures" / "distributions" / "demo-0.1.0-py2.py3-none-any.whl", + extras=["foo"], ) - dependency.extras.append("foo") package = provider.search_for_file(dependency)[0] assert package.name == "demo" assert package.version.text == "0.1.0" - assert package.requires == [ - get_dependency("pendulum", ">=1.4.4"), - get_dependency("cleo", optional=True), + + required = [ + r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional() + ] + optional = [ + r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional() + ] + assert required == [get_dependency("pendulum", ">=1.4.4")] + assert optional == [ + get_dependency("cleo"), + get_dependency("tomlkit"), ] assert package.extras == { "foo": [get_dependency("cleo")], diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 58f7ffe3ac1..f0d3fed425b 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -2,9 +2,11 @@ from clikit.io import NullIO +from poetry.core.packages import Package from poetry.core.packages import ProjectPackage from poetry.core.packages import dependency_from_pep_508 from poetry.core.version.markers import parse_marker +from poetry.factory import Factory from poetry.puzzle import Solver from poetry.puzzle.exceptions import SolverProblemError from poetry.puzzle.provider import Provider as BaseProvider @@ -87,11 +89,11 @@ def check_solver_result(ops, expected): result.append({"job": job, "package": op.package, "skipped": op.skipped}) - assert result == expected + assert expected == result def test_solver_install_single(solver, repo, package): - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") repo.add_package(package_a) @@ -125,7 +127,7 @@ def test_remove_non_installed(solver, repo, locked): def test_install_non_existing_package_fail(solver, repo, package): - package.add_dependency("B", "1") + package.add_dependency(Factory.create_dependency("B", "1")) package_a = get_package("A", "1.0") repo.add_package(package_a) @@ -135,7 +137,7 @@ def test_install_non_existing_package_fail(solver, repo, package): def test_solver_with_deps(solver, repo, package): - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") @@ -159,7 +161,7 @@ def test_solver_with_deps(solver, repo, package): def test_install_honours_not_equal(solver, repo, package): - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") @@ -187,9 +189,9 @@ def test_install_honours_not_equal(solver, repo, package): def test_install_with_deps_in_order(solver, repo, package): - package.add_dependency("A") - package.add_dependency("B") - package.add_dependency("C") + package.add_dependency(Factory.create_dependency("A", "*")) + package.add_dependency(Factory.create_dependency("B", "*")) + package.add_dependency(Factory.create_dependency("C", "*")) package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") @@ -216,7 +218,7 @@ def test_install_with_deps_in_order(solver, repo, package): def test_install_installed(solver, repo, installed, package): - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") installed.add_package(package_a) @@ -230,7 +232,7 @@ def test_install_installed(solver, repo, installed, package): def test_update_installed(solver, repo, installed, package): - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) installed.add_package(get_package("A", "1.0")) @@ -247,8 +249,8 @@ def test_update_installed(solver, repo, installed, package): def test_update_with_use_latest(solver, repo, installed, package, locked): - package.add_dependency("A") - package.add_dependency("B") + package.add_dependency(Factory.create_dependency("A", "*")) + package.add_dependency(Factory.create_dependency("B", "*")) installed.add_package(get_package("A", "1.0")) @@ -276,13 +278,13 @@ def test_update_with_use_latest(solver, repo, installed, package, locked): def test_solver_sets_categories(solver, repo, package): - package.add_dependency("A") - package.add_dependency("B", category="dev") + package.add_dependency(Factory.create_dependency("A", "*")) + package.add_dependency(Factory.create_dependency("B", "*", category="dev")) package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") package_c = get_package("C", "1.0") - package_b.add_dependency("C", "~1.0") + package_b.add_dependency(Factory.create_dependency("C", "~1.0")) repo.add_package(package_a) repo.add_package(package_b) @@ -306,8 +308,8 @@ def test_solver_sets_categories(solver, repo, package): def test_solver_respects_root_package_python_versions(solver, repo, package): solver.provider.set_package_python_versions("~3.4") - package.add_dependency("A") - package.add_dependency("B") + package.add_dependency(Factory.create_dependency("A", "*")) + package.add_dependency(Factory.create_dependency("B", "*")) package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") @@ -316,7 +318,7 @@ def test_solver_respects_root_package_python_versions(solver, repo, package): package_c.python_versions = "^3.4" package_c11 = get_package("C", "1.1") package_c11.python_versions = "^3.6" - package_b.add_dependency("C", "^1.0") + package_b.add_dependency(Factory.create_dependency("C", "^1.0")) repo.add_package(package_a) repo.add_package(package_b) @@ -337,15 +339,15 @@ def test_solver_respects_root_package_python_versions(solver, repo, package): def test_solver_fails_if_mismatch_root_python_versions(solver, repo, package): solver.provider.set_package_python_versions("^3.4") - package.add_dependency("A") - package.add_dependency("B") + package.add_dependency(Factory.create_dependency("A", "*")) + package.add_dependency(Factory.create_dependency("B", "*")) package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") package_b.python_versions = "^3.6" package_c = get_package("C", "1.0") package_c.python_versions = "~3.3" - package_b.add_dependency("C", "~1.0") + package_b.add_dependency(Factory.create_dependency("C", "~1.0")) repo.add_package(package_a) repo.add_package(package_b) @@ -358,15 +360,19 @@ def test_solver_fails_if_mismatch_root_python_versions(solver, repo, package): def test_solver_solves_optional_and_compatible_packages(solver, repo, package): solver.provider.set_package_python_versions("~3.4") package.extras["foo"] = [get_dependency("B")] - package.add_dependency("A", {"version": "*", "python": "^3.4"}) - package.add_dependency("B", {"version": "*", "optional": True}) + package.add_dependency( + Factory.create_dependency("A", {"version": "*", "python": "^3.4"}) + ) + package.add_dependency( + Factory.create_dependency("B", {"version": "*", "optional": True}) + ) package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") package_b.python_versions = "^3.3" package_c = get_package("C", "1.0") package_c.python_versions = "^3.4" - package_b.add_dependency("C", "^1.0") + package_b.add_dependency(Factory.create_dependency("C", "^1.0")) repo.add_package(package_a) repo.add_package(package_b) @@ -385,8 +391,8 @@ def test_solver_solves_optional_and_compatible_packages(solver, repo, package): def test_solver_does_not_return_extras_if_not_requested(solver, repo, package): - package.add_dependency("A") - package.add_dependency("B") + package.add_dependency(Factory.create_dependency("A", "*")) + package.add_dependency(Factory.create_dependency("B", "*")) package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") @@ -410,8 +416,10 @@ def test_solver_does_not_return_extras_if_not_requested(solver, repo, package): def test_solver_returns_extras_if_requested(solver, repo, package): - package.add_dependency("A") - package.add_dependency("B", {"version": "*", "extras": ["foo"]}) + package.add_dependency(Factory.create_dependency("A", "*")) + package.add_dependency( + Factory.create_dependency("B", {"version": "*", "extras": ["foo"]}) + ) package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") @@ -442,9 +450,11 @@ def test_solver_returns_extras_if_requested(solver, repo, package): def test_solver_returns_prereleases_if_requested(solver, repo, package): - package.add_dependency("A") - package.add_dependency("B") - package.add_dependency("C", {"version": "*", "allow-prereleases": True}) + package.add_dependency(Factory.create_dependency("A", "*")) + package.add_dependency(Factory.create_dependency("B", "*")) + package.add_dependency( + Factory.create_dependency("C", {"version": "*", "allow-prereleases": True}) + ) package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") @@ -469,9 +479,9 @@ def test_solver_returns_prereleases_if_requested(solver, repo, package): def test_solver_does_not_return_prereleases_if_not_requested(solver, repo, package): - package.add_dependency("A") - package.add_dependency("B") - package.add_dependency("C") + package.add_dependency(Factory.create_dependency("A", "*")) + package.add_dependency(Factory.create_dependency("B", "*")) + package.add_dependency(Factory.create_dependency("C", "*")) package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") @@ -496,17 +506,19 @@ def test_solver_does_not_return_prereleases_if_not_requested(solver, repo, packa def test_solver_sub_dependencies_with_requirements(solver, repo, package): - package.add_dependency("A") - package.add_dependency("B") + package.add_dependency(Factory.create_dependency("A", "*")) + package.add_dependency(Factory.create_dependency("B", "*")) package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") package_c = get_package("C", "1.0") package_d = get_package("D", "1.0") - package_c.add_dependency("D", {"version": "^1.0", "python": "<4.0"}) - package_a.add_dependency("C") - package_b.add_dependency("D", "^1.0") + package_c.add_dependency( + Factory.create_dependency("D", {"version": "^1.0", "python": "<4.0"}) + ) + package_a.add_dependency(Factory.create_dependency("C", "*")) + package_b.add_dependency(Factory.create_dependency("D", "^1.0")) repo.add_package(package_a) repo.add_package(package_b) @@ -530,9 +542,15 @@ def test_solver_sub_dependencies_with_requirements(solver, repo, package): def test_solver_sub_dependencies_with_requirements_complex(solver, repo, package): - package.add_dependency("A", {"version": "^1.0", "python": "<5.0"}) - package.add_dependency("B", {"version": "^1.0", "python": "<5.0"}) - package.add_dependency("C", {"version": "^1.0", "python": "<4.0"}) + package.add_dependency( + Factory.create_dependency("A", {"version": "^1.0", "python": "<5.0"}) + ) + package.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "python": "<5.0"}) + ) + package.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "python": "<4.0"}) + ) package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") @@ -541,12 +559,22 @@ def test_solver_sub_dependencies_with_requirements_complex(solver, repo, package package_e = get_package("E", "1.0") package_f = get_package("F", "1.0") - package_a.add_dependency("B", {"version": "^1.0", "python": "<4.0"}) - package_a.add_dependency("D", {"version": "^1.0", "python": "<4.0"}) - package_b.add_dependency("E", {"version": "^1.0", "platform": "win32"}) - package_b.add_dependency("F", {"version": "^1.0", "python": "<5.0"}) - package_c.add_dependency("F", {"version": "^1.0", "python": "<4.0"}) - package_d.add_dependency("F") + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "python": "<4.0"}) + ) + package_a.add_dependency( + Factory.create_dependency("D", {"version": "^1.0", "python": "<4.0"}) + ) + package_b.add_dependency( + Factory.create_dependency("E", {"version": "^1.0", "platform": "win32"}) + ) + package_b.add_dependency( + Factory.create_dependency("F", {"version": "^1.0", "python": "<5.0"}) + ) + package_c.add_dependency( + Factory.create_dependency("F", {"version": "^1.0", "python": "<4.0"}) + ) + package_d.add_dependency(Factory.create_dependency("F", "*")) repo.add_package(package_a) repo.add_package(package_b) @@ -574,13 +602,15 @@ def test_solver_sub_dependencies_with_not_supported_python_version( solver, repo, package ): solver.provider.set_package_python_versions("^3.5") - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") package_b.python_versions = "<2.0" - package_a.add_dependency("B", {"version": "^1.0", "python": "<2.0"}) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "python": "<2.0"}) + ) repo.add_package(package_a) repo.add_package(package_b) @@ -594,18 +624,24 @@ def test_solver_with_dependency_in_both_main_and_dev_dependencies( solver, repo, package ): solver.provider.set_package_python_versions("^3.5") - package.add_dependency("A") - package.add_dependency("A", {"version": "*", "extras": ["foo"]}, category="dev") + package.add_dependency(Factory.create_dependency("A", "*")) + package.add_dependency( + Factory.create_dependency( + "A", {"version": "*", "extras": ["foo"]}, category="dev" + ) + ) package_a = get_package("A", "1.0") package_a.extras["foo"] = [get_dependency("C")] - package_a.add_dependency("C", {"version": "^1.0", "optional": True}) - package_a.add_dependency("B", {"version": "^1.0"}) + package_a.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "optional": True}) + ) + package_a.add_dependency(Factory.create_dependency("B", {"version": "^1.0"})) package_b = get_package("B", "1.0") package_c = get_package("C", "1.0") - package_c.add_dependency("D", "^1.0") + package_c.add_dependency(Factory.create_dependency("D", "^1.0")) package_d = get_package("D", "1.0") @@ -640,24 +676,30 @@ def test_solver_with_dependency_in_both_main_and_dev_dependencies( def test_solver_with_dependency_in_both_main_and_dev_dependencies_with_one_more_dependent( solver, repo, package ): - package.add_dependency("A") - package.add_dependency("E") - package.add_dependency("A", {"version": "*", "extras": ["foo"]}, category="dev") + package.add_dependency(Factory.create_dependency("A", "*")) + package.add_dependency(Factory.create_dependency("E", "*")) + package.add_dependency( + Factory.create_dependency( + "A", {"version": "*", "extras": ["foo"]}, category="dev" + ) + ) package_a = get_package("A", "1.0") package_a.extras["foo"] = [get_dependency("C")] - package_a.add_dependency("C", {"version": "^1.0", "optional": True}) - package_a.add_dependency("B", {"version": "^1.0"}) + package_a.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "optional": True}) + ) + package_a.add_dependency(Factory.create_dependency("B", {"version": "^1.0"})) package_b = get_package("B", "1.0") package_c = get_package("C", "1.0") - package_c.add_dependency("D", "^1.0") + package_c.add_dependency(Factory.create_dependency("D", "^1.0")) package_d = get_package("D", "1.0") package_e = get_package("E", "1.0") - package_e.add_dependency("A", "^1.0") + package_e.add_dependency(Factory.create_dependency("A", "^1.0")) repo.add_package(package_a) repo.add_package(package_b) @@ -692,10 +734,10 @@ def test_solver_with_dependency_in_both_main_and_dev_dependencies_with_one_more_ def test_solver_with_dependency_and_prerelease_sub_dependencies(solver, repo, package): - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") - package_a.add_dependency("B", ">=1.0.0.dev2") + package_a.add_dependency(Factory.create_dependency("B", ">=1.0.0.dev2")) repo.add_package(package_a) repo.add_package(get_package("B", "0.9.0")) @@ -717,14 +759,14 @@ def test_solver_with_dependency_and_prerelease_sub_dependencies(solver, repo, pa def test_solver_circular_dependency(solver, repo, package): - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") - package_a.add_dependency("B", "^1.0") + package_a.add_dependency(Factory.create_dependency("B", "^1.0")) package_b = get_package("B", "1.0") - package_b.add_dependency("A", "^1.0") - package_b.add_dependency("C", "^1.0") + package_b.add_dependency(Factory.create_dependency("A", "^1.0")) + package_b.add_dependency(Factory.create_dependency("C", "^1.0")) package_c = get_package("C", "1.0") @@ -747,19 +789,19 @@ def test_solver_circular_dependency(solver, repo, package): def test_solver_circular_dependency_chain(solver, repo, package): - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") - package_a.add_dependency("B", "^1.0") + package_a.add_dependency(Factory.create_dependency("B", "^1.0")) package_b = get_package("B", "1.0") - package_b.add_dependency("C", "^1.0") + package_b.add_dependency(Factory.create_dependency("C", "^1.0")) package_c = get_package("C", "1.0") - package_c.add_dependency("D", "^1.0") + package_c.add_dependency(Factory.create_dependency("D", "^1.0")) package_d = get_package("D", "1.0") - package_d.add_dependency("B", "^1.0") + package_d.add_dependency(Factory.create_dependency("B", "^1.0")) repo.add_package(package_a) repo.add_package(package_b) @@ -791,9 +833,9 @@ def test_solver_dense_dependencies(solver, repo, package): package_ai = get_package("a" + str(i), "1.0") repo.add_package(package_ai) packages.append(package_ai) - package.add_dependency("a" + str(i), "^1.0") + package.add_dependency(Factory.create_dependency("a" + str(i), "^1.0")) for j in range(i): - package_ai.add_dependency("a" + str(j), "^1.0") + package_ai.add_dependency(Factory.create_dependency("a" + str(j), "^1.0")) ops = solver.solve() @@ -803,11 +845,15 @@ def test_solver_dense_dependencies(solver, repo, package): def test_solver_duplicate_dependencies_same_constraint(solver, repo, package): - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") - package_a.add_dependency("B", {"version": "^1.0", "python": "2.7"}) - package_a.add_dependency("B", {"version": "^1.0", "python": ">=3.4"}) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "python": "2.7"}) + ) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "python": ">=3.4"}) + ) package_b = get_package("B", "1.0") @@ -826,11 +872,15 @@ def test_solver_duplicate_dependencies_same_constraint(solver, repo, package): def test_solver_duplicate_dependencies_different_constraints(solver, repo, package): - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") - package_a.add_dependency("B", {"version": "^1.0", "python": "<3.4"}) - package_a.add_dependency("B", {"version": "^2.0", "python": ">=3.4"}) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "python": "<3.4"}) + ) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^2.0", "python": ">=3.4"}) + ) package_b10 = get_package("B", "1.0") package_b20 = get_package("B", "2.0") @@ -854,11 +904,11 @@ def test_solver_duplicate_dependencies_different_constraints(solver, repo, packa def test_solver_duplicate_dependencies_different_constraints_same_requirements( solver, repo, package ): - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") - package_a.add_dependency("B", {"version": "^1.0"}) - package_a.add_dependency("B", {"version": "^2.0"}) + package_a.add_dependency(Factory.create_dependency("B", {"version": "^1.0"})) + package_a.add_dependency(Factory.create_dependency("B", {"version": "^2.0"})) package_b10 = get_package("B", "1.0") package_b20 = get_package("B", "2.0") @@ -879,16 +929,20 @@ def test_solver_duplicate_dependencies_different_constraints_same_requirements( def test_solver_duplicate_dependencies_sub_dependencies(solver, repo, package): - package.add_dependency("A") + package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") - package_a.add_dependency("B", {"version": "^1.0", "python": "<3.4"}) - package_a.add_dependency("B", {"version": "^2.0", "python": ">=3.4"}) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "python": "<3.4"}) + ) + package_a.add_dependency( + Factory.create_dependency("B", {"version": "^2.0", "python": ">=3.4"}) + ) package_b10 = get_package("B", "1.0") package_b20 = get_package("B", "2.0") - package_b10.add_dependency("C", "1.2") - package_b20.add_dependency("C", "1.5") + package_b10.add_dependency(Factory.create_dependency("C", "1.2")) + package_b20.add_dependency(Factory.create_dependency("C", "1.5")) package_c12 = get_package("C", "1.2") package_c15 = get_package("C", "1.5") @@ -914,7 +968,11 @@ def test_solver_duplicate_dependencies_sub_dependencies(solver, repo, package): def test_solver_fails_if_dependency_name_does_not_match_package(solver, repo, package): - package.add_dependency("my-demo", {"git": "https://github.com/demo/demo.git"}) + package.add_dependency( + Factory.create_dependency( + "my-demo", {"git": "https://github.com/demo/demo.git"} + ) + ) with pytest.raises(RuntimeError): solver.solve() @@ -924,17 +982,17 @@ def test_solver_does_not_get_stuck_in_recursion_on_circular_dependency( solver, repo, package ): package_a = get_package("A", "1.0") - package_a.add_dependency("B", "^1.0") + package_a.add_dependency(Factory.create_dependency("B", "^1.0")) package_b = get_package("B", "1.0") - package_b.add_dependency("C", "^1.0") + package_b.add_dependency(Factory.create_dependency("C", "^1.0")) package_c = get_package("C", "1.0") - package_c.add_dependency("B", "^1.0") + package_c.add_dependency(Factory.create_dependency("B", "^1.0")) repo.add_package(package_a) repo.add_package(package_b) repo.add_package(package_c) - package.add_dependency("A", "^1.0") + package.add_dependency(Factory.create_dependency("A", "^1.0")) ops = solver.solve() @@ -954,14 +1012,20 @@ def test_solver_can_resolve_git_dependencies(solver, repo, package): repo.add_package(pendulum) repo.add_package(cleo) - package.add_dependency("demo", {"git": "https://github.com/demo/demo.git"}) + package.add_dependency( + Factory.create_dependency("demo", {"git": "https://github.com/demo/demo.git"}) + ) ops = solver.solve() - demo = get_package("demo", "0.1.2") - demo.source_type = "git" - demo.source_url = "https://github.com/demo/demo.git" - demo.source_reference = "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24" + demo = Package( + "demo", + "0.1.2", + source_type="git", + source_url="https://github.com/demo/demo.git", + source_reference="master", + source_resolved_reference="9cf87a285a2d3fbb0b9fa621997b3acc3631ed24", + ) check_solver_result( ops, @@ -971,7 +1035,8 @@ def test_solver_can_resolve_git_dependencies(solver, repo, package): op = ops[1] assert op.package.source_type == "git" - assert op.package.source_reference.startswith("9cf87a2") + assert op.package.source_reference == "master" + assert op.package.source_resolved_reference.startswith("9cf87a2") def test_solver_can_resolve_git_dependencies_with_extras(solver, repo, package): @@ -981,15 +1046,21 @@ def test_solver_can_resolve_git_dependencies_with_extras(solver, repo, package): repo.add_package(cleo) package.add_dependency( - "demo", {"git": "https://github.com/demo/demo.git", "extras": ["foo"]} + Factory.create_dependency( + "demo", {"git": "https://github.com/demo/demo.git", "extras": ["foo"]} + ) ) ops = solver.solve() - demo = get_package("demo", "0.1.2") - demo.source_type = "git" - demo.source_url = "https://github.com/demo/demo.git" - demo.source_reference = "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24" + demo = Package( + "demo", + "0.1.2", + source_type="git", + source_url="https://github.com/demo/demo.git", + source_reference="master", + source_resolved_reference="9cf87a285a2d3fbb0b9fa621997b3acc3631ed24", + ) check_solver_result( ops, @@ -1012,14 +1083,18 @@ def test_solver_can_resolve_git_dependencies_with_ref(solver, repo, package, ref repo.add_package(pendulum) repo.add_package(cleo) - demo = get_package("demo", "0.1.2") - demo.source_type = "git" - demo.source_url = "https://github.com/demo/demo.git" - demo.source_reference = "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24" + demo = Package( + "demo", + "0.1.2", + source_type="git", + source_url="https://github.com/demo/demo.git", + source_reference=ref[list(ref.keys())[0]], + source_resolved_reference="9cf87a285a2d3fbb0b9fa621997b3acc3631ed24", + ) git_config = {demo.source_type: demo.source_url} git_config.update(ref) - package.add_dependency("demo", git_config) + package.add_dependency(Factory.create_dependency("demo", git_config)) ops = solver.solve() @@ -1031,14 +1106,17 @@ def test_solver_can_resolve_git_dependencies_with_ref(solver, repo, package, ref op = ops[1] assert op.package.source_type == "git" - assert op.package.source_reference.startswith("9cf87a2") + assert op.package.source_reference == ref[list(ref.keys())[0]] + assert op.package.source_resolved_reference.startswith("9cf87a2") def test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requirement_is_compatible( solver, repo, package ): solver.provider.set_package_python_versions("~2.7 || ^3.4") - package.add_dependency("A", {"version": "^1.0", "python": "^3.6"}) + package.add_dependency( + Factory.create_dependency("A", {"version": "^1.0", "python": "^3.6"}) + ) package_a = get_package("A", "1.0.0") package_a.python_versions = ">=3.6" @@ -1054,12 +1132,16 @@ def test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requir solver, repo, package ): solver.provider.set_package_python_versions("~2.7 || ^3.4") - package.add_dependency("A", {"version": "^1.0", "python": "^3.6"}) - package.add_dependency("B", {"version": "^1.0", "python": "^3.5.3"}) + package.add_dependency( + Factory.create_dependency("A", {"version": "^1.0", "python": "^3.6"}) + ) + package.add_dependency( + Factory.create_dependency("B", {"version": "^1.0", "python": "^3.5.3"}) + ) package_a = get_package("A", "1.0.0") package_a.python_versions = ">=3.6" - package_a.add_dependency("B", "^1.0") + package_a.add_dependency(Factory.create_dependency("B", "^1.0")) package_b = get_package("B", "1.0.0") package_b.python_versions = ">=3.5.3" @@ -1082,7 +1164,9 @@ def test_solver_triggers_conflict_for_dependency_python_not_fully_compatible_wit solver, repo, package ): solver.provider.set_package_python_versions("~2.7 || ^3.4") - package.add_dependency("A", {"version": "^1.0", "python": "^3.5"}) + package.add_dependency( + Factory.create_dependency("A", {"version": "^1.0", "python": "^3.5"}) + ) package_a = get_package("A", "1.0.0") package_a.python_versions = ">=3.6" @@ -1097,7 +1181,9 @@ def test_solver_finds_compatible_package_for_dependency_python_not_fully_compati solver, repo, package ): solver.provider.set_package_python_versions("~2.7 || ^3.4") - package.add_dependency("A", {"version": "^1.0", "python": "^3.5"}) + package.add_dependency( + Factory.create_dependency("A", {"version": "^1.0", "python": "^3.5"}) + ) package_a101 = get_package("A", "1.0.1") package_a101.python_versions = ">=3.6" @@ -1121,7 +1207,9 @@ def test_solver_does_not_trigger_new_resolution_on_duplicate_dependencies_if_onl dep2 = dependency_from_pep_508('B (>=2.0); extra == "bar"') dep2.activate() - package.add_dependency("A", {"version": "^1.0", "extras": ["foo", "bar"]}) + package.add_dependency( + Factory.create_dependency("A", {"version": "^1.0", "extras": ["foo", "bar"]}) + ) package_a = get_package("A", "1.0.0") package_a.extras = {"foo": [dep1], "bar": [dep2]} @@ -1153,8 +1241,10 @@ def test_solver_does_not_raise_conflict_for_locked_conditional_dependencies( solver, repo, package ): solver.provider.set_package_python_versions("~2.7 || ^3.4") - package.add_dependency("A", {"version": "^1.0", "python": "^3.6"}) - package.add_dependency("B", "^1.0") + package.add_dependency( + Factory.create_dependency("A", {"version": "^1.0", "python": "^3.6"}) + ) + package.add_dependency(Factory.create_dependency("B", "^1.0")) package_a = get_package("A", "1.0.0") package_a.python_versions = ">=3.6" @@ -1182,19 +1272,23 @@ def test_solver_does_not_raise_conflict_for_locked_conditional_dependencies( def test_solver_returns_extras_if_requested_in_dependencies_and_not_in_root_package( solver, repo, package ): - package.add_dependency("A") - package.add_dependency("B") - package.add_dependency("C") + package.add_dependency(Factory.create_dependency("A", "*")) + package.add_dependency(Factory.create_dependency("B", "*")) + package.add_dependency(Factory.create_dependency("C", "*")) package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") package_c = get_package("C", "1.0") package_d = get_package("D", "1.0") - package_b.add_dependency("C", {"version": "^1.0", "extras": ["foo"]}) + package_b.add_dependency( + Factory.create_dependency("C", {"version": "^1.0", "extras": ["foo"]}) + ) - package_c.add_dependency("D", {"version": "^1.0", "optional": True}) - package_c.extras = {"foo": [get_dependency("D", "^1.0")]} + package_c.add_dependency( + Factory.create_dependency("D", {"version": "^1.0", "optional": True}) + ) + package_c.extras = {"foo": [Factory.create_dependency("D", "^1.0")]} repo.add_package(package_a) repo.add_package(package_b) @@ -1217,13 +1311,13 @@ def test_solver_returns_extras_if_requested_in_dependencies_and_not_in_root_pack def test_solver_should_not_resolve_prerelease_version_if_not_requested( solver, repo, package ): - package.add_dependency("A", "~1.8.0") - package.add_dependency("B", "^0.5.0") + package.add_dependency(Factory.create_dependency("A", "~1.8.0")) + package.add_dependency(Factory.create_dependency("B", "^0.5.0")) package_a185 = get_package("A", "1.8.5") package_a19b1 = get_package("A", "1.9b1") package_b = get_package("B", "0.5.0") - package_b.add_dependency("A", ">=1.9b1") + package_b.add_dependency(Factory.create_dependency("A", ">=1.9b1")) repo.add_package(package_a185) repo.add_package(package_a19b1) @@ -1237,8 +1331,8 @@ def test_solver_ignores_dependencies_with_incompatible_python_full_version_marke solver, repo, package ): solver.provider.set_package_python_versions("^3.6") - package.add_dependency("A", "^1.0") - package.add_dependency("B", "^2.0") + package.add_dependency(Factory.create_dependency("A", "^1.0")) + package.add_dependency(Factory.create_dependency("B", "^2.0")) package_a = get_package("A", "1.0.0") package_a.requires.append( @@ -1271,21 +1365,30 @@ def test_solver_git_dependencies_update(solver, repo, package, installed): repo.add_package(pendulum) repo.add_package(cleo) - demo_installed = get_package("demo", "0.1.2") - demo_installed.source_type = "git" - demo_installed.source_url = "https://github.com/demo/demo.git" - demo_installed.source_reference = "123456" + demo_installed = Package( + "demo", + "0.1.2", + source_type="git", + source_url="https://github.com/demo/demo.git", + source_reference="master", + source_resolved_reference="123456", + ) + demo = Package( + "demo", + "0.1.2", + source_type="git", + source_url="https://github.com/demo/demo.git", + source_reference="master", + source_resolved_reference="9cf87a285a2d3fbb0b9fa621997b3acc3631ed24", + ) installed.add_package(demo_installed) - package.add_dependency("demo", {"git": "https://github.com/demo/demo.git"}) + package.add_dependency( + Factory.create_dependency("demo", {"git": "https://github.com/demo/demo.git"}) + ) ops = solver.solve() - demo = get_package("demo", "0.1.2") - demo.source_type = "git" - demo.source_url = "https://github.com/demo/demo.git" - demo.source_reference = "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24" - check_solver_result( ops, [ @@ -1298,8 +1401,9 @@ def test_solver_git_dependencies_update(solver, repo, package, installed): assert op.job_type == "update" assert op.package.source_type == "git" - assert op.package.source_reference.startswith("9cf87a2") - assert op.initial_package.source_reference == "123456" + assert op.package.source_reference == "master" + assert op.package.source_resolved_reference.startswith("9cf87a2") + assert op.initial_package.source_resolved_reference == "123456" def test_solver_git_dependencies_update_skipped(solver, repo, package, installed): @@ -1308,13 +1412,19 @@ def test_solver_git_dependencies_update_skipped(solver, repo, package, installed repo.add_package(pendulum) repo.add_package(cleo) - demo = get_package("demo", "0.1.2") - demo.source_type = "git" - demo.source_url = "https://github.com/demo/demo.git" - demo.source_reference = "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24" + demo = Package( + "demo", + "0.1.2", + source_type="git", + source_url="https://github.com/demo/demo.git", + source_reference="master", + source_resolved_reference="9cf87a285a2d3fbb0b9fa621997b3acc3631ed24", + ) installed.add_package(demo) - package.add_dependency("demo", {"git": "https://github.com/demo/demo.git"}) + package.add_dependency( + Factory.create_dependency("demo", {"git": "https://github.com/demo/demo.git"}) + ) ops = solver.solve() @@ -1335,14 +1445,20 @@ def test_solver_git_dependencies_short_hash_update_skipped( repo.add_package(pendulum) repo.add_package(cleo) - demo = get_package("demo", "0.1.2") - demo.source_type = "git" - demo.source_url = "https://github.com/demo/demo.git" - demo.source_reference = "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24" + demo = Package( + "demo", + "0.1.2", + source_type="git", + source_url="https://github.com/demo/demo.git", + source_reference="master", + source_resolved_reference="9cf87a285a2d3fbb0b9fa621997b3acc3631ed24", + ) installed.add_package(demo) package.add_dependency( - "demo", {"git": "https://github.com/demo/demo.git", "rev": "9cf87a2"} + Factory.create_dependency( + "demo", {"git": "https://github.com/demo/demo.git", "rev": "9cf87a2"} + ) ) ops = solver.solve() @@ -1351,7 +1467,18 @@ def test_solver_git_dependencies_short_hash_update_skipped( ops, [ {"job": "install", "package": pendulum}, - {"job": "install", "package": demo, "skipped": True}, + { + "job": "install", + "package": Package( + "demo", + "0.1.2", + source_type="git", + source_url="https://github.com/demo/demo.git", + source_reference="9cf87a285a2d3fbb0b9fa621997b3acc3631ed24", + source_resolved_reference="9cf87a285a2d3fbb0b9fa621997b3acc3631ed24", + ), + "skipped": True, + }, ], ) @@ -1369,13 +1496,11 @@ def test_solver_can_resolve_directory_dependencies(solver, repo, package): / "demo" ).as_posix() - package.add_dependency("demo", {"path": path}) + package.add_dependency(Factory.create_dependency("demo", {"path": path})) ops = solver.solve() - demo = get_package("demo", "0.1.2") - demo.source_type = "directory" - demo.source_url = path + demo = Package("demo", "0.1.2", source_type="directory", source_url=path) check_solver_result( ops, @@ -1405,13 +1530,13 @@ def test_solver_can_resolve_directory_dependencies_with_extras(solver, repo, pac / "demo" ).as_posix() - package.add_dependency("demo", {"path": path, "extras": ["foo"]}) + package.add_dependency( + Factory.create_dependency("demo", {"path": path, "extras": ["foo"]}) + ) ops = solver.solve() - demo = get_package("demo", "0.1.2") - demo.source_type = "directory" - demo.source_url = path + demo = Package("demo", "0.1.2", source_type="directory", source_url=path) check_solver_result( ops, @@ -1441,13 +1566,11 @@ def test_solver_can_resolve_sdist_dependencies(solver, repo, package): / "demo-0.1.0.tar.gz" ).as_posix() - package.add_dependency("demo", {"path": path}) + package.add_dependency(Factory.create_dependency("demo", {"path": path})) ops = solver.solve() - demo = get_package("demo", "0.1.0") - demo.source_type = "file" - demo.source_url = path + demo = Package("demo", "0.1.0", source_type="file", source_url=path) check_solver_result( ops, @@ -1475,13 +1598,13 @@ def test_solver_can_resolve_sdist_dependencies_with_extras(solver, repo, package / "demo-0.1.0.tar.gz" ).as_posix() - package.add_dependency("demo", {"path": path, "extras": ["foo"]}) + package.add_dependency( + Factory.create_dependency("demo", {"path": path, "extras": ["foo"]}) + ) ops = solver.solve() - demo = get_package("demo", "0.1.0") - demo.source_type = "file" - demo.source_url = path + demo = Package("demo", "0.1.0", source_type="file", source_url=path) check_solver_result( ops, @@ -1511,13 +1634,11 @@ def test_solver_can_resolve_wheel_dependencies(solver, repo, package): / "demo-0.1.0-py2.py3-none-any.whl" ).as_posix() - package.add_dependency("demo", {"path": path}) + package.add_dependency(Factory.create_dependency("demo", {"path": path})) ops = solver.solve() - demo = get_package("demo", "0.1.0") - demo.source_type = "file" - demo.source_url = path + demo = Package("demo", "0.1.0", source_type="file", source_url=path) check_solver_result( ops, @@ -1545,13 +1666,13 @@ def test_solver_can_resolve_wheel_dependencies_with_extras(solver, repo, package / "demo-0.1.0-py2.py3-none-any.whl" ).as_posix() - package.add_dependency("demo", {"path": path, "extras": ["foo"]}) + package.add_dependency( + Factory.create_dependency("demo", {"path": path, "extras": ["foo"]}) + ) ops = solver.solve() - demo = get_package("demo", "0.1.0") - demo.source_type = "file" - demo.source_url = path + demo = Package("demo", "0.1.0", source_type="file", source_url=path) check_solver_result( ops, @@ -1578,15 +1699,33 @@ def test_solver_can_solve_with_legacy_repository_using_proper_dists( solver = Solver(package, pool, installed, locked, io) - package.add_dependency("isort", "4.3.4") + package.add_dependency(Factory.create_dependency("isort", "4.3.4")) ops = solver.solve() check_solver_result( ops, [ - {"job": "install", "package": get_package("futures", "3.2.0")}, - {"job": "install", "package": get_package("isort", "4.3.4")}, + { + "job": "install", + "package": Package( + "futures", + "3.2.0", + source_type="legacy", + source_url=repo.url, + source_reference=repo.name, + ), + }, + { + "job": "install", + "package": Package( + "isort", + "4.3.4", + source_type="legacy", + source_url=repo.url, + source_reference=repo.name, + ), + }, ], ) @@ -1604,12 +1743,24 @@ def test_solver_can_solve_with_legacy_repository_using_proper_python_compatible_ solver = Solver(package, pool, installed, locked, io) - package.add_dependency("isort", "4.3.4") + package.add_dependency(Factory.create_dependency("isort", "4.3.4")) ops = solver.solve() check_solver_result( - ops, [{"job": "install", "package": get_package("isort", "4.3.4")}] + ops, + [ + { + "job": "install", + "package": Package( + "isort", + "4.3.4", + source_type="legacy", + source_url=repo.url, + source_reference=repo.name, + ), + } + ], ) @@ -1621,7 +1772,7 @@ def test_solver_skips_invalid_versions(package, installed, locked, io): solver = Solver(package, pool, installed, locked, io) - package.add_dependency("trackpy", "^0.4") + package.add_dependency(Factory.create_dependency("trackpy", "^0.4")) ops = solver.solve() @@ -1631,8 +1782,12 @@ def test_solver_skips_invalid_versions(package, installed, locked, io): def test_multiple_constraints_on_root(package, solver, repo): - package.add_dependency("foo", {"version": "^1.0", "python": "^2.7"}) - package.add_dependency("foo", {"version": "^2.0", "python": "^3.7"}) + package.add_dependency( + Factory.create_dependency("foo", {"version": "^1.0", "python": "^2.7"}) + ) + package.add_dependency( + Factory.create_dependency("foo", {"version": "^2.0", "python": "^3.7"}) + ) foo15 = get_package("foo", "1.5.0") foo25 = get_package("foo", "2.5.0") @@ -1652,7 +1807,7 @@ def test_solver_chooses_most_recent_version_amongst_repositories( package, installed, locked, io ): package.python_versions = "^3.7" - package.add_dependency("tomlkit", {"version": "^0.5"}) + package.add_dependency(Factory.create_dependency("tomlkit", {"version": "^0.5"})) repo = MockLegacyRepository() pool = Pool([repo, MockPyPIRepository()]) @@ -1665,15 +1820,17 @@ def test_solver_chooses_most_recent_version_amongst_repositories( ops, [{"job": "install", "package": get_package("tomlkit", "0.5.3")}] ) - assert "" == ops[0].package.source_type - assert "" == ops[0].package.source_url + assert ops[0].package.source_type is None + assert ops[0].package.source_url is None def test_solver_chooses_from_correct_repository_if_forced( package, installed, locked, io ): package.python_versions = "^3.7" - package.add_dependency("tomlkit", {"version": "^0.5", "source": "legacy"}) + package.add_dependency( + Factory.create_dependency("tomlkit", {"version": "^0.5", "source": "legacy"}) + ) repo = MockLegacyRepository() pool = Pool([repo, MockPyPIRepository()]) @@ -1683,7 +1840,19 @@ def test_solver_chooses_from_correct_repository_if_forced( ops = solver.solve() check_solver_result( - ops, [{"job": "install", "package": get_package("tomlkit", "0.5.2")}] + ops, + [ + { + "job": "install", + "package": Package( + "tomlkit", + "0.5.2", + source_type="legacy", + source_url=repo.url, + source_reference=repo.name, + ), + } + ], ) assert "http://legacy.foo.bar" == ops[0].package.source_url @@ -1693,12 +1862,14 @@ def test_solver_chooses_from_correct_repository_if_forced_and_transitive_depende package, installed, locked, io ): package.python_versions = "^3.7" - package.add_dependency("foo", "^1.0") - package.add_dependency("tomlkit", {"version": "^0.5", "source": "legacy"}) + package.add_dependency(Factory.create_dependency("foo", "^1.0")) + package.add_dependency( + Factory.create_dependency("tomlkit", {"version": "^0.5", "source": "legacy"}) + ) repo = Repository() foo = get_package("foo", "1.0.0") - foo.add_dependency("tomlkit", "^0.5.0") + foo.add_dependency(Factory.create_dependency("tomlkit", "^0.5.0")) repo.add_package(foo) pool = Pool([MockLegacyRepository(), repo, MockPyPIRepository()]) @@ -1709,22 +1880,31 @@ def test_solver_chooses_from_correct_repository_if_forced_and_transitive_depende check_solver_result( ops, [ - {"job": "install", "package": get_package("tomlkit", "0.5.2")}, + { + "job": "install", + "package": Package( + "tomlkit", + "0.5.2", + source_type="legacy", + source_url="http://legacy.foo.bar", + source_reference="legacy", + ), + }, {"job": "install", "package": foo}, ], ) assert "http://legacy.foo.bar" == ops[0].package.source_url - assert "" == ops[1].package.source_type - assert "" == ops[1].package.source_url + assert ops[1].package.source_type is None + assert ops[1].package.source_url is None def test_solver_does_not_choose_from_secondary_repository_by_default( package, installed, locked, io ): package.python_versions = "^3.7" - package.add_dependency("clikit", {"version": "^0.2.0"}) + package.add_dependency(Factory.create_dependency("clikit", {"version": "^0.2.0"})) pool = Pool() pool.add_repository(MockPyPIRepository(), secondary=True) @@ -1737,21 +1917,41 @@ def test_solver_does_not_choose_from_secondary_repository_by_default( check_solver_result( ops, [ - {"job": "install", "package": get_package("pastel", "0.1.0")}, + { + "job": "install", + "package": Package( + "pastel", + "0.1.0", + source_type="legacy", + source_url="http://legacy.foo.bar", + source_reference="legacy", + ), + }, {"job": "install", "package": get_package("pylev", "1.3.0")}, - {"job": "install", "package": get_package("clikit", "0.2.4")}, + { + "job": "install", + "package": Package( + "clikit", + "0.2.4", + source_type="legacy", + source_url="http://legacy.foo.bar", + source_reference="legacy", + ), + }, ], ) assert "http://legacy.foo.bar" == ops[0].package.source_url - assert "" == ops[1].package.source_type - assert "" == ops[1].package.source_url + assert ops[1].package.source_type is None + assert ops[1].package.source_url is None assert "http://legacy.foo.bar" == ops[2].package.source_url def test_solver_chooses_from_secondary_if_explicit(package, installed, locked, io): package.python_versions = "^3.7" - package.add_dependency("clikit", {"version": "^0.2.0", "source": "PyPI"}) + package.add_dependency( + Factory.create_dependency("clikit", {"version": "^0.2.0", "source": "PyPI"}) + ) pool = Pool() pool.add_repository(MockPyPIRepository(), secondary=True) @@ -1764,17 +1964,26 @@ def test_solver_chooses_from_secondary_if_explicit(package, installed, locked, i check_solver_result( ops, [ - {"job": "install", "package": get_package("pastel", "0.1.0")}, + { + "job": "install", + "package": Package( + "pastel", + "0.1.0", + source_type="legacy", + source_url="http://legacy.foo.bar", + source_reference="legacy", + ), + }, {"job": "install", "package": get_package("pylev", "1.3.0")}, {"job": "install", "package": get_package("clikit", "0.2.4")}, ], ) assert "http://legacy.foo.bar" == ops[0].package.source_url - assert "" == ops[1].package.source_type - assert "" == ops[1].package.source_url - assert "" == ops[2].package.source_type - assert "" == ops[2].package.source_url + assert ops[1].package.source_type is None + assert ops[1].package.source_url is None + assert ops[2].package.source_type is None + assert ops[2].package.source_url is None def test_solver_discards_packages_with_empty_markers( @@ -1782,14 +1991,18 @@ def test_solver_discards_packages_with_empty_markers( ): package.python_versions = "~2.7 || ^3.4" package.add_dependency( - "a", {"version": "^0.1.0", "markers": "python_version >= '3.4'"} + Factory.create_dependency( + "a", {"version": "^0.1.0", "markers": "python_version >= '3.4'"} + ) ) package_a = get_package("a", "0.1.0") package_a.add_dependency( - "b", {"version": "^0.1.0", "markers": "python_version < '3.2'"} + Factory.create_dependency( + "b", {"version": "^0.1.0", "markers": "python_version < '3.2'"} + ) ) - package_a.add_dependency("c", "^0.2.0") + package_a.add_dependency(Factory.create_dependency("c", "^0.2.0")) package_b = get_package("b", "0.1.0") package_c = get_package("c", "0.2.0") repo.add_package(package_a) @@ -1813,8 +2026,16 @@ def test_solver_does_not_raise_conflict_for_conditional_dev_dependencies( solver, repo, package ): solver.provider.set_package_python_versions("~2.7 || ^3.5") - package.add_dependency("A", {"version": "^1.0", "python": "~2.7"}, category="dev") - package.add_dependency("A", {"version": "^2.0", "python": "^3.5"}, category="dev") + package.add_dependency( + Factory.create_dependency( + "A", {"version": "^1.0", "python": "~2.7"}, category="dev" + ) + ) + package.add_dependency( + Factory.create_dependency( + "A", {"version": "^2.0", "python": "^3.5"}, category="dev" + ) + ) package_a100 = get_package("A", "1.0.0") package_a200 = get_package("A", "2.0.0") @@ -1837,12 +2058,18 @@ def test_solver_does_not_loop_indefinitely_on_duplicate_constraints_with_extras( solver, repo, package ): solver.provider.set_package_python_versions("~2.7 || ^3.5") - package.add_dependency("requests", {"version": "^2.22.0", "extras": ["security"]}) + package.add_dependency( + Factory.create_dependency( + "requests", {"version": "^2.22.0", "extras": ["security"]} + ) + ) requests = get_package("requests", "2.22.0") - requests.add_dependency("idna", ">=2.5,<2.9") + requests.add_dependency(Factory.create_dependency("idna", ">=2.5,<2.9")) requests.add_dependency( - "idna", {"version": ">=2.0.0", "markers": "extra == 'security'"} + Factory.create_dependency( + "idna", {"version": ">=2.0.0", "markers": "extra == 'security'"} + ) ) requests.extras["security"] = [get_dependency("idna", ">=2.0.0")] idna = get_package("idna", "2.8") @@ -1861,13 +2088,19 @@ def test_solver_does_not_loop_indefinitely_on_duplicate_constraints_with_extras( def test_solver_does_not_fail_with_locked_git_and_non_git_dependencies( solver, repo, package, locked, pool, installed, io ): - package.add_dependency("demo", {"git": "https://github.com/demo/demo.git"}) - package.add_dependency("a", "^1.2.3") + package.add_dependency( + Factory.create_dependency("demo", {"git": "https://github.com/demo/demo.git"}) + ) + package.add_dependency(Factory.create_dependency("a", "^1.2.3")) - git_package = get_package("demo", "0.1.2") - git_package.source_type = "git" - git_package.source_url = "https://github.com/demo/demo.git" - git_package.source_reference = "commit" + git_package = Package( + "demo", + "0.1.2", + source_type="git", + source_url="https://github.com/demo/demo.git", + source_reference="master", + source_resolved_reference="commit", + ) installed.add_package(git_package) @@ -1875,6 +2108,7 @@ def test_solver_does_not_fail_with_locked_git_and_non_git_dependencies( locked.add_package(git_package) repo.add_package(get_package("a", "1.2.3")) + repo.add_package(Package("pendulum", "2.1.2")) solver = Solver(package, pool, installed, locked, io) @@ -1891,9 +2125,15 @@ def test_solver_does_not_fail_with_locked_git_and_non_git_dependencies( def test_ignore_python_constraint_no_overlap_dependencies(solver, repo, package): pytest = get_package("demo", "1.0.0") - pytest.add_dependency("configparser", {"version": "^1.2.3", "python": "<3.2"}) + pytest.add_dependency( + Factory.create_dependency( + "configparser", {"version": "^1.2.3", "python": "<3.2"} + ) + ) - package.add_dependency("demo", {"version": "^1.0.0", "python": "^3.6"}) + package.add_dependency( + Factory.create_dependency("demo", {"version": "^1.0.0", "python": "^3.6"}) + ) repo.add_package(pytest) repo.add_package(get_package("configparser", "1.2.3")) @@ -1909,12 +2149,14 @@ def test_solver_should_not_go_into_an_infinite_loop_on_duplicate_dependencies( solver, repo, package ): solver.provider.set_package_python_versions("~2.7 || ^3.5") - package.add_dependency("A", "^1.0") + package.add_dependency(Factory.create_dependency("A", "^1.0")) package_a = get_package("A", "1.0.0") - package_a.add_dependency("B") + package_a.add_dependency(Factory.create_dependency("B", "*")) package_a.add_dependency( - "B", {"version": "^1.0", "markers": "implementation_name == 'pypy'"} + Factory.create_dependency( + "B", {"version": "^1.0", "markers": "implementation_name == 'pypy'"} + ) ) package_b20 = get_package("B", "2.0.0") @@ -1964,7 +2206,7 @@ def test_solver_cannot_choose_another_version_for_directory_dependencies( pendulum = get_package("pendulum", "2.0.3") demo = get_package("demo", "0.1.0") foo = get_package("foo", "1.2.3") - foo.add_dependency("demo", "<0.1.2") + foo.add_dependency(Factory.create_dependency("demo", "<0.1.2")) repo.add_package(foo) repo.add_package(demo) repo.add_package(pendulum) @@ -1978,8 +2220,8 @@ def test_solver_cannot_choose_another_version_for_directory_dependencies( / "demo" ).as_posix() - package.add_dependency("demo", {"path": path}) - package.add_dependency("foo", "^1.2.3") + package.add_dependency(Factory.create_dependency("demo", {"path": path})) + package.add_dependency(Factory.create_dependency("foo", "^1.2.3")) # This is not solvable since the demo version is pinned # via the directory dependency @@ -1993,7 +2235,7 @@ def test_solver_cannot_choose_another_version_for_file_dependencies( pendulum = get_package("pendulum", "2.0.3") demo = get_package("demo", "0.0.8") foo = get_package("foo", "1.2.3") - foo.add_dependency("demo", "<0.1.0") + foo.add_dependency(Factory.create_dependency("demo", "<0.1.0")) repo.add_package(foo) repo.add_package(demo) repo.add_package(pendulum) @@ -2005,8 +2247,8 @@ def test_solver_cannot_choose_another_version_for_file_dependencies( / "demo-0.1.0-py2.py3-none-any.whl" ).as_posix() - package.add_dependency("demo", {"path": path}) - package.add_dependency("foo", "^1.2.3") + package.add_dependency(Factory.create_dependency("demo", {"path": path})) + package.add_dependency(Factory.create_dependency("foo", "^1.2.3")) # This is not solvable since the demo version is pinned # via the file dependency @@ -2020,13 +2262,15 @@ def test_solver_cannot_choose_another_version_for_git_dependencies( pendulum = get_package("pendulum", "2.0.3") demo = get_package("demo", "0.0.8") foo = get_package("foo", "1.2.3") - foo.add_dependency("demo", "<0.1.0") + foo.add_dependency(Factory.create_dependency("demo", "<0.1.0")) repo.add_package(foo) repo.add_package(demo) repo.add_package(pendulum) - package.add_dependency("demo", {"git": "https://github.com/demo/demo.git"}) - package.add_dependency("foo", "^1.2.3") + package.add_dependency( + Factory.create_dependency("demo", {"git": "https://github.com/demo/demo.git"}) + ) + package.add_dependency(Factory.create_dependency("foo", "^1.2.3")) # This is not solvable since the demo version is pinned # via the file dependency @@ -2053,15 +2297,18 @@ def test_solver_cannot_choose_another_version_for_url_dependencies( pendulum = get_package("pendulum", "2.0.3") demo = get_package("demo", "0.0.8") foo = get_package("foo", "1.2.3") - foo.add_dependency("demo", "<0.1.0") + foo.add_dependency(Factory.create_dependency("demo", "<0.1.0")) repo.add_package(foo) repo.add_package(demo) repo.add_package(pendulum) package.add_dependency( - "demo", {"url": "https://foo.bar/distributions/demo-0.1.0-py2.py3-none-any.whl"} + Factory.create_dependency( + "demo", + {"url": "https://foo.bar/distributions/demo-0.1.0-py2.py3-none-any.whl"}, + ) ) - package.add_dependency("foo", "^1.2.3") + package.add_dependency(Factory.create_dependency("foo", "^1.2.3")) # This is not solvable since the demo version is pinned # via the git dependency @@ -2072,12 +2319,15 @@ def test_solver_cannot_choose_another_version_for_url_dependencies( def test_solver_should_not_update_same_version_packages_if_installed_has_no_source_type( solver, repo, package, installed ): - package.add_dependency("foo", "1.0.0") + package.add_dependency(Factory.create_dependency("foo", "1.0.0")) - foo = get_package("foo", "1.0.0") - foo.source_type = "legacy" - foo.source_reference = "custom" - foo.source_url = "https://foo.bar" + foo = Package( + "foo", + "1.0.0", + source_type="legacy", + source_url="https://foo.bar", + source_reference="custom", + ) repo.add_package(foo) installed.add_package(get_package("foo", "1.0.0")) @@ -2090,10 +2340,14 @@ def test_solver_should_use_the_python_constraint_from_the_environment_if_availab solver, repo, package, installed ): solver.provider.set_package_python_versions("~2.7 || ^3.5") - package.add_dependency("A", "^1.0") + package.add_dependency(Factory.create_dependency("A", "^1.0")) a = get_package("A", "1.0.0") - a.add_dependency("B", {"version": "^1.0.0", "markers": 'python_version < "3.2"'}) + a.add_dependency( + Factory.create_dependency( + "B", {"version": "^1.0.0", "markers": 'python_version < "3.2"'} + ) + ) b = get_package("B", "1.0.0") b.python_versions = ">=2.6, <3" @@ -2113,16 +2367,24 @@ def test_solver_should_resolve_all_versions_for_multiple_duplicate_dependencies( ): package.python_versions = "~2.7 || ^3.5" package.add_dependency( - "A", {"version": "^1.0", "markers": "python_version < '3.5'"} + Factory.create_dependency( + "A", {"version": "^1.0", "markers": "python_version < '3.5'"} + ) ) package.add_dependency( - "A", {"version": "^2.0", "markers": "python_version >= '3.5'"} + Factory.create_dependency( + "A", {"version": "^2.0", "markers": "python_version >= '3.5'"} + ) ) package.add_dependency( - "B", {"version": "^3.0", "markers": "python_version < '3.5'"} + Factory.create_dependency( + "B", {"version": "^3.0", "markers": "python_version < '3.5'"} + ) ) package.add_dependency( - "B", {"version": "^4.0", "markers": "python_version >= '3.5'"} + Factory.create_dependency( + "B", {"version": "^4.0", "markers": "python_version >= '3.5'"} + ) ) package_a10 = get_package("A", "1.0.0") @@ -2153,7 +2415,9 @@ def test_solver_should_not_raise_errors_for_irrelevant_python_constraints( ): package.python_versions = "^3.6" solver.provider.set_package_python_versions("^3.6") - package.add_dependency("dataclasses", {"version": "^0.7", "python": "<3.7"}) + package.add_dependency( + Factory.create_dependency("dataclasses", {"version": "^0.7", "python": "<3.7"}) + ) dataclasses = get_package("dataclasses", "0.7") dataclasses.python_versions = ">=3.6, <3.7" @@ -2162,3 +2426,40 @@ def test_solver_should_not_raise_errors_for_irrelevant_python_constraints( ops = solver.solve() check_solver_result(ops, [{"job": "install", "package": dataclasses}]) + + +def test_solver_can_resolve_transitive_extras(solver, repo, package): + package.add_dependency(Factory.create_dependency("requests", "^2.24.0")) + package.add_dependency(Factory.create_dependency("PyOTA", "^2.1.0")) + + requests = get_package("requests", "2.24.0") + requests.add_dependency(Factory.create_dependency("certifi", ">=2017.4.17")) + dep = get_dependency("PyOpenSSL", ">=0.14") + dep.in_extras.append("security") + requests.add_dependency( + Factory.create_dependency("PyOpenSSL", {"version": ">=0.14", "optional": True}) + ) + requests.extras["security"] = [dep] + pyota = get_package("PyOTA", "2.1.0") + pyota.add_dependency( + Factory.create_dependency( + "requests", {"version": ">=2.24.0", "extras": ["security"]} + ) + ) + + repo.add_package(requests) + repo.add_package(pyota) + repo.add_package(get_package("certifi", "2017.4.17")) + repo.add_package(get_package("pyopenssl", "0.14")) + + ops = solver.solve() + + check_solver_result( + ops, + [ + {"job": "install", "package": get_package("certifi", "2017.4.17")}, + {"job": "install", "package": get_package("pyopenssl", "0.14")}, + {"job": "install", "package": requests}, + {"job": "install", "package": pyota}, + ], + ) diff --git a/tests/repositories/test_installed_repository.py b/tests/repositories/test_installed_repository.py index d58774d8cad..79ac262671c 100644 --- a/tests/repositories/test_installed_repository.py +++ b/tests/repositories/test_installed_repository.py @@ -156,5 +156,5 @@ def test_load_editable_with_import_package(repository): assert editable is not None assert editable.name == "editable-with-import" assert editable.version.text == "2.3.4" - assert editable.source_type == "" - assert editable.source_url == "" + assert editable.source_type is None + assert editable.source_url is None diff --git a/tests/repositories/test_legacy_repository.py b/tests/repositories/test_legacy_repository.py index 189de7fd294..1031a48f6c2 100644 --- a/tests/repositories/test_legacy_repository.py +++ b/tests/repositories/test_legacy_repository.py @@ -3,6 +3,7 @@ import pytest from poetry.core.packages import Dependency +from poetry.factory import Factory from poetry.repositories.auth import Auth from poetry.repositories.exceptions import PackageNotFound from poetry.repositories.exceptions import RepositoryError @@ -117,7 +118,10 @@ def test_get_package_information_skips_dependencies_with_invalid_constraints(): package.description == "Python Language Server for the Language Server Protocol" ) - assert sorted(package.requires, key=lambda r: r.name) == [ + assert 19 == len(package.requires) + assert sorted( + [r for r in package.requires if not r.is_optional()], key=lambda r: r.name + ) == [ Dependency("configparser", "*"), Dependency("future", ">=0.14.0"), Dependency("futures", "*"), @@ -142,7 +146,7 @@ def test_get_package_information_skips_dependencies_with_invalid_constraints(): def test_find_packages_no_prereleases(): repo = MockRepository() - packages = repo.find_packages("pyyaml") + packages = repo.find_packages(Factory.create_dependency("pyyaml", "*")) assert len(packages) == 1 @@ -154,7 +158,7 @@ def test_find_packages_no_prereleases(): @pytest.mark.parametrize("constraint,count", [("*", 1), (">=1", 0), (">=19.0.0a0", 1)]) def test_find_packages_only_prereleases(constraint, count): repo = MockRepository() - packages = repo.find_packages("black", constraint=constraint) + packages = repo.find_packages(Factory.create_dependency("black", constraint)) assert len(packages) == count @@ -167,7 +171,7 @@ def test_find_packages_only_prereleases(constraint, count): def test_find_packages_only_prereleases_empty_when_not_any(): repo = MockRepository() - packages = repo.find_packages("black", constraint=">=1") + packages = repo.find_packages(Factory.create_dependency("black", ">=1")) assert len(packages) == 0 @@ -213,6 +217,7 @@ def test_get_package_from_both_py2_and_py3_specific_wheels(): assert "ipython" == package.name assert "5.7.0" == package.version.text assert "*" == package.python_versions + assert 26 == len(package.requires) expected = [ Dependency("appnope", "*"), @@ -229,16 +234,17 @@ def test_get_package_from_both_py2_and_py3_specific_wheels(): Dependency("traitlets", ">=4.2"), Dependency("win-unicode-console", ">=0.5"), ] - assert expected == package.requires + required = [r for r in package.requires if not r.is_optional()] + assert expected == required - assert 'python_version == "2.7"' == str(package.requires[1].marker) + assert 'python_version == "2.7"' == str(required[1].marker) assert 'sys_platform == "win32" and python_version < "3.6"' == str( - package.requires[12].marker + required[12].marker ) assert 'python_version == "2.7" or python_version == "3.3"' == str( - package.requires[4].marker + required[4].marker ) - assert 'sys_platform != "win32"' == str(package.requires[5].marker) + assert 'sys_platform != "win32"' == str(required[5].marker) def test_get_package_with_dist_and_universal_py3_wheel(): @@ -265,7 +271,8 @@ def test_get_package_with_dist_and_universal_py3_wheel(): Dependency("typing", "*"), Dependency("win-unicode-console", ">=0.5"), ] - assert expected == sorted(package.requires, key=lambda dep: dep.name) + required = [r for r in package.requires if not r.is_optional()] + assert expected == sorted(required, key=lambda dep: dep.name) def test_get_package_retrieves_non_sha256_hashes(): diff --git a/tests/repositories/test_pypi_repository.py b/tests/repositories/test_pypi_repository.py index 61c588860ba..ef094f3f1d5 100644 --- a/tests/repositories/test_pypi_repository.py +++ b/tests/repositories/test_pypi_repository.py @@ -9,6 +9,7 @@ from requests.models import Response from poetry.core.packages import Dependency +from poetry.factory import Factory from poetry.repositories.pypi_repository import PyPiRepository from poetry.utils._compat import PY35 from poetry.utils._compat import Path @@ -56,21 +57,21 @@ def _download(self, url, dest): def test_find_packages(): repo = MockRepository() - packages = repo.find_packages("requests", "^2.18") + packages = repo.find_packages(Factory.create_dependency("requests", "^2.18")) assert len(packages) == 5 def test_find_packages_with_prereleases(): repo = MockRepository() - packages = repo.find_packages("toga", ">=0.3.0.dev2") + packages = repo.find_packages(Factory.create_dependency("toga", ">=0.3.0.dev2")) assert len(packages) == 7 def test_find_packages_does_not_select_prereleases_if_not_allowed(): repo = MockRepository() - packages = repo.find_packages("pyyaml") + packages = repo.find_packages(Factory.create_dependency("pyyaml", "*")) assert len(packages) == 1 @@ -78,7 +79,7 @@ def test_find_packages_does_not_select_prereleases_if_not_allowed(): @pytest.mark.parametrize("constraint,count", [("*", 1), (">=1", 0), (">=19.0.0a0", 1)]) def test_find_packages_only_prereleases(constraint, count): repo = MockRepository() - packages = repo.find_packages("black", constraint=constraint) + packages = repo.find_packages(Factory.create_dependency("black", constraint)) assert len(packages) == count @@ -89,7 +90,8 @@ def test_package(): package = repo.package("requests", "2.18.4") assert package.name == "requests" - assert len(package.requires) == 4 + assert len(package.requires) == 9 + assert len([r for r in package.requires if r.is_optional()]) == 5 assert len(package.extras["security"]) == 3 assert len(package.extras["socks"]) == 2 @@ -141,7 +143,8 @@ def test_fallback_can_read_setup_to_get_dependencies(): package = repo.package("sqlalchemy", "1.2.12") assert package.name == "sqlalchemy" - assert len(package.requires) == 0 + assert len(package.requires) == 9 + assert len([r for r in package.requires if r.is_optional()]) == 9 assert package.extras == { "mssql_pymssql": [Dependency("pymssql", "*")], @@ -162,7 +165,10 @@ def test_pypi_repository_supports_reading_bz2_files(): package = repo.package("twisted", "18.9.0") assert package.name == "twisted" - assert sorted(package.requires, key=lambda r: r.name) == [ + assert 28 == len(package.requires) + assert sorted( + [r for r in package.requires if not r.is_optional()], key=lambda r: r.name + ) == [ Dependency("attrs", ">=17.4.0"), Dependency("Automat", ">=0.3.0"), Dependency("constantly", ">=15.1"), @@ -198,7 +204,7 @@ def test_invalid_versions_ignored(): # the json metadata for this package contains one malformed version # and a correct one. - packages = repo.find_packages("pygame-music-grid") + packages = repo.find_packages(Factory.create_dependency("pygame-music-grid", "*")) assert len(packages) == 1 @@ -228,6 +234,6 @@ def test_urls(): def test_use_pypi_pretty_name(): repo = MockRepository(fallback=True) - package = repo.find_packages("twisted") + package = repo.find_packages(Factory.create_dependency("twisted", "*")) assert len(package) == 1 assert package[0].pretty_name == "Twisted" diff --git a/tests/test_factory.py b/tests/test_factory.py index 490dadc30d1..9b326c4b359 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -55,7 +55,7 @@ def test_create_poetry(): assert not requests.is_vcs() assert not requests.allows_prereleases() assert requests.is_optional() - assert requests.extras == ["security"] + assert requests.extras == frozenset(["security"]) pathlib2 = dependencies["pathlib2"] assert pathlib2.pretty_constraint == "^2.2" diff --git a/tests/utils/test_env.py b/tests/utils/test_env.py index 3151ebea10a..4b2cbc786d9 100644 --- a/tests/utils/test_env.py +++ b/tests/utils/test_env.py @@ -559,7 +559,6 @@ def test_remove_keeps_dir_if_not_deleteable(tmp_dir, manager, poetry, config, mo original_rmtree = shutil.rmtree def err_on_rm_venv_only(path, *args, **kwargs): - print(path) if path == str(venv_path): raise OSError(16, "Test error") # ERRNO 16: Device or resource busy else: diff --git a/tests/utils/test_exporter.py b/tests/utils/test_exporter.py index 0c05c907629..99fb48a18e9 100644 --- a/tests/utils/test_exporter.py +++ b/tests/utils/test_exporter.py @@ -8,10 +8,12 @@ from poetry.repositories.legacy_repository import LegacyRepository from poetry.utils._compat import Path from poetry.utils.exporter import Exporter +from poetry.utils.toml_file import TomlFile class Locker(BaseLocker): def __init__(self): + self._lock = TomlFile(Path.cwd().joinpath("poetry.lock")) self._locked = True self._content_hash = self._get_content_hash() diff --git a/tests/utils/test_extras.py b/tests/utils/test_extras.py index 41307eab67a..e06e48096df 100644 --- a/tests/utils/test_extras.py +++ b/tests/utils/test_extras.py @@ -1,19 +1,20 @@ import pytest from poetry.core.packages import Package +from poetry.factory import Factory from poetry.utils.extras import get_extra_package_names _PACKAGE_FOO = Package("foo", "0.1.0") _PACKAGE_SPAM = Package("spam", "0.2.0") _PACKAGE_BAR = Package("bar", "0.3.0") -_PACKAGE_BAR.add_dependency("foo") +_PACKAGE_BAR.add_dependency(Factory.create_dependency("foo", "*")) # recursive dependency _PACKAGE_BAZ = Package("baz", "0.4.0") -_PACKAGE_BAZ.add_dependency("quix") +_PACKAGE_BAZ.add_dependency(Factory.create_dependency("quix", "*")) _PACKAGE_QUIX = Package("quix", "0.5.0") -_PACKAGE_QUIX.add_dependency("baz") +_PACKAGE_QUIX.add_dependency(Factory.create_dependency("baz", "*")) @pytest.mark.parametrize(