or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

build-backend.mdbuilders.mdconfiguration.mdconstraints.mdfactory-core.mdindex.mdjson-validation.mdpackages.mdutilities.mdvcs-support.mdversion-system.md

constraints.mddocs/

0

# Version and Generic Constraint Handling

1

2

Poetry Core provides a comprehensive constraint system for handling version requirements and generic constraints. This includes PEP 440-compliant version constraints, Poetry-specific syntax (caret and tilde), and generic constraint operations.

3

4

## Core Imports

5

6

```python

7

# Version constraints

8

from poetry.core.constraints.version import (

9

Version,

10

VersionConstraint,

11

VersionRange,

12

VersionRangeConstraint,

13

VersionUnion,

14

EmptyConstraint,

15

parse_constraint,

16

parse_marker_version_constraint,

17

constraint_regions

18

)

19

20

# Generic constraints

21

from poetry.core.constraints.generic import (

22

BaseConstraint,

23

Constraint,

24

AnyConstraint,

25

EmptyConstraint as GenericEmptyConstraint,

26

MultiConstraint,

27

UnionConstraint,

28

parse_constraint as parse_generic_constraint,

29

parse_extra_constraint

30

)

31

``` { .api }

32

33

## Version Constraints

34

35

Version constraints handle PEP 440 version specifications with additional Poetry syntax support.

36

37

### parse_constraint

38

39

```python

40

def parse_constraint(constraints: str) -> VersionConstraint:

41

"""

42

Parse string version constraints into constraint objects.

43

44

Args:

45

constraints: Version constraint string supporting various formats:

46

- PEP 440: ">=1.2.0,<2.0.0", "==1.5.*", "~=1.4.2"

47

- Poetry caret: "^1.2.0" (compatible release)

48

- Poetry tilde: "~1.2.0" (reasonably close)

49

- Wildcards: "1.2.*"

50

- Multiple: ">=1.0,<2.0,!=1.5"

51

52

Returns:

53

VersionConstraint object (Version, VersionRange, VersionUnion, etc.)

54

55

Raises:

56

ParseConstraintError: If constraint string is malformed

57

58

Examples:

59

>>> constraint = parse_constraint("^1.2.0")

60

>>> print(constraint) # >=1.2.0,<2.0.0

61

62

>>> constraint = parse_constraint("~1.2.3")

63

>>> print(constraint) # >=1.2.3,<1.3.0

64

65

>>> constraint = parse_constraint(">=2.0,<3.0,!=2.5")

66

>>> isinstance(constraint, VersionUnion)

67

True

68

"""

69

``` { .api }

70

71

### parse_marker_version_constraint

72

73

```python

74

def parse_marker_version_constraint(constraint: str) -> VersionConstraint:

75

"""

76

Parse version constraints from PEP 508 environment markers.

77

78

Args:

79

constraint: Version constraint from marker context

80

81

Returns:

82

VersionConstraint suitable for marker evaluation

83

84

Example:

85

>>> # From marker: python_version >= "3.8"

86

>>> constraint = parse_marker_version_constraint("3.8")

87

>>> print(constraint)

88

"""

89

``` { .api }

90

91

### constraint_regions

92

93

```python

94

def constraint_regions(constraints: list[VersionConstraint]) -> list[VersionRange]:

95

"""

96

Analyze constraint regions for optimization and conflict detection.

97

98

Args:

99

constraints: List of version constraints to analyze

100

101

Returns:

102

List of VersionRange objects representing constraint regions

103

104

Note:

105

Utility function for advanced constraint analysis and optimization.

106

"""

107

``` { .api }

108

109

## Version Constraint Classes

110

111

### Version

112

113

```python

114

class Version(PEP440Version, VersionRangeConstraint):

115

"""

116

Concrete version implementation with PEP 440 compliance and constraints.

117

118

Combines PEP 440 version parsing with constraint satisfaction logic.

119

Supports all PEP 440 version formats including pre-releases, dev releases,

120

and local versions.

121

"""

122

123

@classmethod

124

def parse(cls, version: str) -> Version:

125

"""

126

Parse version string into Version object.

127

128

Args:

129

version: PEP 440 compliant version string

130

131

Returns:

132

Version instance

133

134

Example:

135

>>> v = Version.parse("1.2.3")

136

>>> v = Version.parse("2.0.0a1") # alpha

137

>>> v = Version.parse("1.0.0.dev0") # dev

138

"""

139

140

def allows(self, other: Version) -> bool:

141

"""Check if this version allows another version (equality)."""

142

143

def intersect(self, other: VersionConstraint) -> VersionConstraint:

144

"""Create intersection with another constraint."""

145

146

def union(self, other: VersionConstraint) -> VersionConstraint:

147

"""Create union with another constraint."""

148

``` { .api }

149

150

### VersionRange

151

152

```python

153

class VersionRange(VersionRangeConstraint):

154

"""

155

Represents a range of versions with minimum and maximum bounds.

156

157

Supports inclusive and exclusive bounds, unbounded ranges,

158

and complex range operations.

159

"""

160

161

def __init__(

162

self,

163

min_version: Version | None = None,

164

max_version: Version | None = None,

165

include_min: bool = True,

166

include_max: bool = False,

167

) -> None:

168

"""

169

Create version range.

170

171

Args:

172

min_version: Minimum version (None for unbounded)

173

max_version: Maximum version (None for unbounded)

174

include_min: Whether minimum is inclusive

175

include_max: Whether maximum is inclusive

176

177

Example:

178

>>> # >=1.0.0,<2.0.0

179

>>> range1 = VersionRange(

180

... Version.parse("1.0.0"),

181

... Version.parse("2.0.0")

182

... )

183

184

>>> # >1.0.0,<=1.5.0

185

>>> range2 = VersionRange(

186

... Version.parse("1.0.0"),

187

... Version.parse("1.5.0"),

188

... include_min=False,

189

... include_max=True

190

... )

191

"""

192

193

@property

194

def min(self) -> Version | None:

195

"""Minimum version bound."""

196

197

@property

198

def max(self) -> Version | None:

199

"""Maximum version bound."""

200

201

def allows(self, version: Version) -> bool:

202

"""Check if version falls within this range."""

203

204

def intersect(self, other: VersionConstraint) -> VersionConstraint:

205

"""Create intersection with another constraint."""

206

207

def union(self, other: VersionConstraint) -> VersionConstraint:

208

"""Create union with another constraint."""

209

``` { .api }

210

211

### VersionUnion

212

213

```python

214

class VersionUnion(VersionConstraint):

215

"""

216

Union of multiple version constraints (OR operation).

217

218

Represents constraints like ">=1.0,<2.0 || >=3.0,<4.0" where

219

a version can satisfy any of the constituent constraints.

220

"""

221

222

def __init__(self, *constraints: VersionConstraint) -> None:

223

"""

224

Create union of constraints.

225

226

Args:

227

constraints: Version constraints to union

228

229

Example:

230

>>> range1 = VersionRange(Version.parse("1.0.0"), Version.parse("2.0.0"))

231

>>> range2 = VersionRange(Version.parse("3.0.0"), Version.parse("4.0.0"))

232

>>> union = VersionUnion(range1, range2)

233

"""

234

235

@property

236

def constraints(self) -> tuple[VersionConstraint, ...]:

237

"""Constituent constraints in the union."""

238

239

def allows(self, version: Version) -> bool:

240

"""Check if version satisfies any constraint in union."""

241

``` { .api }

242

243

### EmptyConstraint

244

245

```python

246

class EmptyConstraint(VersionConstraint):

247

"""

248

Version constraint representing no valid versions.

249

250

Used when constraints are impossible to satisfy or

251

when explicitly excluding all versions.

252

"""

253

254

def allows(self, version: Version) -> bool:

255

"""Always returns False - no versions allowed."""

256

257

def is_empty(self) -> bool:

258

"""Always returns True."""

259

``` { .api }

260

261

## Generic Constraints

262

263

Generic constraints provide a framework for non-version constraint operations.

264

265

### parse_constraint (Generic)

266

267

```python

268

def parse_constraint(constraints: str) -> BaseConstraint:

269

"""

270

Parse string constraints into generic constraint objects.

271

272

Args:

273

constraints: Constraint string

274

275

Returns:

276

BaseConstraint implementation

277

278

Example:

279

>>> constraint = parse_constraint("foo")

280

>>> constraint = parse_constraint("foo,bar") # Multi-constraint

281

"""

282

``` { .api }

283

284

### parse_extra_constraint

285

286

```python

287

def parse_extra_constraint(constraints: str) -> BaseConstraint:

288

"""

289

Parse extra/optional feature constraints.

290

291

Args:

292

constraints: Extra constraint string (e.g., "dev,test")

293

294

Returns:

295

BaseConstraint for extra matching

296

297

Example:

298

>>> extra_constraint = parse_extra_constraint("dev,test")

299

>>> # Used for conditional dependencies

300

"""

301

``` { .api }

302

303

## Generic Constraint Classes

304

305

### BaseConstraint

306

307

```python

308

class BaseConstraint:

309

"""

310

Abstract base class for all constraint types.

311

312

Defines the interface that all constraints must implement

313

for matching, intersection, and union operations.

314

"""

315

316

def allows(self, other: Any) -> bool:

317

"""Check if constraint allows a value."""

318

319

def intersect(self, other: BaseConstraint) -> BaseConstraint:

320

"""Create intersection with another constraint."""

321

322

def union(self, other: BaseConstraint) -> BaseConstraint:

323

"""Create union with another constraint."""

324

325

def is_any(self) -> bool:

326

"""Check if constraint matches any value."""

327

328

def is_empty(self) -> bool:

329

"""Check if constraint matches no values."""

330

``` { .api }

331

332

### Constraint

333

334

```python

335

class Constraint(BaseConstraint):

336

"""

337

Basic constraint implementation for exact matching.

338

339

Matches values exactly against a stored constraint value.

340

"""

341

342

def __init__(self, constraint: str) -> None:

343

"""

344

Create constraint with exact matching value.

345

346

Args:

347

constraint: Value to match exactly

348

"""

349

350

@property

351

def constraint(self) -> str:

352

"""The constraint value."""

353

``` { .api }

354

355

### AnyConstraint

356

357

```python

358

class AnyConstraint(BaseConstraint):

359

"""

360

Constraint that matches any value.

361

362

Represents the absence of constraints - all values are valid.

363

"""

364

365

def allows(self, other: Any) -> bool:

366

"""Always returns True - allows any value."""

367

368

def is_any(self) -> bool:

369

"""Always returns True."""

370

``` { .api }

371

372

### EmptyConstraint (Generic)

373

374

```python

375

class EmptyConstraint(BaseConstraint):

376

"""

377

Generic constraint matching no values.

378

379

Represents impossible or explicitly empty constraints.

380

"""

381

382

def allows(self, other: Any) -> bool:

383

"""Always returns False - allows no values."""

384

385

def is_empty(self) -> bool:

386

"""Always returns True."""

387

``` { .api }

388

389

### MultiConstraint

390

391

```python

392

class MultiConstraint(BaseConstraint):

393

"""

394

Conjunction of multiple constraints (AND operation).

395

396

All constituent constraints must be satisfied.

397

"""

398

399

def __init__(self, *constraints: BaseConstraint) -> None:

400

"""

401

Create conjunction of constraints.

402

403

Args:

404

constraints: Constraints that must all be satisfied

405

"""

406

407

@property

408

def constraints(self) -> tuple[BaseConstraint, ...]:

409

"""Constituent constraints."""

410

411

def allows(self, other: Any) -> bool:

412

"""Check if all constraints allow the value."""

413

``` { .api }

414

415

### UnionConstraint

416

417

```python

418

class UnionConstraint(BaseConstraint):

419

"""

420

Disjunction of multiple constraints (OR operation).

421

422

Any constituent constraint can be satisfied.

423

"""

424

425

def __init__(self, *constraints: BaseConstraint) -> None:

426

"""

427

Create disjunction of constraints.

428

429

Args:

430

constraints: Constraints where any can be satisfied

431

"""

432

433

@property

434

def constraints(self) -> tuple[BaseConstraint, ...]:

435

"""Constituent constraints."""

436

437

def allows(self, other: Any) -> bool:

438

"""Check if any constraint allows the value."""

439

``` { .api }

440

441

## Usage Examples

442

443

### Basic Version Constraint Parsing

444

445

```python

446

from poetry.core.constraints.version import parse_constraint, Version

447

448

def demonstrate_version_constraints():

449

"""Show various version constraint formats."""

450

451

# Poetry caret constraint (compatible release)

452

caret = parse_constraint("^1.2.0")

453

print(f"Caret ^1.2.0: {caret}") # >=1.2.0,<2.0.0

454

455

# Poetry tilde constraint (reasonably close)

456

tilde = parse_constraint("~1.2.3")

457

print(f"Tilde ~1.2.3: {tilde}") # >=1.2.3,<1.3.0

458

459

# PEP 440 constraints

460

pep440 = parse_constraint(">=1.0,<2.0,!=1.5")

461

print(f"PEP 440: {pep440}")

462

463

# Wildcard constraints

464

wildcard = parse_constraint("1.2.*")

465

print(f"Wildcard: {wildcard}")

466

467

# Test version satisfaction

468

version = Version.parse("1.2.5")

469

print(f"Version {version} satisfies caret: {caret.allows(version)}")

470

print(f"Version {version} satisfies tilde: {tilde.allows(version)}")

471

472

demonstrate_version_constraints()

473

``` { .api }

474

475

### Complex Constraint Operations

476

477

```python

478

from poetry.core.constraints.version import parse_constraint, VersionUnion

479

480

def constraint_operations():

481

"""Demonstrate constraint intersection and union."""

482

483

# Create individual constraints

484

constraint1 = parse_constraint(">=1.0.0")

485

constraint2 = parse_constraint("<3.0.0")

486

constraint3 = parse_constraint("!=2.0.0")

487

488

# Intersection (AND) - must satisfy all

489

intersection = constraint1.intersect(constraint2).intersect(constraint3)

490

print(f"Intersection: {intersection}") # >=1.0.0,<3.0.0,!=2.0.0

491

492

# Union (OR) - can satisfy any

493

union = constraint1.union(constraint3)

494

print(f"Union: {union}")

495

496

# Test complex constraint

497

test_versions = ["0.9.0", "1.5.0", "2.0.0", "2.5.0", "3.5.0"]

498

499

print(f"\nTesting intersection ({intersection}):")

500

for version_str in test_versions:

501

version = Version.parse(version_str)

502

satisfies = intersection.allows(version)

503

print(f" {version}: {'✓' if satisfies else '✗'}")

504

505

constraint_operations()

506

``` { .api }

507

508

### Building Custom Constraints

509

510

```python

511

from poetry.core.constraints.version import Version, VersionRange, VersionUnion

512

513

def build_custom_constraints():

514

"""Build constraints programmatically."""

515

516

# Create version range manually

517

min_version = Version.parse("2.0.0")

518

max_version = Version.parse("3.0.0")

519

520

range1 = VersionRange(

521

min_version=min_version,

522

max_version=max_version,

523

include_min=True, # >=2.0.0

524

include_max=False # <3.0.0

525

)

526

527

# Create another range

528

range2 = VersionRange(

529

min_version=Version.parse("4.0.0"),

530

max_version=Version.parse("5.0.0"),

531

include_min=True,

532

include_max=False

533

)

534

535

# Combine ranges with union

536

combined = VersionUnion(range1, range2)

537

print(f"Combined constraint: {combined}")

538

# Allows: >=2.0.0,<3.0.0 || >=4.0.0,<5.0.0

539

540

# Test versions against combined constraint

541

test_versions = ["1.5.0", "2.5.0", "3.5.0", "4.5.0", "5.5.0"]

542

543

for version_str in test_versions:

544

version = Version.parse(version_str)

545

allowed = combined.allows(version)

546

print(f"{version}: {'allowed' if allowed else 'rejected'}")

547

548

build_custom_constraints()

549

``` { .api }

550

551

### Generic Constraint Usage

552

553

```python

554

from poetry.core.constraints.generic import (

555

parse_constraint,

556

MultiConstraint,

557

UnionConstraint,

558

AnyConstraint

559

)

560

561

def demonstrate_generic_constraints():

562

"""Show generic constraint operations."""

563

564

# Parse simple constraints

565

constraint1 = parse_constraint("development")

566

constraint2 = parse_constraint("testing")

567

568

# Create multi-constraint (AND)

569

both_required = MultiConstraint(constraint1, constraint2)

570

print(f"Must have both: {both_required}")

571

572

# Create union constraint (OR)

573

either_allowed = UnionConstraint(constraint1, constraint2)

574

print(f"Can have either: {either_allowed}")

575

576

# Any constraint (no restrictions)

577

any_constraint = AnyConstraint()

578

print(f"Any allowed: {any_constraint.is_any()}")

579

580

# Test constraint matching

581

print(f"'development' satisfies both_required: {both_required.allows('development')}")

582

print(f"'testing' satisfies either_allowed: {either_allowed.allows('testing')}")

583

584

demonstrate_generic_constraints()

585

``` { .api }

586

587

### Poetry Constraint Syntax

588

589

```python

590

def poetry_syntax_examples():

591

"""Demonstrate Poetry-specific constraint syntax."""

592

593

constraints = {

594

"^1.2.3": "Caret - compatible release (>=1.2.3,<2.0.0)",

595

"~1.2.3": "Tilde - reasonably close (>=1.2.3,<1.3.0)",

596

"^0.2.3": "Caret with 0.x (>=0.2.3,<0.3.0)",

597

"^0.0.3": "Caret with 0.0.x (>=0.0.3,<0.0.4)",

598

"~1.2": "Tilde major.minor (>=1.2.0,<1.3.0)",

599

"1.2.*": "Wildcard (>=1.2.0,<1.3.0)",

600

">=1.2,<2.0": "Range constraint",

601

"!=1.5": "Exclusion constraint"

602

}

603

604

print("Poetry Constraint Syntax:")

605

for syntax, description in constraints.items():

606

try:

607

parsed = parse_constraint(syntax)

608

print(f"{syntax:15} -> {parsed} ({description})")

609

except Exception as e:

610

print(f"{syntax:15} -> ERROR: {e}")

611

612

poetry_syntax_examples()

613

``` { .api }

614

615

## Error Handling

616

617

```python

618

from poetry.core.exceptions import ParseConstraintError

619

from poetry.core.constraints.version import parse_constraint

620

621

def safe_constraint_parsing(constraint_str: str):

622

"""Safely parse constraints with error handling."""

623

624

try:

625

constraint = parse_constraint(constraint_str)

626

return constraint

627

628

except ParseConstraintError as e:

629

print(f"Invalid constraint '{constraint_str}': {e}")

630

return None

631

632

except Exception as e:

633

print(f"Unexpected error parsing '{constraint_str}': {e}")

634

return None

635

636

# Usage

637

constraints_to_test = [

638

"^1.2.0", # Valid

639

">=1.0,<2.0", # Valid

640

"invalid", # Invalid

641

"^", # Invalid

642

"1.2.3.4.5" # Invalid

643

]

644

645

for constraint_str in constraints_to_test:

646

result = safe_constraint_parsing(constraint_str)

647

if result:

648

print(f"✓ {constraint_str} -> {result}")

649

else:

650

print(f"✗ {constraint_str} -> Failed to parse")

651

``` { .api }