From 3a31f2de4d3379cdddb84cd6601d215dd292ebe1 Mon Sep 17 00:00:00 2001 From: martin-kokos <4807476+martin-kokos@users.noreply.github.com> Date: Sat, 15 Apr 2023 18:22:53 +0200 Subject: [PATCH 01/10] tests: stop using tmp_dir fixture in favor of pytest.tmp_path fixture (#7412) --- .github/workflows/main.yml | 6 + src/poetry/utils/pip.py | 2 +- tests/conftest.py | 40 +-- tests/console/commands/env/conftest.py | 8 +- tests/console/commands/env/test_list.py | 6 +- .../commands/self/test_show_plugins.py | 6 +- tests/console/commands/test_new.py | 18 +- tests/console/conftest.py | 4 +- tests/inspection/test_info.py | 4 +- tests/installation/test_executor.py | 50 ++- tests/integration/test_utils_vcs_git.py | 6 +- .../masonry/builders/test_editable_builder.py | 12 +- tests/plugins/test_plugin_manager.py | 2 +- .../repositories/test_installed_repository.py | 4 +- tests/utils/test_env.py | 284 +++++++++--------- tests/utils/test_env_site.py | 13 +- tests/utils/test_pip.py | 6 +- 17 files changed, 232 insertions(+), 239 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a7d2ee447ba..df018e67b32 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -64,6 +64,12 @@ jobs: if: ${{ matrix.os == 'Windows' }} run: echo "$APPDATA\Python\Scripts" >> $GITHUB_PATH + - name: Enable long paths for git on Windows + if: ${{ matrix.os == 'Windows' }} + # Enable handling long path names (+260 char) on the Windows platform + # https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation + run: git config --system core.longpaths true + - name: Configure poetry run: poetry config virtualenvs.in-project true diff --git a/src/poetry/utils/pip.py b/src/poetry/utils/pip.py index 58b3504fba7..eb38bbf0b3b 100644 --- a/src/poetry/utils/pip.py +++ b/src/poetry/utils/pip.py @@ -55,4 +55,4 @@ def pip_install( try: return environment.run_pip(*args) except EnvCommandError as e: - raise PoetryException(f"Failed to install {path.as_posix()}") from e + raise PoetryException(f"Failed to install {path}") from e diff --git a/tests/conftest.py b/tests/conftest.py index 11e424894f6..98e6030dd18 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,7 +5,6 @@ import re import shutil import sys -import tempfile from contextlib import suppress from pathlib import Path @@ -28,7 +27,6 @@ from poetry.utils.env import EnvManager from poetry.utils.env import SystemEnv from poetry.utils.env import VirtualEnv -from poetry.utils.helpers import remove_directory from tests.helpers import MOCK_DEFAULT_GIT_REVISION from tests.helpers import TestLocker from tests.helpers import TestRepository @@ -169,8 +167,8 @@ def with_chained_null_keyring(mocker: MockerFixture) -> None: @pytest.fixture -def config_cache_dir(tmp_dir: str) -> Path: - path = Path(tmp_dir) / ".cache" / "pypoetry" +def config_cache_dir(tmp_path: Path) -> Path: + path = tmp_path / ".cache" / "pypoetry" path.mkdir(parents=True) return path @@ -219,8 +217,10 @@ def config( @pytest.fixture() -def config_dir(tmp_dir: str) -> Path: - return Path(tempfile.mkdtemp(prefix="poetry_config_", dir=tmp_dir)) +def config_dir(tmp_path: Path) -> Path: + path = tmp_path / "config" + path.mkdir() + return path @pytest.fixture(autouse=True) @@ -296,18 +296,8 @@ def _fixture_dir(name: str) -> Path: @pytest.fixture -def tmp_dir() -> Iterator[str]: - dir_ = tempfile.mkdtemp(prefix="poetry_") - path = Path(dir_) - - yield path.resolve().as_posix() - - remove_directory(path, force=True) - - -@pytest.fixture -def tmp_venv(tmp_dir: str) -> Iterator[VirtualEnv]: - venv_path = Path(tmp_dir) / "venv" +def tmp_venv(tmp_path: Path) -> Iterator[VirtualEnv]: + venv_path = tmp_path / "venv" EnvManager.build_venv(venv_path) @@ -348,14 +338,14 @@ def repo(http: type[httpretty.httpretty]) -> TestRepository: @pytest.fixture def project_factory( - tmp_dir: str, + tmp_path: Path, config: Config, repo: TestRepository, installed: Repository, default_python: str, load_required_fixtures: None, ) -> ProjectFactory: - workspace = Path(tmp_dir) + workspace = tmp_path def _factory( name: str | None = None, @@ -380,9 +370,7 @@ def _factory( project_dir.mkdir(parents=True, exist_ok=True) if pyproject_content: - with project_dir.joinpath("pyproject.toml").open( - "w", encoding="utf-8" - ) as f: + with (project_dir / "pyproject.toml").open("w", encoding="utf-8") as f: f.write(pyproject_content) else: assert name is not None @@ -446,10 +434,10 @@ def set_simple_log_formatter() -> None: @pytest.fixture -def fixture_copier(fixture_base: Path, tmp_dir: str) -> FixtureCopier: +def fixture_copier(fixture_base: Path, tmp_path: Path) -> FixtureCopier: def _copy(relative_path: str, target: Path | None = None) -> Path: - path = fixture_base.joinpath(relative_path) - target = target or Path(tmp_dir, relative_path) + path = fixture_base / relative_path + target = target or (tmp_path / relative_path) target.parent.mkdir(parents=True, exist_ok=True) if target.exists(): diff --git a/tests/console/commands/env/conftest.py b/tests/console/commands/env/conftest.py index 832efe5e5cc..4f613248cc0 100644 --- a/tests/console/commands/env/conftest.py +++ b/tests/console/commands/env/conftest.py @@ -2,7 +2,6 @@ import os -from pathlib import Path from typing import TYPE_CHECKING import pytest @@ -12,6 +11,7 @@ if TYPE_CHECKING: from collections.abc import Iterator + from pathlib import Path from tests.helpers import PoetryTestApplication @@ -25,8 +25,8 @@ def venv_name(app: PoetryTestApplication) -> str: @pytest.fixture -def venv_cache(tmp_dir: str) -> Path: - return Path(tmp_dir) +def venv_cache(tmp_path: Path) -> Path: + return tmp_path @pytest.fixture(scope="module") @@ -49,7 +49,7 @@ def venvs_in_cache_dirs( ) -> list[str]: directories = [] for version in python_versions: - directory = venv_cache.joinpath(f"{venv_name}-py{version}") + directory = venv_cache / f"{venv_name}-py{version}" directory.mkdir(parents=True, exist_ok=True) directories.append(directory.name) return directories diff --git a/tests/console/commands/env/test_list.py b/tests/console/commands/env/test_list.py index 6c949b66d50..687493faabb 100644 --- a/tests/console/commands/env/test_list.py +++ b/tests/console/commands/env/test_list.py @@ -39,7 +39,7 @@ def test_none_activated( ): mocker.patch("poetry.utils.env.EnvManager.get", return_value=env) tester.execute() - expected = "\n".join(venvs_in_cache_dirs).strip() + expected = "\n".join(venvs_in_cache_dirs) assert tester.io.fetch_output().strip() == expected @@ -50,9 +50,7 @@ def test_activated( venv_activate_37: None, ): tester.execute() - expected = ( - "\n".join(venvs_in_cache_dirs).strip().replace("py3.7", "py3.7 (Activated)") - ) + expected = "\n".join(venvs_in_cache_dirs).replace("py3.7", "py3.7 (Activated)") assert tester.io.fetch_output().strip() == expected diff --git a/tests/console/commands/self/test_show_plugins.py b/tests/console/commands/self/test_show_plugins.py index 3ce27786760..c2a93bc632a 100644 --- a/tests/console/commands/self/test_show_plugins.py +++ b/tests/console/commands/self/test_show_plugins.py @@ -1,6 +1,5 @@ from __future__ import annotations -from pathlib import Path from typing import TYPE_CHECKING from typing import Any from typing import Callable @@ -17,6 +16,7 @@ if TYPE_CHECKING: from os import PathLike + from pathlib import Path from cleo.io.io import IO from cleo.testers.command_tester import CommandTester @@ -64,7 +64,7 @@ def plugin_package(plugin_package_requires_dist: list[str]) -> Package: @pytest.fixture() -def plugin_distro(plugin_package: Package, tmp_dir: str) -> metadata.Distribution: +def plugin_distro(plugin_package: Package, tmp_path: Path) -> metadata.Distribution: class MockDistribution(metadata.Distribution): def read_text(self, filename: str) -> str | None: if filename == "METADATA": @@ -81,7 +81,7 @@ def read_text(self, filename: str) -> str | None: return None def locate_file(self, path: PathLike[str]) -> PathLike[str]: - return Path(tmp_dir, path) + return tmp_path / path return MockDistribution() diff --git a/tests/console/commands/test_new.py b/tests/console/commands/test_new.py index 58560b01e89..2b2cc9d83f2 100644 --- a/tests/console/commands/test_new.py +++ b/tests/console/commands/test_new.py @@ -155,18 +155,20 @@ def test_command_new( package_path: str, include_from: str | None, tester: CommandTester, - tmp_dir: str, + tmp_path: Path, ): - path = Path(tmp_dir) / directory - options.append(path.as_posix()) + path = tmp_path / directory + options.append(str(path)) tester.execute(" ".join(options)) verify_project_directory(path, package_name, package_path, include_from) @pytest.mark.parametrize(("fmt",), [(None,), ("md",), ("rst",), ("adoc",), ("creole",)]) -def test_command_new_with_readme(fmt: str | None, tester: CommandTester, tmp_dir: str): +def test_command_new_with_readme( + fmt: str | None, tester: CommandTester, tmp_path: Path +): package = "package" - path = Path(tmp_dir) / package + path = tmp_path / package options = [path.as_posix()] if fmt: @@ -191,7 +193,7 @@ def test_respect_prefer_active_on_new( config: Config, mocker: MockerFixture, tester: CommandTester, - tmp_dir: str, + tmp_path: Path, ): from poetry.utils.env import GET_PYTHON_VERSION_ONELINER @@ -208,8 +210,8 @@ def mock_check_output(cmd: str, *_: Any, **__: Any) -> str: config.config["virtualenvs"]["prefer-active-python"] = prefer_active package = "package" - path = Path(tmp_dir) / package - options = [path.as_posix()] + path = tmp_path / package + options = [str(path)] tester.execute(" ".join(options)) pyproject_file = path / "pyproject.toml" diff --git a/tests/console/conftest.py b/tests/console/conftest.py index eed6665335e..7813273e1a8 100644 --- a/tests/console/conftest.py +++ b/tests/console/conftest.py @@ -40,8 +40,8 @@ def installer() -> NoopInstaller: @pytest.fixture -def env(tmp_dir: str) -> MockEnv: - path = Path(tmp_dir) / ".venv" +def env(tmp_path: Path) -> MockEnv: + path = tmp_path / ".venv" path.mkdir(parents=True) return MockEnv(path=path, is_venv=True) diff --git a/tests/inspection/test_info.py b/tests/inspection/test_info.py index 65628f2ffb4..4b529e3bedc 100644 --- a/tests/inspection/test_info.py +++ b/tests/inspection/test_info.py @@ -36,7 +36,9 @@ def demo_wheel() -> Path: @pytest.fixture def source_dir(tmp_path: Path) -> Path: - return Path(tmp_path.as_posix()) + path = tmp_path / "source" + path.mkdir() + return path @pytest.fixture diff --git a/tests/installation/test_executor.py b/tests/installation/test_executor.py index 05e2cdc648f..c8a529b40a7 100644 --- a/tests/installation/test_executor.py +++ b/tests/installation/test_executor.py @@ -91,8 +91,8 @@ def _prepare( @pytest.fixture -def env(tmp_dir: str) -> MockEnv: - path = Path(tmp_dir) / ".venv" +def env(tmp_path: Path) -> MockEnv: + path = tmp_path / ".venv" path.mkdir(parents=True) return MockEnv(path=path, is_venv=True) @@ -169,19 +169,16 @@ def callback( @pytest.fixture -def copy_wheel(tmp_dir: Path, fixture_dir: FixtureDirGetter) -> Callable[[], Path]: +def copy_wheel(tmp_path: Path, fixture_dir: FixtureDirGetter) -> Callable[[], Path]: def _copy_wheel() -> Path: tmp_name = tempfile.mktemp() - Path(tmp_dir).joinpath(tmp_name).mkdir() + (tmp_path / tmp_name).mkdir() shutil.copyfile( - ( - fixture_dir("distributions") / "demo-0.1.2-py2.py3-none-any.whl" - ).as_posix(), - (Path(tmp_dir) / tmp_name / "demo-0.1.2-py2.py3-none-any.whl").as_posix(), + fixture_dir("distributions") / "demo-0.1.2-py2.py3-none-any.whl", + tmp_path / tmp_name / "demo-0.1.2-py2.py3-none-any.whl", ) - - return Path(tmp_dir) / tmp_name / "demo-0.1.2-py2.py3-none-any.whl" + return tmp_path / tmp_name / "demo-0.1.2-py2.py3-none-any.whl" return _copy_wheel @@ -201,7 +198,7 @@ def test_execute_executes_a_batch_of_operations( config: Config, pool: RepositoryPool, io: BufferedIO, - tmp_dir: str, + tmp_path: Path, mock_file_downloads: None, env: MockEnv, copy_wheel: Callable[[], Path], @@ -209,7 +206,7 @@ def test_execute_executes_a_batch_of_operations( ): wheel_install = mocker.patch.object(WheelInstaller, "install") - config.merge({"cache-dir": tmp_dir}) + config.merge({"cache-dir": str(tmp_path)}) artifact_cache = ArtifactCache(cache_dir=config.artifacts_cache_directory) prepare_spy = mocker.spy(Chef, "_prepare") @@ -312,13 +309,13 @@ def test_execute_prints_warning_for_yanked_package( config: Config, pool: RepositoryPool, io: BufferedIO, - tmp_dir: str, + tmp_path: Path, mock_file_downloads: None, env: MockEnv, operations: list[Operation], has_warning: bool, ): - config.merge({"cache-dir": tmp_dir}) + config.merge({"cache-dir": str(tmp_path)}) executor = Executor(env, pool, config, io) @@ -345,11 +342,11 @@ def test_execute_prints_warning_for_invalid_wheels( config: Config, pool: RepositoryPool, io: BufferedIO, - tmp_dir: str, + tmp_path: Path, mock_file_downloads: None, env: MockEnv, ): - config.merge({"cache-dir": tmp_dir}) + config.merge({"cache-dir": str(tmp_path)}) executor = Executor(env, pool, config, io) @@ -460,11 +457,11 @@ def test_execute_works_with_ansi_output( config: Config, pool: RepositoryPool, io_decorated: BufferedIO, - tmp_dir: str, + tmp_path: Path, mock_file_downloads: None, env: MockEnv, ): - config.merge({"cache-dir": tmp_dir}) + config.merge({"cache-dir": str(tmp_path)}) executor = Executor(env, pool, config, io_decorated) @@ -497,11 +494,11 @@ def test_execute_works_with_no_ansi_output( config: Config, pool: RepositoryPool, io_not_decorated: BufferedIO, - tmp_dir: str, + tmp_path: Path, mock_file_downloads: None, env: MockEnv, ): - config.merge({"cache-dir": tmp_dir}) + config.merge({"cache-dir": str(tmp_path)}) executor = Executor(env, pool, config, io_not_decorated) @@ -581,7 +578,7 @@ def write_line(string: str, **kwargs: Any) -> None: def test_executor_should_delete_incomplete_downloads( config: Config, io: BufferedIO, - tmp_dir: str, + tmp_path: Path, mocker: MockerFixture, pool: RepositoryPool, mock_file_downloads: None, @@ -589,7 +586,7 @@ def test_executor_should_delete_incomplete_downloads( fixture_dir: FixtureDirGetter, ): fixture = fixture_dir("distributions") / "demo-0.1.0-py2.py3-none-any.whl" - destination_fixture = Path(tmp_dir) / "tomlkit-0.5.3-py2.py3-none-any.whl" + destination_fixture = tmp_path / "tomlkit-0.5.3-py2.py3-none-any.whl" shutil.copyfile(str(fixture), str(destination_fixture)) mocker.patch( "poetry.installation.executor.Executor._download_archive", @@ -601,10 +598,10 @@ def test_executor_should_delete_incomplete_downloads( ) mocker.patch( "poetry.installation.executor.ArtifactCache.get_cache_directory_for_link", - return_value=Path(tmp_dir), + return_value=tmp_path, ) - config.merge({"cache-dir": tmp_dir}) + config.merge({"cache-dir": str(tmp_path)}) executor = Executor(env, pool, config, io) @@ -1177,7 +1174,7 @@ def test_executor_fallback_on_poetry_create_error_without_wheel_installer( config: Config, pool: RepositoryPool, io: BufferedIO, - tmp_dir: str, + tmp_path: Path, mock_file_downloads: None, env: MockEnv, fixture_dir: FixtureDirGetter, @@ -1193,7 +1190,7 @@ def test_executor_fallback_on_poetry_create_error_without_wheel_installer( config.merge( { - "cache-dir": tmp_dir, + "cache-dir": str(tmp_path), "installer": {"modern-installation": False}, } ) @@ -1346,7 +1343,6 @@ def test_build_system_requires_not_available( config: Config, pool: RepositoryPool, io: BufferedIO, - tmp_dir: str, mock_file_downloads: None, env: MockEnv, fixture_dir: FixtureDirGetter, diff --git a/tests/integration/test_utils_vcs_git.py b/tests/integration/test_utils_vcs_git.py index aaa864283ce..ae979edf759 100644 --- a/tests/integration/test_utils_vcs_git.py +++ b/tests/integration/test_utils_vcs_git.py @@ -23,7 +23,7 @@ if TYPE_CHECKING: - from _pytest.tmpdir import TempdirFactory + from _pytest.tmpdir import TempPathFactory from dulwich.client import FetchPackResult from dulwich.client import GitClient from pytest_mock import MockerFixture @@ -79,9 +79,9 @@ def source_directory_name(source_url: str) -> str: @pytest.fixture(scope="module") -def local_repo(tmpdir_factory: TempdirFactory, source_directory_name: str) -> Repo: +def local_repo(tmp_path_factory: TempPathFactory, source_directory_name: str) -> Repo: with Repo.init( - tmpdir_factory.mktemp("src") / source_directory_name, mkdir=True + tmp_path_factory.mktemp("src") / source_directory_name, mkdir=True ) as repo: yield repo diff --git a/tests/masonry/builders/test_editable_builder.py b/tests/masonry/builders/test_editable_builder.py index 3e90a0b3e24..eab115b3181 100644 --- a/tests/masonry/builders/test_editable_builder.py +++ b/tests/masonry/builders/test_editable_builder.py @@ -83,8 +83,8 @@ def env_manager(simple_poetry: Poetry) -> EnvManager: @pytest.fixture -def tmp_venv(tmp_dir: str, env_manager: EnvManager) -> VirtualEnv: - venv_path = Path(tmp_dir) / "venv" +def tmp_venv(tmp_path: Path, env_manager: EnvManager) -> VirtualEnv: + venv_path = tmp_path / "venv" env_manager.build_venv(venv_path) @@ -222,10 +222,10 @@ def test_builder_installs_proper_files_for_standard_packages( def test_builder_falls_back_on_setup_and_pip_for_packages_with_build_scripts( - mocker: MockerFixture, extended_poetry: Poetry, tmp_dir: str + mocker: MockerFixture, extended_poetry: Poetry, tmp_path: Path ) -> None: pip_install = mocker.patch("poetry.masonry.builders.editable.pip_install") - env = MockEnv(path=Path(tmp_dir) / "foo") + env = MockEnv(path=tmp_path / "foo") builder = EditableBuilder(extended_poetry, env, NullIO()) builder.build() @@ -235,10 +235,10 @@ def test_builder_falls_back_on_setup_and_pip_for_packages_with_build_scripts( assert [] == env.executed -def test_builder_setup_generation_runs_with_pip_editable(tmp_dir: str) -> None: +def test_builder_setup_generation_runs_with_pip_editable(tmp_path: Path) -> None: # create an isolated copy of the project fixture = Path(__file__).parent.parent.parent / "fixtures" / "extended_project" - extended_project = Path(tmp_dir) / "extended_project" + extended_project = tmp_path / "extended_project" shutil.copytree(fixture, extended_project) assert extended_project.exists() diff --git a/tests/plugins/test_plugin_manager.py b/tests/plugins/test_plugin_manager.py index 868d61f26f0..a3fcc4249d1 100644 --- a/tests/plugins/test_plugin_manager.py +++ b/tests/plugins/test_plugin_manager.py @@ -47,7 +47,7 @@ def activate(self, poetry: Poetry, io: BufferedIO) -> None: @pytest.fixture() -def poetry(tmp_dir: str, config: Config) -> Poetry: +def poetry(tmp_path: Path, config: Config) -> Poetry: poetry = Poetry( CWD / "pyproject.toml", {}, diff --git a/tests/repositories/test_installed_repository.py b/tests/repositories/test_installed_repository.py index 5d71bcd7497..592d99a999a 100644 --- a/tests/repositories/test_installed_repository.py +++ b/tests/repositories/test_installed_repository.py @@ -101,9 +101,9 @@ def test_load_successful(repository: InstalledRepository): def test_load_successful_with_invalid_distribution( - caplog: LogCaptureFixture, mocker: MockerFixture, env: MockEnv, tmp_dir: str + caplog: LogCaptureFixture, mocker: MockerFixture, env: MockEnv, tmp_path: Path ) -> None: - invalid_dist_info = Path(tmp_dir) / "site-packages" / "invalid-0.1.0.dist-info" + invalid_dist_info = tmp_path / "site-packages" / "invalid-0.1.0.dist-info" invalid_dist_info.mkdir(parents=True) mocker.patch( "poetry.utils._compat.metadata.Distribution.discover", diff --git a/tests/utils/test_env.py b/tests/utils/test_env.py index 5e1fb3cdbb9..c8589f48594 100644 --- a/tests/utils/test_env.py +++ b/tests/utils/test_env.py @@ -88,9 +88,9 @@ def manager(poetry: Poetry) -> EnvManager: def test_virtualenvs_with_spaces_in_their_path_work_as_expected( - tmp_dir: str, manager: EnvManager + tmp_path: Path, manager: EnvManager ) -> None: - venv_path = Path(tmp_dir) / "Virtual Env" + venv_path = tmp_path / "Virtual Env" manager.build_venv(venv_path) @@ -100,10 +100,10 @@ def test_virtualenvs_with_spaces_in_their_path_work_as_expected( @pytest.mark.skipif(sys.platform != "darwin", reason="requires darwin") -def test_venv_backup_exclusion(tmp_dir: str, manager: EnvManager) -> None: +def test_venv_backup_exclusion(tmp_path: Path, manager: EnvManager): import xattr - venv_path = Path(tmp_dir) / "Virtual Env" + venv_path = tmp_path / "Virtual Env" manager.build_venv(venv_path) @@ -121,9 +121,9 @@ def test_venv_backup_exclusion(tmp_dir: str, manager: EnvManager) -> None: def test_env_commands_with_spaces_in_their_arg_work_as_expected( - tmp_dir: str, manager: EnvManager + tmp_path: Path, manager: EnvManager ) -> None: - venv_path = Path(tmp_dir) / "Virtual Env" + venv_path = tmp_path / "Virtual Env" manager.build_venv(venv_path) venv = VirtualEnv(venv_path) assert venv.run("python", str(venv.pip), "--version").startswith( @@ -132,9 +132,9 @@ def test_env_commands_with_spaces_in_their_arg_work_as_expected( def test_env_shell_commands_with_stdinput_in_their_arg_work_as_expected( - tmp_dir: str, manager: EnvManager + tmp_path: Path, manager: EnvManager ) -> None: - venv_path = Path(tmp_dir) / "Virtual Env" + venv_path = tmp_path / "Virtual Env" manager.build_venv(venv_path) venv = VirtualEnv(venv_path) run_output_path = Path(venv.run("python", "-", input_=GET_BASE_PREFIX).strip()) @@ -143,9 +143,9 @@ def test_env_shell_commands_with_stdinput_in_their_arg_work_as_expected( def test_env_get_supported_tags_matches_inside_virtualenv( - tmp_dir: str, manager: EnvManager + tmp_path: Path, manager: EnvManager ) -> None: - venv_path = Path(tmp_dir) / "Virtual Env" + venv_path = tmp_path / "Virtual Env" manager.build_venv(venv_path) venv = VirtualEnv(venv_path) @@ -209,7 +209,7 @@ def check_output(cmd: list[str], *args: Any, **kwargs: Any) -> str: def test_activate_activates_non_existing_virtualenv_no_envs_file( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, @@ -219,7 +219,7 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file( if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) mocker.patch("shutil.which", side_effect=lambda py: f"/usr/bin/{py}") mocker.patch( @@ -235,7 +235,7 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file( env = manager.activate("python3.7") m.assert_called_with( - Path(tmp_dir) / f"{venv_name}-py3.7", + tmp_path / f"{venv_name}-py3.7", executable=Path("/usr/bin/python3.7"), flags={ "always-copy": False, @@ -246,18 +246,18 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file( prompt="simple-project-py3.7", ) - envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") + envs_file = TOMLFile(tmp_path / "envs.toml") assert envs_file.exists() envs = envs_file.read() assert envs[venv_name]["minor"] == "3.7" assert envs[venv_name]["patch"] == "3.7.1" - assert env.path == Path(tmp_dir) / f"{venv_name}-py3.7" + assert env.path == tmp_path / f"{venv_name}-py3.7" assert env.base == Path("/prefix") def test_activate_fails_when_python_cannot_be_found( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, @@ -267,9 +267,9 @@ def test_activate_fails_when_python_cannot_be_found( if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] - os.mkdir(os.path.join(tmp_dir, f"{venv_name}-py3.7")) + os.mkdir(tmp_path / f"{venv_name}-py3.7") - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) mocker.patch("shutil.which", return_value=None) @@ -281,7 +281,7 @@ def test_activate_fails_when_python_cannot_be_found( def test_activate_activates_existing_virtualenv_no_envs_file( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, @@ -291,9 +291,9 @@ def test_activate_activates_existing_virtualenv_no_envs_file( if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] - os.mkdir(os.path.join(tmp_dir, f"{venv_name}-py3.7")) + os.mkdir(tmp_path / f"{venv_name}-py3.7") - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) mocker.patch("shutil.which", side_effect=lambda py: f"/usr/bin/{py}") mocker.patch( @@ -310,18 +310,18 @@ def test_activate_activates_existing_virtualenv_no_envs_file( m.assert_not_called() - envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") + envs_file = TOMLFile(tmp_path / "envs.toml") assert envs_file.exists() envs = envs_file.read() assert envs[venv_name]["minor"] == "3.7" assert envs[venv_name]["patch"] == "3.7.1" - assert env.path == Path(tmp_dir) / f"{venv_name}-py3.7" + assert env.path == tmp_path / f"{venv_name}-py3.7" assert env.base == Path("/prefix") def test_activate_activates_same_virtualenv_with_envs_file( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, @@ -331,14 +331,14 @@ def test_activate_activates_same_virtualenv_with_envs_file( if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] - envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") + envs_file = TOMLFile(tmp_path / "envs.toml") doc = tomlkit.document() doc[venv_name] = {"minor": "3.7", "patch": "3.7.1"} envs_file.write(doc) - os.mkdir(os.path.join(tmp_dir, f"{venv_name}-py3.7")) + os.mkdir(tmp_path / f"{venv_name}-py3.7") - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) mocker.patch("shutil.which", side_effect=lambda py: f"/usr/bin/{py}") mocker.patch( @@ -360,12 +360,12 @@ def test_activate_activates_same_virtualenv_with_envs_file( assert envs[venv_name]["minor"] == "3.7" assert envs[venv_name]["patch"] == "3.7.1" - assert env.path == Path(tmp_dir) / f"{venv_name}-py3.7" + assert env.path == tmp_path / f"{venv_name}-py3.7" assert env.base == Path("/prefix") def test_activate_activates_different_virtualenv_with_envs_file( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, @@ -375,14 +375,14 @@ def test_activate_activates_different_virtualenv_with_envs_file( if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] - envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") + envs_file = TOMLFile(tmp_path / "envs.toml") doc = tomlkit.document() doc[venv_name] = {"minor": "3.7", "patch": "3.7.1"} envs_file.write(doc) - os.mkdir(os.path.join(tmp_dir, f"{venv_name}-py3.7")) + os.mkdir(tmp_path / f"{venv_name}-py3.7") - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) mocker.patch("shutil.which", side_effect=lambda py: f"/usr/bin/{py}") mocker.patch( @@ -398,7 +398,7 @@ def test_activate_activates_different_virtualenv_with_envs_file( env = manager.activate("python3.6") m.assert_called_with( - Path(tmp_dir) / f"{venv_name}-py3.6", + tmp_path / f"{venv_name}-py3.6", executable=Path("/usr/bin/python3.6"), flags={ "always-copy": False, @@ -414,12 +414,12 @@ def test_activate_activates_different_virtualenv_with_envs_file( assert envs[venv_name]["minor"] == "3.6" assert envs[venv_name]["patch"] == "3.6.6" - assert env.path == Path(tmp_dir) / f"{venv_name}-py3.6" + assert env.path == tmp_path / f"{venv_name}-py3.6" assert env.base == Path("/prefix") def test_activate_activates_recreates_for_different_patch( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, @@ -429,14 +429,14 @@ def test_activate_activates_recreates_for_different_patch( if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] - envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") + envs_file = TOMLFile(tmp_path / "envs.toml") doc = tomlkit.document() doc[venv_name] = {"minor": "3.7", "patch": "3.7.0"} envs_file.write(doc) - os.mkdir(os.path.join(tmp_dir, f"{venv_name}-py3.7")) + os.mkdir(tmp_path / f"{venv_name}-py3.7") - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) mocker.patch("shutil.which", side_effect=lambda py: f"/usr/bin/{py}") mocker.patch( @@ -463,7 +463,7 @@ def test_activate_activates_recreates_for_different_patch( env = manager.activate("python3.7") build_venv_m.assert_called_with( - Path(tmp_dir) / f"{venv_name}-py3.7", + tmp_path / f"{venv_name}-py3.7", executable=Path("/usr/bin/python3.7"), flags={ "always-copy": False, @@ -473,20 +473,20 @@ def test_activate_activates_recreates_for_different_patch( }, prompt="simple-project-py3.7", ) - remove_venv_m.assert_called_with(Path(tmp_dir) / f"{venv_name}-py3.7") + remove_venv_m.assert_called_with(tmp_path / f"{venv_name}-py3.7") assert envs_file.exists() envs = envs_file.read() assert envs[venv_name]["minor"] == "3.7" assert envs[venv_name]["patch"] == "3.7.1" - assert env.path == Path(tmp_dir) / f"{venv_name}-py3.7" + assert env.path == tmp_path / f"{venv_name}-py3.7" assert env.base == Path("/prefix") - assert (Path(tmp_dir) / f"{venv_name}-py3.7").exists() + assert (tmp_path / f"{venv_name}-py3.7").exists() def test_activate_does_not_recreate_when_switching_minor( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, @@ -496,15 +496,15 @@ def test_activate_does_not_recreate_when_switching_minor( if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] - envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") + envs_file = TOMLFile(tmp_path / "envs.toml") doc = tomlkit.document() doc[venv_name] = {"minor": "3.7", "patch": "3.7.0"} envs_file.write(doc) - os.mkdir(os.path.join(tmp_dir, f"{venv_name}-py3.7")) - os.mkdir(os.path.join(tmp_dir, f"{venv_name}-py3.6")) + os.mkdir(tmp_path / f"{venv_name}-py3.7") + os.mkdir(tmp_path / f"{venv_name}-py3.6") - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) mocker.patch("shutil.which", side_effect=lambda py: f"/usr/bin/{py}") mocker.patch( @@ -532,13 +532,13 @@ def test_activate_does_not_recreate_when_switching_minor( assert envs[venv_name]["minor"] == "3.6" assert envs[venv_name]["patch"] == "3.6.6" - assert env.path == Path(tmp_dir) / f"{venv_name}-py3.6" + assert env.path == tmp_path / f"{venv_name}-py3.6" assert env.base == Path("/prefix") - assert (Path(tmp_dir) / f"{venv_name}-py3.6").exists() + assert (tmp_path / f"{venv_name}-py3.6").exists() def test_deactivate_non_activated_but_existing( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, @@ -549,9 +549,9 @@ def test_deactivate_non_activated_but_existing( del os.environ["VIRTUAL_ENV"] python = ".".join(str(c) for c in sys.version_info[:2]) - (Path(tmp_dir) / f"{venv_name}-py{python}").mkdir() + (tmp_path / f"{venv_name}-py{python}").mkdir() - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) mocker.patch( "subprocess.check_output", @@ -561,11 +561,11 @@ def test_deactivate_non_activated_but_existing( manager.deactivate() env = manager.get() - assert env.path == Path(tmp_dir) / f"{venv_name}-py{python}" + assert env.path == tmp_path / f"{venv_name}-py{python}" def test_deactivate_activated( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, @@ -577,12 +577,10 @@ def test_deactivate_activated( version = Version.from_parts(*sys.version_info[:3]) other_version = Version.parse("3.4") if version.major == 2 else version.next_minor() - (Path(tmp_dir) / f"{venv_name}-py{version.major}.{version.minor}").mkdir() - ( - Path(tmp_dir) / f"{venv_name}-py{other_version.major}.{other_version.minor}" - ).mkdir() + (tmp_path / f"{venv_name}-py{version.major}.{version.minor}").mkdir() + (tmp_path / f"{venv_name}-py{other_version.major}.{other_version.minor}").mkdir() - envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") + envs_file = TOMLFile(tmp_path / "envs.toml") doc = tomlkit.document() doc[venv_name] = { "minor": f"{other_version.major}.{other_version.minor}", @@ -590,7 +588,7 @@ def test_deactivate_activated( } envs_file.write(doc) - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) mocker.patch( "subprocess.check_output", @@ -600,14 +598,14 @@ def test_deactivate_activated( manager.deactivate() env = manager.get() - assert env.path == Path(tmp_dir) / f"{venv_name}-py{version.major}.{version.minor}" + assert env.path == tmp_path / f"{venv_name}-py{version.major}.{version.minor}" envs = envs_file.read() assert len(envs) == 0 def test_get_prefers_explicitly_activated_virtualenvs_over_env_var( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, @@ -616,10 +614,10 @@ def test_get_prefers_explicitly_activated_virtualenvs_over_env_var( ) -> None: os.environ["VIRTUAL_ENV"] = "/environment/prefix" - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) - (Path(tmp_dir) / f"{venv_name}-py3.7").mkdir() + config.merge({"virtualenvs": {"path": str(tmp_path)}}) + (tmp_path / f"{venv_name}-py3.7").mkdir() - envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") + envs_file = TOMLFile(tmp_path / "envs.toml") doc = tomlkit.document() doc[venv_name] = {"minor": "3.7", "patch": "3.7.0"} envs_file.write(doc) @@ -635,41 +633,41 @@ def test_get_prefers_explicitly_activated_virtualenvs_over_env_var( env = manager.get() - assert env.path == Path(tmp_dir) / f"{venv_name}-py3.7" + assert env.path == tmp_path / f"{venv_name}-py3.7" assert env.base == Path("/prefix") def test_list( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, venv_name: str, ) -> None: - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) - (Path(tmp_dir) / f"{venv_name}-py3.7").mkdir() - (Path(tmp_dir) / f"{venv_name}-py3.6").mkdir() + (tmp_path / f"{venv_name}-py3.7").mkdir() + (tmp_path / f"{venv_name}-py3.6").mkdir() venvs = manager.list() assert len(venvs) == 2 - assert venvs[0].path == (Path(tmp_dir) / f"{venv_name}-py3.6") - assert venvs[1].path == (Path(tmp_dir) / f"{venv_name}-py3.7") + assert venvs[0].path == tmp_path / f"{venv_name}-py3.6" + assert venvs[1].path == tmp_path / f"{venv_name}-py3.7" def test_remove_by_python_version( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, mocker: MockerFixture, venv_name: str, ) -> None: - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) - (Path(tmp_dir) / f"{venv_name}-py3.7").mkdir() - (Path(tmp_dir) / f"{venv_name}-py3.6").mkdir() + (tmp_path / f"{venv_name}-py3.7").mkdir() + (tmp_path / f"{venv_name}-py3.6").mkdir() mocker.patch( "subprocess.check_output", @@ -678,23 +676,23 @@ def test_remove_by_python_version( venv = manager.remove("3.6") - expected_venv_path = Path(tmp_dir) / f"{venv_name}-py3.6" + expected_venv_path = tmp_path / f"{venv_name}-py3.6" assert venv.path == expected_venv_path assert not expected_venv_path.exists() def test_remove_by_name( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, mocker: MockerFixture, venv_name: str, ) -> None: - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) - (Path(tmp_dir) / f"{venv_name}-py3.7").mkdir() - (Path(tmp_dir) / f"{venv_name}-py3.6").mkdir() + (tmp_path / f"{venv_name}-py3.7").mkdir() + (tmp_path / f"{venv_name}-py3.6").mkdir() mocker.patch( "subprocess.check_output", @@ -703,23 +701,23 @@ def test_remove_by_name( venv = manager.remove(f"{venv_name}-py3.6") - expected_venv_path = Path(tmp_dir) / f"{venv_name}-py3.6" + expected_venv_path = tmp_path / f"{venv_name}-py3.6" assert venv.path == expected_venv_path assert not expected_venv_path.exists() def test_remove_by_string_with_python_and_version( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, mocker: MockerFixture, venv_name: str, ) -> None: - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) - (Path(tmp_dir) / f"{venv_name}-py3.7").mkdir() - (Path(tmp_dir) / f"{venv_name}-py3.6").mkdir() + (tmp_path / f"{venv_name}-py3.7").mkdir() + (tmp_path / f"{venv_name}-py3.6").mkdir() mocker.patch( "subprocess.check_output", @@ -728,30 +726,30 @@ def test_remove_by_string_with_python_and_version( venv = manager.remove("python3.6") - expected_venv_path = Path(tmp_dir) / f"{venv_name}-py3.6" + expected_venv_path = tmp_path / f"{venv_name}-py3.6" assert venv.path == expected_venv_path assert not expected_venv_path.exists() def test_remove_by_full_path_to_python( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, mocker: MockerFixture, venv_name: str, ) -> None: - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) - (Path(tmp_dir) / f"{venv_name}-py3.7").mkdir() - (Path(tmp_dir) / f"{venv_name}-py3.6").mkdir() + (tmp_path / f"{venv_name}-py3.7").mkdir() + (tmp_path / f"{venv_name}-py3.6").mkdir() mocker.patch( "subprocess.check_output", side_effect=check_output_wrapper(Version.parse("3.6.6")), ) - expected_venv_path = Path(tmp_dir) / f"{venv_name}-py3.6" + expected_venv_path = tmp_path / f"{venv_name}-py3.6" python_path = expected_venv_path / "bin" / "python" venv = manager.remove(str(python_path)) @@ -761,16 +759,16 @@ def test_remove_by_full_path_to_python( def test_raises_if_acting_on_different_project_by_full_path( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, mocker: MockerFixture, ) -> None: - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) different_venv_name = "different-project" - different_venv_path = Path(tmp_dir) / f"{different_venv_name}-py3.6" + different_venv_path = tmp_path / f"{different_venv_name}-py3.6" different_venv_bin_path = different_venv_path / "bin" different_venv_bin_path.mkdir(parents=True) @@ -788,12 +786,12 @@ def test_raises_if_acting_on_different_project_by_full_path( def test_raises_if_acting_on_different_project_by_name( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, ) -> None: - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) different_venv_name = ( EnvManager.generate_env_name( @@ -802,7 +800,7 @@ def test_raises_if_acting_on_different_project_by_name( ) + "-py3.6" ) - different_venv_path = Path(tmp_dir) / different_venv_name + different_venv_path = tmp_path / different_venv_name different_venv_bin_path = different_venv_path / "bin" different_venv_bin_path.mkdir(parents=True) @@ -814,7 +812,7 @@ def test_raises_if_acting_on_different_project_by_name( def test_raises_when_passing_old_env_after_dir_rename( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, @@ -824,17 +822,17 @@ def test_raises_when_passing_old_env_after_dir_rename( # root directory of the project, which will create another venv with new name. # This is not ideal as you still "can't" remove it by name, but it at least doesn't # cause any unwanted side effects - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) previous_venv_name = EnvManager.generate_env_name( poetry.package.name, "previous_dir_name", ) - venv_path = Path(tmp_dir) / f"{venv_name}-py3.6" + venv_path = tmp_path / f"{venv_name}-py3.6" venv_path.mkdir() previous_venv_name = f"{previous_venv_name}-py3.6" - previous_venv_path = Path(tmp_dir) / previous_venv_name + previous_venv_path = tmp_path / previous_venv_name previous_venv_path.mkdir() with pytest.raises(IncorrectEnvError): @@ -842,31 +840,31 @@ def test_raises_when_passing_old_env_after_dir_rename( def test_remove_also_deactivates( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, mocker: MockerFixture, venv_name: str, ) -> None: - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) - (Path(tmp_dir) / f"{venv_name}-py3.7").mkdir() - (Path(tmp_dir) / f"{venv_name}-py3.6").mkdir() + (tmp_path / f"{venv_name}-py3.7").mkdir() + (tmp_path / f"{venv_name}-py3.6").mkdir() mocker.patch( "subprocess.check_output", side_effect=check_output_wrapper(Version.parse("3.6.6")), ) - envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") + envs_file = TOMLFile(tmp_path / "envs.toml") doc = tomlkit.document() doc[venv_name] = {"minor": "3.6", "patch": "3.6.6"} envs_file.write(doc) venv = manager.remove("python3.6") - expected_venv_path = Path(tmp_dir) / f"{venv_name}-py3.6" + expected_venv_path = tmp_path / f"{venv_name}-py3.6" assert venv.path == expected_venv_path assert not expected_venv_path.exists() @@ -875,7 +873,7 @@ def test_remove_also_deactivates( def test_remove_keeps_dir_if_not_deleteable( - tmp_dir: str, + tmp_path: Path, manager: EnvManager, poetry: Poetry, config: Config, @@ -884,9 +882,9 @@ def test_remove_keeps_dir_if_not_deleteable( ) -> None: # Ensure we empty rather than delete folder if its is an active mount point. # See https://github.com/python-poetry/poetry/pull/2064 - config.merge({"virtualenvs": {"path": str(tmp_dir)}}) + config.merge({"virtualenvs": {"path": str(tmp_path)}}) - venv_path = Path(tmp_dir) / f"{venv_name}-py3.6" + venv_path = tmp_path / f"{venv_name}-py3.6" venv_path.mkdir() folder1_path = venv_path / "folder1" @@ -928,17 +926,17 @@ def err_on_rm_venv_only(path: Path | str, *args: Any, **kwargs: Any) -> None: @pytest.mark.skipif(os.name == "nt", reason="Symlinks are not support for Windows") -def test_env_has_symlinks_on_nix(tmp_dir: str, tmp_venv: VirtualEnv) -> None: +def test_env_has_symlinks_on_nix(tmp_path: Path, tmp_venv: VirtualEnv) -> None: assert os.path.islink(tmp_venv.python) -def test_run_with_input(tmp_dir: str, tmp_venv: VirtualEnv) -> None: +def test_run_with_input(tmp_path: Path, tmp_venv: VirtualEnv) -> None: result = tmp_venv.run("python", "-", input_=MINIMAL_SCRIPT) assert result == "Minimal Output" + os.linesep -def test_run_with_input_non_zero_return(tmp_dir: str, tmp_venv: VirtualEnv) -> None: +def test_run_with_input_non_zero_return(tmp_path: Path, tmp_venv: VirtualEnv) -> None: with pytest.raises(EnvCommandError) as process_error: # Test command that will return non-zero returncode. tmp_venv.run("python", "-", input_=ERRORING_SCRIPT) @@ -947,7 +945,7 @@ def test_run_with_input_non_zero_return(tmp_dir: str, tmp_venv: VirtualEnv) -> N def test_run_with_keyboard_interrupt( - tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture + tmp_path: Path, tmp_venv: VirtualEnv, mocker: MockerFixture ) -> None: mocker.patch("subprocess.run", side_effect=KeyboardInterrupt()) with pytest.raises(KeyboardInterrupt): @@ -956,7 +954,7 @@ def test_run_with_keyboard_interrupt( def test_call_with_input_and_keyboard_interrupt( - tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture + tmp_path: Path, tmp_venv: VirtualEnv, mocker: MockerFixture ) -> None: mocker.patch("subprocess.run", side_effect=KeyboardInterrupt()) kwargs = {"call": True} @@ -966,7 +964,7 @@ def test_call_with_input_and_keyboard_interrupt( def test_call_no_input_with_keyboard_interrupt( - tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture + tmp_path: Path, tmp_venv: VirtualEnv, mocker: MockerFixture ) -> None: mocker.patch("subprocess.call", side_effect=KeyboardInterrupt()) kwargs = {"call": True} @@ -976,7 +974,7 @@ def test_call_no_input_with_keyboard_interrupt( def test_run_with_called_process_error( - tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture + tmp_path: Path, tmp_venv: VirtualEnv, mocker: MockerFixture ) -> None: mocker.patch( "subprocess.run", @@ -992,7 +990,7 @@ def test_run_with_called_process_error( def test_call_with_input_and_called_process_error( - tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture + tmp_path: Path, tmp_venv: VirtualEnv, mocker: MockerFixture ) -> None: mocker.patch( "subprocess.run", @@ -1009,7 +1007,7 @@ def test_call_with_input_and_called_process_error( def test_call_no_input_with_called_process_error( - tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture + tmp_path: Path, tmp_venv: VirtualEnv, mocker: MockerFixture ) -> None: mocker.patch( "subprocess.call", @@ -1026,7 +1024,7 @@ def test_call_no_input_with_called_process_error( def test_check_output_with_called_process_error( - tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture + tmp_path: Path, tmp_venv: VirtualEnv, mocker: MockerFixture ) -> None: mocker.patch( "subprocess.check_output", @@ -1067,7 +1065,7 @@ def target(result: list[int]) -> None: def test_run_python_script_called_process_error( - tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture + tmp_path: Path, tmp_venv: VirtualEnv, mocker: MockerFixture ) -> None: mocker.patch( "subprocess.run", @@ -1081,7 +1079,7 @@ def test_run_python_script_called_process_error( assert "some error" in str(error.value) -def test_run_python_script_only_stdout(tmp_dir: str, tmp_venv: VirtualEnv) -> None: +def test_run_python_script_only_stdout(tmp_path: Path, tmp_venv: VirtualEnv) -> None: output = tmp_venv.run_python_script( "import sys; print('some warning', file=sys.stderr); print('some output')" ) @@ -1358,7 +1356,7 @@ def test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir( manager: EnvManager, poetry: Poetry, config: Config, - tmp_dir: str, + tmp_path: Path, mocker: MockerFixture, ) -> None: if "VIRTUAL_ENV" in os.environ: @@ -1367,7 +1365,7 @@ def test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir( config.merge( { "virtualenvs": { - "path": str(Path(tmp_dir) / "virtualenvs"), + "path": str(tmp_path / "virtualenvs"), "in-project": True, } } @@ -1398,7 +1396,7 @@ def test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir( prompt="simple-project-py3.7", ) - envs_file = TOMLFile(Path(tmp_dir) / "virtualenvs" / "envs.toml") + envs_file = TOMLFile(tmp_path / "virtualenvs" / "envs.toml") assert not envs_file.exists() @@ -1479,8 +1477,8 @@ def test_env_no_pip( assert installed_packages == packages -def test_env_finds_the_correct_executables(tmp_dir: str, manager: EnvManager) -> None: - venv_path = Path(tmp_dir) / "Virtual Env" +def test_env_finds_the_correct_executables(tmp_path: Path, manager: EnvManager) -> None: + venv_path = tmp_path / "Virtual Env" manager.build_venv(venv_path, with_pip=True) venv = VirtualEnv(venv_path) @@ -1510,10 +1508,10 @@ def test_env_finds_the_correct_executables(tmp_dir: str, manager: EnvManager) -> def test_env_finds_the_correct_executables_for_generic_env( - tmp_dir: str, manager: EnvManager + tmp_path: Path, manager: EnvManager ) -> None: - venv_path = Path(tmp_dir) / "Virtual Env" - child_venv_path = Path(tmp_dir) / "Child Virtual Env" + venv_path = tmp_path / "Virtual Env" + child_venv_path = tmp_path / "Child Virtual Env" manager.build_venv(venv_path, with_pip=True) parent_venv = VirtualEnv(venv_path) manager.build_venv(child_venv_path, executable=parent_venv.python, with_pip=True) @@ -1535,10 +1533,10 @@ def test_env_finds_the_correct_executables_for_generic_env( def test_env_finds_fallback_executables_for_generic_env( - tmp_dir: str, manager: EnvManager + tmp_path: Path, manager: EnvManager ) -> None: - venv_path = Path(tmp_dir) / "Virtual Env" - child_venv_path = Path(tmp_dir) / "Child Virtual Env" + venv_path = tmp_path / "Virtual Env" + child_venv_path = tmp_path / "Child Virtual Env" manager.build_venv(venv_path, with_pip=True) parent_venv = VirtualEnv(venv_path) manager.build_venv(child_venv_path, executable=parent_venv.python, with_pip=True) @@ -1646,7 +1644,7 @@ def mock_check_output(cmd: str, *args: Any, **kwargs: Any) -> str: def test_generate_env_name_ignores_case_for_case_insensitive_fs( poetry: Poetry, - tmp_dir: str, + tmp_path: Path, ) -> None: venv_name1 = EnvManager.generate_env_name(poetry.package.name, "MyDiR") venv_name2 = EnvManager.generate_env_name(poetry.package.name, "mYdIr") @@ -1656,7 +1654,9 @@ def test_generate_env_name_ignores_case_for_case_insensitive_fs( assert venv_name1 != venv_name2 -def test_generate_env_name_uses_real_path(tmp_dir: str, mocker: MockerFixture) -> None: +def test_generate_env_name_uses_real_path( + tmp_path: Path, mocker: MockerFixture +) -> None: mocker.patch("os.path.realpath", return_value="the_real_dir") venv_name1 = EnvManager.generate_env_name("simple-project", "the_real_dir") venv_name2 = EnvManager.generate_env_name("simple-project", "linked_dir") @@ -1673,10 +1673,10 @@ def extended_without_setup_poetry() -> Poetry: def test_build_environment_called_build_script_specified( - mocker: MockerFixture, extended_without_setup_poetry: Poetry, tmp_dir: str + mocker: MockerFixture, extended_without_setup_poetry: Poetry, tmp_path: Path ) -> None: - project_env = MockEnv(path=Path(tmp_dir) / "project") - ephemeral_env = MockEnv(path=Path(tmp_dir) / "ephemeral") + project_env = MockEnv(path=tmp_path / "project") + ephemeral_env = MockEnv(path=tmp_path / "ephemeral") mocker.patch( "poetry.utils.env.ephemeral_environment" @@ -1698,10 +1698,10 @@ def test_build_environment_called_build_script_specified( def test_build_environment_not_called_without_build_script_specified( - mocker: MockerFixture, poetry: Poetry, tmp_dir: str + mocker: MockerFixture, poetry: Poetry, tmp_path: Path ) -> None: - project_env = MockEnv(path=Path(tmp_dir) / "project") - ephemeral_env = MockEnv(path=Path(tmp_dir) / "ephemeral") + project_env = MockEnv(path=tmp_path / "project") + ephemeral_env = MockEnv(path=tmp_path / "ephemeral") mocker.patch( "poetry.utils.env.ephemeral_environment" diff --git a/tests/utils/test_env_site.py b/tests/utils/test_env_site.py index b1c493761e8..d4882b46530 100644 --- a/tests/utils/test_env_site.py +++ b/tests/utils/test_env_site.py @@ -12,12 +12,12 @@ from pytest_mock import MockerFixture -def test_env_site_simple(tmp_dir: str, mocker: MockerFixture): +def test_env_site_simple(tmp_path: Path, mocker: MockerFixture): # emulate permission error when creating directory mocker.patch("pathlib.Path.mkdir", side_effect=OSError()) - site_packages = SitePackages(Path("/non-existent"), fallbacks=[Path(tmp_dir)]) + site_packages = SitePackages(Path("/non-existent"), fallbacks=[tmp_path]) candidates = site_packages.make_candidates(Path("hello.txt"), writable_only=True) - hello = Path(tmp_dir) / "hello.txt" + hello = tmp_path / "hello.txt" assert len(candidates) == 1 assert candidates[0].as_posix() == hello.as_posix() @@ -30,12 +30,11 @@ def test_env_site_simple(tmp_dir: str, mocker: MockerFixture): assert not (site_packages.path / "hello.txt").exists() -def test_env_site_select_first(tmp_dir: str): - path = Path(tmp_dir) - fallback = path / "fallback" +def test_env_site_select_first(tmp_path: Path): + fallback = tmp_path / "fallback" fallback.mkdir(parents=True) - site_packages = SitePackages(path, fallbacks=[fallback]) + site_packages = SitePackages(tmp_path, fallbacks=[fallback]) candidates = site_packages.make_candidates(Path("hello.txt"), writable_only=True) assert len(candidates) == 2 diff --git a/tests/utils/test_pip.py b/tests/utils/test_pip.py index c6bf8422538..6f5c4851dab 100644 --- a/tests/utils/test_pip.py +++ b/tests/utils/test_pip.py @@ -10,6 +10,8 @@ if TYPE_CHECKING: + from pathlib import Path + from pytest_mock import MockerFixture from poetry.utils.env import VirtualEnv @@ -17,7 +19,7 @@ def test_pip_install_successful( - tmp_dir: str, tmp_venv: VirtualEnv, fixture_dir: FixtureDirGetter + tmp_path: Path, tmp_venv: VirtualEnv, fixture_dir: FixtureDirGetter ): file_path = fixture_dir("distributions/demo-0.1.0-py2.py3-none-any.whl") result = pip_install(file_path, tmp_venv) @@ -26,7 +28,7 @@ def test_pip_install_successful( def test_pip_install_with_keyboard_interrupt( - tmp_dir: str, + tmp_path: Path, tmp_venv: VirtualEnv, fixture_dir: FixtureDirGetter, mocker: MockerFixture, From 5806b42e4e75a903ad64f3045afa068ffe9ddfce Mon Sep 17 00:00:00 2001 From: Bart Kamphorst Date: Mon, 24 Oct 2022 22:54:59 +0200 Subject: [PATCH 02/10] sources: introduce "priority" key for sources and deprecate flags "default" and "secondary", adjust cli accordingly (#7658) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Randy Döring <30527984+radoering@users.noreply.github.com> --- docs/cli.md | 7 +- docs/dependency-specification.md | 2 +- docs/repositories.md | 74 ++++++-- src/poetry/config/source.py | 35 +++- src/poetry/console/commands/source/add.py | 76 +++++++-- src/poetry/console/commands/source/show.py | 18 +- src/poetry/factory.py | 38 +++-- src/poetry/json/schemas/poetry.json | 32 +++- src/poetry/repositories/repository_pool.py | 42 ++++- tests/console/commands/source/conftest.py | 50 +++++- tests/console/commands/source/test_add.py | 161 +++++++++++++++++- tests/console/commands/source/test_show.py | 99 ++++++++--- tests/console/commands/test_config.py | 2 +- .../with_default_source/pyproject.toml | 2 +- .../with_default_source_legacy/README.rst | 2 + .../with_default_source_legacy/pyproject.toml | 61 +++++++ .../pyproject.toml | 4 +- .../pyproject.toml | 24 +++ .../pyproject.toml | 2 +- .../pyproject.toml | 23 +++ .../pyproject.toml | 2 +- .../pyproject.toml | 19 +++ .../pyproject.toml | 19 +++ .../pyproject.toml | 0 .../with_two_default_sources/pyproject.toml | 4 +- .../README.rst | 2 + .../pyproject.toml | 66 +++++++ tests/installation/test_pip_installer.py | 7 +- .../source/complete_invalid_priority.toml | 17 ++ ...plete_invalid_priority_legacy_and_new.toml | 18 ++ .../fixtures/source/complete_invalid_url.toml | 15 ++ .../json/fixtures/source/complete_valid.toml | 3 +- ...nvalid.toml => complete_valid_legacy.toml} | 1 + tests/json/test_schema_sources.py | 39 ++++- tests/puzzle/test_solver.py | 5 +- tests/repositories/test_repository_pool.py | 52 ++++-- tests/test_factory.py | 144 +++++++++++----- tests/utils/test_source.py | 46 ++++- 38 files changed, 1014 insertions(+), 199 deletions(-) create mode 100644 tests/fixtures/with_default_source_legacy/README.rst create mode 100644 tests/fixtures/with_default_source_legacy/pyproject.toml create mode 100644 tests/fixtures/with_non_default_multiple_secondary_sources_legacy/pyproject.toml create mode 100644 tests/fixtures/with_non_default_multiple_sources_legacy/pyproject.toml create mode 100644 tests/fixtures/with_non_default_secondary_source_legacy/pyproject.toml create mode 100644 tests/fixtures/with_non_default_source_explicit/pyproject.toml rename tests/fixtures/{with_non_default_source => with_non_default_source_implicit}/pyproject.toml (100%) create mode 100644 tests/fixtures/with_two_default_sources_legacy/README.rst create mode 100644 tests/fixtures/with_two_default_sources_legacy/pyproject.toml create mode 100644 tests/json/fixtures/source/complete_invalid_priority.toml create mode 100644 tests/json/fixtures/source/complete_invalid_priority_legacy_and_new.toml create mode 100644 tests/json/fixtures/source/complete_invalid_url.toml rename tests/json/fixtures/source/{complete_invalid.toml => complete_valid_legacy.toml} (90%) diff --git a/docs/cli.md b/docs/cli.md index 9bef8f7a699..ae161d1d7ac 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -784,11 +784,12 @@ You cannot use the name `pypi` as it is reserved for use by the default PyPI sou #### Options -* `--default`: Set this source as the [default]({{< relref "repositories#disabling-the-pypi-repository" >}}) (disable PyPI). -* `--secondary`: Set this source as a [secondary]({{< relref "repositories#install-dependencies-from-a-private-repository" >}}) source. +* `--default`: Set this source as the [default]({{< relref "repositories#default-package-source" >}}) (disable PyPI). Deprecated in favor of `--priority`. +* `--secondary`: Set this source as a [secondary]({{< relref "repositories#secondary-package-sources" >}}) source. Deprecated in favor of `--priority`. +* `--priority`: Set the priority of this source. Accepted values are: [`default`]({{< relref "repositories#default-package-source" >}}), and [`secondary`]({{< relref "repositories#secondary-package-sources" >}}). Refer to the dedicated sections in [Repositories]({{< relref "repositories" >}}) for more information. {{% note %}} -You cannot set a source as both `default` and `secondary`. +At most one of the options above can be provided. See [package sources]({{< relref "repositories#package-sources" >}}) for more information. {{% /note %}} ### source show diff --git a/docs/dependency-specification.md b/docs/dependency-specification.md index 85521383b87..1d0d0b51a53 100644 --- a/docs/dependency-specification.md +++ b/docs/dependency-specification.md @@ -258,7 +258,7 @@ you can use the `source` property: [[tool.poetry.source]] name = "foo" url = "https://foo.bar/simple/" -secondary = true +priority = "secondary" [tool.poetry.dependencies] my-cool-package = { version = "*", source = "foo" } diff --git a/docs/repositories.md b/docs/repositories.md index 8d52a10994f..c8d92301532 100644 --- a/docs/repositories.md +++ b/docs/repositories.md @@ -33,7 +33,7 @@ First, [configure](#project-configuration) the [package source](#package-source) project. ```bash -poetry source add --secondary foo https://pypi.example.org/simple/ +poetry source add --priority=secondary foo https://pypi.example.org/simple/ ``` Then, assuming the repository requires authentication, configure credentials for it. @@ -120,12 +120,18 @@ This will generate the following configuration snippet in your [[tool.poetry.source]] name = "foo" url = "https://foo.bar/simple/" -default = false -secondary = false +priority = "primary" ``` -Any package source not marked as `secondary` will take precedence over [PyPI](https://pypi.org). +If `priority` is undefined, the source is considered a primary source that takes precedence over PyPI and secondary sources. +Package sources are considered in the following order: +1. [default source](#default-package-source), +2. primary sources, +3. PyPI (unless disabled by another default source), +4. [secondary sources](#secondary-package-sources), + +Within each priority class, package sources are considered in order of appearance in `pyproject.toml`. {{% note %}} @@ -148,10 +154,10 @@ you must declare **all** package sources to be [secondary](#secondary-package-so By default, Poetry configures [PyPI](https://pypi.org) as the default package source for your project. You can alter this behaviour and exclusively look up packages only from the configured -package sources by adding a **single** source with `default = true`. +package sources by adding a **single** source with `priority = "default"`. ```bash -poetry source add --default foo https://foo.bar/simple/ +poetry source add --priority=default foo https://foo.bar/simple/ ``` {{% warning %}} @@ -164,30 +170,31 @@ as a package source for your project. #### Secondary Package Sources If package sources are configured as secondary, all it means is that these will be given a lower -priority when selecting compatible package distribution that also exists in your default package -source. +priority when selecting compatible package distribution that also exists in your default and primary package sources. -You can configure a package source as a secondary source with `secondary = true` in your package +You can configure a package source as a secondary source with `priority = "secondary"` in your package source configuration. ```bash -poetry source add --secondary foo https://foo.bar/simple/ +poetry source add --priority=secondary https://foo.bar/simple/ ``` There can be more than one secondary package source. -{{% note %}} +#### Package Source Constraint All package sources (including secondary sources) will be searched during the package lookup process. These network requests will occur for all sources, regardless of if the package is found at one or more sources. -In order to limit the search for a specific package to a particular package repository, you can specify the source explicitly. This is strongly suggested for all private packages to avoid dependency confusion attacks. +In order to limit the search for a specific package to a particular package repository, you can specify the source explicitly. ```bash poetry add --source internal-pypi httpx ``` +This results in the following configuration in `pyproject.toml`: + ```toml [tool.poetry.dependencies] ... @@ -195,10 +202,47 @@ httpx = { version = "^0.22", source = "internal-pypi" } [[tool.poetry.source]] name = "internal-pypi" -url = "https://foo.bar/simple/" -secondary = true +url = ... +priority = ... ``` +{{% note %}} + +A repository that is configured to be the only source for retrieving a certain package can itself have any priority. +If a repository is configured to be the source of a package, it will be the only source that is considered for that package +and the repository priority will have no effect on the resolution. + +{{% /note %}} + +{{% note %}} + +Package `source` keys are not inherited by their dependencies. +In particular, if `package-A` is configured to be found in `source = internal-pypi`, +and `package-A` depends on `package-B` that is also to be found on `internal-pypi`, +then `package-B` needs to be configured as such in `pyproject.toml`. +The easiest way to achieve this is to add `package-B` with a wildcard constraint: + +```bash +poetry add --source internal-pypi package-B@* +``` + +This will ensure that `package-B` is searched only in the `internal-pypi` package source. +The version constraints on `package-B` are derived from `package-A` (and other client packages), as usual. + +If you want to avoid additional main dependencies, +you can add `package-B` to a dedicated [dependency group]({{< relref "managing-dependencies#dependency-groups" >}}): + +```bash +poetry add --group explicit --source internal-pypi package-B@* +``` + +{{% /note %}} + +{{% note %}} + +Package source constraints are strongly suggested for all packages that are expected +to be provided only by one specific source to avoid dependency confusion attacks. + {{% /note %}} ### Supported Package Sources @@ -231,7 +275,7 @@ httpx = {version = "^0.22.0", source = "pypi"} {{% warning %}} -If any source within a project is configured with `default = true`, The implicit `pypi` source will +If any source within a project is configured with `priority = "default"`, The implicit `pypi` source will be disabled and not used for any packages. {{% /warning %}} diff --git a/src/poetry/config/source.py b/src/poetry/config/source.py index f3af0c589e2..aa0f9499b08 100644 --- a/src/poetry/config/source.py +++ b/src/poetry/config/source.py @@ -1,14 +1,43 @@ from __future__ import annotations import dataclasses +import warnings + +from poetry.repositories.repository_pool import Priority @dataclasses.dataclass(order=True, eq=True) class Source: name: str url: str - default: bool = dataclasses.field(default=False) - secondary: bool = dataclasses.field(default=False) + default: dataclasses.InitVar[bool] = False + secondary: dataclasses.InitVar[bool] = False + priority: Priority = ( + Priority.PRIMARY + ) # cheating in annotation: str will be converted to Priority in __post_init__ + + def __post_init__(self, default: bool, secondary: bool) -> None: + if isinstance(self.priority, str): + self.priority = Priority[self.priority.upper()] + if default or secondary: + warnings.warn( + ( + "Parameters 'default' and 'secondary' to" + " 'Source' are deprecated. Please provide" + " 'priority' instead." + ), + DeprecationWarning, + stacklevel=2, + ) + if default: + self.priority = Priority.DEFAULT + elif secondary: + self.priority = Priority.SECONDARY def to_dict(self) -> dict[str, str | bool]: - return dataclasses.asdict(self) + return dataclasses.asdict( + self, + dict_factory=lambda x: { + k: v if not isinstance(v, Priority) else v.name.lower() for (k, v) in x + }, + ) diff --git a/src/poetry/console/commands/source/add.py b/src/poetry/console/commands/source/add.py index 81a0ca66e91..6875d444be8 100644 --- a/src/poetry/console/commands/source/add.py +++ b/src/poetry/console/commands/source/add.py @@ -7,6 +7,7 @@ from poetry.config.source import Source from poetry.console.commands.command import Command +from poetry.repositories.repository_pool import Priority class SourceAddCommand(Command): @@ -28,35 +29,74 @@ class SourceAddCommand(Command): ( "Set this source as the default (disable PyPI). A " "default source will also be the fallback source if " - "you add other sources." + "you add other sources. (Deprecated, use --priority)" ), ), - option("secondary", "s", "Set this source as secondary."), + option( + "secondary", + "s", + ( + "Set this source as secondary. (Deprecated, use" + " --priority)" + ), + ), + option( + "priority", + "p", + ( + "Set the priority of this source. One of:" + f" {', '.join(p.name.lower() for p in Priority)}. Defaults to" + f" {Priority.PRIMARY.name.lower()}." + ), + flag=False, + ), ] def handle(self) -> int: from poetry.factory import Factory from poetry.utils.source import source_to_table - name = self.argument("name") - url = self.argument("url") - is_default = self.option("default") - is_secondary = self.option("secondary") + name: str = self.argument("name") + url: str = self.argument("url") + is_default: bool = self.option("default", False) + is_secondary: bool = self.option("secondary", False) + priority: Priority | None = self.option("priority", None) if is_default and is_secondary: self.line_error( - "Cannot configure a source as both default and" - " secondary." + "Cannot configure a source as both default and" + " secondary." ) return 1 - new_source: Source | None = Source( - name=name, url=url, default=is_default, secondary=is_secondary - ) + if is_default or is_secondary: + if priority is not None: + self.line_error( + "Priority was passed through both --priority and a" + " deprecated flag (--default or --secondary). Please only provide" + " one of these." + ) + return 1 + else: + self.line_error( + "Warning: Priority was set through a deprecated flag" + " (--default or --secondary). Consider using --priority next" + " time." + ) + + if is_default: + priority = Priority.DEFAULT + elif is_secondary: + priority = Priority.SECONDARY + elif priority is None: + priority = Priority.PRIMARY + + new_source = Source(name=name, url=url, priority=priority) existing_sources = self.poetry.get_sources() sources = AoT([]) + is_new_source = True for source in existing_sources: if source == new_source: self.line( @@ -64,7 +104,10 @@ def handle(self) -> int: " addition." ) return 0 - elif source.default and is_default: + elif ( + source.priority is Priority.DEFAULT + and new_source.priority is Priority.DEFAULT + ): self.line_error( f"Source with name {source.name} is already set to" " default. Only one default source can be configured at a" @@ -72,16 +115,17 @@ def handle(self) -> int: ) return 1 - if new_source and source.name == name: - self.line(f"Source with name {name} already exists. Updating.") + if source.name == name: source = new_source - new_source = None + is_new_source = False sources.append(source_to_table(source)) - if new_source is not None: + if is_new_source: self.line(f"Adding source with name {name}.") sources.append(source_to_table(new_source)) + else: + self.line(f"Source with name {name} already exists. Updating.") # ensure new source is valid. eg: invalid name etc. try: diff --git a/src/poetry/console/commands/source/show.py b/src/poetry/console/commands/source/show.py index 8a89a39a55c..5014708d391 100644 --- a/src/poetry/console/commands/source/show.py +++ b/src/poetry/console/commands/source/show.py @@ -33,14 +33,12 @@ def handle(self) -> int: return 0 if names and not any(s.name in names for s in sources): - self.line_error(f"No source found with name(s): {', '.join(names)}") + self.line_error( + f"No source found with name(s): {', '.join(names)}", + style="error", + ) return 1 - bool_string = { - True: "yes", - False: "no", - } - for source in sources: if names and source.name not in names: continue @@ -50,12 +48,8 @@ def handle(self) -> int: ["name", f" : {source.name}"], ["url", f" : {source.url}"], [ - "default", - f" : {bool_string.get(source.default, False)}", - ], - [ - "secondary", - f" : {bool_string.get(source.secondary, False)}", + "priority", + f" : {source.priority.name.lower()}", ], ] table.add_rows(rows) diff --git a/src/poetry/factory.py b/src/poetry/factory.py index a2e7205f229..d1a46a654aa 100644 --- a/src/poetry/factory.py +++ b/src/poetry/factory.py @@ -123,6 +123,7 @@ def create_pool( disable_cache: bool = False, ) -> RepositoryPool: from poetry.repositories import RepositoryPool + from poetry.repositories.repository_pool import Priority if io is None: io = NullIO() @@ -136,31 +137,46 @@ def create_pool( repository = cls.create_package_source( source, auth_config, disable_cache=disable_cache ) - is_default = source.get("default", False) - is_secondary = source.get("secondary", False) + priority = Priority[source.get("priority", Priority.PRIMARY.name).upper()] + if "default" in source or "secondary" in source: + warning = ( + "Found deprecated key 'default' or 'secondary' in" + " pyproject.toml configuration for source" + f" {source.get('name')}. Please provide the key 'priority'" + " instead. Accepted values are:" + f" {', '.join(repr(p.name.lower()) for p in Priority)}." + ) + io.write_error_line(f"Warning: {warning}") + if source.get("default"): + priority = Priority.DEFAULT + elif source.get("secondary"): + priority = Priority.SECONDARY + if io.is_debug(): message = f"Adding repository {repository.name} ({repository.url})" - if is_default: + if priority is Priority.DEFAULT: message += " and setting it as the default one" - elif is_secondary: - message += " and setting it as secondary" + else: + message += f" and setting it as {priority.name.lower()}" io.write_line(message) - pool.add_repository(repository, is_default, secondary=is_secondary) + pool.add_repository(repository, priority=priority) - # Put PyPI last to prefer private repositories - # unless we have no default source AND no primary sources - # (default = false, secondary = false) + # Only add PyPI if no default repository is configured if pool.has_default(): if io.is_debug(): io.write_line("Deactivating the PyPI repository") else: from poetry.repositories.pypi_repository import PyPiRepository - default = not pool.has_primary_repositories() + if pool.has_primary_repositories(): + pypi_priority = Priority.SECONDARY + else: + pypi_priority = Priority.DEFAULT + pool.add_repository( - PyPiRepository(disable_cache=disable_cache), default, not default + PyPiRepository(disable_cache=disable_cache), priority=pypi_priority ) return pool diff --git a/src/poetry/json/schemas/poetry.json b/src/poetry/json/schemas/poetry.json index 7532fd836b4..04d0ff340d7 100644 --- a/src/poetry/json/schemas/poetry.json +++ b/src/poetry/json/schemas/poetry.json @@ -26,20 +26,28 @@ "properties": { "name": { "type": "string", - "description": "The name of the repository" + "description": "The name of the repository." }, "url": { "type": "string", - "description": "The url of the repository", + "description": "The url of the repository.", "format": "uri" }, "default": { "type": "boolean", - "description": "Make this repository the default (disable PyPI)" + "description": "Make this repository the default (disable PyPI). (deprecated, see priority)" }, "secondary": { "type": "boolean", - "description": "Declare this repository as secondary, i.e. it will only be looked up last for packages." + "description": "Declare this repository as secondary, i.e. default repositories take precedence. (deprecated, see priority)" + }, + "priority": { + "enum": [ + "primary", + "default", + "secondary" + ], + "description": "Declare the priority of this repository." }, "links": { "type": "boolean", @@ -49,6 +57,22 @@ "type": "boolean", "description": "For PEP 503 simple API repositories, pre-fetch and index the available packages. (experimental)" } + }, + "not": { + "anyOf": [ + { + "required": [ + "priority", + "default" + ] + }, + { + "required": [ + "priority", + "secondary" + ] + } + ] } } } diff --git a/src/poetry/repositories/repository_pool.py b/src/poetry/repositories/repository_pool.py index 79d70479c82..a4dffbbff37 100644 --- a/src/poetry/repositories/repository_pool.py +++ b/src/poetry/repositories/repository_pool.py @@ -1,6 +1,7 @@ from __future__ import annotations import enum +import warnings from collections import OrderedDict from dataclasses import dataclass @@ -71,13 +72,24 @@ def has_repository(self, name: str) -> bool: return name.lower() in self._repositories def repository(self, name: str) -> Repository: + return self._get_prioritized_repository(name).repository + + def get_priority(self, name: str) -> Priority: + return self._get_prioritized_repository(name).priority + + def _get_prioritized_repository(self, name: str) -> PrioritizedRepository: name = name.lower() if self.has_repository(name): - return self._repositories[name].repository + return self._repositories[name] raise IndexError(f'Repository "{name}" does not exist.') def add_repository( - self, repository: Repository, default: bool = False, secondary: bool = False + self, + repository: Repository, + default: bool = False, + secondary: bool = False, + *, + priority: Priority = Priority.PRIMARY, ) -> RepositoryPool: """ Adds a repository to the pool. @@ -88,14 +100,24 @@ def add_repository( f"A repository with name {repository_name} was already added." ) - if default and self.has_default(): + if default or secondary: + warnings.warn( + ( + "Parameters 'default' and 'secondary' to" + " 'RepositoryPool.add_repository' are deprecated. Please provide" + " the keyword-argument 'priority' instead." + ), + DeprecationWarning, + stacklevel=2, + ) + if default: + priority = Priority.DEFAULT + else: + priority = Priority.SECONDARY + + if priority is Priority.DEFAULT and self.has_default(): raise ValueError("Only one repository can be the default.") - priority = Priority.PRIMARY - if default: - priority = Priority.DEFAULT - elif secondary: - priority = Priority.SECONDARY self._repositories[repository_name] = PrioritizedRepository( repository, priority ) @@ -103,7 +125,9 @@ def add_repository( def remove_repository(self, name: str) -> RepositoryPool: if not self.has_repository(name): - raise IndexError(f"Pool can not remove unknown repository '{name}'.") + raise IndexError( + f"RepositoryPool can not remove unknown repository '{name}'." + ) del self._repositories[name.lower()] return self diff --git a/tests/console/commands/source/conftest.py b/tests/console/commands/source/conftest.py index 254b3454297..f9db68a058a 100644 --- a/tests/console/commands/source/conftest.py +++ b/tests/console/commands/source/conftest.py @@ -5,6 +5,7 @@ import pytest from poetry.config.source import Source +from poetry.repositories.repository_pool import Priority if TYPE_CHECKING: @@ -24,15 +25,32 @@ def source_two() -> Source: @pytest.fixture -def source_default() -> Source: +def source_default_deprecated() -> Source: return Source(name="default", url="https://default.com", default=True) @pytest.fixture -def source_secondary() -> Source: +def source_secondary_deprecated() -> Source: return Source(name="secondary", url="https://secondary.com", secondary=True) +@pytest.fixture +def source_primary() -> Source: + return Source(name="primary", url="https://primary.com", priority=Priority.PRIMARY) + + +@pytest.fixture +def source_default() -> Source: + return Source(name="default", url="https://default.com", priority=Priority.DEFAULT) + + +@pytest.fixture +def source_secondary() -> Source: + return Source( + name="secondary", url="https://secondary.com", priority=Priority.SECONDARY + ) + + _existing_source = Source(name="existing", url="https://existing.com") @@ -41,7 +59,7 @@ def source_existing() -> Source: return _existing_source -PYPROJECT_WITH_SOURCES = f""" +PYPROJECT_WITHOUT_SOURCES = """ [tool.poetry] name = "source-command-test" version = "0.1.0" @@ -52,6 +70,10 @@ def source_existing() -> Source: python = "^3.9" [tool.poetry.dev-dependencies] +""" + + +PYPROJECT_WITH_SOURCES = f"""{PYPROJECT_WITHOUT_SOURCES} [[tool.poetry.source]] name = "{_existing_source.name}" @@ -59,6 +81,11 @@ def source_existing() -> Source: """ +@pytest.fixture +def poetry_without_source(project_factory: ProjectFactory) -> Poetry: + return project_factory(pyproject_content=PYPROJECT_WITHOUT_SOURCES) + + @pytest.fixture def poetry_with_source(project_factory: ProjectFactory) -> Poetry: return project_factory(pyproject_content=PYPROJECT_WITH_SOURCES) @@ -74,3 +101,20 @@ def add_multiple_sources( add = command_tester_factory("source add", poetry=poetry_with_source) for source in [source_one, source_two]: add.execute(f"{source.name} {source.url}") + + +@pytest.fixture +def add_all_source_types( + command_tester_factory: CommandTesterFactory, + poetry_with_source: Poetry, + source_primary: Source, + source_default: Source, + source_secondary: Source, +) -> None: + add = command_tester_factory("source add", poetry=poetry_with_source) + for source in [ + source_primary, + source_default, + source_secondary, + ]: + add.execute(f"{source.name} {source.url} --priority={source.name}") diff --git a/tests/console/commands/source/test_add.py b/tests/console/commands/source/test_add.py index 40a53192a4c..d25239c052c 100644 --- a/tests/console/commands/source/test_add.py +++ b/tests/console/commands/source/test_add.py @@ -1,16 +1,16 @@ from __future__ import annotations -import dataclasses - from typing import TYPE_CHECKING import pytest +from poetry.config.source import Source +from poetry.repositories.repository_pool import Priority + if TYPE_CHECKING: from cleo.testers.command_tester import CommandTester - from poetry.config.source import Source from poetry.poetry import Poetry from tests.types import CommandTesterFactory @@ -22,6 +22,28 @@ def tester( return command_tester_factory("source add", poetry=poetry_with_source) +def assert_source_added_legacy( + tester: CommandTester, + poetry: Poetry, + source_existing: Source, + source_added: Source, +) -> None: + assert ( + tester.io.fetch_error().strip() + == "Warning: Priority was set through a deprecated flag" + " (--default or --secondary). Consider using --priority next" + " time." + ) + assert ( + tester.io.fetch_output().strip() + == f"Adding source with name {source_added.name}." + ) + poetry.pyproject.reload() + sources = poetry.get_sources() + assert sources == [source_existing, source_added] + assert tester.status_code == 0 + + def assert_source_added( tester: CommandTester, poetry: Poetry, @@ -48,27 +70,73 @@ def test_source_add_simple( assert_source_added(tester, poetry_with_source, source_existing, source_one) -def test_source_add_default( +def test_source_add_default_legacy( tester: CommandTester, source_existing: Source, source_default: Source, poetry_with_source: Poetry, ) -> None: tester.execute(f"--default {source_default.name} {source_default.url}") + assert_source_added_legacy( + tester, poetry_with_source, source_existing, source_default + ) + + +def test_source_add_secondary_legacy( + tester: CommandTester, + source_existing: Source, + source_secondary: Source, + poetry_with_source: Poetry, +): + tester.execute(f"--secondary {source_secondary.name} {source_secondary.url}") + assert_source_added_legacy( + tester, poetry_with_source, source_existing, source_secondary + ) + + +def test_source_add_default( + tester: CommandTester, + source_existing: Source, + source_default: Source, + poetry_with_source: Poetry, +): + tester.execute(f"--priority=default {source_default.name} {source_default.url}") assert_source_added(tester, poetry_with_source, source_existing, source_default) +def test_source_add_second_default_fails( + tester: CommandTester, + source_existing: Source, + source_default: Source, + poetry_with_source: Poetry, +): + tester.execute(f"--priority=default {source_default.name} {source_default.url}") + assert_source_added(tester, poetry_with_source, source_existing, source_default) + poetry_with_source.pyproject.reload() + + tester.execute(f"--priority=default {source_default.name}1 {source_default.url}") + assert ( + tester.io.fetch_error().strip() + == f"Source with name {source_default.name} is already set to" + " default. Only one default source can be configured at a" + " time." + ) + assert tester.status_code == 1 + + def test_source_add_secondary( tester: CommandTester, source_existing: Source, source_secondary: Source, poetry_with_source: Poetry, ) -> None: - tester.execute(f"--secondary {source_secondary.name} {source_secondary.url}") + tester.execute( + f"--priority=secondary {source_secondary.name} {source_secondary.url}" + ) assert_source_added(tester, poetry_with_source, source_existing, source_secondary) -def test_source_add_error_default_and_secondary(tester: CommandTester) -> None: +def test_source_add_error_default_and_secondary_legacy(tester: CommandTester) -> None: tester.execute("--default --secondary error https://error.com") assert ( tester.io.fetch_error().strip() @@ -77,6 +145,17 @@ def test_source_add_error_default_and_secondary(tester: CommandTester) -> None: assert tester.status_code == 1 +def test_source_add_error_priority_and_deprecated_legacy(tester: CommandTester): + tester.execute("--priority secondary --secondary error https://error.com") + assert ( + tester.io.fetch_error().strip() + == "Priority was passed through both --priority and a" + " deprecated flag (--default or --secondary). Please only provide" + " one of these." + ) + assert tester.status_code == 1 + + def test_source_add_error_pypi(tester: CommandTester) -> None: tester.execute("pypi https://test.pypi.org/simple/") assert ( @@ -87,10 +166,16 @@ def test_source_add_error_pypi(tester: CommandTester) -> None: assert tester.status_code == 1 -def test_source_add_existing( +def test_source_add_existing_legacy( tester: CommandTester, source_existing: Source, poetry_with_source: Poetry ) -> None: tester.execute(f"--default {source_existing.name} {source_existing.url}") + assert ( + tester.io.fetch_error().strip() + == "Warning: Priority was set through a deprecated flag" + " (--default or --secondary). Consider using --priority next" + " time." + ) assert ( tester.io.fetch_output().strip() == f"Source with name {source_existing.name} already exists. Updating." @@ -101,4 +186,64 @@ def test_source_add_existing( assert len(sources) == 1 assert sources[0] != source_existing - assert sources[0] == dataclasses.replace(source_existing, default=True) + expected_source = Source( + name=source_existing.name, url=source_existing.url, priority=Priority.DEFAULT + ) + assert sources[0] == expected_source + + +def test_source_add_existing_no_change( + tester: CommandTester, source_existing: Source, poetry_with_source: Poetry +): + tester.execute(f"--priority=primary {source_existing.name} {source_existing.url}") + assert ( + tester.io.fetch_output().strip() + == f"Source with name {source_existing.name} already exists. Skipping addition." + ) + + poetry_with_source.pyproject.reload() + sources = poetry_with_source.get_sources() + + assert len(sources) == 1 + assert sources[0] == source_existing + + +def test_source_add_existing_updating( + tester: CommandTester, source_existing: Source, poetry_with_source: Poetry +): + tester.execute(f"--priority=default {source_existing.name} {source_existing.url}") + assert ( + tester.io.fetch_output().strip() + == f"Source with name {source_existing.name} already exists. Updating." + ) + + poetry_with_source.pyproject.reload() + sources = poetry_with_source.get_sources() + + assert len(sources) == 1 + assert sources[0] != source_existing + expected_source = Source( + name=source_existing.name, url=source_existing.url, priority=Priority.DEFAULT + ) + assert sources[0] == expected_source + + +def test_source_add_existing_fails_due_to_other_default( + tester: CommandTester, + source_existing: Source, + source_default: Source, + poetry_with_source: Poetry, +): + tester.execute(f"--priority=default {source_default.name} {source_default.url}") + tester.io.fetch_output() + + tester.execute(f"--priority=default {source_existing.name} {source_existing.url}") + + assert ( + tester.io.fetch_error().strip() + == f"Source with name {source_default.name} is already set to" + " default. Only one default source can be configured at a" + " time." + ) + assert tester.io.fetch_output().strip() == "" + assert tester.status_code == 1 diff --git a/tests/console/commands/source/test_show.py b/tests/console/commands/source/test_show.py index d25b9788365..b636d1bd30a 100644 --- a/tests/console/commands/source/test_show.py +++ b/tests/console/commands/source/test_show.py @@ -22,24 +22,38 @@ def tester( return command_tester_factory("source show", poetry=poetry_with_source) +@pytest.fixture +def tester_no_sources( + command_tester_factory: CommandTesterFactory, + poetry_without_source: Poetry, +) -> CommandTester: + return command_tester_factory("source show", poetry=poetry_without_source) + + +@pytest.fixture +def tester_all_types( + command_tester_factory: CommandTesterFactory, + poetry_with_source: Poetry, + add_all_source_types: None, +) -> CommandTester: + return command_tester_factory("source show", poetry=poetry_with_source) + + def test_source_show_simple(tester: CommandTester) -> None: tester.execute("") expected = """\ -name : existing -url : https://existing.com -default : no -secondary : no - -name : one -url : https://one.com -default : no -secondary : no - -name : two -url : https://two.com -default : no -secondary : no +name : existing +url : https://existing.com +priority : primary + +name : one +url : https://one.com +priority : primary + +name : two +url : https://two.com +priority : primary """.splitlines() assert [ line.strip() for line in tester.io.fetch_output().strip().splitlines() @@ -51,10 +65,9 @@ def test_source_show_one(tester: CommandTester, source_one: Source) -> None: tester.execute(f"{source_one.name}") expected = """\ -name : one -url : https://one.com -default : no -secondary : no +name : one +url : https://one.com +priority : primary """.splitlines() assert [ line.strip() for line in tester.io.fetch_output().strip().splitlines() @@ -68,15 +81,13 @@ def test_source_show_two( tester.execute(f"{source_one.name} {source_two.name}") expected = """\ -name : one -url : https://one.com -default : no -secondary : no - -name : two -url : https://two.com -default : no -secondary : no +name : one +url : https://one.com +priority : primary + +name : two +url : https://two.com +priority : primary """.splitlines() assert [ line.strip() for line in tester.io.fetch_output().strip().splitlines() @@ -84,6 +95,40 @@ def test_source_show_two( assert tester.status_code == 0 +@pytest.mark.parametrize( + "source_str", + ( + "source_primary", + "source_default", + "source_secondary", + ), +) +def test_source_show_given_priority( + tester_all_types: CommandTester, source_str: Source, request: pytest.FixtureRequest +) -> None: + source = request.getfixturevalue(source_str) + tester_all_types.execute(f"{source.name}") + + expected = f"""\ +name : {source.name} +url : {source.url} +priority : {source.name} +""".splitlines() + assert [ + line.strip() for line in tester_all_types.io.fetch_output().strip().splitlines() + ] == expected + assert tester_all_types.status_code == 0 + + +def test_source_show_no_sources(tester_no_sources: CommandTester) -> None: + tester_no_sources.execute("error") + assert ( + tester_no_sources.io.fetch_output().strip() + == "No sources configured for this project." + ) + assert tester_no_sources.status_code == 0 + + def test_source_show_error(tester: CommandTester) -> None: tester.execute("error") assert tester.io.fetch_error().strip() == "No source found with name(s): error" diff --git a/tests/console/commands/test_config.py b/tests/console/commands/test_config.py index 283146d2c31..975132f4bc0 100644 --- a/tests/console/commands/test_config.py +++ b/tests/console/commands/test_config.py @@ -164,7 +164,7 @@ def test_list_must_not_display_sources_from_pyproject_toml( config: Config, config_cache_dir: Path, ): - source = fixture_dir("with_non_default_source") + source = fixture_dir("with_non_default_source_implicit") pyproject_content = (source / "pyproject.toml").read_text(encoding="utf-8") poetry = project_factory("foo", pyproject_content=pyproject_content) tester = command_tester_factory("config", poetry=poetry) diff --git a/tests/fixtures/with_default_source/pyproject.toml b/tests/fixtures/with_default_source/pyproject.toml index 0d639ec25d7..7a274d6201d 100644 --- a/tests/fixtures/with_default_source/pyproject.toml +++ b/tests/fixtures/with_default_source/pyproject.toml @@ -58,4 +58,4 @@ my-script = "my_package:main" [[tool.poetry.source]] name = "foo" url = "https://foo.bar/simple/" -default = true +priority = "default" diff --git a/tests/fixtures/with_default_source_legacy/README.rst b/tests/fixtures/with_default_source_legacy/README.rst new file mode 100644 index 00000000000..f7fe15470f9 --- /dev/null +++ b/tests/fixtures/with_default_source_legacy/README.rst @@ -0,0 +1,2 @@ +My Package +========== diff --git a/tests/fixtures/with_default_source_legacy/pyproject.toml b/tests/fixtures/with_default_source_legacy/pyproject.toml new file mode 100644 index 00000000000..0d639ec25d7 --- /dev/null +++ b/tests/fixtures/with_default_source_legacy/pyproject.toml @@ -0,0 +1,61 @@ +[tool.poetry] +name = "my-package" +version = "1.2.3" +description = "Some description." +authors = [ + "Sébastien Eustace " +] +license = "MIT" + +readme = "README.rst" + +homepage = "https://python-poetry.org" +repository = "https://github.com/python-poetry/poetry" +documentation = "https://python-poetry.org/docs" + +keywords = ["packaging", "dependency", "poetry"] + +classifiers = [ + "Topic :: Software Development :: Build Tools", + "Topic :: Software Development :: Libraries :: Python Modules" +] + +# Requirements +[tool.poetry.dependencies] +python = "~2.7 || ^3.6" +cleo = "^0.6" +pendulum = { git = "https://github.com/sdispater/pendulum.git", branch = "2.0" } +requests = { version = "^2.18", optional = true, extras=[ "security" ] } +pathlib2 = { version = "^2.2", python = "~2.7" } + +orator = { version = "^0.9", optional = true } + +# File dependency +demo = { path = "../distributions/demo-0.1.0-py2.py3-none-any.whl" } + +# Dir dependency with setup.py +my-package = { path = "../project_with_setup/" } + +# Dir dependency with pyproject.toml +simple-project = { path = "../simple_project/" } + + +[tool.poetry.extras] +db = [ "orator" ] + +[tool.poetry.dev-dependencies] +pytest = "~3.4" + + +[tool.poetry.scripts] +my-script = "my_package:main" + + +[tool.poetry.plugins."blogtool.parsers"] +".rst" = "some_module::SomeClass" + + +[[tool.poetry.source]] +name = "foo" +url = "https://foo.bar/simple/" +default = true diff --git a/tests/fixtures/with_non_default_multiple_secondary_sources/pyproject.toml b/tests/fixtures/with_non_default_multiple_secondary_sources/pyproject.toml index 933bee96912..d5db76c6094 100644 --- a/tests/fixtures/with_non_default_multiple_secondary_sources/pyproject.toml +++ b/tests/fixtures/with_non_default_multiple_secondary_sources/pyproject.toml @@ -16,9 +16,9 @@ python = "~2.7 || ^3.6" [[tool.poetry.source]] name = "foo" url = "https://foo.bar/simple/" -secondary = true +priority = "secondary" [[tool.poetry.source]] name = "bar" url = "https://bar.baz/simple/" -secondary = true +priority = "secondary" diff --git a/tests/fixtures/with_non_default_multiple_secondary_sources_legacy/pyproject.toml b/tests/fixtures/with_non_default_multiple_secondary_sources_legacy/pyproject.toml new file mode 100644 index 00000000000..933bee96912 --- /dev/null +++ b/tests/fixtures/with_non_default_multiple_secondary_sources_legacy/pyproject.toml @@ -0,0 +1,24 @@ +[tool.poetry] +name = "my-package" +version = "1.2.3" +description = "Some description." +authors = [ + "Your Name " +] +license = "MIT" + +# Requirements +[tool.poetry.dependencies] +python = "~2.7 || ^3.6" + +[tool.poetry.dev-dependencies] + +[[tool.poetry.source]] +name = "foo" +url = "https://foo.bar/simple/" +secondary = true + +[[tool.poetry.source]] +name = "bar" +url = "https://bar.baz/simple/" +secondary = true diff --git a/tests/fixtures/with_non_default_multiple_sources/pyproject.toml b/tests/fixtures/with_non_default_multiple_sources/pyproject.toml index 6cacb602e8b..e40dd03e66e 100644 --- a/tests/fixtures/with_non_default_multiple_sources/pyproject.toml +++ b/tests/fixtures/with_non_default_multiple_sources/pyproject.toml @@ -16,7 +16,7 @@ python = "~2.7 || ^3.6" [[tool.poetry.source]] name = "foo" url = "https://foo.bar/simple/" -secondary = true +priority = "secondary" [[tool.poetry.source]] name = "bar" diff --git a/tests/fixtures/with_non_default_multiple_sources_legacy/pyproject.toml b/tests/fixtures/with_non_default_multiple_sources_legacy/pyproject.toml new file mode 100644 index 00000000000..6cacb602e8b --- /dev/null +++ b/tests/fixtures/with_non_default_multiple_sources_legacy/pyproject.toml @@ -0,0 +1,23 @@ +[tool.poetry] +name = "my-package" +version = "1.2.3" +description = "Some description." +authors = [ + "Your Name " +] +license = "MIT" + +# Requirements +[tool.poetry.dependencies] +python = "~2.7 || ^3.6" + +[tool.poetry.dev-dependencies] + +[[tool.poetry.source]] +name = "foo" +url = "https://foo.bar/simple/" +secondary = true + +[[tool.poetry.source]] +name = "bar" +url = "https://bar.baz/simple/" diff --git a/tests/fixtures/with_non_default_secondary_source/pyproject.toml b/tests/fixtures/with_non_default_secondary_source/pyproject.toml index 453e3f9747f..5cce04b0591 100644 --- a/tests/fixtures/with_non_default_secondary_source/pyproject.toml +++ b/tests/fixtures/with_non_default_secondary_source/pyproject.toml @@ -16,4 +16,4 @@ python = "~2.7 || ^3.6" [[tool.poetry.source]] name = "foo" url = "https://foo.bar/simple/" -secondary = true +priority = "secondary" diff --git a/tests/fixtures/with_non_default_secondary_source_legacy/pyproject.toml b/tests/fixtures/with_non_default_secondary_source_legacy/pyproject.toml new file mode 100644 index 00000000000..453e3f9747f --- /dev/null +++ b/tests/fixtures/with_non_default_secondary_source_legacy/pyproject.toml @@ -0,0 +1,19 @@ +[tool.poetry] +name = "my-package" +version = "1.2.3" +description = "Some description." +authors = [ + "Your Name " +] +license = "MIT" + +# Requirements +[tool.poetry.dependencies] +python = "~2.7 || ^3.6" + +[tool.poetry.dev-dependencies] + +[[tool.poetry.source]] +name = "foo" +url = "https://foo.bar/simple/" +secondary = true diff --git a/tests/fixtures/with_non_default_source_explicit/pyproject.toml b/tests/fixtures/with_non_default_source_explicit/pyproject.toml new file mode 100644 index 00000000000..23e7733cf34 --- /dev/null +++ b/tests/fixtures/with_non_default_source_explicit/pyproject.toml @@ -0,0 +1,19 @@ +[tool.poetry] +name = "my-package" +version = "1.2.3" +description = "Some description." +authors = [ + "Your Name " +] +license = "MIT" + +# Requirements +[tool.poetry.dependencies] +python = "~2.7 || ^3.6" + +[tool.poetry.dev-dependencies] + +[[tool.poetry.source]] +name = "foo" +url = "https://foo.bar/simple/" +priority = "primary" diff --git a/tests/fixtures/with_non_default_source/pyproject.toml b/tests/fixtures/with_non_default_source_implicit/pyproject.toml similarity index 100% rename from tests/fixtures/with_non_default_source/pyproject.toml rename to tests/fixtures/with_non_default_source_implicit/pyproject.toml diff --git a/tests/fixtures/with_two_default_sources/pyproject.toml b/tests/fixtures/with_two_default_sources/pyproject.toml index 0d0a8a5446b..fa84b0a40a7 100644 --- a/tests/fixtures/with_two_default_sources/pyproject.toml +++ b/tests/fixtures/with_two_default_sources/pyproject.toml @@ -58,9 +58,9 @@ my-script = "my_package:main" [[tool.poetry.source]] name = "foo" url = "https://foo.bar/simple/" -default = true +priority = "default" [[tool.poetry.source]] name = "bar" url = "https://bar.foo/simple/" -default = true +priority = "default" diff --git a/tests/fixtures/with_two_default_sources_legacy/README.rst b/tests/fixtures/with_two_default_sources_legacy/README.rst new file mode 100644 index 00000000000..f7fe15470f9 --- /dev/null +++ b/tests/fixtures/with_two_default_sources_legacy/README.rst @@ -0,0 +1,2 @@ +My Package +========== diff --git a/tests/fixtures/with_two_default_sources_legacy/pyproject.toml b/tests/fixtures/with_two_default_sources_legacy/pyproject.toml new file mode 100644 index 00000000000..0d0a8a5446b --- /dev/null +++ b/tests/fixtures/with_two_default_sources_legacy/pyproject.toml @@ -0,0 +1,66 @@ +[tool.poetry] +name = "my-package" +version = "1.2.3" +description = "Some description." +authors = [ + "Sébastien Eustace " +] +license = "MIT" + +readme = "README.rst" + +homepage = "https://python-poetry.org" +repository = "https://github.com/python-poetry/poetry" +documentation = "https://python-poetry.org/docs" + +keywords = ["packaging", "dependency", "poetry"] + +classifiers = [ + "Topic :: Software Development :: Build Tools", + "Topic :: Software Development :: Libraries :: Python Modules" +] + +# Requirements +[tool.poetry.dependencies] +python = "~2.7 || ^3.6" +cleo = "^0.6" +pendulum = { git = "https://github.com/sdispater/pendulum.git", branch = "2.0" } +requests = { version = "^2.18", optional = true, extras=[ "security" ] } +pathlib2 = { version = "^2.2", python = "~2.7" } + +orator = { version = "^0.9", optional = true } + +# File dependency +demo = { path = "../distributions/demo-0.1.0-py2.py3-none-any.whl" } + +# Dir dependency with setup.py +my-package = { path = "../project_with_setup/" } + +# Dir dependency with pyproject.toml +simple-project = { path = "../simple_project/" } + + +[tool.poetry.extras] +db = [ "orator" ] + +[tool.poetry.dev-dependencies] +pytest = "~3.4" + + +[tool.poetry.scripts] +my-script = "my_package:main" + + +[tool.poetry.plugins."blogtool.parsers"] +".rst" = "some_module::SomeClass" + + +[[tool.poetry.source]] +name = "foo" +url = "https://foo.bar/simple/" +default = true + +[[tool.poetry.source]] +name = "bar" +url = "https://bar.foo/simple/" +default = true diff --git a/tests/installation/test_pip_installer.py b/tests/installation/test_pip_installer.py index 29713e1daf0..fa16adc4aa7 100644 --- a/tests/installation/test_pip_installer.py +++ b/tests/installation/test_pip_installer.py @@ -13,6 +13,7 @@ from poetry.installation.pip_installer import PipInstaller from poetry.repositories.legacy_repository import LegacyRepository +from poetry.repositories.repository_pool import Priority from poetry.repositories.repository_pool import RepositoryPool from poetry.utils.authenticator import RepositoryCertificateConfig from poetry.utils.env import NullEnv @@ -137,7 +138,7 @@ def test_install_with_non_pypi_default_repository( default = LegacyRepository("default", "https://default.com") another = LegacyRepository("another", "https://another.com") - pool.add_repository(default, default=True) + pool.add_repository(default, priority=Priority.DEFAULT) pool.add_repository(another) foo = Package( @@ -177,7 +178,7 @@ def test_install_with_certs( default = LegacyRepository("default", "https://foo.bar") pool = RepositoryPool() - pool.add_repository(default, default=True) + pool.add_repository(default, priority=Priority.DEFAULT) installer = PipInstaller(env, NullIO(), pool) @@ -255,7 +256,7 @@ def test_install_with_trusted_host(config: Config, env: NullEnv) -> None: default = LegacyRepository("default", "https://foo.bar") pool = RepositoryPool() - pool.add_repository(default, default=True) + pool.add_repository(default, priority=Priority.DEFAULT) installer = PipInstaller(env, NullIO(), pool) diff --git a/tests/json/fixtures/source/complete_invalid_priority.toml b/tests/json/fixtures/source/complete_invalid_priority.toml new file mode 100644 index 00000000000..ec27c209a2a --- /dev/null +++ b/tests/json/fixtures/source/complete_invalid_priority.toml @@ -0,0 +1,17 @@ +[tool.poetry] +name = "foobar" +version = "0.1.0" +description = "" +authors = ["Your Name "] + +[tool.poetry.dependencies] +python = "^3.10" + +[[tool.poetry.source]] +name = "pypi-simple" +url = "https://pypi.org/simple/" +priority = "arbitrary" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/tests/json/fixtures/source/complete_invalid_priority_legacy_and_new.toml b/tests/json/fixtures/source/complete_invalid_priority_legacy_and_new.toml new file mode 100644 index 00000000000..4e2789b49d8 --- /dev/null +++ b/tests/json/fixtures/source/complete_invalid_priority_legacy_and_new.toml @@ -0,0 +1,18 @@ +[tool.poetry] +name = "foobar" +version = "0.1.0" +description = "" +authors = ["Your Name "] + +[tool.poetry.dependencies] +python = "^3.10" + +[[tool.poetry.source]] +name = "pypi-simple" +url = "https://pypi.org/simple/" +default = false +priority = "primary" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/tests/json/fixtures/source/complete_invalid_url.toml b/tests/json/fixtures/source/complete_invalid_url.toml new file mode 100644 index 00000000000..6c61ba5df9b --- /dev/null +++ b/tests/json/fixtures/source/complete_invalid_url.toml @@ -0,0 +1,15 @@ +[tool.poetry] +name = "foobar" +version = "0.1.0" +description = "" +authors = ["Your Name "] + +[tool.poetry.dependencies] +python = "^3.10" + +[[tool.poetry.source]] +name = "pypi-simple" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/tests/json/fixtures/source/complete_valid.toml b/tests/json/fixtures/source/complete_valid.toml index d0b4565ffa4..46186e1a7a3 100644 --- a/tests/json/fixtures/source/complete_valid.toml +++ b/tests/json/fixtures/source/complete_valid.toml @@ -10,8 +10,7 @@ python = "^3.10" [[tool.poetry.source]] name = "pypi-simple" url = "https://pypi.org/simple/" -default = false -secondary = false +priority = "primary" [build-system] requires = ["poetry-core"] diff --git a/tests/json/fixtures/source/complete_invalid.toml b/tests/json/fixtures/source/complete_valid_legacy.toml similarity index 90% rename from tests/json/fixtures/source/complete_invalid.toml rename to tests/json/fixtures/source/complete_valid_legacy.toml index bd70bc6a833..d0b4565ffa4 100644 --- a/tests/json/fixtures/source/complete_invalid.toml +++ b/tests/json/fixtures/source/complete_valid_legacy.toml @@ -9,6 +9,7 @@ python = "^3.10" [[tool.poetry.source]] name = "pypi-simple" +url = "https://pypi.org/simple/" default = false secondary = false diff --git a/tests/json/test_schema_sources.py b/tests/json/test_schema_sources.py index 22769922e1c..410b85a1183 100644 --- a/tests/json/test_schema_sources.py +++ b/tests/json/test_schema_sources.py @@ -9,16 +9,51 @@ FIXTURE_DIR = Path(__file__).parent / "fixtures" / "source" +def test_pyproject_toml_valid_legacy() -> None: + toml = TOMLFile(FIXTURE_DIR / "complete_valid_legacy.toml").read() + content = toml["tool"]["poetry"] + assert Factory.validate(content) == {"errors": [], "warnings": []} + + def test_pyproject_toml_valid() -> None: toml = TOMLFile(FIXTURE_DIR / "complete_valid.toml").read() content = toml["tool"]["poetry"] assert Factory.validate(content) == {"errors": [], "warnings": []} -def test_pyproject_toml_invalid() -> None: - toml = TOMLFile(FIXTURE_DIR / "complete_invalid.toml").read() +def test_pyproject_toml_invalid_url() -> None: + toml = TOMLFile(FIXTURE_DIR / "complete_invalid_url.toml").read() content = toml["tool"]["poetry"] assert Factory.validate(content) == { "errors": ["[source.0] 'url' is a required property"], "warnings": [], } + + +def test_pyproject_toml_invalid_priority() -> None: + toml = TOMLFile(FIXTURE_DIR / "complete_invalid_priority.toml").read() + content = toml["tool"]["poetry"] + assert Factory.validate(content) == { + "errors": [ + "[source.0.priority] 'arbitrary' is not one of ['primary', 'default'," + " 'secondary']" + ], + "warnings": [], + } + + +def test_pyproject_toml_invalid_priority_legacy_and_new() -> None: + toml = TOMLFile( + FIXTURE_DIR / "complete_invalid_priority_legacy_and_new.toml" + ).read() + content = toml["tool"]["poetry"] + assert Factory.validate(content) == { + "errors": [ + "[source.0] {'name': 'pypi-simple', 'url': " + "'https://pypi.org/simple/', 'default': False, 'priority': " + "'primary'} should not be valid under {'anyOf': [{'required': " + "['priority', 'default']}, {'required': ['priority', " + "'secondary']}]}" + ], + "warnings": [], + } diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 0018da79da5..3eaed889351 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -23,6 +23,7 @@ from poetry.puzzle.exceptions import SolverProblemError from poetry.puzzle.provider import IncompatibleConstraintsError from poetry.repositories.repository import Repository +from poetry.repositories.repository_pool import Priority from poetry.repositories.repository_pool import RepositoryPool from poetry.utils.env import MockEnv from tests.helpers import MOCK_DEFAULT_GIT_REVISION @@ -2915,7 +2916,7 @@ def test_solver_does_not_choose_from_secondary_repository_by_default( package.add_dependency(Factory.create_dependency("clikit", {"version": "^0.2.0"})) pool = RepositoryPool() - pool.add_repository(MockPyPIRepository(), secondary=True) + pool.add_repository(MockPyPIRepository(), priority=Priority.SECONDARY) pool.add_repository(MockLegacyRepository()) solver = Solver(package, pool, [], [], io) @@ -2965,7 +2966,7 @@ def test_solver_chooses_from_secondary_if_explicit( ) pool = RepositoryPool() - pool.add_repository(MockPyPIRepository(), secondary=True) + pool.add_repository(MockPyPIRepository(), priority=Priority.SECONDARY) pool.add_repository(MockLegacyRepository()) solver = Solver(package, pool, [], [], io) diff --git a/tests/repositories/test_repository_pool.py b/tests/repositories/test_repository_pool.py index ccc0e269224..7bc77331eec 100644 --- a/tests/repositories/test_repository_pool.py +++ b/tests/repositories/test_repository_pool.py @@ -8,6 +8,7 @@ from poetry.repositories import RepositoryPool from poetry.repositories.exceptions import PackageNotFound from poetry.repositories.legacy_repository import LegacyRepository +from poetry.repositories.repository_pool import Priority from tests.helpers import get_dependency from tests.helpers import get_package @@ -27,6 +28,7 @@ def test_pool_with_initial_repositories() -> None: assert len(pool.repositories) == 1 assert not pool.has_default() assert pool.has_primary_repositories() + assert pool.get_priority("repo") == Priority.PRIMARY def test_repository_no_repository() -> None: @@ -47,20 +49,36 @@ def test_adding_repositories_with_same_name_twice_raises_value_error() -> None: RepositoryPool([repo1]).add_repository(repo2) -def test_repository_from_normal_pool() -> None: +@pytest.mark.parametrize("priority", (p for p in Priority)) +def test_repository_from_single_repo_pool(priority: Priority) -> None: repo = LegacyRepository("foo", "https://foo.bar") pool = RepositoryPool() - pool.add_repository(repo) - - assert pool.repository("foo") is repo + pool.add_repository(repo, priority=priority) -def test_repository_from_secondary_pool() -> None: + assert pool.repository("foo") is repo + assert pool.get_priority("foo") == priority + + +@pytest.mark.parametrize( + ("default", "secondary", "expected_priority"), + [ + (False, True, Priority.SECONDARY), + (True, False, Priority.DEFAULT), + (True, True, Priority.DEFAULT), + ], +) +def test_repository_from_single_repo_pool_legacy( + default: bool, secondary: bool, expected_priority: Priority +) -> None: repo = LegacyRepository("foo", "https://foo.bar") pool = RepositoryPool() - pool.add_repository(repo, secondary=True) + + with pytest.warns(DeprecationWarning): + pool.add_repository(repo, default=default, secondary=secondary) assert pool.repository("foo") is repo + assert pool.get_priority("foo") == expected_priority def test_repository_with_normal_default_and_secondary_repositories() -> None: @@ -71,9 +89,9 @@ def test_repository_with_normal_default_and_secondary_repositories() -> None: pool = RepositoryPool() pool.add_repository(repo1) - pool.add_repository(secondary, secondary=True) + pool.add_repository(secondary, priority=Priority.SECONDARY) pool.add_repository(repo2) - pool.add_repository(default, default=True) + pool.add_repository(default, priority=Priority.DEFAULT) assert pool.repository("secondary") is secondary assert pool.repository("default") is default @@ -115,19 +133,19 @@ def test_remove_default_repository() -> None: pool = RepositoryPool() pool.add_repository(repo1) pool.add_repository(repo2) - pool.add_repository(default, default=True) + pool.add_repository(default, priority=Priority.DEFAULT) assert pool.has_default() pool.remove_repository("default") + assert not pool.has_repository("default") assert not pool.has_default() - pool.add_repository(new_default, default=True) + pool.add_repository(new_default, priority=Priority.DEFAULT) + assert pool.get_priority("new_default") is Priority.DEFAULT assert pool.has_default() - assert pool.repositories[0] is new_default - assert not pool.has_repository("default") def test_repository_ordering() -> None: @@ -141,21 +159,21 @@ def test_repository_ordering() -> None: secondary3 = LegacyRepository("secondary3", "https://secondary3.com") pool = RepositoryPool() - pool.add_repository(secondary1, secondary=True) + pool.add_repository(secondary1, priority=Priority.SECONDARY) pool.add_repository(primary1) - pool.add_repository(default1, default=True) + pool.add_repository(default1, priority=Priority.DEFAULT) pool.add_repository(primary2) - pool.add_repository(secondary2, secondary=True) + pool.add_repository(secondary2, priority=Priority.SECONDARY) pool.remove_repository("primary2") pool.remove_repository("secondary2") pool.add_repository(primary3) - pool.add_repository(secondary3, secondary=True) + pool.add_repository(secondary3, priority=Priority.SECONDARY) assert pool.repositories == [default1, primary1, primary3, secondary1, secondary3] with pytest.raises(ValueError): - pool.add_repository(default2, default=True) + pool.add_repository(default2, priority=Priority.DEFAULT) def test_pool_get_package_in_any_repository() -> None: diff --git a/tests/test_factory.py b/tests/test_factory.py index 5b56ddc6200..356d0b24142 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -5,6 +5,7 @@ import pytest +from cleo.io.buffered_io import BufferedIO from deepdiff import DeepDiff from packaging.utils import canonicalize_name from poetry.core.constraints.version import parse_constraint @@ -13,6 +14,7 @@ from poetry.plugins.plugin import Plugin from poetry.repositories.legacy_repository import LegacyRepository from poetry.repositories.pypi_repository import PyPiRepository +from poetry.repositories.repository_pool import Priority from poetry.toml.file import TOMLFile from tests.helpers import mock_metadata_entry_points @@ -206,40 +208,80 @@ def test_create_poetry_with_multi_constraints_dependency(): assert len(package.requires) == 2 -def test_poetry_with_default_source(with_simple_keyring: None): - poetry = Factory().create_poetry(fixtures_dir / "with_default_source") +def test_poetry_with_default_source_legacy(with_simple_keyring: None): + io = BufferedIO() + poetry = Factory().create_poetry(fixtures_dir / "with_default_source_legacy", io=io) assert len(poetry.pool.repositories) == 1 + assert "Found deprecated key" in io.fetch_error() + + +def test_poetry_with_default_source(with_simple_keyring: None): + io = BufferedIO() + poetry = Factory().create_poetry(fixtures_dir / "with_default_source", io=io) + assert len(poetry.pool.repositories) == 1 + assert io.fetch_error() == "" -def test_poetry_with_non_default_source(with_simple_keyring: None): - poetry = Factory().create_poetry(fixtures_dir / "with_non_default_source") - assert len(poetry.pool.repositories) == 2 +@pytest.mark.parametrize( + "fixture_filename", + ("with_non_default_source_implicit", "with_non_default_source_explicit"), +) +def test_poetry_with_non_default_source( + fixture_filename: str, with_simple_keyring: None +): + poetry = Factory().create_poetry(fixtures_dir / fixture_filename) assert not poetry.pool.has_default() + assert poetry.pool.has_repository("PyPI") + assert poetry.pool.get_priority("PyPI") is Priority.SECONDARY + assert isinstance(poetry.pool.repository("PyPI"), PyPiRepository) + assert poetry.pool.has_repository("foo") + assert poetry.pool.get_priority("foo") is Priority.PRIMARY + assert isinstance(poetry.pool.repository("foo"), LegacyRepository) + assert {repo.name for repo in poetry.pool.repositories} == {"PyPI", "foo"} + - assert poetry.pool.repositories[0].name == "foo" - assert isinstance(poetry.pool.repositories[0], LegacyRepository) +def test_poetry_with_non_default_secondary_source_legacy(with_simple_keyring: None): + poetry = Factory().create_poetry( + fixtures_dir / "with_non_default_secondary_source_legacy" + ) - assert poetry.pool.repositories[1].name == "PyPI" - assert isinstance(poetry.pool.repositories[1], PyPiRepository) + assert poetry.pool.has_repository("PyPI") + assert isinstance(poetry.pool.repository("PyPI"), PyPiRepository) + assert poetry.pool.get_priority("PyPI") is Priority.DEFAULT + assert poetry.pool.has_repository("foo") + assert isinstance(poetry.pool.repository("foo"), LegacyRepository) + assert [repo.name for repo in poetry.pool.repositories] == ["PyPI", "foo"] def test_poetry_with_non_default_secondary_source(with_simple_keyring: None): poetry = Factory().create_poetry(fixtures_dir / "with_non_default_secondary_source") - assert len(poetry.pool.repositories) == 2 + assert poetry.pool.has_repository("PyPI") + assert isinstance(poetry.pool.repository("PyPI"), PyPiRepository) + assert poetry.pool.get_priority("PyPI") is Priority.DEFAULT + assert poetry.pool.has_repository("foo") + assert isinstance(poetry.pool.repository("foo"), LegacyRepository) + assert [repo.name for repo in poetry.pool.repositories] == ["PyPI", "foo"] - assert poetry.pool.has_default() - repository = poetry.pool.repositories[0] - assert repository.name == "PyPI" - assert isinstance(repository, PyPiRepository) +def test_poetry_with_non_default_multiple_secondary_sources_legacy( + with_simple_keyring: None, +): + poetry = Factory().create_poetry( + fixtures_dir / "with_non_default_multiple_secondary_sources_legacy" + ) - repository = poetry.pool.repositories[1] - assert repository.name == "foo" - assert isinstance(repository, LegacyRepository) + assert poetry.pool.has_repository("PyPI") + assert isinstance(poetry.pool.repository("PyPI"), PyPiRepository) + assert poetry.pool.get_priority("PyPI") is Priority.DEFAULT + assert poetry.pool.has_repository("foo") + assert isinstance(poetry.pool.repository("foo"), LegacyRepository) + assert poetry.pool.has_repository("bar") + assert isinstance(poetry.pool.repository("bar"), LegacyRepository) + assert {repo.name for repo in poetry.pool.repositories} == {"PyPI", "foo", "bar"} def test_poetry_with_non_default_multiple_secondary_sources(with_simple_keyring: None): @@ -247,52 +289,60 @@ def test_poetry_with_non_default_multiple_secondary_sources(with_simple_keyring: fixtures_dir / "with_non_default_multiple_secondary_sources" ) - assert len(poetry.pool.repositories) == 3 + assert poetry.pool.has_repository("PyPI") + assert isinstance(poetry.pool.repository("PyPI"), PyPiRepository) + assert poetry.pool.get_priority("PyPI") is Priority.DEFAULT + assert poetry.pool.has_repository("foo") + assert isinstance(poetry.pool.repository("foo"), LegacyRepository) + assert poetry.pool.has_repository("bar") + assert isinstance(poetry.pool.repository("bar"), LegacyRepository) + assert {repo.name for repo in poetry.pool.repositories} == {"PyPI", "foo", "bar"} - assert poetry.pool.has_default() - repository = poetry.pool.repositories[0] - assert repository.name == "PyPI" - assert isinstance(repository, PyPiRepository) - - repository = poetry.pool.repositories[1] - assert repository.name == "foo" - assert isinstance(repository, LegacyRepository) +def test_poetry_with_non_default_multiple_sources_legacy(with_simple_keyring: None): + poetry = Factory().create_poetry( + fixtures_dir / "with_non_default_multiple_sources_legacy" + ) - repository = poetry.pool.repositories[2] - assert repository.name == "bar" - assert isinstance(repository, LegacyRepository) + assert not poetry.pool.has_default() + assert poetry.pool.has_repository("bar") + assert isinstance(poetry.pool.repository("bar"), LegacyRepository) + assert poetry.pool.has_repository("PyPI") + assert poetry.pool.get_priority("PyPI") is Priority.SECONDARY + assert isinstance(poetry.pool.repository("PyPI"), PyPiRepository) + assert poetry.pool.has_repository("foo") + assert isinstance(poetry.pool.repository("foo"), LegacyRepository) + assert {repo.name for repo in poetry.pool.repositories} == {"bar", "PyPI", "foo"} def test_poetry_with_non_default_multiple_sources(with_simple_keyring: None): poetry = Factory().create_poetry(fixtures_dir / "with_non_default_multiple_sources") - assert len(poetry.pool.repositories) == 3 - assert not poetry.pool.has_default() - - repository = poetry.pool.repositories[0] - assert repository.name == "bar" - assert isinstance(repository, LegacyRepository) - - repository = poetry.pool.repositories[1] - assert repository.name == "foo" - assert isinstance(repository, LegacyRepository) - - repository = poetry.pool.repositories[2] - assert repository.name == "PyPI" - assert isinstance(repository, PyPiRepository) + assert poetry.pool.has_repository("PyPI") + assert isinstance(poetry.pool.repository("PyPI"), PyPiRepository) + assert poetry.pool.get_priority("PyPI") is Priority.SECONDARY + assert poetry.pool.has_repository("bar") + assert isinstance(poetry.pool.repository("bar"), LegacyRepository) + assert poetry.pool.has_repository("foo") + assert isinstance(poetry.pool.repository("foo"), LegacyRepository) + assert {repo.name for repo in poetry.pool.repositories} == {"PyPI", "bar", "foo"} def test_poetry_with_no_default_source(): poetry = Factory().create_poetry(fixtures_dir / "sample_project") - assert len(poetry.pool.repositories) == 1 + assert poetry.pool.has_repository("PyPI") + assert poetry.pool.get_priority("PyPI") is Priority.DEFAULT + assert isinstance(poetry.pool.repository("PyPI"), PyPiRepository) + assert {repo.name for repo in poetry.pool.repositories} == {"PyPI"} - assert poetry.pool.has_default() - assert poetry.pool.repositories[0].name == "PyPI" - assert isinstance(poetry.pool.repositories[0], PyPiRepository) +def test_poetry_with_two_default_sources_legacy(with_simple_keyring: None): + with pytest.raises(ValueError) as e: + Factory().create_poetry(fixtures_dir / "with_two_default_sources_legacy") + + assert str(e.value) == "Only one repository can be the default." def test_poetry_with_two_default_sources(with_simple_keyring: None): diff --git a/tests/utils/test_source.py b/tests/utils/test_source.py index a970b7262ca..a5093324d04 100644 --- a/tests/utils/test_source.py +++ b/tests/utils/test_source.py @@ -7,6 +7,7 @@ from tomlkit.items import Trivia from poetry.config.source import Source +from poetry.repositories.repository_pool import Priority from poetry.utils.source import source_to_table @@ -16,25 +17,58 @@ ( Source("foo", "https://example.com"), { - "default": False, "name": "foo", - "secondary": False, + "priority": "primary", "url": "https://example.com", }, ), ( - Source("bar", "https://example.com/bar", True, True), + Source("bar", "https://example.com/bar", priority=Priority.SECONDARY), { - "default": True, "name": "bar", - "secondary": True, + "priority": "secondary", "url": "https://example.com/bar", }, ), ], ) -def test_source_to_table(source: Source, table_body: dict[str, str | bool]): +def test_source_to_table(source: Source, table_body: dict[str, str | bool]) -> None: table = Table(Container(), Trivia(), False) table._value = table_body assert source_to_table(source) == table + + +def test_source_default_is_primary() -> None: + source = Source("foo", "https://example.com") + assert source.priority == Priority.PRIMARY + + +@pytest.mark.parametrize( + ("default", "secondary", "expected_priority"), + [ + (False, True, Priority.SECONDARY), + (True, False, Priority.DEFAULT), + (True, True, Priority.DEFAULT), + ], +) +def test_source_legacy_handling( + default: bool, secondary: bool, expected_priority: Priority +) -> None: + with pytest.warns(DeprecationWarning): + source = Source( + "foo", "https://example.com", default=default, secondary=secondary + ) + assert source.priority == expected_priority + + +@pytest.mark.parametrize( + ("priority", "expected_priority"), + [ + ("secondary", Priority.SECONDARY), + ("SECONDARY", Priority.SECONDARY), + ], +) +def test_source_priority_as_string(priority: str, expected_priority: Priority) -> None: + source = Source("foo", "https://example.com", priority=priority) + assert source.priority == Priority.SECONDARY From 4c37735165b6ddfd1d10fea508c9f083ad7addb4 Mon Sep 17 00:00:00 2001 From: Bart Kamphorst Date: Mon, 24 Oct 2022 22:13:49 +0200 Subject: [PATCH 03/10] sources: introduce priority "explicit" (#7658) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Explicit sources are considered only for packages that explicitly indicate their source. Co-authored-by: Randy Döring <30527984+radoering@users.noreply.github.com> --- docs/cli.md | 2 +- docs/repositories.md | 19 +++++- src/poetry/json/schemas/poetry.json | 3 +- src/poetry/repositories/repository_pool.py | 28 ++++++-- tests/console/commands/source/conftest.py | 9 +++ tests/console/commands/source/test_add.py | 10 +++ tests/console/commands/source/test_show.py | 1 + .../with_explicit_source/pyproject.toml | 19 ++++++ .../json/fixtures/source/complete_valid.toml | 2 +- tests/json/test_schema_sources.py | 2 +- tests/puzzle/test_solver.py | 65 +++++++++++++++++++ tests/repositories/test_repository_pool.py | 19 +++++- tests/test_factory.py | 13 ++++ tests/utils/test_source.py | 4 +- 14 files changed, 184 insertions(+), 12 deletions(-) create mode 100644 tests/fixtures/with_explicit_source/pyproject.toml diff --git a/docs/cli.md b/docs/cli.md index ae161d1d7ac..e57b1548e2e 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -786,7 +786,7 @@ You cannot use the name `pypi` as it is reserved for use by the default PyPI sou * `--default`: Set this source as the [default]({{< relref "repositories#default-package-source" >}}) (disable PyPI). Deprecated in favor of `--priority`. * `--secondary`: Set this source as a [secondary]({{< relref "repositories#secondary-package-sources" >}}) source. Deprecated in favor of `--priority`. -* `--priority`: Set the priority of this source. Accepted values are: [`default`]({{< relref "repositories#default-package-source" >}}), and [`secondary`]({{< relref "repositories#secondary-package-sources" >}}). Refer to the dedicated sections in [Repositories]({{< relref "repositories" >}}) for more information. +* `--priority`: Set the priority of this source. Accepted values are: [`default`]({{< relref "repositories#default-package-source" >}}), [`secondary`]({{< relref "repositories#secondary-package-sources" >}}), and [`explicit`]({{< relref "repositories#explicit-package-sources" >}}). Refer to the dedicated sections in [Repositories]({{< relref "repositories" >}}) for more information. {{% note %}} At most one of the options above can be provided. See [package sources]({{< relref "repositories#package-sources" >}}) for more information. diff --git a/docs/repositories.md b/docs/repositories.md index c8d92301532..38f993892f4 100644 --- a/docs/repositories.md +++ b/docs/repositories.md @@ -123,7 +123,7 @@ url = "https://foo.bar/simple/" priority = "primary" ``` -If `priority` is undefined, the source is considered a primary source that takes precedence over PyPI and secondary sources. +If `priority` is undefined, the source is considered a primary source that takes precedence over PyPI, secondary and explicit sources. Package sources are considered in the following order: 1. [default source](#default-package-source), @@ -131,6 +131,8 @@ Package sources are considered in the following order: 3. PyPI (unless disabled by another default source), 4. [secondary sources](#secondary-package-sources), +[Explicit sources](#explicit-package-sources) are considered only for packages that explicitly [indicate their source](#package-source-constraint). + Within each priority class, package sources are considered in order of appearance in `pyproject.toml`. {{% note %}} @@ -181,6 +183,20 @@ poetry source add --priority=secondary https://foo.bar/simple/ There can be more than one secondary package source. +#### Explicit Package Sources + +*Introduced in 1.5.0* + +If package sources are configured as explicit, these sources are only searched when a package configuration [explicitly indicates](#package-source-constraint) that it should be found on this package source. + +You can configure a package source as an explicit source with `priority = "explicit` in your package source configuration. + +```bash +poetry source add --priority=explicit foo https://foo.bar/simple/ +``` + +There can be more than one explicit package source. + #### Package Source Constraint All package sources (including secondary sources) will be searched during the package lookup @@ -209,6 +225,7 @@ priority = ... {{% note %}} A repository that is configured to be the only source for retrieving a certain package can itself have any priority. +In particular, it does not need to have priority `"explicit"`. If a repository is configured to be the source of a package, it will be the only source that is considered for that package and the repository priority will have no effect on the resolution. diff --git a/src/poetry/json/schemas/poetry.json b/src/poetry/json/schemas/poetry.json index 04d0ff340d7..c9191b03d23 100644 --- a/src/poetry/json/schemas/poetry.json +++ b/src/poetry/json/schemas/poetry.json @@ -45,7 +45,8 @@ "enum": [ "primary", "default", - "secondary" + "secondary", + "explicit" ], "description": "Declare the priority of this repository." }, diff --git a/src/poetry/repositories/repository_pool.py b/src/poetry/repositories/repository_pool.py index a4dffbbff37..304f7e9ed33 100644 --- a/src/poetry/repositories/repository_pool.py +++ b/src/poetry/repositories/repository_pool.py @@ -26,6 +26,7 @@ class Priority(IntEnum): DEFAULT = enum.auto() PRIMARY = enum.auto() SECONDARY = enum.auto() + EXPLICIT = enum.auto() @dataclass(frozen=True) @@ -51,11 +52,30 @@ def __init__( @property def repositories(self) -> list[Repository]: - unsorted_repositories = self._repositories.values() - sorted_repositories = sorted( - unsorted_repositories, key=lambda prio_repo: prio_repo.priority + """ + Returns the repositories in the pool, + in the order they will be searched for packages. + + ATTENTION: For backwards compatibility and practical reasons, + repositories with priority EXPLICIT are NOT included, + because they will not be searched. + """ + sorted_repositories = self._sorted_repositories + return [ + prio_repo.repository + for prio_repo in sorted_repositories + if prio_repo.priority is not Priority.EXPLICIT + ] + + @property + def all_repositories(self) -> list[Repository]: + return [prio_repo.repository for prio_repo in self._sorted_repositories] + + @property + def _sorted_repositories(self) -> list[PrioritizedRepository]: + return sorted( + self._repositories.values(), key=lambda prio_repo: prio_repo.priority ) - return [prio_repo.repository for prio_repo in sorted_repositories] def has_default(self) -> bool: return self._contains_priority(Priority.DEFAULT) diff --git a/tests/console/commands/source/conftest.py b/tests/console/commands/source/conftest.py index f9db68a058a..5ec79df2c9e 100644 --- a/tests/console/commands/source/conftest.py +++ b/tests/console/commands/source/conftest.py @@ -51,6 +51,13 @@ def source_secondary() -> Source: ) +@pytest.fixture +def source_explicit() -> Source: + return Source( + name="explicit", url="https://explicit.com", priority=Priority.EXPLICIT + ) + + _existing_source = Source(name="existing", url="https://existing.com") @@ -110,11 +117,13 @@ def add_all_source_types( source_primary: Source, source_default: Source, source_secondary: Source, + source_explicit: Source, ) -> None: add = command_tester_factory("source add", poetry=poetry_with_source) for source in [ source_primary, source_default, source_secondary, + source_explicit, ]: add.execute(f"{source.name} {source.url} --priority={source.name}") diff --git a/tests/console/commands/source/test_add.py b/tests/console/commands/source/test_add.py index d25239c052c..468e9dec271 100644 --- a/tests/console/commands/source/test_add.py +++ b/tests/console/commands/source/test_add.py @@ -136,6 +136,16 @@ def test_source_add_secondary( assert_source_added(tester, poetry_with_source, source_existing, source_secondary) +def test_source_add_explicit( + tester: CommandTester, + source_existing: Source, + source_explicit: Source, + poetry_with_source: Poetry, +) -> None: + tester.execute(f"--priority=explicit {source_explicit.name} {source_explicit.url}") + assert_source_added(tester, poetry_with_source, source_existing, source_explicit) + + def test_source_add_error_default_and_secondary_legacy(tester: CommandTester) -> None: tester.execute("--default --secondary error https://error.com") assert ( diff --git a/tests/console/commands/source/test_show.py b/tests/console/commands/source/test_show.py index b636d1bd30a..d3c94682650 100644 --- a/tests/console/commands/source/test_show.py +++ b/tests/console/commands/source/test_show.py @@ -101,6 +101,7 @@ def test_source_show_two( "source_primary", "source_default", "source_secondary", + "source_explicit", ), ) def test_source_show_given_priority( diff --git a/tests/fixtures/with_explicit_source/pyproject.toml b/tests/fixtures/with_explicit_source/pyproject.toml new file mode 100644 index 00000000000..e19ad99bad4 --- /dev/null +++ b/tests/fixtures/with_explicit_source/pyproject.toml @@ -0,0 +1,19 @@ +[tool.poetry] +name = "my-package" +version = "1.2.3" +description = "Some description." +authors = [ + "Your Name " +] +license = "MIT" + +# Requirements +[tool.poetry.dependencies] +python = "~2.7 || ^3.6" + +[tool.poetry.dev-dependencies] + +[[tool.poetry.source]] +name = "explicit" +url = "https://explicit.com/simple/" +priority = "explicit" diff --git a/tests/json/fixtures/source/complete_valid.toml b/tests/json/fixtures/source/complete_valid.toml index 46186e1a7a3..fedab7bed39 100644 --- a/tests/json/fixtures/source/complete_valid.toml +++ b/tests/json/fixtures/source/complete_valid.toml @@ -10,7 +10,7 @@ python = "^3.10" [[tool.poetry.source]] name = "pypi-simple" url = "https://pypi.org/simple/" -priority = "primary" +priority = "explicit" [build-system] requires = ["poetry-core"] diff --git a/tests/json/test_schema_sources.py b/tests/json/test_schema_sources.py index 410b85a1183..78e446bc6b3 100644 --- a/tests/json/test_schema_sources.py +++ b/tests/json/test_schema_sources.py @@ -36,7 +36,7 @@ def test_pyproject_toml_invalid_priority() -> None: assert Factory.validate(content) == { "errors": [ "[source.0.priority] 'arbitrary' is not one of ['primary', 'default'," - " 'secondary']" + " 'secondary', 'explicit']" ], "warnings": [], } diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 3eaed889351..080fa975c63 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -2998,6 +2998,71 @@ def test_solver_chooses_from_secondary_if_explicit( assert ops[2].package.source_url is None +def test_solver_does_not_choose_from_explicit_repository( + package: ProjectPackage, io: NullIO +) -> None: + package.python_versions = "^3.7" + package.add_dependency(Factory.create_dependency("attrs", {"version": "^17.4.0"})) + + pool = RepositoryPool() + pool.add_repository(MockPyPIRepository(), priority=Priority.EXPLICIT) + pool.add_repository(MockLegacyRepository()) + + solver = Solver(package, pool, [], [], io) + + with pytest.raises(SolverProblemError): + solver.solve() + + +def test_solver_chooses_direct_dependency_from_explicit_if_explicit( + package: ProjectPackage, + io: NullIO, +) -> None: + package.python_versions = "^3.7" + package.add_dependency( + Factory.create_dependency("pylev", {"version": "^1.2.0", "source": "PyPI"}) + ) + + pool = RepositoryPool() + pool.add_repository(MockPyPIRepository(), priority=Priority.EXPLICIT) + pool.add_repository(MockLegacyRepository()) + + solver = Solver(package, pool, [], [], io) + + transaction = solver.solve() + + ops = check_solver_result( + transaction, + [ + {"job": "install", "package": get_package("pylev", "1.3.0")}, + ], + ) + + assert ops[0].package.source_type is None + assert ops[0].package.source_url is None + + +def test_solver_ignores_explicit_repo_for_transient_dependencies( + package: ProjectPackage, + io: NullIO, +) -> None: + # clikit depends on pylev, which is in MockPyPIRepository (explicit) but not in + # MockLegacyRepository + package.python_versions = "^3.7" + package.add_dependency( + Factory.create_dependency("clikit", {"version": "^0.2.0", "source": "PyPI"}) + ) + + pool = RepositoryPool() + pool.add_repository(MockPyPIRepository(), priority=Priority.EXPLICIT) + pool.add_repository(MockLegacyRepository()) + + solver = Solver(package, pool, [], [], io) + + with pytest.raises(SolverProblemError): + solver.solve() + + def test_solver_discards_packages_with_empty_markers( package: ProjectPackage, repo: Repository, diff --git a/tests/repositories/test_repository_pool.py b/tests/repositories/test_repository_pool.py index 7bc77331eec..21c5941d73d 100644 --- a/tests/repositories/test_repository_pool.py +++ b/tests/repositories/test_repository_pool.py @@ -81,26 +81,43 @@ def test_repository_from_single_repo_pool_legacy( assert pool.get_priority("foo") == expected_priority -def test_repository_with_normal_default_and_secondary_repositories() -> None: +def test_repository_with_normal_default_secondary_and_explicit_repositories(): secondary = LegacyRepository("secondary", "https://secondary.com") default = LegacyRepository("default", "https://default.com") repo1 = LegacyRepository("foo", "https://foo.bar") repo2 = LegacyRepository("bar", "https://bar.baz") + explicit = LegacyRepository("explicit", "https://bar.baz") pool = RepositoryPool() pool.add_repository(repo1) pool.add_repository(secondary, priority=Priority.SECONDARY) pool.add_repository(repo2) + pool.add_repository(explicit, priority=Priority.EXPLICIT) pool.add_repository(default, priority=Priority.DEFAULT) assert pool.repository("secondary") is secondary assert pool.repository("default") is default assert pool.repository("foo") is repo1 assert pool.repository("bar") is repo2 + assert pool.repository("explicit") is explicit assert pool.has_default() assert pool.has_primary_repositories() +def test_repository_explicit_repositories_do_not_show() -> None: + explicit = LegacyRepository("explicit", "https://explicit.com") + default = LegacyRepository("default", "https://default.com") + + pool = RepositoryPool() + pool.add_repository(explicit, priority=Priority.EXPLICIT) + pool.add_repository(default, priority=Priority.DEFAULT) + + assert pool.repository("explicit") is explicit + assert pool.repository("default") is default + assert pool.repositories == [default] + assert pool.all_repositories == [default, explicit] + + def test_remove_non_existing_repository_raises_indexerror() -> None: pool = RepositoryPool() diff --git a/tests/test_factory.py b/tests/test_factory.py index 356d0b24142..2ba1104e768 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -338,6 +338,19 @@ def test_poetry_with_no_default_source(): assert {repo.name for repo in poetry.pool.repositories} == {"PyPI"} +def test_poetry_with_explicit_source(with_simple_keyring: None) -> None: + poetry = Factory().create_poetry(fixtures_dir / "with_explicit_source") + + assert len(poetry.pool.repositories) == 1 + assert len(poetry.pool.all_repositories) == 2 + assert poetry.pool.has_repository("PyPI") + assert poetry.pool.get_priority("PyPI") is Priority.DEFAULT + assert isinstance(poetry.pool.repository("PyPI"), PyPiRepository) + assert poetry.pool.has_repository("explicit") + assert isinstance(poetry.pool.repository("explicit"), LegacyRepository) + assert [repo.name for repo in poetry.pool.repositories] == ["PyPI"] + + def test_poetry_with_two_default_sources_legacy(with_simple_keyring: None): with pytest.raises(ValueError) as e: Factory().create_poetry(fixtures_dir / "with_two_default_sources_legacy") diff --git a/tests/utils/test_source.py b/tests/utils/test_source.py index a5093324d04..4908a6e0978 100644 --- a/tests/utils/test_source.py +++ b/tests/utils/test_source.py @@ -23,10 +23,10 @@ }, ), ( - Source("bar", "https://example.com/bar", priority=Priority.SECONDARY), + Source("bar", "https://example.com/bar", priority=Priority.EXPLICIT), { "name": "bar", - "priority": "secondary", + "priority": "explicit", "url": "https://example.com/bar", }, ), From 3855bc589427bdbe945f6f59226e2b54c76cbb0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20D=C3=B6ring?= <30527984+radoering@users.noreply.github.com> Date: Sun, 16 Apr 2023 07:30:43 +0200 Subject: [PATCH 04/10] tests: consistently use fixture_dir --- tests/conftest.py | 10 +- tests/console/commands/self/test_update.py | 10 +- tests/console/commands/test_build.py | 15 +- tests/console/commands/test_check.py | 24 +- tests/console/conftest.py | 14 +- tests/helpers.py | 12 +- tests/inspection/test_info.py | 34 +- tests/installation/test_pip_installer.py | 10 +- .../masonry/builders/test_editable_builder.py | 39 +- .../version_solver/test_unsolvable.py | 10 +- tests/packages/test_locker.py | 35 +- tests/plugins/test_plugin_manager.py | 19 +- tests/puzzle/test_provider.py | 152 +++----- tests/puzzle/test_solver.py | 353 ++++++++---------- tests/test_factory.py | 128 ++++--- tests/utils/test_env.py | 20 +- tests/utils/test_helpers.py | 10 +- 17 files changed, 419 insertions(+), 476 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 98e6030dd18..65abc45743a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -282,6 +282,11 @@ def http() -> Iterator[type[httpretty.httpretty]]: yield httpretty +@pytest.fixture +def project_root() -> Path: + return Path(__file__).parent.parent + + @pytest.fixture(scope="session") def fixture_base() -> Path: return Path(__file__).parent / "fixtures" @@ -417,11 +422,6 @@ def _factory( return _factory -@pytest.fixture -def project_root() -> Path: - return Path(__file__).parent.parent - - @pytest.fixture(autouse=True) def set_simple_log_formatter() -> None: """ diff --git a/tests/console/commands/self/test_update.py b/tests/console/commands/self/test_update.py index 6720406de8b..d2c6ef0c933 100644 --- a/tests/console/commands/self/test_update.py +++ b/tests/console/commands/self/test_update.py @@ -1,6 +1,5 @@ from __future__ import annotations -from pathlib import Path from typing import TYPE_CHECKING import pytest @@ -20,12 +19,11 @@ from tests.helpers import TestRepository from tests.types import CommandTesterFactory + from tests.types import FixtureDirGetter -FIXTURES = Path(__file__).parent.joinpath("fixtures") - -@pytest.fixture() -def setup(mocker: MockerFixture, fixture_dir: Path): +@pytest.fixture +def setup(mocker: MockerFixture, fixture_dir: FixtureDirGetter) -> None: mocker.patch.object( Executor, "_download", @@ -46,7 +44,7 @@ def test_self_update_can_update_from_recommended_installation( tester: CommandTester, repo: TestRepository, installed: TestRepository, -): +) -> None: new_version = Version.parse(__version__).next_minor().text old_poetry = Package("poetry", __version__) diff --git a/tests/console/commands/test_build.py b/tests/console/commands/test_build.py index 2d7ed87007a..ed9f3a3c5fa 100644 --- a/tests/console/commands/test_build.py +++ b/tests/console/commands/test_build.py @@ -3,23 +3,26 @@ import shutil import tarfile -from pathlib import Path from typing import TYPE_CHECKING from poetry.factory import Factory if TYPE_CHECKING: + from pathlib import Path + from poetry.utils.env import VirtualEnv from tests.types import CommandTesterFactory + from tests.types import FixtureDirGetter def test_build_with_multiple_readme_files( - tmp_path: Path, tmp_venv: VirtualEnv, command_tester_factory: CommandTesterFactory -): - source_dir = ( - Path(__file__).parent.parent.parent / "fixtures" / "with_multiple_readme_files" - ) + fixture_dir: FixtureDirGetter, + tmp_path: Path, + tmp_venv: VirtualEnv, + command_tester_factory: CommandTesterFactory, +) -> None: + source_dir = fixture_dir("with_multiple_readme_files") target_dir = tmp_path / "project" shutil.copytree(str(source_dir), str(target_dir)) diff --git a/tests/console/commands/test_check.py b/tests/console/commands/test_check.py index f23276c5c6d..771f95813dc 100644 --- a/tests/console/commands/test_check.py +++ b/tests/console/commands/test_check.py @@ -1,6 +1,5 @@ from __future__ import annotations -from pathlib import Path from typing import TYPE_CHECKING import pytest @@ -11,6 +10,7 @@ from pytest_mock import MockerFixture from tests.types import CommandTesterFactory + from tests.types import FixtureDirGetter @pytest.fixture() @@ -18,7 +18,7 @@ def tester(command_tester_factory: CommandTesterFactory) -> CommandTester: return command_tester_factory("check") -def test_check_valid(tester: CommandTester): +def test_check_valid(tester: CommandTester) -> None: tester.execute() expected = """\ @@ -28,17 +28,14 @@ def test_check_valid(tester: CommandTester): assert tester.io.fetch_output() == expected -def test_check_invalid(mocker: MockerFixture, tester: CommandTester): +def test_check_invalid( + mocker: MockerFixture, tester: CommandTester, fixture_dir: FixtureDirGetter +) -> None: from poetry.toml import TOMLFile mocker.patch( "poetry.poetry.Poetry.file", - return_value=TOMLFile( - Path(__file__).parent.parent.parent - / "fixtures" - / "invalid_pyproject" - / "pyproject.toml" - ), + return_value=TOMLFile(fixture_dir("invalid_pyproject") / "pyproject.toml"), new_callable=mocker.PropertyMock, ) @@ -61,13 +58,12 @@ def test_check_invalid(mocker: MockerFixture, tester: CommandTester): assert tester.io.fetch_error() == expected -def test_check_private(mocker: MockerFixture, tester: CommandTester): +def test_check_private( + mocker: MockerFixture, tester: CommandTester, fixture_dir: FixtureDirGetter +) -> None: mocker.patch( "poetry.factory.Factory.locate", - return_value=Path(__file__).parent.parent.parent - / "fixtures" - / "private_pyproject" - / "pyproject.toml", + return_value=fixture_dir("private_pyproject") / "pyproject.toml", ) tester.execute() diff --git a/tests/console/conftest.py b/tests/console/conftest.py index 7813273e1a8..8d04adf2c42 100644 --- a/tests/console/conftest.py +++ b/tests/console/conftest.py @@ -2,7 +2,6 @@ import os -from pathlib import Path from typing import TYPE_CHECKING import pytest @@ -22,6 +21,7 @@ if TYPE_CHECKING: from collections.abc import Iterator + from pathlib import Path from pytest_mock import MockerFixture @@ -31,6 +31,7 @@ from poetry.utils.env import Env from tests.conftest import Config from tests.types import CommandTesterFactory + from tests.types import FixtureDirGetter from tests.types import ProjectFactory @@ -96,11 +97,12 @@ def project_directory() -> str: @pytest.fixture -def poetry(project_directory: str, project_factory: ProjectFactory) -> Poetry: - return project_factory( - name="simple", - source=Path(__file__).parent.parent / "fixtures" / project_directory, - ) +def poetry( + project_directory: str, + project_factory: ProjectFactory, + fixture_dir: FixtureDirGetter, +) -> Poetry: + return project_factory(name="simple", source=fixture_dir(project_directory)) @pytest.fixture diff --git a/tests/helpers.py b/tests/helpers.py index 3ab77da917b..b21de6a551d 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -68,13 +68,6 @@ def get_dependency( return Factory.create_dependency(name, constraint or "*", groups=groups) -def fixture(path: str | None = None) -> Path: - if path: - return FIXTURE_PATH / path - else: - return FIXTURE_PATH - - def copy_or_symlink(source: Path, dest: Path) -> None: if dest.is_symlink() or dest.is_file(): dest.unlink() # missing_ok is only available in Python >= 3.8 @@ -113,7 +106,7 @@ def mock_clone( parsed = ParsedUrl.parse(url) path = re.sub(r"(.git)?$", "", parsed.pathname.lstrip("/")) - folder = Path(__file__).parent / "fixtures" / "git" / parsed.resource / path + folder = FIXTURE_PATH / "git" / parsed.resource / path if not source_root: source_root = Path(Config.create().get("cache-dir")) / "src" @@ -128,8 +121,7 @@ def mock_clone( def mock_download(url: str, dest: Path) -> None: parts = urllib.parse.urlparse(url) - fixtures = Path(__file__).parent / "fixtures" - fixture = fixtures / parts.path.lstrip("/") + fixture = FIXTURE_PATH / parts.path.lstrip("/") copy_or_symlink(fixture, dest) diff --git a/tests/inspection/test_info.py b/tests/inspection/test_info.py index 4b529e3bedc..4b1e0335944 100644 --- a/tests/inspection/test_info.py +++ b/tests/inspection/test_info.py @@ -1,6 +1,5 @@ from __future__ import annotations -from pathlib import Path from subprocess import CalledProcessError from typing import TYPE_CHECKING @@ -13,10 +12,11 @@ if TYPE_CHECKING: + from pathlib import Path + from pytest_mock import MockerFixture -FIXTURE_DIR_BASE = Path(__file__).parent.parent / "fixtures" -FIXTURE_DIR_INSPECTIONS = FIXTURE_DIR_BASE / "inspection" + from tests.types import FixtureDirGetter @pytest.fixture(autouse=True) @@ -25,13 +25,13 @@ def pep517_metadata_mock() -> None: @pytest.fixture -def demo_sdist() -> Path: - return FIXTURE_DIR_BASE / "distributions" / "demo-0.1.0.tar.gz" +def demo_sdist(fixture_dir: FixtureDirGetter) -> Path: + return fixture_dir("distributions") / "demo-0.1.0.tar.gz" @pytest.fixture -def demo_wheel() -> Path: - return FIXTURE_DIR_BASE / "distributions" / "demo-0.1.0-py2.py3-none-any.whl" +def demo_wheel(fixture_dir: FixtureDirGetter) -> Path: + return fixture_dir("distributions") / "demo-0.1.0-py2.py3-none-any.whl" @pytest.fixture @@ -128,15 +128,15 @@ def test_info_from_bdist(demo_wheel: Path) -> None: demo_check_info(info) -def test_info_from_poetry_directory() -> None: +def test_info_from_poetry_directory(fixture_dir: FixtureDirGetter) -> None: info = PackageInfo.from_directory( - FIXTURE_DIR_INSPECTIONS / "demo", disable_build=True + fixture_dir("inspection") / "demo", disable_build=True ) demo_check_info(info) def test_info_from_poetry_directory_fallback_on_poetry_create_error( - mocker: MockerFixture, + mocker: MockerFixture, fixture_dir: FixtureDirGetter ) -> None: mock_create_poetry = mocker.patch( "poetry.inspection.info.Factory.create_poetry", side_effect=RuntimeError @@ -146,16 +146,16 @@ def test_info_from_poetry_directory_fallback_on_poetry_create_error( "poetry.inspection.info.get_pep517_metadata" ) - PackageInfo.from_directory(FIXTURE_DIR_INSPECTIONS / "demo_poetry_package") + PackageInfo.from_directory(fixture_dir("inspection") / "demo_poetry_package") assert mock_create_poetry.call_count == 1 assert mock_get_poetry_package.call_count == 1 assert mock_get_pep517_metadata.call_count == 1 -def test_info_from_requires_txt() -> None: +def test_info_from_requires_txt(fixture_dir: FixtureDirGetter) -> None: info = PackageInfo.from_metadata( - FIXTURE_DIR_INSPECTIONS / "demo_only_requires_txt.egg-info" + fixture_dir("inspection") / "demo_only_requires_txt.egg-info" ) assert info is not None demo_check_info(info) @@ -171,9 +171,9 @@ def test_info_from_setup_cfg(demo_setup_cfg: Path) -> None: demo_check_info(info, requires_dist={"package"}) -def test_info_no_setup_pkg_info_no_deps() -> None: +def test_info_no_setup_pkg_info_no_deps(fixture_dir: FixtureDirGetter) -> None: info = PackageInfo.from_directory( - FIXTURE_DIR_INSPECTIONS / "demo_no_setup_pkg_info_no_deps", + fixture_dir("inspection") / "demo_no_setup_pkg_info_no_deps", disable_build=True, ) assert info.name == "demo" @@ -250,8 +250,8 @@ def test_info_setup_missing_mandatory_should_trigger_pep517( assert spy.call_count == 1 -def test_info_prefer_poetry_config_over_egg_info() -> None: +def test_info_prefer_poetry_config_over_egg_info(fixture_dir: FixtureDirGetter) -> None: info = PackageInfo.from_directory( - FIXTURE_DIR_INSPECTIONS / "demo_with_obsolete_egg_info" + fixture_dir("inspection") / "demo_with_obsolete_egg_info" ) demo_check_info(info) diff --git a/tests/installation/test_pip_installer.py b/tests/installation/test_pip_installer.py index fa16adc4aa7..2a07da92f73 100644 --- a/tests/installation/test_pip_installer.py +++ b/tests/installation/test_pip_installer.py @@ -24,6 +24,7 @@ from poetry.utils.env import VirtualEnv from tests.conftest import Config + from tests.types import FixtureDirGetter @pytest.fixture @@ -278,7 +279,10 @@ def test_install_with_trusted_host(config: Config, env: NullEnv) -> None: def test_install_directory_fallback_on_poetry_create_error( - mocker: MockerFixture, tmp_venv: VirtualEnv, pool: RepositoryPool + mocker: MockerFixture, + tmp_venv: VirtualEnv, + pool: RepositoryPool, + fixture_dir: FixtureDirGetter, ) -> None: mock_create_poetry = mocker.patch( "poetry.factory.Factory.create_poetry", side_effect=RuntimeError @@ -293,9 +297,7 @@ def test_install_directory_fallback_on_poetry_create_error( "demo", "1.0.0", source_type="directory", - source_url=str( - Path(__file__).parent.parent / "fixtures/inspection/demo_poetry_package" - ), + source_url=str(fixture_dir("inspection") / "demo_poetry_package"), ) installer = PipInstaller(tmp_venv, NullIO(), pool) diff --git a/tests/masonry/builders/test_editable_builder.py b/tests/masonry/builders/test_editable_builder.py index eab115b3181..28b6628a972 100644 --- a/tests/masonry/builders/test_editable_builder.py +++ b/tests/masonry/builders/test_editable_builder.py @@ -28,51 +28,40 @@ from pytest_mock import MockerFixture from poetry.poetry import Poetry + from tests.types import FixtureDirGetter @pytest.fixture() -def simple_poetry() -> Poetry: - poetry = Factory().create_poetry( - Path(__file__).parent.parent.parent / "fixtures" / "simple_project" - ) +def simple_poetry(fixture_dir: FixtureDirGetter) -> Poetry: + poetry = Factory().create_poetry(fixture_dir("simple_project")) return poetry @pytest.fixture() -def project_with_include() -> Poetry: - poetry = Factory().create_poetry( - Path(__file__).parent.parent.parent / "fixtures" / "with-include" - ) +def project_with_include(fixture_dir: FixtureDirGetter) -> Poetry: + poetry = Factory().create_poetry(fixture_dir("with-include")) return poetry @pytest.fixture() -def extended_poetry() -> Poetry: - poetry = Factory().create_poetry( - Path(__file__).parent.parent.parent / "fixtures" / "extended_project" - ) +def extended_poetry(fixture_dir: FixtureDirGetter) -> Poetry: + poetry = Factory().create_poetry(fixture_dir("extended_project")) return poetry @pytest.fixture() -def extended_without_setup_poetry() -> Poetry: - poetry = Factory().create_poetry( - Path(__file__).parent.parent.parent - / "fixtures" - / "extended_project_without_setup" - ) +def extended_without_setup_poetry(fixture_dir: FixtureDirGetter) -> Poetry: + poetry = Factory().create_poetry(fixture_dir("extended_project_without_setup")) return poetry @pytest.fixture -def with_multiple_readme_files() -> Poetry: - poetry = Factory().create_poetry( - Path(__file__).parent.parent.parent / "fixtures" / "with_multiple_readme_files" - ) +def with_multiple_readme_files(fixture_dir: FixtureDirGetter) -> Poetry: + poetry = Factory().create_poetry(fixture_dir("with_multiple_readme_files")) return poetry @@ -235,9 +224,11 @@ def test_builder_falls_back_on_setup_and_pip_for_packages_with_build_scripts( assert [] == env.executed -def test_builder_setup_generation_runs_with_pip_editable(tmp_path: Path) -> None: +def test_builder_setup_generation_runs_with_pip_editable( + fixture_dir: FixtureDirGetter, tmp_path: Path +) -> None: # create an isolated copy of the project - fixture = Path(__file__).parent.parent.parent / "fixtures" / "extended_project" + fixture = fixture_dir("extended_project") extended_project = tmp_path / "extended_project" shutil.copytree(fixture, extended_project) diff --git a/tests/mixology/version_solver/test_unsolvable.py b/tests/mixology/version_solver/test_unsolvable.py index 3886949a672..92cc5a98515 100644 --- a/tests/mixology/version_solver/test_unsolvable.py +++ b/tests/mixology/version_solver/test_unsolvable.py @@ -1,6 +1,5 @@ from __future__ import annotations -from pathlib import Path from typing import TYPE_CHECKING from poetry.factory import Factory @@ -13,6 +12,7 @@ from poetry.repositories import Repository from tests.mixology.version_solver.conftest import Provider + from tests.types import FixtureDirGetter def test_no_version_matching_constraint( @@ -94,11 +94,13 @@ def test_disjoint_root_constraints( def test_disjoint_root_constraints_path_dependencies( - root: ProjectPackage, provider: Provider, repo: Repository + root: ProjectPackage, + provider: Provider, + repo: Repository, + fixture_dir: FixtureDirGetter, ) -> None: provider.set_package_python_versions("^3.7") - fixtures = Path(__file__).parent.parent.parent / "fixtures" - project_dir = fixtures.joinpath("with_conditional_path_deps") + project_dir = fixture_dir("with_conditional_path_deps") dependency1 = Factory.create_dependency("demo", {"path": project_dir / "demo_one"}) root.add_dependency(dependency1) dependency2 = Factory.create_dependency("demo", {"path": project_dir / "demo_two"}) diff --git a/tests/packages/test_locker.py b/tests/packages/test_locker.py index 80b689b1570..bdd80e4ec86 100644 --- a/tests/packages/test_locker.py +++ b/tests/packages/test_locker.py @@ -737,14 +737,13 @@ def test_locker_should_raise_an_error_if_lock_version_is_newer_and_not_allowed( def test_root_extras_dependencies_are_ordered( - locker: Locker, root: ProjectPackage + locker: Locker, root: ProjectPackage, fixture_base: Path ) -> None: - root_dir = Path(__file__).parent.parent.joinpath("fixtures") - Factory.create_dependency("B", "1.0.0", root_dir=root_dir) - Factory.create_dependency("C", "1.0.0", root_dir=root_dir) - package_first = Factory.create_dependency("first", "1.0.0", root_dir=root_dir) - package_second = Factory.create_dependency("second", "1.0.0", root_dir=root_dir) - package_third = Factory.create_dependency("third", "1.0.0", root_dir=root_dir) + Factory.create_dependency("B", "1.0.0", root_dir=fixture_base) + Factory.create_dependency("C", "1.0.0", root_dir=fixture_base) + package_first = Factory.create_dependency("first", "1.0.0", root_dir=fixture_base) + package_second = Factory.create_dependency("second", "1.0.0", root_dir=fixture_base) + package_third = Factory.create_dependency("third", "1.0.0", root_dir=fixture_base) root.extras = { "C": [package_third, package_second, package_first], @@ -834,25 +833,24 @@ def test_locker_should_neither_emit_warnings_nor_raise_error_for_lower_compatibl def test_locker_dumps_dependency_information_correctly( - locker: Locker, root: ProjectPackage + locker: Locker, root: ProjectPackage, fixture_base: Path ) -> None: - root_dir = Path(__file__).parent.parent.joinpath("fixtures") package_a = get_package("A", "1.0.0") package_a.add_dependency( Factory.create_dependency( - "B", {"path": "project_with_extras", "develop": True}, root_dir=root_dir + "B", {"path": "project_with_extras", "develop": True}, root_dir=fixture_base ) ) package_a.add_dependency( Factory.create_dependency( "C", {"path": "directory/project_with_transitive_directory_dependencies"}, - root_dir=root_dir, + root_dir=fixture_base, ) ) package_a.add_dependency( Factory.create_dependency( - "D", {"path": "distributions/demo-0.1.0.tar.gz"}, root_dir=root_dir + "D", {"path": "distributions/demo-0.1.0.tar.gz"}, root_dir=fixture_base ) ) package_a.add_dependency( @@ -969,15 +967,14 @@ def test_locker_dumps_subdir(locker: Locker, root: ProjectPackage) -> None: def test_locker_dumps_dependency_extras_in_correct_order( - locker: Locker, root: ProjectPackage + locker: Locker, root: ProjectPackage, fixture_base: Path ) -> None: - root_dir = Path(__file__).parent.parent.joinpath("fixtures") package_a = get_package("A", "1.0.0") - Factory.create_dependency("B", "1.0.0", root_dir=root_dir) - Factory.create_dependency("C", "1.0.0", root_dir=root_dir) - package_first = Factory.create_dependency("first", "1.0.0", root_dir=root_dir) - package_second = Factory.create_dependency("second", "1.0.0", root_dir=root_dir) - package_third = Factory.create_dependency("third", "1.0.0", root_dir=root_dir) + Factory.create_dependency("B", "1.0.0", root_dir=fixture_base) + Factory.create_dependency("C", "1.0.0", root_dir=fixture_base) + package_first = Factory.create_dependency("first", "1.0.0", root_dir=fixture_base) + package_second = Factory.create_dependency("second", "1.0.0", root_dir=fixture_base) + package_third = Factory.create_dependency("third", "1.0.0", root_dir=fixture_base) package_a.extras = { "C": [package_third, package_second, package_first], diff --git a/tests/plugins/test_plugin_manager.py b/tests/plugins/test_plugin_manager.py index a3fcc4249d1..fb2ce6c94f7 100644 --- a/tests/plugins/test_plugin_manager.py +++ b/tests/plugins/test_plugin_manager.py @@ -1,6 +1,5 @@ from __future__ import annotations -from pathlib import Path from typing import TYPE_CHECKING import pytest @@ -21,8 +20,7 @@ from pytest_mock import MockerFixture from tests.conftest import Config - -CWD = Path(__file__).parent.parent / "fixtures" / "simple_project" + from tests.types import FixtureDirGetter class ManagerFactory(Protocol): @@ -46,13 +44,14 @@ def activate(self, poetry: Poetry, io: BufferedIO) -> None: poetry.package.version = "9.9.9" -@pytest.fixture() -def poetry(tmp_path: Path, config: Config) -> Poetry: +@pytest.fixture +def poetry(fixture_dir: FixtureDirGetter, config: Config) -> Poetry: + project_path = fixture_dir("simple_project") poetry = Poetry( - CWD / "pyproject.toml", + project_path / "pyproject.toml", {}, ProjectPackage("simple-project", "1.2.3"), - Locker(CWD / "poetry.lock", {}), + Locker(project_path / "poetry.lock", {}), config, ) @@ -82,7 +81,7 @@ def test_load_plugins_and_activate( poetry: Poetry, io: BufferedIO, with_my_plugin: None, -): +) -> None: manager = manager_factory() manager.load_plugins() manager.activate(poetry, io) @@ -106,7 +105,7 @@ def test_load_plugins_with_invalid_plugin( poetry: Poetry, io: BufferedIO, with_invalid_plugin: None, -): +) -> None: manager = manager_factory() with pytest.raises(ValueError): @@ -118,7 +117,7 @@ def test_load_plugins_with_plugins_disabled( poetry: Poetry, io: BufferedIO, with_my_plugin: None, -): +) -> None: no_plugin_manager.load_plugins() assert poetry.package.version.text == "1.2.3" diff --git a/tests/puzzle/test_provider.py b/tests/puzzle/test_provider.py index 9554a1fdae9..560ae872f05 100644 --- a/tests/puzzle/test_provider.py +++ b/tests/puzzle/test_provider.py @@ -1,6 +1,5 @@ from __future__ import annotations -from pathlib import Path from subprocess import CalledProcessError from typing import TYPE_CHECKING @@ -29,6 +28,8 @@ if TYPE_CHECKING: from pytest_mock import MockerFixture + from tests.types import FixtureDirGetter + SOME_URL = "https://example.com/path.tar.gz" @@ -157,7 +158,7 @@ def test_search_for_direct_origin_and_extras( @pytest.mark.parametrize("value", [True, False]) -def test_search_for_vcs_retains_develop_flag(provider: Provider, value: bool): +def test_search_for_vcs_retains_develop_flag(provider: Provider, value: bool) -> None: dependency = VCSDependency( "demo", "git", "https://github.com/demo/demo.git", develop=value ) @@ -165,7 +166,7 @@ def test_search_for_vcs_retains_develop_flag(provider: Provider, value: bool): assert package.develop == value -def test_search_for_vcs_setup_egg_info(provider: Provider): +def test_search_for_vcs_setup_egg_info(provider: Provider) -> None: dependency = VCSDependency("demo", "git", "https://github.com/demo/demo.git") package = provider.search_for_direct_origin_dependency(dependency) @@ -183,7 +184,7 @@ def test_search_for_vcs_setup_egg_info(provider: Provider): } -def test_search_for_vcs_setup_egg_info_with_extras(provider: Provider): +def test_search_for_vcs_setup_egg_info_with_extras(provider: Provider) -> None: dependency = VCSDependency( "demo", "git", "https://github.com/demo/demo.git", extras=["foo"] ) @@ -203,7 +204,7 @@ def test_search_for_vcs_setup_egg_info_with_extras(provider: Provider): } -def test_search_for_vcs_read_setup(provider: Provider, mocker: MockerFixture): +def test_search_for_vcs_read_setup(provider: Provider, mocker: MockerFixture) -> None: mocker.patch("poetry.utils.env.EnvManager.get", return_value=MockEnv()) dependency = VCSDependency("demo", "git", "https://github.com/demo/demo.git") @@ -225,7 +226,7 @@ def test_search_for_vcs_read_setup(provider: Provider, mocker: MockerFixture): def test_search_for_vcs_read_setup_with_extras( provider: Provider, mocker: MockerFixture -): +) -> None: mocker.patch("poetry.utils.env.EnvManager.get", return_value=MockEnv()) dependency = VCSDependency( @@ -245,7 +246,7 @@ def test_search_for_vcs_read_setup_with_extras( def test_search_for_vcs_read_setup_raises_error_if_no_version( provider: Provider, mocker: MockerFixture -): +) -> None: mocker.patch( "poetry.inspection.info.get_pep517_metadata", return_value=PackageInfo(name="demo", version=None), @@ -258,15 +259,12 @@ def test_search_for_vcs_read_setup_raises_error_if_no_version( @pytest.mark.parametrize("directory", ["demo", "non-canonical-name"]) -def test_search_for_directory_setup_egg_info(provider: Provider, directory: str): +def test_search_for_directory_setup_egg_info( + provider: Provider, directory: str, fixture_dir: FixtureDirGetter +) -> None: dependency = DirectoryDependency( "demo", - Path(__file__).parent.parent - / "fixtures" - / "git" - / "github.com" - / "demo" - / directory, + fixture_dir("git") / "github.com" / "demo" / directory, ) package = provider.search_for_direct_origin_dependency(dependency) @@ -284,15 +282,12 @@ def test_search_for_directory_setup_egg_info(provider: Provider, directory: str) } -def test_search_for_directory_setup_egg_info_with_extras(provider: Provider): +def test_search_for_directory_setup_egg_info_with_extras( + provider: Provider, fixture_dir: FixtureDirGetter +) -> None: dependency = DirectoryDependency( "demo", - Path(__file__).parent.parent - / "fixtures" - / "git" - / "github.com" - / "demo" - / "demo", + fixture_dir("git") / "github.com" / "demo" / "demo", extras=["foo"], ) @@ -312,21 +307,13 @@ def test_search_for_directory_setup_egg_info_with_extras(provider: Provider): @pytest.mark.parametrize("directory", ["demo", "non-canonical-name"]) -def test_search_for_directory_setup_with_base(provider: Provider, directory: str): +def test_search_for_directory_setup_with_base( + provider: Provider, directory: str, fixture_dir: FixtureDirGetter +) -> None: dependency = DirectoryDependency( "demo", - Path(__file__).parent.parent - / "fixtures" - / "git" - / "github.com" - / "demo" - / directory, - base=Path(__file__).parent.parent - / "fixtures" - / "git" - / "github.com" - / "demo" - / directory, + fixture_dir("git") / "github.com" / "demo" / directory, + base=fixture_dir("git") / "github.com" / "demo" / directory, ) package = provider.search_for_direct_origin_dependency(dependency) @@ -342,29 +329,17 @@ def test_search_for_directory_setup_with_base(provider: Provider, directory: str "foo": [get_dependency("cleo")], "bar": [get_dependency("tomlkit")], } - assert package.root_dir == ( - Path(__file__).parent.parent - / "fixtures" - / "git" - / "github.com" - / "demo" - / directory - ) + assert package.root_dir == (fixture_dir("git") / "github.com" / "demo" / directory) def test_search_for_directory_setup_read_setup( - provider: Provider, mocker: MockerFixture -): + provider: Provider, mocker: MockerFixture, fixture_dir: FixtureDirGetter +) -> None: mocker.patch("poetry.utils.env.EnvManager.get", return_value=MockEnv()) dependency = DirectoryDependency( "demo", - Path(__file__).parent.parent - / "fixtures" - / "git" - / "github.com" - / "demo" - / "demo", + fixture_dir("git") / "github.com" / "demo" / "demo", ) package = provider.search_for_direct_origin_dependency(dependency) @@ -383,18 +358,13 @@ def test_search_for_directory_setup_read_setup( def test_search_for_directory_setup_read_setup_with_extras( - provider: Provider, mocker: MockerFixture -): + provider: Provider, mocker: MockerFixture, fixture_dir: FixtureDirGetter +) -> None: mocker.patch("poetry.utils.env.EnvManager.get", return_value=MockEnv()) dependency = DirectoryDependency( "demo", - Path(__file__).parent.parent - / "fixtures" - / "git" - / "github.com" - / "demo" - / "demo", + fixture_dir("git") / "github.com" / "demo" / "demo", extras=["foo"], ) @@ -413,15 +383,12 @@ def test_search_for_directory_setup_read_setup_with_extras( } -def test_search_for_directory_setup_read_setup_with_no_dependencies(provider: Provider): +def test_search_for_directory_setup_read_setup_with_no_dependencies( + provider: Provider, fixture_dir: FixtureDirGetter +) -> None: dependency = DirectoryDependency( "demo", - Path(__file__).parent.parent - / "fixtures" - / "git" - / "github.com" - / "demo" - / "no-dependencies", + fixture_dir("git") / "github.com" / "demo" / "no-dependencies", ) package = provider.search_for_direct_origin_dependency(dependency) @@ -432,10 +399,12 @@ def test_search_for_directory_setup_read_setup_with_no_dependencies(provider: Pr assert package.extras == {} -def test_search_for_directory_poetry(provider: Provider): +def test_search_for_directory_poetry( + provider: Provider, fixture_dir: FixtureDirGetter +) -> None: dependency = DirectoryDependency( "project-with-extras", - Path(__file__).parent.parent / "fixtures" / "project_with_extras", + fixture_dir("project_with_extras"), ) package = provider.search_for_direct_origin_dependency(dependency) @@ -460,10 +429,12 @@ def test_search_for_directory_poetry(provider: Provider): } -def test_search_for_directory_poetry_with_extras(provider: Provider): +def test_search_for_directory_poetry_with_extras( + provider: Provider, fixture_dir: FixtureDirGetter +) -> None: dependency = DirectoryDependency( "project-with-extras", - Path(__file__).parent.parent / "fixtures" / "project_with_extras", + fixture_dir("project_with_extras"), extras=["extras_a"], ) @@ -489,13 +460,12 @@ def test_search_for_directory_poetry_with_extras(provider: Provider): } -def test_search_for_file_sdist(provider: Provider): +def test_search_for_file_sdist( + provider: Provider, fixture_dir: FixtureDirGetter +) -> None: dependency = FileDependency( "demo", - Path(__file__).parent.parent - / "fixtures" - / "distributions" - / "demo-0.1.0.tar.gz", + fixture_dir("distributions") / "demo-0.1.0.tar.gz", ) package = provider.search_for_direct_origin_dependency(dependency) @@ -520,13 +490,12 @@ def test_search_for_file_sdist(provider: Provider): } -def test_search_for_file_sdist_with_extras(provider: Provider): +def test_search_for_file_sdist_with_extras( + provider: Provider, fixture_dir: FixtureDirGetter +) -> None: dependency = FileDependency( "demo", - Path(__file__).parent.parent - / "fixtures" - / "distributions" - / "demo-0.1.0.tar.gz", + fixture_dir("distributions") / "demo-0.1.0.tar.gz", extras=["foo"], ) @@ -552,13 +521,12 @@ def test_search_for_file_sdist_with_extras(provider: Provider): } -def test_search_for_file_wheel(provider: Provider): +def test_search_for_file_wheel( + provider: Provider, fixture_dir: FixtureDirGetter +) -> None: dependency = FileDependency( "demo", - Path(__file__).parent.parent - / "fixtures" - / "distributions" - / "demo-0.1.0-py2.py3-none-any.whl", + fixture_dir("distributions") / "demo-0.1.0-py2.py3-none-any.whl", ) package = provider.search_for_direct_origin_dependency(dependency) @@ -583,13 +551,12 @@ def test_search_for_file_wheel(provider: Provider): } -def test_search_for_file_wheel_with_extras(provider: Provider): +def test_search_for_file_wheel_with_extras( + provider: Provider, fixture_dir: FixtureDirGetter +) -> None: dependency = FileDependency( "demo", - Path(__file__).parent.parent - / "fixtures" - / "distributions" - / "demo-0.1.0-py2.py3-none-any.whl", + fixture_dir("distributions") / "demo-0.1.0-py2.py3-none-any.whl", extras=["foo"], ) @@ -639,10 +606,9 @@ def test_complete_package_does_not_merge_different_source_names( def test_complete_package_preserves_source_type( - provider: Provider, root: ProjectPackage + provider: Provider, root: ProjectPackage, fixture_dir: FixtureDirGetter ) -> None: - fixtures = Path(__file__).parent.parent / "fixtures" - project_dir = fixtures.joinpath("with_conditional_path_deps") + project_dir = fixture_dir("with_conditional_path_deps") for folder in ["demo_one", "demo_two"]: path = (project_dir / folder).as_posix() root.add_dependency(Factory.create_dependency("demo", {"path": path})) @@ -737,7 +703,7 @@ def test_complete_package_with_extras_preserves_source_name( @pytest.mark.parametrize("with_extra", [False, True]) def test_complete_package_fetches_optional_vcs_dependency_only_if_requested( provider: Provider, repository: Repository, mocker: MockerFixture, with_extra: bool -): +) -> None: optional_vcs_dependency = Factory.create_dependency( "demo", {"git": "https://github.com/demo/demo.git", "optional": True} ) diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 080fa975c63..10e67c9ab6e 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -2,7 +2,6 @@ import re -from pathlib import Path from typing import TYPE_CHECKING from typing import Any @@ -43,6 +42,7 @@ from poetry.installation.operations.operation import Operation from poetry.puzzle.provider import Provider from poetry.puzzle.transaction import Transaction + from tests.types import FixtureDirGetter DEFAULT_SOURCE_REF = ( VCSDependency("poetry", "git", "git@github.com:python-poetry/poetry.git").branch @@ -115,7 +115,7 @@ def check_solver_result( def test_solver_install_single( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -128,7 +128,7 @@ def test_solver_install_single( def test_solver_remove_if_no_longer_locked( package: ProjectPackage, pool: RepositoryPool, io: NullIO -): +) -> None: package_a = get_package("A", "1.0") solver = Solver(package, pool, [package_a], [package_a], io) @@ -139,7 +139,7 @@ def test_solver_remove_if_no_longer_locked( def test_remove_non_installed( package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO -): +) -> None: package_a = get_package("A", "1.0") repo.add_package(package_a) @@ -151,7 +151,7 @@ def test_remove_non_installed( def test_install_non_existing_package_fail( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("B", "1")) package_a = get_package("A", "1.0") @@ -163,7 +163,7 @@ def test_install_non_existing_package_fail( def test_install_unpublished_package_does_not_fail( package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO -): +) -> None: package.add_dependency(Factory.create_dependency("B", "1")) package_a = get_package("A", "1.0") @@ -188,7 +188,9 @@ def test_install_unpublished_package_does_not_fail( ) -def test_solver_with_deps(solver: Solver, repo: Repository, package: ProjectPackage): +def test_solver_with_deps( + solver: Solver, repo: Repository, package: ProjectPackage +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -214,7 +216,7 @@ def test_solver_with_deps(solver: Solver, repo: Repository, package: ProjectPack def test_install_honours_not_equal( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -244,7 +246,7 @@ def test_install_honours_not_equal( def test_install_with_deps_in_order( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) package.add_dependency(Factory.create_dependency("C", "*")) @@ -275,7 +277,7 @@ def test_install_with_deps_in_order( def test_install_installed( package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -291,7 +293,7 @@ def test_install_installed( def test_update_installed( package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -309,7 +311,7 @@ def test_update_installed( def test_update_with_use_latest( package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) @@ -337,7 +339,9 @@ def test_update_with_use_latest( ) -def test_solver_sets_groups(solver: Solver, repo: Repository, package: ProjectPackage): +def test_solver_sets_groups( + solver: Solver, repo: Repository, package: ProjectPackage +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*", groups=["dev"])) @@ -368,7 +372,7 @@ def test_solver_sets_groups(solver: Solver, repo: Repository, package: ProjectPa def test_solver_respects_root_package_python_versions( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: set_package_python_versions(solver.provider, "~3.4") package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) @@ -401,7 +405,7 @@ def test_solver_respects_root_package_python_versions( def test_solver_fails_if_mismatch_root_python_versions( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: set_package_python_versions(solver.provider, "^3.4") package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) @@ -423,7 +427,7 @@ def test_solver_fails_if_mismatch_root_python_versions( def test_solver_ignores_python_restricted_if_mismatch_root_package_python_versions( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: set_package_python_versions(solver.provider, "~3.8") package.add_dependency( Factory.create_dependency("A", {"version": "1.0", "python": "<3.8"}) @@ -447,7 +451,7 @@ def test_solver_ignores_python_restricted_if_mismatch_root_package_python_versio def test_solver_solves_optional_and_compatible_packages( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: set_package_python_versions(solver.provider, "~3.4") package.extras["foo"] = [get_dependency("B")] package.add_dependency( @@ -482,7 +486,7 @@ def test_solver_solves_optional_and_compatible_packages( def test_solver_does_not_return_extras_if_not_requested( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) @@ -509,7 +513,7 @@ def test_solver_does_not_return_extras_if_not_requested( def test_solver_returns_extras_if_requested( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency( Factory.create_dependency("B", {"version": "*", "extras": ["foo"]}) @@ -549,7 +553,7 @@ def test_solver_returns_extras_only_requested( repo: Repository, package: ProjectPackage, enabled_extra: bool | None, -): +) -> None: extras = [enabled_extra] if enabled_extra is not None else [] package.add_dependency(Factory.create_dependency("A", "*")) @@ -611,7 +615,7 @@ def test_solver_returns_extras_when_multiple_extras_use_same_dependency( repo: Repository, package: ProjectPackage, enabled_extra: bool | None, -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -660,7 +664,7 @@ def test_solver_returns_extras_only_requested_nested( repo: Repository, package: ProjectPackage, enabled_extra: bool | None, -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -715,7 +719,7 @@ def test_solver_returns_extras_only_requested_nested( def test_solver_finds_extras_next_to_non_extras( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: # Root depends on A[foo] package.add_dependency( Factory.create_dependency("A", {"version": "*", "extras": ["foo"]}) @@ -762,7 +766,7 @@ def test_solver_finds_extras_next_to_non_extras( def test_solver_merge_extras_into_base_package_multiple_repos_fixes_5727( solver: Solver, repo: Repository, pool: RepositoryPool, package: ProjectPackage -): +) -> None: package.add_dependency( Factory.create_dependency("A", {"version": "*", "source": "legacy"}) ) @@ -794,7 +798,7 @@ def test_solver_merge_extras_into_base_package_multiple_repos_fixes_5727( def test_solver_returns_extras_if_excluded_by_markers_without_extras( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency( Factory.create_dependency("A", {"version": "*", "extras": ["foo"]}) ) @@ -833,7 +837,7 @@ def test_solver_returns_extras_if_excluded_by_markers_without_extras( def test_solver_returns_prereleases_if_requested( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) package.add_dependency( @@ -864,7 +868,7 @@ def test_solver_returns_prereleases_if_requested( def test_solver_does_not_return_prereleases_if_not_requested( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) package.add_dependency(Factory.create_dependency("C", "*")) @@ -893,7 +897,7 @@ def test_solver_does_not_return_prereleases_if_not_requested( def test_solver_sub_dependencies_with_requirements( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) @@ -931,7 +935,7 @@ def test_solver_sub_dependencies_with_requirements( def test_solver_sub_dependencies_with_requirements_complex( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency( Factory.create_dependency("A", {"version": "^1.0", "python": "<5.0"}) ) @@ -990,7 +994,7 @@ def test_solver_sub_dependencies_with_requirements_complex( def test_solver_sub_dependencies_with_not_supported_python_version( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: set_package_python_versions(solver.provider, "^3.5") package.add_dependency(Factory.create_dependency("A", "*")) @@ -1012,7 +1016,7 @@ def test_solver_sub_dependencies_with_not_supported_python_version( def test_solver_sub_dependencies_with_not_supported_python_version_transitive( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: set_package_python_versions(solver.provider, "^3.4") package.add_dependency( @@ -1056,7 +1060,7 @@ def test_solver_sub_dependencies_with_not_supported_python_version_transitive( def test_solver_with_dependency_in_both_main_and_dev_dependencies( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: set_package_python_versions(solver.provider, "^3.5") package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency( @@ -1109,7 +1113,7 @@ def test_solver_with_dependency_in_both_main_and_dev_dependencies( def test_solver_with_dependency_in_both_main_and_dev_dependencies_with_one_more_dependent( # noqa: E501 solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("E", "*")) package.add_dependency( @@ -1169,7 +1173,7 @@ def test_solver_with_dependency_in_both_main_and_dev_dependencies_with_one_more_ def test_solver_with_dependency_and_prerelease_sub_dependencies( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -1196,7 +1200,7 @@ def test_solver_with_dependency_and_prerelease_sub_dependencies( def test_solver_circular_dependency( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -1228,7 +1232,7 @@ def test_solver_circular_dependency( def test_solver_circular_dependency_chain( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -1265,7 +1269,7 @@ def test_solver_circular_dependency_chain( def test_solver_dense_dependencies( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: # The root package depends on packages A0...An-1, # And package Ai depends on packages A0...Ai-1 # This graph is a transitive tournament @@ -1288,7 +1292,7 @@ def test_solver_dense_dependencies( def test_solver_duplicate_dependencies_same_constraint( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -1317,7 +1321,7 @@ def test_solver_duplicate_dependencies_same_constraint( def test_solver_duplicate_dependencies_different_constraints( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -1349,7 +1353,7 @@ def test_solver_duplicate_dependencies_different_constraints( def test_solver_duplicate_dependencies_different_constraints_same_requirements( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -1376,7 +1380,7 @@ def test_solver_duplicate_dependencies_different_constraints_same_requirements( def test_solver_duplicate_dependencies_different_constraints_merge_by_marker( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -1414,7 +1418,7 @@ def test_solver_duplicate_dependencies_different_constraints_merge_by_marker( @pytest.mark.parametrize("git_first", [False, True]) def test_solver_duplicate_dependencies_different_sources_types_are_preserved( solver: Solver, repo: Repository, package: ProjectPackage, git_first: bool -): +) -> None: pendulum = get_package("pendulum", "2.0.3") repo.add_package(pendulum) repo.add_package(get_package("cleo", "1.0.0")) @@ -1482,7 +1486,7 @@ def test_solver_duplicate_dependencies_different_sources_types_are_preserved( def test_solver_duplicate_dependencies_different_constraints_merge_no_markers( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "1.0")) @@ -1683,7 +1687,7 @@ def test_solver_duplicate_dependencies_different_constraints_discard_no_markers3 def test_solver_duplicate_dependencies_ignore_overrides_with_empty_marker_intersection( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: """ Distinct requirements per marker: * Python 2.7: A (which requires B) and B @@ -1754,7 +1758,7 @@ def test_solver_duplicate_dependencies_ignore_overrides_with_empty_marker_inters def test_solver_duplicate_dependencies_ignore_overrides_with_empty_marker_intersection2( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: """ Empty intersection between top level dependency and transient dependency. """ @@ -1797,7 +1801,7 @@ def test_solver_duplicate_dependencies_ignore_overrides_with_empty_marker_inters def test_solver_duplicate_dependencies_sub_dependencies( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -1836,10 +1840,11 @@ def test_solver_duplicate_dependencies_sub_dependencies( ) -def test_duplicate_path_dependencies(solver: Solver, package: ProjectPackage) -> None: +def test_duplicate_path_dependencies( + solver: Solver, package: ProjectPackage, fixture_dir: FixtureDirGetter +) -> None: set_package_python_versions(solver.provider, "^3.7") - fixtures = Path(__file__).parent.parent / "fixtures" - project_dir = fixtures / "with_conditional_path_deps" + project_dir = fixture_dir("with_conditional_path_deps") path1 = (project_dir / "demo_one").as_posix() demo1 = Package("demo", "1.2.3", source_type="directory", source_url=path1) @@ -1869,11 +1874,10 @@ def test_duplicate_path_dependencies(solver: Solver, package: ProjectPackage) -> def test_duplicate_path_dependencies_same_path( - solver: Solver, package: ProjectPackage + solver: Solver, package: ProjectPackage, fixture_dir: FixtureDirGetter ) -> None: set_package_python_versions(solver.provider, "^3.7") - fixtures = Path(__file__).parent.parent / "fixtures" - project_dir = fixtures / "with_conditional_path_deps" + project_dir = fixture_dir("with_conditional_path_deps") path1 = (project_dir / "demo_one").as_posix() demo1 = Package("demo", "1.2.3", source_type="directory", source_url=path1) @@ -1895,7 +1899,7 @@ def test_duplicate_path_dependencies_same_path( def test_solver_fails_if_dependency_name_does_not_match_package( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency( Factory.create_dependency( "my-demo", {"git": "https://github.com/demo/demo.git"} @@ -1908,7 +1912,7 @@ def test_solver_fails_if_dependency_name_does_not_match_package( def test_solver_does_not_get_stuck_in_recursion_on_circular_dependency( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package_a = get_package("A", "1.0") package_a.add_dependency(Factory.create_dependency("B", "^1.0")) package_b = get_package("B", "1.0") @@ -1936,7 +1940,7 @@ def test_solver_does_not_get_stuck_in_recursion_on_circular_dependency( def test_solver_can_resolve_git_dependencies( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") repo.add_package(pendulum) @@ -1971,7 +1975,7 @@ def test_solver_can_resolve_git_dependencies( def test_solver_can_resolve_git_dependencies_with_extras( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") repo.add_package(pendulum) @@ -2011,7 +2015,7 @@ def test_solver_can_resolve_git_dependencies_with_extras( ) def test_solver_can_resolve_git_dependencies_with_ref( solver: Solver, repo: Repository, package: ProjectPackage, ref: dict[str, str] -): +) -> None: pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") repo.add_package(pendulum) @@ -2046,7 +2050,7 @@ def test_solver_can_resolve_git_dependencies_with_ref( def test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requirement_is_compatible( # noqa: E501 solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: set_package_python_versions(solver.provider, "~2.7 || ^3.4") package.add_dependency( Factory.create_dependency("A", {"version": "^1.0", "python": "^3.6"}) @@ -2064,7 +2068,7 @@ def test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requir def test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requirement_is_compatible_multiple( # noqa: E501 solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: set_package_python_versions(solver.provider, "~2.7 || ^3.4") package.add_dependency( Factory.create_dependency("A", {"version": "^1.0", "python": "^3.6"}) @@ -2096,7 +2100,7 @@ def test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requir def test_solver_triggers_conflict_for_dependency_python_not_fully_compatible_with_package_python( # noqa: E501 solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: set_package_python_versions(solver.provider, "~2.7 || ^3.4") package.add_dependency( Factory.create_dependency("A", {"version": "^1.0", "python": "^3.5"}) @@ -2113,7 +2117,7 @@ def test_solver_triggers_conflict_for_dependency_python_not_fully_compatible_wit def test_solver_finds_compatible_package_for_dependency_python_not_fully_compatible_with_package_python( # noqa: E501 solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: set_package_python_versions(solver.provider, "~2.7 || ^3.4") package.add_dependency( Factory.create_dependency("A", {"version": "^1.0", "python": "^3.5"}) @@ -2135,7 +2139,7 @@ def test_solver_finds_compatible_package_for_dependency_python_not_fully_compati def test_solver_does_not_trigger_new_resolution_on_duplicate_dependencies_if_only_extras( # noqa: E501 solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: dep1 = Dependency.create_from_pep_508('B (>=1.0); extra == "foo"') dep1.activate() dep2 = Dependency.create_from_pep_508('B (>=2.0); extra == "bar"') @@ -2173,7 +2177,7 @@ def test_solver_does_not_trigger_new_resolution_on_duplicate_dependencies_if_onl def test_solver_does_not_raise_conflict_for_locked_conditional_dependencies( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: set_package_python_versions(solver.provider, "~2.7 || ^3.4") package.add_dependency( Factory.create_dependency("A", {"version": "^1.0", "python": "^3.6"}) @@ -2205,7 +2209,7 @@ def test_solver_does_not_raise_conflict_for_locked_conditional_dependencies( def test_solver_returns_extras_if_requested_in_dependencies_and_not_in_root_package( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) package.add_dependency(Factory.create_dependency("C", "*")) @@ -2244,7 +2248,7 @@ def test_solver_returns_extras_if_requested_in_dependencies_and_not_in_root_pack def test_solver_should_not_resolve_prerelease_version_if_not_requested( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "~1.8.0")) package.add_dependency(Factory.create_dependency("B", "^0.5.0")) @@ -2263,7 +2267,7 @@ def test_solver_should_not_resolve_prerelease_version_if_not_requested( def test_solver_ignores_dependencies_with_incompatible_python_full_version_marker( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: set_package_python_versions(solver.provider, "^3.6") package.add_dependency(Factory.create_dependency("A", "^1.0")) package.add_dependency(Factory.create_dependency("B", "^2.0")) @@ -2296,7 +2300,7 @@ def test_solver_ignores_dependencies_with_incompatible_python_full_version_marke def test_solver_git_dependencies_update( package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO -): +) -> None: pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") repo.add_package(pendulum) @@ -2345,7 +2349,7 @@ def test_solver_git_dependencies_update( def test_solver_git_dependencies_update_skipped( package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO -): +) -> None: pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") repo.add_package(pendulum) @@ -2378,7 +2382,7 @@ def test_solver_git_dependencies_update_skipped( def test_solver_git_dependencies_short_hash_update_skipped( package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO -): +) -> None: pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") repo.add_package(pendulum) @@ -2423,19 +2427,15 @@ def test_solver_git_dependencies_short_hash_update_skipped( def test_solver_can_resolve_directory_dependencies( - solver: Solver, repo: Repository, package: ProjectPackage -): + solver: Solver, + repo: Repository, + package: ProjectPackage, + fixture_dir: FixtureDirGetter, +) -> None: pendulum = get_package("pendulum", "2.0.3") repo.add_package(pendulum) - path = ( - Path(__file__).parent.parent - / "fixtures" - / "git" - / "github.com" - / "demo" - / "demo" - ).as_posix() + path = (fixture_dir("git") / "github.com" / "demo" / "demo").as_posix() package.add_dependency(Factory.create_dependency("demo", {"path": path})) @@ -2460,8 +2460,9 @@ def test_solver_can_resolve_directory_dependencies_nested_editable( repo: Repository, pool: RepositoryPool, io: NullIO, -): - base = Path(__file__).parent.parent / "fixtures" / "project_with_nested_local" + fixture_dir: FixtureDirGetter, +) -> None: + base = fixture_dir("project_with_nested_local") poetry = Factory().create_poetry(cwd=base) package = poetry.package @@ -2511,21 +2512,17 @@ def test_solver_can_resolve_directory_dependencies_nested_editable( def test_solver_can_resolve_directory_dependencies_with_extras( - solver: Solver, repo: Repository, package: ProjectPackage -): + solver: Solver, + repo: Repository, + package: ProjectPackage, + fixture_dir: FixtureDirGetter, +) -> None: pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") repo.add_package(pendulum) repo.add_package(cleo) - path = ( - Path(__file__).parent.parent - / "fixtures" - / "git" - / "github.com" - / "demo" - / "demo" - ).as_posix() + path = (fixture_dir("git") / "github.com" / "demo" / "demo").as_posix() package.add_dependency( Factory.create_dependency("demo", {"path": path, "extras": ["foo"]}) @@ -2553,17 +2550,15 @@ def test_solver_can_resolve_directory_dependencies_with_extras( def test_solver_can_resolve_sdist_dependencies( - solver: Solver, repo: Repository, package: ProjectPackage -): + solver: Solver, + repo: Repository, + package: ProjectPackage, + fixture_dir: FixtureDirGetter, +) -> None: pendulum = get_package("pendulum", "2.0.3") repo.add_package(pendulum) - path = ( - Path(__file__).parent.parent - / "fixtures" - / "distributions" - / "demo-0.1.0.tar.gz" - ).as_posix() + path = (fixture_dir("distributions") / "demo-0.1.0.tar.gz").as_posix() package.add_dependency(Factory.create_dependency("demo", {"path": path})) @@ -2585,19 +2580,17 @@ def test_solver_can_resolve_sdist_dependencies( def test_solver_can_resolve_sdist_dependencies_with_extras( - solver: Solver, repo: Repository, package: ProjectPackage -): + solver: Solver, + repo: Repository, + package: ProjectPackage, + fixture_dir: FixtureDirGetter, +) -> None: pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") repo.add_package(pendulum) repo.add_package(cleo) - path = ( - Path(__file__).parent.parent - / "fixtures" - / "distributions" - / "demo-0.1.0.tar.gz" - ).as_posix() + path = (fixture_dir("distributions") / "demo-0.1.0.tar.gz").as_posix() package.add_dependency( Factory.create_dependency("demo", {"path": path, "extras": ["foo"]}) @@ -2625,17 +2618,15 @@ def test_solver_can_resolve_sdist_dependencies_with_extras( def test_solver_can_resolve_wheel_dependencies( - solver: Solver, repo: Repository, package: ProjectPackage -): + solver: Solver, + repo: Repository, + package: ProjectPackage, + fixture_dir: FixtureDirGetter, +) -> None: pendulum = get_package("pendulum", "2.0.3") repo.add_package(pendulum) - path = ( - Path(__file__).parent.parent - / "fixtures" - / "distributions" - / "demo-0.1.0-py2.py3-none-any.whl" - ).as_posix() + path = (fixture_dir("distributions") / "demo-0.1.0-py2.py3-none-any.whl").as_posix() package.add_dependency(Factory.create_dependency("demo", {"path": path})) @@ -2657,19 +2648,17 @@ def test_solver_can_resolve_wheel_dependencies( def test_solver_can_resolve_wheel_dependencies_with_extras( - solver: Solver, repo: Repository, package: ProjectPackage -): + solver: Solver, + repo: Repository, + package: ProjectPackage, + fixture_dir: FixtureDirGetter, +) -> None: pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") repo.add_package(pendulum) repo.add_package(cleo) - path = ( - Path(__file__).parent.parent - / "fixtures" - / "distributions" - / "demo-0.1.0-py2.py3-none-any.whl" - ).as_posix() + path = (fixture_dir("distributions") / "demo-0.1.0-py2.py3-none-any.whl").as_posix() package.add_dependency( Factory.create_dependency("demo", {"path": path, "extras": ["foo"]}) @@ -2698,7 +2687,7 @@ def test_solver_can_resolve_wheel_dependencies_with_extras( def test_solver_can_solve_with_legacy_repository_using_proper_dists( package: ProjectPackage, io: NullIO -): +) -> None: repo = MockLegacyRepository() pool = RepositoryPool([repo]) @@ -2741,7 +2730,7 @@ def test_solver_can_solve_with_legacy_repository_using_proper_dists( def test_solver_can_solve_with_legacy_repository_using_proper_python_compatible_dists( package: ProjectPackage, io: NullIO, -): +) -> None: package.python_versions = "^3.7" repo = MockLegacyRepository() @@ -2770,7 +2759,7 @@ def test_solver_can_solve_with_legacy_repository_using_proper_python_compatible_ ) -def test_solver_skips_invalid_versions(package: ProjectPackage, io: NullIO): +def test_solver_skips_invalid_versions(package: ProjectPackage, io: NullIO) -> None: package.python_versions = "^3.7" repo = MockPyPIRepository() @@ -2789,7 +2778,7 @@ def test_solver_skips_invalid_versions(package: ProjectPackage, io: NullIO): def test_multiple_constraints_on_root( package: ProjectPackage, solver: Solver, repo: Repository -): +) -> None: package.add_dependency( Factory.create_dependency("foo", {"version": "^1.0", "python": "^2.7"}) ) @@ -2813,7 +2802,7 @@ def test_multiple_constraints_on_root( def test_solver_chooses_most_recent_version_amongst_repositories( package: ProjectPackage, io: NullIO -): +) -> None: package.python_versions = "^3.7" package.add_dependency(Factory.create_dependency("tomlkit", {"version": "^0.5"})) @@ -2834,7 +2823,7 @@ def test_solver_chooses_most_recent_version_amongst_repositories( def test_solver_chooses_from_correct_repository_if_forced( package: ProjectPackage, io: NullIO -): +) -> None: package.python_versions = "^3.7" package.add_dependency( Factory.create_dependency("tomlkit", {"version": "^0.5", "source": "legacy"}) @@ -2869,7 +2858,7 @@ def test_solver_chooses_from_correct_repository_if_forced( def test_solver_chooses_from_correct_repository_if_forced_and_transitive_dependency( package: ProjectPackage, io: NullIO, -): +) -> None: package.python_versions = "^3.7" package.add_dependency(Factory.create_dependency("foo", "^1.0")) package.add_dependency( @@ -2911,7 +2900,7 @@ def test_solver_chooses_from_correct_repository_if_forced_and_transitive_depende def test_solver_does_not_choose_from_secondary_repository_by_default( package: ProjectPackage, io: NullIO -): +) -> None: package.python_versions = "^3.7" package.add_dependency(Factory.create_dependency("clikit", {"version": "^0.2.0"})) @@ -2959,7 +2948,7 @@ def test_solver_does_not_choose_from_secondary_repository_by_default( def test_solver_chooses_from_secondary_if_explicit( package: ProjectPackage, io: NullIO, -): +) -> None: package.python_versions = "^3.7" package.add_dependency( Factory.create_dependency("clikit", {"version": "^0.2.0", "source": "PyPI"}) @@ -3068,7 +3057,7 @@ def test_solver_discards_packages_with_empty_markers( repo: Repository, pool: RepositoryPool, io: NullIO, -): +) -> None: package.python_versions = "~2.7 || ^3.4" package.add_dependency( Factory.create_dependency( @@ -3104,7 +3093,7 @@ def test_solver_discards_packages_with_empty_markers( def test_solver_does_not_raise_conflict_for_conditional_dev_dependencies( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: set_package_python_versions(solver.provider, "~2.7 || ^3.5") package.add_dependency( Factory.create_dependency( @@ -3136,7 +3125,7 @@ def test_solver_does_not_raise_conflict_for_conditional_dev_dependencies( def test_solver_does_not_loop_indefinitely_on_duplicate_constraints_with_extras( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: set_package_python_versions(solver.provider, "~2.7 || ^3.5") package.add_dependency( Factory.create_dependency( @@ -3170,7 +3159,7 @@ def test_solver_does_not_fail_with_locked_git_and_non_git_dependencies( repo: Repository, pool: RepositoryPool, io: NullIO, -): +) -> None: package.add_dependency( Factory.create_dependency("demo", {"git": "https://github.com/demo/demo.git"}) ) @@ -3205,7 +3194,7 @@ def test_solver_does_not_fail_with_locked_git_and_non_git_dependencies( def test_ignore_python_constraint_no_overlap_dependencies( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: pytest = get_package("demo", "1.0.0") pytest.add_dependency( Factory.create_dependency( @@ -3230,7 +3219,7 @@ def test_ignore_python_constraint_no_overlap_dependencies( def test_solver_should_not_go_into_an_infinite_loop_on_duplicate_dependencies( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: set_package_python_versions(solver.provider, "~2.7 || ^3.5") package.add_dependency(Factory.create_dependency("A", "^1.0")) @@ -3263,7 +3252,7 @@ def test_solver_should_not_go_into_an_infinite_loop_on_duplicate_dependencies( def test_solver_synchronize_single( package: ProjectPackage, pool: RepositoryPool, io: NullIO -): +) -> None: package_a = get_package("a", "1.0") solver = Solver(package, pool, [package_a], [], io) @@ -3279,7 +3268,7 @@ def test_solver_with_synchronization_keeps_critical_package( package: ProjectPackage, pool: RepositoryPool, io: NullIO, -): +) -> None: package_pip = get_package("setuptools", "1.0") solver = Solver(package, pool, [package_pip], [], io) @@ -3289,8 +3278,11 @@ def test_solver_with_synchronization_keeps_critical_package( def test_solver_cannot_choose_another_version_for_directory_dependencies( - solver: Solver, repo: Repository, package: ProjectPackage -): + solver: Solver, + repo: Repository, + package: ProjectPackage, + fixture_dir: FixtureDirGetter, +) -> None: pendulum = get_package("pendulum", "2.0.3") demo = get_package("demo", "0.1.0") foo = get_package("foo", "1.2.3") @@ -3299,14 +3291,7 @@ def test_solver_cannot_choose_another_version_for_directory_dependencies( repo.add_package(demo) repo.add_package(pendulum) - path = ( - Path(__file__).parent.parent - / "fixtures" - / "git" - / "github.com" - / "demo" - / "demo" - ).as_posix() + path = (fixture_dir("git") / "github.com" / "demo" / "demo").as_posix() package.add_dependency(Factory.create_dependency("demo", {"path": path})) package.add_dependency(Factory.create_dependency("foo", "^1.2.3")) @@ -3318,8 +3303,11 @@ def test_solver_cannot_choose_another_version_for_directory_dependencies( def test_solver_cannot_choose_another_version_for_file_dependencies( - solver: Solver, repo: Repository, package: ProjectPackage -): + solver: Solver, + repo: Repository, + package: ProjectPackage, + fixture_dir: FixtureDirGetter, +) -> None: pendulum = get_package("pendulum", "2.0.3") demo = get_package("demo", "0.0.8") foo = get_package("foo", "1.2.3") @@ -3328,12 +3316,7 @@ def test_solver_cannot_choose_another_version_for_file_dependencies( repo.add_package(demo) repo.add_package(pendulum) - path = ( - Path(__file__).parent.parent - / "fixtures" - / "distributions" - / "demo-0.1.0-py2.py3-none-any.whl" - ).as_posix() + path = (fixture_dir("distributions") / "demo-0.1.0-py2.py3-none-any.whl").as_posix() package.add_dependency(Factory.create_dependency("demo", {"path": path})) package.add_dependency(Factory.create_dependency("foo", "^1.2.3")) @@ -3346,7 +3329,7 @@ def test_solver_cannot_choose_another_version_for_file_dependencies( def test_solver_cannot_choose_another_version_for_git_dependencies( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: pendulum = get_package("pendulum", "2.0.3") demo = get_package("demo", "0.0.8") foo = get_package("foo", "1.2.3") @@ -3371,13 +3354,9 @@ def test_solver_cannot_choose_another_version_for_url_dependencies( repo: Repository, package: ProjectPackage, http: type[httpretty.httpretty], -): - path = ( - Path(__file__).parent.parent - / "fixtures" - / "distributions" - / "demo-0.1.0-py2.py3-none-any.whl" - ) + fixture_dir: FixtureDirGetter, +) -> None: + path = fixture_dir("distributions") / "demo-0.1.0-py2.py3-none-any.whl" http.register_uri( "GET", @@ -3409,7 +3388,7 @@ def test_solver_cannot_choose_another_version_for_url_dependencies( def test_solver_should_not_update_same_version_packages_if_installed_has_no_source_type( package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO -): +) -> None: package.add_dependency(Factory.create_dependency("foo", "1.0.0")) foo = Package( @@ -3458,7 +3437,7 @@ def test_solver_should_use_the_python_constraint_from_the_environment_if_availab def test_solver_should_resolve_all_versions_for_multiple_duplicate_dependencies( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.python_versions = "~2.7 || ^3.5" package.add_dependency( Factory.create_dependency( @@ -3506,7 +3485,7 @@ def test_solver_should_resolve_all_versions_for_multiple_duplicate_dependencies( def test_solver_should_not_raise_errors_for_irrelevant_python_constraints( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.python_versions = "^3.6" set_package_python_versions(solver.provider, "^3.6") package.add_dependency( @@ -3524,7 +3503,7 @@ def test_solver_should_not_raise_errors_for_irrelevant_python_constraints( def test_solver_can_resolve_transitive_extras( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("requests", "^2.24.0")) package.add_dependency(Factory.create_dependency("PyOTA", "^2.1.0")) @@ -3562,7 +3541,7 @@ def test_solver_can_resolve_transitive_extras( def test_solver_can_resolve_for_packages_with_missing_extras( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency( Factory.create_dependency( "django-anymail", {"version": "^6.0", "extras": ["postmark"]} @@ -3599,7 +3578,7 @@ def test_solver_can_resolve_for_packages_with_missing_extras( def test_solver_can_resolve_python_restricted_package_dependencies( package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO -): +) -> None: package.add_dependency( Factory.create_dependency("futures", {"version": "^3.3.0", "python": "~2.7"}) ) @@ -3630,7 +3609,7 @@ def test_solver_can_resolve_python_restricted_package_dependencies( def test_solver_should_not_raise_errors_for_irrelevant_transitive_python_constraints( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: package.python_versions = "~2.7 || ^3.5" set_package_python_versions(solver.provider, "~2.7 || ^3.5") package.add_dependency(Factory.create_dependency("virtualenv", "^20.4.3")) @@ -3685,7 +3664,7 @@ def test_solver_keeps_multiple_locked_dependencies_for_same_package( pool: RepositoryPool, io: NullIO, is_locked: bool, -): +) -> None: package.add_dependency( Factory.create_dependency("A", {"version": "~1.1", "python": "<3.7"}) ) @@ -3739,7 +3718,7 @@ def test_solver_does_not_update_ref_of_locked_vcs_package( pool: RepositoryPool, io: NullIO, is_locked: bool, -): +) -> None: locked_ref = "123456" latest_ref = "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24" demo_locked = Package( @@ -3795,7 +3774,7 @@ def test_solver_does_not_fetch_locked_vcs_package_with_ref( pool: RepositoryPool, io: NullIO, mocker: MockerFixture, -): +) -> None: locked_ref = "123456" demo_locked = Package( "demo", @@ -3824,8 +3803,11 @@ def test_solver_does_not_fetch_locked_vcs_package_with_ref( def test_solver_direct_origin_dependency_with_extras_requested_by_other_package( - solver: Solver, repo: Repository, package: ProjectPackage -): + solver: Solver, + repo: Repository, + package: ProjectPackage, + fixture_dir: FixtureDirGetter, +) -> None: """ Another package requires the same dependency with extras that is required by the project as direct origin dependency without any extras. @@ -3840,14 +3822,7 @@ def test_solver_direct_origin_dependency_with_extras_requested_by_other_package( repo.add_package(pendulum) repo.add_package(cleo) - path = ( - Path(__file__).parent.parent - / "fixtures" - / "git" - / "github.com" - / "demo" - / "demo" - ).as_posix() + path = (fixture_dir("git") / "github.com" / "demo" / "demo").as_posix() # project requires path dependency of demo while demo-foo requires demo[foo] package.add_dependency(Factory.create_dependency("demo", {"path": path})) @@ -3877,7 +3852,7 @@ def test_solver_direct_origin_dependency_with_extras_requested_by_other_package( def test_solver_incompatible_dependency_with_and_without_extras( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: """ The solver first encounters a requirement for google-auth and then later an incompatible requirement for google-auth[aiohttp]. @@ -3929,7 +3904,7 @@ def test_solver_incompatible_dependency_with_and_without_extras( def test_update_with_prerelease_and_no_solution( package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO -): +) -> None: # Locked and installed: cleo which depends on an old version of crashtest. cleo = get_package("cleo", "1.0.0a5") crashtest = get_package("crashtest", "0.3.0") @@ -4001,7 +3976,7 @@ def test_update_with_use_latest_vs_lock( pool: RepositoryPool, io: NullIO, is_locked: bool, -): +) -> None: """ A1 depends on B2, A2 and A3 depend on B1. Same for C. B1 depends on A2/C2, B2 depends on A1/C1. diff --git a/tests/test_factory.py b/tests/test_factory.py index 2ba1104e768..d3a39620e86 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -1,6 +1,5 @@ from __future__ import annotations -from pathlib import Path from typing import TYPE_CHECKING import pytest @@ -26,8 +25,6 @@ from poetry.poetry import Poetry from tests.types import FixtureDirGetter -fixtures_dir = Path(__file__).parent / "fixtures" - class MyPlugin(Plugin): def activate(self, poetry: Poetry, io: IO) -> None: @@ -35,8 +32,8 @@ def activate(self, poetry: Poetry, io: IO) -> None: poetry.package.readmes = ("README.md",) -def test_create_poetry(): - poetry = Factory().create_poetry(fixtures_dir / "sample_project") +def test_create_poetry(fixture_dir: FixtureDirGetter) -> None: + poetry = Factory().create_poetry(fixture_dir("sample_project")) package = poetry.package @@ -48,7 +45,7 @@ def test_create_poetry(): for readme in package.readmes: assert ( - readme.relative_to(fixtures_dir).as_posix() == "sample_project/README.rst" + readme.relative_to(fixture_dir("sample_project")).as_posix() == "README.rst" ) assert package.homepage == "https://python-poetry.org" @@ -147,8 +144,10 @@ def test_create_poetry(): ("project_with_extras",), ], ) -def test_create_pyproject_from_package(project: str): - poetry = Factory().create_poetry(fixtures_dir / project) +def test_create_pyproject_from_package( + project: str, fixture_dir: FixtureDirGetter +) -> None: + poetry = Factory().create_poetry(fixture_dir(project)) package = poetry.package pyproject = Factory.create_pyproject_from_package(package) @@ -177,8 +176,10 @@ def test_create_pyproject_from_package(project: str): assert not DeepDiff(expected, result) -def test_create_poetry_with_packages_and_includes(): - poetry = Factory().create_poetry(fixtures_dir / "with-include") +def test_create_poetry_with_packages_and_includes( + fixture_dir: FixtureDirGetter, +) -> None: + poetry = Factory().create_poetry(fixture_dir("with-include")) package = poetry.package @@ -198,9 +199,11 @@ def test_create_poetry_with_packages_and_includes(): ] -def test_create_poetry_with_multi_constraints_dependency(): +def test_create_poetry_with_multi_constraints_dependency( + fixture_dir: FixtureDirGetter, +) -> None: poetry = Factory().create_poetry( - fixtures_dir / "project_with_multi_constraints_dependency" + fixture_dir("project_with_multi_constraints_dependency") ) package = poetry.package @@ -208,30 +211,34 @@ def test_create_poetry_with_multi_constraints_dependency(): assert len(package.requires) == 2 -def test_poetry_with_default_source_legacy(with_simple_keyring: None): +def test_poetry_with_default_source_legacy( + fixture_dir: FixtureDirGetter, with_simple_keyring: None +) -> None: io = BufferedIO() - poetry = Factory().create_poetry(fixtures_dir / "with_default_source_legacy", io=io) + poetry = Factory().create_poetry(fixture_dir("with_default_source_legacy"), io=io) assert len(poetry.pool.repositories) == 1 assert "Found deprecated key" in io.fetch_error() -def test_poetry_with_default_source(with_simple_keyring: None): +def test_poetry_with_default_source( + fixture_dir: FixtureDirGetter, with_simple_keyring: None +) -> None: io = BufferedIO() - poetry = Factory().create_poetry(fixtures_dir / "with_default_source", io=io) + poetry = Factory().create_poetry(fixture_dir("with_default_source"), io=io) assert len(poetry.pool.repositories) == 1 assert io.fetch_error() == "" @pytest.mark.parametrize( - "fixture_filename", + "project", ("with_non_default_source_implicit", "with_non_default_source_explicit"), ) def test_poetry_with_non_default_source( - fixture_filename: str, with_simple_keyring: None -): - poetry = Factory().create_poetry(fixtures_dir / fixture_filename) + project: str, fixture_dir: FixtureDirGetter, with_simple_keyring: None +) -> None: + poetry = Factory().create_poetry(fixture_dir(project)) assert not poetry.pool.has_default() assert poetry.pool.has_repository("PyPI") @@ -243,9 +250,11 @@ def test_poetry_with_non_default_source( assert {repo.name for repo in poetry.pool.repositories} == {"PyPI", "foo"} -def test_poetry_with_non_default_secondary_source_legacy(with_simple_keyring: None): +def test_poetry_with_non_default_secondary_source_legacy( + fixture_dir: FixtureDirGetter, with_simple_keyring: None +) -> None: poetry = Factory().create_poetry( - fixtures_dir / "with_non_default_secondary_source_legacy" + fixture_dir("with_non_default_secondary_source_legacy") ) assert poetry.pool.has_repository("PyPI") @@ -256,8 +265,10 @@ def test_poetry_with_non_default_secondary_source_legacy(with_simple_keyring: No assert [repo.name for repo in poetry.pool.repositories] == ["PyPI", "foo"] -def test_poetry_with_non_default_secondary_source(with_simple_keyring: None): - poetry = Factory().create_poetry(fixtures_dir / "with_non_default_secondary_source") +def test_poetry_with_non_default_secondary_source( + fixture_dir: FixtureDirGetter, with_simple_keyring: None +) -> None: + poetry = Factory().create_poetry(fixture_dir("with_non_default_secondary_source")) assert poetry.pool.has_repository("PyPI") assert isinstance(poetry.pool.repository("PyPI"), PyPiRepository) @@ -268,10 +279,11 @@ def test_poetry_with_non_default_secondary_source(with_simple_keyring: None): def test_poetry_with_non_default_multiple_secondary_sources_legacy( + fixture_dir: FixtureDirGetter, with_simple_keyring: None, -): +) -> None: poetry = Factory().create_poetry( - fixtures_dir / "with_non_default_multiple_secondary_sources_legacy" + fixture_dir("with_non_default_multiple_secondary_sources_legacy") ) assert poetry.pool.has_repository("PyPI") @@ -284,9 +296,11 @@ def test_poetry_with_non_default_multiple_secondary_sources_legacy( assert {repo.name for repo in poetry.pool.repositories} == {"PyPI", "foo", "bar"} -def test_poetry_with_non_default_multiple_secondary_sources(with_simple_keyring: None): +def test_poetry_with_non_default_multiple_secondary_sources( + fixture_dir: FixtureDirGetter, with_simple_keyring: None +) -> None: poetry = Factory().create_poetry( - fixtures_dir / "with_non_default_multiple_secondary_sources" + fixture_dir("with_non_default_multiple_secondary_sources") ) assert poetry.pool.has_repository("PyPI") @@ -299,9 +313,11 @@ def test_poetry_with_non_default_multiple_secondary_sources(with_simple_keyring: assert {repo.name for repo in poetry.pool.repositories} == {"PyPI", "foo", "bar"} -def test_poetry_with_non_default_multiple_sources_legacy(with_simple_keyring: None): +def test_poetry_with_non_default_multiple_sources_legacy( + fixture_dir: FixtureDirGetter, with_simple_keyring: None +) -> None: poetry = Factory().create_poetry( - fixtures_dir / "with_non_default_multiple_sources_legacy" + fixture_dir("with_non_default_multiple_sources_legacy") ) assert not poetry.pool.has_default() @@ -315,8 +331,10 @@ def test_poetry_with_non_default_multiple_sources_legacy(with_simple_keyring: No assert {repo.name for repo in poetry.pool.repositories} == {"bar", "PyPI", "foo"} -def test_poetry_with_non_default_multiple_sources(with_simple_keyring: None): - poetry = Factory().create_poetry(fixtures_dir / "with_non_default_multiple_sources") +def test_poetry_with_non_default_multiple_sources( + fixture_dir: FixtureDirGetter, with_simple_keyring: None +) -> None: + poetry = Factory().create_poetry(fixture_dir("with_non_default_multiple_sources")) assert not poetry.pool.has_default() assert poetry.pool.has_repository("PyPI") @@ -329,8 +347,8 @@ def test_poetry_with_non_default_multiple_sources(with_simple_keyring: None): assert {repo.name for repo in poetry.pool.repositories} == {"PyPI", "bar", "foo"} -def test_poetry_with_no_default_source(): - poetry = Factory().create_poetry(fixtures_dir / "sample_project") +def test_poetry_with_no_default_source(fixture_dir: FixtureDirGetter) -> None: + poetry = Factory().create_poetry(fixture_dir("sample_project")) assert poetry.pool.has_repository("PyPI") assert poetry.pool.get_priority("PyPI") is Priority.DEFAULT @@ -338,8 +356,10 @@ def test_poetry_with_no_default_source(): assert {repo.name for repo in poetry.pool.repositories} == {"PyPI"} -def test_poetry_with_explicit_source(with_simple_keyring: None) -> None: - poetry = Factory().create_poetry(fixtures_dir / "with_explicit_source") +def test_poetry_with_explicit_source( + fixture_dir: FixtureDirGetter, with_simple_keyring: None +) -> None: + poetry = Factory().create_poetry(fixture_dir("with_explicit_source")) assert len(poetry.pool.repositories) == 1 assert len(poetry.pool.all_repositories) == 2 @@ -351,29 +371,33 @@ def test_poetry_with_explicit_source(with_simple_keyring: None) -> None: assert [repo.name for repo in poetry.pool.repositories] == ["PyPI"] -def test_poetry_with_two_default_sources_legacy(with_simple_keyring: None): +def test_poetry_with_two_default_sources_legacy( + fixture_dir: FixtureDirGetter, with_simple_keyring: None +) -> None: with pytest.raises(ValueError) as e: - Factory().create_poetry(fixtures_dir / "with_two_default_sources_legacy") + Factory().create_poetry(fixture_dir("with_two_default_sources_legacy")) assert str(e.value) == "Only one repository can be the default." -def test_poetry_with_two_default_sources(with_simple_keyring: None): +def test_poetry_with_two_default_sources( + fixture_dir: FixtureDirGetter, with_simple_keyring: None +) -> None: with pytest.raises(ValueError) as e: - Factory().create_poetry(fixtures_dir / "with_two_default_sources") + Factory().create_poetry(fixture_dir("with_two_default_sources")) assert str(e.value) == "Only one repository can be the default." -def test_validate(): - complete = TOMLFile(fixtures_dir / "complete.toml") +def test_validate(fixture_dir: FixtureDirGetter) -> None: + complete = TOMLFile(fixture_dir("complete.toml")) content = complete.read()["tool"]["poetry"] assert Factory.validate(content) == {"errors": [], "warnings": []} -def test_validate_fails(): - complete = TOMLFile(fixtures_dir / "complete.toml") +def test_validate_fails(fixture_dir: FixtureDirGetter) -> None: + complete = TOMLFile(fixture_dir("complete.toml")) content = complete.read()["tool"]["poetry"] content["this key is not in the schema"] = "" @@ -385,11 +409,11 @@ def test_validate_fails(): assert Factory.validate(content) == {"errors": [expected], "warnings": []} -def test_create_poetry_fails_on_invalid_configuration(): +def test_create_poetry_fails_on_invalid_configuration( + fixture_dir: FixtureDirGetter, +) -> None: with pytest.raises(RuntimeError) as e: - Factory().create_poetry( - Path(__file__).parent / "fixtures" / "invalid_pyproject" / "pyproject.toml" - ) + Factory().create_poetry(fixture_dir("invalid_pyproject") / "pyproject.toml") expected = """\ The Poetry configuration is invalid: @@ -398,7 +422,7 @@ def test_create_poetry_fails_on_invalid_configuration(): assert str(e.value) == expected -def test_create_poetry_with_local_config(fixture_dir: FixtureDirGetter): +def test_create_poetry_with_local_config(fixture_dir: FixtureDirGetter) -> None: poetry = Factory().create_poetry(fixture_dir("with_local_config")) assert not poetry.config.get("virtualenvs.in-project") @@ -409,9 +433,11 @@ def test_create_poetry_with_local_config(fixture_dir: FixtureDirGetter): assert not poetry.config.get("virtualenvs.options.system-site-packages") -def test_create_poetry_with_plugins(mocker: MockerFixture): +def test_create_poetry_with_plugins( + mocker: MockerFixture, fixture_dir: FixtureDirGetter +) -> None: mock_metadata_entry_points(mocker, MyPlugin) - poetry = Factory().create_poetry(fixtures_dir / "sample_project") + poetry = Factory().create_poetry(fixture_dir("sample_project")) assert poetry.package.readmes == ("README.md",) diff --git a/tests/utils/test_env.py b/tests/utils/test_env.py index c8589f48594..a5f8957424f 100644 --- a/tests/utils/test_env.py +++ b/tests/utils/test_env.py @@ -42,6 +42,7 @@ from poetry.poetry import Poetry from tests.conftest import Config + from tests.types import FixtureDirGetter from tests.types import ProjectFactory MINIMAL_SCRIPT = """\ @@ -77,9 +78,8 @@ def sys_path(self) -> list[str]: @pytest.fixture() -def poetry(project_factory: ProjectFactory) -> Poetry: - fixture = Path(__file__).parent.parent / "fixtures" / "simple_project" - return project_factory("simple", source=fixture) +def poetry(project_factory: ProjectFactory, fixture_dir: FixtureDirGetter) -> Poetry: + return project_factory("simple", source=fixture_dir("simple_project")) @pytest.fixture() @@ -100,7 +100,7 @@ def test_virtualenvs_with_spaces_in_their_path_work_as_expected( @pytest.mark.skipif(sys.platform != "darwin", reason="requires darwin") -def test_venv_backup_exclusion(tmp_path: Path, manager: EnvManager): +def test_venv_backup_exclusion(tmp_path: Path, manager: EnvManager) -> None: import xattr venv_path = tmp_path / "Virtual Env" @@ -1042,7 +1042,7 @@ def test_check_output_with_called_process_error( @pytest.mark.parametrize("out", ["sys.stdout", "sys.stderr"]) def test_call_does_not_block_on_full_pipe( tmp_path: Path, tmp_venv: VirtualEnv, out: str -): +) -> None: """see https://github.com/python-poetry/poetry/issues/7698""" script = tmp_path / "script.py" script.write_text( @@ -1664,10 +1664,8 @@ def test_generate_env_name_uses_real_path( @pytest.fixture() -def extended_without_setup_poetry() -> Poetry: - poetry = Factory().create_poetry( - Path(__file__).parent.parent / "fixtures" / "extended_project_without_setup" - ) +def extended_without_setup_poetry(fixture_dir: FixtureDirGetter) -> Poetry: + poetry = Factory().create_poetry(fixture_dir("extended_project_without_setup")) return poetry @@ -1713,6 +1711,7 @@ def test_build_environment_not_called_without_build_script_specified( def test_create_venv_project_name_empty_sets_correct_prompt( + fixture_dir: FixtureDirGetter, project_factory: ProjectFactory, config: Config, mocker: MockerFixture, @@ -1721,8 +1720,7 @@ def test_create_venv_project_name_empty_sets_correct_prompt( if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] - fixture = Path(__file__).parent.parent / "fixtures" / "no_name_project" - poetry = project_factory("no", source=fixture) + poetry = project_factory("no", source=fixture_dir("no_name_project")) manager = EnvManager(poetry) poetry.package.python_versions = "^3.7" diff --git a/tests/utils/test_helpers.py b/tests/utils/test_helpers.py index dad06297609..55314a7d743 100644 --- a/tests/utils/test_helpers.py +++ b/tests/utils/test_helpers.py @@ -1,6 +1,5 @@ from __future__ import annotations -from pathlib import Path from typing import TYPE_CHECKING import pytest @@ -14,7 +13,7 @@ from tests.types import FixtureDirGetter -def test_parse_requires(): +def test_parse_requires() -> None: requires = """\ jsonschema>=2.6.0.0,<3.0.0.0 lockfile>=0.12.0.0,<0.13.0.0 @@ -71,10 +70,8 @@ def test_parse_requires(): def test_default_hash(fixture_dir: FixtureDirGetter) -> None: - root_dir = Path(__file__).parent.parent.parent - file_path = root_dir / fixture_dir("distributions/demo-0.1.0.tar.gz") sha_256 = "9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad" - assert get_file_hash(file_path) == sha_256 + assert get_file_hash(fixture_dir("distributions") / "demo-0.1.0.tar.gz") == sha_256 try: @@ -130,6 +127,5 @@ def test_default_hash(fixture_dir: FixtureDirGetter) -> None: def test_guaranteed_hash( hash_name: str, expected: str, fixture_dir: FixtureDirGetter ) -> None: - root_dir = Path(__file__).parent.parent.parent - file_path = root_dir / fixture_dir("distributions/demo-0.1.0.tar.gz") + file_path = fixture_dir("distributions") / "demo-0.1.0.tar.gz" assert get_file_hash(file_path, hash_name) == expected From 99f106c55bf17ddb8ff8db133cdc9bb36e0f05f5 Mon Sep 17 00:00:00 2001 From: David Hotham Date: Sun, 16 Apr 2023 15:29:00 +0100 Subject: [PATCH 05/10] some unit test typechecking (#7802) --- pyproject.toml | 9 ++ tests/config/test_config.py | 11 +- tests/console/commands/cache/test_list.py | 4 +- tests/console/commands/debug/test_resolve.py | 8 +- tests/console/commands/env/test_list.py | 12 +- tests/console/commands/env/test_remove.py | 10 +- .../console/commands/self/test_add_plugins.py | 16 +-- .../commands/self/test_remove_plugins.py | 4 +- .../commands/self/test_show_plugins.py | 6 +- tests/console/commands/test_about.py | 2 +- tests/console/commands/test_add.py | 130 +++++++++--------- tests/console/commands/test_config.py | 24 ++-- tests/console/commands/test_init.py | 60 ++++---- tests/console/commands/test_install.py | 40 +++--- tests/console/commands/test_lock.py | 12 +- tests/console/commands/test_new.py | 6 +- tests/console/commands/test_publish.py | 16 ++- tests/console/commands/test_remove.py | 12 +- tests/console/commands/test_run.py | 8 +- tests/console/commands/test_shell.py | 6 +- tests/console/commands/test_update.py | 2 +- tests/console/commands/test_version.py | 12 +- tests/console/test_application.py | 10 +- tests/installation/test_chef.py | 4 +- tests/installation/test_executor.py | 50 +++---- tests/installation/test_installer.py | 115 ++++++++-------- tests/integration/test_utils_vcs_git.py | 8 +- tests/plugins/test_plugin_manager.py | 12 +- tests/puzzle/test_provider.py | 3 +- tests/puzzle/test_solver.py | 2 +- .../repositories/test_installed_repository.py | 40 +++--- tests/utils/test_authenticator.py | 78 +++++------ tests/utils/test_password_manager.py | 43 +++--- 33 files changed, 410 insertions(+), 365 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7f87ec3c2ff..63e65101815 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -160,6 +160,11 @@ enable_error_code = [ "redundant-expr", "truthy-bool", ] +exclude = [ + "tests/fixtures", + "tests/masonry/builders/fixtures", + "tests/utils/fixtures" +] # use of importlib-metadata backport at python3.7 makes it impossible to # satisfy mypy without some ignores: but we get a different set of ignores at @@ -178,6 +183,10 @@ warn_unused_ignores = false [[tool.mypy.overrides]] module = [ 'cachecontrol.*', + 'cachy.*', + 'deepdiff.*', + 'httpretty.*', + 'keyring.*', 'lockfile.*', 'pexpect.*', 'requests_toolbelt.*', diff --git a/tests/config/test_config.py b/tests/config/test_config.py index 0d4f3ba7a2b..0194e7799d5 100644 --- a/tests/config/test_config.py +++ b/tests/config/test_config.py @@ -5,6 +5,7 @@ from pathlib import Path from typing import TYPE_CHECKING +from typing import Any import pytest @@ -19,7 +20,7 @@ from collections.abc import Iterator -def get_options_based_on_normalizer(normalizer: Callable) -> str: +def get_options_based_on_normalizer(normalizer: Callable[[str], Any]) -> str: flattened_config = flatten_dict(obj=Config.default_config, delimiter=".") for k in flattened_config: @@ -30,13 +31,13 @@ def get_options_based_on_normalizer(normalizer: Callable) -> str: @pytest.mark.parametrize( ("name", "value"), [("installer.parallel", True), ("virtualenvs.create", True)] ) -def test_config_get_default_value(config: Config, name: str, value: bool): +def test_config_get_default_value(config: Config, name: str, value: bool) -> None: assert config.get(name) is value def test_config_get_processes_depended_on_values( config: Config, config_cache_dir: Path -): +) -> None: assert str(config_cache_dir / "virtualenvs") == config.get("virtualenvs.path") @@ -62,7 +63,7 @@ def test_config_get_from_environment_variable( env_var: str, env_value: str, value: bool, -): +) -> None: os.environ[env_var] = env_value assert config.get(name) is value @@ -73,6 +74,6 @@ def test_config_get_from_environment_variable( ) def test_config_expands_tilde_for_virtualenvs_path( config: Config, path_config: str, expected: Path -): +) -> None: config.merge({"virtualenvs": {"path": path_config}}) assert config.virtualenvs_path == expected diff --git a/tests/console/commands/cache/test_list.py b/tests/console/commands/cache/test_list.py index 22db34cb0de..2def8564831 100644 --- a/tests/console/commands/cache/test_list.py +++ b/tests/console/commands/cache/test_list.py @@ -20,7 +20,7 @@ def tester(command_tester_factory: CommandTesterFactory) -> CommandTester: def test_cache_list( tester: CommandTester, mock_caches: None, repository_one: str, repository_two: str -): +) -> None: tester.execute() expected = f"""\ @@ -31,7 +31,7 @@ def test_cache_list( assert tester.io.fetch_output() == expected -def test_cache_list_empty(tester: CommandTester, repository_cache_dir: Path): +def test_cache_list_empty(tester: CommandTester, repository_cache_dir: Path) -> None: tester.execute() expected = """\ diff --git a/tests/console/commands/debug/test_resolve.py b/tests/console/commands/debug/test_resolve.py index ddb45d4d1c8..1d733e3612f 100644 --- a/tests/console/commands/debug/test_resolve.py +++ b/tests/console/commands/debug/test_resolve.py @@ -33,7 +33,7 @@ def __add_packages(repo: TestRepository) -> None: repo.add_package(get_package("cleo", "0.6.5")) -def test_debug_resolve_gives_resolution_results(tester: CommandTester): +def test_debug_resolve_gives_resolution_results(tester: CommandTester) -> None: tester.execute("cachy") expected = """\ @@ -48,7 +48,9 @@ def test_debug_resolve_gives_resolution_results(tester: CommandTester): assert tester.io.fetch_output() == expected -def test_debug_resolve_tree_option_gives_the_dependency_tree(tester: CommandTester): +def test_debug_resolve_tree_option_gives_the_dependency_tree( + tester: CommandTester, +) -> None: tester.execute("cachy --tree") expected = """\ @@ -63,7 +65,7 @@ def test_debug_resolve_tree_option_gives_the_dependency_tree(tester: CommandTest assert tester.io.fetch_output() == expected -def test_debug_resolve_git_dependency(tester: CommandTester): +def test_debug_resolve_git_dependency(tester: CommandTester) -> None: tester.execute("git+https://github.com/demo/demo.git") expected = """\ diff --git a/tests/console/commands/env/test_list.py b/tests/console/commands/env/test_list.py index 687493faabb..b617d3e39c2 100644 --- a/tests/console/commands/env/test_list.py +++ b/tests/console/commands/env/test_list.py @@ -36,7 +36,7 @@ def test_none_activated( venvs_in_cache_dirs: list[str], mocker: MockerFixture, env: MockEnv, -): +) -> None: mocker.patch("poetry.utils.env.EnvManager.get", return_value=env) tester.execute() expected = "\n".join(venvs_in_cache_dirs) @@ -48,13 +48,15 @@ def test_activated( venvs_in_cache_dirs: list[str], venv_cache: Path, venv_activate_37: None, -): +) -> None: tester.execute() expected = "\n".join(venvs_in_cache_dirs).replace("py3.7", "py3.7 (Activated)") assert tester.io.fetch_output().strip() == expected -def test_in_project_venv(tester: CommandTester, venvs_in_project_dir: list[str]): +def test_in_project_venv( + tester: CommandTester, venvs_in_project_dir: list[str] +) -> None: tester.execute() expected = ".venv (Activated)\n" assert tester.io.fetch_output() == expected @@ -62,7 +64,7 @@ def test_in_project_venv(tester: CommandTester, venvs_in_project_dir: list[str]) def test_in_project_venv_no_explicit_config( tester: CommandTester, venvs_in_project_dir_none: list[str] -): +) -> None: tester.execute() expected = ".venv (Activated)\n" assert tester.io.fetch_output() == expected @@ -70,7 +72,7 @@ def test_in_project_venv_no_explicit_config( def test_in_project_venv_is_false( tester: CommandTester, venvs_in_project_dir_false: list[str] -): +) -> None: tester.execute() expected = "" assert tester.io.fetch_output() == expected diff --git a/tests/console/commands/env/test_remove.py b/tests/console/commands/env/test_remove.py index 428d4f13dfd..9586be90ea1 100644 --- a/tests/console/commands/env/test_remove.py +++ b/tests/console/commands/env/test_remove.py @@ -29,7 +29,7 @@ def test_remove_by_python_version( venvs_in_cache_dirs: list[str], venv_name: str, venv_cache: Path, -): +) -> None: check_output = mocker.patch( "subprocess.check_output", side_effect=check_output_wrapper(Version.parse("3.6.6")), @@ -49,7 +49,7 @@ def test_remove_by_name( venvs_in_cache_dirs: list[str], venv_name: str, venv_cache: Path, -): +) -> None: expected = "" for name in venvs_in_cache_dirs: @@ -67,7 +67,7 @@ def test_remove_all( venvs_in_cache_dirs: list[str], venv_name: str, venv_cache: Path, -): +) -> None: expected = {""} tester.execute("--all") for name in venvs_in_cache_dirs: @@ -81,7 +81,7 @@ def test_remove_all_and_version( venvs_in_cache_dirs: list[str], venv_name: str, venv_cache: Path, -): +) -> None: expected = {""} tester.execute(f"--all {venvs_in_cache_dirs[0]}") for name in venvs_in_cache_dirs: @@ -95,7 +95,7 @@ def test_remove_multiple( venvs_in_cache_dirs: list[str], venv_name: str, venv_cache: Path, -): +) -> None: expected = {""} removed_envs = venvs_in_cache_dirs[0:2] remaining_envs = venvs_in_cache_dirs[2:] diff --git a/tests/console/commands/self/test_add_plugins.py b/tests/console/commands/self/test_add_plugins.py index e7bd3c1707f..981a3fe8db8 100644 --- a/tests/console/commands/self/test_add_plugins.py +++ b/tests/console/commands/self/test_add_plugins.py @@ -38,7 +38,7 @@ def assert_plugin_add_result( def test_add_no_constraint( tester: CommandTester, repo: TestRepository, -): +) -> None: repo.add_package(Package("poetry-plugin", "0.1.0")) tester.execute("poetry-plugin") @@ -61,7 +61,7 @@ def test_add_no_constraint( def test_add_with_constraint( tester: CommandTester, repo: TestRepository, -): +) -> None: repo.add_package(Package("poetry-plugin", "0.1.0")) repo.add_package(Package("poetry-plugin", "0.2.0")) @@ -84,7 +84,7 @@ def test_add_with_constraint( def test_add_with_git_constraint( tester: CommandTester, repo: TestRepository, -): +) -> None: repo.add_package(Package("pendulum", "2.0.5")) tester.execute("git+https://github.com/demo/poetry-plugin.git") @@ -109,7 +109,7 @@ def test_add_with_git_constraint( def test_add_with_git_constraint_with_extras( tester: CommandTester, repo: TestRepository, -): +) -> None: repo.add_package(Package("pendulum", "2.0.5")) repo.add_package(Package("tomlkit", "0.7.0")) @@ -153,7 +153,7 @@ def test_add_with_git_constraint_with_subdirectory( rev: str | None, tester: CommandTester, repo: TestRepository, -): +) -> None: repo.add_package(Package("pendulum", "2.0.5")) tester.execute(url) @@ -189,7 +189,7 @@ def test_add_existing_plugin_warns_about_no_operation( tester: CommandTester, repo: TestRepository, installed: TestRepository, -): +) -> None: pyproject = SelfCommand.get_default_system_pyproject_file() with open(pyproject, "w", encoding="utf-8", newline="") as f: f.write( @@ -230,7 +230,7 @@ def test_add_existing_plugin_updates_if_requested( tester: CommandTester, repo: TestRepository, installed: TestRepository, -): +) -> None: pyproject = SelfCommand.get_default_system_pyproject_file() with open(pyproject, "w", encoding="utf-8", newline="") as f: f.write( @@ -276,7 +276,7 @@ def test_adding_a_plugin_can_update_poetry_dependencies_if_needed( tester: CommandTester, repo: TestRepository, installed: TestRepository, -): +) -> None: poetry_package = Package("poetry", "1.2.0") poetry_package.add_dependency(Factory.create_dependency("tomlkit", "^0.7.0")) diff --git a/tests/console/commands/self/test_remove_plugins.py b/tests/console/commands/self/test_remove_plugins.py index 17f24df5708..9d1dfbaeae3 100644 --- a/tests/console/commands/self/test_remove_plugins.py +++ b/tests/console/commands/self/test_remove_plugins.py @@ -66,7 +66,7 @@ def install_plugin(installed: Repository) -> None: installed.add_package(plugin) -def test_remove_installed_package(tester: CommandTester): +def test_remove_installed_package(tester: CommandTester) -> None: tester.execute("poetry-plugin") expected = """\ @@ -87,7 +87,7 @@ def test_remove_installed_package(tester: CommandTester): assert not dependencies -def test_remove_installed_package_dry_run(tester: CommandTester): +def test_remove_installed_package_dry_run(tester: CommandTester) -> None: tester.execute("poetry-plugin --dry-run") expected = f"""\ diff --git a/tests/console/commands/self/test_show_plugins.py b/tests/console/commands/self/test_show_plugins.py index c2a93bc632a..410473f9133 100644 --- a/tests/console/commands/self/test_show_plugins.py +++ b/tests/console/commands/self/test_show_plugins.py @@ -153,7 +153,7 @@ def mock_metadata_entry_points( def test_show_displays_installed_plugins( app: PoetryTestApplication, tester: CommandTester, -): +) -> None: tester.execute("") expected = """ @@ -179,7 +179,7 @@ def test_show_displays_installed_plugins( def test_show_displays_installed_plugins_with_multiple_plugins( app: PoetryTestApplication, tester: CommandTester, -): +) -> None: tester.execute("") expected = """ @@ -205,7 +205,7 @@ def test_show_displays_installed_plugins_with_multiple_plugins( def test_show_displays_installed_plugins_with_dependencies( app: PoetryTestApplication, tester: CommandTester, -): +) -> None: tester.execute("") expected = """ diff --git a/tests/console/commands/test_about.py b/tests/console/commands/test_about.py index 8f6902c35b3..4368acfd094 100644 --- a/tests/console/commands/test_about.py +++ b/tests/console/commands/test_about.py @@ -16,7 +16,7 @@ def tester(command_tester_factory: CommandTesterFactory) -> CommandTester: return command_tester_factory("about") -def test_about(tester: CommandTester): +def test_about(tester: CommandTester) -> None: from poetry.utils._compat import metadata tester.execute() diff --git a/tests/console/commands/test_add.py b/tests/console/commands/test_add.py index 706aa637edc..8daf01ea9d9 100644 --- a/tests/console/commands/test_add.py +++ b/tests/console/commands/test_add.py @@ -74,7 +74,7 @@ def old_tester(tester: CommandTester) -> CommandTester: def test_add_no_constraint( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester -): +) -> None: repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.2.0")) @@ -104,7 +104,7 @@ def test_add_no_constraint( def test_add_replace_by_constraint( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester -): +) -> None: repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.2.0")) @@ -151,7 +151,7 @@ def test_add_replace_by_constraint( def test_add_no_constraint_editable_error( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester -): +) -> None: content = app.poetry.file.read()["tool"]["poetry"] repo.add_package(get_package("cachy", "0.2.0")) @@ -172,7 +172,7 @@ def test_add_no_constraint_editable_error( def test_add_equal_constraint( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester -): +) -> None: repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.2.0")) @@ -196,7 +196,7 @@ def test_add_equal_constraint( def test_add_greater_constraint( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester -): +) -> None: repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.2.0")) @@ -224,7 +224,7 @@ def test_add_constraint_with_extras( repo: TestRepository, tester: CommandTester, extra_name: str, -): +) -> None: cachy1 = get_package("cachy", "0.1.0") cachy1.extras = {"msgpack": [get_dependency("msgpack-python")]} msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6", optional=True) @@ -255,7 +255,7 @@ def test_add_constraint_with_extras( def test_add_constraint_dependencies( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester -): +) -> None: cachy2 = get_package("cachy", "0.2.0") msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6") cachy2.add_dependency(msgpack_dep) @@ -288,7 +288,7 @@ def test_add_git_constraint( repo: TestRepository, tester: CommandTester, tmp_venv: VirtualEnv, -): +) -> None: tester.command.set_env(tmp_venv) repo.add_package(get_package("pendulum", "1.4.4")) @@ -325,7 +325,7 @@ def test_add_git_constraint_with_poetry( repo: TestRepository, tester: CommandTester, tmp_venv: VirtualEnv, -): +) -> None: tester.command.set_env(tmp_venv) repo.add_package(get_package("pendulum", "1.4.4")) @@ -356,7 +356,7 @@ def test_add_git_constraint_with_extras( tester: CommandTester, tmp_venv: VirtualEnv, extra_name: str, -): +) -> None: tester.command.set_env(tmp_venv) repo.add_package(get_package("pendulum", "1.4.4")) @@ -409,7 +409,7 @@ def test_add_git_constraint_with_subdirectory( repo: TestRepository, tester: CommandTester, env: MockEnv, -): +) -> None: tester.execute(url) expected = """\ @@ -446,7 +446,7 @@ def test_add_git_ssh_constraint( repo: TestRepository, tester: CommandTester, tmp_venv: VirtualEnv, -): +) -> None: tester.command.set_env(tmp_venv) repo.add_package(get_package("pendulum", "1.4.4")) @@ -495,7 +495,7 @@ def test_add_directory_constraint( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester, -): +) -> None: repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("cleo", "0.6.5")) @@ -537,7 +537,7 @@ def test_add_directory_with_poetry( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester, -): +) -> None: repo.add_package(get_package("pendulum", "1.4.4")) path = "../git/github.com/demo/pyproject-demo" @@ -569,7 +569,7 @@ def test_add_file_constraint_wheel( repo: TestRepository, tester: CommandTester, poetry: Poetry, -): +) -> None: repo.add_package(get_package("pendulum", "1.4.4")) path = "../distributions/demo-0.1.0-py2.py3-none-any.whl" @@ -605,7 +605,7 @@ def test_add_file_constraint_sdist( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester, -): +) -> None: repo.add_package(get_package("pendulum", "1.4.4")) path = "../distributions/demo-0.1.0.tar.gz" @@ -639,7 +639,7 @@ def test_add_constraint_with_extras_option( repo: TestRepository, tester: CommandTester, extra_name: str, -): +) -> None: cachy2 = get_package("cachy", "0.2.0") cachy2.extras = {"msgpack": [get_dependency("msgpack-python")]} msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6", optional=True) @@ -681,7 +681,7 @@ def test_add_url_constraint_wheel( repo: TestRepository, tester: CommandTester, mocker: MockerFixture, -): +) -> None: p = mocker.patch("pathlib.Path.cwd") p.return_value = Path(__file__) / ".." @@ -723,7 +723,7 @@ def test_add_url_constraint_wheel_with_extras( tester: CommandTester, extra_name: str, mocker: MockerFixture, -): +) -> None: repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("cleo", "0.6.5")) repo.add_package(get_package("tomlkit", "0.5.5")) @@ -767,7 +767,7 @@ def test_add_url_constraint_wheel_with_extras( def test_add_constraint_with_python( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester -): +) -> None: cachy2 = get_package("cachy", "0.2.0") repo.add_package(get_package("cachy", "0.1.0")) @@ -801,7 +801,7 @@ def test_add_constraint_with_platform( repo: TestRepository, tester: CommandTester, env: MockEnv, -): +) -> None: platform = sys.platform env._platform = platform env._marker_env = None @@ -842,7 +842,7 @@ def test_add_constraint_with_source( poetry: Poetry, tester: CommandTester, mocker: MockerFixture, -): +) -> None: repo = LegacyRepository(name="my-index", url="https://my-index.fake") repo.add_package(get_package("cachy", "0.2.0")) mocker.patch.object( @@ -890,7 +890,7 @@ def test_add_constraint_with_source( def test_add_constraint_with_source_that_does_not_exist( app: PoetryTestApplication, tester: CommandTester -): +) -> None: with pytest.raises(IndexError) as e: tester.execute("foo --source i-dont-exist") @@ -902,7 +902,7 @@ def test_add_constraint_not_found_with_source( poetry: Poetry, mocker: MockerFixture, tester: CommandTester, -): +) -> None: repo = LegacyRepository(name="my-index", url="https://my-index.fake") mocker.patch.object(repo, "find_packages", return_value=[]) @@ -919,7 +919,7 @@ def test_add_constraint_not_found_with_source( def test_add_to_section_that_does_not_exist_yet( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester -): +) -> None: repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.2.0")) @@ -962,7 +962,7 @@ def test_add_to_section_that_does_not_exist_yet( def test_add_to_dev_section_deprecated( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester -): +) -> None: repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.2.0")) @@ -997,7 +997,7 @@ def test_add_to_dev_section_deprecated( def test_add_should_not_select_prereleases( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester -): +) -> None: repo.add_package(get_package("pyyaml", "3.13")) repo.add_package(get_package("pyyaml", "4.2b2")) @@ -1027,7 +1027,7 @@ def test_add_should_not_select_prereleases( def test_add_should_skip_when_adding_existing_package_with_no_constraint( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester -): +) -> None: content = app.poetry.file.read() content["tool"]["poetry"]["dependencies"]["foo"] = "^1.0" app.poetry.file.write(content) @@ -1051,7 +1051,7 @@ def test_add_should_skip_when_adding_existing_package_with_no_constraint( def test_add_should_skip_when_adding_canonicalized_existing_package_with_no_constraint( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester -): +) -> None: content = app.poetry.file.read() content["tool"]["poetry"]["dependencies"]["foo-bar"] = "^1.0" app.poetry.file.write(content) @@ -1077,7 +1077,7 @@ def test_add_latest_should_not_create_duplicate_keys( project_factory: ProjectFactory, repo: TestRepository, command_tester_factory: CommandTesterFactory, -): +) -> None: pyproject_content = """\ [tool.poetry] name = "simple-project" @@ -1113,7 +1113,7 @@ def test_add_latest_should_not_create_duplicate_keys( def test_add_should_work_when_adding_existing_package_with_latest_constraint( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester -): +) -> None: content = app.poetry.file.read() content["tool"]["poetry"]["dependencies"]["foo"] = "^1.0" app.poetry.file.write(content) @@ -1145,7 +1145,7 @@ def test_add_should_work_when_adding_existing_package_with_latest_constraint( def test_add_chooses_prerelease_if_only_prereleases_are_available( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester -): +) -> None: repo.add_package(get_package("foo", "1.2.3b0")) repo.add_package(get_package("foo", "1.2.3b1")) @@ -1168,7 +1168,7 @@ def test_add_chooses_prerelease_if_only_prereleases_are_available( def test_add_prefers_stable_releases( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester -): +) -> None: repo.add_package(get_package("foo", "1.2.3")) repo.add_package(get_package("foo", "1.2.4b1")) @@ -1192,7 +1192,7 @@ def test_add_prefers_stable_releases( def test_add_with_lock( app: PoetryTestApplication, repo: TestRepository, tester: CommandTester -): +) -> None: content_hash = app.poetry.locker._get_content_hash() repo.add_package(get_package("cachy", "0.2.0")) @@ -1216,7 +1216,7 @@ def test_add_no_constraint_old_installer( repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.2.0")) @@ -1250,7 +1250,7 @@ def test_add_equal_constraint_old_installer( repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.2.0")) @@ -1278,7 +1278,7 @@ def test_add_greater_constraint_old_installer( repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.2.0")) @@ -1308,7 +1308,7 @@ def test_add_constraint_with_extras_old_installer( installer: NoopInstaller, old_tester: CommandTester, extra_name: str, -): +) -> None: cachy1 = get_package("cachy", "0.1.0") cachy1.extras = {"msgpack": [get_dependency("msgpack-python")]} msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6", optional=True) @@ -1343,7 +1343,7 @@ def test_add_constraint_dependencies_old_installer( repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: cachy2 = get_package("cachy", "0.2.0") msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6") cachy2.add_dependency(msgpack_dep) @@ -1377,7 +1377,7 @@ def test_add_git_constraint_old_installer( repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("cleo", "0.6.5")) @@ -1413,7 +1413,7 @@ def test_add_git_constraint_with_poetry_old_installer( repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: repo.add_package(get_package("pendulum", "1.4.4")) old_tester.execute("git+https://github.com/demo/pyproject-demo.git") @@ -1443,7 +1443,7 @@ def test_add_git_constraint_with_extras_old_installer( installer: NoopInstaller, old_tester: CommandTester, extra_name: str, -): +) -> None: repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("cleo", "0.6.5")) repo.add_package(get_package("tomlkit", "0.5.5")) @@ -1483,7 +1483,7 @@ def test_add_git_ssh_constraint_old_installer( repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("cleo", "0.6.5")) @@ -1524,7 +1524,7 @@ def test_add_directory_constraint_old_installer( repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("cleo", "0.6.5")) @@ -1563,7 +1563,7 @@ def test_add_directory_with_poetry_old_installer( repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: repo.add_package(get_package("pendulum", "1.4.4")) path = "../git/github.com/demo/pyproject-demo" @@ -1596,7 +1596,7 @@ def test_add_file_constraint_wheel_old_installer( repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: repo.add_package(get_package("pendulum", "1.4.4")) path = "../distributions/demo-0.1.0-py2.py3-none-any.whl" @@ -1634,7 +1634,7 @@ def test_add_file_constraint_sdist_old_installer( repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: repo.add_package(get_package("pendulum", "1.4.4")) path = "../distributions/demo-0.1.0.tar.gz" @@ -1670,7 +1670,7 @@ def test_add_constraint_with_extras_option_old_installer( installer: NoopInstaller, old_tester: CommandTester, extra_name: str, -): +) -> None: cachy2 = get_package("cachy", "0.2.0") cachy2.extras = {"msgpack": [get_dependency("msgpack-python")]} msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6", optional=True) @@ -1714,7 +1714,7 @@ def test_add_url_constraint_wheel_old_installer( installer: NoopInstaller, mocker: MockerFixture, old_tester: CommandTester, -): +) -> None: p = mocker.patch("pathlib.Path.cwd") p.return_value = Path(__file__) / ".." @@ -1757,7 +1757,7 @@ def test_add_url_constraint_wheel_with_extras_old_installer( installer: NoopInstaller, old_tester: CommandTester, extra_name: str, -): +) -> None: repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("cleo", "0.6.5")) repo.add_package(get_package("tomlkit", "0.5.5")) @@ -1803,7 +1803,7 @@ def test_add_constraint_with_python_old_installer( repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: cachy2 = get_package("cachy", "0.2.0") repo.add_package(get_package("cachy", "0.1.0")) @@ -1839,7 +1839,7 @@ def test_add_constraint_with_platform_old_installer( installer: NoopInstaller, env: MockEnv, old_tester: CommandTester, -): +) -> None: platform = sys.platform env._platform = platform env._marker_env = None @@ -1882,7 +1882,7 @@ def test_add_constraint_with_source_old_installer( installer: NoopInstaller, old_tester: CommandTester, mocker: MockerFixture, -): +) -> None: repo = LegacyRepository(name="my-index", url="https://my-index.fake") repo.add_package(get_package("cachy", "0.2.0")) mocker.patch.object( @@ -1931,7 +1931,7 @@ def test_add_constraint_with_source_old_installer( def test_add_constraint_with_source_that_does_not_exist_old_installer( app: PoetryTestApplication, old_tester: CommandTester -): +) -> None: with pytest.raises(IndexError) as e: old_tester.execute("foo --source i-dont-exist") @@ -1943,7 +1943,7 @@ def test_add_constraint_not_found_with_source_old_installer( poetry: Poetry, mocker: MockerFixture, old_tester: CommandTester, -): +) -> None: repo = LegacyRepository(name="my-index", url="https://my-index.fake") mocker.patch.object(repo, "find_packages", return_value=[]) @@ -1963,7 +1963,7 @@ def test_add_to_section_that_does_no_exist_yet_old_installer( repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.2.0")) @@ -1997,7 +1997,7 @@ def test_add_should_not_select_prereleases_old_installer( repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: repo.add_package(get_package("pyyaml", "3.13")) repo.add_package(get_package("pyyaml", "4.2b2")) @@ -2031,7 +2031,7 @@ def test_add_should_skip_when_adding_existing_package_with_no_constraint_old_ins repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: content = app.poetry.file.read() content["tool"]["poetry"]["dependencies"]["foo"] = "^1.0" app.poetry.file.write(content) @@ -2059,7 +2059,7 @@ def test_add_should_work_when_adding_existing_package_with_latest_constraint_old repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: content = app.poetry.file.read() content["tool"]["poetry"]["dependencies"]["foo"] = "^1.0" app.poetry.file.write(content) @@ -2094,7 +2094,7 @@ def test_add_chooses_prerelease_if_only_prereleases_are_available_old_installer( repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: repo.add_package(get_package("foo", "1.2.3b0")) repo.add_package(get_package("foo", "1.2.3b1")) @@ -2120,7 +2120,7 @@ def test_add_preferes_stable_releases_old_installer( repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: repo.add_package(get_package("foo", "1.2.3")) repo.add_package(get_package("foo", "1.2.4b1")) @@ -2147,7 +2147,7 @@ def test_add_with_lock_old_installer( repo: TestRepository, installer: NoopInstaller, old_tester: CommandTester, -): +) -> None: repo.add_package(get_package("cachy", "0.2.0")) old_tester.execute("cachy --lock") @@ -2169,7 +2169,7 @@ def test_add_keyboard_interrupt_restore_content( repo: TestRepository, command_tester_factory: CommandTesterFactory, mocker: MockerFixture, -): +) -> None: tester = command_tester_factory("add", poetry=poetry_with_up_to_date_lockfile) mocker.patch( @@ -2202,7 +2202,7 @@ def test_add_with_dry_run_keep_files_intact( poetry_with_up_to_date_lockfile: Poetry, repo: TestRepository, command_tester_factory: CommandTesterFactory, -): +) -> None: tester = command_tester_factory("add", poetry=poetry_with_up_to_date_lockfile) original_pyproject_content = poetry_with_up_to_date_lockfile.file.read() @@ -2224,7 +2224,7 @@ def test_add_should_not_change_lock_file_when_dependency_installation_fail( repo: TestRepository, command_tester_factory: CommandTesterFactory, mocker: MockerFixture, -): +) -> None: tester = command_tester_factory("add", poetry=poetry_with_up_to_date_lockfile) repo.add_package(get_package("docker", "4.3.1")) diff --git a/tests/console/commands/test_config.py b/tests/console/commands/test_config.py index 975132f4bc0..063200edbf8 100644 --- a/tests/console/commands/test_config.py +++ b/tests/console/commands/test_config.py @@ -34,7 +34,7 @@ def tester(command_tester_factory: CommandTesterFactory) -> CommandTester: def test_show_config_with_local_config_file_empty( tester: CommandTester, mocker: MockerFixture -): +) -> None: mocker.patch( "poetry.factory.Factory.create_poetry", side_effect=PyProjectException("[tool.poetry] section not found"), @@ -46,7 +46,7 @@ def test_show_config_with_local_config_file_empty( def test_list_displays_default_value_if_not_set( tester: CommandTester, config: Config, config_cache_dir: Path -): +) -> None: tester.execute("--list") cache_dir = json.dumps(str(config_cache_dir)) @@ -74,7 +74,7 @@ def test_list_displays_default_value_if_not_set( def test_list_displays_set_get_setting( tester: CommandTester, config: Config, config_cache_dir: Path -): +) -> None: tester.execute("virtualenvs.create false") tester.execute("--list") @@ -103,7 +103,7 @@ def test_list_displays_set_get_setting( assert tester.io.fetch_output() == expected -def test_display_single_setting(tester: CommandTester, config: Config): +def test_display_single_setting(tester: CommandTester, config: Config) -> None: tester.execute("virtualenvs.create") expected = """true @@ -114,7 +114,7 @@ def test_display_single_setting(tester: CommandTester, config: Config): def test_display_single_local_setting( command_tester_factory: CommandTesterFactory, fixture_dir: FixtureDirGetter -): +) -> None: tester = command_tester_factory( "config", poetry=Factory().create_poetry(fixture_dir("with_local_config")) ) @@ -128,7 +128,7 @@ def test_display_single_local_setting( def test_list_displays_set_get_local_setting( tester: CommandTester, config: Config, config_cache_dir: Path -): +) -> None: tester.execute("virtualenvs.create false --local") tester.execute("--list") @@ -163,7 +163,7 @@ def test_list_must_not_display_sources_from_pyproject_toml( command_tester_factory: CommandTesterFactory, config: Config, config_cache_dir: Path, -): +) -> None: source = fixture_dir("with_non_default_source_implicit") pyproject_content = (source / "pyproject.toml").read_text(encoding="utf-8") poetry = project_factory("foo", pyproject_content=pyproject_content) @@ -195,7 +195,9 @@ def test_list_must_not_display_sources_from_pyproject_toml( assert tester.io.fetch_output() == expected -def test_set_pypi_token(tester: CommandTester, auth_config_source: DictConfigSource): +def test_set_pypi_token( + tester: CommandTester, auth_config_source: DictConfigSource +) -> None: tester.execute("pypi-token.pypi mytoken") tester.execute("--list") @@ -206,7 +208,7 @@ def test_set_client_cert( tester: CommandTester, auth_config_source: DictConfigSource, mocker: MockerFixture, -): +) -> None: mocker.spy(ConfigSource, "__init__") tester.execute("certificates.foo.client-cert path/to/cert.pem") @@ -231,7 +233,7 @@ def test_set_cert( mocker: MockerFixture, value: str, result: str | bool, -): +) -> None: mocker.spy(ConfigSource, "__init__") tester.execute(f"certificates.foo.cert {value}") @@ -241,7 +243,7 @@ def test_set_cert( def test_config_installer_parallel( tester: CommandTester, command_tester_factory: CommandTesterFactory -): +) -> None: tester.execute("--local installer.parallel") assert tester.io.fetch_output().strip() == "true" diff --git a/tests/console/commands/test_init.py b/tests/console/commands/test_init.py index e3da0a989a6..b4bed706b99 100644 --- a/tests/console/commands/test_init.py +++ b/tests/console/commands/test_init.py @@ -95,7 +95,7 @@ def init_basic_toml() -> str: def test_basic_interactive( tester: CommandTester, init_basic_inputs: str, init_basic_toml: str -): +) -> None: tester.execute(inputs=init_basic_inputs) assert init_basic_toml in tester.io.fetch_output() @@ -106,7 +106,7 @@ def test_noninteractive( poetry: Poetry, repo: TestRepository, tmp_path: Path, -): +) -> None: command = app.find("init") command._pool = poetry.pool @@ -128,7 +128,9 @@ def test_noninteractive( assert 'pytest = "^3.6.0"' in toml_content -def test_interactive_with_dependencies(tester: CommandTester, repo: TestRepository): +def test_interactive_with_dependencies( + tester: CommandTester, repo: TestRepository +) -> None: repo.add_package(get_package("django-pendulum", "0.1.6-pre4")) repo.add_package(get_package("pendulum", "2.0.0")) repo.add_package(get_package("pytest", "3.6.0")) @@ -183,7 +185,7 @@ def test_interactive_with_dependencies(tester: CommandTester, repo: TestReposito # Regression test for https://github.com/python-poetry/poetry/issues/2355 def test_interactive_with_dependencies_and_no_selection( tester: CommandTester, repo: TestRepository -): +) -> None: repo.add_package(get_package("django-pendulum", "0.1.6-pre4")) repo.add_package(get_package("pendulum", "2.0.0")) repo.add_package(get_package("pytest", "3.6.0")) @@ -224,7 +226,7 @@ def test_interactive_with_dependencies_and_no_selection( assert expected in tester.io.fetch_output() -def test_empty_license(tester: CommandTester): +def test_empty_license(tester: CommandTester) -> None: inputs = [ "my-package", # Package name "1.2.3", # Version @@ -254,7 +256,9 @@ def test_empty_license(tester: CommandTester): assert expected in tester.io.fetch_output() -def test_interactive_with_git_dependencies(tester: CommandTester, repo: TestRepository): +def test_interactive_with_git_dependencies( + tester: CommandTester, repo: TestRepository +) -> None: repo.add_package(get_package("pendulum", "2.0.0")) repo.add_package(get_package("pytest", "3.6.0")) @@ -332,7 +336,7 @@ def test_generate_choice_list( tester: CommandTester, package_name: str, _generate_choice_list_packages: list[Package], -): +) -> None: init_command = tester.command packages = _generate_choice_list_packages @@ -345,7 +349,7 @@ def test_generate_choice_list( def test_interactive_with_git_dependencies_with_reference( tester: CommandTester, repo: TestRepository -): +) -> None: repo.add_package(get_package("pendulum", "2.0.0")) repo.add_package(get_package("pytest", "3.6.0")) @@ -391,7 +395,7 @@ def test_interactive_with_git_dependencies_with_reference( def test_interactive_with_git_dependencies_and_other_name( tester: CommandTester, repo: TestRepository -): +) -> None: repo.add_package(get_package("pendulum", "2.0.0")) repo.add_package(get_package("pytest", "3.6.0")) @@ -440,7 +444,7 @@ def test_interactive_with_directory_dependency( repo: TestRepository, source_dir: Path, fixture_dir: FixtureDirGetter, -): +) -> None: repo.add_package(get_package("pendulum", "2.0.0")) repo.add_package(get_package("pytest", "3.6.0")) @@ -491,7 +495,7 @@ def test_interactive_with_directory_dependency_and_other_name( repo: TestRepository, source_dir: Path, fixture_dir: FixtureDirGetter, -): +) -> None: repo.add_package(get_package("pendulum", "2.0.0")) repo.add_package(get_package("pytest", "3.6.0")) @@ -543,7 +547,7 @@ def test_interactive_with_file_dependency( repo: TestRepository, source_dir: Path, fixture_dir: FixtureDirGetter, -): +) -> None: repo.add_package(get_package("pendulum", "2.0.0")) repo.add_package(get_package("pytest", "3.6.0")) @@ -592,7 +596,7 @@ def test_interactive_with_file_dependency( def test_interactive_with_wrong_dependency_inputs( tester: CommandTester, repo: TestRepository -): +) -> None: inputs = [ "my-package", # Package name "1.2.3", # Version @@ -636,7 +640,7 @@ def test_interactive_with_wrong_dependency_inputs( assert expected in tester.io.fetch_output() -def test_python_option(tester: CommandTester): +def test_python_option(tester: CommandTester) -> None: inputs = [ "my-package", # Package name "1.2.3", # Version @@ -666,7 +670,7 @@ def test_python_option(tester: CommandTester): assert expected in tester.io.fetch_output() -def test_predefined_dependency(tester: CommandTester, repo: TestRepository): +def test_predefined_dependency(tester: CommandTester, repo: TestRepository) -> None: repo.add_package(get_package("pendulum", "2.0.0")) inputs = [ @@ -702,7 +706,7 @@ def test_predefined_dependency(tester: CommandTester, repo: TestRepository): def test_predefined_and_interactive_dependencies( tester: CommandTester, repo: TestRepository -): +) -> None: repo.add_package(get_package("pendulum", "2.0.0")) repo.add_package(get_package("pyramid", "1.10")) @@ -743,7 +747,7 @@ def test_predefined_and_interactive_dependencies( assert 'pyramid = "^1.10"' in output -def test_predefined_dev_dependency(tester: CommandTester, repo: TestRepository): +def test_predefined_dev_dependency(tester: CommandTester, repo: TestRepository) -> None: repo.add_package(get_package("pytest", "3.6.0")) inputs = [ @@ -782,7 +786,7 @@ def test_predefined_dev_dependency(tester: CommandTester, repo: TestRepository): def test_predefined_and_interactive_dev_dependencies( tester: CommandTester, repo: TestRepository -): +) -> None: repo.add_package(get_package("pytest", "3.6.0")) repo.add_package(get_package("pytest-requests", "0.2.0")) @@ -828,7 +832,7 @@ def test_predefined_and_interactive_dev_dependencies( assert 'pytest = "^3.6.0"' in output -def test_predefined_all_options(tester: CommandTester, repo: TestRepository): +def test_predefined_all_options(tester: CommandTester, repo: TestRepository) -> None: repo.add_package(get_package("pendulum", "2.0.0")) repo.add_package(get_package("pytest", "3.6.0")) @@ -875,7 +879,7 @@ def test_predefined_all_options(tester: CommandTester, repo: TestRepository): assert expected in output -def test_add_package_with_extras_and_whitespace(tester: CommandTester): +def test_add_package_with_extras_and_whitespace(tester: CommandTester) -> None: result = tester.command._parse_requirements(["databases[postgresql, sqlite]"]) assert result[0]["name"] == "databases" @@ -889,7 +893,7 @@ def test_init_existing_pyproject_simple( source_dir: Path, init_basic_inputs: str, init_basic_toml: str, -): +) -> None: pyproject_file = source_dir / "pyproject.toml" existing_section = """ [tool.black] @@ -907,7 +911,7 @@ def test_init_existing_pyproject_consistent_linesep( init_basic_inputs: str, init_basic_toml: str, linesep: str, -): +) -> None: pyproject_file = source_dir / "pyproject.toml" existing_section = """ [tool.black] @@ -929,7 +933,7 @@ def test_init_non_interactive_existing_pyproject_add_dependency( source_dir: Path, init_basic_inputs: str, repo: TestRepository, -): +) -> None: pyproject_file = source_dir / "pyproject.toml" existing_section = """ [tool.black] @@ -967,7 +971,7 @@ def test_init_non_interactive_existing_pyproject_add_dependency( def test_init_existing_pyproject_with_build_system_fails( tester: CommandTester, source_dir: Path, init_basic_inputs: str -): +) -> None: pyproject_file = source_dir / "pyproject.toml" existing_section = """ [build-system] @@ -997,14 +1001,14 @@ def test_init_existing_pyproject_with_build_system_fails( " foo 2.0 ", ], ) -def test__validate_package_valid(name: str | None): +def test_validate_package_valid(name: str | None) -> None: assert InitCommand._validate_package(name) == name @pytest.mark.parametrize( "name", ["foo bar 2.0", " foo bar 2.0 ", "foo bar foobar 2.0"] ) -def test__validate_package_invalid(name: str): +def test_validate_package_invalid(name: str) -> None: with pytest.raises(ValueError): assert InitCommand._validate_package(name) @@ -1023,7 +1027,7 @@ def test_package_include( tester: CommandTester, package_name: str, include: str | None, -): +) -> None: tester.execute( inputs="\n".join( ( @@ -1074,7 +1078,7 @@ def test_respect_prefer_active_on_init( mocker: MockerFixture, tester: CommandTester, source_dir: Path, -): +) -> None: from poetry.utils.env import GET_PYTHON_VERSION_ONELINER orig_check_output = subprocess.check_output diff --git a/tests/console/commands/test_install.py b/tests/console/commands/test_install.py index a1e9379de49..6f4f407dd63 100644 --- a/tests/console/commands/test_install.py +++ b/tests/console/commands/test_install.py @@ -119,7 +119,7 @@ def test_group_options_are_passed_to_the_installer( with_root: bool, tester: CommandTester, mocker: MockerFixture, -): +) -> None: """ Group options are passed properly to the installer. """ @@ -155,7 +155,7 @@ def test_group_options_are_passed_to_the_installer( def test_sync_option_is_passed_to_the_installer( tester: CommandTester, mocker: MockerFixture -): +) -> None: """ The --sync option is passed properly to the installer. """ @@ -169,7 +169,7 @@ def test_sync_option_is_passed_to_the_installer( @pytest.mark.parametrize("compile", [False, True]) def test_compile_option_is_passed_to_the_installer( tester: CommandTester, mocker: MockerFixture, compile: bool -): +) -> None: """ The --compile option is passed properly to the installer. """ @@ -187,7 +187,7 @@ def test_compile_option_is_passed_to_the_installer( @pytest.mark.parametrize("skip_directory_cli_value", [True, False]) def test_no_directory_is_passed_to_installer( tester: CommandTester, mocker: MockerFixture, skip_directory_cli_value: bool -): +) -> None: """ The --no-directory option is passed to the installer. """ @@ -204,7 +204,7 @@ def test_no_directory_is_passed_to_installer( def test_no_all_extras_doesnt_populate_installer( tester: CommandTester, mocker: MockerFixture -): +) -> None: """ Not passing --all-extras means the installer doesn't see any extras. """ @@ -215,7 +215,9 @@ def test_no_all_extras_doesnt_populate_installer( assert not tester.command.installer._extras -def test_all_extras_populates_installer(tester: CommandTester, mocker: MockerFixture): +def test_all_extras_populates_installer( + tester: CommandTester, mocker: MockerFixture +) -> None: """ The --all-extras option results in extras passed to the installer. """ @@ -229,7 +231,7 @@ def test_all_extras_populates_installer(tester: CommandTester, mocker: MockerFix def test_extras_are_parsed_and_populate_installer( tester: CommandTester, mocker: MockerFixture, -): +) -> None: mocker.patch.object(tester.command.installer, "run", return_value=0) tester.execute('--extras "first second third"') @@ -237,7 +239,9 @@ def test_extras_are_parsed_and_populate_installer( assert tester.command.installer._extras == ["first", "second", "third"] -def test_extras_conflicts_all_extras(tester: CommandTester, mocker: MockerFixture): +def test_extras_conflicts_all_extras( + tester: CommandTester, mocker: MockerFixture +) -> None: """ The --extras doesn't make sense with --all-extras. """ @@ -266,7 +270,7 @@ def test_only_root_conflicts_with_without_only( options: str, tester: CommandTester, mocker: MockerFixture, -): +) -> None: mocker.patch.object(tester.command.installer, "run", return_value=0) tester.execute(f"{options} --only-root") @@ -300,7 +304,7 @@ def test_invalid_groups_with_without_only( options: dict[str, str], valid_groups: set[str], should_raise: bool, -): +) -> None: mocker.patch.object(tester.command.installer, "run", return_value=0) cmd_args = " ".join(f"{flag} {groups}" for (flag, groups) in options.items()) @@ -324,7 +328,7 @@ def test_invalid_groups_with_without_only( def test_remove_untracked_outputs_deprecation_warning( tester: CommandTester, mocker: MockerFixture, -): +) -> None: mocker.patch.object(tester.command.installer, "run", return_value=0) tester.execute("--remove-untracked") @@ -337,7 +341,9 @@ def test_remove_untracked_outputs_deprecation_warning( ) -def test_dry_run_populates_installer(tester: CommandTester, mocker: MockerFixture): +def test_dry_run_populates_installer( + tester: CommandTester, mocker: MockerFixture +) -> None: """ The --dry-run option results in extras passed to the installer. """ @@ -349,7 +355,7 @@ def test_dry_run_populates_installer(tester: CommandTester, mocker: MockerFixtur assert tester.command.installer._dry_run is True -def test_dry_run_does_not_build(tester: CommandTester, mocker: MockerFixture): +def test_dry_run_does_not_build(tester: CommandTester, mocker: MockerFixture) -> None: mocker.patch.object(tester.command.installer, "run", return_value=0) mocked_editable_builder = mocker.patch( "poetry.masonry.builders.editable.EditableBuilder" @@ -360,7 +366,7 @@ def test_dry_run_does_not_build(tester: CommandTester, mocker: MockerFixture): assert mocked_editable_builder.return_value.build.call_count == 0 -def test_install_logs_output(tester: CommandTester, mocker: MockerFixture): +def test_install_logs_output(tester: CommandTester, mocker: MockerFixture) -> None: mocker.patch.object(tester.command.installer, "run", return_value=0) mocker.patch("poetry.masonry.builders.editable.EditableBuilder") @@ -373,7 +379,9 @@ def test_install_logs_output(tester: CommandTester, mocker: MockerFixture): ) -def test_install_logs_output_decorated(tester: CommandTester, mocker: MockerFixture): +def test_install_logs_output_decorated( + tester: CommandTester, mocker: MockerFixture +) -> None: mocker.patch.object(tester.command.installer, "run", return_value=0) mocker.patch("poetry.masonry.builders.editable.EditableBuilder") @@ -402,7 +410,7 @@ def test_install_path_dependency_does_not_exist( fixture_dir: FixtureDirGetter, project: str, options: str, -): +) -> None: poetry = _project_factory(project, project_factory, fixture_dir) poetry.locker.locked(True) tester = command_tester_factory("install", poetry=poetry) diff --git a/tests/console/commands/test_lock.py b/tests/console/commands/test_lock.py index 506f82a1776..7a910caccf7 100644 --- a/tests/console/commands/test_lock.py +++ b/tests/console/commands/test_lock.py @@ -93,7 +93,7 @@ def test_lock_check_outdated( command_tester_factory: CommandTesterFactory, poetry_with_outdated_lockfile: Poetry, http: type[httpretty.httpretty], -): +) -> None: http.disable() locker = Locker( @@ -119,7 +119,7 @@ def test_lock_check_up_to_date( command_tester_factory: CommandTesterFactory, poetry_with_up_to_date_lockfile: Poetry, http: type[httpretty.httpretty], -): +) -> None: http.disable() locker = Locker( @@ -141,7 +141,7 @@ def test_lock_no_update( command_tester_factory: CommandTesterFactory, poetry_with_old_lockfile: Poetry, repo: TestRepository, -): +) -> None: repo.add_package(get_package("sampleproject", "1.3.1")) repo.add_package(get_package("sampleproject", "2.0.0")) @@ -178,7 +178,7 @@ def test_lock_no_update_path_dependencies( command_tester_factory: CommandTesterFactory, poetry_with_nested_path_deps_old_lockfile: Poetry, repo: TestRepository, -): +) -> None: """ The lock file contains a variant of the directory dependency "quix" that does not depend on "sampleproject". Although the version of "quix" has not been changed, @@ -214,7 +214,7 @@ def test_lock_path_dependency_does_not_exist( fixture_dir: FixtureDirGetter, project: str, update: bool, -): +) -> None: poetry = _project_factory(project, project_factory, fixture_dir) locker = Locker( lock=poetry.pyproject.file.path.parent / "poetry.lock", @@ -242,7 +242,7 @@ def test_lock_path_dependency_deleted_from_pyproject( fixture_dir: FixtureDirGetter, project: str, update: bool, -): +) -> None: poetry = _project_factory(project, project_factory, fixture_dir) locker = Locker( lock=poetry.pyproject.file.path.parent / "poetry.lock", diff --git a/tests/console/commands/test_new.py b/tests/console/commands/test_new.py index 2b2cc9d83f2..7dd76f7cea7 100644 --- a/tests/console/commands/test_new.py +++ b/tests/console/commands/test_new.py @@ -156,7 +156,7 @@ def test_command_new( include_from: str | None, tester: CommandTester, tmp_path: Path, -): +) -> None: path = tmp_path / directory options.append(str(path)) tester.execute(" ".join(options)) @@ -166,7 +166,7 @@ def test_command_new( @pytest.mark.parametrize(("fmt",), [(None,), ("md",), ("rst",), ("adoc",), ("creole",)]) def test_command_new_with_readme( fmt: str | None, tester: CommandTester, tmp_path: Path -): +) -> None: package = "package" path = tmp_path / package options = [path.as_posix()] @@ -194,7 +194,7 @@ def test_respect_prefer_active_on_new( mocker: MockerFixture, tester: CommandTester, tmp_path: Path, -): +) -> None: from poetry.utils.env import GET_PYTHON_VERSION_ONELINER orig_check_output = subprocess.check_output diff --git a/tests/console/commands/test_publish.py b/tests/console/commands/test_publish.py index f4241a1a870..5f230c01ff3 100644 --- a/tests/console/commands/test_publish.py +++ b/tests/console/commands/test_publish.py @@ -23,7 +23,7 @@ def test_publish_returns_non_zero_code_for_upload_errors( app: PoetryTestApplication, app_tester: ApplicationTester, http: type[httpretty.httpretty], -): +) -> None: http.register_uri( http.POST, "https://upload.pypi.org/legacy/", status=400, body="Bad Request" ) @@ -48,7 +48,7 @@ def test_publish_returns_non_zero_code_for_connection_errors( app: PoetryTestApplication, app_tester: ApplicationTester, http: type[httpretty.httpretty], -): +) -> None: def request_callback(*_: Any, **__: Any) -> None: raise requests.ConnectionError() @@ -65,7 +65,9 @@ def request_callback(*_: Any, **__: Any) -> None: assert expected in app_tester.io.fetch_error() -def test_publish_with_cert(app_tester: ApplicationTester, mocker: MockerFixture): +def test_publish_with_cert( + app_tester: ApplicationTester, mocker: MockerFixture +) -> None: publisher_publish = mocker.patch("poetry.publishing.Publisher.publish") app_tester.execute("publish --cert path/to/ca.pem") @@ -75,7 +77,9 @@ def test_publish_with_cert(app_tester: ApplicationTester, mocker: MockerFixture) ] == publisher_publish.call_args -def test_publish_with_client_cert(app_tester: ApplicationTester, mocker: MockerFixture): +def test_publish_with_client_cert( + app_tester: ApplicationTester, mocker: MockerFixture +) -> None: publisher_publish = mocker.patch("poetry.publishing.Publisher.publish") app_tester.execute("publish --client-cert path/to/client.pem") @@ -94,7 +98,7 @@ def test_publish_with_client_cert(app_tester: ApplicationTester, mocker: MockerF ) def test_publish_dry_run_skip_existing( app_tester: ApplicationTester, http: type[httpretty.httpretty], options: str -): +) -> None: http.register_uri( http.POST, "https://upload.pypi.org/legacy/", status=409, body="Conflict" ) @@ -113,7 +117,7 @@ def test_publish_dry_run_skip_existing( def test_skip_existing_output( app_tester: ApplicationTester, http: type[httpretty.httpretty] -): +) -> None: http.register_uri( http.POST, "https://upload.pypi.org/legacy/", status=409, body="Conflict" ) diff --git a/tests/console/commands/test_remove.py b/tests/console/commands/test_remove.py index 0a2fa1b6c00..68c16edf065 100644 --- a/tests/console/commands/test_remove.py +++ b/tests/console/commands/test_remove.py @@ -48,7 +48,7 @@ def test_remove_without_specific_group_removes_from_all_groups( repo: TestRepository, command_tester_factory: CommandTesterFactory, installed: Repository, -): +) -> None: """ Removing without specifying a group removes packages from all groups. """ @@ -105,7 +105,7 @@ def test_remove_without_specific_group_removes_from_specific_groups( repo: TestRepository, command_tester_factory: CommandTesterFactory, installed: Repository, -): +) -> None: """ Removing with a specific group given removes packages only from this group. """ @@ -162,7 +162,7 @@ def test_remove_does_not_live_empty_groups( repo: TestRepository, command_tester_factory: CommandTesterFactory, installed: Repository, -): +) -> None: """ Empty groups are automatically discarded after package removal. """ @@ -208,7 +208,7 @@ def test_remove_canonicalized_named_removes_dependency_correctly( repo: TestRepository, command_tester_factory: CommandTesterFactory, installed: Repository, -): +) -> None: """ Removing a dependency using a canonicalized named removes the dependency. """ @@ -267,7 +267,7 @@ def test_remove_command_should_not_write_changes_upon_installer_errors( repo: TestRepository, command_tester_factory: CommandTesterFactory, mocker: MockerFixture, -): +) -> None: repo.add_package(Package("foo", "2.0.0")) command_tester_factory("add").execute("foo") @@ -285,7 +285,7 @@ def test_remove_with_dry_run_keep_files_intact( poetry_with_up_to_date_lockfile: Poetry, repo: TestRepository, command_tester_factory: CommandTesterFactory, -): +) -> None: tester = command_tester_factory("remove", poetry=poetry_with_up_to_date_lockfile) original_pyproject_content = poetry_with_up_to_date_lockfile.file.read() diff --git a/tests/console/commands/test_run.py b/tests/console/commands/test_run.py index 7442ac10f37..53c5dbbb3d3 100644 --- a/tests/console/commands/test_run.py +++ b/tests/console/commands/test_run.py @@ -45,14 +45,14 @@ def poetry_with_scripts( ) -def test_run_passes_all_args(app_tester: ApplicationTester, env: MockEnv): +def test_run_passes_all_args(app_tester: ApplicationTester, env: MockEnv) -> None: app_tester.execute("run python -V") assert [["python", "-V"]] == env.executed def test_run_keeps_options_passed_before_command( app_tester: ApplicationTester, env: MockEnv -): +) -> None: app_tester.execute("-V --no-ansi run python", decorated=True) assert not app_tester.io.is_decorated() @@ -64,7 +64,7 @@ def test_run_keeps_options_passed_before_command( def test_run_has_helpful_error_when_command_not_found( app_tester: ApplicationTester, env: MockEnv, capfd: pytest.CaptureFixture[str] -): +) -> None: nonexistent_command = "nonexistent-command" env._execute = True app_tester.execute(f"run {nonexistent_command}") @@ -94,7 +94,7 @@ def test_run_has_helpful_error_when_command_not_found( def test_run_console_scripts_of_editable_dependencies_on_windows( tmp_venv: VirtualEnv, command_tester_factory: CommandTesterFactory, -): +) -> None: """ On Windows, Poetry installs console scripts of editable dependencies by creating in the environment's `Scripts/` directory both: diff --git a/tests/console/commands/test_shell.py b/tests/console/commands/test_shell.py index abdaec46789..a8d0b8407db 100644 --- a/tests/console/commands/test_shell.py +++ b/tests/console/commands/test_shell.py @@ -20,7 +20,7 @@ def tester(command_tester_factory: CommandTesterFactory) -> CommandTester: return command_tester_factory("shell") -def test_shell(tester: CommandTester, mocker: MockerFixture): +def test_shell(tester: CommandTester, mocker: MockerFixture) -> None: shell_activate = mocker.patch("poetry.utils.shell.Shell.activate") tester.execute() @@ -32,7 +32,7 @@ def test_shell(tester: CommandTester, mocker: MockerFixture): assert tester.status_code == 0 -def test_shell_already_active(tester: CommandTester, mocker: MockerFixture): +def test_shell_already_active(tester: CommandTester, mocker: MockerFixture) -> None: os.environ["POETRY_ACTIVE"] = "1" shell_activate = mocker.patch("poetry.utils.shell.Shell.activate") @@ -71,7 +71,7 @@ def test__is_venv_activated( real_prefix: str | None, prefix: str, expected: bool, -): +) -> None: mocker.patch.object(tester.command.env, "_path", Path("foobar")) mocker.patch("sys.prefix", prefix) diff --git a/tests/console/commands/test_update.py b/tests/console/commands/test_update.py index dd3306cf2ba..cb0f0052ac1 100644 --- a/tests/console/commands/test_update.py +++ b/tests/console/commands/test_update.py @@ -40,7 +40,7 @@ def test_update_with_dry_run_keep_files_intact( poetry_with_up_to_date_lockfile: Poetry, repo: TestRepository, command_tester_factory: CommandTesterFactory, -): +) -> None: tester = command_tester_factory("update", poetry=poetry_with_up_to_date_lockfile) original_pyproject_content = poetry_with_up_to_date_lockfile.file.read() diff --git a/tests/console/commands/test_version.py b/tests/console/commands/test_version.py index d578b7fd918..33d5cb3824f 100644 --- a/tests/console/commands/test_version.py +++ b/tests/console/commands/test_version.py @@ -51,31 +51,31 @@ def tester(command_tester_factory: CommandTesterFactory) -> CommandTester: ) def test_increment_version( version: str, rule: str, expected: str, command: VersionCommand -): +) -> None: assert command.increment_version(version, rule).text == expected -def test_version_show(tester: CommandTester): +def test_version_show(tester: CommandTester) -> None: tester.execute() assert tester.io.fetch_output() == "simple-project 1.2.3\n" -def test_short_version_show(tester: CommandTester): +def test_short_version_show(tester: CommandTester) -> None: tester.execute("--short") assert tester.io.fetch_output() == "1.2.3\n" -def test_version_update(tester: CommandTester): +def test_version_update(tester: CommandTester) -> None: tester.execute("2.0.0") assert tester.io.fetch_output() == "Bumping version from 1.2.3 to 2.0.0\n" -def test_short_version_update(tester: CommandTester): +def test_short_version_update(tester: CommandTester) -> None: tester.execute("--short 2.0.0") assert tester.io.fetch_output() == "2.0.0\n" -def test_dry_run(tester: CommandTester): +def test_dry_run(tester: CommandTester) -> None: old_pyproject = tester.command.poetry.file.path.read_text() tester.execute("--dry-run major") diff --git a/tests/console/test_application.py b/tests/console/test_application.py index 9ed2e2aeb54..7bca231d04a 100644 --- a/tests/console/test_application.py +++ b/tests/console/test_application.py @@ -39,7 +39,7 @@ def with_add_command_plugin(mocker: MockerFixture) -> None: mock_metadata_entry_points(mocker, AddCommandPlugin) -def test_application_with_plugins(with_add_command_plugin: None): +def test_application_with_plugins(with_add_command_plugin: None) -> None: app = Application() tester = ApplicationTester(app) @@ -49,7 +49,7 @@ def test_application_with_plugins(with_add_command_plugin: None): assert tester.status_code == 0 -def test_application_with_plugins_disabled(with_add_command_plugin: None): +def test_application_with_plugins_disabled(with_add_command_plugin: None) -> None: app = Application() tester = ApplicationTester(app) @@ -59,7 +59,7 @@ def test_application_with_plugins_disabled(with_add_command_plugin: None): assert tester.status_code == 0 -def test_application_execute_plugin_command(with_add_command_plugin: None): +def test_application_execute_plugin_command(with_add_command_plugin: None) -> None: app = Application() tester = ApplicationTester(app) @@ -71,7 +71,7 @@ def test_application_execute_plugin_command(with_add_command_plugin: None): def test_application_execute_plugin_command_with_plugins_disabled( with_add_command_plugin: None, -): +) -> None: app = Application() tester = ApplicationTester(app) @@ -83,7 +83,7 @@ def test_application_execute_plugin_command_with_plugins_disabled( @pytest.mark.parametrize("disable_cache", [True, False]) -def test_application_verify_source_cache_flag(disable_cache: bool): +def test_application_verify_source_cache_flag(disable_cache: bool) -> None: app = Application() tester = ApplicationTester(app) diff --git a/tests/installation/test_chef.py b/tests/installation/test_chef.py index c03dfd07c4d..ca00ba01e73 100644 --- a/tests/installation/test_chef.py +++ b/tests/installation/test_chef.py @@ -68,7 +68,7 @@ def test_prepare_directory( config_cache_dir: Path, artifact_cache: ArtifactCache, fixture_dir: FixtureDirGetter, -): +) -> None: chef = Chef( artifact_cache, EnvManager.get_system_env(), Factory.create_pool(config) ) @@ -107,7 +107,7 @@ def test_prepare_directory_editable( config_cache_dir: Path, artifact_cache: ArtifactCache, fixture_dir: FixtureDirGetter, -): +) -> None: chef = Chef( artifact_cache, EnvManager.get_system_env(), Factory.create_pool(config) ) diff --git a/tests/installation/test_executor.py b/tests/installation/test_executor.py index c8a529b40a7..18d5cd1b60c 100644 --- a/tests/installation/test_executor.py +++ b/tests/installation/test_executor.py @@ -203,7 +203,7 @@ def test_execute_executes_a_batch_of_operations( env: MockEnv, copy_wheel: Callable[[], Path], fixture_dir: FixtureDirGetter, -): +) -> None: wheel_install = mocker.patch.object(WheelInstaller, "install") config.merge({"cache-dir": str(tmp_path)}) @@ -314,7 +314,7 @@ def test_execute_prints_warning_for_yanked_package( env: MockEnv, operations: list[Operation], has_warning: bool, -): +) -> None: config.merge({"cache-dir": str(tmp_path)}) executor = Executor(env, pool, config, io) @@ -345,7 +345,7 @@ def test_execute_prints_warning_for_invalid_wheels( tmp_path: Path, mock_file_downloads: None, env: MockEnv, -): +) -> None: config.merge({"cache-dir": str(tmp_path)}) executor = Executor(env, pool, config, io) @@ -404,7 +404,7 @@ def test_execute_shows_skipped_operations_if_verbose( io: BufferedIO, config_cache_dir: Path, env: MockEnv, -): +) -> None: config.merge({"cache-dir": config_cache_dir.as_posix()}) executor = Executor(env, pool, config, io) @@ -432,7 +432,7 @@ def test_execute_should_show_errors( mocker: MockerFixture, io: BufferedIO, env: MockEnv, -): +) -> None: executor = Executor(env, pool, config, io) executor.verbose() @@ -460,7 +460,7 @@ def test_execute_works_with_ansi_output( tmp_path: Path, mock_file_downloads: None, env: MockEnv, -): +) -> None: config.merge({"cache-dir": str(tmp_path)}) executor = Executor(env, pool, config, io_decorated) @@ -497,7 +497,7 @@ def test_execute_works_with_no_ansi_output( tmp_path: Path, mock_file_downloads: None, env: MockEnv, -): +) -> None: config.merge({"cache-dir": str(tmp_path)}) executor = Executor(env, pool, config, io_not_decorated) @@ -525,7 +525,7 @@ def test_execute_should_show_operation_as_cancelled_on_subprocess_keyboard_inter mocker: MockerFixture, io: BufferedIO, env: MockEnv, -): +) -> None: executor = Executor(env, pool, config, io) executor.verbose() @@ -550,7 +550,7 @@ def test_execute_should_gracefully_handle_io_error( mocker: MockerFixture, io: BufferedIO, env: MockEnv, -): +) -> None: executor = Executor(env, pool, config, io) executor.verbose() @@ -584,7 +584,7 @@ def test_executor_should_delete_incomplete_downloads( mock_file_downloads: None, env: MockEnv, fixture_dir: FixtureDirGetter, -): +) -> None: fixture = fixture_dir("distributions") / "demo-0.1.0-py2.py3-none-any.whl" destination_fixture = tmp_path / "tomlkit-0.5.3-py2.py3-none-any.whl" shutil.copyfile(str(fixture), str(destination_fixture)) @@ -613,7 +613,7 @@ def test_executor_should_delete_incomplete_downloads( def verify_installed_distribution( venv: VirtualEnv, package: Package, url_reference: dict[str, Any] | None = None -): +) -> None: distributions = list(venv.site_packages.distributions(name=package.name)) assert len(distributions) == 1 @@ -660,7 +660,7 @@ def test_executor_should_not_write_pep610_url_references_for_cached_package( pool: RepositoryPool, config: Config, io: BufferedIO, -): +) -> None: link_cached = fixture_dir("distributions") / "demo-0.1.0-py2.py3-none-any.whl" package.files = [ { @@ -685,7 +685,7 @@ def test_executor_should_write_pep610_url_references_for_wheel_files( config: Config, io: BufferedIO, fixture_dir: FixtureDirGetter, -): +) -> None: url = (fixture_dir("distributions") / "demo-0.1.0-py2.py3-none-any.whl").resolve() package = Package("demo", "0.1.0", source_type="file", source_url=url.as_posix()) # Set package.files so the executor will attempt to hash the package @@ -718,7 +718,7 @@ def test_executor_should_write_pep610_url_references_for_non_wheel_files( config: Config, io: BufferedIO, fixture_dir: FixtureDirGetter, -): +) -> None: url = (fixture_dir("distributions") / "demo-0.1.0.tar.gz").resolve() package = Package("demo", "0.1.0", source_type="file", source_url=url.as_posix()) # Set package.files so the executor will attempt to hash the package @@ -754,7 +754,7 @@ def test_executor_should_write_pep610_url_references_for_directories( wheel: Path, fixture_dir: FixtureDirGetter, mocker: MockerFixture, -): +) -> None: url = (fixture_dir("git") / "github.com" / "demo" / "demo").resolve() package = Package( "demo", "0.1.2", source_type="directory", source_url=url.as_posix() @@ -782,7 +782,7 @@ def test_executor_should_write_pep610_url_references_for_editable_directories( wheel: Path, fixture_dir: FixtureDirGetter, mocker: MockerFixture, -): +) -> None: url = (fixture_dir("git") / "github.com" / "demo" / "demo").resolve() package = Package( "demo", @@ -815,7 +815,7 @@ def test_executor_should_write_pep610_url_references_for_wheel_urls( mocker: MockerFixture, fixture_dir: FixtureDirGetter, is_artifact_cached: bool, -): +) -> None: if is_artifact_cached: link_cached = fixture_dir("distributions") / "demo-0.1.0-py2.py3-none-any.whl" mocker.patch( @@ -887,7 +887,7 @@ def test_executor_should_write_pep610_url_references_for_non_wheel_urls( is_wheel_cached: bool, expect_artifact_building: bool, expect_artifact_download: bool, -): +) -> None: built_wheel = fixture_dir("distributions") / "demo-0.1.0-py2.py3-none-any.whl" mock_prepare = mocker.patch( "poetry.installation.chef.Chef._prepare", @@ -899,7 +899,9 @@ def test_executor_should_write_pep610_url_references_for_non_wheel_urls( cached_sdist = fixture_dir("distributions") / "demo-0.1.0.tar.gz" cached_wheel = fixture_dir("distributions") / "demo-0.1.0-py2.py3-none-any.whl" - def mock_get_cached_archive_for_link_func(_: Link, *, strict: bool, **__: Any): + def mock_get_cached_archive_for_link_func( + _: Link, *, strict: bool, **__: Any + ) -> None: if is_wheel_cached and not strict: return cached_wheel if is_sdist_cached: @@ -966,7 +968,7 @@ def test_executor_should_write_pep610_url_references_for_git( mocker: MockerFixture, fixture_dir: FixtureDirGetter, is_artifact_cached: bool, -): +) -> None: if is_artifact_cached: link_cached = fixture_dir("distributions") / "demo-0.1.2-py2.py3-none-any.whl" mocker.patch( @@ -1029,7 +1031,7 @@ def test_executor_should_write_pep610_url_references_for_editable_git( wheel: Path, mocker: MockerFixture, fixture_dir: FixtureDirGetter, -): +) -> None: source_resolved_reference = "123456" source_url = "https://github.com/demo/demo.git" @@ -1106,7 +1108,7 @@ def test_executor_should_write_pep610_url_references_for_git_with_subdirectories io: BufferedIO, mock_file_downloads: None, wheel: Path, -): +) -> None: package = Package( "demo", "0.1.2", @@ -1159,7 +1161,7 @@ def test_executor_should_be_initialized_with_correct_workers( cpu_count: int | None, side_effect: Exception | None, expected_workers: int, -): +) -> None: config.merge({"installer": {"max-workers": max_workers}}) mocker.patch("os.cpu_count", return_value=cpu_count, side_effect=side_effect) @@ -1178,7 +1180,7 @@ def test_executor_fallback_on_poetry_create_error_without_wheel_installer( mock_file_downloads: None, env: MockEnv, fixture_dir: FixtureDirGetter, -): +) -> None: mock_pip_install = mocker.patch("poetry.installation.executor.pip_install") mock_sdist_builder = mocker.patch("poetry.core.masonry.builders.sdist.SdistBuilder") mock_editable_builder = mocker.patch( diff --git a/tests/installation/test_installer.py b/tests/installation/test_installer.py index 4d6e975b45a..39e07fb5b06 100644 --- a/tests/installation/test_installer.py +++ b/tests/installation/test_installer.py @@ -76,12 +76,14 @@ def updates(self) -> list[DependencyPackage]: def removals(self) -> list[DependencyPackage]: return self._uninstalls - def _do_execute_operation(self, operation: Operation) -> None: - super()._do_execute_operation(operation) + def _do_execute_operation(self, operation: Operation) -> int: + ret_val = super()._do_execute_operation(operation) if not operation.skipped: getattr(self, f"_{operation.job_type}s").append(operation.package) + return ret_val + def _execute_install(self, operation: Operation) -> int: return 0 @@ -109,7 +111,7 @@ def __init__(self, lock_path: Path) -> None: self._content_hash = self._get_content_hash() @property - def written_data(self) -> dict | None: + def written_data(self) -> dict[str, Any] | None: return self._written_data def set_lock_path(self, lock: Path) -> Locker: @@ -122,7 +124,7 @@ def locked(self, is_locked: bool = True) -> Locker: return self - def mock_lock_data(self, data: dict) -> None: + def mock_lock_data(self, data: dict[str, Any]) -> None: self._lock_data = data def is_locked(self) -> bool: @@ -134,7 +136,7 @@ def is_fresh(self) -> bool: def _get_content_hash(self) -> str: return "123456789" - def _write_lock_data(self, data: dict) -> None: + def _write_lock_data(self, data: dict[str, Any]) -> None: for package in data["package"]: python_versions = str(package["python-versions"]) package["python-versions"] = python_versions @@ -201,13 +203,14 @@ def installer( return installer -def fixture(name: str) -> dict: +def fixture(name: str) -> dict[str, Any]: file = TOMLFile(Path(__file__).parent / "fixtures" / f"{name}.test") + content: dict[str, Any] = file.read() - return json.loads(json.dumps(file.read())) + return content -def test_run_no_dependencies(installer: Installer, locker: Locker): +def test_run_no_dependencies(installer: Installer, locker: Locker) -> None: result = installer.run() assert result == 0 @@ -217,7 +220,7 @@ def test_run_no_dependencies(installer: Installer, locker: Locker): def test_run_with_dependencies( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: package_a = get_package("A", "1.0") package_b = get_package("B", "1.1") repo.add_package(package_a) @@ -239,7 +242,7 @@ def test_run_update_after_removing_dependencies( repo: Repository, package: ProjectPackage, installed: CustomInstalledRepository, -): +) -> None: locker.locked(True) locker.mock_lock_data( { @@ -403,7 +406,7 @@ def test_run_install_with_dependency_groups( repo: Repository, package: ProjectPackage, installed: CustomInstalledRepository, -): +) -> None: _configure_run_install_dev( locker, repo, @@ -431,7 +434,7 @@ def test_run_install_does_not_remove_locked_packages_if_installed_but_not_requir repo: Repository, package: ProjectPackage, installed: CustomInstalledRepository, -): +) -> None: package_a = get_package("a", "1.0") package_b = get_package("b", "1.1") package_c = get_package("c", "1.2") @@ -502,7 +505,7 @@ def test_run_install_removes_locked_packages_if_installed_and_synchronization_is repo: Repository, package: ProjectPackage, installed: CustomInstalledRepository, -): +) -> None: package_a = get_package("a", "1.0") package_b = get_package("b", "1.1") package_c = get_package("c", "1.2") @@ -573,7 +576,7 @@ def test_run_install_removes_no_longer_locked_packages_if_installed( repo: Repository, package: ProjectPackage, installed: CustomInstalledRepository, -): +) -> None: package_a = get_package("a", "1.0") package_b = get_package("b", "1.1") package_c = get_package("c", "1.2") @@ -655,7 +658,7 @@ def test_run_install_with_synchronization( repo: Repository, package: ProjectPackage, installed: CustomInstalledRepository, -): +) -> None: package_a = get_package("a", "1.0") package_b = get_package("b", "1.1") package_c = get_package("c", "1.2") @@ -728,7 +731,7 @@ def test_run_install_with_synchronization( def test_run_whitelist_add( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: locker.locked(True) locker.mock_lock_data( { @@ -777,7 +780,7 @@ def test_run_whitelist_remove( repo: Repository, package: ProjectPackage, installed: CustomInstalledRepository, -): +) -> None: locker.locked(True) locker.mock_lock_data( { @@ -832,7 +835,7 @@ def test_run_whitelist_remove( def test_add_with_sub_dependencies( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: package_a = get_package("A", "1.0") package_b = get_package("B", "1.1") package_c = get_package("C", "1.2") @@ -857,7 +860,7 @@ def test_add_with_sub_dependencies( def test_run_with_python_versions( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: package.python_versions = "~2.7 || ^3.4" package_a = get_package("A", "1.0") @@ -885,7 +888,7 @@ def test_run_with_python_versions( def test_run_with_optional_and_python_restricted_dependencies( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: package.python_versions = "~2.7 || ^3.4" package_a = get_package("A", "1.0") @@ -932,7 +935,7 @@ def test_run_with_optional_and_platform_restricted_dependencies( repo: Repository, package: ProjectPackage, mocker: MockerFixture, -): +) -> None: mocker.patch("sys.platform", "darwin") package_a = get_package("A", "1.0") @@ -975,7 +978,7 @@ def test_run_with_optional_and_platform_restricted_dependencies( def test_run_with_dependencies_extras( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") package_c = get_package("C", "1.0") @@ -1003,7 +1006,7 @@ def test_run_with_dependencies_extras( def test_run_with_dependencies_nested_extras( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") package_c = get_package("C", "1.0") @@ -1035,7 +1038,7 @@ def test_run_with_dependencies_nested_extras( def test_run_does_not_install_extras_if_not_requested( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: package.extras["foo"] = [get_dependency("D")] package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") @@ -1067,7 +1070,7 @@ def test_run_does_not_install_extras_if_not_requested( def test_run_installs_extras_if_requested( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: package.extras["foo"] = [get_dependency("D")] package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") @@ -1100,7 +1103,7 @@ def test_run_installs_extras_if_requested( def test_run_installs_extras_with_deps_if_requested( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: package.extras["foo"] = [get_dependency("C")] package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") @@ -1135,7 +1138,7 @@ def test_run_installs_extras_with_deps_if_requested( def test_run_installs_extras_with_deps_if_requested_locked( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: locker.locked(True) locker.mock_lock_data(fixture("extras-with-dependencies")) package.extras["foo"] = [get_dependency("C")] @@ -1170,7 +1173,7 @@ def test_installer_with_pypi_repository( installed: CustomInstalledRepository, config: Config, env: NullEnv, -): +) -> None: pool = RepositoryPool() pool.add_repository(MockRepository()) @@ -1194,7 +1197,7 @@ def test_run_installs_with_local_file( repo: Repository, package: ProjectPackage, fixture_dir: FixtureDirGetter, -): +) -> None: root_dir = Path(__file__).parent.parent.parent package.root_dir = root_dir locker.set_lock_path(root_dir) @@ -1222,7 +1225,7 @@ def test_run_installs_wheel_with_no_requires_dist( repo: Repository, package: ProjectPackage, fixture_dir: FixtureDirGetter, -): +) -> None: root_dir = Path(__file__).parent.parent.parent package.root_dir = root_dir locker.set_lock_path(root_dir) @@ -1252,7 +1255,7 @@ def test_run_installs_with_local_poetry_directory_and_extras( package: ProjectPackage, tmpdir: Path, fixture_dir: FixtureDirGetter, -): +) -> None: root_dir = Path(__file__).parent.parent.parent package.root_dir = root_dir locker.set_lock_path(root_dir) @@ -1284,7 +1287,7 @@ def test_run_installs_with_local_poetry_directory_and_skip_directory_flag( package: ProjectPackage, fixture_dir: FixtureDirGetter, skip_directory: bool, -): +) -> None: """When we set Installer.skip_directory(True) no path dependencies should be installed (including transitive dependencies). """ @@ -1333,7 +1336,7 @@ def test_run_installs_with_local_poetry_file_transitive( package: ProjectPackage, tmpdir: str, fixture_dir: FixtureDirGetter, -): +) -> None: root_dir = fixture_dir("directory") package.root_dir = root_dir locker.set_lock_path(root_dir) @@ -1368,7 +1371,7 @@ def test_run_installs_with_local_setuptools_directory( package: ProjectPackage, tmpdir: Path, fixture_dir: FixtureDirGetter, -): +) -> None: root_dir = Path(__file__).parent.parent.parent package.root_dir = root_dir locker.set_lock_path(root_dir) @@ -1395,7 +1398,7 @@ def test_run_installs_with_local_setuptools_directory( def test_run_with_prereleases( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: locker.locked(True) locker.mock_lock_data( { @@ -1487,7 +1490,7 @@ def test_run_changes_category_if_needed( def test_run_update_all_with_lock( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: locker.locked(True) locker.mock_lock_data( { @@ -1527,7 +1530,7 @@ def test_run_update_all_with_lock( def test_run_update_with_locked_extras( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: locker.locked(True) locker.mock_lock_data( { @@ -1601,7 +1604,7 @@ def test_run_update_with_locked_extras( def test_run_install_duplicate_dependencies_different_constraints( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -1645,7 +1648,7 @@ def test_run_install_duplicate_dependencies_different_constraints( def test_run_install_duplicate_dependencies_different_constraints_with_lock( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: locker.locked(True) locker.mock_lock_data( { @@ -1757,7 +1760,7 @@ def test_run_update_uninstalls_after_removal_transient_dependency( repo: Repository, package: ProjectPackage, installed: CustomInstalledRepository, -): +) -> None: locker.locked(True) locker.mock_lock_data( { @@ -1820,7 +1823,7 @@ def test_run_install_duplicate_dependencies_different_constraints_with_lock_upda repo: Repository, package: ProjectPackage, installed: CustomInstalledRepository, -): +) -> None: locker.locked(True) locker.mock_lock_data( { @@ -1933,7 +1936,7 @@ def test_installer_test_solver_finds_compatible_package_for_dependency_python_no repo: Repository, package: ProjectPackage, installed: CustomInstalledRepository, -): +) -> None: package.python_versions = "~2.7 || ^3.4" package.add_dependency( Factory.create_dependency("A", {"version": "^1.0", "python": "^3.5"}) @@ -1965,7 +1968,7 @@ def test_installer_required_extras_should_not_be_removed_when_updating_single_de env: NullEnv, pool: RepositoryPool, config: Config, -): +) -> None: package.add_dependency(Factory.create_dependency("A", {"version": "^1.0"})) package_a = get_package("A", "1.0.0") @@ -2031,7 +2034,7 @@ def test_installer_required_extras_should_not_be_removed_when_updating_single_de env: NullEnv, mocker: MockerFixture, config: Config, -): +) -> None: mocker.patch("sys.platform", "darwin") pool = RepositoryPool() @@ -2093,7 +2096,7 @@ def test_installer_required_extras_should_be_installed( installed: CustomInstalledRepository, env: NullEnv, config: Config, -): +) -> None: pool = RepositoryPool() pool.add_repository(MockRepository()) @@ -2145,7 +2148,7 @@ def test_installer_required_extras_should_be_installed( def test_update_multiple_times_with_split_dependencies_is_idempotent( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: locker.locked(True) locker.mock_lock_data( { @@ -2231,7 +2234,7 @@ def test_installer_can_install_dependencies_from_forced_source( installed: CustomInstalledRepository, env: NullEnv, config: Config, -): +) -> None: package.python_versions = "^3.7" package.add_dependency( Factory.create_dependency("tomlkit", {"version": "^0.5", "source": "legacy"}) @@ -2262,7 +2265,7 @@ def test_installer_can_install_dependencies_from_forced_source( def test_run_installs_with_url_file( installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage -): +) -> None: url = "https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl" package.add_dependency(Factory.create_dependency("demo", {"url": url})) @@ -2330,7 +2333,7 @@ def test_run_installs_with_same_version_url_files( def test_installer_uses_prereleases_if_they_are_compatible( installer: Installer, locker: Locker, package: ProjectPackage, repo: Repository -): +) -> None: package.python_versions = "~2.7 || ^3.4" package.add_dependency( Factory.create_dependency( @@ -2366,7 +2369,7 @@ def test_installer_can_handle_old_lock_files( repo: Repository, installed: CustomInstalledRepository, config: Config, -): +) -> None: pool = RepositoryPool() pool.add_repository(MockRepository()) @@ -2439,7 +2442,7 @@ def test_installer_does_not_write_lock_file_when_installation_fails( repo: Repository, package: ProjectPackage, mocker: MockerFixture, -): +) -> None: repo.add_package(get_package("A", "1.0")) package.add_dependency(Factory.create_dependency("A", "~1.0")) @@ -2463,7 +2466,7 @@ def test_run_with_dependencies_quiet( repo: Repository, package: ProjectPackage, quiet: bool, -): +) -> None: package_a = get_package("A", "1.0") package_b = get_package("B", "1.1") repo.add_package(package_a) @@ -2490,7 +2493,7 @@ def test_run_with_dependencies_quiet( def test_installer_should_use_the_locked_version_of_git_dependencies( installer: Installer, locker: Locker, package: ProjectPackage, repo: Repository -): +) -> None: locker.locked(True) locker.mock_lock_data( { @@ -2559,7 +2562,7 @@ def test_installer_should_use_the_locked_version_of_git_dependencies_with_extras package: ProjectPackage, repo: Repository, is_locked: bool, -): +) -> None: if is_locked: locker.locked(True) locker.mock_lock_data(fixture("with-vcs-dependency-with-extras")) @@ -2602,7 +2605,7 @@ def test_installer_should_use_the_locked_version_of_git_dependencies_without_ref package: ProjectPackage, repo: Repository, is_locked: bool, -): +) -> None: """ If there is no explicit reference (branch or tag or rev) in pyproject.toml, HEAD is used. @@ -2644,7 +2647,7 @@ def test_installer_distinguishes_locked_packages_by_source( repo: Repository, package: ProjectPackage, env_platform: str, -): +) -> None: # Require 1.11.0+cpu from pytorch for most platforms, but specify 1.11.0 and pypi on # darwin. package.add_dependency( diff --git a/tests/integration/test_utils_vcs_git.py b/tests/integration/test_utils_vcs_git.py index ae979edf759..afbc256bd6b 100644 --- a/tests/integration/test_utils_vcs_git.py +++ b/tests/integration/test_utils_vcs_git.py @@ -112,7 +112,7 @@ def remote_default_branch(remote_default_ref: bytes) -> str: # Regression test for https://github.com/python-poetry/poetry/issues/6722 -def test_use_system_git_client_from_environment_variables(): +def test_use_system_git_client_from_environment_variables() -> None: os.environ["POETRY_EXPERIMENTAL_SYSTEM_GIT_CLIENT"] = "true" assert Git.is_using_legacy_client() @@ -132,7 +132,7 @@ def test_git_clone_default_branch_head( remote_refs: FetchPackResult, remote_default_ref: bytes, mocker: MockerFixture, -): +) -> None: spy = mocker.spy(Git, "_clone") spy_legacy = mocker.spy(Git, "_clone_legacy") @@ -143,7 +143,7 @@ def test_git_clone_default_branch_head( spy.assert_called() -def test_git_clone_fails_for_non_existent_branch(source_url: str): +def test_git_clone_fails_for_non_existent_branch(source_url: str) -> None: branch = uuid.uuid4().hex with pytest.raises(PoetryConsoleError) as e: @@ -152,7 +152,7 @@ def test_git_clone_fails_for_non_existent_branch(source_url: str): assert f"Failed to clone {source_url} at '{branch}'" in str(e.value) -def test_git_clone_fails_for_non_existent_revision(source_url: str): +def test_git_clone_fails_for_non_existent_revision(source_url: str) -> None: revision = sha1(uuid.uuid4().bytes).hexdigest() with pytest.raises(PoetryConsoleError) as e: diff --git a/tests/plugins/test_plugin_manager.py b/tests/plugins/test_plugin_manager.py index fb2ce6c94f7..05ebd7aa5b8 100644 --- a/tests/plugins/test_plugin_manager.py +++ b/tests/plugins/test_plugin_manager.py @@ -1,5 +1,6 @@ from __future__ import annotations +from pathlib import Path from typing import TYPE_CHECKING import pytest @@ -17,6 +18,7 @@ if TYPE_CHECKING: + from cleo.io.io import IO from pytest_mock import MockerFixture from tests.conftest import Config @@ -29,9 +31,9 @@ def __call__(self, group: str = Plugin.group) -> PluginManager: class MyPlugin(Plugin): - def activate(self, poetry: Poetry, io: BufferedIO) -> None: + def activate(self, poetry: Poetry, io: IO) -> None: io.write_line("Setting readmes") - poetry.package.readmes = ("README.md",) + poetry.package.readmes = (Path("README.md"),) class MyCommandPlugin(ApplicationPlugin): @@ -39,7 +41,7 @@ class MyCommandPlugin(ApplicationPlugin): class InvalidPlugin: - def activate(self, poetry: Poetry, io: BufferedIO) -> None: + def activate(self, poetry: Poetry, io: IO) -> None: io.write_line("Updating version") poetry.package.version = "9.9.9" @@ -59,7 +61,7 @@ def poetry(fixture_dir: FixtureDirGetter, config: Config) -> Poetry: @pytest.fixture() -def io() -> BufferedIO: +def io() -> IO: return BufferedIO() @@ -86,7 +88,7 @@ def test_load_plugins_and_activate( manager.load_plugins() manager.activate(poetry, io) - assert poetry.package.readmes == ("README.md",) + assert poetry.package.readmes == (Path("README.md"),) assert io.fetch_output() == "Setting readmes\n" diff --git a/tests/puzzle/test_provider.py b/tests/puzzle/test_provider.py index 560ae872f05..1264321b9d0 100644 --- a/tests/puzzle/test_provider.py +++ b/tests/puzzle/test_provider.py @@ -2,6 +2,7 @@ from subprocess import CalledProcessError from typing import TYPE_CHECKING +from typing import Any import pytest @@ -35,7 +36,7 @@ class MockEnv(BaseMockEnv): - def run(self, bin: str, *args: str) -> None: + def run(self, bin: str, *args: str, **kwargs: Any) -> str | int: raise EnvCommandError(CalledProcessError(1, "python", output="")) diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 10e67c9ab6e..9b80652fd68 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -3410,7 +3410,7 @@ def test_solver_should_not_update_same_version_packages_if_installed_has_no_sour def test_solver_should_use_the_python_constraint_from_the_environment_if_available( solver: Solver, repo: Repository, package: ProjectPackage -): +) -> None: set_package_python_versions(solver.provider, "~2.7 || ^3.5") package.add_dependency(Factory.create_dependency("A", "^1.0")) diff --git a/tests/repositories/test_installed_repository.py b/tests/repositories/test_installed_repository.py index 592d99a999a..628852708a1 100644 --- a/tests/repositories/test_installed_repository.py +++ b/tests/repositories/test_installed_repository.py @@ -51,10 +51,10 @@ class MockEnv(BaseMockEnv): @property - def paths(self) -> dict[str, Path]: + def paths(self) -> dict[str, str]: return { - "purelib": SITE_PURELIB, - "platlib": SITE_PLATLIB, + "purelib": SITE_PURELIB.as_posix(), + "platlib": SITE_PLATLIB.as_posix(), } @property @@ -96,7 +96,7 @@ def get_package_from_repository( return None -def test_load_successful(repository: InstalledRepository): +def test_load_successful(repository: InstalledRepository) -> None: assert len(repository.packages) == len(INSTALLED_RESULTS) @@ -119,12 +119,12 @@ def test_load_successful_with_invalid_distribution( assert str(invalid_dist_info) in message -def test_load_ensure_isolation(repository: InstalledRepository): +def test_load_ensure_isolation(repository: InstalledRepository) -> None: package = get_package_from_repository("attrs", repository) assert package is None -def test_load_standard_package(repository: InstalledRepository): +def test_load_standard_package(repository: InstalledRepository) -> None: cleo = get_package_from_repository("cleo", repository) assert cleo is not None assert cleo.name == "cleo" @@ -139,7 +139,7 @@ def test_load_standard_package(repository: InstalledRepository): assert foo.version.text == "0.1.0" -def test_load_git_package(repository: InstalledRepository): +def test_load_git_package(repository: InstalledRepository) -> None: pendulum = get_package_from_repository("pendulum", repository) assert pendulum is not None assert pendulum.name == "pendulum" @@ -153,7 +153,7 @@ def test_load_git_package(repository: InstalledRepository): assert pendulum.source_reference == "bb058f6b78b2d28ef5d9a5e759cfa179a1a713d6" -def test_load_git_package_pth(repository: InstalledRepository): +def test_load_git_package_pth(repository: InstalledRepository) -> None: bender = get_package_from_repository("bender", repository) assert bender is not None assert bender.name == "bender" @@ -161,14 +161,14 @@ def test_load_git_package_pth(repository: InstalledRepository): assert bender.source_type == "git" -def test_load_platlib_package(repository: InstalledRepository): +def test_load_platlib_package(repository: InstalledRepository) -> None: lib64 = get_package_from_repository("lib64", repository) assert lib64 is not None assert lib64.name == "lib64" assert lib64.version.text == "2.3.4" -def test_load_editable_package(repository: InstalledRepository): +def test_load_editable_package(repository: InstalledRepository) -> None: # test editable package with text .pth file editable = get_package_from_repository("editable", repository) assert editable is not None @@ -181,7 +181,7 @@ def test_load_editable_package(repository: InstalledRepository): ) -def test_load_editable_with_import_package(repository: InstalledRepository): +def test_load_editable_with_import_package(repository: InstalledRepository) -> None: # test editable package with executable .pth file editable = get_package_from_repository("editable-with-import", repository) assert editable is not None @@ -191,7 +191,7 @@ def test_load_editable_with_import_package(repository: InstalledRepository): assert editable.source_url is None -def test_load_standard_package_with_pth_file(repository: InstalledRepository): +def test_load_standard_package_with_pth_file(repository: InstalledRepository) -> None: # test standard packages with .pth file is not treated as editable standard = get_package_from_repository("standard", repository) assert standard is not None @@ -201,7 +201,7 @@ def test_load_standard_package_with_pth_file(repository: InstalledRepository): assert standard.source_url is None -def test_load_pep_610_compliant_git_packages(repository: InstalledRepository): +def test_load_pep_610_compliant_git_packages(repository: InstalledRepository) -> None: package = get_package_from_repository("git-pep-610", repository) assert package is not None @@ -215,7 +215,7 @@ def test_load_pep_610_compliant_git_packages(repository: InstalledRepository): def test_load_pep_610_compliant_git_packages_no_requested_version( repository: InstalledRepository, -): +) -> None: package = get_package_from_repository( "git-pep-610-no-requested-version", repository ) @@ -234,7 +234,7 @@ def test_load_pep_610_compliant_git_packages_no_requested_version( def test_load_pep_610_compliant_git_packages_with_subdirectory( repository: InstalledRepository, -): +) -> None: package = get_package_from_repository("git-pep-610-subdirectory", repository) assert package is not None assert package.name == "git-pep-610-subdirectory" @@ -246,7 +246,7 @@ def test_load_pep_610_compliant_git_packages_with_subdirectory( assert package.source_subdirectory == "subdir" -def test_load_pep_610_compliant_url_packages(repository: InstalledRepository): +def test_load_pep_610_compliant_url_packages(repository: InstalledRepository) -> None: package = get_package_from_repository("url-pep-610", repository) assert package is not None @@ -259,7 +259,7 @@ def test_load_pep_610_compliant_url_packages(repository: InstalledRepository): ) -def test_load_pep_610_compliant_file_packages(repository: InstalledRepository): +def test_load_pep_610_compliant_file_packages(repository: InstalledRepository) -> None: package = get_package_from_repository("file-pep-610", repository) assert package is not None @@ -269,7 +269,9 @@ def test_load_pep_610_compliant_file_packages(repository: InstalledRepository): assert package.source_url == "/path/to/distributions/file-pep-610-1.2.3.tar.gz" -def test_load_pep_610_compliant_directory_packages(repository: InstalledRepository): +def test_load_pep_610_compliant_directory_packages( + repository: InstalledRepository, +) -> None: package = get_package_from_repository("directory-pep-610", repository) assert package is not None @@ -282,7 +284,7 @@ def test_load_pep_610_compliant_directory_packages(repository: InstalledReposito def test_load_pep_610_compliant_editable_directory_packages( repository: InstalledRepository, -): +) -> None: package = get_package_from_repository("editable-directory-pep-610", repository) assert package is not None diff --git a/tests/utils/test_authenticator.py b/tests/utils/test_authenticator.py index 05c5490ac11..2a0d1bee564 100644 --- a/tests/utils/test_authenticator.py +++ b/tests/utils/test_authenticator.py @@ -42,12 +42,12 @@ def mock_remote(http: type[httpretty.httpretty]) -> None: @pytest.fixture() -def repo(): +def repo() -> dict[str, dict[str, str]]: return {"foo": {"url": "https://foo.bar/simple/"}} @pytest.fixture -def mock_config(config: Config, repo: dict[str, dict[str, str]]): +def mock_config(config: Config, repo: dict[str, dict[str, str]]) -> Config: config.merge( { "repositories": repo, @@ -60,7 +60,7 @@ def mock_config(config: Config, repo: dict[str, dict[str, str]]): def test_authenticator_uses_url_provided_credentials( mock_config: Config, mock_remote: None, http: type[httpretty.httpretty] -): +) -> None: authenticator = Authenticator(mock_config, NullIO()) authenticator.request("get", "https://foo001:bar002@foo.bar/files/foo-0.1.0.tar.gz") @@ -71,7 +71,7 @@ def test_authenticator_uses_url_provided_credentials( def test_authenticator_uses_credentials_from_config_if_not_provided( mock_config: Config, mock_remote: None, http: type[httpretty.httpretty] -): +) -> None: authenticator = Authenticator(mock_config, NullIO()) authenticator.request("get", "https://foo.bar/files/foo-0.1.0.tar.gz") @@ -85,7 +85,7 @@ def test_authenticator_uses_username_only_credentials( mock_remote: None, http: type[httpretty.httpretty], with_simple_keyring: None, -): +) -> None: authenticator = Authenticator(mock_config, NullIO()) authenticator.request("get", "https://foo001@foo.bar/files/foo-0.1.0.tar.gz") @@ -96,7 +96,7 @@ def test_authenticator_uses_username_only_credentials( def test_authenticator_uses_password_only_credentials( mock_config: Config, mock_remote: None, http: type[httpretty.httpretty] -): +) -> None: authenticator = Authenticator(mock_config, NullIO()) authenticator.request("get", "https://:bar002@foo.bar/files/foo-0.1.0.tar.gz") @@ -111,7 +111,7 @@ def test_authenticator_uses_empty_strings_as_default_password( repo: dict[str, dict[str, str]], http: type[httpretty.httpretty], with_simple_keyring: None, -): +) -> None: config.merge( { "repositories": repo, @@ -132,7 +132,7 @@ def test_authenticator_uses_empty_strings_as_default_username( mock_remote: None, repo: dict[str, dict[str, str]], http: type[httpretty.httpretty], -): +) -> None: config.merge( { "repositories": repo, @@ -155,7 +155,7 @@ def test_authenticator_falls_back_to_keyring_url( http: type[httpretty.httpretty], with_simple_keyring: None, dummy_keyring: DummyBackend, -): +) -> None: config.merge( { "repositories": repo, @@ -163,7 +163,7 @@ def test_authenticator_falls_back_to_keyring_url( ) dummy_keyring.set_password( - "https://foo.bar/simple/", None, SimpleCredential(None, "bar") + "https://foo.bar/simple/", None, SimpleCredential("foo", "bar") ) authenticator = Authenticator(config, NullIO()) @@ -171,7 +171,7 @@ def test_authenticator_falls_back_to_keyring_url( request = http.last_request() - assert request.headers["Authorization"] == "Basic OmJhcg==" + assert request.headers["Authorization"] == "Basic Zm9vOmJhcg==" def test_authenticator_falls_back_to_keyring_netloc( @@ -181,35 +181,35 @@ def test_authenticator_falls_back_to_keyring_netloc( http: type[httpretty.httpretty], with_simple_keyring: None, dummy_keyring: DummyBackend, -): +) -> None: config.merge( { "repositories": repo, } ) - dummy_keyring.set_password("foo.bar", None, SimpleCredential(None, "bar")) + dummy_keyring.set_password("foo.bar", None, SimpleCredential("foo", "bar")) authenticator = Authenticator(config, NullIO()) authenticator.request("get", "https://foo.bar/files/foo-0.1.0.tar.gz") request = http.last_request() - assert request.headers["Authorization"] == "Basic OmJhcg==" + assert request.headers["Authorization"] == "Basic Zm9vOmJhcg==" @pytest.mark.filterwarnings("ignore::pytest.PytestUnhandledThreadExceptionWarning") def test_authenticator_request_retries_on_exception( mocker: MockerFixture, config: Config, http: type[httpretty.httpretty] -): +) -> None: sleep = mocker.patch("time.sleep") sdist_uri = f"https://foo.bar/files/{uuid.uuid4()!s}/foo-0.1.0.tar.gz" content = str(uuid.uuid4()) - seen = [] + seen: list[str] = [] def callback( - request: requests.Request, uri: str, response_headers: dict - ) -> list[int | dict | str]: + request: requests.Request, uri: str, response_headers: dict[str, str] + ) -> list[int | dict[str, str] | str]: if seen.count(uri) < 2: seen.append(uri) raise requests.exceptions.ConnectionError("Disconnected") @@ -226,7 +226,7 @@ def callback( @pytest.mark.filterwarnings("ignore::pytest.PytestUnhandledThreadExceptionWarning") def test_authenticator_request_raises_exception_when_attempts_exhausted( mocker: MockerFixture, config: Config, http: type[httpretty.httpretty] -): +) -> None: sleep = mocker.patch("time.sleep") sdist_uri = f"https://foo.bar/files/{uuid.uuid4()!s}/foo-0.1.0.tar.gz" @@ -246,15 +246,15 @@ def test_authenticator_request_respects_retry_header( mocker: MockerFixture, config: Config, http: type[httpretty.httpretty], -): +) -> None: sleep = mocker.patch("time.sleep") sdist_uri = f"https://foo.bar/files/{uuid.uuid4()!s}/foo-0.1.0.tar.gz" content = str(uuid.uuid4()) - seen = [] + seen: list[str] = [] def callback( - request: requests.Request, uri: str, response_headers: dict - ) -> list[int | dict | str]: + request: requests.Request, uri: str, response_headers: dict[str, str] + ) -> list[int | dict[str, str] | str]: if not seen.count(uri): seen.append(uri) return [429, {"Retry-After": "42"}, "Retry later"] @@ -290,14 +290,14 @@ def test_authenticator_request_retries_on_status_code( http: type[httpretty.httpretty], status: int, attempts: int, -): +) -> None: sleep = mocker.patch("time.sleep") sdist_uri = f"https://foo.bar/files/{uuid.uuid4()!s}/foo-0.1.0.tar.gz" content = str(uuid.uuid4()) def callback( - request: requests.Request, uri: str, response_headers: dict - ) -> list[int | dict | str]: + request: requests.Request, uri: str, response_headers: dict[str, str] + ) -> list[int | dict[str, str] | str]: return [status, response_headers, content] http.register_uri(httpretty.GET, sdist_uri, body=callback) @@ -319,7 +319,7 @@ def test_authenticator_uses_env_provided_credentials( mock_remote: type[httpretty.httpretty], http: type[httpretty.httpretty], monkeypatch: MonkeyPatch, -): +) -> None: monkeypatch.setenv("POETRY_HTTP_BASIC_FOO_USERNAME", "bar") monkeypatch.setenv("POETRY_HTTP_BASIC_FOO_PASSWORD", "baz") @@ -350,7 +350,7 @@ def test_authenticator_uses_certs_from_config_if_not_provided( mocker: MockerFixture, cert: str | None, client_cert: str | None, -): +) -> None: configured_cert = "/path/to/cert" configured_client_cert = "/path/to/client-cert" @@ -380,7 +380,7 @@ def test_authenticator_uses_certs_from_config_if_not_provided( def test_authenticator_uses_credentials_from_config_matched_by_url_path( config: Config, mock_remote: None, http: type[httpretty.httpretty] -): +) -> None: config.merge( { "repositories": { @@ -413,7 +413,7 @@ def test_authenticator_uses_credentials_from_config_matched_by_url_path( def test_authenticator_uses_credentials_from_config_with_at_sign_in_path( config: Config, mock_remote: None, http: type[httpretty.httpretty] -): +) -> None: config.merge( { "repositories": { @@ -439,7 +439,7 @@ def test_authenticator_falls_back_to_keyring_url_matched_by_path( http: type[httpretty.httpretty], with_simple_keyring: None, dummy_keyring: DummyBackend, -): +) -> None: config.merge( { "repositories": { @@ -450,10 +450,10 @@ def test_authenticator_falls_back_to_keyring_url_matched_by_path( ) dummy_keyring.set_password( - "https://foo.bar/alpha/files/simple/", None, SimpleCredential(None, "bar") + "https://foo.bar/alpha/files/simple/", None, SimpleCredential("foo", "bar") ) dummy_keyring.set_password( - "https://foo.bar/beta/files/simple/", None, SimpleCredential(None, "baz") + "https://foo.bar/beta/files/simple/", None, SimpleCredential("foo", "baz") ) authenticator = Authenticator(config, NullIO()) @@ -461,13 +461,13 @@ def test_authenticator_falls_back_to_keyring_url_matched_by_path( authenticator.request("get", "https://foo.bar/alpha/files/simple/foo-0.1.0.tar.gz") request = http.last_request() - basic_auth = base64.b64encode(b":bar").decode() + basic_auth = base64.b64encode(b"foo:bar").decode() assert request.headers["Authorization"] == f"Basic {basic_auth}" authenticator.request("get", "https://foo.bar/beta/files/simple/foo-0.1.0.tar.gz") request = http.last_request() - basic_auth = base64.b64encode(b":baz").decode() + basic_auth = base64.b64encode(b"foo:baz").decode() assert request.headers["Authorization"] == f"Basic {basic_auth}" @@ -477,7 +477,7 @@ def test_authenticator_uses_env_provided_credentials_matched_by_url_path( mock_remote: type[httpretty.httpretty], http: type[httpretty.httpretty], monkeypatch: MonkeyPatch, -): +) -> None: monkeypatch.setenv("POETRY_HTTP_BASIC_FOO_ALPHA_USERNAME", "bar") monkeypatch.setenv("POETRY_HTTP_BASIC_FOO_ALPHA_PASSWORD", "alpha") monkeypatch.setenv("POETRY_HTTP_BASIC_FOO_BETA_USERNAME", "baz") @@ -513,7 +513,7 @@ def test_authenticator_azure_feed_guid_credentials( http: type[httpretty.httpretty], with_simple_keyring: None, dummy_keyring: DummyBackend, -): +) -> None: config.merge( { "repositories": { @@ -558,7 +558,7 @@ def test_authenticator_add_repository( http: type[httpretty.httpretty], with_simple_keyring: None, dummy_keyring: DummyBackend, -): +) -> None: config.merge( { "http-basic": { @@ -594,7 +594,7 @@ def test_authenticator_git_repositories( http: type[httpretty.httpretty], with_simple_keyring: None, dummy_keyring: DummyBackend, -): +) -> None: config.merge( { "repositories": { diff --git a/tests/utils/test_password_manager.py b/tests/utils/test_password_manager.py index 468ef14f689..d09f88b1fdf 100644 --- a/tests/utils/test_password_manager.py +++ b/tests/utils/test_password_manager.py @@ -20,7 +20,7 @@ def test_set_http_password( config: Config, with_simple_keyring: None, dummy_keyring: DummyBackend -): +) -> None: manager = PasswordManager(config) assert manager.keyring.is_available() @@ -35,13 +35,14 @@ def test_set_http_password( def test_get_http_auth( config: Config, with_simple_keyring: None, dummy_keyring: DummyBackend -): +) -> None: dummy_keyring.set_password("poetry-repository-foo", "bar", "baz") config.auth_config_source.add_property("http-basic.foo", {"username": "bar"}) manager = PasswordManager(config) assert manager.keyring.is_available() auth = manager.get_http_auth("foo") + assert auth is not None assert auth["username"] == "bar" assert auth["password"] == "baz" @@ -49,7 +50,7 @@ def test_get_http_auth( def test_delete_http_password( config: Config, with_simple_keyring: None, dummy_keyring: DummyBackend -): +) -> None: dummy_keyring.set_password("poetry-repository-foo", "bar", "baz") config.auth_config_source.add_property("http-basic.foo", {"username": "bar"}) manager = PasswordManager(config) @@ -63,7 +64,7 @@ def test_delete_http_password( def test_set_pypi_token( config: Config, with_simple_keyring: None, dummy_keyring: DummyBackend -): +) -> None: manager = PasswordManager(config) assert manager.keyring.is_available() @@ -76,7 +77,7 @@ def test_set_pypi_token( def test_get_pypi_token( config: Config, with_simple_keyring: None, dummy_keyring: DummyBackend -): +) -> None: dummy_keyring.set_password("poetry-repository-foo", "__token__", "baz") manager = PasswordManager(config) @@ -86,7 +87,7 @@ def test_get_pypi_token( def test_delete_pypi_token( config: Config, with_simple_keyring: None, dummy_keyring: DummyBackend -): +) -> None: dummy_keyring.set_password("poetry-repository-foo", "__token__", "baz") manager = PasswordManager(config) @@ -98,7 +99,7 @@ def test_delete_pypi_token( def test_set_http_password_with_unavailable_backend( config: Config, with_fail_keyring: None -): +) -> None: manager = PasswordManager(config) assert not manager.keyring.is_available() @@ -111,7 +112,7 @@ def test_set_http_password_with_unavailable_backend( def test_get_http_auth_with_unavailable_backend( config: Config, with_fail_keyring: None -): +) -> None: config.auth_config_source.add_property( "http-basic.foo", {"username": "bar", "password": "baz"} ) @@ -119,6 +120,7 @@ def test_get_http_auth_with_unavailable_backend( assert not manager.keyring.is_available() auth = manager.get_http_auth("foo") + assert auth is not None assert auth["username"] == "bar" assert auth["password"] == "baz" @@ -126,7 +128,7 @@ def test_get_http_auth_with_unavailable_backend( def test_delete_http_password_with_unavailable_backend( config: Config, with_fail_keyring: None -): +) -> None: config.auth_config_source.add_property( "http-basic.foo", {"username": "bar", "password": "baz"} ) @@ -140,7 +142,7 @@ def test_delete_http_password_with_unavailable_backend( def test_set_pypi_token_with_unavailable_backend( config: Config, with_fail_keyring: None -): +) -> None: manager = PasswordManager(config) assert not manager.keyring.is_available() @@ -151,7 +153,7 @@ def test_set_pypi_token_with_unavailable_backend( def test_get_pypi_token_with_unavailable_backend( config: Config, with_fail_keyring: None -): +) -> None: config.auth_config_source.add_property("pypi-token.foo", "baz") manager = PasswordManager(config) @@ -161,7 +163,7 @@ def test_get_pypi_token_with_unavailable_backend( def test_delete_pypi_token_with_unavailable_backend( config: Config, with_fail_keyring: None -): +) -> None: config.auth_config_source.add_property("pypi-token.foo", "baz") manager = PasswordManager(config) @@ -173,7 +175,7 @@ def test_delete_pypi_token_with_unavailable_backend( def test_keyring_raises_errors_on_keyring_errors( mocker: MockerFixture, with_fail_keyring: None -): +) -> None: mocker.patch("poetry.utils.password_manager.PoetryKeyring._check") key_ring = PoetryKeyring("poetry") @@ -189,7 +191,7 @@ def test_keyring_raises_errors_on_keyring_errors( def test_keyring_with_chainer_backend_and_fail_keyring_should_be_unavailable( with_chained_fail_keyring: None, -): +) -> None: key_ring = PoetryKeyring("poetry") assert not key_ring.is_available() @@ -197,7 +199,7 @@ def test_keyring_with_chainer_backend_and_fail_keyring_should_be_unavailable( def test_keyring_with_chainer_backend_and_null_keyring_should_be_unavailable( with_chained_null_keyring: None, -): +) -> None: key_ring = PoetryKeyring("poetry") assert not key_ring.is_available() @@ -205,7 +207,7 @@ def test_keyring_with_chainer_backend_and_null_keyring_should_be_unavailable( def test_null_keyring_should_be_unavailable( with_null_keyring: None, -): +) -> None: key_ring = PoetryKeyring("poetry") assert not key_ring.is_available() @@ -213,7 +215,7 @@ def test_null_keyring_should_be_unavailable( def test_fail_keyring_should_be_unavailable( with_fail_keyring: None, -): +) -> None: key_ring = PoetryKeyring("poetry") assert not key_ring.is_available() @@ -221,13 +223,14 @@ def test_fail_keyring_should_be_unavailable( def test_get_http_auth_from_environment_variables( environ: None, config: Config, with_simple_keyring: None -): +) -> None: os.environ["POETRY_HTTP_BASIC_FOO_USERNAME"] = "bar" os.environ["POETRY_HTTP_BASIC_FOO_PASSWORD"] = "baz" manager = PasswordManager(config) auth = manager.get_http_auth("foo") + assert auth is not None assert auth["username"] == "bar" assert auth["password"] == "baz" @@ -238,7 +241,7 @@ def test_get_pypi_token_with_env_var_positive( config: Config, with_simple_keyring: None, dummy_keyring: DummyBackend, -): +) -> None: sample_token = "sampletoken-1234" repo_name = "foo" manager = PasswordManager(config) @@ -252,7 +255,7 @@ def test_get_pypi_token_with_env_var_positive( def test_get_pypi_token_with_env_var_not_available( config: Config, with_simple_keyring: None, dummy_keyring: DummyBackend -): +) -> None: repo_name = "foo" manager = PasswordManager(config) From b1fcfc02014f306a0b55a99829670de4d67b22a3 Mon Sep 17 00:00:00 2001 From: Martin Miglio Date: Sun, 16 Apr 2023 10:40:01 -0400 Subject: [PATCH 06/10] pre-commit matches pyproject.toml in subdirs (#7242) --- .pre-commit-hooks.yaml | 2 +- docs/pre-commit-hooks.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index d2662b8a420..13154790b87 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -5,7 +5,7 @@ language: python language_version: python3 pass_filenames: false - files: ^pyproject.toml$ + files: ^(.*/)?pyproject.toml$ - id: poetry-lock name: poetry-lock diff --git a/docs/pre-commit-hooks.md b/docs/pre-commit-hooks.md index 5fcbd62ef27..ab833dab3f3 100644 --- a/docs/pre-commit-hooks.md +++ b/docs/pre-commit-hooks.md @@ -34,6 +34,9 @@ to make sure the poetry configuration does not get committed in a broken state. The hook takes the same arguments as the poetry command. For more information see the [check command]({{< relref "cli#check" >}}). +{{% note %}} +If the `pyproject.toml` file is not in the root directory, you can specify `args: ["-C", "./subdirectory"]`. +{{% /note %}} ## poetry-lock From aa33315853099aef9449a2d7ba576da99ab53a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82?= <23004737+rafrafek@users.noreply.github.com> Date: Mon, 17 Apr 2023 12:18:49 +0200 Subject: [PATCH 07/10] Remove `language_version` from pre-commit hooks (#6989) --- .pre-commit-hooks.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 13154790b87..08f733c18e8 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -3,7 +3,6 @@ description: run poetry check to validate config entry: poetry check language: python - language_version: python3 pass_filenames: false files: ^(.*/)?pyproject.toml$ @@ -12,7 +11,6 @@ description: run poetry lock to update lock file entry: poetry lock language: python - language_version: python3 pass_filenames: false - id: poetry-export @@ -20,7 +18,6 @@ description: run poetry export to sync lock file with requirements.txt entry: poetry export language: python - language_version: python3 pass_filenames: false files: ^poetry.lock$ args: ["-f", "requirements.txt", "-o", "requirements.txt"] From 152f01e4cf2b45b9868a97c2c762dd0a7c4aad65 Mon Sep 17 00:00:00 2001 From: Alejandro Angulo <5242883+alejandro-angulo@users.noreply.github.com> Date: Mon, 17 Apr 2023 04:03:17 -0700 Subject: [PATCH 08/10] Added environment variables for configurations (#6711) --- docs/configuration.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/docs/configuration.md b/docs/configuration.md index cc477f22609..4277cb1a095 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -149,6 +149,8 @@ You can override the Cache directory by setting the `POETRY_CACHE_DIR` environme **Type**: `string` +**Environment Variable**: `POETRY_CACHE_DIR` + The path to the cache directory used by Poetry. Defaults to one of the following directories: @@ -163,6 +165,8 @@ Defaults to one of the following directories: **Default**: `false` +**Environment Variable**: `POETRY_EXPERIMENTAL_SYSTEM_GIT_CLIENT` + *Introduced in 1.2.0* Use system git client backend for git related tasks. @@ -177,6 +181,8 @@ If you encounter any problems with it, set to `true` to use the system git backe **Default**: `number_of_cores + 4` +**Environment Variable**: `POETRY_INSTALLER_MAX_WORKERS` + *Introduced in 1.2.0* Set the maximum number of workers while using the parallel installer. @@ -209,6 +215,8 @@ you encounter on the [issue tracker](https://github.com/python-poetry/poetry/iss **Default**: `false` +**Environment Variable**: `POETRY_INSTALLER_NO_BINARY` + *Introduced in 1.2.0* When set this configuration allows users to configure package distribution format policy for all or @@ -258,6 +266,8 @@ across all your projects if incorrectly set. **Default**: `true` +**Environment Variable**: `POETRY_INSTALLER_PARALLEL` + *Introduced in 1.1.4* Use parallel execution when using the new (`>=1.1.0`) installer. @@ -268,6 +278,8 @@ Use parallel execution when using the new (`>=1.1.0`) installer. **Default**: `true` +**Environment Variable**: `POETRY_VIRTUALENVS_CREATE` + Create a new virtual environment if one doesn't already exist. If set to `false`, Poetry will not create a new virtual environment. If it detects a virtual environment @@ -294,6 +306,8 @@ might contain additional Python packages as well. **Default**: `None` +**Environment Variable**: `POETRY_VIRTUALENVS_IN_PROJECT` + Create the virtualenv inside the project's root directory. If not set explicitly, `poetry` by default will create virtual environment under @@ -310,6 +324,8 @@ If set to `false`, `poetry` will ignore any existing `.venv` directory. **Default**: `false` +**Environment Variable**: `POETRY_VIRTUALENVS_OPTIONS_ALWAYS_COPY` + *Introduced in 1.2.0* If set to `true` the `--always-copy` parameter is passed to `virtualenv` on creation of the virtual environment, so that @@ -321,6 +337,8 @@ all needed files are copied into it instead of symlinked. **Default**: `false` +**Environment Variable**: `POETRY_VIRTUALENVS_OPTIONS_NO_PIP` + *Introduced in 1.2.0* If set to `true` the `--no-pip` parameter is passed to `virtualenv` on creation of the virtual environment. This means @@ -341,6 +359,8 @@ packages. This is desirable for production environments. **Default**: `false` +**Environment Variable**: `POETRY_VIRTUALENVS_OPTIONS_NO_SETUPTOOLS` + *Introduced in 1.2.0* If set to `true` the `--no-setuptools` parameter is passed to `virtualenv` on creation of the virtual environment. This @@ -358,6 +378,8 @@ available within a virtual environment. This can cause some features in these to **Default**: `false` +**Environment Variable**: `POETRY_VIRTUALENVS_OPTIONS_SYSTEM_SITE_PACKAGES` + Give the virtual environment access to the system site-packages directory. Applies on virtualenv creation. @@ -367,6 +389,8 @@ Applies on virtualenv creation. **Default**: `{cache-dir}/virtualenvs` +**Environment Variable**: `POETRY_VIRTUALENVS_PATH` + Directory where virtual environments will be created. {{% note %}} @@ -379,6 +403,8 @@ This setting controls the global virtual environment storage path. It most likel **Default**: `false` +**Environment Variable**: `POETRY_VIRTUALENVS_PREFER_ACTIVE_PYTHON` + *Introduced in 1.2.0* Use currently activated Python version to create a new virtual environment. @@ -390,6 +416,8 @@ If set to `false`, Python version used during Poetry installation is used. **Default**: `{project_name}-py{python_version}` +**Environment Variable**: `POETRY_VIRTUALENVS_PROMPT` + *Introduced in 1.2.0* Format string defining the prompt to be displayed when the virtual environment is activated. @@ -399,12 +427,16 @@ The variables `project_name` and `python_version` are available for formatting. **Type**: `string` +**Environment Variable**: `POETRY_REPOSITORIES_` + Set a new alternative repository. See [Repositories]({{< relref "repositories" >}}) for more information. ### `http-basic.`: **Type**: `(string, string)` +**Environment Variable**: `POETRY_HTTP_BASIC_` + Set repository credentials (`username` and `password`) for ``. See [Repositories - Configuring credentials]({{< relref "repositories#configuring-credentials" >}}) for more information. @@ -413,6 +445,8 @@ for more information. **Type**: `string` +**Environment Variable**: `POETRY_PYPI_TOKEN_` + Set repository credentials (using an API token) for ``. See [Repositories - Configuring credentials]({{< relref "repositories#configuring-credentials" >}}) for more information. @@ -421,6 +455,8 @@ for more information. **Type**: `string | boolean` +**Environment Variable**: `POETRY_CERTIFICATES__CERT` + Set custom certificate authority for repository ``. See [Repositories - Configuring credentials - Custom certificate authority]({{< relref "repositories#custom-certificate-authority-and-mutual-tls-authentication" >}}) for more information. @@ -432,6 +468,8 @@ repository. **Type**: `string` +**Environment Variable**: `POETRY_CERTIFICATES__CLIENT_CERT` + Set client certificate for repository ``. See [Repositories - Configuring credentials - Custom certificate authority]({{< relref "repositories#custom-certificate-authority-and-mutual-tls-authentication" >}}) for more information. From d699b24b4b2fabe8ed676ffc903cb8b17ab63d25 Mon Sep 17 00:00:00 2001 From: Andrea Ghensi Date: Mon, 17 Apr 2023 13:35:52 +0200 Subject: [PATCH 09/10] Add --executable option to env info command (#7547) --- docs/managing-environments.md | 11 +++++++++++ src/poetry/console/commands/env/info.py | 15 ++++++++++++++- tests/console/commands/env/test_info.py | 6 ++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/docs/managing-environments.md b/docs/managing-environments.md index 1716a2f9d50..a3a0cc37f57 100644 --- a/docs/managing-environments.md +++ b/docs/managing-environments.md @@ -39,6 +39,7 @@ pyenv install 3.9.8 pyenv local 3.9.8 # Activate Python 3.9 for the current project poetry install ``` + {{% /note %}} {{% note %}} @@ -106,6 +107,13 @@ to `env info`: poetry env info --path ``` +If you only want to know the path to the python executable (useful for running mypy from a global environment without installing it in the virtual environment), you can pass the `--executable` option +to `env info`: + +```bash +poetry env info --executable +``` + ## Listing the environments associated with the project You can also list all the virtual environments associated with the current project @@ -140,10 +148,13 @@ poetry env remove test-O3eWbxRl-py3.7 ``` You can delete more than one environment at a time. + ```bash poetry env remove python3.6 python3.7 python3.8 ``` + Use the `--all` option to delete all virtual environments at once. + ```bash poetry env remove --all ``` diff --git a/src/poetry/console/commands/env/info.py b/src/poetry/console/commands/env/info.py index abb78f30cda..2de77f73a6d 100644 --- a/src/poetry/console/commands/env/info.py +++ b/src/poetry/console/commands/env/info.py @@ -15,7 +15,12 @@ class EnvInfoCommand(Command): name = "env info" description = "Displays information about the current environment." - options = [option("path", "p", "Only display the environment's path.")] + options = [ + option("path", "p", "Only display the environment's path."), + option( + "executable", "e", "Only display the environment's python executable path." + ), + ] def handle(self) -> int: from poetry.utils.env import EnvManager @@ -30,6 +35,14 @@ def handle(self) -> int: return 0 + if self.option("executable"): + if not env.is_venv(): + return 1 + + self.line(str(env.python)) + + return 0 + self._display_complete_info(env) return 0 diff --git a/tests/console/commands/env/test_info.py b/tests/console/commands/env/test_info.py index e4f5826e49c..bdc8a0eb57b 100644 --- a/tests/console/commands/env/test_info.py +++ b/tests/console/commands/env/test_info.py @@ -58,3 +58,9 @@ def test_env_info_displays_path_only(tester: CommandTester): tester.execute("--path") expected = str(Path("/prefix")) + "\n" assert tester.io.fetch_output() == expected + + +def test_env_info_displays_executable_only(tester: CommandTester): + tester.execute("--executable") + expected = str(sys.executable) + "\n" + assert tester.io.fetch_output() == expected From ec35b837bae2ded6e42a9e96eb2798445b95dcfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20D=C3=B6ring?= <30527984+radoering@users.noreply.github.com> Date: Tue, 18 Apr 2023 12:29:47 +0200 Subject: [PATCH 10/10] chore: update poetry-plugin-export (#7806) --- poetry.lock | 10 +++++----- pyproject.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 36bd49a456a..4cf7f64935c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. [[package]] name = "attrs" @@ -1164,14 +1164,14 @@ importlib-metadata = {version = ">=1.7.0", markers = "python_version < \"3.8\""} [[package]] name = "poetry-plugin-export" -version = "1.3.0" +version = "1.3.1" description = "Poetry plugin to export the dependencies to various formats" category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ - {file = "poetry_plugin_export-1.3.0-py3-none-any.whl", hash = "sha256:6e5919bf84afcb08cdd419a03f909f490d8671f00633a3c6df8ba09b0820dc2f"}, - {file = "poetry_plugin_export-1.3.0.tar.gz", hash = "sha256:61ae5ec1db233aba947a48e1ce54c6ff66afd0e1c87195d6bce64c73a5ae658c"}, + {file = "poetry_plugin_export-1.3.1-py3-none-any.whl", hash = "sha256:941d7ba02a59671d6327b16dc6deecc9262477abbc120d728a500cf125bc1e06"}, + {file = "poetry_plugin_export-1.3.1.tar.gz", hash = "sha256:d949742757a8a5f0b5810495bffaf4ed8a767f2e2ffda9887cf72f896deabf84"}, ] [package.dependencies] @@ -1983,4 +1983,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "2.0" python-versions = "^3.7" -content-hash = "fb909b5c273da18b6715b134312d9a97edfa8dbfc2c7807fde3ace3d179c21ff" +content-hash = "60024b1508baf21776d5214a4af23c8a1ad180be8e2197df91971881a4be4824" diff --git a/pyproject.toml b/pyproject.toml index 63e65101815..ae1a50bee98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ generate-setup-file = false python = "^3.7" poetry-core = "1.5.2" -poetry-plugin-export = "^1.3.0" +poetry-plugin-export = "^1.3.1" "backports.cached-property" = { version = "^1.0.2", python = "<3.8" } build = "^0.10.0" cachecontrol = { version = "^0.12.9", extras = ["filecache"] }