0
# PyProject Configuration
1
2
Poetry Core provides robust pyproject.toml parsing and management with full support for Poetry configuration sections and standard build system configuration. This handles both the `[tool.poetry]` section and the standard PEP 517 `[build-system]` configuration.
3
4
## Core Imports
5
6
```python
7
from poetry.core.pyproject.toml import PyProjectTOML
8
from poetry.core.pyproject.tables import BuildSystem
9
from poetry.core.pyproject.exceptions import PyProjectError
10
``` { .api }
11
12
## PyProjectTOML
13
14
Main class for handling pyproject.toml file reading and configuration management.
15
16
### PyProjectTOML.__init__
17
18
```python
19
class PyProjectTOML:
20
def __init__(self, path: Path) -> None:
21
"""
22
Initialize PyProject TOML handler.
23
24
Args:
25
path: Path to pyproject.toml file
26
27
Note:
28
File is loaded lazily on first access to data/configuration.
29
30
Example:
31
>>> from pathlib import Path
32
>>> pyproject = PyProjectTOML(Path("pyproject.toml"))
33
"""
34
``` { .api }
35
36
### Properties
37
38
#### path
39
40
```python
41
@property
42
def path(self) -> Path:
43
"""
44
Path to the pyproject.toml file.
45
46
Returns:
47
Path object pointing to the TOML file
48
49
Example:
50
>>> pyproject = PyProjectTOML(Path("pyproject.toml"))
51
>>> print(pyproject.path)
52
/path/to/project/pyproject.toml
53
"""
54
``` { .api }
55
56
#### data
57
58
```python
59
@property
60
def data(self) -> dict[str, Any]:
61
"""
62
Raw TOML data from the pyproject.toml file.
63
64
Returns:
65
Dictionary containing all TOML sections and data
66
67
Raises:
68
PyProjectError: If TOML file is malformed or cannot be parsed
69
70
Example:
71
>>> pyproject = PyProjectTOML(Path("pyproject.toml"))
72
>>> data = pyproject.data
73
>>> print(data.keys())
74
dict_keys(['build-system', 'tool', 'project'])
75
76
>>> # Access sections
77
>>> build_system = data.get('build-system', {})
78
>>> tool_section = data.get('tool', {})
79
"""
80
``` { .api }
81
82
#### poetry_config
83
84
```python
85
@property
86
def poetry_config(self) -> dict[str, Any]:
87
"""
88
Poetry configuration from [tool.poetry] section.
89
90
Returns:
91
Dictionary containing Poetry-specific configuration
92
93
Raises:
94
PyProjectError: If [tool.poetry] section is not found
95
96
Example:
97
>>> pyproject = PyProjectTOML(Path("pyproject.toml"))
98
>>> config = pyproject.poetry_config
99
>>> print(config['name'])
100
my-package
101
>>> print(config['version'])
102
1.0.0
103
>>> print(config.get('description', ''))
104
A sample package
105
"""
106
``` { .api }
107
108
#### build_system
109
110
```python
111
@property
112
def build_system(self) -> BuildSystem:
113
"""
114
Build system configuration from [build-system] section.
115
116
Returns:
117
BuildSystem object with build backend and requirements
118
119
Note:
120
If no [build-system] section exists, defaults to Poetry Core backend
121
122
Example:
123
>>> pyproject = PyProjectTOML(Path("pyproject.toml"))
124
>>> build_sys = pyproject.build_system
125
>>> print(build_sys.build_backend)
126
poetry.core.masonry.api
127
>>> print(build_sys.requires)
128
['poetry-core']
129
"""
130
``` { .api }
131
132
### Methods
133
134
#### is_poetry_project
135
136
```python
137
def is_poetry_project(self) -> bool:
138
"""
139
Check if project is a valid Poetry project.
140
141
Returns:
142
True if project has [tool.poetry] section or valid [project] metadata
143
144
Note:
145
A project is considered a Poetry project if:
146
1. It has a [tool.poetry] section, OR
147
2. It has [project] section with name and version (no dynamic fields)
148
149
Example:
150
>>> pyproject = PyProjectTOML(Path("pyproject.toml"))
151
>>> if pyproject.is_poetry_project():
152
... print("This is a Poetry project")
153
... else:
154
... print("Not a Poetry project")
155
"""
156
``` { .api }
157
158
#### save
159
160
```python
161
def save(self) -> None:
162
"""
163
Save changes to the pyproject.toml file.
164
165
Raises:
166
IOError: If file cannot be written
167
168
Note:
169
This writes the current data dictionary back to the file.
170
Use with caution as it overwrites the entire file.
171
172
Example:
173
>>> pyproject = PyProjectTOML(Path("pyproject.toml"))
174
>>> data = pyproject.data
175
>>> data['tool']['poetry']['version'] = '2.0.0'
176
>>> pyproject.save() # Write changes back
177
"""
178
``` { .api }
179
180
## BuildSystem
181
182
Represents the [build-system] table configuration according to PEP 517/518.
183
184
### BuildSystem.__init__
185
186
```python
187
class BuildSystem:
188
def __init__(
189
self,
190
build_backend: str | None = None,
191
requires: list[str] | None = None
192
) -> None:
193
"""
194
Create build system configuration.
195
196
Args:
197
build_backend: Build backend module path
198
requires: List of build requirements
199
200
Defaults:
201
build_backend: "setuptools.build_meta:__legacy__" if None
202
requires: ["setuptools", "wheel"] if None
203
204
Example:
205
>>> build_sys = BuildSystem(
206
... build_backend="poetry.core.masonry.api",
207
... requires=["poetry-core"]
208
... )
209
"""
210
``` { .api }
211
212
### Properties
213
214
#### build_backend
215
216
```python
217
@property
218
def build_backend(self) -> str:
219
"""
220
Build backend module path.
221
222
Returns:
223
Module path to PEP 517 build backend
224
225
Example:
226
>>> build_sys.build_backend
227
'poetry.core.masonry.api'
228
"""
229
``` { .api }
230
231
#### requires
232
233
```python
234
@property
235
def requires(self) -> list[str]:
236
"""
237
Build requirements list.
238
239
Returns:
240
List of PEP 508 requirement strings needed for building
241
242
Example:
243
>>> build_sys.requires
244
['poetry-core']
245
"""
246
``` { .api }
247
248
#### dependencies
249
250
```python
251
@property
252
def dependencies(self) -> list[Dependency]:
253
"""
254
Build dependencies as Dependency objects.
255
256
Returns:
257
List of Dependency objects parsed from requires list
258
259
Note:
260
Automatically handles PEP 508 strings, local files, and directories
261
262
Example:
263
>>> deps = build_sys.dependencies
264
>>> for dep in deps:
265
... print(f"{dep.name}: {dep.constraint}")
266
poetry-core: *
267
"""
268
``` { .api }
269
270
## Configuration Examples
271
272
### Loading and Inspecting Configuration
273
274
```python
275
from pathlib import Path
276
from poetry.core.pyproject.toml import PyProjectTOML
277
278
def inspect_pyproject(project_path: Path):
279
"""Inspect pyproject.toml configuration."""
280
281
pyproject_file = project_path / "pyproject.toml"
282
283
try:
284
pyproject = PyProjectTOML(pyproject_file)
285
286
# Check if it's a Poetry project
287
if not pyproject.is_poetry_project():
288
print("β Not a Poetry project")
289
return
290
291
print("β Poetry project detected")
292
print(f"π Config file: {pyproject.path}")
293
294
# Poetry configuration
295
config = pyproject.poetry_config
296
print(f"\nπ¦ Package Information:")
297
print(f" Name: {config.get('name', 'Unknown')}")
298
print(f" Version: {config.get('version', 'Unknown')}")
299
print(f" Description: {config.get('description', 'No description')}")
300
301
# Authors
302
authors = config.get('authors', [])
303
if authors:
304
print(f" Authors: {', '.join(authors)}")
305
306
# Dependencies
307
dependencies = config.get('dependencies', {})
308
if dependencies:
309
print(f"\nπ Dependencies ({len(dependencies)}):")
310
for name, constraint in dependencies.items():
311
print(f" {name}: {constraint}")
312
313
# Development dependencies
314
group_deps = config.get('group', {})
315
if 'dev' in group_deps and 'dependencies' in group_deps['dev']:
316
dev_deps = group_deps['dev']['dependencies']
317
print(f"\nπ οΈ Dev Dependencies ({len(dev_deps)}):")
318
for name, constraint in dev_deps.items():
319
print(f" {name}: {constraint}")
320
321
# Build system
322
build_sys = pyproject.build_system
323
print(f"\nποΈ Build System:")
324
print(f" Backend: {build_sys.build_backend}")
325
print(f" Requires: {', '.join(build_sys.requires)}")
326
327
except Exception as e:
328
print(f"β Error reading configuration: {e}")
329
330
# Usage
331
inspect_pyproject(Path("./my-poetry-project"))
332
``` { .api }
333
334
### Creating Configuration Programmatically
335
336
```python
337
import toml
338
from pathlib import Path
339
from poetry.core.pyproject.toml import PyProjectTOML
340
341
def create_pyproject_config(project_dir: Path):
342
"""Create a new pyproject.toml configuration."""
343
344
config = {
345
"build-system": {
346
"requires": ["poetry-core"],
347
"build-backend": "poetry.core.masonry.api"
348
},
349
"tool": {
350
"poetry": {
351
"name": "my-new-package",
352
"version": "0.1.0",
353
"description": "A new Python package",
354
"authors": ["Your Name <you@example.com>"],
355
"readme": "README.md",
356
"packages": [{"include": "my_new_package"}],
357
"dependencies": {
358
"python": "^3.8"
359
},
360
"group": {
361
"dev": {
362
"dependencies": {
363
"pytest": "^7.0.0",
364
"black": "^22.0.0",
365
"isort": "^5.0.0"
366
}
367
}
368
}
369
}
370
}
371
}
372
373
# Write to file
374
pyproject_file = project_dir / "pyproject.toml"
375
with pyproject_file.open("w") as f:
376
toml.dump(config, f)
377
378
# Verify with PyProjectTOML
379
pyproject = PyProjectTOML(pyproject_file)
380
print(f"β Created Poetry project: {pyproject.poetry_config['name']}")
381
382
return pyproject
383
384
# Usage
385
project = create_pyproject_config(Path("./new-project"))
386
``` { .api }
387
388
### Configuration Validation
389
390
```python
391
from poetry.core.factory import Factory
392
from poetry.core.pyproject.toml import PyProjectTOML
393
394
def validate_pyproject_config(pyproject_path: Path):
395
"""Validate Poetry configuration against schemas."""
396
397
try:
398
pyproject = PyProjectTOML(pyproject_path)
399
400
if not pyproject.is_poetry_project():
401
print("β Not a Poetry project")
402
return False
403
404
# Validate using Factory
405
validation_result = Factory.validate(pyproject.data)
406
407
# Report errors
408
if validation_result["errors"]:
409
print("β Configuration Errors:")
410
for error in validation_result["errors"]:
411
print(f" β’ {error}")
412
413
# Report warnings
414
if validation_result["warnings"]:
415
print("β οΈ Configuration Warnings:")
416
for warning in validation_result["warnings"]:
417
print(f" β’ {warning}")
418
419
# Overall status
420
is_valid = not validation_result["errors"]
421
if is_valid:
422
print("β Configuration is valid")
423
else:
424
print("β Configuration has errors")
425
426
return is_valid
427
428
except Exception as e:
429
print(f"β Validation failed: {e}")
430
return False
431
432
# Usage
433
is_valid = validate_pyproject_config(Path("pyproject.toml"))
434
``` { .api }
435
436
### Working with Different Configuration Sections
437
438
```python
439
from poetry.core.pyproject.toml import PyProjectTOML
440
441
def explore_config_sections(pyproject_path: Path):
442
"""Explore different sections of pyproject.toml."""
443
444
pyproject = PyProjectTOML(pyproject_path)
445
data = pyproject.data
446
447
print("π PyProject Configuration Sections:")
448
print("=" * 50)
449
450
# Build system section
451
if "build-system" in data:
452
build_sys = data["build-system"]
453
print("ποΈ [build-system]")
454
print(f" requires = {build_sys.get('requires', [])}")
455
print(f" build-backend = '{build_sys.get('build-backend', '')}'")
456
print()
457
458
# Project section (PEP 621)
459
if "project" in data:
460
project = data["project"]
461
print("π¦ [project]")
462
print(f" name = '{project.get('name', '')}'")
463
print(f" version = '{project.get('version', '')}'")
464
print(f" description = '{project.get('description', '')}'")
465
if "dependencies" in project:
466
print(f" dependencies = {project['dependencies']}")
467
print()
468
469
# Tool sections
470
if "tool" in data:
471
tool = data["tool"]
472
print("π§ [tool] sections:")
473
474
for tool_name in tool.keys():
475
print(f" β’ tool.{tool_name}")
476
477
# Poetry-specific configuration
478
if "poetry" in tool:
479
poetry_config = tool["poetry"]
480
print(f"\nπ [tool.poetry]")
481
print(f" name = '{poetry_config.get('name', '')}'")
482
print(f" version = '{poetry_config.get('version', '')}'")
483
484
# Dependencies
485
if "dependencies" in poetry_config:
486
deps = poetry_config["dependencies"]
487
print(f" dependencies = {dict(list(deps.items())[:3])}...")
488
489
# Dependency groups
490
if "group" in poetry_config:
491
groups = poetry_config["group"]
492
print(f" dependency groups: {list(groups.keys())}")
493
494
# Scripts and entry points
495
if "scripts" in poetry_config:
496
scripts = poetry_config["scripts"]
497
print(f" scripts: {list(scripts.keys())}")
498
499
print("\n" + "=" * 50)
500
501
# Usage
502
explore_config_sections(Path("pyproject.toml"))
503
``` { .api }
504
505
### Dynamic Configuration Updates
506
507
```python
508
from poetry.core.pyproject.toml import PyProjectTOML
509
510
def update_pyproject_config(pyproject_path: Path):
511
"""Demonstrate dynamic configuration updates."""
512
513
pyproject = PyProjectTOML(pyproject_path)
514
515
# Get current data
516
data = pyproject.data
517
poetry_config = pyproject.poetry_config
518
519
print(f"Current version: {poetry_config.get('version')}")
520
521
# Update version
522
new_version = "2.0.0"
523
if "tool" not in data:
524
data["tool"] = {}
525
if "poetry" not in data["tool"]:
526
data["tool"]["poetry"] = {}
527
528
data["tool"]["poetry"]["version"] = new_version
529
530
# Add new dependency
531
if "dependencies" not in data["tool"]["poetry"]:
532
data["tool"]["poetry"]["dependencies"] = {}
533
534
data["tool"]["poetry"]["dependencies"]["requests"] = "^2.28.0"
535
536
# Add development dependency
537
if "group" not in data["tool"]["poetry"]:
538
data["tool"]["poetry"]["group"] = {}
539
if "dev" not in data["tool"]["poetry"]["group"]:
540
data["tool"]["poetry"]["group"]["dev"] = {"dependencies": {}}
541
542
data["tool"]["poetry"]["group"]["dev"]["dependencies"]["pytest"] = "^7.0.0"
543
544
# Update metadata
545
data["tool"]["poetry"]["description"] = "Updated package description"
546
data["tool"]["poetry"]["keywords"] = ["python", "package", "updated"]
547
548
print(f"β Updated configuration:")
549
print(f" Version: {data['tool']['poetry']['version']}")
550
print(f" Description: {data['tool']['poetry']['description']}")
551
print(f" New dependencies: {list(data['tool']['poetry']['dependencies'].keys())}")
552
553
# Save changes (uncomment to actually save)
554
# pyproject.save()
555
# print("πΎ Changes saved to pyproject.toml")
556
557
# Usage
558
update_pyproject_config(Path("pyproject.toml"))
559
``` { .api }
560
561
## Error Handling
562
563
### Configuration Error Handling
564
565
```python
566
from poetry.core.pyproject.toml import PyProjectTOML
567
from poetry.core.pyproject.exceptions import PyProjectError
568
569
def safe_config_loading(pyproject_path: Path):
570
"""Safely load configuration with comprehensive error handling."""
571
572
try:
573
# Check if file exists
574
if not pyproject_path.exists():
575
print(f"β File not found: {pyproject_path}")
576
return None
577
578
pyproject = PyProjectTOML(pyproject_path)
579
580
# Try to access data (triggers parsing)
581
try:
582
data = pyproject.data
583
print(f"β Successfully loaded TOML data")
584
585
except PyProjectError as e:
586
print(f"β TOML parsing error: {e}")
587
return None
588
589
# Try to access Poetry config
590
try:
591
poetry_config = pyproject.poetry_config
592
print(f"β Found Poetry configuration")
593
594
except PyProjectError as e:
595
print(f"β οΈ No Poetry configuration: {e}")
596
# Could still be a valid project with [project] section
597
598
# Try to access build system
599
try:
600
build_sys = pyproject.build_system
601
print(f"β Build system: {build_sys.build_backend}")
602
603
except Exception as e:
604
print(f"β οΈ Build system error: {e}")
605
606
return pyproject
607
608
except Exception as e:
609
print(f"β Unexpected error: {e}")
610
return None
611
612
def validate_required_fields(pyproject: PyProjectTOML):
613
"""Validate required configuration fields."""
614
615
try:
616
config = pyproject.poetry_config
617
618
# Required fields
619
required_fields = ["name", "version"]
620
missing_fields = []
621
622
for field in required_fields:
623
if field not in config:
624
missing_fields.append(field)
625
626
if missing_fields:
627
print(f"β Missing required fields: {missing_fields}")
628
return False
629
630
# Validate field formats
631
name = config["name"]
632
version = config["version"]
633
634
if not isinstance(name, str) or not name.strip():
635
print(f"β Invalid name: '{name}'")
636
return False
637
638
if not isinstance(version, str) or not version.strip():
639
print(f"β Invalid version: '{version}'")
640
return False
641
642
print(f"β Required fields valid: {name} v{version}")
643
return True
644
645
except PyProjectError:
646
print("β Cannot validate - no Poetry configuration")
647
return False
648
649
# Usage
650
pyproject = safe_config_loading(Path("pyproject.toml"))
651
if pyproject:
652
validate_required_fields(pyproject)
653
``` { .api }
654
655
## Type Definitions
656
657
```python
658
from typing import Any, Dict
659
from pathlib import Path
660
661
# Configuration types
662
ConfigDict = Dict[str, Any]
663
TomlData = Dict[str, Any]
664
PoetryConfig = Dict[str, Any]
665
666
# Build system types
667
BuildBackend = str
668
BuildRequirements = List[str]
669
``` { .api }