def test_parse_constraint_multi_wilcard(input): assert parse_constraint(input) == VersionUnion( VersionRange( Version.from_parts(2, 7, 0), Version.from_parts(3, 0, 0), True, False ), VersionRange(Version.from_parts(3, 2, 0), None, True, False), )
def test_parse_constraint_multi(input: str) -> None: assert parse_constraint(input) == VersionRange( Version.from_parts(2, 0, 0), Version.from_parts(3, 0, 0), include_min=False, include_max=True, )
def _get_lock_data(self) -> "TOMLDocument": if not self._lock.exists(): raise RuntimeError( "No lockfile found. Unable to read locked packages") try: lock_data = self._lock.read() except TOMLKitError as e: raise RuntimeError(f"Unable to read the lock file ({e}).") lock_version = Version.parse(lock_data["metadata"].get( "lock-version", "1.0")) current_version = Version.parse(self._VERSION) # We expect the locker to be able to read lock files # from the same semantic versioning range accepted_versions = parse_constraint("^{}".format( Version.from_parts(current_version.major, 0))) lock_version_allowed = accepted_versions.allows(lock_version) if lock_version_allowed and current_version < lock_version: logger.warning( "The lock file might not be compatible with the current version of Poetry.\n" "Upgrade Poetry to ensure the lock file is read properly or, alternatively, " "regenerate the lock file with the `poetry lock` command.") elif not lock_version_allowed: raise RuntimeError( "The lock file is not compatible with the current version of Poetry.\n" "Upgrade Poetry to be able to read the lock file or, alternatively, " "regenerate the lock file with the `poetry lock` command.") return lock_data
def _setup_build(self) -> None: builder = SdistBuilder(self._poetry) setup = self._path / "setup.py" has_setup = setup.exists() if has_setup: self._io.write_error_line( "<warning>A setup.py file already exists. Using it.</warning>") else: with setup.open("w", encoding="utf-8") as f: f.write(decode(builder.build_setup())) try: if self._env.pip_version < Version.from_parts(19, 0): pip_install(self._path, self._env, upgrade=True, editable=True) else: # Temporarily rename pyproject.toml shutil.move(str(self._poetry.file), str(self._poetry.file.with_suffix(".tmp"))) try: pip_install(self._path, self._env, upgrade=True, editable=True) finally: shutil.move( str(self._poetry.file.with_suffix(".tmp")), str(self._poetry.file), ) finally: if not has_setup: os.remove(str(setup))
def mock_subprocess_calls(setup, current_python, mocker): mocker.patch( "subprocess.check_output", side_effect=check_output_wrapper(Version.from_parts(*current_python)), ) mocker.patch( "subprocess.Popen.communicate", side_effect=[("/prefix", None), ("/prefix", None), ("/prefix", None)], )
def mock_subprocess_calls(setup: None, current_python: tuple[int, int, int], mocker: MockerFixture) -> None: mocker.patch( "subprocess.check_output", side_effect=check_output_wrapper(Version.from_parts(*current_python)), ) mocker.patch( "subprocess.Popen.communicate", side_effect=[("/prefix", None), ("/prefix", None), ("/prefix", None)], )
def test_deactivate_activated( tmp_dir: str, manager: EnvManager, poetry: Poetry, config: Config, mocker: MockerFixture, ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] venv_name = manager.generate_env_name("simple-project", str(poetry.file.parent)) 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() envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") doc = tomlkit.document() doc[venv_name] = { "minor": f"{other_version.major}.{other_version.minor}", "patch": other_version.text, } envs_file.write(doc) config.merge({"virtualenvs": {"path": str(tmp_dir)}}) mocker.patch( "subprocess.check_output", side_effect=check_output_wrapper(), ) manager.deactivate(NullIO()) env = manager.get() assert env.path == Path( tmp_dir) / f"{venv_name}-py{version.major}.{version.minor}" assert Path("/prefix") envs = envs_file.read() assert len(envs) == 0
def test_create_venv_uses_patch_version_to_detect_compatibility( manager: EnvManager, poetry: Poetry, config: Config, mocker: MockerFixture, config_virtualenvs_path: Path, ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] version = Version.from_parts(*sys.version_info[:3]) poetry.package.python_versions = "^" + ".".join( str(c) for c in sys.version_info[:3]) venv_name = manager.generate_env_name("simple-project", str(poetry.file.parent)) mocker.patch("sys.version_info", (version.major, version.minor, version.patch + 1)) check_output = mocker.patch( "subprocess.check_output", side_effect=check_output_wrapper(Version.parse("3.6.9")), ) m = mocker.patch("poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kwargs: "") manager.create_venv(NullIO()) assert not check_output.called m.assert_called_with( config_virtualenvs_path / f"{venv_name}-py{version.major}.{version.minor}", executable=None, flags={ "always-copy": False, "system-site-packages": False, "no-pip": False, "no-setuptools": False, }, prompt=f"simple-project-py{version.major}.{version.minor}", )
def test_create_venv_uses_patch_version_to_detect_compatibility_with_executable( manager: EnvManager, poetry: "Poetry", config: "Config", mocker: "MockerFixture", config_virtualenvs_path: Path, ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] version = Version.from_parts(*sys.version_info[:3]) poetry.package.python_versions = f"~{version.major}.{version.minor-1}.0" venv_name = manager.generate_env_name("simple-project", str(poetry.file.parent)) check_output = mocker.patch( "subprocess.check_output", side_effect=check_output_wrapper( Version.parse(f"{version.major}.{version.minor - 1}.0")), ) m = mocker.patch("poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kwargs: "") manager.create_venv( NullIO(), executable=f"python{version.major}.{version.minor - 1}") assert check_output.called m.assert_called_with( config_virtualenvs_path / f"{venv_name}-py{version.major}.{version.minor - 1}", executable=f"python{version.major}.{version.minor - 1}", flags={ "always-copy": False, "system-site-packages": False }, with_pip=True, with_setuptools=True, with_wheel=True, )
import pytest from poetry.core.semver.helpers import parse_constraint from poetry.core.semver.version import Version from poetry.core.semver.version_range import VersionRange from poetry.core.semver.version_union import VersionUnion @pytest.mark.parametrize( "constraint,version", [ ( "~=3.8", VersionRange( min=Version.from_parts(3, 8), max=Version.from_parts(4, 0), include_min=True, ), ), ( "== 3.8.*", VersionRange( min=Version.from_parts(3, 8), max=Version.from_parts(3, 9, 0), include_min=True, ), ), ( "~= 3.8", VersionRange( min=Version.from_parts(3, 8),
def parse_single_constraint(constraint: str) -> VersionConstraint: from poetry.core.semver.patterns import BASIC_CONSTRAINT from poetry.core.semver.patterns import CARET_CONSTRAINT from poetry.core.semver.patterns import TILDE_CONSTRAINT from poetry.core.semver.patterns import TILDE_PEP440_CONSTRAINT from poetry.core.semver.patterns import X_CONSTRAINT from poetry.core.semver.version import Version from poetry.core.semver.version_range import VersionRange from poetry.core.semver.version_union import VersionUnion m = re.match(r"(?i)^v?[xX*](\.[xX*])*$", constraint) if m: return VersionRange() # Tilde range m = TILDE_CONSTRAINT.match(constraint) if m: version = Version.parse(m.group("version")) high = version.stable.next_minor() if version.release.precision == 1: high = version.stable.next_major() return VersionRange(version, high, include_min=True) # PEP 440 Tilde range (~=) m = TILDE_PEP440_CONSTRAINT.match(constraint) if m: version = Version.parse(m.group("version")) if version.release.precision == 2: high = version.stable.next_major() else: high = version.stable.next_minor() return VersionRange(version, high, include_min=True) # Caret range m = CARET_CONSTRAINT.match(constraint) if m: version = Version.parse(m.group("version")) return VersionRange(version, version.next_breaking(), include_min=True) # X Range m = X_CONSTRAINT.match(constraint) if m: op = m.group("op") major = int(m.group(2)) minor = m.group(3) if minor is not None: version = Version.from_parts(major, int(minor), 0) result: VersionConstraint = VersionRange(version, version.next_minor(), include_min=True) else: if major == 0: result = VersionRange(max=Version.from_parts(1, 0, 0)) else: version = Version.from_parts(major, 0, 0) result = VersionRange(version, version.next_major(), include_min=True) if op == "!=": result = VersionRange().difference(result) return result # Basic comparator m = BASIC_CONSTRAINT.match(constraint) if m: op = m.group("op") version_string = m.group("version") if version_string == "dev": version_string = "0.0-dev" try: version = Version.parse(version_string) except ValueError: raise ValueError( f"Could not parse version constraint: {constraint}") if op == "<": return VersionRange(max=version) if op == "<=": return VersionRange(max=version, include_max=True) if op == ">": return VersionRange(min=version) if op == ">=": return VersionRange(min=version, include_min=True) if op == "!=": return VersionUnion(VersionRange(max=version), VersionRange(min=version)) return version from poetry.core.semver.exceptions import ParseConstraintError raise ParseConstraintError( f"Could not parse version constraint: {constraint}")
import pytest from poetry.core.semver.helpers import parse_constraint from poetry.core.semver.version import Version from poetry.core.semver.version_range import VersionRange from poetry.core.semver.version_union import VersionUnion from poetry.core.version.pep440 import ReleaseTag @pytest.mark.parametrize( "constraint,version", [ ( "~=3.8", VersionRange( min=Version.from_parts(3, 8), max=Version.from_parts(4, 0), include_min=True, ), ), ( "== 3.8.*", VersionRange( min=Version.from_parts(3, 8), max=Version.from_parts(3, 9, 0), include_min=True, ), ), ( "~= 3.8", VersionRange(
from poetry.core.semver.helpers import parse_constraint from poetry.core.semver.version import Version from poetry.core.semver.version_range import VersionRange from poetry.core.semver.version_union import VersionUnion from poetry.core.version.pep440 import ReleaseTag @pytest.mark.parametrize( "input,constraint", [ ("*", VersionRange()), ("*.*", VersionRange()), ("v*.*", VersionRange()), ("*.x.*", VersionRange()), ("x.X.x.*", VersionRange()), (">1.0.0", VersionRange(min=Version.from_parts(1, 0, 0))), ("<1.2.3", VersionRange(max=Version.from_parts(1, 2, 3))), ("<=1.2.3", VersionRange(max=Version.from_parts(1, 2, 3), include_max=True)), (">=1.2.3", VersionRange(min=Version.from_parts(1, 2, 3), include_min=True)), ("=1.2.3", Version.from_parts(1, 2, 3)), ("1.2.3", Version.from_parts(1, 2, 3)), ("1!2.3.4", Version.from_parts(2, 3, 4, epoch=1)), ("=1.0", Version.from_parts(1, 0, 0)), ("1.2.3b5", Version.from_parts(1, 2, 3, pre=ReleaseTag("beta", 5))), (">= 1.2.3", VersionRange(min=Version.from_parts(1, 2, 3), include_min=True)), ( ">dev", VersionRange(min=Version.from_parts(0, 0, dev=ReleaseTag("dev"))),
from __future__ import annotations import pytest from poetry.core.semver.empty_constraint import EmptyConstraint from poetry.core.semver.version import Version from poetry.core.semver.version_range import VersionRange from poetry.core.version.exceptions import InvalidVersion from poetry.core.version.pep440 import ReleaseTag @pytest.mark.parametrize( "text,version", [ ("1.0.0", Version.from_parts(1, 0, 0)), ("1", Version.from_parts(1, 0, 0)), ("1.0", Version.from_parts(1, 0, 0)), ("1b1", Version.from_parts(1, 0, 0, pre=ReleaseTag("beta", 1))), ("1.0b1", Version.from_parts(1, 0, 0, pre=ReleaseTag("beta", 1))), ("1.0.0b1", Version.from_parts(1, 0, 0, pre=ReleaseTag("beta", 1))), ("1.0.0-b1", Version.from_parts(1, 0, 0, pre=ReleaseTag("beta", 1))), ("1.0.0-beta.1", Version.from_parts(1, 0, 0, pre=ReleaseTag("beta", 1))), ("1.0.0+1", Version.from_parts(1, 0, 0, local=1)), ("1.0.0-1", Version.from_parts(1, 0, 0, post=ReleaseTag("post", 1))), ("1.0.0.0", Version.from_parts(1, 0, 0, extra=0)), ("1.0.0-post", Version.from_parts(1, 0, 0, post=ReleaseTag("post"))), ("1.0.0-post1", Version.from_parts(1, 0, 0, post=ReleaseTag("post", 1))), ("0.6c", Version.from_parts(0, 6, 0, pre=ReleaseTag("rc", 0))), ("0.6pre", Version.from_parts(0, 6, 0, pre=ReleaseTag("preview", 0))),
def install_directory(self, package: Package) -> str | int: from cleo.io.null_io import NullIO from poetry.factory import Factory assert package.source_url is not None if package.root_dir: req = package.root_dir / package.source_url else: req = Path(package.source_url).resolve(strict=False) pyproject = PyProjectTOML(os.path.join(req, "pyproject.toml")) if pyproject.is_poetry_project(): # Even if there is a build system specified # some versions of pip (< 19.0.0) don't understand it # so we need to check the version of pip to know # if we can rely on the build system legacy_pip = self._env.pip_version < Version.from_parts(19, 0, 0) package_poetry = Factory().create_poetry( pyproject.file.path.parent) builder: Builder if package.develop and not package_poetry.package.build_script: from poetry.masonry.builders.editable import EditableBuilder # This is a Poetry package in editable mode # we can use the EditableBuilder without going through pip # to install it, unless it has a build script. builder = EditableBuilder(package_poetry, self._env, NullIO()) builder.build() return 0 elif legacy_pip or package_poetry.package.build_script: from poetry.core.masonry.builders.sdist import SdistBuilder # We need to rely on creating a temporary setup.py # file since the version of pip does not support # build-systems # We also need it for non-PEP-517 packages builder = SdistBuilder(package_poetry) with builder.setup_py(): if package.develop: return pip_install( path=req, environment=self._env, upgrade=True, editable=True, ) return pip_install(path=req, environment=self._env, deps=False, upgrade=True) if package.develop: return pip_install(path=req, environment=self._env, upgrade=True, editable=True) return pip_install(path=req, environment=self._env, deps=False, upgrade=True)