def get_pip_version(self): # type: () -> Version output = self.run("python", "-m", "pip", "--version").strip() m = re.match("pip (.+?)(?: from .+)?$", output) if not m: return Version.parse("0.0") return Version.parse(m.group(1))
def _get_lock_data(self): # type: () -> dict 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("Unable to read the lock file ({}).".format(e)) lock_version = Version.parse(lock_data["metadata"].get("lock-version", "1.0")) current_version = Version.parse(self._VERSION) accepted_versions = parse_constraint( "^{}".format(Version(current_version.major, current_version.minor)) ) 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 __init__(self, version_info=(3, 7, 0), python_implementation="CPython", platform="darwin", os_name="posix", is_venv=False, pip_version="19.1", **kwargs): super(MockEnv, self).__init__(**kwargs) self._version_info = version_info self._python_implementation = python_implementation self._platform = platform self._os_name = os_name self._is_venv = is_venv self._pip_version = Version.parse(pip_version)
def get_pip_version(self): # type: () -> Version from pip import __version__ return Version.parse(__version__)
def create_venv(self, io, name=None, executable=None, force=False ): # type: (IO, Optional[str], Optional[str], bool) -> Env if self._env is not None and not force: return self._env cwd = self._poetry.file.parent env = self.get(reload=True) if env.is_venv() and not force: # Already inside a virtualenv. return env create_venv = self._poetry.config.get("virtualenvs.create") root_venv = self._poetry.config.get("virtualenvs.in-project") venv_path = self._poetry.config.get("virtualenvs.path") if root_venv: venv_path = cwd / ".venv" elif venv_path is None: venv_path = Path(CACHE_DIR) / "virtualenvs" else: venv_path = Path(venv_path) if not name: name = self._poetry.package.name python_patch = ".".join([str(v) for v in sys.version_info[:3]]) python_minor = ".".join([str(v) for v in sys.version_info[:2]]) if executable: python_minor = decode( subprocess.check_output( " ".join([ executable, "-c", "\"import sys; print('.'.join([str(s) for s in sys.version_info[:2]]))\"", ]), shell=True, ).strip()) supported_python = self._poetry.package.python_constraint if not supported_python.allows(Version.parse(python_minor)): # The currently activated or chosen Python version # is not compatible with the Python constraint specified # for the project. # If an executable has been specified, we stop there # and notify the user of the incompatibility. # Otherwise, we try to find a compatible Python version. if executable: raise NoCompatiblePythonVersionFound( self._poetry.package.python_versions, python_minor) io.write_line( "<warning>The currently activated Python version {} " "is not supported by the project ({}).\n" "Trying to find and use a compatible version.</warning> ". format(python_patch, self._poetry.package.python_versions)) for python_to_try in reversed( sorted( self._poetry.package.AVAILABLE_PYTHONS, key=lambda v: (v.startswith("3"), -len(v), v), )): if len(python_to_try) == 1: if not parse_constraint("^{}.0".format( python_to_try)).allows_any(supported_python): continue elif not supported_python.allows_all( parse_constraint(python_to_try + ".*")): continue python = "python" + python_to_try if io.is_debug(): io.write_line("<debug>Trying {}</debug>".format(python)) try: python_patch = decode( subprocess.check_output( " ".join([ python, "-c", "\"import sys; print('.'.join([str(s) for s in sys.version_info[:3]]))\"", ]), stderr=subprocess.STDOUT, shell=True, ).strip()) except CalledProcessError: continue if not python_patch: continue if supported_python.allows(Version.parse(python_patch)): io.write_line("Using <c1>{}</c1> ({})".format( python, python_patch)) executable = python python_minor = ".".join(python_patch.split(".")[:2]) break if not executable: raise NoCompatiblePythonVersionFound( self._poetry.package.python_versions) if root_venv: venv = venv_path else: name = self.generate_env_name(name, str(cwd)) name = "{}-py{}".format(name, python_minor.strip()) venv = venv_path / name if not venv.exists(): if create_venv is False: io.write_line("<fg=black;bg=yellow>" "Skipping virtualenv creation, " "as specified in config file." "</>") return SystemEnv(Path(sys.prefix)) io.write_line("Creating virtualenv <c1>{}</> in {}".format( name, str(venv_path))) self.build_venv(str(venv), executable=executable) else: if force: io.write_line("Recreating virtualenv <c1>{}</> in {}".format( name, str(venv))) self.remove_venv(str(venv)) self.build_venv(str(venv), executable=executable) elif io.is_very_verbose(): io.write_line( "Virtualenv <c1>{}</> already exists.".format(name)) # venv detection: # stdlib venv may symlink sys.executable, so we can't use realpath. # but others can symlink *to* the venv Python, # so we can't just use sys.executable. # So we just check every item in the symlink tree (generally <= 3) p = os.path.normcase(sys.executable) paths = [p] while os.path.islink(p): p = os.path.normcase( os.path.join(os.path.dirname(p), os.readlink(p))) paths.append(p) p_venv = os.path.normcase(str(venv)) if any(p.startswith(p_venv) for p in paths): # Running properly in the virtualenv, don't need to do anything return SystemEnv(Path(sys.prefix), self.get_base_prefix()) return VirtualEnv(venv)
def remove(self, python): # type: (str) -> Env venv_path = self._poetry.config.get("virtualenvs.path") if venv_path is None: venv_path = Path(CACHE_DIR) / "virtualenvs" else: venv_path = Path(venv_path) cwd = self._poetry.file.parent envs_file = TomlFile(venv_path / self.ENVS_FILE) base_env_name = self.generate_env_name(self._poetry.package.name, str(cwd)) if python.startswith(base_env_name): venvs = self.list() for venv in venvs: if venv.path.name == python: # Exact virtualenv name if not envs_file.exists(): self.remove_venv(str(venv.path)) return venv venv_minor = ".".join( str(v) for v in venv.version_info[:2]) base_env_name = self.generate_env_name(cwd.name, str(cwd)) envs = envs_file.read() current_env = envs.get(base_env_name) if not current_env: self.remove_venv(str(venv.path)) return venv if current_env["minor"] == venv_minor: del envs[base_env_name] envs_file.write(envs) self.remove_venv(str(venv.path)) return venv raise ValueError( '<warning>Environment "{}" does not exist.</warning>'.format( python)) try: python_version = Version.parse(python) python = "python{}".format(python_version.major) if python_version.precision > 1: python += ".{}".format(python_version.minor) except ValueError: # Executable in PATH or full executable path pass try: python_version = decode( subprocess.check_output( " ".join([ python, "-c", "\"import sys; print('.'.join([str(s) for s in sys.version_info[:3]]))\"", ]), shell=True, )) except CalledProcessError as e: raise EnvCommandError(e) python_version = Version.parse(python_version.strip()) minor = "{}.{}".format(python_version.major, python_version.minor) name = "{}-py{}".format(base_env_name, minor) venv = venv_path / name if not venv.exists(): raise ValueError( '<warning>Environment "{}" does not exist.</warning>'.format( name)) if envs_file.exists(): envs = envs_file.read() current_env = envs.get(base_env_name) if current_env is not None: current_minor = current_env["minor"] if current_minor == minor: del envs[base_env_name] envs_file.write(envs) self.remove_venv(str(venv)) return VirtualEnv(venv)
def activate(self, python, io): # type: (str, IO) -> Env venv_path = self._poetry.config.get("virtualenvs.path") if venv_path is None: venv_path = Path(CACHE_DIR) / "virtualenvs" else: venv_path = Path(venv_path) cwd = self._poetry.file.parent envs_file = TomlFile(venv_path / self.ENVS_FILE) try: python_version = Version.parse(python) python = "python{}".format(python_version.major) if python_version.precision > 1: python += ".{}".format(python_version.minor) except ValueError: # Executable in PATH or full executable path pass try: python_version = decode( subprocess.check_output( " ".join([ python, "-c", "\"import sys; print('.'.join([str(s) for s in sys.version_info[:3]]))\"", ]), shell=True, )) except CalledProcessError as e: raise EnvCommandError(e) python_version = Version.parse(python_version.strip()) minor = "{}.{}".format(python_version.major, python_version.minor) patch = python_version.text create = False envs = tomlkit.document() base_env_name = self.generate_env_name(self._poetry.package.name, str(cwd)) if envs_file.exists(): envs = envs_file.read() current_env = envs.get(base_env_name) if current_env is not None: current_minor = current_env["minor"] current_patch = current_env["patch"] if current_minor == minor and current_patch != patch: # We need to recreate create = True name = "{}-py{}".format(base_env_name, minor) venv = venv_path / name # Create if needed if not venv.exists() or venv.exists() and create: in_venv = os.environ.get("VIRTUAL_ENV") is not None if in_venv or not venv.exists(): create = True if venv.exists(): # We need to check if the patch version is correct _venv = VirtualEnv(venv) current_patch = ".".join( str(v) for v in _venv.version_info[:3]) if patch != current_patch: create = True self.create_venv(io, executable=python, force=create) # Activate envs[base_env_name] = {"minor": minor, "patch": patch} envs_file.write(envs) return self.get(reload=True)
def test_self_update_should_install_all_necessary_elements( app, http, mocker, environ, tmp_dir): os.environ["POETRY_HOME"] = tmp_dir command = app.find("self update") version = Version.parse(__version__).next_minor.text mocker.patch( "poetry.repositories.pypi_repository.PyPiRepository.find_packages", return_value=[Package("poetry", version)], ) mocker.patch.object(command, "_check_recommended_installation", return_value=None) mocker.patch.object(command, "_get_release_name", return_value="poetry-{}-darwin".format(version)) mocker.patch("subprocess.check_output", return_value=b"Python 3.8.2") http.register_uri( "GET", command.BASE_URL + "/{}/poetry-{}-darwin.sha256sum".format(version, version), body=FIXTURES.joinpath("poetry-1.0.5-darwin.sha256sum").read_bytes(), ) http.register_uri( "GET", command.BASE_URL + "/{}/poetry-{}-darwin.tar.gz".format(version, version), body=FIXTURES.joinpath("poetry-1.0.5-darwin.tar.gz").read_bytes(), ) tester = CommandTester(command) tester.execute() bin_ = Path(tmp_dir).joinpath("bin") lib = Path(tmp_dir).joinpath("lib") assert bin_.exists() script = bin_.joinpath("poetry") assert script.exists() expected_script = """\ # -*- coding: utf-8 -*- import glob import sys import os lib = os.path.normpath(os.path.join(os.path.realpath(__file__), "../..", "lib")) vendors = os.path.join(lib, "poetry", "_vendor") current_vendors = os.path.join( vendors, "py{}".format(".".join(str(v) for v in sys.version_info[:2])) ) sys.path.insert(0, lib) sys.path.insert(0, current_vendors) if __name__ == "__main__": from poetry.console import main main() """ if not WINDOWS: expected_script = "#!/usr/bin/env python\n" + expected_script assert expected_script == script.read_text() if WINDOWS: bat = bin_.joinpath("poetry.bat") expected_bat = '@echo off\r\npython "{}" %*\r\n'.format( str(script).replace(os.environ.get("USERPROFILE", ""), "%USERPROFILE%")) assert bat.exists() with bat.open(newline="") as f: assert expected_bat == f.read() assert lib.exists() assert lib.joinpath("poetry").exists()