Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/poetry/puzzle/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
from poetry.core.packages.directory_dependency import DirectoryDependency
from poetry.core.packages.file_dependency import FileDependency
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.vcs_dependency import VCSDependency
from poetry.core.version.markers import BaseMarker
Expand Down Expand Up @@ -390,6 +391,7 @@ def get_package_from_vcs(
)

def _search_for_file(self, dependency: FileDependency) -> Package:
dependency.validate(raise_error=True)
package = self.get_package_from_file(dependency.full_path)

self.validate_package_for_dependency(dependency=dependency, package=package)
Expand Down Expand Up @@ -420,6 +422,7 @@ def get_package_from_file(cls, file_path: Path) -> Package:
return package

def _search_for_directory(self, dependency: DirectoryDependency) -> Package:
dependency.validate(raise_error=True)
package = self.get_package_from_directory(dependency.full_path)

self.validate_package_for_dependency(dependency=dependency, package=package)
Expand Down Expand Up @@ -652,6 +655,11 @@ def complete_package(
if locked is not None and locked.package.is_same_package_as(dep):
continue
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, dependency_package
Expand Down
38 changes: 38 additions & 0 deletions tests/console/commands/test_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from poetry.poetry import Poetry
from tests.types import CommandTesterFactory
from tests.types import FixtureDirGetter
from tests.types import ProjectFactory


Expand Down Expand Up @@ -69,6 +70,22 @@ def tester(
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(
("options", "groups"),
[
Expand Down Expand Up @@ -291,3 +308,24 @@ def test_install_logs_output_decorated(tester: CommandTester, mocker: MockerFixt
)
assert tester.status_code == 0
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)
57 changes: 57 additions & 0 deletions tests/console/commands/test_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,63 @@ def test_lock_no_update_path_dependencies(
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])
def test_lock_with_incompatible_lockfile(
command_tester_factory: CommandTesterFactory,
Expand Down
31 changes: 31 additions & 0 deletions tests/console/commands/test_show.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from poetry.core.packages.dependency_group import DependencyGroup

from poetry.factory import Factory
from poetry.utils._compat import tomllib
from tests.helpers import MOCK_DEFAULT_GIT_REVISION
from tests.helpers import get_package

Expand Down Expand Up @@ -2140,3 +2141,33 @@ def test_url_dependency_is_not_outdated_by_repository_package(
# version in the repository.
tester.execute("--outdated")
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("")
20 changes: 20 additions & 0 deletions tests/fixtures/deleted_directory_dependency/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions tests/fixtures/deleted_directory_dependency/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[tool.poetry]
name = "project-with-missing-directory-dependency"
version = "1.2.3"
description = "This is a description"
authors = ["Your Name <[email protected]>"]
license = "MIT"
packages = []

[tool.poetry.dependencies]
python = "*"
20 changes: 20 additions & 0 deletions tests/fixtures/deleted_file_dependency/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions tests/fixtures/deleted_file_dependency/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[tool.poetry]
name = "project-with-missing-directory-dependency"
version = "1.2.3"
description = "This is a description"
authors = ["Your Name <[email protected]>"]
license = "MIT"
packages = []

[tool.poetry.dependencies]
python = "*"
20 changes: 20 additions & 0 deletions tests/fixtures/missing_directory_dependency/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions tests/fixtures/missing_directory_dependency/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[tool.poetry]
name = "project-with-missing-directory-dependency"
version = "1.2.3"
description = "This is a description"
authors = ["Your Name <[email protected]>"]
license = "MIT"
packages = []

[tool.poetry.dependencies]
python = "*"

[tool.poetry.dev-dependencies]
missing = { path = "./missing" }
20 changes: 20 additions & 0 deletions tests/fixtures/missing_file_dependency/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions tests/fixtures/missing_file_dependency/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[tool.poetry]
name = "project-with-missing-directory-dependency"
version = "1.2.3"
description = "This is a description"
authors = ["Your Name <[email protected]>"]
license = "MIT"
packages = []

[tool.poetry.dependencies]
python = "*"

[tool.poetry.dev-dependencies]
missing = { file = "missing-0.1.0-py2.py3-none-any.whl" }