findpython
A Python library and CLI that discovers every Python interpreter installed on your system, across PATH, pyenv, asdf, Rye, uv, macOS, and Windows.
Repository Health
Technical Analysis
FindPython is a Python library and command-line tool that locates every Python interpreter installed on a machine, searching PATH, pyenv, asdf, Rye, uv, the macOS Framework directory, and the Windows registry. It normalizes results into a single PythonVersion object exposing version, architecture, implementation, and free-threaded status, so tools that need to select a specific Python runtime don’t have to reimplement discovery logic per platform and version manager.
Originally a rewrite of the pythonfinder project, it powers interpreter resolution in tools like PDM and other Python project managers that must pick the right runtime from several candidates already installed on a developer’s machine.
What You Get
- A
find()/find_all()API returningPythonVersionobjects matching major/minor/patch, prerelease, architecture, implementation, or free-threaded criteria - A
findpythonCLI for discovering interpreters from the shell without writing any Python - Pluggable providers for PATH, pyenv, asdf, Rye, uv, macOS Framework, and Windows registry, plus a
register_providerhook for custom sources - Deduplication by executable path, resolved symlink target, or binary content hash to avoid listing the same interpreter twice
Common Use Cases
- Project/version managers (e.g. PDM) resolving which installed Python satisfies a
requires-pythonconstraint - CLI tools that let a user pick from multiple installed Python versions
- CI or bootstrap scripts that must locate a specific Python version across pyenv/asdf/uv installs before running
- Developer tooling that needs to detect free-threaded (no-GIL) builds or specific architectures
Under The Hood
Architecture
A Finder orchestrates a list of BaseProvider instances (PathProvider, AsdfProvider, PyenvProvider, RyeProvider, UvProvider, WinregProvider, MacOSProvider), each implementing find_pythons() to yield candidate PythonVersion objects from provider-specific locations. Finder.find_all() collects all candidates into a set, builds a version matcher from PythonVersion.matches(), then deduplicates via a selectable key (interpreter path, binary hash, or executable path), sorts by symlink-ness and path length, and returns results ordered by version descending. PythonVersion is a dataclass whose version, architecture, implementation, and free-threaded properties are computed lazily by shelling out to the interpreter itself, cached per-process via lru_cache. Changing the dataclass’s fields would ripple into every provider’s version_maker call, making it the central seam of the design alongside BaseProvider.find_pythons.
Tech Stack
Pure Python (3.9+) with packaging for version parsing and platformdirs for locating version-manager config directories cross-platform. Interpreter introspection happens entirely through subprocess calls to each discovered Python rather than import machinery. The build backend is pdm-backend with SCM-based dynamic versioning, and CI runs a full matrix across Python 3.9–3.14 on Ubuntu, Windows, and macOS via GitHub Actions using pdm-project/setup-pdm.
Code Quality
A tests/ directory covers the finder, CLI argument parsing, POSIX-specific behavior, and utility functions, using pytest with a mocked_python fixture to simulate installed interpreters without touching the real filesystem. CI runs the full suite plus a live findpython --all -v smoke test on every supported OS/Python combination. Pre-commit hooks enforce ruff (with bugbear, comprehensions, and complexity checks capped at 10), black, and mypy; the codebase uses from __future__ import annotations with comprehensive type hints throughout. Error handling around subprocess calls is explicit, narrowly catching OSError, CalledProcessError, TimeoutExpired, and InvalidVersion rather than swallowing broadly.
API Design
The public API is minimal and ergonomic — top-level find()/find_all() functions proxy straight to Finder(), so the common case needs zero setup, while power users needing symlink resolution, custom providers, or provider selection instantiate Finder directly. Version criteria accept either explicit major/minor/patch keyword arguments or a single loosely-formatted string, reducing boilerplate for CLI-driven use. Documentation is thorough for a small utility, with concrete example output for every calling convention. The main rough edge is that PythonVersion properties require actually spawning the interpreter, so consumers must understand this isn’t a free, cross-process cached operation.