File: test_py_spec.py

package info (click to toggle)
python-virtualenv 20.33.1%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 1,124 kB
  • sloc: python: 10,907; sh: 160; ansic: 61; csh: 47; makefile: 8
file content (134 lines) | stat: -rw-r--r-- 4,358 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
from __future__ import annotations

import sys
from copy import copy

import pytest

from virtualenv.discovery.py_spec import PythonSpec


def test_bad_py_spec():
    text = "python2.3.4.5"
    spec = PythonSpec.from_string_spec(text)
    assert text in repr(spec)
    assert spec.str_spec == text
    assert spec.path == text
    content = vars(spec)
    del content["str_spec"]
    del content["path"]
    assert all(v is None for v in content.values())


def test_py_spec_first_digit_only_major():
    spec = PythonSpec.from_string_spec("278")
    assert spec.major == 2
    assert spec.minor == 78


def test_spec_satisfies_path_ok():
    spec = PythonSpec.from_string_spec(sys.executable)
    assert spec.satisfies(spec) is True


def test_spec_satisfies_path_nok(tmp_path):
    spec = PythonSpec.from_string_spec(sys.executable)
    of = PythonSpec.from_string_spec(str(tmp_path))
    assert spec.satisfies(of) is False


def test_spec_satisfies_arch():
    spec_1 = PythonSpec.from_string_spec("python-32")
    spec_2 = PythonSpec.from_string_spec("python-64")

    assert spec_1.satisfies(spec_1) is True
    assert spec_2.satisfies(spec_1) is False


def test_spec_satisfies_free_threaded():
    spec_1 = PythonSpec.from_string_spec("python3.13t")
    spec_2 = PythonSpec.from_string_spec("python3.13")

    assert spec_1.satisfies(spec_1) is True
    assert spec_1.free_threaded is True
    assert spec_2.satisfies(spec_1) is False
    assert spec_2.free_threaded is False


@pytest.mark.parametrize(
    ("req", "spec"),
    [("py", "python"), ("jython", "jython"), ("CPython", "cpython")],
)
def test_spec_satisfies_implementation_ok(req, spec):
    spec_1 = PythonSpec.from_string_spec(req)
    spec_2 = PythonSpec.from_string_spec(spec)
    assert spec_1.satisfies(spec_1) is True
    assert spec_2.satisfies(spec_1) is True


def test_spec_satisfies_implementation_nok():
    spec_1 = PythonSpec.from_string_spec("cpython")
    spec_2 = PythonSpec.from_string_spec("jython")
    assert spec_2.satisfies(spec_1) is False
    assert spec_1.satisfies(spec_2) is False


def _version_satisfies_pairs():
    target = set()
    version = tuple(str(i) for i in sys.version_info[0:3])
    for threading in (False, True):
        for i in range(len(version) + 1):
            req = ".".join(version[0:i])
            for j in range(i + 1):
                sat = ".".join(version[0:j])
                # can be satisfied in both directions
                if sat:
                    target.add((req, sat))
                # else: no version => no free-threading info
                target.add((sat, req))
                if not threading or not sat or not req:
                    # free-threading info requires a version
                    continue
                target.add((f"{req}t", f"{sat}t"))
                target.add((f"{sat}t", f"{req}t"))

    return sorted(target)


@pytest.mark.parametrize(("req", "spec"), _version_satisfies_pairs())
def test_version_satisfies_ok(req, spec):
    req_spec = PythonSpec.from_string_spec(f"python{req}")
    sat_spec = PythonSpec.from_string_spec(f"python{spec}")
    assert sat_spec.satisfies(req_spec) is True


def _version_not_satisfies_pairs():
    target = set()
    version = tuple(str(i) for i in sys.version_info[0:3])
    for major in range(len(version)):
        req = ".".join(version[0 : major + 1])
        for minor in range(major + 1):
            sat_ver = list(sys.version_info[0 : minor + 1])
            for patch in range(minor + 1):
                for o in [1, -1]:
                    temp = copy(sat_ver)
                    temp[patch] += o
                    if temp[patch] < 0:
                        continue
                    sat = ".".join(str(i) for i in temp)
                    target.add((req, sat))
    return sorted(target)


@pytest.mark.parametrize(("req", "spec"), _version_not_satisfies_pairs())
def test_version_satisfies_nok(req, spec):
    req_spec = PythonSpec.from_string_spec(f"python{req}")
    sat_spec = PythonSpec.from_string_spec(f"python{spec}")
    assert sat_spec.satisfies(req_spec) is False


def test_relative_spec(tmp_path, monkeypatch):
    monkeypatch.chdir(tmp_path)
    a_relative_path = str((tmp_path / "a" / "b").relative_to(tmp_path))
    spec = PythonSpec.from_string_spec(a_relative_path)
    assert spec.path == a_relative_path