python-discovery 1.1.3__tar.gz → 1.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_discovery-1.1.3 → python_discovery-1.2.0}/.pre-commit-config.yaml +2 -2
- {python_discovery-1.1.3 → python_discovery-1.2.0}/PKG-INFO +1 -1
- {python_discovery-1.1.3 → python_discovery-1.2.0}/docs/explanation.rst +20 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/docs/how-to/standalone-usage.rst +20 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/docs/index.rst +1 -0
- python_discovery-1.2.0/docs/reference/environment-variables.rst +38 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/docs/tutorial/getting-started.rst +27 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/src/python_discovery/_cached_py_info.py +2 -1
- {python_discovery-1.1.3 → python_discovery-1.2.0}/tests/test_cached_py_info.py +11 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/.github/FUNDING.yaml +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/.github/SECURITY.md +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/.github/dependabot.yaml +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/.github/release.yaml +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/.github/workflows/check.yaml +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/.github/workflows/release.yaml +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/.gitignore +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/.readthedocs.yaml +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/.readthedocs.yml +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/CODE_OF_CONDUCT.md +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/LICENSE +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/README.md +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/docs/_static/custom.css +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/docs/_static/logo.svg +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/docs/conf.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/docs/reference/api.rst +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/pyproject.toml +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/src/python_discovery/__init__.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/src/python_discovery/_cache.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/src/python_discovery/_compat.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/src/python_discovery/_discovery.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/src/python_discovery/_py_info.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/src/python_discovery/_py_spec.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/src/python_discovery/_specifier.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/src/python_discovery/_windows/__init__.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/src/python_discovery/_windows/_pep514.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/src/python_discovery/_windows/_propose.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/src/python_discovery/py.typed +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/tests/conftest.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/tests/py_info/test_py_info.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/tests/py_info/test_py_info_exe_based_of.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/tests/test_cache.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/tests/test_discovery.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/tests/test_discovery_extra.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/tests/test_py_info_extra.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/tests/test_py_spec.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/tests/test_py_spec_extra.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/tests/test_specifier.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/tests/windows/conftest.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/tests/windows/test_windows.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/tests/windows/test_windows_pep514.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/tests/windows/winreg_mock_values.py +0 -0
- {python_discovery-1.1.3 → python_discovery-1.2.0}/tox.toml +0 -0
|
@@ -15,11 +15,11 @@ repos:
|
|
|
15
15
|
- id: codespell
|
|
16
16
|
additional_dependencies: ["tomli>=2.4"]
|
|
17
17
|
- repo: https://github.com/tox-dev/pyproject-fmt
|
|
18
|
-
rev: "v2.
|
|
18
|
+
rev: "v2.18.1"
|
|
19
19
|
hooks:
|
|
20
20
|
- id: pyproject-fmt
|
|
21
21
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
22
|
-
rev: "v0.15.
|
|
22
|
+
rev: "v0.15.6"
|
|
23
23
|
hooks:
|
|
24
24
|
- id: ruff-format
|
|
25
25
|
- id: ruff
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-discovery
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Python interpreter discovery
|
|
5
5
|
Project-URL: Changelog, https://github.com/tox-dev/python-discovery/releases
|
|
6
6
|
Project-URL: Documentation, https://python-discovery.readthedocs.io
|
|
@@ -90,6 +90,26 @@ with `filelock <https://py-filelock.readthedocs.io/>`_-based locking for safe co
|
|
|
90
90
|
can also pass ``cache=None`` to disable caching, or implement your own backend (see
|
|
91
91
|
:doc:`/how-to/standalone-usage`).
|
|
92
92
|
|
|
93
|
+
Subprocess timeout behavior
|
|
94
|
+
----------------------------
|
|
95
|
+
|
|
96
|
+
When python-discovery verifies an interpreter candidate, it runs a subprocess to query its metadata.
|
|
97
|
+
On slow systems (especially Windows), Python startup can take significant time. The default timeout
|
|
98
|
+
is **15 seconds** to balance responsiveness with accommodation for real-world conditions.
|
|
99
|
+
|
|
100
|
+
If your system consistently hits timeouts, you can customize the timeout via the
|
|
101
|
+
``PY_DISCOVERY_TIMEOUT`` environment variable (in seconds):
|
|
102
|
+
|
|
103
|
+
.. code-block:: console
|
|
104
|
+
|
|
105
|
+
# Increase timeout to 30 seconds
|
|
106
|
+
export PY_DISCOVERY_TIMEOUT=30
|
|
107
|
+
python -c "from python_discovery import get_interpreter; get_interpreter('python3.12')"
|
|
108
|
+
|
|
109
|
+
The timeout applies to each individual interpreter being queried. If you set a value that is too low,
|
|
110
|
+
legitimate interpreters may be skipped; if too high, the discovery process may take longer to fail
|
|
111
|
+
when encountering problematic interpreters.
|
|
112
|
+
|
|
93
113
|
Spec format reference
|
|
94
114
|
-----------------------
|
|
95
115
|
|
|
@@ -42,6 +42,26 @@ shell. You can override these to control exactly where the library looks.
|
|
|
42
42
|
env = {**os.environ, "PATH": "/usr/local/bin:/usr/bin"}
|
|
43
43
|
result = get_interpreter("python3.12", env=env)
|
|
44
44
|
|
|
45
|
+
Customize interpreter query timeout
|
|
46
|
+
-------------------------------------
|
|
47
|
+
|
|
48
|
+
On slower systems (especially Windows), Python startup can take more than the default 15 seconds.
|
|
49
|
+
If your discovery process times out when looking for interpreters, you can extend the timeout via
|
|
50
|
+
the ``PY_DISCOVERY_TIMEOUT`` environment variable.
|
|
51
|
+
|
|
52
|
+
.. code-block:: python
|
|
53
|
+
|
|
54
|
+
import os
|
|
55
|
+
|
|
56
|
+
from python_discovery import get_interpreter
|
|
57
|
+
|
|
58
|
+
# Increase timeout to 30 seconds for slow environments
|
|
59
|
+
env = {**os.environ, "PY_DISCOVERY_TIMEOUT": "30"}
|
|
60
|
+
result = get_interpreter("python3.12", env=env)
|
|
61
|
+
|
|
62
|
+
The timeout value should be a number in seconds. Each interpreter candidate is given this much time
|
|
63
|
+
to respond. If a timeout occurs, the candidate is skipped and the search continues with the next one.
|
|
64
|
+
|
|
45
65
|
Read interpreter metadata
|
|
46
66
|
---------------------------
|
|
47
67
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Environment Variables
|
|
2
|
+
======================
|
|
3
|
+
|
|
4
|
+
``PY_DISCOVERY_TIMEOUT``
|
|
5
|
+
------------------------
|
|
6
|
+
|
|
7
|
+
Controls the timeout for querying individual Python interpreters.
|
|
8
|
+
|
|
9
|
+
**Type:** Float (seconds)
|
|
10
|
+
|
|
11
|
+
**Default:** ``15``
|
|
12
|
+
|
|
13
|
+
**Description:**
|
|
14
|
+
|
|
15
|
+
When python-discovery verifies an interpreter candidate, it runs a subprocess to collect metadata
|
|
16
|
+
(version, architecture, platform, etc.). On slower systems—particularly Windows with antivirus
|
|
17
|
+
software or other tools—Python startup can exceed the default timeout.
|
|
18
|
+
|
|
19
|
+
Setting this variable extends the allowed time for each interpreter query.
|
|
20
|
+
|
|
21
|
+
**Examples:**
|
|
22
|
+
|
|
23
|
+
.. code-block:: bash
|
|
24
|
+
|
|
25
|
+
# Allow interpreters 30 seconds to respond
|
|
26
|
+
export PY_DISCOVERY_TIMEOUT=30
|
|
27
|
+
|
|
28
|
+
# Or pass in Python
|
|
29
|
+
import os
|
|
30
|
+
os.environ["PY_DISCOVERY_TIMEOUT"] = "30"
|
|
31
|
+
|
|
32
|
+
**Notes:**
|
|
33
|
+
|
|
34
|
+
- The timeout applies per candidate, not to the entire discovery process
|
|
35
|
+
- If a candidate times out, it is skipped and discovery continues with the next one
|
|
36
|
+
- Setting the value too low may skip legitimate interpreters
|
|
37
|
+
- Setting it too high increases discovery time when encountering problematic interpreters
|
|
38
|
+
- The value is read from the environment dict passed to :func:`~python_discovery.get_interpreter`
|
|
@@ -178,3 +178,30 @@ Every call will run a subprocess to query the interpreter, so this is slower for
|
|
|
178
178
|
from python_discovery import get_interpreter
|
|
179
179
|
|
|
180
180
|
result = get_interpreter("python3.12")
|
|
181
|
+
|
|
182
|
+
Handling slow interpreter queries
|
|
183
|
+
----------------------------------
|
|
184
|
+
|
|
185
|
+
On some systems (especially Windows with antivirus or other tools), Python startup is slow. If discovery
|
|
186
|
+
times out, increase the timeout using the ``PY_DISCOVERY_TIMEOUT`` environment variable.
|
|
187
|
+
|
|
188
|
+
.. code-block:: python
|
|
189
|
+
|
|
190
|
+
import os
|
|
191
|
+
|
|
192
|
+
from python_discovery import get_interpreter
|
|
193
|
+
|
|
194
|
+
# Allow up to 30 seconds per interpreter
|
|
195
|
+
os.environ["PY_DISCOVERY_TIMEOUT"] = "30"
|
|
196
|
+
result = get_interpreter("python3.12", cache=cache)
|
|
197
|
+
|
|
198
|
+
Or, pass it directly in a custom environment dict:
|
|
199
|
+
|
|
200
|
+
.. code-block:: python
|
|
201
|
+
|
|
202
|
+
import os
|
|
203
|
+
|
|
204
|
+
from python_discovery import get_interpreter
|
|
205
|
+
|
|
206
|
+
env = {**os.environ, "PY_DISCOVERY_TIMEOUT": "30"}
|
|
207
|
+
result = get_interpreter("python3.12", env=env, cache=cache)
|
|
@@ -189,6 +189,7 @@ def _run_subprocess(
|
|
|
189
189
|
) -> tuple[Exception | None, PythonInfo | None]:
|
|
190
190
|
start_cookie = gen_cookie()
|
|
191
191
|
end_cookie = gen_cookie()
|
|
192
|
+
timeout = float(env.get("PY_DISCOVERY_TIMEOUT", "15"))
|
|
192
193
|
with _resolve_py_info_script() as py_info_script:
|
|
193
194
|
cmd = [exe, str(py_info_script), start_cookie, end_cookie]
|
|
194
195
|
env = dict(env)
|
|
@@ -206,7 +207,7 @@ def _run_subprocess(
|
|
|
206
207
|
encoding="utf-8",
|
|
207
208
|
errors="backslashreplace",
|
|
208
209
|
)
|
|
209
|
-
out, err = process.communicate(timeout=
|
|
210
|
+
out, err = process.communicate(timeout=timeout)
|
|
210
211
|
code = process.returncode
|
|
211
212
|
except TimeoutExpired:
|
|
212
213
|
process.kill()
|
|
@@ -123,6 +123,17 @@ def test_run_subprocess_timeout(mocker: MockerFixture) -> None:
|
|
|
123
123
|
assert mock_process.communicate.call_count == 2
|
|
124
124
|
|
|
125
125
|
|
|
126
|
+
def test_run_subprocess_custom_timeout(mocker: MockerFixture) -> None:
|
|
127
|
+
mock_process = MagicMock()
|
|
128
|
+
mock_process.communicate.return_value = (json.dumps(PythonInfo().to_dict()), "")
|
|
129
|
+
mock_process.returncode = 0
|
|
130
|
+
mocker.patch("python_discovery._cached_py_info.Popen", return_value=mock_process)
|
|
131
|
+
env = dict(os.environ)
|
|
132
|
+
env["PY_DISCOVERY_TIMEOUT"] = "30"
|
|
133
|
+
_run_subprocess(PythonInfo, sys.executable, env)
|
|
134
|
+
mock_process.communicate.assert_called_once_with(timeout=30.0)
|
|
135
|
+
|
|
136
|
+
|
|
126
137
|
def test_run_subprocess_nonzero_exit(mocker: MockerFixture) -> None:
|
|
127
138
|
mock_process = MagicMock()
|
|
128
139
|
mock_process.communicate.return_value = ("some output", "some error")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_discovery-1.1.3 → python_discovery-1.2.0}/tests/py_info/test_py_info_exe_based_of.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|