def test_python_path(monkeypatch, tmp_path, python_path_on): result = cli_run( [ensure_text(str(tmp_path)), "--without-pip", "--activators", ""]) monkeypatch.chdir(tmp_path) case_sensitive = fs_is_case_sensitive() def _get_sys_path(flag=None): cmd = [str(result.creator.exe)] if flag: cmd.append(flag) cmd.extend( ["-c", "import json; import sys; print(json.dumps(sys.path))"]) return [ i if case_sensitive else i.lower() for i in json.loads(subprocess.check_output(cmd)) ] monkeypatch.delenv(str("PYTHONPATH"), raising=False) base = _get_sys_path() # note the value result.creator.interpreter.system_stdlib cannot be set, as that would disable our custom site.py python_paths = [ str(Path(result.creator.interpreter.prefix)), str(Path(result.creator.interpreter.system_stdlib) / "b"), str(result.creator.purelib / "a"), str(result.creator.purelib), str(result.creator.bin_dir), str(tmp_path / "base"), str(tmp_path / "base_sep") + os.sep, "name", "name{}".format(os.sep), str(tmp_path.parent / (ensure_text(tmp_path.name) + "_suffix")), ".", "..", "", ] python_path_env = os.pathsep.join(ensure_str(i) for i in python_paths) monkeypatch.setenv(str("PYTHONPATH"), python_path_env) extra_all = _get_sys_path(None if python_path_on else "-E") if python_path_on: assert extra_all[0] == "" # the cwd is always injected at start as '' extra_all = extra_all[1:] assert base[0] == "" base = base[1:] assert not (set(base) - set(extra_all)) # all base paths are present abs_python_paths = list( OrderedDict((os.path.abspath(ensure_text(i)), None) for i in python_paths).keys()) abs_python_paths = [ i if case_sensitive else i.lower() for i in abs_python_paths ] extra_as_python_path = extra_all[:len(abs_python_paths)] assert abs_python_paths == extra_as_python_path # python paths are there at the start non_python_path = extra_all[len(abs_python_paths):] assert non_python_path == [ i for i in base if i not in extra_as_python_path ] else: assert base == extra_all
def sources(cls, interpreter): for src in super(PyPy2Posix, cls).sources(interpreter): yield src host_lib = Path(interpreter.system_prefix) / "lib" if host_lib.exists(): yield PathRefToDest(host_lib, dest=lambda self, _: self.lib)
from collections import defaultdict from contextlib import contextmanager from copy import copy from shutil import copy2 from zipfile import ZipFile import six from virtualenv.info import IS_ZIPAPP from virtualenv.util.path import Path from virtualenv.util.subprocess import Popen, subprocess from virtualenv.util.zipapp import ensure_file_on_disk from . import BUNDLE_SUPPORT, MAX BUNDLE_FOLDER = Path(os.path.abspath(__file__)).parent def get_wheels(for_py_version, wheel_cache_dir, extra_search_dir, download, packages): # not all wheels are compatible with all python versions, so we need to py version qualify it processed = copy(packages) # 1. acquire from bundle acquire_from_bundle(processed, for_py_version, wheel_cache_dir) # 2. acquire from extra search dir acquire_from_dir(processed, for_py_version, wheel_cache_dir, extra_search_dir) # 3. download from the internet if download and processed: download_wheel(processed, for_py_version, wheel_cache_dir) # in the end just get the wheels wheels = _get_wheels(wheel_cache_dir, packages)
from ast import literal_eval from collections import OrderedDict from textwrap import dedent from six import add_metaclass from virtualenv.discovery.cached_py_info import LogCmd from virtualenv.info import WIN_CPYTHON_2 from virtualenv.util.path import Path, safe_delete from virtualenv.util.six import ensure_str, ensure_text from virtualenv.util.subprocess import run_cmd from virtualenv.version import __version__ from .pyenv_cfg import PyEnvCfg HERE = Path(os.path.abspath(__file__)).parent DEBUG_SCRIPT = HERE / "debug.py" class CreatorMeta(object): def __init__(self): self.error = None @add_metaclass(ABCMeta) class Creator(object): """A class that given a python Interpreter creates a virtual environment""" def __init__(self, options, interpreter): """Construct a new virtual environment creator. :param options: the CLI option as parsed from :meth:`add_parser_arguments`
def sources(cls, interpreter): for src in super(Pypy2Windows, cls).sources(interpreter): yield src yield PathRefToDest(Path(interpreter.system_prefix) / "libs", dest=lambda self, s: self.dest / s.name)
def stdlib(self): if self._stdlib is None: self._stdlib = Path( self.interpreter.sysconfig_path("stdlib", config_var=self._config_vars)) return self._stdlib
return False host_include_marker = getattr(builtin_classs, "host_include_marker", None) if host_include_marker is None: return False marker = host_include_marker(CURRENT) return not marker.exists() @pytest.mark.xfail( condition=bool(os.environ.get(str("CI_RUN"))), strict=False, reason= "did not manage to setup CI to run with VC 14.1 C++ compiler, but passes locally", ) @pytest.mark.skipif( not Path(CURRENT.system_include).exists() and not builtin_shows_marker_missing(), reason="Building C-Extensions requires header files with host python", ) @pytest.mark.parametrize("creator", list(i for i in CREATOR_CLASSES.keys() if i != "builtin")) def test_can_build_c_extensions(creator, tmp_path, coverage_env): env, greet = tmp_path / "env", str(tmp_path / "greet") shutil.copytree(str(Path(__file__).parent.resolve() / "greet"), greet) session = cli_run( ["--creator", creator, "--seeder", "app-data", str(env), "-vvv"]) coverage_env() cmd = [ str(session.creator.script("pip")),
def _load_pypi_info(name): return PythonInfo._from_json( (Path(__file__).parent / "{}.json".format(name)).read_text())
@pytest.mark.parametrize("case", ["mixed", "lower", "upper"]) def test_discovery_via_path(monkeypatch, case, tmp_path, caplog, session_app_data): caplog.set_level(logging.DEBUG) current = PythonInfo.current_system(session_app_data) core = "somethingVeryCryptic{}".format(".".join(str(i) for i in current.version_info[0:3])) name = "somethingVeryCryptic" if case == "lower": name = name.lower() elif case == "upper": name = name.upper() exe_name = "{}{}{}".format(name, current.version_info.major, ".exe" if sys.platform == "win32" else "") target = tmp_path / current.distutils_install["scripts"] target.mkdir(parents=True) executable = target / exe_name os.symlink(sys.executable, ensure_text(str(executable))) pyvenv_cfg = Path(sys.executable).parents[1] / "pyvenv.cfg" if pyvenv_cfg.exists(): (target / pyvenv_cfg.name).write_bytes(pyvenv_cfg.read_bytes()) new_path = os.pathsep.join([str(target)] + os.environ.get(str("PATH"), str("")).split(os.pathsep)) monkeypatch.setenv(str("PATH"), new_path) interpreter = get_interpreter(core) assert interpreter is not None def test_discovery_via_path_not_found(tmp_path, monkeypatch): monkeypatch.setenv(str("PATH"), str(tmp_path)) interpreter = get_interpreter(uuid4().hex) assert interpreter is None
def system(session_app_data): return get_env_debug_info(Path(CURRENT.system_executable), DEBUG_SCRIPT, session_app_data)
def _executables(cls, interpreter): host = Path(interpreter.system_executable) for path in (host.parent / n for n in ("python.exe", "pythonw.exe", host.name)): yield host, [path.name]
import sys import pytest from testing import path from testing.py_info import read_fixture from virtualenv.util.path import Path # Allows to import from `testing` into test submodules. sys.path.append(str(Path(__file__).parent)) @pytest.fixture def py_info(py_info_name): return read_fixture(py_info_name) @pytest.fixture def mock_files(mocker): return lambda paths, files: path.mock_files(mocker, paths, files) @pytest.fixture def mock_pypy_libs(mocker): return lambda pypy, libs: path.mock_pypy_libs(mocker, pypy, libs)
from __future__ import absolute_import, unicode_literals import shutil import subprocess import sys import pytest import six from virtualenv.discovery.py_info import PythonInfo from virtualenv.run import run_via_cli from virtualenv.util.path import Path HERE = Path(__file__).parent CURRENT = PythonInfo.current_system() @pytest.fixture(scope="session") def zipapp_build_env(tmp_path_factory): create_env_path = None if sys.version_info[0:2] >= (3, 5) and CURRENT.implementation != "PyPy": exe = CURRENT.executable # guaranteed to contain a recent enough pip (tox.ini) else: create_env_path = tmp_path_factory.mktemp("zipapp-create-env") exe, found = None, False # prefer CPython as builder as pypy is slow for impl in ["cpython", ""]: for version in range(8, 4, -1): try: # create a virtual environment which is also guaranteed to contain a recent enough pip (bundled) session = run_via_cli([
def _executables(cls, interpreter): host = Path(interpreter.system_executable) targets = sorted( "{}{}".format(name, PyPy.suffix) for name in cls.exe_names(interpreter) ) yield host, targets
import logging import os import pipes import sys from collections import OrderedDict from virtualenv.app_data import AppDataDisabled from virtualenv.discovery.py_info import PythonInfo from virtualenv.info import PY2 from virtualenv.util.path import Path from virtualenv.util.six import ensure_text from virtualenv.util.subprocess import Popen, subprocess _CACHE = OrderedDict() _CACHE[Path(sys.executable)] = PythonInfo() def from_exe(cls, app_data, exe, raise_on_error=True, ignore_cache=False): """""" result = _get_from_cache(cls, app_data, exe, ignore_cache=ignore_cache) if isinstance(result, Exception): if raise_on_error: raise result else: logging.info("%s", str(result)) result = None return result def _get_from_cache(cls, app_data, exe, ignore_cache=True):
def wheel(self): return Wheel(Path(self.filename))
def script_dir(self): return self.dest / Path(self.interpreter.distutils_install["scripts"])
def templates(self): yield Path("activate.fish")
def __init__(self, folder): self._lock = None path = Path(folder) self.path = path.resolve() if path.exists() else path
def special_name_dir(tmp_path, special_char_name): dest = Path(str(tmp_path)) / special_char_name yield dest if six.PY2 and sys.platform == "win32" and not IS_PYPY: # pytest python2 windows does not support unicode delete shutil.rmtree(ensure_text(str(dest)))
def mappings(cls, interpreter): mappings = [(Path(interpreter.system_stdlib_platform), cls.to_stdlib_platform)] if interpreter.system_stdlib_platform != interpreter.system_stdlib: mappings.append((Path(interpreter.system_stdlib), cls.to_stdlib)) return mappings
def test_do_update_first(tmp_path, mocker, freezer): freezer.move_to(_UP_NOW) wheel = get_embed_wheel("pip", "3.9") app_data_outer = AppDataDiskFolder(str(tmp_path / "app")) extra = tmp_path / "extra" extra.mkdir() pip_version_remote = [ (wheel_path(wheel, (1, 0, 0)), None), (wheel_path(wheel, (0, 1, 0)), _UP_NOW - timedelta(days=1)), (wheel_path(wheel, (0, 0, 1)), _UP_NOW - timedelta(days=2)), (wheel.path, _UP_NOW - timedelta(days=3)), (wheel_path(wheel, (-1, 0, 0)), _UP_NOW - timedelta(days=30)), ] download_wheels = (Wheel(Path(i[0])) for i in pip_version_remote) def _download_wheel(distribution, version_spec, for_py_version, search_dirs, app_data, to_folder): assert distribution == "pip" assert for_py_version == "3.9" assert [str(i) for i in search_dirs] == [str(extra)] assert isinstance(app_data, AppDataDiskFolder) assert to_folder == app_data_outer.house return next(download_wheels) download_wheel = mocker.patch("virtualenv.seed.wheels.acquire.download_wheel", side_effect=_download_wheel) releases = { Wheel(Path(wheel)).version: [ {"upload_time": datetime.strftime(release_date, "%Y-%m-%dT%H:%M:%S") if release_date is not None else None}, ] for wheel, release_date in pip_version_remote } pypi_release = json.dumps({"releases": releases}) @contextmanager def _release(of, context): assert of == "https://pypi.org/pypi/pip/json" assert context is None yield StringIO(pypi_release) url_o = mocker.patch("virtualenv.seed.wheels.periodic_update.urlopen", side_effect=_release) last_update = _UP_NOW - timedelta(days=14) u_log = UpdateLog(started=last_update, completed=last_update, versions=[], periodic=True) read_dict = mocker.patch("virtualenv.app_data.via_disk_folder.JSONStoreDisk.read", return_value=u_log.to_dict()) write = mocker.patch("virtualenv.app_data.via_disk_folder.JSONStoreDisk.write") versions = do_update("pip", "3.9", str(pip_version_remote[-1][0]), str(app_data_outer), [str(extra)], True) assert download_wheel.call_count == len(pip_version_remote) assert url_o.call_count == 1 expected = [ NewVersion(Path(wheel).name, _UP_NOW, None if release is None else release.replace(microsecond=0)) for wheel, release in pip_version_remote ] assert versions == expected assert read_dict.call_count == 1 assert write.call_count == 1 wrote_json = write.call_args[0][0] assert wrote_json == { "started": dump_datetime(last_update), "completed": dump_datetime(_UP_NOW), "periodic": True, "versions": [e.to_dict() for e in expected], }
def non_write_able(dest, value): common = Path(*os.path.commonprefix([value.parts, dest.parts])) raise ArgumentTypeError( "the destination {} is not write-able at {}".format( dest.relative_to(common), common), )
def download(): while True: path = pip_version_remote[at["index"]] at["index"] += 1 yield Wheel(Path(path))
def host_include_marker(cls, interpreter): return Path(interpreter.system_include) / "PyPy.h"
def system(): return get_env_debug_info(Path(CURRENT.system_executable), DEBUG_SCRIPT)
from __future__ import absolute_import, unicode_literals from virtualenv.seed.wheels.util import Wheel from virtualenv.util.path import Path BUNDLE_FOLDER = Path(__file__).absolute().parent BUNDLE_SUPPORT = { "3.10": { "pip": "pip-20.2.4-py2.py3-none-any.whl", "setuptools": "setuptools-50.3.2-py3-none-any.whl", "wheel": "wheel-0.35.1-py2.py3-none-any.whl", }, "3.9": { "pip": "pip-20.2.4-py2.py3-none-any.whl", "setuptools": "setuptools-50.3.2-py3-none-any.whl", "wheel": "wheel-0.35.1-py2.py3-none-any.whl", }, "3.8": { "pip": "pip-20.2.4-py2.py3-none-any.whl", "setuptools": "setuptools-50.3.2-py3-none-any.whl", "wheel": "wheel-0.35.1-py2.py3-none-any.whl", }, "3.7": { "pip": "pip-20.2.4-py2.py3-none-any.whl", "setuptools": "setuptools-50.3.2-py3-none-any.whl", "wheel": "wheel-0.35.1-py2.py3-none-any.whl", }, "3.6": { "pip": "pip-20.2.4-py2.py3-none-any.whl", "setuptools": "setuptools-50.3.2-py3-none-any.whl", "wheel": "wheel-0.35.1-py2.py3-none-any.whl",
def stdlib_platform(self): if self._stdlib_platform is None: self._stdlib_platform = Path( self.interpreter.sysconfig_path("platstdlib", config_var=self._config_vars)) return self._stdlib_platform
def image_ref(cls, interpreter): return Path(interpreter.prefix) / "Python"
def templates(self): yield Path("activate.bat") yield Path("deactivate.bat") yield Path("pydoc.bat")