def test_getsitepackages_system_site(tmp_path): # Test without --system-site-packages session = cli_run([ensure_text(str(tmp_path))]) system_site_packages = get_expected_system_site_packages(session) out = subprocess.check_output( [str(session.creator.exe), "-c", r"import site; print(site.getsitepackages())"], universal_newlines=True, ) site_packages = ast.literal_eval(out) for system_site_package in system_site_packages: assert system_site_package not in site_packages # Test with --system-site-packages session = cli_run([ensure_text(str(tmp_path)), "--system-site-packages"]) system_site_packages = get_expected_system_site_packages(session) out = subprocess.check_output( [str(session.creator.exe), "-c", r"import site; print(site.getsitepackages())"], universal_newlines=True, ) site_packages = ast.literal_eval(out) for system_site_package in system_site_packages: assert system_site_package in site_packages
def test_create_vcs_ignore_exists_override(tmp_path): git_ignore = tmp_path / ".gitignore" git_ignore.write_text("magic") cli_run([ str(tmp_path), "--without-pip", "--no-vcs-ignore", "--activators", "" ]) assert git_ignore.read_text() == "magic"
def test_populated_read_only_cache_and_symlinked_app_data( tmp_path, current_fastest, temp_app_data, monkeypatch): dest = tmp_path / "venv" cmd = [ "--seeder", "app-data", "--creator", current_fastest, "--symlink-app-data", "-vv", str(dest), ] assert cli_run(cmd) subprocess.check_call( (str(dest.joinpath("bin/python")), "-c", "import pip")) cached_py_info._CACHE.clear() # necessary to re-trigger py info discovery safe_delete(dest) # should succeed with special flag when read-only with read_only_dir(temp_app_data): assert cli_run(["--read-only-app-data"] + cmd) subprocess.check_call( (str(dest.joinpath("bin/python")), "-c", "import pip"))
def test_failed_to_find_implementation(of_id, mocker): mocker.patch("virtualenv.run.plugin.creators.CreatorSelector._OPTIONS", return_value={}) with pytest.raises(RuntimeError) as context: cli_run(["-p", of_id]) assert repr(context.value).startswith( 'RuntimeError("No virtualenv implementation for')
def test_failed_to_find_implementation(of_id, mocker): mocker.patch("virtualenv.run.plugin.creators.CreatorSelector._OPTIONS", return_value={}) with pytest.raises(RuntimeError) as context: cli_run(["-p", of_id]) assert repr(context.value) == repr( RuntimeError("No virtualenv implementation for {}".format(PythonInfo.current_system())) )
def test_help(capsys): with pytest.raises(SystemExit) as context: cli_run(args=["-h", "-vvv"]) assert context.value.code == 0 out, err = capsys.readouterr() assert not err assert out
def test_failed_to_find_bad_spec(): of_id = uuid4().hex with pytest.raises(RuntimeError) as context: cli_run(["-p", of_id]) msg = repr( RuntimeError( "failed to find interpreter for Builtin discover of python_spec={!r}" .format(of_id))) assert repr(context.value) == msg
def _run(name): try: cli_run([ "--seeder", "app-data", str(tmp_path / name), "--no-pip", "--no-setuptools" ]) except Exception as exception: # noqa as_str = str(exception) exceptions.append(as_str)
def test_version(capsys): with pytest.raises(SystemExit) as context: cli_run(args=["--version"]) assert context.value.code == 0 out, err = capsys.readouterr() extra = out if six.PY2 else err content = out if six.PY3 else err assert not extra assert __version__ in content
def test_create_clear_resets(tmp_path, creator, clear, caplog): caplog.set_level(logging.DEBUG) if creator == "venv" and clear is False: pytest.skip("venv without clear might fail") marker = tmp_path / "magic" cmd = [str(tmp_path), "--seeder", "app-data", "--without-pip", "--creator", creator, "-vvv"] cli_run(cmd) marker.write_text("") # if we a marker file this should be gone on a clear run, remain otherwise assert marker.exists() cli_run(cmd + (["--clear"] if clear else [])) assert marker.exists() is not clear
def test_base_bootstrap_via_pip_invoke(tmp_path, coverage_env, current_fastest, no): bundle_ver = BUNDLE_SUPPORT[PythonInfo.current_system().version_release_str] create_cmd = [ "--seeder", "pip", str(tmp_path / "env"), "--download", "--pip", bundle_ver["pip"].split("-")[1], "--setuptools", bundle_ver["setuptools"].split("-")[1], "--creator", current_fastest, ] if no: create_cmd.append("--no-{}".format(no)) result = cli_run(create_cmd) coverage_env() assert result site_package = result.creator.purelib pip = site_package / "pip" setuptools = site_package / "setuptools" wheel = site_package / "wheel" files_post_first_create = list(site_package.iterdir()) if no: no_file = locals()[no] assert no not in files_post_first_create for key in ("pip", "setuptools", "wheel"): if key == no: continue assert locals()[key] in files_post_first_create
def test_base_bootstrap_via_pip_invoke(tmp_path, coverage_env, current_fastest): bundle_ver = BUNDLE_SUPPORT[PythonInfo.current_system().version_release_str] create_cmd = [ "--seeder", "pip", str(tmp_path / "env"), "--download", "--pip", bundle_ver["pip"].split("-")[1], "--setuptools", bundle_ver["setuptools"].split("-")[1], "--creator", current_fastest, ] result = cli_run(create_cmd) coverage_env() assert result site_package = result.creator.purelib pip = site_package / "pip" setuptools = site_package / "setuptools" wheel = site_package / "wheel" files_post_first_create = list(site_package.iterdir()) assert pip in files_post_first_create assert setuptools in files_post_first_create assert wheel in files_post_first_create
def test_create_distutils_cfg(creator, tmp_path, monkeypatch): cmd = [ ensure_text(str(tmp_path)), "--activators", "", "--creator", creator, ] result = cli_run(cmd) app = Path(__file__).parent / "console_app" dest = tmp_path / "console_app" shutil.copytree(str(app), str(dest)) setup_cfg = dest / "setup.cfg" conf = dedent( """ [install] prefix={}/a install_scripts={}/b """ ).format(tmp_path, tmp_path) setup_cfg.write_text(setup_cfg.read_text() + conf) monkeypatch.chdir(dest) # distutils will read the setup.cfg from the cwd, so change to that install_demo_cmd = [str(result.creator.script("pip")), "install", str(dest), "--no-use-pep517"] subprocess.check_call(install_demo_cmd) magic = result.creator.script("magic") # console scripts are created in the right location assert magic.exists() package_folder = result.creator.platlib / "demo" # prefix is set to the virtualenv prefix for install assert package_folder.exists()
def create_venv(build_item, bin_dir, src_dir): """ Create a virtualEnv and install the requirements specified within the source code """ packages_file = build_item["virtualenv"] venv_path = os.path.join(bin_dir, "venv") pip_bin = os.path.join(venv_path, "bin/pip") virtualenv.cli_run([venv_path]) cmd = [pip_bin, "install", "-r", os.path.join(src_dir, packages_file)] cmd = shlex.split(" ".join(cmd)) install_proc = Popen(cmd, stdout=PIPE, stderr=PIPE) _, stderr_data = install_proc.communicate() if install_proc.returncode != 0: raise IPOLConstructVirtualenvError(stderr_data)
def test_no_preimport_threading(tmp_path, no_coverage): session = cli_run([ensure_text(str(tmp_path))]) out = subprocess.check_output( [str(session.creator.exe), "-c", r"import sys; print('\n'.join(sorted(sys.modules)))"], universal_newlines=True, ) imported = set(out.splitlines()) assert "threading" not in imported
def zipapp_test_env(tmp_path_factory): base_path = tmp_path_factory.mktemp("zipapp-test") session = cli_run( ["-v", "--activators", "", "--without-pip", str(base_path / "env")]) yield session.creator.exe shutil.rmtree(str(base_path))
def test_cross_major(cross_python, coverage_env, tmp_path, current_fastest, session_app_data): cmd = [ "-v", "-v", "-p", ensure_text(cross_python.executable), ensure_text(str(tmp_path)), "--no-setuptools", "--no-wheel", "--activators", "", "--creator", current_fastest, ] result = cli_run(cmd) pip_scripts = { i.name.replace(".exe", "") for i in result.creator.script_dir.iterdir() if i.name.startswith("pip") } major, minor = cross_python.version_info[0:2] assert pip_scripts == { "pip", "pip-{}.{}".format(major, minor), "pip{}".format(major) } coverage_env() env = PythonInfo.from_exe(str(result.creator.exe), session_app_data) assert env.version_info.major != CURRENT.version_info.major
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 = cli_run([ "-vvv", "-p", "{}3.{}".format(impl, version), "--activators", "", str(create_env_path) ]) exe = str(session.creator.exe) found = True break except Exception: pass if found: break else: raise RuntimeError("could not find a python to build zipapp") cmd = [ str(Path(exe).parent / "pip"), "install", "pip>=19.3", "packaging>=20" ] subprocess.check_call(cmd) yield exe if create_env_path is not None: shutil.rmtree(str(create_env_path))
def test_zip_importer_can_import_setuptools(tmp_path): """We're patching the loaders so might fail on r/o loaders, such as zipimporter on CPython<3.8""" result = cli_run([ str(tmp_path / "venv"), "--activators", "", "--no-pip", "--no-wheel", "--copies" ]) zip_path = tmp_path / "site-packages.zip" with zipfile.ZipFile(str(zip_path), "w", zipfile.ZIP_DEFLATED) as zip_handler: lib = str(result.creator.purelib) for root, _, files in os.walk(lib): base = root[len(lib):].lstrip(os.pathsep) for file in files: if not file.startswith("_virtualenv"): zip_handler.write(filename=os.path.join(root, file), arcname=os.path.join(base, file)) for folder in result.creator.purelib.iterdir(): if not folder.name.startswith("_virtualenv"): if folder.is_dir(): shutil.rmtree(str(folder), ignore_errors=True) else: folder.unlink() env = os.environ.copy() env[str("PYTHONPATH")] = str(zip_path) subprocess.check_call([ str(result.creator.exe), "-c", "from setuptools.dist import Distribution" ], env=env)
def test_py_pyc_missing(tmp_path, mocker, session_app_data, py, pyc): """Ensure that creation can succeed if os.pyc exists (even if os.py has been deleted)""" previous = Python2.from_stdlib def from_stdlib(mappings, name): path, to, exists = previous(mappings, name) if name.endswith("py"): exists = py elif name.endswith("pyc"): exists = pyc return path, to, exists mocker.patch.object(Python2, "from_stdlib", side_effect=from_stdlib) result = cli_run([ ensure_text(str(tmp_path)), "--without-pip", "--activators", "", "-vv" ]) py_at = Python2.from_stdlib(Python2.mappings(CURRENT), "os.py")[1](result.creator, Path("os.py")) py = pyc is False or py # if pyc is False we fallback to serve the py, which will exist (as we only mock the check) assert py_at.exists() is py pyc_at = Python2.from_stdlib(Python2.mappings(CURRENT), "osc.py")[1](result.creator, Path("os.pyc")) assert pyc_at.exists() is pyc
def test_can_build_c_extensions(creator, tmp_path, coverage_env): session = cli_run( ["--creator", creator, "--seed", "app-data", str(tmp_path), "-vvv"]) coverage_env() cmd = [ str(session.creator.script("pip")), "install", "--no-index", "--no-deps", "--disable-pip-version-check", "-vvv", str(Path(__file__).parent.resolve() / "greet"), ] process = Popen(cmd) process.communicate() assert process.returncode == 0 process = Popen( [str(session.creator.exe), "-c", "import greet; greet.greet('World')"], universal_newlines=True, stdout=subprocess.PIPE, ) out, _ = process.communicate() assert process.returncode == 0 assert out == "Hello World!\n"
def test_base_bootstrap_link_via_app_data_no(tmp_path, coverage_env, current_fastest, session_app_data, pkg): create_cmd = [str(tmp_path), "--seeder", "app-data", "--no-{}".format(pkg)] result = cli_run(create_cmd) assert not (result.creator.purelib / pkg).exists() for key in {"pip", "setuptools", "wheel"} - {pkg}: assert (result.creator.purelib / key).exists()
def activation_python(tmp_path_factory, special_char_name, current_fastest): dest = os.path.join(six.ensure_text(str(tmp_path_factory.mktemp("activation-tester-env"))), special_char_name) session = cli_run(["--without-pip", dest, "--prompt", special_char_name, "--creator", current_fastest, "-vv"]) pydoc_test = session.creator.purelib / "pydoc_test.py" pydoc_test.write_text('"""This is pydoc_test.py"""') yield session if WIN_CPYTHON_2: # PY2 windows does not support unicode delete shutil.rmtree(dest)
def test_base_bootstrap_via_pip_invoke(tmp_path, coverage_env, mocker, current_fastest, no): bundle_ver = BUNDLE_SUPPORT[ PythonInfo.current_system().version_release_str] extra_search_dir = tmp_path / "extra" extra_search_dir.mkdir() original = PipInvoke._execute def _execute(cmd, env): assert set(cmd[-4:]) == { "--find-links", str(extra_search_dir), str(BUNDLE_FOLDER) } return original(cmd, env) run = mocker.patch.object(PipInvoke, "_execute", side_effect=_execute) create_cmd = [ "--seeder", "pip", str(tmp_path / "env"), "--download", "--pip", bundle_ver["pip"].split("-")[1], "--setuptools", bundle_ver["setuptools"].split("-")[1], "--creator", current_fastest, "--extra-search-dir", str(extra_search_dir), ] if no: create_cmd.append("--no-{}".format(no)) result = cli_run(create_cmd) coverage_env() assert result assert run.call_count == 1 site_package = result.creator.purelib pip = site_package / "pip" setuptools = site_package / "setuptools" wheel = site_package / "wheel" files_post_first_create = list(site_package.iterdir()) if no: no_file = locals()[no] assert no not in files_post_first_create for key in ("pip", "setuptools", "wheel"): if key == no: continue assert locals()[key] in files_post_first_create
def test_base_bootstrap_link_via_app_data_not_writable(tmp_path, current_fastest, read_only_app_data, monkeypatch): dest = tmp_path / "venv" result = cli_run([ "--seeder", "app-data", "--creator", current_fastest, "-vv", str(dest) ]) assert result
def old_virtualenv(tmp_path_factory, session_app_data): if CURRENT.is_old_virtualenv: return CURRENT.executable else: env_for_old_virtualenv = tmp_path_factory.mktemp( "env-for-old-virtualenv") result = cli_run([ "--no-download", "--activators", "", str(env_for_old_virtualenv), "--no-periodic-update" ]) # noinspection PyBroadException try: process = Popen( [ str(result.creator.script("pip")), "install", "--no-index", "--disable-pip-version-check", str( Path(__file__).resolve().parent / "virtualenv-16.7.9-py2.py3-none-any.whl"), "-v", ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) _, __ = process.communicate() assert not process.returncode except Exception: return RuntimeError("failed to install old virtualenv") # noinspection PyBroadException try: old_virtualenv_at = tmp_path_factory.mktemp("old-virtualenv") cmd = [ str(result.creator.script("virtualenv")), str(old_virtualenv_at), "--no-pip", "--no-setuptools", "--no-wheel", ] process = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) _, __ = process.communicate() assert not process.returncode if result.creator.interpreter.implementation == "PyPy" and IS_WIN: # old virtualenv creates pypy paths wrong on windows, so have to hardcode it return str(old_virtualenv_at / "bin" / "pypy.exe") exe_path = CURRENT.discover_exe( session_app_data, prefix=str(old_virtualenv_at)).original_executable return exe_path except Exception as exception: return RuntimeError( "failed to create old virtualenv {}".format(exception))
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 test_create_long_path(current_fastest, tmp_path): if sys.platform == "darwin": max_shebang_length = 512 else: max_shebang_length = 127 # filenames can be at most 255 long on macOS, so split to to levels count = max_shebang_length - len(str(tmp_path)) folder = tmp_path / ("a" * (count // 2)) / ("b" * (count // 2)) / "c" folder.mkdir(parents=True) cmd = [str(folder)] result = cli_run(cmd) subprocess.check_call([str(result.creator.script("pip")), "--version"])
def test_debug_bad_virtualenv(tmp_path): cmd = [str(tmp_path), "--without-pip"] result = cli_run(cmd) # if the site.py is removed/altered the debug should fail as no one is around to fix the paths site_py = result.creator.stdlib / "site.py" site_py.unlink() # insert something that writes something on the stdout site_py.write_text('import sys; sys.stdout.write(repr("std-out")); sys.stderr.write("std-err"); raise ValueError') debug_info = result.creator.debug assert debug_info["returncode"] assert debug_info["err"].startswith("std-err") assert "std-out" in debug_info["out"] assert debug_info["exception"]
def test_prompt_set(tmp_path, creator, prompt): cmd = [str(tmp_path), "--seeder", "app-data", "--without-pip", "--creator", creator] if prompt is not None: cmd.extend(["--prompt", "magic"]) result = cli_run(cmd) actual_prompt = tmp_path.name if prompt is None else prompt cfg = PyEnvCfg.from_file(result.creator.pyenv_cfg.path) if prompt is None: assert "prompt" not in cfg else: if creator != "venv": assert "prompt" in cfg, list(cfg.content.keys()) assert cfg["prompt"] == actual_prompt