Skip to content

Commit 9f5e0a8

Browse files
adriangbradoering
andcommitted
provider: validate path dependencies explicitly
Co-authored-by: Randy Döring <[email protected]>
1 parent 0b1d869 commit 9f5e0a8

File tree

12 files changed

+260
-0
lines changed

12 files changed

+260
-0
lines changed

src/poetry/puzzle/provider.py

+8
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
from poetry.core.packages.directory_dependency import DirectoryDependency
5151
from poetry.core.packages.file_dependency import FileDependency
5252
from poetry.core.packages.package import Package
53+
from poetry.core.packages.path_dependency import PathDependency
5354
from poetry.core.packages.url_dependency import URLDependency
5455
from poetry.core.packages.vcs_dependency import VCSDependency
5556
from poetry.core.version.markers import BaseMarker
@@ -390,6 +391,7 @@ def get_package_from_vcs(
390391
)
391392

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

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

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

425428
self.validate_package_for_dependency(dependency=dependency, package=package)
@@ -652,6 +655,11 @@ def complete_package(
652655
if locked is not None and locked.package.is_same_package_as(dep):
653656
continue
654657
self.search_for_direct_origin_dependency(dep)
658+
else:
659+
for dep in _dependencies:
660+
if dep.is_file() or dep.is_directory():
661+
dep = cast("PathDependency", dep)
662+
dep.validate(raise_error=True)
655663

656664
dependencies = self._get_dependencies_with_overrides(
657665
_dependencies, dependency_package

tests/console/commands/test_install.py

+38
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
from poetry.poetry import Poetry
1616
from tests.types import CommandTesterFactory
17+
from tests.types import FixtureDirGetter
1718
from tests.types import ProjectFactory
1819

1920

@@ -69,6 +70,22 @@ def tester(
6970
return command_tester_factory("install")
7071

7172

73+
def _project_factory(
74+
fixture_name: str,
75+
project_factory: ProjectFactory,
76+
fixture_dir: FixtureDirGetter,
77+
) -> Poetry:
78+
source = fixture_dir(fixture_name)
79+
pyproject_content = (source / "pyproject.toml").read_text(encoding="utf-8")
80+
poetry_lock_content = (source / "poetry.lock").read_text(encoding="utf-8")
81+
return project_factory(
82+
name="foobar",
83+
pyproject_content=pyproject_content,
84+
poetry_lock_content=poetry_lock_content,
85+
source=source,
86+
)
87+
88+
7289
@pytest.mark.parametrize(
7390
("options", "groups"),
7491
[
@@ -291,3 +308,24 @@ def test_install_logs_output_decorated(tester: CommandTester, mocker: MockerFixt
291308
)
292309
assert tester.status_code == 0
293310
assert tester.io.fetch_output() == expected
311+
312+
313+
@pytest.mark.parametrize("options", ["", "--without dev"])
314+
@pytest.mark.parametrize(
315+
"project", ["missing_directory_dependency", "missing_file_dependency"]
316+
)
317+
def test_install_path_dependency_does_not_exist(
318+
command_tester_factory: CommandTesterFactory,
319+
project_factory: ProjectFactory,
320+
fixture_dir: FixtureDirGetter,
321+
project: str,
322+
options: str,
323+
):
324+
poetry = _project_factory(project, project_factory, fixture_dir)
325+
poetry.locker.locked(True)
326+
tester = command_tester_factory("install", poetry=poetry)
327+
if options:
328+
tester.execute(options)
329+
else:
330+
with pytest.raises(ValueError, match="does not exist"):
331+
tester.execute(options)

tests/console/commands/test_lock.py

+57
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,63 @@ def test_lock_no_update_path_dependencies(
204204
assert {p.name for p in packages} == {"quix", "sampleproject"}
205205

206206

207+
@pytest.mark.parametrize("update", [True, False])
208+
@pytest.mark.parametrize(
209+
"project", ["missing_directory_dependency", "missing_file_dependency"]
210+
)
211+
def test_lock_path_dependency_does_not_exist(
212+
command_tester_factory: CommandTesterFactory,
213+
project_factory: ProjectFactory,
214+
fixture_dir: FixtureDirGetter,
215+
project: str,
216+
update: bool,
217+
):
218+
poetry = _project_factory(project, project_factory, fixture_dir)
219+
locker = Locker(
220+
lock=poetry.pyproject.file.path.parent / "poetry.lock",
221+
local_config=poetry.locker._local_config,
222+
)
223+
poetry.set_locker(locker)
224+
options = "" if update else "--no-update"
225+
226+
tester = command_tester_factory("lock", poetry=poetry)
227+
if update or "directory" in project:
228+
# directory dependencies are always updated
229+
with pytest.raises(ValueError, match="does not exist"):
230+
tester.execute(options)
231+
else:
232+
tester.execute(options)
233+
234+
235+
@pytest.mark.parametrize("update", [True, False])
236+
@pytest.mark.parametrize(
237+
"project", ["deleted_directory_dependency", "deleted_file_dependency"]
238+
)
239+
def test_lock_path_dependency_deleted_from_pyproject(
240+
command_tester_factory: CommandTesterFactory,
241+
project_factory: ProjectFactory,
242+
fixture_dir: FixtureDirGetter,
243+
project: str,
244+
update: bool,
245+
):
246+
poetry = _project_factory(project, project_factory, fixture_dir)
247+
locker = Locker(
248+
lock=poetry.pyproject.file.path.parent / "poetry.lock",
249+
local_config=poetry.locker._local_config,
250+
)
251+
poetry.set_locker(locker)
252+
253+
tester = command_tester_factory("lock", poetry=poetry)
254+
if update:
255+
tester.execute("")
256+
else:
257+
tester.execute("--no-update")
258+
259+
packages = locker.locked_repository().packages
260+
261+
assert {p.name for p in packages} == set()
262+
263+
207264
@pytest.mark.parametrize("is_no_update", [False, True])
208265
def test_lock_with_incompatible_lockfile(
209266
command_tester_factory: CommandTesterFactory,

tests/console/commands/test_show.py

+31
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from poetry.core.packages.dependency_group import DependencyGroup
99

1010
from poetry.factory import Factory
11+
from poetry.utils._compat import tomllib
1112
from tests.helpers import MOCK_DEFAULT_GIT_REVISION
1213
from tests.helpers import get_package
1314

@@ -2140,3 +2141,33 @@ def test_url_dependency_is_not_outdated_by_repository_package(
21402141
# version in the repository.
21412142
tester.execute("--outdated")
21422143
assert tester.io.fetch_output() == ""
2144+
2145+
2146+
@pytest.mark.parametrize(
2147+
("project_directory", "required_fixtures"),
2148+
[
2149+
(
2150+
"deleted_directory_dependency",
2151+
[],
2152+
),
2153+
],
2154+
)
2155+
def test_show_outdated_missing_directory_dependency(
2156+
tester: CommandTester,
2157+
poetry: Poetry,
2158+
installed: Repository,
2159+
repo: TestRepository,
2160+
):
2161+
with (poetry.pyproject.file.path.parent / "poetry.lock").open(mode="rb") as f:
2162+
data = tomllib.load(f)
2163+
poetry.locker.mock_lock_data(data)
2164+
2165+
poetry.package.add_dependency(
2166+
Factory.create_dependency(
2167+
"missing",
2168+
{"path": data["package"][0]["source"]["url"]},
2169+
)
2170+
)
2171+
2172+
with pytest.raises(ValueError, match="does not exist"):
2173+
tester.execute("")

tests/fixtures/deleted_directory_dependency/poetry.lock

+20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[tool.poetry]
2+
name = "project-with-missing-directory-dependency"
3+
version = "1.2.3"
4+
description = "This is a description"
5+
authors = ["Your Name <[email protected]>"]
6+
license = "MIT"
7+
packages = []
8+
9+
[tool.poetry.dependencies]
10+
python = "*"

tests/fixtures/deleted_file_dependency/poetry.lock

+20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[tool.poetry]
2+
name = "project-with-missing-directory-dependency"
3+
version = "1.2.3"
4+
description = "This is a description"
5+
authors = ["Your Name <[email protected]>"]
6+
license = "MIT"
7+
packages = []
8+
9+
[tool.poetry.dependencies]
10+
python = "*"

tests/fixtures/missing_directory_dependency/poetry.lock

+20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[tool.poetry]
2+
name = "project-with-missing-directory-dependency"
3+
version = "1.2.3"
4+
description = "This is a description"
5+
authors = ["Your Name <[email protected]>"]
6+
license = "MIT"
7+
packages = []
8+
9+
[tool.poetry.dependencies]
10+
python = "*"
11+
12+
[tool.poetry.dev-dependencies]
13+
missing = { path = "./missing" }

tests/fixtures/missing_file_dependency/poetry.lock

+20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[tool.poetry]
2+
name = "project-with-missing-directory-dependency"
3+
version = "1.2.3"
4+
description = "This is a description"
5+
authors = ["Your Name <[email protected]>"]
6+
license = "MIT"
7+
packages = []
8+
9+
[tool.poetry.dependencies]
10+
python = "*"
11+
12+
[tool.poetry.dev-dependencies]
13+
missing = { file = "missing-0.1.0-py2.py3-none-any.whl" }

0 commit comments

Comments
 (0)