or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-framework.mdcore-management.mddependency-resolution.mdenvironment-management.mdindex.mdinstallation-sync.mdproject-management.md

installation-sync.mddocs/

0

# Installation & Synchronization

1

2

Package installation management with support for wheels, source distributions, editable installs, and environment synchronization. This system handles the actual package installation process and keeps environments in sync with project specifications.

3

4

## Capabilities

5

6

### Installation Manager

7

8

Central coordinator for package installation operations with support for multiple installation backends and strategies.

9

10

```python { .api }

11

class InstallManager:

12

"""

13

Main installation coordinator managing package installation,

14

uninstallation, and environment synchronization.

15

"""

16

17

def __init__(self, environment: BaseEnvironment, use_install_cache: bool = True):

18

"""

19

Initialize installation manager.

20

21

Args:

22

environment: Target environment for installation

23

use_install_cache: Enable installation caching

24

"""

25

26

def install(self, candidates: list[Candidate]) -> None:

27

"""

28

Install packages from candidate list.

29

30

Args:

31

candidates: List of resolved package candidates to install

32

33

Raises:

34

InstallationError: Installation failures

35

"""

36

37

def uninstall(self, packages: list[str]) -> None:

38

"""

39

Uninstall packages by name.

40

41

Args:

42

packages: List of package names to uninstall

43

44

Raises:

45

UninstallError: Uninstallation failures

46

"""

47

48

def reinstall(self, packages: list[str]) -> None:

49

"""

50

Reinstall packages (uninstall then install).

51

52

Args:

53

packages: List of package names to reinstall

54

"""

55

56

def get_installation_plan(

57

self,

58

candidates: list[Candidate]

59

) -> InstallationPlan:

60

"""

61

Generate installation plan without executing.

62

63

Args:

64

candidates: Package candidates to analyze

65

66

Returns:

67

Installation plan with operations and dependencies

68

"""

69

```

70

71

### Synchronization Systems

72

73

Environment synchronization ensuring installed packages exactly match project specifications.

74

75

```python { .api }

76

class BaseSynchronizer(ABC):

77

"""

78

Abstract base class for environment synchronizers.

79

80

Synchronizers ensure the environment matches the lockfile

81

by installing missing packages and removing extras.

82

"""

83

84

@abstractmethod

85

def synchronize(

86

self,

87

candidates: dict[str, Candidate],

88

tracked_names: set[str] = None,

89

retry_times: int = 1

90

) -> None:

91

"""

92

Synchronize environment with candidate specifications.

93

94

Args:

95

candidates: Target package candidates

96

tracked_names: Names of packages to track

97

retry_times: Number of retry attempts

98

"""

99

100

class Synchronizer(BaseSynchronizer):

101

"""

102

Standard synchronizer using pip for package operations.

103

104

Provides reliable installation with comprehensive error handling

105

and support for all standard Python package formats.

106

"""

107

108

def __init__(

109

self,

110

candidate: Candidate,

111

environment: BaseEnvironment,

112

clean: bool = False,

113

dry_run: bool = False

114

):

115

"""

116

Initialize synchronizer.

117

118

Args:

119

candidate: Package candidate for synchronization

120

environment: Target environment

121

clean: Remove packages not in candidate list

122

dry_run: Show operations without executing

123

"""

124

125

def synchronize(

126

self,

127

candidates: dict[str, Candidate],

128

tracked_names: set[str] = None,

129

retry_times: int = 1

130

) -> None:

131

"""Synchronize using pip-based installation"""

132

133

class UvSynchronizer(BaseSynchronizer):

134

"""

135

High-performance synchronizer using UV for package operations.

136

137

Provides faster installation and resolution with improved

138

performance for large dependency sets.

139

"""

140

141

def synchronize(

142

self,

143

candidates: dict[str, Candidate],

144

tracked_names: set[str] = None,

145

retry_times: int = 1

146

) -> None:

147

"""Synchronize using UV-based installation"""

148

```

149

150

### Installation Plans

151

152

Planning and preview functionality for installation operations.

153

154

```python { .api }

155

@dataclass

156

class InstallationPlan:

157

"""

158

Installation plan containing all operations to be performed.

159

160

Provides preview of installation operations including downloads,

161

builds, installations, and uninstallations.

162

"""

163

164

to_install: list[Candidate]

165

to_uninstall: list[str]

166

to_update: list[tuple[str, Candidate]]

167

168

@property

169

def total_size(self) -> int:

170

"""Total download size in bytes"""

171

172

@property

173

def operation_count(self) -> int:

174

"""Total number of operations"""

175

176

def format_summary(self) -> str:

177

"""

178

Format human-readable installation summary.

179

180

Returns:

181

Formatted summary of planned operations

182

"""

183

```

184

185

### Package Candidate Management

186

187

Package candidate representation and metadata handling for installation operations.

188

189

```python { .api }

190

class Candidate:

191

"""

192

Represents a specific package version candidate for installation.

193

194

Contains all metadata and information needed for package installation

195

including source locations, dependencies, and build requirements.

196

"""

197

198

@property

199

def name(self) -> str:

200

"""Package name"""

201

202

@property

203

def version(self) -> str:

204

"""Package version"""

205

206

@property

207

def source_type(self) -> str:

208

"""Source type (wheel, sdist, vcs, etc.)"""

209

210

@property

211

def dependencies(self) -> list[Requirement]:

212

"""Package dependencies"""

213

214

@property

215

def requires_python(self) -> str | None:

216

"""Python version requirement"""

217

218

def get_metadata(self) -> dict:

219

"""

220

Get package metadata.

221

222

Returns:

223

Dictionary containing package metadata

224

"""

225

226

def prepare(self, environment: BaseEnvironment) -> PreparedCandidate:

227

"""

228

Prepare candidate for installation.

229

230

Args:

231

environment: Target environment

232

233

Returns:

234

Prepared candidate ready for installation

235

"""

236

237

class PreparedCandidate:

238

"""

239

Candidate prepared for installation with resolved metadata and build artifacts.

240

"""

241

242

@property

243

def wheel_path(self) -> Path | None:

244

"""Path to wheel file if available"""

245

246

@property

247

def metadata_dir(self) -> Path:

248

"""Path to extracted metadata directory"""

249

250

def install(self, installer: InstallManager) -> None:

251

"""Install this prepared candidate"""

252

```

253

254

### Installation Strategies

255

256

Different installation strategies for various package types and requirements.

257

258

```python { .api }

259

class InstallationStrategy(ABC):

260

"""Base class for installation strategies"""

261

262

@abstractmethod

263

def install_candidate(

264

self,

265

candidate: PreparedCandidate,

266

environment: BaseEnvironment

267

) -> None:

268

"""Install prepared candidate"""

269

270

class WheelInstaller(InstallationStrategy):

271

"""Strategy for installing wheel distributions"""

272

273

def install_candidate(

274

self,

275

candidate: PreparedCandidate,

276

environment: BaseEnvironment

277

) -> None:

278

"""Install wheel using installer library"""

279

280

class EditableInstaller(InstallationStrategy):

281

"""Strategy for editable/development installations"""

282

283

def install_candidate(

284

self,

285

candidate: PreparedCandidate,

286

environment: BaseEnvironment

287

) -> None:

288

"""Install package in editable mode"""

289

```

290

291

### Usage Examples

292

293

#### Basic Installation Operations

294

295

```python

296

from pdm.installers import InstallManager

297

from pdm.environments import PythonEnvironment

298

from pdm.models.candidates import Candidate

299

300

# Setup environment and installer

301

env = PythonEnvironment(python_executable="python3.9")

302

installer = InstallManager(env, use_install_cache=True)

303

304

# Install packages

305

candidates = [

306

Candidate.from_requirement("requests>=2.25.0"),

307

Candidate.from_requirement("click>=8.0.0")

308

]

309

310

installer.install(candidates)

311

312

# Uninstall packages

313

installer.uninstall(["requests", "click"])

314

```

315

316

#### Environment Synchronization

317

318

```python

319

from pdm.installers import Synchronizer, UvSynchronizer

320

from pdm.environments import PythonEnvironment

321

from pdm.project import Project

322

323

project = Project()

324

env = PythonEnvironment(project.python.executable)

325

326

# Get lockfile candidates

327

lockfile = project.lockfile.read()

328

candidates = {

329

name: Candidate.from_lockfile_entry(entry)

330

for name, entry in lockfile["package"].items()

331

}

332

333

# Synchronize with standard synchronizer

334

sync = Synchronizer(env, clean=True, dry_run=False)

335

sync.synchronize(candidates)

336

337

# Or use UV synchronizer for better performance

338

uv_sync = UvSynchronizer(env)

339

uv_sync.synchronize(candidates)

340

```

341

342

#### Installation Planning

343

344

```python

345

from pdm.installers import InstallManager

346

from pdm.environments import PythonEnvironment

347

348

env = PythonEnvironment()

349

installer = InstallManager(env)

350

351

# Generate installation plan

352

candidates = [

353

Candidate.from_requirement("django>=4.0"),

354

Candidate.from_requirement("psycopg2-binary")

355

]

356

357

plan = installer.get_installation_plan(candidates)

358

359

# Review plan before installation

360

print(plan.format_summary())

361

print(f"Total operations: {plan.operation_count}")

362

print(f"Download size: {plan.total_size / (1024*1024):.1f} MB")

363

364

# Execute if acceptable

365

if input("Proceed? (y/N): ").lower() == 'y':

366

installer.install(candidates)

367

```

368

369

#### Custom Installation Strategy

370

371

```python

372

from pdm.installers import InstallationStrategy, InstallManager

373

from pdm.environments import BaseEnvironment

374

from pdm.models.candidates import PreparedCandidate

375

376

class CustomInstaller(InstallationStrategy):

377

"""Custom installation strategy with logging"""

378

379

def install_candidate(

380

self,

381

candidate: PreparedCandidate,

382

environment: BaseEnvironment

383

) -> None:

384

print(f"Installing {candidate.name} {candidate.version}")

385

386

# Custom pre-installation steps

387

self._pre_install_hook(candidate)

388

389

# Standard wheel installation

390

if candidate.wheel_path:

391

environment.install_wheel(candidate.wheel_path)

392

else:

393

environment.install_sdist(candidate.source_dir)

394

395

# Custom post-installation steps

396

self._post_install_hook(candidate)

397

398

def _pre_install_hook(self, candidate: PreparedCandidate) -> None:

399

"""Custom pre-installation logic"""

400

pass

401

402

def _post_install_hook(self, candidate: PreparedCandidate) -> None:

403

"""Custom post-installation logic"""

404

pass

405

406

# Use custom installer

407

env = PythonEnvironment()

408

installer = InstallManager(env)

409

installer.strategy = CustomInstaller()

410

```

411

412

#### Batch Installation Operations

413

414

```python

415

from pdm.installers import InstallManager

416

from pdm.environments import PythonEnvironment

417

import asyncio

418

419

async def install_packages_batch(

420

packages: list[str],

421

environment: BaseEnvironment

422

) -> None:

423

"""Install multiple packages in batches"""

424

425

installer = InstallManager(environment)

426

batch_size = 5

427

428

for i in range(0, len(packages), batch_size):

429

batch = packages[i:i + batch_size]

430

candidates = [

431

Candidate.from_requirement(pkg) for pkg in batch

432

]

433

434

print(f"Installing batch {i//batch_size + 1}: {batch}")

435

installer.install(candidates)

436

437

# Brief pause between batches

438

await asyncio.sleep(1)

439

440

# Usage

441

packages = [

442

"requests", "click", "rich", "typer", "httpx",

443

"pydantic", "fastapi", "pytest", "black", "mypy"

444

]

445

446

env = PythonEnvironment()

447

asyncio.run(install_packages_batch(packages, env))

448

```