Modern, extensible Python project management tool with comprehensive environment and build system support
—
Automated Python distribution management including installation, version management, and distribution discovery. Supports multiple Python versions and provides integration with UV and other Python managers.
Central manager for Python installations providing discovery, installation, and management of multiple Python versions.
class PythonManager:
"""
Manager for Python installations and distributions.
Handles downloading, installing, and managing multiple Python versions
with support for different sources and installation methods.
"""
def __init__(self, directory: Path):
"""
Initialize Python manager.
Args:
directory (Path): Directory for Python installations
"""
@property
def directory(self) -> Path:
"""
Directory containing Python installations.
Returns:
Path to Python installations directory
"""
def get_installed(self) -> dict[str, 'InstalledDistribution']:
"""
Get all installed Python distributions.
Returns:
Dict mapping version strings to InstalledDistribution instances
"""
def install(self, identifier: str) -> 'InstalledDistribution':
"""
Install Python distribution by version identifier.
Args:
identifier (str): Version identifier (e.g., '3.11', '3.10.5', 'pypy3.9')
Returns:
InstalledDistribution instance for the installed Python
Raises:
PythonDistributionUnknownError: If identifier is not recognized
PythonDistributionResolutionError: If installation fails
"""
def remove(self, identifier: str) -> None:
"""
Remove installed Python distribution.
Args:
identifier (str): Version identifier to remove
"""
def find_distribution(self, identifier: str) -> 'InstalledDistribution | None':
"""
Find installed distribution by identifier.
Args:
identifier (str): Version identifier to find
Returns:
InstalledDistribution if found, None otherwise
"""
def get_available(self) -> list[str]:
"""
Get list of available Python versions for installation.
Returns:
List of available version identifiers
"""
def update_all(self) -> list[str]:
"""
Update all installed Python distributions.
Returns:
List of updated version identifiers
"""Represents an installed Python distribution with version information and utility methods.
class InstalledDistribution:
"""
Represents an installed Python distribution.
Provides access to Python executable, version information,
and utilities for managing the installation.
"""
def __init__(self, path: Path, python_path: Path):
"""
Initialize installed distribution.
Args:
path (Path): Installation directory path
python_path (Path): Path to Python executable
"""
@property
def path(self) -> Path:
"""
Installation directory path.
Returns:
Path to installation directory
"""
@property
def name(self) -> str:
"""
Distribution name (e.g., 'cpython', 'pypy').
Returns:
Distribution name string
"""
@property
def python_path(self) -> Path:
"""
Path to Python executable.
Returns:
Path to python executable
"""
@property
def version(self) -> str:
"""
Python version string.
Returns:
Version string (e.g., '3.11.2')
"""
@property
def python_version(self) -> tuple[int, int, int]:
"""
Python version as tuple.
Returns:
Tuple of (major, minor, patch) version numbers
"""
@property
def implementation(self) -> str:
"""
Python implementation name.
Returns:
Implementation name ('cpython', 'pypy', etc.)
"""
@property
def architecture(self) -> str:
"""
Architecture string.
Returns:
Architecture ('x86_64', 'arm64', etc.)
"""
@property
def platform(self) -> str:
"""
Platform string.
Returns:
Platform ('linux', 'darwin', 'win32', etc.)
"""
def needs_update(self) -> bool:
"""
Check if distribution needs updating.
Returns:
True if a newer version is available
"""
def update(self) -> 'InstalledDistribution':
"""
Update this distribution to latest version.
Returns:
New InstalledDistribution instance after update
"""
def get_system_info(self) -> dict[str, str]:
"""
Get detailed system information for this Python.
Returns:
Dict with system information (version, platform, paths, etc.)
"""
def run_command(self, command: list[str], **kwargs):
"""
Run command with this Python executable.
Args:
command (list[str]): Command to run (python will be prepended)
**kwargs: Additional arguments for subprocess
Returns:
Command execution result
"""Utilities for discovering and validating Python installations on the system including system Python and managed installations.
def discover_system_pythons() -> list[str]:
"""
Discover Python installations on system PATH.
Returns:
List of Python executable paths found on system
"""
def validate_python_installation(python_path: Path) -> bool:
"""
Validate that path points to working Python installation.
Args:
python_path (Path): Path to Python executable
Returns:
True if Python installation is valid and working
"""
def get_python_version(python_path: Path) -> str:
"""
Get version string from Python executable.
Args:
python_path (Path): Path to Python executable
Returns:
Version string (e.g., '3.11.2')
Raises:
PythonDistributionResolutionError: If version cannot be determined
"""
def find_python_by_version(version: str) -> Path | None:
"""
Find Python executable by version on system.
Args:
version (str): Version to search for (e.g., '3.11', '3.10.5')
Returns:
Path to Python executable or None if not found
"""
def get_python_info(python_path: Path) -> dict[str, str]:
"""
Get detailed information about Python installation.
Args:
python_path (Path): Path to Python executable
Returns:
Dict with Python information (version, implementation, platform, etc.)
"""Python version parsing, comparison, and resolution utilities for handling different version formats and specifiers.
def parse_version_spec(spec: str) -> dict[str, str]:
"""
Parse version specification into components.
Args:
spec (str): Version specification (e.g., '3.11.2', 'pypy3.9', 'python3.10')
Returns:
Dict with parsed components (implementation, major, minor, patch)
"""
def resolve_version_identifier(identifier: str) -> str:
"""
Resolve version identifier to specific version.
Args:
identifier (str): Version identifier to resolve
Returns:
Resolved specific version string
Raises:
PythonDistributionUnknownError: If identifier cannot be resolved
"""
def compare_versions(version1: str, version2: str) -> int:
"""
Compare two Python version strings.
Args:
version1 (str): First version string
version2 (str): Second version string
Returns:
-1 if version1 < version2, 0 if equal, 1 if version1 > version2
"""
def get_latest_version(versions: list[str]) -> str:
"""
Get latest version from list of version strings.
Args:
versions (list[str]): List of version strings
Returns:
Latest version string
"""
def version_matches_spec(version: str, spec: str) -> bool:
"""
Check if version matches specification.
Args:
version (str): Version to check
spec (str): Version specification (supports ranges, ~=, etc.)
Returns:
True if version matches specification
"""Support for different Python installation sources including official releases, PyPy, and custom sources.
class InstallationSource:
"""Base class for Python installation sources."""
def get_available_versions(self) -> list[str]:
"""Get list of available versions from this source."""
def download_distribution(self, version: str, target_dir: Path) -> Path:
"""Download distribution for version to target directory."""
def install_distribution(self, version: str, target_dir: Path) -> InstalledDistribution:
"""Install distribution for version to target directory."""
class OfficialSource(InstallationSource):
"""Official Python.org releases."""
BASE_URL = "https://www.python.org/ftp/python/"
def get_available_versions(self) -> list[str]:
"""Get available CPython versions from python.org."""
class PyPySource(InstallationSource):
"""PyPy releases."""
BASE_URL = "https://downloads.python.org/pypy/"
def get_available_versions(self) -> list[str]:
"""Get available PyPy versions."""
class UVSource(InstallationSource):
"""UV Python installations."""
def get_available_versions(self) -> list[str]:
"""Get Python versions available through UV."""
def install_distribution(self, version: str, target_dir: Path) -> InstalledDistribution:
"""Install Python using UV."""Integration between Python installations and hatch environments for seamless Python version management.
def get_environment_python(app, env_name: str) -> InstalledDistribution | None:
"""
Get Python distribution for environment.
Args:
app: Application instance
env_name (str): Environment name
Returns:
Python distribution for environment or None
"""
def set_environment_python(app, env_name: str, python_path: Path) -> None:
"""
Set Python distribution for environment.
Args:
app: Application instance
env_name (str): Environment name
python_path (Path): Path to Python executable
"""
def ensure_python_for_environment(app, env_config: dict) -> InstalledDistribution:
"""
Ensure Python is available for environment configuration.
Args:
app: Application instance
env_config (dict): Environment configuration
Returns:
Python distribution for environment
"""
def resolve_python_requirement(requirement: str) -> list[str]:
"""
Resolve Python requirement to specific versions.
Args:
requirement (str): Python version requirement (e.g., '>=3.9,<3.12')
Returns:
List of Python versions that satisfy requirement
"""from hatch.python.core import PythonManager
from pathlib import Path
# Create Python manager
python_manager = PythonManager(Path.home() / '.hatch' / 'pythons')
# Get installed Python versions
installed = python_manager.get_installed()
print("Installed Python versions:")
for version, distribution in installed.items():
print(f" {version}: {distribution.python_path}")
# Install new Python version
if '3.11' not in installed:
print("Installing Python 3.11...")
python_311 = python_manager.install('3.11')
print(f"Installed at: {python_311.python_path}")
# Get available versions
available = python_manager.get_available()
print(f"Available versions: {available[:10]}...") # Show first 10from hatch.python.core import PythonManager, InstalledDistribution
python_manager = PythonManager(Path.home() / '.hatch' / 'pythons')
# Find specific distribution
python_311 = python_manager.find_distribution('3.11')
if python_311:
print(f"Python 3.11 found at: {python_311.python_path}")
print(f"Version: {python_311.version}")
print(f"Implementation: {python_311.implementation}")
print(f"Architecture: {python_311.architecture}")
# Check if needs update
if python_311.needs_update():
print("Update available")
updated = python_311.update()
print(f"Updated to: {updated.version}")
# Get detailed system info
info = python_311.get_system_info()
print(f"System info: {info}")
# Run command with this Python
result = python_311.run_command(['-c', 'import sys; print(sys.version)'])
print(f"Command output: {result.stdout}")from hatch.python import (
discover_system_pythons,
validate_python_installation,
get_python_version,
find_python_by_version
)
# Discover system Pythons
system_pythons = discover_system_pythons()
print("System Python installations:")
for python_path in system_pythons:
if validate_python_installation(Path(python_path)):
version = get_python_version(Path(python_path))
print(f" {python_path}: {version}")
# Find specific version on system
python_310 = find_python_by_version('3.10')
if python_310:
print(f"Found Python 3.10 at: {python_310}")
else:
print("Python 3.10 not found on system")from hatch.python import (
parse_version_spec,
resolve_version_identifier,
compare_versions,
version_matches_spec
)
# Parse version specifications
spec = parse_version_spec('pypy3.9.12')
print(f"Parsed spec: {spec}")
# Resolve version identifier
try:
resolved = resolve_version_identifier('3.11')
print(f"Resolved 3.11 to: {resolved}")
except PythonDistributionUnknownError as e:
print(f"Resolution failed: {e}")
# Compare versions
result = compare_versions('3.11.2', '3.10.8')
print(f"3.11.2 vs 3.10.8: {result}") # Should be 1 (newer)
# Check version matching
matches = version_matches_spec('3.11.2', '>=3.10,<3.12')
print(f"3.11.2 matches >=3.10,<3.12: {matches}") # Should be Truefrom hatch.python.sources import OfficialSource, PyPySource, UVSource
# Use official Python source
official = OfficialSource()
available_versions = official.get_available_versions()
print(f"Official Python versions: {available_versions[:10]}")
# Install using UV
uv_source = UVSource()
if uv_source.is_available():
uv_python = uv_source.install_distribution('3.11.8', target_dir)
print(f"Installed via UV: {uv_python.python_path}")
# Use PyPy source
pypy_source = PyPySource()
pypy_versions = pypy_source.get_available_versions()
print(f"PyPy versions: {pypy_versions}")from hatch.cli.application import Application
from hatch.python import get_environment_python, ensure_python_for_environment
app = Application(lambda code: exit(code))
# Get Python for environment
env_python = get_environment_python(app, 'test')
if env_python:
print(f"Test environment Python: {env_python.version}")
# Ensure Python for environment configuration
env_config = {
'python': '3.11',
'dependencies': ['pytest', 'coverage']
}
python_dist = ensure_python_for_environment(app, env_config)
print(f"Ensured Python: {python_dist.version} at {python_dist.python_path}")
# Environment will use this Python
env = app.get_environment('test')
env.create() # Uses the ensured Python versionfrom hatch.python.core import PythonManager
python_manager = PythonManager(Path.home() / '.hatch' / 'pythons')
# Install multiple versions
versions_to_install = ['3.9', '3.10', '3.11', '3.12']
for version in versions_to_install:
if version not in python_manager.get_installed():
print(f"Installing Python {version}...")
try:
python_manager.install(version)
print(f"✓ Installed Python {version}")
except Exception as e:
print(f"✗ Failed to install Python {version}: {e}")
# Update all installed versions
print("Updating all Python installations...")
updated = python_manager.update_all()
print(f"Updated versions: {updated}")
# Remove old versions
installed = python_manager.get_installed()
for version, distribution in installed.items():
if distribution.needs_update():
print(f"Python {version} has updates available")Install with Tessl CLI
npx tessl i tessl/pypi-hatch