"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "poetry/console/commands/init.py" between
poetry-1.1.15.tar.gz and poetry-1.2.0.tar.gz

About: Poetry is a tool for dependency management and packaging in Python.

init.py  (poetry-1.1.15):init.py  (poetry-1.2.0)
# -*- coding: utf-8 -*- from __future__ import annotations
from __future__ import unicode_literals
import os
import re
import sys import sys
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
from typing import Dict from typing import Dict
from typing import List from typing import Mapping
from typing import Tuple
from typing import Union from typing import Union
from cleo import option from cleo.helpers import option
from packaging.utils import canonicalize_name
from tomlkit import inline_table from tomlkit import inline_table
from poetry.core.pyproject import PyProjectException from poetry.console.commands.command import Command
from poetry.core.pyproject.toml import PyProjectTOML from poetry.console.commands.env_command import EnvCommand
from poetry.utils._compat import OrderedDict from poetry.utils.dependency_specification import parse_dependency_specification
from poetry.utils._compat import Path
from poetry.utils._compat import urlparse if TYPE_CHECKING:
from packaging.utils import NormalizedName
from poetry.core.packages.package import Package
from tomlkit.items import InlineTable
from .command import Command from poetry.repositories import Pool
from .env_command import EnvCommand
Requirements = Dict[str, Union[str, Mapping[str, Any]]]
class InitCommand(Command): class InitCommand(Command):
name = "init" name = "init"
description = ( description = (
"Creates a basic <comment>pyproject.toml</> file in the current director y." "Creates a basic <comment>pyproject.toml</> file in the current director y."
) )
options = [ options = [
option("name", None, "Name of the package.", flag=False), option("name", None, "Name of the package.", flag=False),
option("description", None, "Description of the package.", flag=False), option("description", None, "Description of the package.", flag=False),
skipping to change at line 56 skipping to change at line 60
None, None,
"Package to require for development, with an optional version constr aint, " "Package to require for development, with an optional version constr aint, "
"e.g. requests:^2.10.0 or requests=2.11.1.", "e.g. requests:^2.10.0 or requests=2.11.1.",
flag=False, flag=False,
multiple=True, multiple=True,
), ),
option("license", "l", "License of the package.", flag=False), option("license", "l", "License of the package.", flag=False),
] ]
help = """\ help = """\
The <c1>init</c1> command creates a basic <comment>pyproject.toml</> file in the The <c1>init</c1> command creates a basic <comment>pyproject.toml</> file in the
current directory. \
current directory.
""" """
def __init__(self): def __init__(self) -> None:
super(InitCommand, self).__init__() super().__init__()
self._pool: Pool | None = None
self._pool = None def handle(self) -> int:
from pathlib import Path
def handle(self): from poetry.core.pyproject.toml import PyProjectTOML
from poetry.core.vcs.git import GitConfig from poetry.core.vcs.git import GitConfig
from poetry.layouts import layout from poetry.layouts import layout
from poetry.utils._compat import Path
from poetry.utils.env import SystemEnv from poetry.utils.env import SystemEnv
pyproject = PyProjectTOML(Path.cwd() / "pyproject.toml") pyproject = PyProjectTOML(Path.cwd() / "pyproject.toml")
if pyproject.file.exists(): if pyproject.file.exists():
if pyproject.is_poetry_project(): if pyproject.is_poetry_project():
self.line( self.line_error(
"<error>A pyproject.toml file with a poetry section already "<error>A pyproject.toml file with a poetry section already"
exists.</error>" " exists.</error>"
) )
return 1 return 1
if pyproject.data.get("build-system"): if pyproject.data.get("build-system"):
self.line( self.line_error(
"<error>A pyproject.toml file with a defined build-system al "<error>A pyproject.toml file with a defined build-system al
ready exists.</error>" ready"
" exists.</error>"
) )
return 1 return 1
vcs_config = GitConfig() vcs_config = GitConfig()
self.line("") if self.io.is_interactive():
self.line( self.line("")
"This command will guide you through creating your <info>pyproject.t self.line(
oml</> config." "This command will guide you through creating your"
) " <info>pyproject.toml</> config."
self.line("") )
self.line("")
name = self.option("name") name = self.option("name")
if not name: if not name:
name = Path.cwd().name.lower() name = Path.cwd().name.lower()
question = self.create_question( question = self.create_question(
"Package name [<comment>{}</comment>]: ".format(name), default=n ame f"Package name [<comment>{name}</comment>]: ", default=name
) )
name = self.ask(question) name = self.ask(question)
version = "0.1.0" version = "0.1.0"
question = self.create_question( question = self.create_question(
"Version [<comment>{}</comment>]: ".format(version), default=version f"Version [<comment>{version}</comment>]: ", default=version
) )
version = self.ask(question) version = self.ask(question)
description = self.option("description") or "" description = self.option("description")
question = self.create_question( if not description:
"Description [<comment>{}</comment>]: ".format(description), description = self.ask(self.create_question("Description []: ", defa
default=description, ult=""))
)
description = self.ask(question)
author = self.option("author") author = self.option("author")
if not author and vcs_config and vcs_config.get("user.name"): if not author and vcs_config.get("user.name"):
author = vcs_config["user.name"] author = vcs_config["user.name"]
author_email = vcs_config.get("user.email") author_email = vcs_config.get("user.email")
if author_email: if author_email:
author += " <{}>".format(author_email) author += f" <{author_email}>"
question = self.create_question( question = self.create_question(
"Author [<comment>{}</comment>, n to skip]: ".format(author), defaul t=author f"Author [<comment>{author}</comment>, n to skip]: ", default=author
) )
question.set_validator(lambda v: self._validate_author(v, author)) question.set_validator(lambda v: self._validate_author(v, author))
author = self.ask(question) author = self.ask(question)
if not author: if not author:
authors = [] authors = []
else: else:
authors = [author] authors = [author]
license = self.option("license") or "" license = self.option("license")
if not license:
question = self.create_question( license = self.ask(self.create_question("License []: ", default=""))
"License [<comment>{}</comment>]: ".format(license), default=license
)
question.set_validator(self._validate_license)
license = self.ask(question)
python = self.option("python") python = self.option("python")
if not python: if not python:
current_env = SystemEnv(Path(sys.executable)) current_env = SystemEnv(Path(sys.executable))
default_python = "^{}".format( default_python = "^" + ".".join(
".".join(str(v) for v in current_env.version_info[:2]) str(v) for v in current_env.version_info[:2]
) )
question = self.create_question( question = self.create_question(
"Compatible Python versions [<comment>{}</comment>]: ".format( f"Compatible Python versions [<comment>{default_python}</comment
default_python >]: ",
),
default=default_python, default=default_python,
) )
python = self.ask(question) python = self.ask(question)
self.line("") if self.io.is_interactive():
self.line("")
requirements = {} requirements: Requirements = {}
if self.option("dependency"): if self.option("dependency"):
requirements = self._format_requirements( requirements = self._format_requirements(
self._determine_requirements(self.option("dependency")) self._determine_requirements(self.option("dependency"))
) )
question = "Would you like to define your main dependencies interactivel y?" question = "Would you like to define your main dependencies interactivel y?"
help_message = ( help_message = """\
"You can specify a package in the following forms:\n" You can specify a package in the following forms:
" - A single name (<b>requests</b>)\n" - A single name (<b>requests</b>): this will search for matches on PyPI
" - A name and a constraint (<b>requests@^2.23.0</b>)\n" - A name and a constraint (<b>requests@^2.23.0</b>)
" - A git url (<b>git+https://github.com/python-poetry/poetry.git</ - A git url (<b>git+https://github.com/python-poetry/poetry.git</b>)
b>)\n" - A git url with a revision\
" - A git url with a revision (<b>git+https://github.com/python-poe (<b>git+https://github.com/python-poetry/poetry.git#develop</b>)
try/poetry.git#develop</b>)\n" - A file path (<b>../my-package/my-package.whl</b>)
" - A file path (<b>../my-package/my-package.whl</b>)\n" - A directory (<b>../my-package/</b>)
" - A directory (<b>../my-package/</b>)\n" - A url (<b>https://example.com/packages/my-package-0.1.0.tar.gz</b>)
" - A url (<b>https://example.com/packages/my-package-0.1.0.tar.gz< """
/b>)\n"
)
help_displayed = False help_displayed = False
if self.confirm(question, True): if self.confirm(question, True):
self.line(help_message) if self.io.is_interactive():
help_displayed = True self.line(help_message)
help_displayed = True
requirements.update( requirements.update(
self._format_requirements(self._determine_requirements([])) self._format_requirements(self._determine_requirements([]))
) )
self.line("") if self.io.is_interactive():
self.line("")
dev_requirements = {} dev_requirements: Requirements = {}
if self.option("dev-dependency"): if self.option("dev-dependency"):
dev_requirements = self._format_requirements( dev_requirements = self._format_requirements(
self._determine_requirements(self.option("dev-dependency")) self._determine_requirements(self.option("dev-dependency"))
) )
question = ( question = (
"Would you like to define your development dependencies interactivel y?" "Would you like to define your development dependencies interactivel y?"
) )
if self.confirm(question, True): if self.confirm(question, True):
if not help_displayed: if self.io.is_interactive() and not help_displayed:
self.line(help_message) self.line(help_message)
dev_requirements.update( dev_requirements.update(
self._format_requirements(self._determine_requirements([])) self._format_requirements(self._determine_requirements([]))
) )
self.line("") if self.io.is_interactive():
self.line("")
layout_ = layout("standard")( layout_ = layout("standard")(
name, name,
version, version,
description=description, description=description,
author=authors[0] if authors else None, author=authors[0] if authors else None,
license=license, license=license,
python=python, python=python,
dependencies=requirements, dependencies=requirements,
dev_dependencies=dev_requirements, dev_dependencies=dev_requirements,
) )
content = layout_.generate_poetry_content(original=pyproject) content = layout_.generate_poetry_content()
for section in content:
pyproject.data.append(section, content[section])
if self.io.is_interactive(): if self.io.is_interactive():
self.line("<info>Generated file</info>") self.line("<info>Generated file</info>")
self.line("") self.line("")
self.line(content) self.line(pyproject.data.as_string().replace("\r\n", "\n"))
self.line("") self.line("")
if not self.confirm("Do you confirm generation?", True): if not self.confirm("Do you confirm generation?", True):
self.line("<error>Command aborted</error>") self.line_error("<error>Command aborted</error>")
return 1 return 1
with (Path.cwd() / "pyproject.toml").open("w", encoding="utf-8") as f: pyproject.save()
f.write(content)
return 0
def _generate_choice_list(
self, matches: list[Package], canonicalized_name: NormalizedName
) -> list[str]:
choices = []
matches_names = [p.name for p in matches]
exact_match = canonicalized_name in matches_names
if exact_match:
choices.append(matches[matches_names.index(canonicalized_name)].pret
ty_name)
for found_package in matches:
if len(choices) >= 10:
break
if found_package.name == canonicalized_name:
continue
choices.append(found_package.pretty_name)
return choices
def _determine_requirements( def _determine_requirements(
self, requires, allow_prereleases=False, source=None self,
): # type: (List[str], bool) -> List[Dict[str, str]] requires: list[str],
allow_prereleases: bool = False,
source: str | None = None,
) -> list[dict[str, Any]]:
if not requires: if not requires:
requires = [] result = []
package = self.ask( question = self.create_question(
"Search for package to add (or leave blank to continue):" "Package to add or search for (leave blank to skip):"
) )
while package is not None: question.set_validator(self._validate_package)
package = self.ask(question)
while package:
constraint = self._parse_requirements([package])[0] constraint = self._parse_requirements([package])[0]
if ( if (
"git" in constraint "git" in constraint
or "url" in constraint or "url" in constraint
or "path" in constraint or "path" in constraint
or "version" in constraint or "version" in constraint
): ):
self.line("Adding <info>{}</info>".format(package)) self.line(f"Adding <info>{package}</info>")
requires.append(constraint) result.append(constraint)
package = self.ask("\nAdd a package:") package = self.ask("\nAdd a package (leave blank to skip):")
continue continue
matches = self._get_pool().search(constraint["name"]) canonicalized_name = canonicalize_name(constraint["name"])
matches = self._get_pool().search(canonicalized_name)
if not matches: if not matches:
self.line("<error>Unable to find package</error>") self.line_error("<error>Unable to find package</error>")
package = False package = False
else: else:
choices = [] choices = self._generate_choice_list(matches, canonicalized_
matches_names = [p.name for p in matches] name)
exact_match = constraint["name"] in matches_names
if exact_match:
choices.append(
matches[matches_names.index(constraint["name"])].pre
tty_name
)
for found_package in matches: info_string = (
if len(choices) >= 10: f"Found <info>{len(matches)}</info> packages matching"
break f" <c1>{package}</c1>"
if found_package.name.lower() == constraint["name"].lowe
r():
continue
choices.append(found_package.pretty_name)
self.line(
"Found <info>{}</info> packages matching <c1>{}</c1>".fo
rmat(
len(matches), 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 selecte
d
choices.append("")
package = self.choice( package = self.choice(
"\nEnter package # to add, or the complete package name "\nEnter package # to add, or the complete package name
if it is not listed", if it"
" is not listed",
choices, choices,
attempts=3, attempts=3,
default=len(choices) - 1,
) )
if not package:
self.line("<warning>No package selected</warning>")
# package selected by user, set constraint name to package n ame # package selected by user, set constraint name to package n ame
if package is not False: if package:
constraint["name"] = package constraint["name"] = package
# no constraint yet, determine the best version automatically # no constraint yet, determine the best version automatically
if package is not False and "version" not in constraint: if package and "version" not in constraint:
question = self.create_question( question = self.create_question(
"Enter the version constraint to require " "Enter the version constraint to require "
"(or leave blank to use the latest version):" "(or leave blank to use the latest version):"
) )
question.attempts = 3 question.attempts = 3
question.validator = lambda x: (x or "").strip() or False question.validator = lambda x: (x or "").strip() or False
package_constraint = self.ask(question) package_constraint = self.ask(question)
if package_constraint is None: if package_constraint is None:
_, package_constraint = self._find_best_version_for_pack age( _, package_constraint = self._find_best_version_for_pack age(
package package
) )
self.line( self.line(
"Using version <b>{}</b> for <c1>{}</c1>".format( f"Using version <b>{package_constraint}</b> for"
package_constraint, package f" <c1>{package}</c1>"
)
) )
constraint["version"] = package_constraint constraint["version"] = package_constraint
if package is not False: if package:
requires.append(constraint) result.append(constraint)
package = self.ask("\nAdd a package:") if self.io.is_interactive():
package = self.ask("\nAdd a package (leave blank to skip):")
return requires return result
requires = self._parse_requirements(requires)
result = [] result = []
for requirement in requires: for requirement in self._parse_requirements(requires):
if "git" in requirement or "url" in requirement or "path" in require ment: if "git" in requirement or "url" in requirement or "path" in require ment:
result.append(requirement) result.append(requirement)
continue continue
elif "version" not in requirement: elif "version" not in requirement:
# determine the best version automatically # determine the best version automatically
name, version = self._find_best_version_for_package( name, version = self._find_best_version_for_package(
requirement["name"], requirement["name"],
allow_prereleases=allow_prereleases, allow_prereleases=allow_prereleases,
source=source, source=source,
) )
requirement["version"] = version requirement["version"] = version
requirement["name"] = name requirement["name"] = name
self.line( self.line(f"Using version <b>{version}</b> for <c1>{name}</c1>")
"Using version <b>{}</b> for <c1>{}</c1>".format(version, na
me)
)
else: else:
# check that the specified version/constraint exists # check that the specified version/constraint exists
# before we proceed # before we proceed
name, _ = self._find_best_version_for_package( name, _ = self._find_best_version_for_package(
requirement["name"], requirement["name"],
requirement["version"], requirement["version"],
allow_prereleases=allow_prereleases, allow_prereleases=allow_prereleases,
source=source, source=source,
) )
requirement["name"] = name requirement["name"] = name
result.append(requirement) result.append(requirement)
return result return result
def _find_best_version_for_package( def _find_best_version_for_package(
self, name, required_version=None, allow_prereleases=False, source=None self,
): # type: (...) -> Tuple[str, str] 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 from poetry.version.version_selector import VersionSelector
selector = VersionSelector(self._get_pool()) selector = VersionSelector(self._get_pool())
package = selector.find_best_candidate( package = selector.find_best_candidate(
name, required_version, allow_prereleases=allow_prereleases, source= source name, required_version, allow_prereleases=allow_prereleases, source= source
) )
if not package: if not package:
# TODO: find similar # TODO: find similar
raise ValueError( raise ValueError(f"Could not find a matching version of package {nam
"Could not find a matching version of package {}".format(name) e}")
)
return package.pretty_name, selector.find_recommended_require_version(pa ckage) return package.pretty_name, selector.find_recommended_require_version(pa ckage)
def _parse_requirements( def _parse_requirements(self, requirements: list[str]) -> list[dict[str, Any
self, requirements ]]:
): # type: (List[str]) -> List[Dict[str, str]] from poetry.core.pyproject.exceptions import PyProjectException
from poetry.puzzle.provider import Provider
result = []
try: try:
cwd = self.poetry.file.parent cwd = self.poetry.file.parent
except (PyProjectException, RuntimeError): except (PyProjectException, RuntimeError):
cwd = Path.cwd() cwd = Path.cwd()
for requirement in requirements: return [
requirement = requirement.strip() parse_dependency_specification(
extras = [] requirement=requirement,
extras_m = re.search(r"\[([\w\d,-_ ]+)\]$", requirement) env=self.env if isinstance(self, EnvCommand) else None,
if extras_m: cwd=cwd,
extras = [e.strip() for e in extras_m.group(1).split(",")]
requirement, _ = requirement.split("[")
url_parsed = urlparse.urlparse(requirement)
if url_parsed.scheme and url_parsed.netloc:
# Url
if url_parsed.scheme in ["git+https", "git+ssh"]:
from poetry.core.vcs.git import Git
from poetry.core.vcs.git import ParsedUrl
parsed = ParsedUrl.parse(requirement)
url = Git.normalize_url(requirement)
pair = OrderedDict([("name", parsed.name), ("git", url.url)]
)
if parsed.rev:
pair["rev"] = url.revision
if extras:
pair["extras"] = extras
package = Provider.get_package_from_vcs(
"git", url.url, rev=pair.get("rev")
)
pair["name"] = package.name
result.append(pair)
continue
elif url_parsed.scheme in ["http", "https"]:
package = Provider.get_package_from_url(requirement)
pair = OrderedDict(
[("name", package.name), ("url", package.source_url)]
)
if extras:
pair["extras"] = extras
result.append(pair)
continue
elif (os.path.sep in requirement or "/" in requirement) and cwd.join
path(
requirement
).exists():
path = cwd.joinpath(requirement)
if path.is_file():
package = Provider.get_package_from_file(path.resolve())
else:
package = Provider.get_package_from_directory(path)
result.append(
OrderedDict(
[
("name", package.name),
("path", path.relative_to(cwd).as_posix()),
]
+ ([("extras", extras)] if extras else [])
)
)
continue
pair = re.sub(
"^([^@=: ]+)(?:@|==|(?<![<>~!])=|:| )(.*)$", "\\1 \\2", requirem
ent
) )
pair = pair.strip() for requirement in requirements
]
require = OrderedDict()
if " " in pair:
name, version = pair.split(" ", 2)
extras_m = re.search(r"\[([\w\d,-_]+)\]$", name)
if extras_m:
extras = [e.strip() for e in extras_m.group(1).split(",")]
name, _ = name.split("[")
require["name"] = name
if version != "latest":
require["version"] = version
else:
m = re.match(
r"^([^><=!: ]+)((?:>=|<=|>|<|!=|~=|~|\^).*)$", requirement.s
trip()
)
if m:
name, constraint = m.group(1), m.group(2)
extras_m = re.search(r"\[([\w\d,-_]+)\]$", name)
if extras_m:
extras = [e.strip() for e in extras_m.group(1).split(","
)]
name, _ = name.split("[")
require["name"] = name def _format_requirements(self, requirements: list[dict[str, str]]) -> Requir
require["version"] = constraint ements:
else: requires: Requirements = {}
extras_m = re.search(r"\[([\w\d,-_]+)\]$", pair)
if extras_m:
extras = [e.strip() for e in extras_m.group(1).split(","
)]
pair, _ = pair.split("[")
require["name"] = pair
if extras:
require["extras"] = extras
result.append(require)
return result
def _format_requirements(
self, requirements
): # type: (List[Dict[str, str]]) -> Dict[str, Union[str, Dict[str, str]]]
requires = {}
for requirement in requirements: for requirement in requirements:
name = requirement.pop("name") name = requirement.pop("name")
constraint: str | InlineTable
if "version" in requirement and len(requirement) == 1: if "version" in requirement and len(requirement) == 1:
constraint = requirement["version"] constraint = requirement["version"]
else: else:
constraint = inline_table() constraint = inline_table()
constraint.trivia.trail = "\n" constraint.trivia.trail = "\n"
constraint.update(requirement) constraint.update(requirement)
requires[name] = constraint requires[name] = constraint
return requires return requires
def _validate_author(self, author, default): def _validate_author(self, author: str, default: str) -> str | None:
from poetry.core.packages.package import AUTHOR_REGEX from poetry.core.packages.package import AUTHOR_REGEX
author = author or default author = author or default
if author in ["n", "no"]: if author in ["n", "no"]:
return return None
m = AUTHOR_REGEX.match(author) m = AUTHOR_REGEX.match(author)
if not m: if not m:
raise ValueError( raise ValueError(
"Invalid author string. Must be in the format: " "Invalid author string. Must be in the format: "
"John Smith <john@example.com>" "John Smith <john@example.com>"
) )
return author return author
def _validate_license(self, license): @staticmethod
from poetry.core.spdx import license_by_id def _validate_package(package: str | None) -> str | None:
if package and len(package.split()) > 2:
if license: raise ValueError("Invalid package definition.")
license_by_id(license)
return license return package
def _get_pool(self): def _get_pool(self) -> Pool:
from poetry.repositories import Pool from poetry.repositories import Pool
from poetry.repositories.pypi_repository import PyPiRepository from poetry.repositories.pypi_repository import PyPiRepository
if isinstance(self, EnvCommand): if isinstance(self, EnvCommand):
return self.poetry.pool return self.poetry.pool
if self._pool is None: if self._pool is None:
self._pool = Pool() self._pool = Pool()
self._pool.add_repository(PyPiRepository()) self._pool.add_repository(PyPiRepository())
 End of changes. 71 change blocks. 
270 lines changed or deleted 192 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)