def _non_success_exit_code(capsys, target): with pytest.raises(SystemExit) as context: run_via_cli(args=[target]) assert context.value.code != 0 out, err = capsys.readouterr() assert not out, out return err
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: run_via_cli(["-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: run_via_cli(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: run_via_cli(["-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 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"] run_via_cli(cmd) marker.write_text("") # if we a marker file this should be gone on a clear run, remain otherwise assert marker.exists() run_via_cli(cmd + (["--clear"] if clear else [])) assert marker.exists() is not clear
def test_version(capsys): with pytest.raises(SystemExit) as context: run_via_cli(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 import virtualenv assert virtualenv.__file__ in content
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 = run_via_cli(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 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([ "-v", "-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 zipapp_test_env(tmp_path_factory): base_path = tmp_path_factory.mktemp("zipapp-test") session = run_via_cli( ["-v", "--activators", "", "--without-pip", str(base_path / "env")]) yield session.creator.exe shutil.rmtree(str(base_path))
def run(args=None): start = datetime.now() from virtualenv.error import ProcessCallFailed from virtualenv.run import run_via_cli if args is None: args = sys.argv[1:] try: run_via_cli(args) except ProcessCallFailed as exception: print("subprocess call failed for {}".format(exception.cmd)) print(exception.out, file=sys.stdout, end="") print(exception.err, file=sys.stderr, end="") raise SystemExit(exception.code) finally: logging.info("done in %.0fms", (datetime.now() - start).total_seconds() * 1000)
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 = run_via_cli([ "--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_debug_bad_virtualenv(tmp_path): cmd = [str(tmp_path), "--without-pip"] result = run_via_cli(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_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 = run_via_cli(cmd) subprocess.check_call([str(result.creator.script("pip")), "--version"])
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 = run_via_cli(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
def test_cross_major(cross_python, coverage_env, tmp_path, current_fastest): cmd = [ "-v", "-v", "-p", six.ensure_text(cross_python.executable), six.ensure_text(str(tmp_path)), "--no-seed", "--activators", "", "--creator", current_fastest, ] result = run_via_cli(cmd) coverage_env() env = PythonInfo.from_exe(str(result.creator.exe)) assert env.version_info.major != CURRENT.version_info.major
def run(args=None, options=None): start = datetime.now() from virtualenv.error import ProcessCallFailed from virtualenv.run import run_via_cli if args is None: args = sys.argv[1:] try: session = run_via_cli(args, options) logging.warning( "created virtual environment in %.0fms %s with seeder %s", (datetime.now() - start).total_seconds() * 1000, six.ensure_text(str(session.creator)), six.ensure_text(str(session.seeder)), ) except ProcessCallFailed as exception: print("subprocess call failed for {}".format(exception.cmd)) print(exception.out, file=sys.stdout, end="") print(exception.err, file=sys.stderr, end="") raise SystemExit(exception.code)
def test_can_build_c_extensions(creator, tmp_path, coverage_env): session = run_via_cli(["--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(tmp_path, coverage_env, current_fastest): current = PythonInfo.current_system() bundle_ver = BUNDLE_SUPPORT[current.version_release_str] create_cmd = [ six.ensure_text(str(tmp_path / "env")), "--seeder", "app-data", "--extra-search-dir", six.ensure_text(str(BUNDLE_FOLDER)), "--download", "--pip", bundle_ver["pip"].split("-")[1], "--setuptools", bundle_ver["setuptools"].split("-")[1], "--clear-app-data", "--creator", current_fastest, "-vv", ] result = run_via_cli(create_cmd) coverage_env() assert result # uninstalling pip/setuptools now should leave us with a ensure_safe_to_do env site_package = result.creator.purelib pip = site_package / "pip" setuptools = site_package / "setuptools" files_post_first_create = list(site_package.iterdir()) assert pip in files_post_first_create assert setuptools in files_post_first_create for pip_exe in [ result.creator.script_dir / "pip{}{}".format(suffix, result.creator.exe.suffix) for suffix in ( "", "{}".format(current.version_info.major), "-{}.{}".format(current.version_info.major, current.version_info.minor), ) ]: assert pip_exe.exists() process = Popen([ six.ensure_text(str(pip_exe)), "--version", "--disable-pip-version-check" ]) _, __ = process.communicate() assert not process.returncode remove_cmd = [ str(result.creator.exe), "-m", "pip", "--verbose", "--disable-pip-version-check", "uninstall", "-y", "setuptools", ] process = Popen(remove_cmd) _, __ = process.communicate() assert not process.returncode assert site_package.exists() files_post_first_uninstall = list(site_package.iterdir()) assert pip in files_post_first_uninstall assert setuptools not in files_post_first_uninstall # check we can run it again and will work - checks both overwrite and reuse cache result = run_via_cli(create_cmd) coverage_env() assert result files_post_second_create = list(site_package.iterdir()) assert files_post_first_create == files_post_second_create process = Popen(remove_cmd + ["pip", "wheel"]) _, __ = process.communicate() assert not process.returncode # pip is greedy here, removing all packages removes the site-package too if site_package.exists(): post_run = list(site_package.iterdir()) assert not post_run, "\n".join(str(i) for i in post_run) if sys.version_info[0:2] == (3, 4) and os.environ.get( str("PIP_REQ_TRACKER")): os.environ.pop(str("PIP_REQ_TRACKER"))
def test_create_no_seed(python, creator, isolated, system, coverage_env, special_name_dir, method): dest = special_name_dir cmd = [ "-v", "-v", "-p", six.ensure_text(python), six.ensure_text(str(dest)), "--without-pip", "--activators", "", "--creator", creator, "--{}".format(method), ] if isolated == "global": cmd.append("--system-site-packages") result = run_via_cli(cmd) coverage_env() if IS_PYPY: # pypy cleans up file descriptors periodically so our (many) subprocess calls impact file descriptor limits # force a cleanup of these on system where the limit is low-ish (e.g. MacOS 256) gc.collect() content = list(result.creator.purelib.iterdir()) assert not content, "\n".join(six.ensure_text(str(i)) for i in content) assert result.creator.env_name == six.ensure_text(dest.name) debug = result.creator.debug sys_path = cleanup_sys_path(debug["sys"]["path"]) system_sys_path = cleanup_sys_path(system["sys"]["path"]) our_paths = set(sys_path) - set(system_sys_path) our_paths_repr = "\n".join(six.ensure_text(repr(i)) for i in our_paths) # ensure we have at least one extra path added assert len(our_paths) >= 1, our_paths_repr # ensure all additional paths are related to the virtual environment for path in our_paths: msg = "\n{}\ndoes not start with {}\nhas:\n{}".format( six.ensure_text(str(path)), six.ensure_text(str(dest)), "\n".join(six.ensure_text(str(p)) for p in system_sys_path), ) assert str(path).startswith(str(dest)), msg # ensure there's at least a site-packages folder as part of the virtual environment added assert any(p for p in our_paths if p.parts[-1] == "site-packages"), our_paths_repr # ensure the global site package is added or not, depending on flag last_from_system_path = next(i for i in reversed(system_sys_path) if str(i).startswith(system["sys"]["prefix"])) if isolated == "isolated": assert last_from_system_path not in sys_path else: common = [] for left, right in zip(reversed(system_sys_path), reversed(sys_path)): if left == right: common.append(left) else: break def list_to_str(iterable): return [six.ensure_text(str(i)) for i in iterable] assert common, "\n".join( difflib.unified_diff(list_to_str(sys_path), list_to_str(system_sys_path)))