From f5e625c2cbe6027b9d8b65cd1d33af0707bb07ff Mon Sep 17 00:00:00 2001 From: juhoautio Date: Sat, 28 May 2022 02:14:53 +0300 Subject: [PATCH 01/14] new command to support more options Adding all the missing options that are asked interactively by the init command --- docs/cli.md | 11 +++++- src/poetry/console/commands/new.py | 63 ++++++++++++++++++++++++++---- tests/console/commands/test_new.py | 49 ++++++++++++++++++++++- 3 files changed, 112 insertions(+), 11 deletions(-) diff --git a/docs/cli.md b/docs/cli.md index d95615a21d6..e05f3225a47 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -102,6 +102,13 @@ my-package ### Options * `--name`: Set the resulting package name. +* `--description`: Description of the package. +* `--package-version`: Set the version of the package. +* `--author`: Author of the package. +* `--python`: Compatible Python versions. +* `--license`: License of the package. +* `--dependency`: Package to require with an optional version constraint, e.g. `requests:^2.10.0` or `requests=2.11.1`. (see [add]({{< relref "#add" >}}). +* `--dev-dependency`: Development requirements, see `--dependency`. * `--src`: Use the src layout for the project. * `--readme`: Specify the readme file format. One of `md` (default) or `rst`. @@ -123,8 +130,8 @@ poetry init * `--description`: Description of the package. * `--author`: Author of the package. * `--python` Compatible Python versions. -* `--dependency`: Package to require with a version constraint. Should be in format `foo:1.0.0`. -* `--dev-dependency`: Development requirements, see `--require`. +* `--dependency`: Package to require with an optional version constraint, e.g. `requests:^2.10.0` or `requests=2.11.1`. (see [add]({{< relref "#add" >}}). +* `--dev-dependency`: Development requirements, see `--dependency`. ## install diff --git a/src/poetry/console/commands/new.py b/src/poetry/console/commands/new.py index 40e8cc0fcd0..ea466f2714d 100644 --- a/src/poetry/console/commands/new.py +++ b/src/poetry/console/commands/new.py @@ -3,14 +3,19 @@ import sys from contextlib import suppress +from typing import TYPE_CHECKING from cleo.helpers import argument from cleo.helpers import option -from poetry.console.commands.command import Command +from poetry.console.commands.init import InitCommand -class NewCommand(Command): +if TYPE_CHECKING: + from poetry.console.commands.init import Requirements + + +class NewCommand(InitCommand): name = "new" description = "Creates a new Python project at ." @@ -18,6 +23,27 @@ class NewCommand(Command): arguments = [argument("path", "The path to create the project at.")] options = [ option("name", None, "Set the resulting package name.", flag=False), + option("description", None, "Description of the package.", flag=False), + option("package-version", None, "Set the version of the package.", flag=False), + option("author", None, "Author name of the package.", flag=False), + option("python", None, "Compatible Python versions.", flag=False), + option( + "dependency", + None, + "Package to require, with an optional version constraint, " + "e.g. requests:^2.10.0 or requests=2.11.1.", + flag=False, + multiple=True, + ), + option( + "dev-dependency", + None, + "Package to require for development, with an optional version constraint, " + "e.g. requests:^2.10.0 or requests=2.11.1.", + flag=False, + multiple=True, + ), + option("license", "l", "License of the package.", flag=False), option("src", None, "Use the src layout for the project."), option( "readme", @@ -50,6 +76,10 @@ def handle(self) -> None: if not name: name = path.name + description = self.option("description") or "" + license = self.option("license") or "" + version = self.option("package-version") or "0.1.0" + if path.exists() and list(path.glob("*")): # Directory is not empty. Aborting. raise RuntimeError( @@ -59,22 +89,41 @@ def handle(self) -> None: readme_format = self.option("readme") or "md" config = GitConfig() - author = None - if config.get("user.name"): + author = self.option("author") + if not author and config.get("user.name"): author = config["user.name"] author_email = config.get("user.email") if author_email: author += f" <{author_email}>" current_env = SystemEnv(Path(sys.executable)) - default_python = "^" + ".".join(str(v) for v in current_env.version_info[:2]) + + python = self.option("python") + if not python: + python = "^" + ".".join(str(v) for v in current_env.version_info[:2]) + + requirements: Requirements = {} + if self.option("dependency"): + requirements = self._format_requirements( + self._determine_requirements(self.option("dependency")) + ) + + dev_requirements: Requirements = {} + if self.option("dev-dependency"): + dev_requirements = self._format_requirements( + self._determine_requirements(self.option("dev-dependency")) + ) layout_ = layout_cls( name, - "0.1.0", + version, + description=description, author=author, + license=license, + python=python, + dependencies=requirements, + dev_dependencies=dev_requirements, readme_format=readme_format, - python=default_python, ) layout_.create(path) diff --git a/tests/console/commands/test_new.py b/tests/console/commands/test_new.py index 7af8d6f0db7..4179c7a38a3 100644 --- a/tests/console/commands/test_new.py +++ b/tests/console/commands/test_new.py @@ -6,12 +6,14 @@ import pytest from poetry.factory import Factory +from tests.helpers import get_package if TYPE_CHECKING: from cleo.testers.command_tester import CommandTester from poetry.poetry import Poetry + from tests.helpers import TestRepository from tests.types import CommandTesterFactory @@ -23,13 +25,14 @@ def tester(command_tester_factory: CommandTesterFactory) -> CommandTester: def verify_project_directory( path: Path, package_name: str, package_path: str, include_from: str | None = None ) -> Poetry: + module_name = package_name.replace("-", "_") package_path = Path(package_path) assert path.is_dir() pyproject = path / "pyproject.toml" assert pyproject.is_file() - init_file = path / package_path / "__init__.py" + init_file = path / module_name / "__init__.py" assert init_file.is_file() tests_init_file = path / "tests" / "__init__.py" @@ -44,7 +47,7 @@ def verify_project_directory( "from": include_from, } else: - package_include = {"include": package_path.parts[0]} + package_include = {"include": module_name} packages = poetry.local_config.get("packages") @@ -170,3 +173,45 @@ def test_command_new_with_readme(fmt: str | None, tester: CommandTester, tmp_dir poetry = verify_project_directory(path, package, package, None) assert poetry.local_config.get("readme") == f"README.{fmt or 'md'}" + + +def test_command_new_with_dependencies( + tester: CommandTester, repo: TestRepository, tmp_dir: str +): + repo.add_package(get_package("pendulum", "2.0.0")) + repo.add_package(get_package("pytest", "3.6.0")) + + package_path = "package-path" + path = Path(tmp_dir) / package_path + options = [ + path.as_posix(), + "--name custom-package", + "--package-version 1.2.3", + "--description 'My Description'", + "--author 'Your Name ' ", + "--license 'My License'", + "--python '^3.8' ", + "--dependency pendulum", + "--dev-dependency pytest", + ] + tester.execute(" ".join(options)) + poetry = verify_project_directory(path, "custom-package", package_path, None) + + expected = """\ +[tool.poetry] +name = "custom-package" +version = "1.2.3" +description = "My Description" +authors = ["Your Name "] +license = "My License" +readme = "README.md" +packages = [{include = "custom_package"}] + +[tool.poetry.dependencies] +python = "^3.8" +pendulum = "^2.0.0" + +[tool.poetry.group.dev.dependencies] +pytest = "^3.6.0" +""" + assert expected in poetry.pyproject.data.as_string() From e4e036d3858b871728a936a07f51984bc73fc502 Mon Sep 17 00:00:00 2001 From: juhoautio Date: Tue, 31 May 2022 01:07:30 +0300 Subject: [PATCH 02/14] Fix mypy error --- src/poetry/console/commands/new.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/poetry/console/commands/new.py b/src/poetry/console/commands/new.py index ea466f2714d..b658f106ba7 100644 --- a/src/poetry/console/commands/new.py +++ b/src/poetry/console/commands/new.py @@ -53,7 +53,7 @@ class NewCommand(InitCommand): ), ] - def handle(self) -> None: + def handle(self) -> int: from pathlib import Path from poetry.core.vcs.git import GitConfig @@ -136,3 +136,5 @@ def handle(self) -> None: f"Created package {layout_._package_name} in" f" {path.as_posix()}" ) + + return 0 From 5e82630b896d8e9b45e1bd0af0308f0271f66751 Mon Sep 17 00:00:00 2001 From: juhoautio Date: Tue, 31 May 2022 01:21:32 +0300 Subject: [PATCH 03/14] Fix the new test --- tests/console/commands/test_new.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/console/commands/test_new.py b/tests/console/commands/test_new.py index 4179c7a38a3..607fe98e8b1 100644 --- a/tests/console/commands/test_new.py +++ b/tests/console/commands/test_new.py @@ -23,16 +23,18 @@ def tester(command_tester_factory: CommandTesterFactory) -> CommandTester: def verify_project_directory( - path: Path, package_name: str, package_path: str, include_from: str | None = None + path: Path, + package_name: str, + package_path: str, + include_from: str | None = None, ) -> Poetry: - module_name = package_name.replace("-", "_") package_path = Path(package_path) assert path.is_dir() pyproject = path / "pyproject.toml" assert pyproject.is_file() - init_file = path / module_name / "__init__.py" + init_file = path / package_path / "__init__.py" assert init_file.is_file() tests_init_file = path / "tests" / "__init__.py" @@ -47,7 +49,7 @@ def verify_project_directory( "from": include_from, } else: - package_include = {"include": module_name} + package_include = {"include": package_path.parts[0]} packages = poetry.local_config.get("packages") @@ -181,11 +183,11 @@ def test_command_new_with_dependencies( repo.add_package(get_package("pendulum", "2.0.0")) repo.add_package(get_package("pytest", "3.6.0")) - package_path = "package-path" + package_path = "custompackage" path = Path(tmp_dir) / package_path options = [ path.as_posix(), - "--name custom-package", + "--name custompackage", "--package-version 1.2.3", "--description 'My Description'", "--author 'Your Name ' ", @@ -195,17 +197,16 @@ def test_command_new_with_dependencies( "--dev-dependency pytest", ] tester.execute(" ".join(options)) - poetry = verify_project_directory(path, "custom-package", package_path, None) + poetry = verify_project_directory(path, "custompackage", package_path, None) expected = """\ [tool.poetry] -name = "custom-package" +name = "custompackage" version = "1.2.3" description = "My Description" authors = ["Your Name "] license = "My License" readme = "README.md" -packages = [{include = "custom_package"}] [tool.poetry.dependencies] python = "^3.8" From 73c0cfb61f9059fa068684c4b87fdbf3d0fdda6b Mon Sep 17 00:00:00 2001 From: juhoautio Date: Tue, 31 May 2022 01:28:11 +0300 Subject: [PATCH 04/14] Docs: add a missing bracket --- docs/cli.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli.md b/docs/cli.md index e05f3225a47..5cf82470b11 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -107,7 +107,7 @@ my-package * `--author`: Author of the package. * `--python`: Compatible Python versions. * `--license`: License of the package. -* `--dependency`: Package to require with an optional version constraint, e.g. `requests:^2.10.0` or `requests=2.11.1`. (see [add]({{< relref "#add" >}}). +* `--dependency`: Package to require with an optional version constraint, e.g. `requests:^2.10.0` or `requests=2.11.1`. (see [add]({{< relref "#add" >}})). * `--dev-dependency`: Development requirements, see `--dependency`. * `--src`: Use the src layout for the project. * `--readme`: Specify the readme file format. One of `md` (default) or `rst`. From cd0bee953b56b1323086f213f4b0329568e3efd9 Mon Sep 17 00:00:00 2001 From: juhoautio Date: Tue, 31 May 2022 01:28:59 +0300 Subject: [PATCH 05/14] Docs: add a missing bracket --- docs/cli.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli.md b/docs/cli.md index 5cf82470b11..fc88a27e33c 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -130,7 +130,7 @@ poetry init * `--description`: Description of the package. * `--author`: Author of the package. * `--python` Compatible Python versions. -* `--dependency`: Package to require with an optional version constraint, e.g. `requests:^2.10.0` or `requests=2.11.1`. (see [add]({{< relref "#add" >}}). +* `--dependency`: Package to require with an optional version constraint, e.g. `requests:^2.10.0` or `requests=2.11.1`. (see [add]({{< relref "#add" >}})). * `--dev-dependency`: Development requirements, see `--dependency`. From 125e3015bcfeb28f94169b093b538f85662096fc Mon Sep 17 00:00:00 2001 From: juhoautio Date: Tue, 31 May 2022 01:29:53 +0300 Subject: [PATCH 06/14] Docs: add a missing colon --- docs/cli.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli.md b/docs/cli.md index fc88a27e33c..1e6e9e49311 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -129,7 +129,7 @@ poetry init * `--name`: Name of the package. * `--description`: Description of the package. * `--author`: Author of the package. -* `--python` Compatible Python versions. +* `--python`: Compatible Python versions. * `--dependency`: Package to require with an optional version constraint, e.g. `requests:^2.10.0` or `requests=2.11.1`. (see [add]({{< relref "#add" >}})). * `--dev-dependency`: Development requirements, see `--dependency`. From f4a190aa493cac90a19eeb7dadfb1996c6fa98a8 Mon Sep 17 00:00:00 2001 From: juhoautio Date: Tue, 31 May 2022 23:56:14 +0300 Subject: [PATCH 07/14] Try to fix the test on Windows --- tests/console/commands/test_new.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/console/commands/test_new.py b/tests/console/commands/test_new.py index 607fe98e8b1..a567743bd36 100644 --- a/tests/console/commands/test_new.py +++ b/tests/console/commands/test_new.py @@ -215,4 +215,5 @@ def test_command_new_with_dependencies( [tool.poetry.group.dev.dependencies] pytest = "^3.6.0" """ - assert expected in poetry.pyproject.data.as_string() + # Replacing possible \r\n because as_string adds them on Windows + assert expected in poetry.pyproject.data.as_string().replace("\r\n", "\n") From 7d578a10d3d0f1074f94a48fe3ea90f67ddebfa1 Mon Sep 17 00:00:00 2001 From: juhoautio Date: Tue, 7 Jun 2022 23:42:40 +0300 Subject: [PATCH 08/14] Drop -l as a short version of --license --- src/poetry/console/commands/init.py | 2 +- src/poetry/console/commands/new.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/poetry/console/commands/init.py b/src/poetry/console/commands/init.py index 1897829a4a4..5faf78c5d40 100644 --- a/src/poetry/console/commands/init.py +++ b/src/poetry/console/commands/init.py @@ -54,7 +54,7 @@ class InitCommand(Command): flag=False, multiple=True, ), - option("license", "l", "License of the package.", flag=False), + option("license", "License of the package.", flag=False), ] help = """\ diff --git a/src/poetry/console/commands/new.py b/src/poetry/console/commands/new.py index b658f106ba7..a7060e26230 100644 --- a/src/poetry/console/commands/new.py +++ b/src/poetry/console/commands/new.py @@ -43,7 +43,7 @@ class NewCommand(InitCommand): flag=False, multiple=True, ), - option("license", "l", "License of the package.", flag=False), + option("license", "License of the package.", flag=False), option("src", None, "Use the src layout for the project."), option( "readme", From a09c47b38a15c2803c8bec159f857258eac80838 Mon Sep 17 00:00:00 2001 From: juhoautio Date: Wed, 8 Jun 2022 00:44:15 +0300 Subject: [PATCH 09/14] Extract helpers.requirements, don't inherit InitCommand --- src/poetry/console/commands/init.py | 250 +++++++++------------------- src/poetry/console/commands/new.py | 41 ++++- src/poetry/utils/requirements.py | 123 ++++++++++++++ tests/console/commands/test_init.py | 3 +- 4 files changed, 241 insertions(+), 176 deletions(-) create mode 100644 src/poetry/utils/requirements.py diff --git a/src/poetry/console/commands/init.py b/src/poetry/console/commands/init.py index 5faf78c5d40..ef377731598 100644 --- a/src/poetry/console/commands/init.py +++ b/src/poetry/console/commands/init.py @@ -2,29 +2,25 @@ import sys -from pathlib import Path from typing import TYPE_CHECKING from typing import Any -from typing import Dict -from typing import Mapping -from typing import Union from cleo.helpers import option -from tomlkit import inline_table from poetry.console.commands.command import Command from poetry.console.commands.env_command import EnvCommand -from poetry.utils.dependency_specification import parse_dependency_specification from poetry.utils.helpers import canonicalize_name +from poetry.utils.requirements import determine_requirements_from_list +from poetry.utils.requirements import find_best_version_for_package +from poetry.utils.requirements import format_requirements +from poetry.utils.requirements import parse_requirements if TYPE_CHECKING: from poetry.core.packages.package import Package - from tomlkit.items import InlineTable from poetry.repositories import Pool - -Requirements = Dict[str, Union[str, Mapping[str, Any]]] + from poetry.utils.requirements import Requirements class InitCommand(Command): @@ -168,7 +164,7 @@ def handle(self) -> int: requirements: Requirements = {} if self.option("dependency"): - requirements = self._format_requirements( + requirements = format_requirements( self._determine_requirements(self.option("dependency")) ) @@ -190,15 +186,13 @@ def handle(self) -> int: if self.io.is_interactive(): self.line(help_message) help_displayed = True - requirements.update( - self._format_requirements(self._determine_requirements([])) - ) + requirements.update(format_requirements(self._determine_requirements([]))) if self.io.is_interactive(): self.line("") dev_requirements: Requirements = {} if self.option("dev-dependency"): - dev_requirements = self._format_requirements( + dev_requirements = format_requirements( self._determine_requirements(self.option("dev-dependency")) ) @@ -210,7 +204,7 @@ def handle(self) -> int: self.line(help_message) dev_requirements.update( - self._format_requirements(self._determine_requirements([])) + format_requirements(self._determine_requirements([])) ) if self.io.is_interactive(): self.line("") @@ -263,182 +257,102 @@ def _generate_choice_list( return choices - def _determine_requirements( - self, - requires: list[str], - allow_prereleases: bool = False, - source: str | None = None, - ) -> list[dict[str, Any]]: - if not requires: - result = [] - - package = self.ask( - "Search for package to add (or leave blank to continue):" - ) - while package: - constraint = self._parse_requirements([package])[0] - if ( - "git" in constraint - or "url" in constraint - or "path" in constraint - or "version" in constraint - ): - self.line(f"Adding {package}") - result.append(constraint) - package = self.ask("\nAdd a package:") - continue - - canonicalized_name = canonicalize_name(constraint["name"]) - matches = self._get_pool().search(canonicalized_name) - if not matches: - self.line_error("Unable to find package") - package = False - else: - choices = self._generate_choice_list(matches, canonicalized_name) - - info_string = ( - f"Found {len(matches)} packages matching" - f" {package}" - ) - - if len(matches) > 10: - info_string += "\nShowing the first 10 matches" - - self.line(info_string) - - # Default to an empty value to signal no package was selected - choices.append("") + def _determine_requirements_interactive(self) -> list[dict[str, Any]]: + result = [] - package = self.choice( - "\nEnter package # to add, or the complete package name if it" - " is not listed", - choices, - attempts=3, - default=len(choices) - 1, - ) + package = self.ask("Search for package to add (or leave blank to continue):") + while package: + constraint = parse_requirements([package], self, None)[0] + if ( + "git" in constraint + or "url" in constraint + or "path" in constraint + or "version" in constraint + ): + self.line(f"Adding {package}") + result.append(constraint) + package = self.ask("\nAdd a package:") + continue - if not package: - self.line("No package selected") + canonicalized_name = canonicalize_name(constraint["name"]) + matches = self._get_pool().search(canonicalized_name) + if not matches: + self.line_error("Unable to find package") + package = False + else: + choices = self._generate_choice_list(matches, canonicalized_name) - # package selected by user, set constraint name to package name - if package: - constraint["name"] = package + info_string = ( + f"Found {len(matches)} packages matching" + f" {package}" + ) - # no constraint yet, determine the best version automatically - if package and "version" not in constraint: - question = self.create_question( - "Enter the version constraint to require " - "(or leave blank to use the latest version):" - ) - question.attempts = 3 - question.validator = lambda x: (x or "").strip() or False + if len(matches) > 10: + info_string += "\nShowing the first 10 matches" - package_constraint = self.ask(question) + self.line(info_string) - if package_constraint is None: - _, package_constraint = self._find_best_version_for_package( - package - ) + # Default to an empty value to signal no package was selected + choices.append("") - self.line( - f"Using version {package_constraint} for" - f" {package}" - ) + package = self.choice( + "\nEnter package # to add, or the complete package name if it" + " is not listed", + choices, + attempts=3, + default=len(choices) - 1, + ) - constraint["version"] = package_constraint + if not package: + self.line("No package selected") + # package selected by user, set constraint name to package name if package: - result.append(constraint) + constraint["name"] = package - if self.io.is_interactive(): - package = self.ask("\nAdd a package:") + # no constraint yet, determine the best version automatically + if package and "version" not in constraint: + question = self.create_question( + "Enter the version constraint to require " + "(or leave blank to use the latest version):" + ) + question.attempts = 3 + question.validator = lambda x: (x or "").strip() or False - return result + package_constraint = self.ask(question) - result = [] - for requirement in self._parse_requirements(requires): - if "git" in requirement or "url" in requirement or "path" in requirement: - result.append(requirement) - continue - elif "version" not in requirement: - # determine the best version automatically - name, version = self._find_best_version_for_package( - requirement["name"], - allow_prereleases=allow_prereleases, - source=source, - ) - requirement["version"] = version - requirement["name"] = name + if package_constraint is None: + _, package_constraint = find_best_version_for_package( + self._get_pool(), package + ) - self.line(f"Using version {version} for {name}") - else: - # check that the specified version/constraint exists - # before we proceed - name, _ = self._find_best_version_for_package( - requirement["name"], - requirement["version"], - allow_prereleases=allow_prereleases, - source=source, - ) + self.line( + f"Using version {package_constraint} for" + f" {package}" + ) + + constraint["version"] = package_constraint - requirement["name"] = name + if package: + result.append(constraint) - result.append(requirement) + if self.io.is_interactive(): + package = self.ask("\nAdd a package:") return result - def _find_best_version_for_package( + def _determine_requirements( self, - name: str, - required_version: str | None = None, + requires: list[str], allow_prereleases: bool = False, source: str | None = None, - ) -> tuple[str, str]: - from poetry.version.version_selector import VersionSelector - - selector = VersionSelector(self._get_pool()) - package = selector.find_best_candidate( - name, required_version, allow_prereleases=allow_prereleases, source=source - ) - - if not package: - # TODO: find similar - raise ValueError(f"Could not find a matching version of package {name}") - - return package.pretty_name, selector.find_recommended_require_version(package) - - def _parse_requirements(self, requirements: list[str]) -> list[dict[str, Any]]: - from poetry.core.pyproject.exceptions import PyProjectException - - try: - cwd = self.poetry.file.parent - except (PyProjectException, RuntimeError): - cwd = Path.cwd() - - return [ - parse_dependency_specification( - requirement=requirement, - env=self.env if isinstance(self, EnvCommand) else None, - cwd=cwd, + ) -> list[dict[str, Any]]: + if not requires: + return self._determine_requirements_interactive() + else: + return determine_requirements_from_list( + self, self._get_pool(), requires, allow_prereleases, source ) - for requirement in requirements - ] - - def _format_requirements(self, requirements: list[dict[str, str]]) -> Requirements: - requires: Requirements = {} - for requirement in requirements: - name = requirement.pop("name") - constraint: str | InlineTable - if "version" in requirement and len(requirement) == 1: - constraint = requirement["version"] - else: - constraint = inline_table() - constraint.trivia.trail = "\n" - constraint.update(requirement) - - requires[name] = constraint - - return requires def _validate_author(self, author: str, default: str) -> str | None: from poetry.core.packages.package import AUTHOR_REGEX diff --git a/src/poetry/console/commands/new.py b/src/poetry/console/commands/new.py index a7060e26230..8c283b78aee 100644 --- a/src/poetry/console/commands/new.py +++ b/src/poetry/console/commands/new.py @@ -8,14 +8,18 @@ from cleo.helpers import argument from cleo.helpers import option -from poetry.console.commands.init import InitCommand +from poetry.console.commands.command import Command +from poetry.console.commands.env_command import EnvCommand +from poetry.utils.requirements import determine_requirements_from_list +from poetry.utils.requirements import format_requirements if TYPE_CHECKING: - from poetry.console.commands.init import Requirements + from poetry.repositories import Pool + from poetry.utils.requirements import Requirements -class NewCommand(InitCommand): +class NewCommand(Command): name = "new" description = "Creates a new Python project at ." @@ -53,6 +57,11 @@ class NewCommand(InitCommand): ), ] + def __init__(self) -> None: + super().__init__() + + self._pool: Pool | None = None + def handle(self) -> int: from pathlib import Path @@ -104,14 +113,18 @@ def handle(self) -> int: requirements: Requirements = {} if self.option("dependency"): - requirements = self._format_requirements( - self._determine_requirements(self.option("dependency")) + requirements = format_requirements( + determine_requirements_from_list( + self, self._get_pool(), self.option("dependency") + ) ) dev_requirements: Requirements = {} if self.option("dev-dependency"): - dev_requirements = self._format_requirements( - self._determine_requirements(self.option("dev-dependency")) + dev_requirements = format_requirements( + determine_requirements_from_list( + self, self._get_pool(), self.option("dev-dependency") + ) ) layout_ = layout_cls( @@ -138,3 +151,17 @@ def handle(self) -> int: ) return 0 + + # TODO this code is duplicated with init.py. how to abstract nicely? + def _get_pool(self) -> Pool: + from poetry.repositories import Pool + from poetry.repositories.pypi_repository import PyPiRepository + + if isinstance(self, EnvCommand): + return self.poetry.pool + + if self._pool is None: + self._pool = Pool() + self._pool.add_repository(PyPiRepository()) + + return self._pool diff --git a/src/poetry/utils/requirements.py b/src/poetry/utils/requirements.py new file mode 100644 index 00000000000..4937bcfa919 --- /dev/null +++ b/src/poetry/utils/requirements.py @@ -0,0 +1,123 @@ +from __future__ import annotations + +from pathlib import Path +from typing import TYPE_CHECKING +from typing import Any +from typing import Dict +from typing import Mapping +from typing import Union + +from tomlkit import inline_table + +from poetry.utils.dependency_specification import parse_dependency_specification + + +if TYPE_CHECKING: + from tomlkit.items import InlineTable + + from poetry.console.commands.command import Command + from poetry.repositories import Pool + from poetry.utils.env import Env + + +Requirements = Dict[str, Union[str, Mapping[str, Any]]] + + +def parse_requirements( + requirements: list[str], command: Command, env: Env | None +) -> list[dict[str, Any]]: + from poetry.core.pyproject.exceptions import PyProjectException + + try: + cwd = command.poetry.file.parent + except (PyProjectException, RuntimeError): + cwd = Path.cwd() + + return [ + parse_dependency_specification( + requirement=requirement, + env=env, + cwd=cwd, + ) + for requirement in requirements + ] + + +def format_requirements(requirements: list[dict[str, str]]) -> Requirements: + requires: Requirements = {} + for requirement in requirements: + name = requirement.pop("name") + constraint: str | InlineTable + if "version" in requirement and len(requirement) == 1: + constraint = requirement["version"] + else: + constraint = inline_table() + constraint.trivia.trail = "\n" + constraint.update(requirement) + + requires[name] = constraint + + return requires + + +def find_best_version_for_package( + pool: Pool, + name: str, + required_version: str | None = None, + allow_prereleases: bool = False, + source: str | None = None, +) -> tuple[str, str]: + from poetry.version.version_selector import VersionSelector + + selector = VersionSelector(pool) + package = selector.find_best_candidate( + name, required_version, allow_prereleases=allow_prereleases, source=source + ) + + if not package: + # TODO: find similar + raise ValueError(f"Could not find a matching version of package {name}") + + return package.pretty_name, selector.find_recommended_require_version(package) + + +def determine_requirements_from_list( + command: Command, + pool: Pool, + requires: list[str], + allow_prereleases: bool = False, + source: str | None = None, +) -> list[dict[str, Any]]: + result = [] + for requirement in parse_requirements(requires, command, None): + if "git" in requirement or "url" in requirement or "path" in requirement: + result.append(requirement) + continue + elif "version" not in requirement: + # determine the best version automatically + name, version = find_best_version_for_package( + pool, + requirement["name"], + allow_prereleases=allow_prereleases, + source=source, + ) + requirement["version"] = version + requirement["name"] = name + + command.line(f"Using version {version} for {name}") + else: + # check that the specified version/constraint exists + # before we proceed + name, _ = find_best_version_for_package( + pool, + requirement["name"], + requirement["version"], + allow_prereleases=allow_prereleases, + source=source, + ) + + requirement["name"] = name + + result.append(requirement) + + return result diff --git a/tests/console/commands/test_init.py b/tests/console/commands/test_init.py index c733bba2dfb..64e32d3f038 100644 --- a/tests/console/commands/test_init.py +++ b/tests/console/commands/test_init.py @@ -14,6 +14,7 @@ from poetry.repositories import Pool from poetry.utils._compat import decode from poetry.utils.helpers import canonicalize_name +from poetry.utils.requirements import parse_requirements from tests.helpers import PoetryTestApplication from tests.helpers import get_package @@ -780,7 +781,7 @@ def test_predefined_and_interactive_dev_dependencies( def test_add_package_with_extras_and_whitespace(tester: CommandTester): - result = tester.command._parse_requirements(["databases[postgresql, sqlite]"]) + result = parse_requirements(["databases[postgresql, sqlite]"], tester.command, None) assert result[0]["name"] == "databases" assert len(result[0]["extras"]) == 2 From efb1336dc475f903573ec47f31a78ae68a706535 Mon Sep 17 00:00:00 2001 From: Juho Autio Date: Mon, 12 Sep 2022 23:03:23 +0300 Subject: [PATCH 10/14] Extract function: create_pool --- src/poetry/console/commands/init.py | 7 ++----- src/poetry/console/commands/new.py | 8 ++------ src/poetry/utils/requirements.py | 10 ++++++++++ 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/poetry/console/commands/init.py b/src/poetry/console/commands/init.py index fb95ad3e833..7e44d2abbe3 100644 --- a/src/poetry/console/commands/init.py +++ b/src/poetry/console/commands/init.py @@ -10,6 +10,7 @@ from poetry.console.commands.command import Command from poetry.console.commands.env_command import EnvCommand +from poetry.utils.requirements import create_pool from poetry.utils.requirements import determine_requirements_from_list from poetry.utils.requirements import find_best_version_for_package from poetry.utils.requirements import format_requirements @@ -379,14 +380,10 @@ def _validate_package(package: str | None) -> str | None: return package def _get_pool(self) -> Pool: - from poetry.repositories import Pool - from poetry.repositories.pypi_repository import PyPiRepository - if isinstance(self, EnvCommand): return self.poetry.pool if self._pool is None: - self._pool = Pool() - self._pool.add_repository(PyPiRepository()) + self._pool = create_pool() return self._pool diff --git a/src/poetry/console/commands/new.py b/src/poetry/console/commands/new.py index b97fbec92ef..aa8485bbcd1 100644 --- a/src/poetry/console/commands/new.py +++ b/src/poetry/console/commands/new.py @@ -10,6 +10,7 @@ from poetry.console.commands.command import Command from poetry.console.commands.env_command import EnvCommand +from poetry.utils.requirements import create_pool from poetry.utils.requirements import determine_requirements_from_list from poetry.utils.requirements import format_requirements @@ -151,16 +152,11 @@ def handle(self) -> int: return 0 - # TODO this code is duplicated with init.py. how to abstract nicely? def _get_pool(self) -> Pool: - from poetry.repositories import Pool - from poetry.repositories.pypi_repository import PyPiRepository - if isinstance(self, EnvCommand): return self.poetry.pool if self._pool is None: - self._pool = Pool() - self._pool.add_repository(PyPiRepository()) + self._pool = create_pool() return self._pool diff --git a/src/poetry/utils/requirements.py b/src/poetry/utils/requirements.py index 4937bcfa919..3472200b948 100644 --- a/src/poetry/utils/requirements.py +++ b/src/poetry/utils/requirements.py @@ -121,3 +121,13 @@ def determine_requirements_from_list( result.append(requirement) return result + + +def create_pool() -> Pool: + from poetry.repositories import Pool + from poetry.repositories.pypi_repository import PyPiRepository + + pool = Pool() + pool.add_repository(PyPiRepository()) + + return pool From ad7e353a7b259ab856dd9b3411b3923bc6f426ad Mon Sep 17 00:00:00 2001 From: Juho Autio Date: Sat, 24 Sep 2022 10:40:31 +0300 Subject: [PATCH 11/14] Drop --dev-dependencies from `new` --- docs/cli.md | 1 - src/poetry/console/commands/new.py | 17 ----------------- tests/console/commands/test_new.py | 4 ---- 3 files changed, 22 deletions(-) diff --git a/docs/cli.md b/docs/cli.md index 6a77dcd58b4..790256df059 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -108,7 +108,6 @@ my-package * `--python`: Compatible Python versions. * `--license`: License of the package. * `--dependency`: Package to require with an optional version constraint, e.g. `requests:^2.10.0` or `requests=2.11.1`. (see [add]({{< relref "#add" >}})). -* `--dev-dependency`: Development requirements, see `--dependency`. * `--src`: Use the src layout for the project. * `--readme`: Specify the readme file extension. Default is `md`. If you intend to publish to PyPI keep the [recommendations for a PyPI-friendly README](https://packaging.python.org/en/latest/guides/making-a-pypi-friendly-readme/) diff --git a/src/poetry/console/commands/new.py b/src/poetry/console/commands/new.py index aa8485bbcd1..59a9d7a7f55 100644 --- a/src/poetry/console/commands/new.py +++ b/src/poetry/console/commands/new.py @@ -39,14 +39,6 @@ class NewCommand(Command): flag=False, multiple=True, ), - option( - "dev-dependency", - None, - "Package to require for development, with an optional version constraint, " - "e.g. requests:^2.10.0 or requests=2.11.1.", - flag=False, - multiple=True, - ), option("license", "License of the package.", flag=False), option("src", None, "Use the src layout for the project."), option( @@ -119,14 +111,6 @@ def handle(self) -> int: ) ) - dev_requirements: Requirements = {} - if self.option("dev-dependency"): - dev_requirements = format_requirements( - determine_requirements_from_list( - self, self._get_pool(), self.option("dev-dependency") - ) - ) - layout_ = layout_cls( name, version, @@ -135,7 +119,6 @@ def handle(self) -> int: license=license, python=python, dependencies=requirements, - dev_dependencies=dev_requirements, readme_format=readme_format, ) layout_.create(path) diff --git a/tests/console/commands/test_new.py b/tests/console/commands/test_new.py index 0eed9f097da..b6cb5a5e14f 100644 --- a/tests/console/commands/test_new.py +++ b/tests/console/commands/test_new.py @@ -194,7 +194,6 @@ def test_command_new_with_dependencies( "--license 'My License'", "--python '^3.8' ", "--dependency pendulum", - "--dev-dependency pytest", ] tester.execute(" ".join(options)) poetry = verify_project_directory(path, "custompackage", package_path, None) @@ -211,9 +210,6 @@ def test_command_new_with_dependencies( [tool.poetry.dependencies] python = "^3.8" pendulum = "^2.0.0" - -[tool.poetry.group.dev.dependencies] -pytest = "^3.6.0" """ # Replacing possible \r\n because as_string adds them on Windows assert expected in poetry.pyproject.data.as_string().replace("\r\n", "\n") From 865c652d1a382f61d3cfb2465400cb94b4f1b8bd Mon Sep 17 00:00:00 2001 From: Juho Autio Date: Mon, 10 Oct 2022 22:35:42 +0300 Subject: [PATCH 12/14] Move pool creation inline --- src/poetry/console/commands/init.py | 9 ++++----- src/poetry/console/commands/new.py | 9 ++++----- src/poetry/utils/requirements.py | 10 ---------- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/poetry/console/commands/init.py b/src/poetry/console/commands/init.py index 7e44d2abbe3..6fc8e5b0c0e 100644 --- a/src/poetry/console/commands/init.py +++ b/src/poetry/console/commands/init.py @@ -9,8 +9,6 @@ from packaging.utils import canonicalize_name from poetry.console.commands.command import Command -from poetry.console.commands.env_command import EnvCommand -from poetry.utils.requirements import create_pool from poetry.utils.requirements import determine_requirements_from_list from poetry.utils.requirements import find_best_version_for_package from poetry.utils.requirements import format_requirements @@ -380,10 +378,11 @@ def _validate_package(package: str | None) -> str | None: return package def _get_pool(self) -> Pool: - if isinstance(self, EnvCommand): - return self.poetry.pool + from poetry.repositories import Pool + from poetry.repositories.pypi_repository import PyPiRepository if self._pool is None: - self._pool = create_pool() + self._pool = Pool() + self._pool.add_repository(PyPiRepository()) return self._pool diff --git a/src/poetry/console/commands/new.py b/src/poetry/console/commands/new.py index 59a9d7a7f55..fc09b421528 100644 --- a/src/poetry/console/commands/new.py +++ b/src/poetry/console/commands/new.py @@ -9,8 +9,6 @@ from cleo.helpers import option from poetry.console.commands.command import Command -from poetry.console.commands.env_command import EnvCommand -from poetry.utils.requirements import create_pool from poetry.utils.requirements import determine_requirements_from_list from poetry.utils.requirements import format_requirements @@ -136,10 +134,11 @@ def handle(self) -> int: return 0 def _get_pool(self) -> Pool: - if isinstance(self, EnvCommand): - return self.poetry.pool + from poetry.repositories import Pool + from poetry.repositories.pypi_repository import PyPiRepository if self._pool is None: - self._pool = create_pool() + self._pool = Pool() + self._pool.add_repository(PyPiRepository()) return self._pool diff --git a/src/poetry/utils/requirements.py b/src/poetry/utils/requirements.py index 3472200b948..4937bcfa919 100644 --- a/src/poetry/utils/requirements.py +++ b/src/poetry/utils/requirements.py @@ -121,13 +121,3 @@ def determine_requirements_from_list( result.append(requirement) return result - - -def create_pool() -> Pool: - from poetry.repositories import Pool - from poetry.repositories.pypi_repository import PyPiRepository - - pool = Pool() - pool.add_repository(PyPiRepository()) - - return pool From b3e5364a3dbc0748be568372bad362191786c864 Mon Sep 17 00:00:00 2001 From: Juho Autio Date: Mon, 10 Oct 2022 23:15:12 +0300 Subject: [PATCH 13/14] Pool -> AbstractRepository --- src/poetry/console/commands/init.py | 20 +++++++++----------- src/poetry/console/commands/new.py | 16 +++++++--------- src/poetry/console/commands/show.py | 2 +- src/poetry/utils/requirements.py | 12 ++++++------ src/poetry/version/version_selector.py | 8 ++++---- tests/console/commands/test_init.py | 5 ++--- tests/console/commands/test_new.py | 1 + 7 files changed, 30 insertions(+), 34 deletions(-) diff --git a/src/poetry/console/commands/init.py b/src/poetry/console/commands/init.py index 6fc8e5b0c0e..6fd50954b31 100644 --- a/src/poetry/console/commands/init.py +++ b/src/poetry/console/commands/init.py @@ -19,7 +19,7 @@ from packaging.utils import NormalizedName from poetry.core.packages.package import Package - from poetry.repositories import Pool + from poetry.repositories.abstract_repository import AbstractRepository from poetry.utils.requirements import Requirements @@ -61,7 +61,7 @@ class InitCommand(Command): def __init__(self) -> None: super().__init__() - self._pool: Pool | None = None + self._repository: AbstractRepository | None = None def handle(self) -> int: from pathlib import Path @@ -274,7 +274,7 @@ def _determine_requirements_interactive(self) -> list[dict[str, Any]]: continue canonicalized_name = canonicalize_name(constraint["name"]) - matches = self._get_pool().search(canonicalized_name) + matches = self._get_repository().search(canonicalized_name) if not matches: self.line_error("Unable to find package") package = False @@ -322,7 +322,7 @@ def _determine_requirements_interactive(self) -> list[dict[str, Any]]: if package_constraint is None: _, package_constraint = find_best_version_for_package( - self._get_pool(), package + self._get_repository(), package ) self.line( @@ -350,7 +350,7 @@ def _determine_requirements( return self._determine_requirements_interactive() else: return determine_requirements_from_list( - self, self._get_pool(), requires, allow_prereleases, source + self, self._get_repository(), requires, allow_prereleases, source ) def _validate_author(self, author: str, default: str) -> str | None: @@ -377,12 +377,10 @@ def _validate_package(package: str | None) -> str | None: return package - def _get_pool(self) -> Pool: - from poetry.repositories import Pool + def _get_repository(self) -> AbstractRepository: from poetry.repositories.pypi_repository import PyPiRepository - if self._pool is None: - self._pool = Pool() - self._pool.add_repository(PyPiRepository()) + if self._repository is None: + self._repository = PyPiRepository() - return self._pool + return self._repository diff --git a/src/poetry/console/commands/new.py b/src/poetry/console/commands/new.py index fc09b421528..52edbf4cfd8 100644 --- a/src/poetry/console/commands/new.py +++ b/src/poetry/console/commands/new.py @@ -14,7 +14,7 @@ if TYPE_CHECKING: - from poetry.repositories import Pool + from poetry.repositories.abstract_repository import AbstractRepository from poetry.utils.requirements import Requirements @@ -50,7 +50,7 @@ class NewCommand(Command): def __init__(self) -> None: super().__init__() - self._pool: Pool | None = None + self._repository: AbstractRepository | None = None def handle(self) -> int: from pathlib import Path @@ -105,7 +105,7 @@ def handle(self) -> int: if self.option("dependency"): requirements = format_requirements( determine_requirements_from_list( - self, self._get_pool(), self.option("dependency") + self, self._get_repository(), self.option("dependency") ) ) @@ -133,12 +133,10 @@ def handle(self) -> int: return 0 - def _get_pool(self) -> Pool: - from poetry.repositories import Pool + def _get_repository(self) -> AbstractRepository: from poetry.repositories.pypi_repository import PyPiRepository - if self._pool is None: - self._pool = Pool() - self._pool.add_repository(PyPiRepository()) + if self._repository is None: + self._repository = PyPiRepository() - return self._pool + return self._repository diff --git a/src/poetry/console/commands/show.py b/src/poetry/console/commands/show.py index 7b0c55b3e24..fe8b2b150e8 100644 --- a/src/poetry/console/commands/show.py +++ b/src/poetry/console/commands/show.py @@ -528,7 +528,7 @@ def find_latest_package( return provider.search_for_direct_origin_dependency(dep) name = package.name - selector = VersionSelector(self.poetry.pool) + selector = VersionSelector(self.poetry.pool.repositories[0]) return selector.find_best_candidate(name, f">={package.pretty_version}") diff --git a/src/poetry/utils/requirements.py b/src/poetry/utils/requirements.py index 4937bcfa919..1ddfb265880 100644 --- a/src/poetry/utils/requirements.py +++ b/src/poetry/utils/requirements.py @@ -16,7 +16,7 @@ from tomlkit.items import InlineTable from poetry.console.commands.command import Command - from poetry.repositories import Pool + from poetry.repositories.abstract_repository import AbstractRepository from poetry.utils.env import Env @@ -61,7 +61,7 @@ def format_requirements(requirements: list[dict[str, str]]) -> Requirements: def find_best_version_for_package( - pool: Pool, + repository: AbstractRepository, name: str, required_version: str | None = None, allow_prereleases: bool = False, @@ -69,7 +69,7 @@ def find_best_version_for_package( ) -> tuple[str, str]: from poetry.version.version_selector import VersionSelector - selector = VersionSelector(pool) + selector = VersionSelector(repository) package = selector.find_best_candidate( name, required_version, allow_prereleases=allow_prereleases, source=source ) @@ -83,7 +83,7 @@ def find_best_version_for_package( def determine_requirements_from_list( command: Command, - pool: Pool, + repository: AbstractRepository, requires: list[str], allow_prereleases: bool = False, source: str | None = None, @@ -96,7 +96,7 @@ def determine_requirements_from_list( elif "version" not in requirement: # determine the best version automatically name, version = find_best_version_for_package( - pool, + repository, requirement["name"], allow_prereleases=allow_prereleases, source=source, @@ -109,7 +109,7 @@ def determine_requirements_from_list( # check that the specified version/constraint exists # before we proceed name, _ = find_best_version_for_package( - pool, + repository, requirement["name"], requirement["version"], allow_prereleases=allow_prereleases, diff --git a/src/poetry/version/version_selector.py b/src/poetry/version/version_selector.py index 476e3635444..63cb9d01bf1 100644 --- a/src/poetry/version/version_selector.py +++ b/src/poetry/version/version_selector.py @@ -8,12 +8,12 @@ if TYPE_CHECKING: from poetry.core.packages.package import Package - from poetry.repositories import Pool + from poetry.repositories.abstract_repository import AbstractRepository class VersionSelector: - def __init__(self, pool: Pool) -> None: - self._pool = pool + def __init__(self, repository: AbstractRepository) -> None: + self._repository = repository def find_best_candidate( self, @@ -36,7 +36,7 @@ def find_best_candidate( "source": source, }, ) - candidates = self._pool.find_packages(dependency) + candidates = self._repository.find_packages(dependency) only_prereleases = all(c.version.is_unstable() for c in candidates) if not candidates: diff --git a/tests/console/commands/test_init.py b/tests/console/commands/test_init.py index d5032feb7b0..4e02348b42d 100644 --- a/tests/console/commands/test_init.py +++ b/tests/console/commands/test_init.py @@ -13,7 +13,6 @@ from packaging.utils import canonicalize_name from poetry.console.commands.init import InitCommand -from poetry.repositories import Pool from poetry.utils._compat import decode from poetry.utils.requirements import parse_requirements from tests.helpers import PoetryTestApplication @@ -47,7 +46,7 @@ def source_dir(tmp_path: Path) -> Iterator[Path]: def patches(mocker: MockerFixture, source_dir: Path, repo: TestRepository) -> None: mocker.patch("pathlib.Path.cwd", return_value=source_dir) mocker.patch( - "poetry.console.commands.init.InitCommand._get_pool", return_value=Pool([repo]) + "poetry.console.commands.init.InitCommand._get_repository", return_value=repo ) @@ -107,7 +106,7 @@ def test_noninteractive( tmp_path: Path, ): command = app.find("init") - command._pool = poetry.pool + command._repository = repo repo.add_package(get_package("pytest", "3.6.0")) diff --git a/tests/console/commands/test_new.py b/tests/console/commands/test_new.py index b6cb5a5e14f..f144e74aab9 100644 --- a/tests/console/commands/test_new.py +++ b/tests/console/commands/test_new.py @@ -182,6 +182,7 @@ def test_command_new_with_dependencies( ): repo.add_package(get_package("pendulum", "2.0.0")) repo.add_package(get_package("pytest", "3.6.0")) + tester.command._repository = repo package_path = "custompackage" path = Path(tmp_dir) / package_path From 4341009eb08069e40c0d67db77c3edd8a2bf3334 Mon Sep 17 00:00:00 2001 From: Juho Autio Date: Mon, 10 Oct 2022 23:52:51 +0300 Subject: [PATCH 14/14] Revert "Pool -> AbstractRepository" This reverts commit b3e5364a3dbc0748be568372bad362191786c864. --- src/poetry/console/commands/init.py | 20 +++++++++++--------- src/poetry/console/commands/new.py | 16 +++++++++------- src/poetry/console/commands/show.py | 2 +- src/poetry/utils/requirements.py | 12 ++++++------ src/poetry/version/version_selector.py | 8 ++++---- tests/console/commands/test_init.py | 5 +++-- tests/console/commands/test_new.py | 1 - 7 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/poetry/console/commands/init.py b/src/poetry/console/commands/init.py index 6fd50954b31..6fc8e5b0c0e 100644 --- a/src/poetry/console/commands/init.py +++ b/src/poetry/console/commands/init.py @@ -19,7 +19,7 @@ from packaging.utils import NormalizedName from poetry.core.packages.package import Package - from poetry.repositories.abstract_repository import AbstractRepository + from poetry.repositories import Pool from poetry.utils.requirements import Requirements @@ -61,7 +61,7 @@ class InitCommand(Command): def __init__(self) -> None: super().__init__() - self._repository: AbstractRepository | None = None + self._pool: Pool | None = None def handle(self) -> int: from pathlib import Path @@ -274,7 +274,7 @@ def _determine_requirements_interactive(self) -> list[dict[str, Any]]: continue canonicalized_name = canonicalize_name(constraint["name"]) - matches = self._get_repository().search(canonicalized_name) + matches = self._get_pool().search(canonicalized_name) if not matches: self.line_error("Unable to find package") package = False @@ -322,7 +322,7 @@ def _determine_requirements_interactive(self) -> list[dict[str, Any]]: if package_constraint is None: _, package_constraint = find_best_version_for_package( - self._get_repository(), package + self._get_pool(), package ) self.line( @@ -350,7 +350,7 @@ def _determine_requirements( return self._determine_requirements_interactive() else: return determine_requirements_from_list( - self, self._get_repository(), requires, allow_prereleases, source + self, self._get_pool(), requires, allow_prereleases, source ) def _validate_author(self, author: str, default: str) -> str | None: @@ -377,10 +377,12 @@ def _validate_package(package: str | None) -> str | None: return package - def _get_repository(self) -> AbstractRepository: + def _get_pool(self) -> Pool: + from poetry.repositories import Pool from poetry.repositories.pypi_repository import PyPiRepository - if self._repository is None: - self._repository = PyPiRepository() + if self._pool is None: + self._pool = Pool() + self._pool.add_repository(PyPiRepository()) - return self._repository + return self._pool diff --git a/src/poetry/console/commands/new.py b/src/poetry/console/commands/new.py index 52edbf4cfd8..fc09b421528 100644 --- a/src/poetry/console/commands/new.py +++ b/src/poetry/console/commands/new.py @@ -14,7 +14,7 @@ if TYPE_CHECKING: - from poetry.repositories.abstract_repository import AbstractRepository + from poetry.repositories import Pool from poetry.utils.requirements import Requirements @@ -50,7 +50,7 @@ class NewCommand(Command): def __init__(self) -> None: super().__init__() - self._repository: AbstractRepository | None = None + self._pool: Pool | None = None def handle(self) -> int: from pathlib import Path @@ -105,7 +105,7 @@ def handle(self) -> int: if self.option("dependency"): requirements = format_requirements( determine_requirements_from_list( - self, self._get_repository(), self.option("dependency") + self, self._get_pool(), self.option("dependency") ) ) @@ -133,10 +133,12 @@ def handle(self) -> int: return 0 - def _get_repository(self) -> AbstractRepository: + def _get_pool(self) -> Pool: + from poetry.repositories import Pool from poetry.repositories.pypi_repository import PyPiRepository - if self._repository is None: - self._repository = PyPiRepository() + if self._pool is None: + self._pool = Pool() + self._pool.add_repository(PyPiRepository()) - return self._repository + return self._pool diff --git a/src/poetry/console/commands/show.py b/src/poetry/console/commands/show.py index fe8b2b150e8..7b0c55b3e24 100644 --- a/src/poetry/console/commands/show.py +++ b/src/poetry/console/commands/show.py @@ -528,7 +528,7 @@ def find_latest_package( return provider.search_for_direct_origin_dependency(dep) name = package.name - selector = VersionSelector(self.poetry.pool.repositories[0]) + selector = VersionSelector(self.poetry.pool) return selector.find_best_candidate(name, f">={package.pretty_version}") diff --git a/src/poetry/utils/requirements.py b/src/poetry/utils/requirements.py index 1ddfb265880..4937bcfa919 100644 --- a/src/poetry/utils/requirements.py +++ b/src/poetry/utils/requirements.py @@ -16,7 +16,7 @@ from tomlkit.items import InlineTable from poetry.console.commands.command import Command - from poetry.repositories.abstract_repository import AbstractRepository + from poetry.repositories import Pool from poetry.utils.env import Env @@ -61,7 +61,7 @@ def format_requirements(requirements: list[dict[str, str]]) -> Requirements: def find_best_version_for_package( - repository: AbstractRepository, + pool: Pool, name: str, required_version: str | None = None, allow_prereleases: bool = False, @@ -69,7 +69,7 @@ def find_best_version_for_package( ) -> tuple[str, str]: from poetry.version.version_selector import VersionSelector - selector = VersionSelector(repository) + selector = VersionSelector(pool) package = selector.find_best_candidate( name, required_version, allow_prereleases=allow_prereleases, source=source ) @@ -83,7 +83,7 @@ def find_best_version_for_package( def determine_requirements_from_list( command: Command, - repository: AbstractRepository, + pool: Pool, requires: list[str], allow_prereleases: bool = False, source: str | None = None, @@ -96,7 +96,7 @@ def determine_requirements_from_list( elif "version" not in requirement: # determine the best version automatically name, version = find_best_version_for_package( - repository, + pool, requirement["name"], allow_prereleases=allow_prereleases, source=source, @@ -109,7 +109,7 @@ def determine_requirements_from_list( # check that the specified version/constraint exists # before we proceed name, _ = find_best_version_for_package( - repository, + pool, requirement["name"], requirement["version"], allow_prereleases=allow_prereleases, diff --git a/src/poetry/version/version_selector.py b/src/poetry/version/version_selector.py index 63cb9d01bf1..476e3635444 100644 --- a/src/poetry/version/version_selector.py +++ b/src/poetry/version/version_selector.py @@ -8,12 +8,12 @@ if TYPE_CHECKING: from poetry.core.packages.package import Package - from poetry.repositories.abstract_repository import AbstractRepository + from poetry.repositories import Pool class VersionSelector: - def __init__(self, repository: AbstractRepository) -> None: - self._repository = repository + def __init__(self, pool: Pool) -> None: + self._pool = pool def find_best_candidate( self, @@ -36,7 +36,7 @@ def find_best_candidate( "source": source, }, ) - candidates = self._repository.find_packages(dependency) + candidates = self._pool.find_packages(dependency) only_prereleases = all(c.version.is_unstable() for c in candidates) if not candidates: diff --git a/tests/console/commands/test_init.py b/tests/console/commands/test_init.py index 4e02348b42d..d5032feb7b0 100644 --- a/tests/console/commands/test_init.py +++ b/tests/console/commands/test_init.py @@ -13,6 +13,7 @@ from packaging.utils import canonicalize_name from poetry.console.commands.init import InitCommand +from poetry.repositories import Pool from poetry.utils._compat import decode from poetry.utils.requirements import parse_requirements from tests.helpers import PoetryTestApplication @@ -46,7 +47,7 @@ def source_dir(tmp_path: Path) -> Iterator[Path]: def patches(mocker: MockerFixture, source_dir: Path, repo: TestRepository) -> None: mocker.patch("pathlib.Path.cwd", return_value=source_dir) mocker.patch( - "poetry.console.commands.init.InitCommand._get_repository", return_value=repo + "poetry.console.commands.init.InitCommand._get_pool", return_value=Pool([repo]) ) @@ -106,7 +107,7 @@ def test_noninteractive( tmp_path: Path, ): command = app.find("init") - command._repository = repo + command._pool = poetry.pool repo.add_package(get_package("pytest", "3.6.0")) diff --git a/tests/console/commands/test_new.py b/tests/console/commands/test_new.py index f144e74aab9..b6cb5a5e14f 100644 --- a/tests/console/commands/test_new.py +++ b/tests/console/commands/test_new.py @@ -182,7 +182,6 @@ def test_command_new_with_dependencies( ): repo.add_package(get_package("pendulum", "2.0.0")) repo.add_package(get_package("pytest", "3.6.0")) - tester.command._repository = repo package_path = "custompackage" path = Path(tmp_dir) / package_path