def test_pyenv_shims(self): # type: () -> None py35, _, run_pyenv = ensure_python_distribution(PY35) py36 = ensure_python_interpreter(PY36) pyenv_root = str(run_pyenv(["root"]).strip()) pyenv_shims = os.path.join(pyenv_root, "shims") def pyenv_global(*versions): run_pyenv(["global"] + list(versions)) def assert_shim(shim_name, expected_binary_path): python = PythonInterpreter.from_binary( os.path.join(pyenv_shims, shim_name)) assert expected_binary_path == python.binary with temporary_dir() as pex_root: with ENV.patch(PEX_ROOT=pex_root) as pex_env: with environment_as(PYENV_ROOT=pyenv_root, **pex_env): pyenv_global(PY35, PY36) assert_shim("python3", py35) pyenv_global(PY36, PY35) # The python3 shim is now pointing at python3.6 but the Pex cache has a valid # entry for the old python3.5 association (the interpreter still exists.) assert_shim("python3", py35) # The shim pointer is now invalid since python3.5 was uninstalled and so should # be re-read. py35_deleted = "{}.uninstalled".format(py35) os.rename(py35, py35_deleted) try: assert_shim("python3", py36) finally: os.rename(py35_deleted, py35)
def test_pex_run_strip_env(): with temporary_dir() as pex_root: pex_env = dict(PEX_MODULE='does_not_exist_in_sub_pex', PEX_ROOT=pex_root) with environment_as(**pex_env), temporary_dir() as pex_chroot: pex_builder = PEXBuilder(path=pex_chroot) with tempfile.NamedTemporaryFile(mode="w") as fp: fp.write( dedent(""" import json import os print(json.dumps({k: v for k, v in os.environ.items() if k.startswith("PEX_")})) """)) fp.flush() pex_builder.set_executable(fp.name, 'print_pex_env.py') pex_builder.freeze() stdout, returncode = run_simple_pex(pex_chroot) assert 0 == returncode assert {} == json.loads(stdout.decode('utf-8')), ( 'Expected the entrypoint environment to be stripped of PEX_ environment variables.' ) assert pex_env == { k: v for k, v in os.environ.items() if k.startswith("PEX_") }, ('Expected the parent environment to be left un-stripped.')
def test_iter_candidates_empty_paths(self, test_interpreter1): # type: (str) -> None # Whereas `paths=None` should inspect $PATH, `paths=[]` means to search nothing. with environment_as(PATH=test_interpreter1): assert [] == list(PythonInterpreter.iter_candidates(paths=[])) assert [PythonInterpreter.from_binary(test_interpreter1) ] == list(PythonInterpreter.iter_candidates(paths=None))
def assert_pex_vars_hermetic(): v = Variables() assert os.environ == v.copy() existing = os.environ.get('TEST') expected = (existing or '') + 'different' assert expected != existing with environment_as(TEST=expected): assert expected != v.copy().get('TEST')
def assert_pex_vars_hermetic(): # type: () -> None v = Variables() assert os.environ.copy() == v.copy() existing = os.environ.get("TEST") expected = (existing or "") + "different" assert expected != existing with environment_as(TEST=expected): assert expected != v.copy().get("TEST")
def test_parse_requirements_stress(chroot): # type: (str) -> None with safe_open(os.path.join(chroot, "other-requirements.txt"), "w") as fp: fp.write( # This includes both example snippets taken directly from # https://pip.pypa.io/en/stable/reference/pip_install/#requirements-file-format # not already covered by # https://pip.pypa.io/en/stable/reference/pip_install/#example-requirements-file. dedent( """\ SomeProject SomeProject == 1.3 SomeProject >=1.2,<2.0 SomeProject[foo, bar] SomeProject~=1.4.2 SomeProject ==5.4 ; python_version < '2.7' SomeProject; sys_platform == 'win32' SomeProject @ https://example.com/somewhere/over/here SomeProject @ file:somewhere/over/here FooProject >= 1.2 --global-option="--no-user-cfg" \\ --install-option="--prefix='/usr/local'" \\ --install-option="--no-compile" git+https://git.example.com/MyProject.git@da39a3ee5e6b4b0d3255bfef95601890afd80709#egg=MyProject git+ssh://git.example.com/MyProject#egg=MyProject git+file:///home/user/projects/MyProject#egg=MyProject&subdirectory=pkg_dir # N.B. This is not from the Pip docs unlike the examples above. We just want to # chain in one more set of stress tests. -r extra/stress.txt """ ) ) touch("somewhere/over/here/pyproject.toml") with safe_open(os.path.join(chroot, "extra", "stress.txt"), "w") as fp: fp.write( # These are tests of edge cases not included anywhere in the examples found in # https://pip.pypa.io/en/stable/reference/pip_install/#requirements-file-format. dedent( """\ -c file:subdir/more-requirements.txt a/local/project[foo]; python_full_version == "2.7.8" ./another/local/project;python_version == "2.7.*" ./another/local/project ./ # Local projects with basenames that are invalid Python project names (trailing _): tmp/tmpW8tdb_ tmp/tmpW8tdb_[foo] tmp/tmpW8tdb_[foo];python_version == "3.9" hg+http://hg.example.com/MyProject@da39a3ee5e6b#egg=AnotherProject[extra,more];python_version=="3.9.*"&subdirectory=foo/bar ftp://a/${PROJECT_NAME}-1.0.tar.gz http://a/${PROJECT_NAME}-1.0.zip https://a/numpy-1.9.2-cp34-none-win32.whl Django@ git+https://github.com/django/django.git Django@git+https://github.com/django/django.git@stable/2.1.x Django@ git+https://github.com/django/django.git@fd209f62f1d83233cc634443cfac5ee4328d98b8 Django @ file:projects/django-2.3.zip; python_version >= "3.10" """ ) ) touch("extra/pyproject.toml") touch("extra/a/local/project/pyproject.toml") touch("extra/another/local/project/setup.py") touch("extra/tmp/tmpW8tdb_/setup.py") touch("extra/projects/django-2.3.zip") with safe_open(os.path.join(chroot, "subdir", "more-requirements.txt"), "w") as fp: fp.write( # This checks requirements (`ReqInfo`s) are wrapped up into `Constraints`. dedent( """\ AnotherProject """ ) ) req_iter = parse_requirements( Source.from_text( # N.B.: Taken verbatim from: # https://pip.pypa.io/en/stable/reference/pip_install/#example-requirements-file dedent( """\ # ####### example-requirements.txt ####### # ###### Requirements without Version Specifiers ###### nose nose-cov beautifulsoup4 # ###### Requirements with Version Specifiers ###### # See https://www.python.org/dev/peps/pep-0440/#version-specifiers docopt == 0.6.1 # Version Matching. Must be version 0.6.1 keyring >= 4.1.1 # Minimum version 4.1.1 coverage != 3.5 # Version Exclusion. Anything except version 3.5 Mopidy-Dirble ~= 1.1 # Compatible release. Same as >= 1.1, == 1.* # ###### Refer to other requirements files ###### -r other-requirements.txt # # ###### A particular file ###### ./downloads/numpy-1.9.2-cp34-none-win32.whl http://wxpython.org/Phoenix/snapshot-builds/wxPython_Phoenix-3.0.3.dev1820+49a8884-cp34-none-win_amd64.whl # ###### Additional Requirements without Version Specifiers ###### # Same as 1st section, just here to show that you can put things in any order. rejected green # """ ), ) ) # Ensure local non-distribution files matching distribution names are not erroneously probed # as distributions to find name and version metadata. touch("nose") touch("downloads/numpy-1.9.2-cp34-none-win32.whl") with environment_as(PROJECT_NAME="Project"): results = normalize_results(req_iter) assert [ req(project_name="nose"), req(project_name="nose-cov"), req(project_name="beautifulsoup4"), req(project_name="docopt", specifier="==0.6.1"), req(project_name="keyring", specifier=">=4.1.1"), req(project_name="coverage", specifier="!=3.5"), req(project_name="Mopidy-Dirble", specifier="~=1.1"), req(project_name="SomeProject"), req(project_name="SomeProject", specifier="==1.3"), req(project_name="SomeProject", specifier=">=1.2,<2.0"), req(project_name="SomeProject", extras=["foo", "bar"]), req(project_name="SomeProject", specifier="~=1.4.2"), req(project_name="SomeProject", specifier="==5.4", marker="python_version < '2.7'"), req(project_name="SomeProject", marker="sys_platform == 'win32'"), url_req(project_name="SomeProject", url="https://example.com/somewhere/over/here"), local_req(path=os.path.realpath("somewhere/over/here")), req(project_name="FooProject", specifier=">=1.2"), url_req( project_name="MyProject", url="git+https://git.example.com/MyProject.git@da39a3ee5e6b4b0d3255bfef95601890afd80709", ), url_req(project_name="MyProject", url="git+ssh://git.example.com/MyProject"), url_req(project_name="MyProject", url="git+file:/home/user/projects/MyProject"), Constraint(DUMMY_LINE, Requirement.parse("AnotherProject")), local_req( path=os.path.realpath("extra/a/local/project"), extras=["foo"], marker="python_full_version == '2.7.8'", ), local_req( path=os.path.realpath("extra/another/local/project"), marker="python_version == '2.7.*'", ), local_req(path=os.path.realpath("extra/another/local/project")), local_req(path=os.path.realpath("extra")), local_req(path=os.path.realpath("extra/tmp/tmpW8tdb_")), local_req(path=os.path.realpath("extra/tmp/tmpW8tdb_"), extras=["foo"]), local_req( path=os.path.realpath("extra/tmp/tmpW8tdb_"), extras=["foo"], marker="python_version == '3.9'", ), url_req( project_name="AnotherProject", url="hg+http://hg.example.com/MyProject@da39a3ee5e6b", extras=["more", "extra"], marker="python_version == '3.9.*'", ), url_req(project_name="Project", url="ftp://a/Project-1.0.tar.gz", specifier="==1.0"), url_req(project_name="Project", url="http://a/Project-1.0.zip", specifier="==1.0"), url_req( project_name="numpy", url="https://a/numpy-1.9.2-cp34-none-win32.whl", specifier="==1.9.2", ), url_req(project_name="Django", url="git+https://github.com/django/django.git"), url_req(project_name="Django", url="git+https://github.com/django/django.git@stable/2.1.x"), url_req( project_name="Django", url="git+https://github.com/django/django.git@fd209f62f1d83233cc634443cfac5ee4328d98b8", ), url_req( project_name="Django", url=os.path.realpath("extra/projects/django-2.3.zip"), specifier="==2.3", marker="python_version>='3.10'", ), url_req( project_name="numpy", url=os.path.realpath("./downloads/numpy-1.9.2-cp34-none-win32.whl"), specifier="==1.9.2", ), url_req( project_name="wxPython_Phoenix", url="http://wxpython.org/Phoenix/snapshot-builds/wxPython_Phoenix-3.0.3.dev1820+49a8884-cp34-none-win_amd64.whl", specifier="==3.0.3.dev1820+49a8884", ), req(project_name="rejected"), req(project_name="green"), ] == results
def test_pex_vars_hermetic(): with environment_as(PEX_IGNORE_RCFILES='True'): assert_pex_vars_hermetic()
def test_pex_vars_hermetic(): # type: () -> None with environment_as(PEX_IGNORE_RCFILES="True"): assert_pex_vars_hermetic()
def pyenv_shell(*versions): # type: (*str) -> Iterator[None] with environment_as(PYENV_VERSION=":".join(versions)): yield
def test_pyenv_shims(self, tmpdir): # type: (Any) -> None py35, _, run_pyenv = ensure_python_distribution(PY35) py36 = ensure_python_interpreter(PY36) pyenv_root = str(run_pyenv(["root"]).strip()) pyenv_shims = os.path.join(pyenv_root, "shims") def pyenv_global(*versions): # type: (*str) -> None run_pyenv(["global"] + list(versions)) def pyenv_local(*versions): # type: (*str) -> None run_pyenv(["local"] + list(versions)) @contextmanager def pyenv_shell(*versions): # type: (*str) -> Iterator[None] with environment_as(PYENV_VERSION=":".join(versions)): yield pex_root = os.path.join(str(tmpdir), "pex_root") cwd = safe_mkdir(os.path.join(str(tmpdir), "home", "jake", "project")) with ENV.patch(PEX_ROOT=pex_root) as pex_env, environment_as( PYENV_ROOT=pyenv_root, PEX_PYTHON_PATH=pyenv_shims, **pex_env ), pyenv_shell(), pushd(cwd): pyenv = Pyenv.find() assert pyenv is not None assert pyenv_root == pyenv.root def interpreter_for_shim(shim_name): # type: (str) -> PythonInterpreter binary = os.path.join(pyenv_shims, shim_name) return PythonInterpreter.from_binary(binary, pyenv=pyenv) def assert_shim( shim_name, # type: str expected_binary_path, # type: str ): # type: (...) -> None python = interpreter_for_shim(shim_name) assert expected_binary_path == python.binary def assert_shim_inactive(shim_name): # type: (str) -> None with pytest.raises(PythonInterpreter.IdentificationError): interpreter_for_shim(shim_name) pyenv_global(PY35, PY36) assert_shim("python", py35) assert_shim("python3", py35) assert_shim("python3.5", py35) assert_shim("python3.6", py36) pyenv_global(PY36, PY35) assert_shim("python", py36) assert_shim("python3", py36) assert_shim("python3.6", py36) assert_shim("python3.5", py35) pyenv_local(PY35) assert_shim("python", py35) assert_shim("python3", py35) assert_shim("python3.5", py35) assert_shim_inactive("python3.6") with pyenv_shell(PY36): assert_shim("python", py36) assert_shim("python3", py36) assert_shim("python3.6", py36) assert_shim_inactive("python3.5") with pyenv_shell(PY35, PY36): assert_shim("python", py35) assert_shim("python3", py35) assert_shim("python3.5", py35) assert_shim("python3.6", py36) # The shim pointer is now invalid since python3.5 was uninstalled and so # should be re-read and found invalid. py35_version_dir = os.path.dirname(os.path.dirname(py35)) py35_deleted = "{}.uninstalled".format(py35_version_dir) os.rename(py35_version_dir, py35_deleted) try: assert_shim_inactive("python") assert_shim_inactive("python3") assert_shim_inactive("python3.5") finally: os.rename(py35_deleted, py35_version_dir) assert_shim("python", py35)