Skip to content

Commit 5cb3fef

Browse files
authored
Merge branch 'main' into poetry-documentation-proofread
2 parents cecf58a + 297ae7d commit 5cb3fef

File tree

7 files changed

+167
-16
lines changed

7 files changed

+167
-16
lines changed

.pre-commit-config.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ repos:
2828
- id: validate_manifest
2929

3030
- repo: https://github.com/astral-sh/ruff-pre-commit
31-
rev: v0.11.5
31+
rev: v0.11.6
3232
hooks:
3333
- id: ruff
3434
- id: ruff-format
3535

3636
- repo: https://github.com/woodruffw/zizmor-pre-commit
37-
rev: v1.5.2
37+
rev: v1.6.0
3838
hooks:
3939
- id: zizmor

src/poetry/console/application.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -251,11 +251,11 @@ def _run(self, io: IO) -> int:
251251
# display the error cleanly unless the user uses verbose or debug
252252
self._configure_global_options(io)
253253

254-
self._load_plugins(io)
254+
with directory(self._working_directory):
255+
self._load_plugins(io)
255256

256-
exit_code: int = 1
257+
exit_code: int = 1
257258

258-
with directory(self._working_directory):
259259
try:
260260
exit_code = super()._run(io)
261261
except PoetryRuntimeError as e:

src/poetry/puzzle/provider.py

+9
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from poetry.packages.direct_origin import DirectOrigin
3131
from poetry.packages.package_collection import PackageCollection
3232
from poetry.puzzle.exceptions import OverrideNeededError
33+
from poetry.repositories.repository_pool import Priority
3334
from poetry.utils.helpers import get_file_hash
3435

3536

@@ -753,6 +754,14 @@ def get_locked(self, dependency: Dependency) -> DependencyPackage | None:
753754
if package.satisfies(dependency):
754755
if explicit_source := self._explicit_sources.get(dependency.name):
755756
dependency.source_name = explicit_source
757+
elif (
758+
not dependency.source_name
759+
and package.source_type == "legacy"
760+
and package.source_reference
761+
and self._pool.get_priority(package.source_reference)
762+
== Priority.EXPLICIT
763+
):
764+
continue
756765
return DependencyPackage(dependency, package)
757766
return None
758767

tests/console/test_application_global_options.py

+68-10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from pathlib import Path
77
from typing import TYPE_CHECKING
8+
from typing import ClassVar
89

910
import pytest
1011

@@ -13,7 +14,10 @@
1314
from cleo.testers.application_tester import ApplicationTester
1415

1516
from poetry.console.application import Application
17+
from poetry.console.commands.command import Command
1618
from poetry.console.commands.version import VersionCommand
19+
from poetry.plugins import ApplicationPlugin
20+
from tests.helpers import mock_metadata_entry_points
1721
from tests.helpers import switch_working_directory
1822

1923

@@ -27,11 +31,58 @@
2731
NO_PYPROJECT_TOML_ERROR = "Poetry could not find a pyproject.toml file in"
2832

2933

34+
class CheckProjectPathCommand(Command):
35+
name = "check-project-path"
36+
37+
description = "Check Project Path Command"
38+
39+
def handle(self) -> int:
40+
if not self.poetry.pyproject_path.exists():
41+
raise RuntimeError(
42+
f"Wrong project path in handle: {self.poetry.pyproject_path}\nWorking directory: {Path.cwd()}"
43+
)
44+
45+
return 0
46+
47+
48+
class EarlyPoetryAccessPlugin(ApplicationPlugin):
49+
commands: ClassVar[list[type[Command]]] = [CheckProjectPathCommand]
50+
51+
def activate(self, application: Application) -> None:
52+
super().activate(application)
53+
54+
# access application.poetry
55+
# see https://github.com/nat-n/poethepoet/issues/288
56+
if not application.poetry.pyproject_path.exists():
57+
raise RuntimeError(
58+
f"Wrong project path in activate: {application.poetry.pyproject_path}\nWorking directory: {Path.cwd()}"
59+
)
60+
61+
62+
@pytest.fixture
63+
def with_early_poetry_access_plugin(mocker: MockerFixture) -> None:
64+
mock_metadata_entry_points(mocker, EarlyPoetryAccessPlugin)
65+
66+
3067
@pytest.fixture
3168
def project_source_directory(fixture_copier: FixtureCopier) -> Path:
3269
return fixture_copier("up_to_date_lock")
3370

3471

72+
@pytest.fixture
73+
def relative_project_source_directory(project_source_directory: Path) -> Path:
74+
# ensure pre-conditions are met
75+
cwd = Path.cwd()
76+
assert project_source_directory.is_relative_to(cwd)
77+
78+
# construct relative path
79+
relative_source_directory = project_source_directory.relative_to(cwd)
80+
assert relative_source_directory.as_posix() != project_source_directory.as_posix()
81+
assert not relative_source_directory.is_absolute()
82+
83+
return relative_source_directory
84+
85+
3586
@pytest.fixture
3687
def tester() -> ApplicationTester:
3788
return ApplicationTester(Application())
@@ -149,20 +200,14 @@ def test_application_with_context_parameters(
149200
def test_application_with_relative_project_parameter(
150201
tester: ApplicationTester,
151202
project_source_directory: Path,
203+
relative_project_source_directory: Path,
152204
with_mocked_version_command: None,
153205
tmp_path_factory: TempPathFactory,
154206
) -> None:
155-
# ensure pre-conditions are met
156207
cwd = Path.cwd()
157-
assert project_source_directory.is_relative_to(cwd)
158-
159-
# construct relative path
160-
relative_source_directory = project_source_directory.relative_to(cwd)
161-
assert relative_source_directory.as_posix() != project_source_directory.as_posix()
162-
assert not relative_source_directory.is_absolute()
163-
164-
# we expect application run to be executed within current cwd but project to be a subdirectory
165-
args = f"--directory '{cwd}' --project {relative_source_directory} version"
208+
# we expect application run to be executed within current cwd
209+
# but project to be a subdirectory
210+
args = f"--directory '{cwd}' --project {relative_project_source_directory} version"
166211

167212
# we switch cwd to a new temporary directory unrelated to the project directory
168213
new_working_dir = tmp_path_factory.mktemp("unrelated-working-directory")
@@ -181,6 +226,19 @@ def test_application_with_relative_project_parameter(
181226
""")
182227

183228

229+
def test_application_with_relative_directory_parameter_and_early_poetry_access_plugin(
230+
tester: ApplicationTester,
231+
with_early_poetry_access_plugin: None,
232+
relative_project_source_directory: Path,
233+
) -> None:
234+
"""see https://github.com/nat-n/poethepoet/issues/288"""
235+
tester.execute(
236+
f"--directory {relative_project_source_directory} check-project-path"
237+
)
238+
239+
assert tester.status_code == 0, tester.io.fetch_error()
240+
241+
184242
@pytest.mark.parametrize(
185243
("parameter", "check", "result"),
186244
[

tests/helpers.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -277,10 +277,15 @@ def mock_metadata_entry_points(
277277
name: str = "my-plugin",
278278
dist: metadata.Distribution | None = None,
279279
) -> None:
280+
def patched_entry_points(*args: Any, **kwargs: Any) -> list[metadata.EntryPoint]:
281+
if "group" in kwargs and kwargs["group"] != getattr(cls, "group", None):
282+
return []
283+
return [make_entry_point_from_plugin(name, cls, dist)]
284+
280285
mocker.patch.object(
281286
metadata,
282287
"entry_points",
283-
return_value=[make_entry_point_from_plugin(name, cls, dist)],
288+
side_effect=patched_entry_points,
284289
)
285290

286291

tests/plugins/test_plugin_manager.py

+2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ class MyCommandPlugin(ApplicationPlugin):
5757

5858

5959
class InvalidPlugin:
60+
group = "poetry.plugin"
61+
6062
def activate(self, poetry: Poetry, io: IO) -> None:
6163
io.write_line("Updating version")
6264
poetry.package.version = Version.parse("9.9.9")

tests/puzzle/test_solver.py

+77
Original file line numberDiff line numberDiff line change
@@ -3776,6 +3776,83 @@ def test_multiple_constraints_explicit_source_transitive_locked_use_latest(
37763776
assert ops[2].package.source_reference == "explicit2"
37773777

37783778

3779+
@pytest.mark.parametrize("locked", [False, True])
3780+
def test_multiple_constraints_incomplete_explicit_source_transitive_locked(
3781+
package: ProjectPackage,
3782+
repo: Repository,
3783+
pool: RepositoryPool,
3784+
io: NullIO,
3785+
locked: bool,
3786+
) -> None:
3787+
"""
3788+
The root package depends on
3789+
* lib == 1.0+cu ; sys_platform == "linux" with source=explicit
3790+
* lib == 1.0 ; sys_platform == "darwin" with no explicit source
3791+
* other >= 1.0
3792+
"other" depends on "lib"
3793+
3794+
Since the source for lib 1.0+cu has the priority "explicit",
3795+
the default source must be chosen for lib 1.0.
3796+
Since the multiple constraints are incomplete - they are only defined for linux
3797+
and darwin, there is another hidden override that also requires lib via other.
3798+
In this hidden override lib 1.0 from the default source must be chosen
3799+
(because the other source has the priority "explicit").
3800+
"""
3801+
package.add_dependency(
3802+
Factory.create_dependency(
3803+
"lib",
3804+
{
3805+
"version": "1.0+cu",
3806+
"source": "explicit",
3807+
"markers": "sys_platform == 'linux'",
3808+
},
3809+
)
3810+
)
3811+
package.add_dependency(
3812+
Factory.create_dependency(
3813+
"lib",
3814+
{
3815+
"version": "1.0",
3816+
"markers": "sys_platform == 'darwin'",
3817+
},
3818+
)
3819+
)
3820+
package.add_dependency(Factory.create_dependency("other", {"version": ">=1.0"}))
3821+
3822+
explicit_repo = Repository("explicit")
3823+
pool.add_repository(explicit_repo, priority=Priority.EXPLICIT)
3824+
3825+
package_lib_explicit = Package(
3826+
"lib", "1.0+cu", source_type="legacy", source_reference="explicit"
3827+
)
3828+
explicit_repo.add_package(package_lib_explicit)
3829+
package_lib_default = Package("lib", "1.0")
3830+
repo.add_package(package_lib_default)
3831+
3832+
package_other = Package("other", "1.5")
3833+
package_other.add_dependency(Factory.create_dependency("lib", ">=1.0"))
3834+
repo.add_package(package_other)
3835+
3836+
if locked:
3837+
# order does not matter because packages are sorted in the provicer
3838+
# (latest first) so that the package from the explicit source is preferred
3839+
locked_packages = [package_lib_default, package_lib_explicit, package_other]
3840+
else:
3841+
locked_packages = []
3842+
solver = Solver(package, pool, [], locked_packages, io)
3843+
3844+
transaction = solver.solve()
3845+
3846+
check_solver_result(
3847+
transaction,
3848+
[
3849+
{"job": "install", "package": package_lib_default},
3850+
{"job": "install", "package": package_lib_explicit},
3851+
{"job": "install", "package": package_other},
3852+
],
3853+
)
3854+
3855+
37793856
def test_solver_discards_packages_with_empty_markers(
37803857
package: ProjectPackage,
37813858
repo: Repository,

0 commit comments

Comments
 (0)