Skip to content

Commit

Permalink
fix: explicit source dependency is not satisfied by direct origin
Browse files Browse the repository at this point in the history
  • Loading branch information
ralbertazzi committed May 20, 2023
1 parent 093820a commit 298c906
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 7 deletions.
23 changes: 23 additions & 0 deletions docs/dependency-specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,29 @@ The constraints **must** have different requirements (like `python`)
otherwise it will cause an error when resolving dependencies.
{{% /note %}}

### Combining git / url / path dependencies with source repositories

Direct origin (`git`/ `url`/ `path`) dependencies can satisfy the requirement of a dependency that
doesn't explicitly specify a source, even when mutually exclusive markers are used. For instance
in the following example the url package will also be a valid solution for the second requirement:
```toml
foo = [
{ platform = "darwin", url = "https://example.com/example-1.0-py3-none-any.whl" },
{ platform = "linux", version = "^1.0" },
]
```

Sometimes you may instead want to use a direct origin dependency for specific conditions
(i.e. a compiled package that is not available on PyPI for a certain platform/architecture) while
falling back on source repositories in other cases. In this case you should explicitly ask for your
dependency to be satisfied by another `source`. For example:
```toml
foo = [
{ platform = "darwin", url = "https://example.com/foo-1.0.0-py3-none-macosx_11_0_arm64.whl" },
{ platform = "linux", version = "^1.0", source = "pypi" },
]
```

## Expanded dependency specification syntax

In the case of more complex dependency specifications, you may find that you
Expand Down
8 changes: 2 additions & 6 deletions src/poetry/puzzle/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,12 +280,8 @@ def search_for(self, dependency: Dependency) -> list[DependencyPackage]:
#
# We rely on the VersionSolver resolving direct-origin dependencies first.
direct_origin_package = self._direct_origin_packages.get(dependency.name)
if direct_origin_package is not None:
packages = (
[direct_origin_package]
if dependency.constraint.allows(direct_origin_package.version)
else []
)
if direct_origin_package and direct_origin_package.satisfies(dependency):
packages = [direct_origin_package]
return PackageCollection(dependency, packages)

packages = self._pool.find_packages(dependency)
Expand Down
37 changes: 36 additions & 1 deletion tests/puzzle/test_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def test_search_for(
Dependency("foo", ">=2"),
URLDependency("foo", SOME_URL),
[Package("foo", "3")],
[],
[Package("foo", "3")],
),
(
Dependency("foo", ">=1", extras=["bar"]),
Expand Down Expand Up @@ -722,3 +722,38 @@ def test_complete_package_fetches_optional_vcs_dependency_only_if_requested(
spy.assert_called()
else:
spy.assert_not_called()


def test_source_dependency_is_satisfied_by_direct_origin(
provider: Provider, repository: Repository
) -> None:
direct_origin_package = Package("foo", "1.1", source_type="url")
repository.add_package(Package("foo", "1.0"))
provider._direct_origin_packages = {"foo": direct_origin_package}
dep = Dependency("foo", ">=1")

assert provider.search_for(dep) == [direct_origin_package]


def test_explicit_source_dependency_is_not_satisfied_by_direct_origin(
provider: Provider, repository: Repository
) -> None:
repo_package = Package("foo", "1.0")
repository.add_package(repo_package)
provider._direct_origin_packages = {"foo": Package("foo", "1.1", source_type="url")}
dep = Dependency("foo", ">=1")
dep.source_name = repository.name

assert provider.search_for(dep) == [repo_package]


def test_source_dependency_is_not_satisfied_by_incompatible_direct_origin(
provider: Provider, repository: Repository
) -> None:
repo_package = Package("foo", "2.0")
repository.add_package(repo_package)
provider._direct_origin_packages = {"foo": Package("foo", "1.0", source_type="url")}
dep = Dependency("foo", ">=2")
dep.source_name = repository.name

assert provider.search_for(dep) == [repo_package]

0 comments on commit 298c906

Please sign in to comment.