diff --git a/src/poetry/mixology/version_solver.py b/src/poetry/mixology/version_solver.py index 2ce20639cca..fc1531cfc25 100644 --- a/src/poetry/mixology/version_solver.py +++ b/src/poetry/mixology/version_solver.py @@ -43,7 +43,8 @@ class DependencyCache: def __init__(self, provider: Provider) -> None: self.provider = provider self.cache: dict[ - tuple[str, str | None, str | None, str | None], list[DependencyPackage] + tuple[str, str | None, str | None, str | None, str | None], + list[DependencyPackage], ] = {} self.search_for = functools.lru_cache( maxsize=128 @@ -58,6 +59,7 @@ def _search_for(self, dependency: Dependency) -> list[DependencyPackage]: dependency.source_type, dependency.source_url, dependency.source_reference, + dependency.source_subdirectory, ) packages = self.cache.get(key) diff --git a/src/poetry/puzzle/provider.py b/src/poetry/puzzle/provider.py index f45158cba07..c097d32418a 100644 --- a/src/poetry/puzzle/provider.py +++ b/src/poetry/puzzle/provider.py @@ -574,7 +574,7 @@ def complete_package(self, package: DependencyPackage) -> DependencyPackage: # of source type, reference etc. are taking into consideration when duplicates # are identified. duplicates: dict[ - tuple[str, str | None, str | None, str | None], list[Dependency] + tuple[str, str | None, str | None, str | None, str | None], list[Dependency] ] = {} for dep in dependencies: key = ( @@ -582,6 +582,7 @@ def complete_package(self, package: DependencyPackage) -> DependencyPackage: dep.source_type, dep.source_url, dep.source_reference, + dep.source_subdirectory, ) if key not in duplicates: duplicates[key] = [] diff --git a/tests/fixtures/git/github.com/demo/subdirectories/one-copy/pyproject.toml b/tests/fixtures/git/github.com/demo/subdirectories/one-copy/pyproject.toml new file mode 100644 index 00000000000..39265efe4a3 --- /dev/null +++ b/tests/fixtures/git/github.com/demo/subdirectories/one-copy/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "one" +version = "1.0.0" +description = "Some description." +authors = [] +license = "MIT" + +[tool.poetry.dependencies] +python = "^3.7" diff --git a/tests/fixtures/git/github.com/demo/subdirectories/one/pyproject.toml b/tests/fixtures/git/github.com/demo/subdirectories/one/pyproject.toml new file mode 100644 index 00000000000..39265efe4a3 --- /dev/null +++ b/tests/fixtures/git/github.com/demo/subdirectories/one/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "one" +version = "1.0.0" +description = "Some description." +authors = [] +license = "MIT" + +[tool.poetry.dependencies] +python = "^3.7" diff --git a/tests/fixtures/git/github.com/demo/subdirectories/two/pyproject.toml b/tests/fixtures/git/github.com/demo/subdirectories/two/pyproject.toml new file mode 100644 index 00000000000..58fde435649 --- /dev/null +++ b/tests/fixtures/git/github.com/demo/subdirectories/two/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "two" +version = "2.0.0" +description = "Some description." +authors = [] +license = "MIT" + +[tool.poetry.dependencies] +python = "^3.7" diff --git a/tests/fixtures/with_conditional_path_deps/demo_one/pyproject.toml b/tests/fixtures/with_conditional_path_deps/demo_one/pyproject.toml new file mode 100644 index 00000000000..85dab468dd6 --- /dev/null +++ b/tests/fixtures/with_conditional_path_deps/demo_one/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "demo" +version = "1.2.3" +description = "Some description." +authors = [] +license = "MIT" + +[tool.poetry.dependencies] +python = "^3.7" diff --git a/tests/fixtures/with_conditional_path_deps/demo_two/pyproject.toml b/tests/fixtures/with_conditional_path_deps/demo_two/pyproject.toml new file mode 100644 index 00000000000..85dab468dd6 --- /dev/null +++ b/tests/fixtures/with_conditional_path_deps/demo_two/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "demo" +version = "1.2.3" +description = "Some description." +authors = [] +license = "MIT" + +[tool.poetry.dependencies] +python = "^3.7" diff --git a/tests/fixtures/with_conditional_path_deps/pyproject.toml b/tests/fixtures/with_conditional_path_deps/pyproject.toml new file mode 100644 index 00000000000..1e2ea754059 --- /dev/null +++ b/tests/fixtures/with_conditional_path_deps/pyproject.toml @@ -0,0 +1,13 @@ +[tool.poetry] +name = "sample" +version = "1.0.0" +description = "Sample Project" +authors = [] +license = "MIT" + +[tool.poetry.dependencies] +python = "^3.7" +demo = [ + { path = "demo_one", platform = "linux" }, + { path = "demo_two", platform = "win32" }, +] diff --git a/tests/mixology/version_solver/test_dependency_cache.py b/tests/mixology/version_solver/test_dependency_cache.py index bb03a1c4de2..c1645604e28 100644 --- a/tests/mixology/version_solver/test_dependency_cache.py +++ b/tests/mixology/version_solver/test_dependency_cache.py @@ -59,3 +59,65 @@ def test_solver_dependency_cache_respects_source_type( package_git.package.source_resolved_reference == "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24" ) + + +def test_solver_dependency_cache_respects_subdirectories( + root: ProjectPackage, provider: Provider, repo: Repository +): + dependency_one = Factory.create_dependency( + "one", + { + "git": "https://github.com/demo/subdirectories.git", + "subdirectory": "one", + "platform": "linux", + }, + ) + dependency_one_copy = Factory.create_dependency( + "one", + { + "git": "https://github.com/demo/subdirectories.git", + "subdirectory": "one-copy", + "platform": "win32", + }, + ) + + root.add_dependency(dependency_one) + root.add_dependency(dependency_one_copy) + + cache = DependencyCache(provider) + cache.search_for.cache_clear() + + # ensure cache was never hit for both calls + cache.search_for(dependency_one) + cache.search_for(dependency_one_copy) + assert not cache.search_for.cache_info().hits + + packages_one = cache.search_for(dependency_one) + packages_one_copy = cache.search_for(dependency_one_copy) + + if not is_poetry_core_1_1_0a7_compat: + assert cache.search_for.cache_info().hits == 2 + assert cache.search_for.cache_info().currsize == 2 + + assert len(packages_one) == len(packages_one_copy) == 1 + + package_one = packages_one[0] + package_one_copy = packages_one_copy[0] + + assert package_one.package.name == package_one_copy.name + assert package_one.package.version.text == package_one_copy.package.version.text + assert package_one.package.source_type == package_one_copy.source_type == "git" + assert ( + package_one.package.source_resolved_reference + == package_one_copy.source_resolved_reference + == "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24" + ) + assert ( + package_one.package.source_subdirectory != package_one_copy.source_subdirectory + ) + assert package_one.package.source_subdirectory == "one" + assert package_one_copy.package.source_subdirectory == "one-copy" + + assert package_one.dependency.marker.intersect( + package_one_copy.dependency.marker + ).is_empty() diff --git a/tests/puzzle/test_provider.py b/tests/puzzle/test_provider.py index ea1b552c907..a1f7076ae74 100644 --- a/tests/puzzle/test_provider.py +++ b/tests/puzzle/test_provider.py @@ -12,7 +12,9 @@ from poetry.core.packages.project_package import ProjectPackage from poetry.core.packages.vcs_dependency import VCSDependency +from poetry.factory import Factory from poetry.inspection.info import PackageInfo +from poetry.packages import DependencyPackage from poetry.puzzle.provider import Provider from poetry.repositories.pool import Pool from poetry.repositories.repository import Repository @@ -510,3 +512,76 @@ def test_search_for_file_wheel_with_extras(provider: Provider): "foo": [get_dependency("cleo")], "bar": [get_dependency("tomlkit")], } + + +def test_complete_package_preserves_source_type(provider: Provider) -> None: + fixtures = Path(__file__).parent.parent / "fixtures" + project_dir = fixtures.joinpath("with_conditional_path_deps") + poetry = Factory().create_poetry(cwd=project_dir) + + complete_package = provider.complete_package( + DependencyPackage(poetry.package.to_dependency(), poetry.package) + ) + + requires = complete_package.package.all_requires + + assert len(requires) == 2 + + assert {requires[0].source_url, requires[1].source_url} == { + project_dir.joinpath("demo_one").as_posix(), + project_dir.joinpath("demo_two").as_posix(), + } + assert {str(requires[0].marker), str(requires[1].marker)} == { + 'sys_platform == "linux"', + 'sys_platform == "win32"', + } + + +def test_complete_package_preserves_source_type_with_subdirectories( + provider: Provider, root: ProjectPackage +) -> None: + dependency_one = Factory.create_dependency( + "one", + { + "git": "https://github.com/demo/subdirectories.git", + "subdirectory": "one", + "platform": "linux", + }, + ) + dependency_one_copy = Factory.create_dependency( + "one", + { + "git": "https://github.com/demo/subdirectories.git", + "subdirectory": "one-copy", + "platform": "win32", + }, + ) + dependency_two = Factory.create_dependency( + "two", + {"git": "https://github.com/demo/subdirectories.git", "subdirectory": "two"}, + ) + + root.add_dependency( + Factory.create_dependency( + "one", + { + "git": "https://github.com/demo/subdirectories.git", + "subdirectory": "one", + "platform": "linux", + }, + ) + ) + root.add_dependency(dependency_one_copy) + root.add_dependency(dependency_two) + + complete_package = provider.complete_package( + DependencyPackage(root.to_dependency(), root) + ) + + requires = complete_package.package.all_requires + assert len(requires) == 3 + assert {r.to_pep_508() for r in requires} == { + dependency_one.to_pep_508(), + dependency_one_copy.to_pep_508(), + dependency_two.to_pep_508(), + }