or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application.mdcli-commands.mdconfiguration.mdenvironment-management.mdindex.mdplugin-system.mdproject-management.mdpython-management.md

python-management.mddocs/

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

```