File: standalone-usage.rst

package info (click to toggle)
python-discovery 1.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 492 kB
  • sloc: python: 4,511; makefile: 9; sh: 8
file content (163 lines) | stat: -rw-r--r-- 5,004 bytes parent folder | download
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
How-to guides
=============

Search specific directories first
-----------------------------------

If you know a likely location for the interpreter, pass it via ``try_first_with`` to check there
before the normal search. This is useful when you have a custom Python install outside the
standard locations.

.. code-block:: python

   from python_discovery import get_interpreter

   info = get_interpreter("python3.12", try_first_with=["/opt/python/bin"])
   if info is not None:
       print(info.executable)

Restrict the search environment
---------------------------------

By default, python-discovery reads environment variables like ``PATH`` and ``PYENV_ROOT`` from your
shell. You can override these to control exactly where the library looks.

.. mermaid::

    flowchart TD
        Env["Custom env dict"] --> Call["get_interpreter(spec, env=env)"]
        Call --> PATH["PATH"]
        Call --> Pyenv["PYENV_ROOT"]
        Call --> UV["UV_PYTHON_INSTALL_DIR"]
        Call --> Mise["MISE_DATA_DIR"]

        style Env fill:#4a90d9,stroke:#2a5f8f,color:#fff

.. code-block:: python

   import os

   from python_discovery import get_interpreter

   env = {**os.environ, "PATH": "/usr/local/bin:/usr/bin"}
   result = get_interpreter("python3.12", env=env)

Customize interpreter query timeout
-------------------------------------

On slower systems (especially Windows), Python startup can take more than the default 15 seconds.
If your discovery process times out when looking for interpreters, you can extend the timeout via
the ``PY_DISCOVERY_TIMEOUT`` environment variable.

.. code-block:: python

   import os

   from python_discovery import get_interpreter

   # Increase timeout to 30 seconds for slow environments
   env = {**os.environ, "PY_DISCOVERY_TIMEOUT": "30"}
   result = get_interpreter("python3.12", env=env)

The timeout value should be a number in seconds. Each interpreter candidate is given this much time
to respond. If a timeout occurs, the candidate is skipped and the search continues with the next one.

Read interpreter metadata
---------------------------

Once you have a :class:`~python_discovery.PythonInfo`, you can inspect everything about the interpreter.

.. mermaid::

    classDiagram
        class PythonInfo {
            +executable: str
            +system_executable: str
            +implementation: str
            +version_info: VersionInfo
            +architecture: int
            +platform: str
            +sysconfig_vars: dict
            +sysconfig_paths: dict
            +machine: str
            +free_threaded: bool
        }

.. code-block:: python

   from pathlib import Path

   from python_discovery import DiskCache, get_interpreter

   cache = DiskCache(root=Path("~/.cache/python-discovery").expanduser())
   info = get_interpreter("python3.12", cache=cache)

   info.executable           # Resolved path to the binary.
   info.system_executable    # The underlying system interpreter (outside any venv).
   info.implementation       # "CPython", "PyPy", "GraalPy", etc.
   info.version_info         # VersionInfo(major, minor, micro, releaselevel, serial).
   info.architecture         # 64 or 32.
   info.platform             # sys.platform value ("linux", "darwin", "win32").
   info.machine              # ISA: "arm64", "x86_64", etc.
   info.free_threaded        # True if this is a no-GIL build.
   info.sysconfig_vars       # All sysconfig.get_config_vars() values.
   info.sysconfig_paths      # All sysconfig.get_paths() values.

Implement a custom cache backend
-----------------------------------

The built-in :class:`~python_discovery.DiskCache` stores results as JSON files with
`filelock <https://py-filelock.readthedocs.io/>`_-based locking. If you need a different storage
strategy (e.g., in-memory, database-backed), implement the :class:`~python_discovery.PyInfoCache`
protocol.

.. mermaid::

    classDiagram
        class PyInfoCache {
            <<Protocol>>
            +py_info(path) ContentStore
            +py_info_clear() None
        }
        class ContentStore {
            <<Protocol>>
            +exists() bool
            +read() dict | None
            +write(content) None
            +remove() None
            +locked() context
        }
        class DiskCache {
            +root: Path
        }
        PyInfoCache <|.. DiskCache
        PyInfoCache --> ContentStore

.. code-block:: python

   from pathlib import Path

   from python_discovery import ContentStore, PyInfoCache


   class MyContentStore:
       def __init__(self, path: Path) -> None:
           self._path = path

       def exists(self) -> bool: ...

       def read(self) -> dict | None: ...

       def write(self, content: dict) -> None: ...

       def remove(self) -> None: ...

       def locked(self): ...


   class MyCache:
       def py_info(self, path: Path) -> MyContentStore: ...

       def py_info_clear(self) -> None: ...

Any object that matches the protocol works -- no inheritance required.