Commit 651d82db by Adrian Garcia Badaracco Committed by GitHub

Fail with comprehensible error message if path dependencies do not exist (#6844)

Co-authored-by: Randy Döring <30527984+radoering@users.noreply.github.com>
parent 0b1d869a
...@@ -50,6 +50,7 @@ if TYPE_CHECKING: ...@@ -50,6 +50,7 @@ if TYPE_CHECKING:
from poetry.core.packages.directory_dependency import DirectoryDependency from poetry.core.packages.directory_dependency import DirectoryDependency
from poetry.core.packages.file_dependency import FileDependency from poetry.core.packages.file_dependency import FileDependency
from poetry.core.packages.package import Package from poetry.core.packages.package import Package
from poetry.core.packages.path_dependency import PathDependency
from poetry.core.packages.url_dependency import URLDependency from poetry.core.packages.url_dependency import URLDependency
from poetry.core.packages.vcs_dependency import VCSDependency from poetry.core.packages.vcs_dependency import VCSDependency
from poetry.core.version.markers import BaseMarker from poetry.core.version.markers import BaseMarker
...@@ -390,6 +391,7 @@ class Provider: ...@@ -390,6 +391,7 @@ class Provider:
) )
def _search_for_file(self, dependency: FileDependency) -> Package: def _search_for_file(self, dependency: FileDependency) -> Package:
dependency.validate(raise_error=True)
package = self.get_package_from_file(dependency.full_path) package = self.get_package_from_file(dependency.full_path)
self.validate_package_for_dependency(dependency=dependency, package=package) self.validate_package_for_dependency(dependency=dependency, package=package)
...@@ -420,6 +422,7 @@ class Provider: ...@@ -420,6 +422,7 @@ class Provider:
return package return package
def _search_for_directory(self, dependency: DirectoryDependency) -> Package: def _search_for_directory(self, dependency: DirectoryDependency) -> Package:
dependency.validate(raise_error=True)
package = self.get_package_from_directory(dependency.full_path) package = self.get_package_from_directory(dependency.full_path)
self.validate_package_for_dependency(dependency=dependency, package=package) self.validate_package_for_dependency(dependency=dependency, package=package)
...@@ -652,6 +655,11 @@ class Provider: ...@@ -652,6 +655,11 @@ class Provider:
if locked is not None and locked.package.is_same_package_as(dep): if locked is not None and locked.package.is_same_package_as(dep):
continue continue
self.search_for_direct_origin_dependency(dep) self.search_for_direct_origin_dependency(dep)
else:
for dep in _dependencies:
if dep.is_file() or dep.is_directory():
dep = cast("PathDependency", dep)
dep.validate(raise_error=True)
dependencies = self._get_dependencies_with_overrides( dependencies = self._get_dependencies_with_overrides(
_dependencies, dependency_package _dependencies, dependency_package
......
...@@ -14,6 +14,7 @@ if TYPE_CHECKING: ...@@ -14,6 +14,7 @@ if TYPE_CHECKING:
from poetry.poetry import Poetry from poetry.poetry import Poetry
from tests.types import CommandTesterFactory from tests.types import CommandTesterFactory
from tests.types import FixtureDirGetter
from tests.types import ProjectFactory from tests.types import ProjectFactory
...@@ -69,6 +70,22 @@ def tester( ...@@ -69,6 +70,22 @@ def tester(
return command_tester_factory("install") return command_tester_factory("install")
def _project_factory(
fixture_name: str,
project_factory: ProjectFactory,
fixture_dir: FixtureDirGetter,
) -> Poetry:
source = fixture_dir(fixture_name)
pyproject_content = (source / "pyproject.toml").read_text(encoding="utf-8")
poetry_lock_content = (source / "poetry.lock").read_text(encoding="utf-8")
return project_factory(
name="foobar",
pyproject_content=pyproject_content,
poetry_lock_content=poetry_lock_content,
source=source,
)
@pytest.mark.parametrize( @pytest.mark.parametrize(
("options", "groups"), ("options", "groups"),
[ [
...@@ -291,3 +308,24 @@ def test_install_logs_output_decorated(tester: CommandTester, mocker: MockerFixt ...@@ -291,3 +308,24 @@ def test_install_logs_output_decorated(tester: CommandTester, mocker: MockerFixt
) )
assert tester.status_code == 0 assert tester.status_code == 0
assert tester.io.fetch_output() == expected assert tester.io.fetch_output() == expected
@pytest.mark.parametrize("options", ["", "--without dev"])
@pytest.mark.parametrize(
"project", ["missing_directory_dependency", "missing_file_dependency"]
)
def test_install_path_dependency_does_not_exist(
command_tester_factory: CommandTesterFactory,
project_factory: ProjectFactory,
fixture_dir: FixtureDirGetter,
project: str,
options: str,
):
poetry = _project_factory(project, project_factory, fixture_dir)
poetry.locker.locked(True)
tester = command_tester_factory("install", poetry=poetry)
if options:
tester.execute(options)
else:
with pytest.raises(ValueError, match="does not exist"):
tester.execute(options)
...@@ -204,6 +204,63 @@ def test_lock_no_update_path_dependencies( ...@@ -204,6 +204,63 @@ def test_lock_no_update_path_dependencies(
assert {p.name for p in packages} == {"quix", "sampleproject"} assert {p.name for p in packages} == {"quix", "sampleproject"}
@pytest.mark.parametrize("update", [True, False])
@pytest.mark.parametrize(
"project", ["missing_directory_dependency", "missing_file_dependency"]
)
def test_lock_path_dependency_does_not_exist(
command_tester_factory: CommandTesterFactory,
project_factory: ProjectFactory,
fixture_dir: FixtureDirGetter,
project: str,
update: bool,
):
poetry = _project_factory(project, project_factory, fixture_dir)
locker = Locker(
lock=poetry.pyproject.file.path.parent / "poetry.lock",
local_config=poetry.locker._local_config,
)
poetry.set_locker(locker)
options = "" if update else "--no-update"
tester = command_tester_factory("lock", poetry=poetry)
if update or "directory" in project:
# directory dependencies are always updated
with pytest.raises(ValueError, match="does not exist"):
tester.execute(options)
else:
tester.execute(options)
@pytest.mark.parametrize("update", [True, False])
@pytest.mark.parametrize(
"project", ["deleted_directory_dependency", "deleted_file_dependency"]
)
def test_lock_path_dependency_deleted_from_pyproject(
command_tester_factory: CommandTesterFactory,
project_factory: ProjectFactory,
fixture_dir: FixtureDirGetter,
project: str,
update: bool,
):
poetry = _project_factory(project, project_factory, fixture_dir)
locker = Locker(
lock=poetry.pyproject.file.path.parent / "poetry.lock",
local_config=poetry.locker._local_config,
)
poetry.set_locker(locker)
tester = command_tester_factory("lock", poetry=poetry)
if update:
tester.execute("")
else:
tester.execute("--no-update")
packages = locker.locked_repository().packages
assert {p.name for p in packages} == set()
@pytest.mark.parametrize("is_no_update", [False, True]) @pytest.mark.parametrize("is_no_update", [False, True])
def test_lock_with_incompatible_lockfile( def test_lock_with_incompatible_lockfile(
command_tester_factory: CommandTesterFactory, command_tester_factory: CommandTesterFactory,
......
...@@ -8,6 +8,7 @@ from poetry.core.packages.dependency_group import MAIN_GROUP ...@@ -8,6 +8,7 @@ from poetry.core.packages.dependency_group import MAIN_GROUP
from poetry.core.packages.dependency_group import DependencyGroup from poetry.core.packages.dependency_group import DependencyGroup
from poetry.factory import Factory from poetry.factory import Factory
from poetry.utils._compat import tomllib
from tests.helpers import MOCK_DEFAULT_GIT_REVISION from tests.helpers import MOCK_DEFAULT_GIT_REVISION
from tests.helpers import get_package from tests.helpers import get_package
...@@ -2140,3 +2141,33 @@ def test_url_dependency_is_not_outdated_by_repository_package( ...@@ -2140,3 +2141,33 @@ def test_url_dependency_is_not_outdated_by_repository_package(
# version in the repository. # version in the repository.
tester.execute("--outdated") tester.execute("--outdated")
assert tester.io.fetch_output() == "" assert tester.io.fetch_output() == ""
@pytest.mark.parametrize(
("project_directory", "required_fixtures"),
[
(
"deleted_directory_dependency",
[],
),
],
)
def test_show_outdated_missing_directory_dependency(
tester: CommandTester,
poetry: Poetry,
installed: Repository,
repo: TestRepository,
):
with (poetry.pyproject.file.path.parent / "poetry.lock").open(mode="rb") as f:
data = tomllib.load(f)
poetry.locker.mock_lock_data(data)
poetry.package.add_dependency(
Factory.create_dependency(
"missing",
{"path": data["package"][0]["source"]["url"]},
)
)
with pytest.raises(ValueError, match="does not exist"):
tester.execute("")
# This file is automatically @generated by Poetry and should not be changed by hand.
[[package]]
name = "missing"
version = "1.2.3"
description = ""
category = "main"
optional = false
python-versions = "*"
files = []
develop = false
[package.source]
type = "directory"
url = "missing"
[metadata]
lock-version = "2.0"
python-versions = "*"
content-hash = "bec78476925e4cda6b22e91551ce4337264bdc3394c4f8297ad238f67a436d0e"
[tool.poetry]
name = "project-with-missing-directory-dependency"
version = "1.2.3"
description = "This is a description"
authors = ["Your Name <you@example.com>"]
license = "MIT"
packages = []
[tool.poetry.dependencies]
python = "*"
# This file is automatically @generated by Poetry and should not be changed by hand.
[[package]]
name = "missing"
version = "1.2.3"
description = ""
category = "main"
optional = false
python-versions = "*"
files = []
develop = false
[package.source]
type = "file"
url = "missing-0.1.0-py2.py3-none-any.whl"
[metadata]
lock-version = "2.0"
python-versions = "*"
content-hash = "bec78476925e4cda6b22e91551ce4337264bdc3394c4f8297ad238f67a436d0e"
[tool.poetry]
name = "project-with-missing-directory-dependency"
version = "1.2.3"
description = "This is a description"
authors = ["Your Name <you@example.com>"]
license = "MIT"
packages = []
[tool.poetry.dependencies]
python = "*"
# This file is automatically @generated by Poetry and should not be changed by hand.
[[package]]
name = "missing"
version = "1.2.3"
description = ""
category = "dev"
optional = false
python-versions = "*"
files = []
develop = false
[package.source]
type = "directory"
url = "missing"
[metadata]
lock-version = "2.0"
python-versions = "*"
content-hash = "bec78476925e4cda6b22e91551ce4337264bdc3394c4f8297ad238f67a436d0e"
[tool.poetry]
name = "project-with-missing-directory-dependency"
version = "1.2.3"
description = "This is a description"
authors = ["Your Name <you@example.com>"]
license = "MIT"
packages = []
[tool.poetry.dependencies]
python = "*"
[tool.poetry.dev-dependencies]
missing = { path = "./missing" }
# This file is automatically @generated by Poetry and should not be changed by hand.
[[package]]
name = "missing"
version = "1.2.3"
description = ""
category = "dev"
optional = false
python-versions = "*"
files = []
develop = false
[package.source]
type = "file"
url = "missing-0.1.0-py2.py3-none-any.whl"
[metadata]
lock-version = "2.0"
python-versions = "*"
content-hash = "bec78476925e4cda6b22e91551ce4337264bdc3394c4f8297ad238f67a436d0e"
[tool.poetry]
name = "project-with-missing-directory-dependency"
version = "1.2.3"
description = "This is a description"
authors = ["Your Name <you@example.com>"]
license = "MIT"
packages = []
[tool.poetry.dependencies]
python = "*"
[tool.poetry.dev-dependencies]
missing = { file = "missing-0.1.0-py2.py3-none-any.whl" }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment