示例#1
0
def mocked_invoker(**sysattrs):
    major = sysattrs.pop("major", 3)
    pyenv = sysattrs.pop("pyenv", None)
    use_path = sysattrs.pop("use_path", False)
    exe_exists = sysattrs.pop("exe_exists", True)
    sysattrs.setdefault("base_prefix", "/usr")
    sysattrs.setdefault("prefix", sysattrs["base_prefix"])
    sysattrs["base_prefix"] = runez.resolved_path(sysattrs["base_prefix"])
    sysattrs["prefix"] = runez.resolved_path(sysattrs["prefix"])
    sysattrs.setdefault("executable",
                        "%s/bin/python" % sysattrs["base_prefix"])
    sysattrs.setdefault("version_info", (major, 7, 1))
    sysattrs.setdefault("version",
                        ".".join(str(s) for s in sysattrs["version_info"]))
    scanner = None if not pyenv else PythonInstallationScanner(pyenv)
    with patch("runez.pyenv.os.path.realpath", side_effect=lambda x: x):
        with patch("runez.pyenv.sys") as mocked:
            for k, v in sysattrs.items():
                setattr(mocked, k, v)

            if isinstance(exe_exists, bool):
                with patch("runez.pyenv.is_executable",
                           return_value=exe_exists):
                    return PythonDepot(scanner=scanner, use_path=use_path)

            with patch("runez.pyenv.is_executable", side_effect=exe_exists):
                return PythonDepot(scanner=scanner, use_path=use_path)
示例#2
0
def test_venv(temp_folder, logged):
    depot = PythonDepot(use_path=False)
    import sys
    p = depot.find_python(sys.executable)
    assert p is depot.invoker

    # Simulate an explicit reference to a venv python
    mk_python("8.6.1")
    mk_python("8.6.1",
              prefix="foo",
              base_prefix=".pyenv/versions/8.6.1",
              folder=".venv")
    assert str(depot) == "0 scanned"
    pvenv = depot.find_python(".venv/bin/python")
    assert str(depot) == "0 scanned"
    assert depot.find_python(".venv") == pvenv
    assert str(pvenv) == ".pyenv/versions/8.6.1 [cpython:8.6.1]"

    # Edge case: version is found via .pyenv first
    depot = PythonDepot(scanner=CustomScanner(".pyenv"), use_path=False)
    assert str(depot) == "1 scanned"
    pvenv = depot.find_python(".venv/bin/python")
    assert str(pvenv) == ".pyenv/versions/8.6.1 [cpython:8.6.1]"
    assert depot.scanned == [pvenv]

    p95 = depot.find_python("9.5.1")
    assert p95.problem is None
    assert str(depot) == "2 scanned"
    assert depot.scanned == [p95, pvenv]
    assert not logged
示例#3
0
def test_depot_adhoc(temp_folder, monkeypatch):
    depot = PythonDepot(use_path=False)
    p11 = depot.find_python("11.0.0")
    pfoo = depot.find_python("/foo")
    assert p11.problem == "not available"

    # Only paths are cached
    p11b = depot.find_python("11.0.0")
    assert p11 is not p11b
    assert p11 == p11b
    assert depot.find_python("/foo") == pfoo

    # Edge case: check we can still compare incomplete objects (missing spec.version here for 'pfoo')
    assert pfoo == pfoo
    assert not (pfoo < pfoo)
    assert not (pfoo > pfoo)
    assert pfoo < p11
    assert p11 > pfoo
    assert not (p11 < pfoo)
    assert not (pfoo > p11)

    # Edge case: comparison still works even when there is no spec, arbitrarily sort no spec lower...
    pfoo.spec = None
    assert pfoo < p11

    mk_python("python", folder="some-path", version="11.0.0")
    py_path = os.path.realpath("some-path/bin/python")
    p11 = depot.find_python(py_path)
    assert depot.find_python("./some-path/") == p11
    assert depot.find_python("some-path/bin") == p11
    assert str(p11) == "some-path/bin/python [cpython:11.0.0]"
示例#4
0
def test_invoker():
    depot = PythonDepot(use_path=False)
    assert depot.find_python(None) is depot.invoker
    assert depot.find_python("") is depot.invoker
    assert depot.find_python("py") is depot.invoker
    assert depot.find_python("python") is depot.invoker
    assert depot.find_python(depot.invoker.executable) is depot.invoker
    assert depot.find_python("invoker") is depot.invoker
    assert depot.find_python("%s" % sys.version_info[0]) is depot.invoker
    assert "invoker" in str(depot.invoker)
    assert str(depot.invoker) == repr(
        depot.invoker)  # Identical when coloring is off

    # Linux case with py3
    depot = mocked_invoker()
    assert depot.invoker.executable == "/usr/bin/python3.7"
    assert depot.invoker.folder == runez.to_path("/usr/bin")
    assert depot.invoker.major == 3

    # Linux case without py3
    depot = mocked_invoker(major=2, base_prefix="/usr/local")
    assert depot.invoker.executable == "/usr/local/bin/python2.7"
    assert depot.invoker.major == 2

    # Linux case with only /usr/bin/python
    depot = mocked_invoker(
        major=2,
        exe_exists=lambda x: "python2" not in x and "python3" not in x)
    assert depot.invoker.executable == "/usr/bin/python"
    assert depot.invoker.major == 2

    # Use sys.executable when prefix can't be used to determine invoker
    depot = mocked_invoker(major=2,
                           base_prefix=None,
                           executable="/foo",
                           exe_exists=False)
    assert depot.invoker.executable == "/foo"
    assert depot.invoker.major == 2

    # macos silly path choices
    depot = mocked_invoker(
        major=2,
        base_prefix="/System/Library/Frameworks/Python.framework/Versions/2.7")
    assert depot.invoker.executable == "/usr/bin/python2"
    assert depot.invoker.major == 2

    depot = mocked_invoker(
        base_prefix=
        "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7"
    )
    assert depot.invoker.executable == "/usr/bin/python3"
    assert depot.invoker.major == 3

    depot = mocked_invoker(
        base_prefix=
        "/usr/local/Cellar/[email protected]/3.7.1_1/Frameworks/Python.framework/Versions/3.7"
    )
    assert depot.invoker.executable == "/usr/local/bin/python3"
    assert depot.invoker.major == 3
示例#5
0
def test_unknown():
    depot = PythonDepot(use_path=False)
    p = depot.find_python("foo")
    assert str(p) == "foo [not available]"
    assert p.executable == "foo"
    assert p.folder is None
    assert p.major is None
    assert p.problem == "not available"
    assert p.spec.canonical == "foo:"
    assert p.spec.text == "foo"
    assert p.major is None
    assert p.version is None
示例#6
0
def cmd_diagnostics():
    """Show system diagnostics sample"""
    parser = runez.cli.parser()
    parser.add_argument("--border",
                        default="colon",
                        choices=NAMED_BORDERS,
                        help="Use custom border.")
    parser.add_argument("--pyenv",
                        default="~/.pyenv",
                        help="Pyenv folder to scan for python installations.")
    args = parser.parse_args()

    from runez.pyenv import PythonDepot, PythonInstallationScanner

    scanner = PythonInstallationScanner(args.pyenv) if args.pyenv else None
    depot = PythonDepot(scanner=scanner, use_path=True)
    available = depot.representation()
    print(
        PrettyTable.two_column_diagnostics(runez.SYS_INFO.diagnostics(),
                                           available,
                                           border=args.border))
示例#7
0
def test_empty_depot():
    depot = PythonDepot(use_path=False)
    assert str(depot) == "0 scanned"
    assert depot.from_path == []
    assert depot.scanned == []

    assert depot.find_python(depot.invoker) is depot.invoker
    assert depot.find_python(PythonSpec(
        depot.invoker.executable)) is depot.invoker
    assert depot.find_python(PythonSpec("invoker")) is depot.invoker
    assert depot.find_python("invoker") is depot.invoker
    assert depot.find_python(depot.invoker.spec.family) is depot.invoker
    assert depot.representation() == ""

    p = depot.find_python("foo")
    assert str(p) == "foo [not available]"
    assert repr(p) == "foo [not available]"
    assert p.problem == "not available"
    assert str(depot) == "0 scanned"
示例#8
0
def test_sorting(temp_folder):
    mk_python("3.6.1")
    mk_python("3.7.2")
    mk_python("3.8.3")
    mk_python("conda-4.6.1")
    mk_python("miniconda3-4.3.2")
    depot = PythonDepot(scanner=PythonInstallationScanner(".pyenv"),
                        use_path=False)
    assert str(depot) == "5 scanned"
    versions = [p.spec.canonical for p in depot.scanned]
    assert versions == [
        "conda:4.6.1", "conda:4.3.2", "cpython:3.8.3", "cpython:3.7.2",
        "cpython:3.6.1"
    ]
示例#9
0
 def _diagnostics(self):
     yield "base", self.base_folder
     yield "invoker python", PythonDepot(use_path=False).invoker.representation()
示例#10
0
 def available_pythons(self):
     pyenv = self.pyenv()
     scanner = PythonInstallationScanner(pyenv) if pyenv else None
     return PythonDepot(scanner=scanner)
示例#11
0
def test_depot(temp_folder, monkeypatch, logged):
    # Create some pyenv-style python installation mocks (using version 8 so it sorts above any real version...)
    mk_python("8.6.1")
    mk_python("8.7.2")
    runez.symlink("8.6.1",
                  ".pyenv/versions/8.6",
                  must_exist=False,
                  logger=None)

    # Verify that if invoker is one of the pyenv-installations, it is properly detected
    depot = mocked_invoker(pyenv=".pyenv",
                           base_prefix=".pyenv/versions/8.6.1",
                           version_info=(8, 6, 1))
    p8 = depot.find_python("8")
    p86 = depot.find_python("8.6")
    assert str(
        p8
    ) == ".pyenv/versions/8.7.2 [cpython:8.7.2]"  # Latest version 8 (invoker doesn't take precedence)
    assert p8.folder == runez.to_path(".pyenv/versions/8.7.2/bin").absolute()
    assert p86 is depot.invoker
    assert str(depot) == "2 scanned"
    assert depot.scanned == [p8, p86]
    assert depot.from_path == []
    assert not logged

    mk_python("8.8.3", executable=False)
    mk_python("8.9.0")
    mk_python("miniconda3-4.7.12")

    # Create some PATH-style python installation mocks (using version 9 so it sorts higher than pyenv ones)
    mk_python("python", folder="path1", version="9.5.1")
    mk_python("python3", folder="path1", content=[
        "foo"
    ])  # Invalid: mocked _pv.py does not return the right number of lines
    mk_python("python", folder="path2",
              content=["foo; bar"])  # --version fails
    mk_python("some-other-python-exe-name", folder="path3", version="8.5.0")
    mk_python("python3", folder="path3")  # Invalid version
    with runez.CurrentFolder("path3/bin"):
        runez.symlink("some-other-python-exe-name", "python", logger=None)

    monkeypatch.setenv("PATH", "bar:path1/bin:path2/bin:path3/bin")
    scanner = PythonInstallationScanner(".pyenv")
    assert str(scanner) == "portable python [.pyenv]"
    depot = PythonDepot(scanner=scanner, use_path=True)
    assert str(depot) == "4 scanned, 2 from PATH"
    r = depot.representation()
    assert "Installed portable python:" in r
    assert "Available pythons from PATH:" in r

    depot.find_preferred_python("8.7.2,8.9.0", "8.7", "8.10")
    assert depot.preferred_python.version == "8.7.2"

    depot.find_preferred_python("8.7.2,8.9.0", "8.7", "8.8")
    assert depot.preferred_python.version == "8.9.0"
    assert depot.find_python(None) is depot.preferred_python
    assert depot.find_python("8") is depot.preferred_python

    depot.find_preferred_python("8.7.2,8.9.0", "10.7", "10.8")
    assert depot.preferred_python is None

    depot.find_preferred_python("")
    assert depot.preferred_python is None

    assert len(depot.from_path) == 2
    assert len(depot.scanned) == 4
    assert depot.scan_path_env_var(
    ) is None  # Already scanned to try and find invoker
    p95 = depot.find_python("9.5.1")
    assert str(p95) == "path1/bin/python [cpython:9.5.1]"

    check_find_python(depot, "9", "path1/bin/python [cpython:9.5.1]")
    check_find_python(depot, "42.4", "42.4 [not available]")
    check_find_python(depot, "foo", "foo [not available]")
    check_find_python(depot, "python:43.0.0", "python:43.0.0 [not available]")

    with pytest.raises(runez.system.AbortException):
        depot.find_python("/bar", fatal=True)

    pbar = depot.find_python("/bar")
    assert str(pbar) == "/bar [not an executable]"
    assert pbar.problem
    assert not pbar.satisfies(depot.spec_from_text("python"))

    p8 = depot.find_python("8")
    p8a = depot.find_python(PythonSpec("8"))
    assert p8a is p8
    p86 = depot.find_python("8.6")
    p87 = depot.find_python("8.7")
    p88 = depot.find_python("8.8")
    p89 = depot.find_python("8.9")
    c = depot.find_python("conda")
    c47 = depot.find_python("conda:4.7")
    assert c47 is c
    assert depot.find_python(PythonSpec("conda47")) is c47
    assert depot.scanned == [p89, p87, p86, c47]

    assert p8.major == 8
    assert p88.major == 8
    assert str(p8) == ".pyenv/versions/8.9.0 [cpython:8.9.0]"
    assert str(p88) == "8.8 [not available]"
    assert str(c47) == ".pyenv/versions/miniconda3-4.7.12 [conda:4.7.12]"
    assert p8 is p89
    assert p8 == p89
    assert p8 != p88
    assert p8 != pbar
    assert p8.satisfies(PythonSpec("python"))
    assert p8.satisfies(PythonSpec("python8"))
    assert p8.satisfies(PythonSpec("py8.9.0"))
    assert not p8.satisfies(PythonSpec("py8.9.1"))
    assert c47.satisfies(PythonSpec("conda47"))
    assert len({p8, p89}) == 1
    assert len({p8, p89, p88}) == 2