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 8d072c3
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 6 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
43 changes: 43 additions & 0 deletions tests/puzzle/test_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -722,3 +722,46 @@ 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")
result = provider.search_for(dep)

assert len(result) == 1
assert result[0].package == 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

result = provider.search_for(dep)

assert len(result) == 1
assert result[0].package == 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

result = provider.search_for(dep)

assert len(result) == 1
assert result[0].package == repo_package

0 comments on commit 8d072c3

Please sign in to comment.