0
# Python Management
1
2
Automated Python distribution management including installation, version management, and distribution discovery. Supports multiple Python versions and provides integration with UV and other Python managers.
3
4
## Capabilities
5
6
### Python Manager
7
8
Central manager for Python installations providing discovery, installation, and management of multiple Python versions.
9
10
```python { .api }
11
class PythonManager:
12
"""
13
Manager for Python installations and distributions.
14
15
Handles downloading, installing, and managing multiple Python versions
16
with support for different sources and installation methods.
17
"""
18
19
def __init__(self, directory: Path):
20
"""
21
Initialize Python manager.
22
23
Args:
24
directory (Path): Directory for Python installations
25
"""
26
27
@property
28
def directory(self) -> Path:
29
"""
30
Directory containing Python installations.
31
32
Returns:
33
Path to Python installations directory
34
"""
35
36
def get_installed(self) -> dict[str, 'InstalledDistribution']:
37
"""
38
Get all installed Python distributions.
39
40
Returns:
41
Dict mapping version strings to InstalledDistribution instances
42
"""
43
44
def install(self, identifier: str) -> 'InstalledDistribution':
45
"""
46
Install Python distribution by version identifier.
47
48
Args:
49
identifier (str): Version identifier (e.g., '3.11', '3.10.5', 'pypy3.9')
50
51
Returns:
52
InstalledDistribution instance for the installed Python
53
54
Raises:
55
PythonDistributionUnknownError: If identifier is not recognized
56
PythonDistributionResolutionError: If installation fails
57
"""
58
59
def remove(self, identifier: str) -> None:
60
"""
61
Remove installed Python distribution.
62
63
Args:
64
identifier (str): Version identifier to remove
65
"""
66
67
def find_distribution(self, identifier: str) -> 'InstalledDistribution | None':
68
"""
69
Find installed distribution by identifier.
70
71
Args:
72
identifier (str): Version identifier to find
73
74
Returns:
75
InstalledDistribution if found, None otherwise
76
"""
77
78
def get_available(self) -> list[str]:
79
"""
80
Get list of available Python versions for installation.
81
82
Returns:
83
List of available version identifiers
84
"""
85
86
def update_all(self) -> list[str]:
87
"""
88
Update all installed Python distributions.
89
90
Returns:
91
List of updated version identifiers
92
"""
93
```
94
95
### Installed Distribution
96
97
Represents an installed Python distribution with version information and utility methods.
98
99
```python { .api }
100
class InstalledDistribution:
101
"""
102
Represents an installed Python distribution.
103
104
Provides access to Python executable, version information,
105
and utilities for managing the installation.
106
"""
107
108
def __init__(self, path: Path, python_path: Path):
109
"""
110
Initialize installed distribution.
111
112
Args:
113
path (Path): Installation directory path
114
python_path (Path): Path to Python executable
115
"""
116
117
@property
118
def path(self) -> Path:
119
"""
120
Installation directory path.
121
122
Returns:
123
Path to installation directory
124
"""
125
126
@property
127
def name(self) -> str:
128
"""
129
Distribution name (e.g., 'cpython', 'pypy').
130
131
Returns:
132
Distribution name string
133
"""
134
135
@property
136
def python_path(self) -> Path:
137
"""
138
Path to Python executable.
139
140
Returns:
141
Path to python executable
142
"""
143
144
@property
145
def version(self) -> str:
146
"""
147
Python version string.
148
149
Returns:
150
Version string (e.g., '3.11.2')
151
"""
152
153
@property
154
def python_version(self) -> tuple[int, int, int]:
155
"""
156
Python version as tuple.
157
158
Returns:
159
Tuple of (major, minor, patch) version numbers
160
"""
161
162
@property
163
def implementation(self) -> str:
164
"""
165
Python implementation name.
166
167
Returns:
168
Implementation name ('cpython', 'pypy', etc.)
169
"""
170
171
@property
172
def architecture(self) -> str:
173
"""
174
Architecture string.
175
176
Returns:
177
Architecture ('x86_64', 'arm64', etc.)
178
"""
179
180
@property
181
def platform(self) -> str:
182
"""
183
Platform string.
184
185
Returns:
186
Platform ('linux', 'darwin', 'win32', etc.)
187
"""
188
189
def needs_update(self) -> bool:
190
"""
191
Check if distribution needs updating.
192
193
Returns:
194
True if a newer version is available
195
"""
196
197
def update(self) -> 'InstalledDistribution':
198
"""
199
Update this distribution to latest version.
200
201
Returns:
202
New InstalledDistribution instance after update
203
"""
204
205
def get_system_info(self) -> dict[str, str]:
206
"""
207
Get detailed system information for this Python.
208
209
Returns:
210
Dict with system information (version, platform, paths, etc.)
211
"""
212
213
def run_command(self, command: list[str], **kwargs):
214
"""
215
Run command with this Python executable.
216
217
Args:
218
command (list[str]): Command to run (python will be prepended)
219
**kwargs: Additional arguments for subprocess
220
221
Returns:
222
Command execution result
223
"""
224
```
225
226
### Python Discovery
227
228
Utilities for discovering and validating Python installations on the system including system Python and managed installations.
229
230
```python { .api }
231
def discover_system_pythons() -> list[str]:
232
"""
233
Discover Python installations on system PATH.
234
235
Returns:
236
List of Python executable paths found on system
237
"""
238
239
def validate_python_installation(python_path: Path) -> bool:
240
"""
241
Validate that path points to working Python installation.
242
243
Args:
244
python_path (Path): Path to Python executable
245
246
Returns:
247
True if Python installation is valid and working
248
"""
249
250
def get_python_version(python_path: Path) -> str:
251
"""
252
Get version string from Python executable.
253
254
Args:
255
python_path (Path): Path to Python executable
256
257
Returns:
258
Version string (e.g., '3.11.2')
259
260
Raises:
261
PythonDistributionResolutionError: If version cannot be determined
262
"""
263
264
def find_python_by_version(version: str) -> Path | None:
265
"""
266
Find Python executable by version on system.
267
268
Args:
269
version (str): Version to search for (e.g., '3.11', '3.10.5')
270
271
Returns:
272
Path to Python executable or None if not found
273
"""
274
275
def get_python_info(python_path: Path) -> dict[str, str]:
276
"""
277
Get detailed information about Python installation.
278
279
Args:
280
python_path (Path): Path to Python executable
281
282
Returns:
283
Dict with Python information (version, implementation, platform, etc.)
284
"""
285
```
286
287
### Version Resolution
288
289
Python version parsing, comparison, and resolution utilities for handling different version formats and specifiers.
290
291
```python { .api }
292
def parse_version_spec(spec: str) -> dict[str, str]:
293
"""
294
Parse version specification into components.
295
296
Args:
297
spec (str): Version specification (e.g., '3.11.2', 'pypy3.9', 'python3.10')
298
299
Returns:
300
Dict with parsed components (implementation, major, minor, patch)
301
"""
302
303
def resolve_version_identifier(identifier: str) -> str:
304
"""
305
Resolve version identifier to specific version.
306
307
Args:
308
identifier (str): Version identifier to resolve
309
310
Returns:
311
Resolved specific version string
312
313
Raises:
314
PythonDistributionUnknownError: If identifier cannot be resolved
315
"""
316
317
def compare_versions(version1: str, version2: str) -> int:
318
"""
319
Compare two Python version strings.
320
321
Args:
322
version1 (str): First version string
323
version2 (str): Second version string
324
325
Returns:
326
-1 if version1 < version2, 0 if equal, 1 if version1 > version2
327
"""
328
329
def get_latest_version(versions: list[str]) -> str:
330
"""
331
Get latest version from list of version strings.
332
333
Args:
334
versions (list[str]): List of version strings
335
336
Returns:
337
Latest version string
338
"""
339
340
def version_matches_spec(version: str, spec: str) -> bool:
341
"""
342
Check if version matches specification.
343
344
Args:
345
version (str): Version to check
346
spec (str): Version specification (supports ranges, ~=, etc.)
347
348
Returns:
349
True if version matches specification
350
"""
351
```
352
353
### Installation Sources
354
355
Support for different Python installation sources including official releases, PyPy, and custom sources.
356
357
```python { .api }
358
class InstallationSource:
359
"""Base class for Python installation sources."""
360
361
def get_available_versions(self) -> list[str]:
362
"""Get list of available versions from this source."""
363
364
def download_distribution(self, version: str, target_dir: Path) -> Path:
365
"""Download distribution for version to target directory."""
366
367
def install_distribution(self, version: str, target_dir: Path) -> InstalledDistribution:
368
"""Install distribution for version to target directory."""
369
370
class OfficialSource(InstallationSource):
371
"""Official Python.org releases."""
372
373
BASE_URL = "https://www.python.org/ftp/python/"
374
375
def get_available_versions(self) -> list[str]:
376
"""Get available CPython versions from python.org."""
377
378
class PyPySource(InstallationSource):
379
"""PyPy releases."""
380
381
BASE_URL = "https://downloads.python.org/pypy/"
382
383
def get_available_versions(self) -> list[str]:
384
"""Get available PyPy versions."""
385
386
class UVSource(InstallationSource):
387
"""UV Python installations."""
388
389
def get_available_versions(self) -> list[str]:
390
"""Get Python versions available through UV."""
391
392
def install_distribution(self, version: str, target_dir: Path) -> InstalledDistribution:
393
"""Install Python using UV."""
394
```
395
396
### Python Environment Integration
397
398
Integration between Python installations and hatch environments for seamless Python version management.
399
400
```python { .api }
401
def get_environment_python(app, env_name: str) -> InstalledDistribution | None:
402
"""
403
Get Python distribution for environment.
404
405
Args:
406
app: Application instance
407
env_name (str): Environment name
408
409
Returns:
410
Python distribution for environment or None
411
"""
412
413
def set_environment_python(app, env_name: str, python_path: Path) -> None:
414
"""
415
Set Python distribution for environment.
416
417
Args:
418
app: Application instance
419
env_name (str): Environment name
420
python_path (Path): Path to Python executable
421
"""
422
423
def ensure_python_for_environment(app, env_config: dict) -> InstalledDistribution:
424
"""
425
Ensure Python is available for environment configuration.
426
427
Args:
428
app: Application instance
429
env_config (dict): Environment configuration
430
431
Returns:
432
Python distribution for environment
433
"""
434
435
def resolve_python_requirement(requirement: str) -> list[str]:
436
"""
437
Resolve Python requirement to specific versions.
438
439
Args:
440
requirement (str): Python version requirement (e.g., '>=3.9,<3.12')
441
442
Returns:
443
List of Python versions that satisfy requirement
444
"""
445
```
446
447
## Usage Examples
448
449
### Basic Python Management
450
451
```python
452
from hatch.python.core import PythonManager
453
from pathlib import Path
454
455
# Create Python manager
456
python_manager = PythonManager(Path.home() / '.hatch' / 'pythons')
457
458
# Get installed Python versions
459
installed = python_manager.get_installed()
460
print("Installed Python versions:")
461
for version, distribution in installed.items():
462
print(f" {version}: {distribution.python_path}")
463
464
# Install new Python version
465
if '3.11' not in installed:
466
print("Installing Python 3.11...")
467
python_311 = python_manager.install('3.11')
468
print(f"Installed at: {python_311.python_path}")
469
470
# Get available versions
471
available = python_manager.get_available()
472
print(f"Available versions: {available[:10]}...") # Show first 10
473
```
474
475
### Working with Installed Distributions
476
477
```python
478
from hatch.python.core import PythonManager, InstalledDistribution
479
480
python_manager = PythonManager(Path.home() / '.hatch' / 'pythons')
481
482
# Find specific distribution
483
python_311 = python_manager.find_distribution('3.11')
484
if python_311:
485
print(f"Python 3.11 found at: {python_311.python_path}")
486
print(f"Version: {python_311.version}")
487
print(f"Implementation: {python_311.implementation}")
488
print(f"Architecture: {python_311.architecture}")
489
490
# Check if needs update
491
if python_311.needs_update():
492
print("Update available")
493
updated = python_311.update()
494
print(f"Updated to: {updated.version}")
495
496
# Get detailed system info
497
info = python_311.get_system_info()
498
print(f"System info: {info}")
499
500
# Run command with this Python
501
result = python_311.run_command(['-c', 'import sys; print(sys.version)'])
502
print(f"Command output: {result.stdout}")
503
```
504
505
### Python Discovery and Validation
506
507
```python
508
from hatch.python import (
509
discover_system_pythons,
510
validate_python_installation,
511
get_python_version,
512
find_python_by_version
513
)
514
515
# Discover system Pythons
516
system_pythons = discover_system_pythons()
517
print("System Python installations:")
518
for python_path in system_pythons:
519
if validate_python_installation(Path(python_path)):
520
version = get_python_version(Path(python_path))
521
print(f" {python_path}: {version}")
522
523
# Find specific version on system
524
python_310 = find_python_by_version('3.10')
525
if python_310:
526
print(f"Found Python 3.10 at: {python_310}")
527
else:
528
print("Python 3.10 not found on system")
529
```
530
531
### Version Resolution
532
533
```python
534
from hatch.python import (
535
parse_version_spec,
536
resolve_version_identifier,
537
compare_versions,
538
version_matches_spec
539
)
540
541
# Parse version specifications
542
spec = parse_version_spec('pypy3.9.12')
543
print(f"Parsed spec: {spec}")
544
545
# Resolve version identifier
546
try:
547
resolved = resolve_version_identifier('3.11')
548
print(f"Resolved 3.11 to: {resolved}")
549
except PythonDistributionUnknownError as e:
550
print(f"Resolution failed: {e}")
551
552
# Compare versions
553
result = compare_versions('3.11.2', '3.10.8')
554
print(f"3.11.2 vs 3.10.8: {result}") # Should be 1 (newer)
555
556
# Check version matching
557
matches = version_matches_spec('3.11.2', '>=3.10,<3.12')
558
print(f"3.11.2 matches >=3.10,<3.12: {matches}") # Should be True
559
```
560
561
### Custom Installation Sources
562
563
```python
564
from hatch.python.sources import OfficialSource, PyPySource, UVSource
565
566
# Use official Python source
567
official = OfficialSource()
568
available_versions = official.get_available_versions()
569
print(f"Official Python versions: {available_versions[:10]}")
570
571
# Install using UV
572
uv_source = UVSource()
573
if uv_source.is_available():
574
uv_python = uv_source.install_distribution('3.11.8', target_dir)
575
print(f"Installed via UV: {uv_python.python_path}")
576
577
# Use PyPy source
578
pypy_source = PyPySource()
579
pypy_versions = pypy_source.get_available_versions()
580
print(f"PyPy versions: {pypy_versions}")
581
```
582
583
### Environment Integration
584
585
```python
586
from hatch.cli.application import Application
587
from hatch.python import get_environment_python, ensure_python_for_environment
588
589
app = Application(lambda code: exit(code))
590
591
# Get Python for environment
592
env_python = get_environment_python(app, 'test')
593
if env_python:
594
print(f"Test environment Python: {env_python.version}")
595
596
# Ensure Python for environment configuration
597
env_config = {
598
'python': '3.11',
599
'dependencies': ['pytest', 'coverage']
600
}
601
602
python_dist = ensure_python_for_environment(app, env_config)
603
print(f"Ensured Python: {python_dist.version} at {python_dist.python_path}")
604
605
# Environment will use this Python
606
env = app.get_environment('test')
607
env.create() # Uses the ensured Python version
608
```
609
610
### Batch Operations
611
612
```python
613
from hatch.python.core import PythonManager
614
615
python_manager = PythonManager(Path.home() / '.hatch' / 'pythons')
616
617
# Install multiple versions
618
versions_to_install = ['3.9', '3.10', '3.11', '3.12']
619
for version in versions_to_install:
620
if version not in python_manager.get_installed():
621
print(f"Installing Python {version}...")
622
try:
623
python_manager.install(version)
624
print(f"✓ Installed Python {version}")
625
except Exception as e:
626
print(f"✗ Failed to install Python {version}: {e}")
627
628
# Update all installed versions
629
print("Updating all Python installations...")
630
updated = python_manager.update_all()
631
print(f"Updated versions: {updated}")
632
633
# Remove old versions
634
installed = python_manager.get_installed()
635
for version, distribution in installed.items():
636
if distribution.needs_update():
637
print(f"Python {version} has updates available")
638
```