def do_use(project: Project, python: str) -> None: """Use the specified python version and save in project config. The python can be a version string or interpreter path. """ if Path(python).is_absolute(): python_path = python else: python_path = shutil.which(python) if not python_path: finder = pythonfinder.Finder() try: python_path = finder.find_python_version( python).path.as_posix() except AttributeError: raise NoPythonVersion( f"Python {python} is not found on the system.") python_version = get_python_version(python_path, True) if not project.python_requires.contains(python_version): raise NoPythonVersion("The target Python version {} doesn't satisfy " "the Python requirement: {}".format( python_version, project.python_requires)) context.io.echo("Using Python interpreter: {} ({})".format( context.io.green(python_path), python_version)) project.config["python"] = Path(python_path).as_posix() project.config.save_config()
def do_use(project: Project, python: str, first: bool = False) -> None: """Use the specified python version and save in project config. The python can be a version string or interpreter path. """ import pythonfinder python = python.strip() if python and not all(c.isdigit() for c in python.split(".")): if Path(python).exists(): python_path = find_python_in_path(python) else: python_path = shutil.which(python) if not python_path: raise NoPythonVersion(f"{python} is not a valid Python.") python_version, is_64bit = get_python_version(python_path, True) else: finder = pythonfinder.Finder() pythons = [] args = [int(v) for v in python.split(".") if v != ""] for i, entry in enumerate(finder.find_all_python_versions(*args)): python_version, is_64bit = get_python_version(entry.path.as_posix(), True) pythons.append((entry.path.as_posix(), python_version, is_64bit)) if not pythons: raise NoPythonVersion(f"Python {python} is not available on the system.") if not first and len(pythons) > 1: for i, (path, python_version, is_64bit) in enumerate(pythons): stream.echo( f"{i}. {stream.green(path)} " f"({get_python_version_string(python_version, is_64bit)})" ) selection = click.prompt( "Please select:", type=click.Choice([str(i) for i in range(len(pythons))]), default="0", show_choices=False, ) else: selection = 0 python_path, python_version, is_64bit = pythons[int(selection)] if not project.python_requires.contains(python_version): raise NoPythonVersion( "The target Python version {} doesn't satisfy " "the Python requirement: {}".format(python_version, project.python_requires) ) stream.echo( "Using Python interpreter: {} ({})".format( stream.green(python_path), get_python_version_string(python_version, is_64bit), ) ) old_path = project.config.get("python.path") new_path = python_path project.project_config["python.path"] = Path(new_path).as_posix() if old_path and Path(old_path) != Path(new_path) and not project.is_global: stream.echo(stream.cyan("Updating executable scripts...")) project.environment.update_shebangs(new_path)
def resolve_interpreter(self) -> PythonInfo: """Get the Python interpreter path.""" config = self.config if self.project_config.get( "python.path") and not os.getenv("PDM_IGNORE_SAVED_PYTHON"): saved_path = self.project_config["python.path"] try: return PythonInfo.from_path(saved_path) except (ValueError, FileNotFoundError): del self.project_config["python.path"] if os.name == "nt": suffix = ".exe" scripts = "Scripts" else: suffix = "" scripts = "bin" virtual_env = os.getenv("VIRTUAL_ENV") if config["use_venv"] and virtual_env: return PythonInfo.from_path( os.path.join(virtual_env, scripts, f"python{suffix}")) for py_version in self.find_interpreters(): if self.python_requires.contains(str(py_version.version)): self.python = py_version return py_version raise NoPythonVersion( "No Python that satisfies {} is found on the system.".format( self.python_requires))
def resolve_interpreter(self) -> str: """Get the Python interpreter path.""" config = self.config if self.project_config.get( "python.path") and not os.getenv("PDM_IGNORE_SAVED_PYTHON"): saved_path = self.project_config["python.path"] if not os.path.isfile(saved_path): del self.project_config["python.path"] else: return saved_path if os.name == "nt": suffix = ".exe" scripts = "Scripts" else: suffix = "" scripts = "bin" virtual_env = os.getenv("VIRTUAL_ENV") if config["use_venv"] and virtual_env: return os.path.join(virtual_env, scripts, f"python{suffix}") for py_version in self.find_interpreters(): if self.python_requires.contains(str(py_version.version)): self.python_executable = py_version.executable return self.python_executable raise NoPythonVersion( "No Python that satisfies {} is found on the system.".format( self.python_requires))
def do_use(project: Project, python: str, first: bool = False) -> None: """Use the specified python version and save in project config. The python can be a version string or interpreter path. """ if not all(c.isdigit() for c in python.split(".")): if Path(python).exists(): python_path = Path(python).absolute().as_posix() else: python_path = shutil.which(python) if not python_path: raise NoPythonVersion(f"{python} is not a valid Python.") python_version = get_python_version(python_path, True) else: finder = pythonfinder.Finder() pythons = [] args = [int(v) for v in python.split(".")] for i, entry in enumerate(finder.find_all_python_versions(*args)): python_version = get_python_version(entry.path.as_posix(), True) pythons.append((entry.path.as_posix(), python_version)) if not pythons: raise NoPythonVersion( f"Python {python} is not available on the system.") if not first and len(pythons) > 1: for i, (path, python_version) in enumerate(pythons): context.io.echo( f"{i}: {context.io.green(path)} ({python_version})") selection = click.prompt( "Please select:", type=click.Choice([str(i) for i in range(len(pythons))]), default="0", show_choices=False, ) else: selection = 0 python_path, python_version = pythons[int(selection)] if not project.python_requires.contains(python_version): raise NoPythonVersion("The target Python version {} doesn't satisfy " "the Python requirement: {}".format( python_version, project.python_requires)) context.io.echo("Using Python interpreter: {} ({})".format( context.io.green(python_path), python_version)) project.config["python.path"] = Path(python_path).as_posix() project.config.save_config()
def python_executable(self) -> str: """Get the Python interpreter path.""" config = self.config if self.project_config.get( "python.path") and not os.getenv("PDM_IGNORE_SAVED_PYTHON"): return self.project_config["python.path"] path = None if config["use_venv"]: path = get_venv_python(self.root) if path: stream.echo( f"Virtualenv interpreter {stream.green(path)} is detected.", err=True, verbosity=stream.DETAIL, ) if not path and PYENV_INSTALLED and config.get("python.use_pyenv", True): path = Path(PYENV_ROOT, "shims", "python").as_posix() if not path: path = shutil.which("python") version = None if path: try: version, _ = get_python_version(path, True) except (FileNotFoundError, subprocess.CalledProcessError): version = None if not version or not self.python_requires.contains(version): finder = Finder() for python in finder.find_all_python_versions(): version, _ = get_python_version(python.path.as_posix(), True) if self.python_requires.contains(version): path = python.path.as_posix() break else: version = ".".join(map(str, sys.version_info[:3])) if self.python_requires.contains(version): path = sys.executable if path: if os.path.normcase(path) == os.path.normcase(sys.executable): # Refer to the base interpreter to allow for venvs path = getattr(sys, "_base_executable", sys.executable) stream.echo( "Using Python interpreter: {} ({})".format( stream.green(path), version), err=True, ) if not os.getenv("PDM_IGNORE_SAVED_PYTHON"): self.project_config["python.path"] = Path(path).as_posix() return path raise NoPythonVersion( "No Python that satisfies {} is found on the system.".format( self.python_requires))
def do_use(project: Project, python: Optional[str] = "", first: Optional[bool] = False) -> None: """Use the specified python version and save in project config. The python can be a version string or interpreter path. """ def version_matcher(py_version): return project.python_requires.contains(str(py_version.version)) python = python.strip() found_interpreters = list( dict.fromkeys( filter(version_matcher, project.find_interpreters(python)))) if not found_interpreters: raise NoPythonVersion("Python interpreter is not found on the system.") if first or len(found_interpreters) == 1: selected_python = found_interpreters[0] else: project.core.ui.echo("Please enter the Python interpreter to use") for i, py_version in enumerate(found_interpreters): python_version = str(py_version.version) is_64bit = py_version.get_architecture() == "64bit" version_string = get_python_version_string(python_version, is_64bit) project.core.ui.echo( f"{i}. {termui.green(py_version.executable)} ({version_string})" ) selection = click.prompt( "Please select:", type=click.Choice([str(i) for i in range(len(found_interpreters))]), default="0", show_choices=False, ) selected_python = found_interpreters[int(selection)] old_path = project.config.get("python.path") new_path = selected_python.executable python_version = str(selected_python.version) is_64bit = selected_python.get_architecture() == "64bit" project.core.ui.echo("Using Python interpreter: {} ({})".format( termui.green(str(new_path)), get_python_version_string(python_version, is_64bit), )) project.python_executable = new_path if old_path and Path(old_path) != Path(new_path) and not project.is_global: project.core.ui.echo(termui.cyan("Updating executable scripts...")) project.environment.update_shebangs(new_path)
def do_use(project: Project, python: str = "", first: bool = False) -> None: """Use the specified python version and save in project config. The python can be a version string or interpreter path. """ def version_matcher(py_version: PythonInfo) -> bool: return project.python_requires.contains(str(py_version.version)) if python: python = python.strip() found_interpreters = list( dict.fromkeys( filter(version_matcher, project.find_interpreters(python)))) if not found_interpreters: raise NoPythonVersion("Python interpreter is not found on the system.") if first or len(found_interpreters) == 1: selected_python = found_interpreters[0] else: project.core.ui.echo("Please enter the Python interpreter to use") for i, py_version in enumerate(found_interpreters): project.core.ui.echo( f"{i}. {termui.green(py_version.executable)} ({py_version.identifier})" ) selection = click.prompt( "Please select:", type=click.Choice([str(i) for i in range(len(found_interpreters))]), default="0", show_choices=False, ) selected_python = found_interpreters[int(selection)] old_path = project.python.executable if "python.path" in project.config else None new_path = selected_python.executable project.core.ui.echo("Using Python interpreter: {} ({})".format( termui.green(str(new_path)), selected_python.identifier, )) project.python = selected_python if (old_path and Path(old_path) != Path(new_path) and not project.environment.is_global): project.core.ui.echo(termui.cyan("Updating executable scripts...")) project.environment.update_shebangs(old_path, new_path)
def python_executable(self) -> str: """Get the Python interpreter path.""" config = self.project.config if config.get("python.path"): return config["python.path"] if PYENV_INSTALLED and config.get("python.use_pyenv", True): return os.path.join(PYENV_ROOT, "shims", "python") if "VIRTUAL_ENV" in os.environ: stream.echo( "An activated virtualenv is detected, reuse the interpreter now.", err=True, verbosity=stream.DETAIL, ) return get_venv_python(self.project.root) # First try what `python` refers to. path = shutil.which("python") version = None if path: version, _ = get_python_version(path, True) if not version or not self.python_requires.contains(version): finder = Finder() for python in finder.find_all_python_versions(): version, _ = get_python_version(python.path.as_posix(), True) if self.python_requires.contains(version): path = python.path.as_posix() break else: version = ".".join(map(str, sys.version_info[:3])) if self.python_requires.contains(version): path = sys.executable if path: stream.echo( "Using Python interpreter: {} ({})".format(stream.green(path), version) ) self.project.project_config["python.path"] = Path(path).as_posix() return path raise NoPythonVersion( "No Python that satisfies {} is found on the system.".format( self.python_requires ) )
def resolve_interpreter(self) -> PythonInfo: """Get the Python interpreter path.""" config = self.config if config.get("python.path") and not os.getenv("PDM_IGNORE_SAVED_PYTHON"): saved_path = config["python.path"] python = PythonInfo.from_path(saved_path) if python.valid and self.python_requires.contains( str(python.version), True ): return python self.project_config.pop("python.path", None) if os.name == "nt": suffix = ".exe" scripts = "Scripts" else: suffix = "" scripts = "bin" # Resolve virtual environments from env-vars virtual_env = os.getenv("VIRTUAL_ENV", os.getenv("CONDA_PREFIX")) if config["python.use_venv"] and virtual_env: python = PythonInfo.from_path( os.path.join(virtual_env, scripts, f"python{suffix}") ) if python.valid: return python for py_version in self.find_interpreters(): if py_version.valid and self.python_requires.contains( str(py_version.version), True ): self.python = py_version return py_version raise NoPythonVersion( "No Python that satisfies {} is found on the system.".format( self.python_requires ) )
def find_python_in_path(path: os.PathLike) -> str: """Find a python interpreter from the given path, the input argument could be: - A valid path to the interpreter - A Python root diretory that contains the interpreter """ pathlib_path = Path(path).absolute() if pathlib_path.is_file(): return pathlib_path.as_posix() if os.name == "nt": for root_dir in (pathlib_path, pathlib_path / "Scripts"): if root_dir.joinpath("python.exe").exists(): return root_dir.joinpath("python.exe").as_posix() else: executable_pattern = re.compile(r"python(?:\d(?:\.\d+m?)?)?$") for python in pathlib_path.joinpath("bin").glob("python*"): if executable_pattern.match(python.name): return python.as_posix() raise NoPythonVersion(f"No Python interpreter is found at {path!r}")
def python_executable(self) -> str: """Get the Python interpreter path.""" path = None if self.config["python"]: path = self.config["python"] try: get_python_version(path) return path except Exception: pass path = None version = None # First try what `python` refers to. path = shutil.which("python") if path: version = get_python_version(path, True) else: finder = Finder() for python in finder.find_all_python_versions(): version = ".".join( map(str, get_python_version(python.path.as_posix()))) if self.python_requires.contains(version): path = python.path.as_posix() break else: version = ".".join(map(str, sys.version_info[:3])) if self.python_requires.contains(version): path = sys.executable if path: context.io.echo("Using Python interpreter: {} ({})".format( context.io.green(path), version)) self.config["python"] = Path(path).as_posix() self.config.save_config() return path raise NoPythonVersion( "No Python that satisfies {} is found on the system.".format( self.python_requires))
def python_executable(self) -> str: """Get the Python interpreter path.""" if self.config.get("python.path"): path = self.config["python.path"] try: get_python_version(path) return path except Exception: pass if PYENV_INSTALLED and self.config.get("python.use_pyenv", True): return os.path.join(PYENV_ROOT, "shims", "python") # First try what `python` refers to. path = shutil.which("python") version = None if path: version = get_python_version(path, True) if not version or not self.python_requires.contains(version): finder = Finder() for python in finder.find_all_python_versions(): version = get_python_version(python.path.as_posix(), True) if self.python_requires.contains(version): path = python.path.as_posix() break else: version = ".".join(map(str, sys.version_info[:3])) if self.python_requires.contains(version): path = sys.executable if path: context.io.echo("Using Python interpreter: {} ({})".format( context.io.green(path), version)) self.config["python.path"] = Path(path).as_posix() self.config.save_config() return path raise NoPythonVersion( "No Python that satisfies {} is found on the system.".format( self.python_requires))
def do_use( project: Project, python: str = "", first: bool = False, ignore_remembered: bool = False, ) -> None: """Use the specified python version and save in project config. The python can be a version string or interpreter path. """ if python: python = python.strip() def version_matcher(py_version: PythonInfo) -> bool: return project.python_requires.contains(str(py_version.version), True) if not project.cache_dir.exists(): project.cache_dir.mkdir(parents=True) use_cache: JSONFileCache[str, str] = JSONFileCache( project.cache_dir / "use_cache.json" ) selected_python: PythonInfo | None = None if python and not ignore_remembered: if use_cache.has_key(python): path = use_cache.get(python) cached_python = PythonInfo.from_path(path) if not cached_python.valid: project.core.ui.echo( f"The last selection is corrupted. {path!r}", fg="red", err=True, ) elif version_matcher(cached_python): project.core.ui.echo( "Using the last selection, add '-i' to ignore it.", fg="yellow", err=True, ) selected_python = cached_python if selected_python is None: found_interpreters = list(dict.fromkeys(project.find_interpreters(python))) matching_interperters = list(filter(version_matcher, found_interpreters)) if not found_interpreters: raise NoPythonVersion("Python interpreter is not found on the system.") if not matching_interperters: project.core.ui.echo("Interpreters found but not matching:", err=True) for py in found_interpreters: project.core.ui.echo(f" - {py.executable} ({py.identifier})", err=True) raise NoPythonVersion( "No python is found meeting the requirement " f"{termui.green('python' + str(project.python_requires))}" ) if first or len(matching_interperters) == 1: selected_python = matching_interperters[0] else: project.core.ui.echo("Please enter the Python interpreter to use") for i, py_version in enumerate(matching_interperters): project.core.ui.echo( f"{i}. {termui.green(str(py_version.executable))} " f"({py_version.identifier})" ) selection = click.prompt( "Please select:", type=click.Choice([str(i) for i in range(len(matching_interperters))]), default="0", show_choices=False, ) selected_python = matching_interperters[int(selection)] if python: use_cache.set(python, selected_python.path.as_posix()) if not selected_python.valid: path = str(selected_python.executable) raise InvalidPyVersion(f"Invalid Python interpreter: {path}") old_python = project.python if "python.path" in project.config else None project.core.ui.echo( "Using Python interpreter: {} ({})".format( termui.green(str(selected_python.executable)), selected_python.identifier, ) ) project.python = selected_python if ( old_python and old_python.path != selected_python.path and not project.environment.is_global ): project.core.ui.echo(termui.cyan("Updating executable scripts...")) project.environment.update_shebangs(selected_python.executable.as_posix())