Poetry PEP 517 Build Backend for building Python packages with lightweight, compliant, self-contained build system
—
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.
from poetry.core.pyproject.toml import PyProjectTOML
from poetry.core.pyproject.tables import BuildSystem
from poetry.core.pyproject.exceptions import PyProjectError
``` { .api }
## PyProjectTOML
Main class for handling pyproject.toml file reading and configuration management.
### PyProjectTOML.__init__
```python
class PyProjectTOML:
def __init__(self, path: Path) -> None:
"""
Initialize PyProject TOML handler.
Args:
path: Path to pyproject.toml file
Note:
File is loaded lazily on first access to data/configuration.
Example:
>>> from pathlib import Path
>>> pyproject = PyProjectTOML(Path("pyproject.toml"))
"""
``` { .api }
### Properties
#### path
```python
@property
def path(self) -> Path:
"""
Path to the pyproject.toml file.
Returns:
Path object pointing to the TOML file
Example:
>>> pyproject = PyProjectTOML(Path("pyproject.toml"))
>>> print(pyproject.path)
/path/to/project/pyproject.toml
"""
``` { .api }
#### data
```python
@property
def data(self) -> dict[str, Any]:
"""
Raw TOML data from the pyproject.toml file.
Returns:
Dictionary containing all TOML sections and data
Raises:
PyProjectError: If TOML file is malformed or cannot be parsed
Example:
>>> pyproject = PyProjectTOML(Path("pyproject.toml"))
>>> data = pyproject.data
>>> print(data.keys())
dict_keys(['build-system', 'tool', 'project'])
>>> # Access sections
>>> build_system = data.get('build-system', {})
>>> tool_section = data.get('tool', {})
"""
``` { .api }
#### poetry_config
```python
@property
def poetry_config(self) -> dict[str, Any]:
"""
Poetry configuration from [tool.poetry] section.
Returns:
Dictionary containing Poetry-specific configuration
Raises:
PyProjectError: If [tool.poetry] section is not found
Example:
>>> pyproject = PyProjectTOML(Path("pyproject.toml"))
>>> config = pyproject.poetry_config
>>> print(config['name'])
my-package
>>> print(config['version'])
1.0.0
>>> print(config.get('description', ''))
A sample package
"""
``` { .api }
#### build_system
```python
@property
def build_system(self) -> BuildSystem:
"""
Build system configuration from [build-system] section.
Returns:
BuildSystem object with build backend and requirements
Note:
If no [build-system] section exists, defaults to Poetry Core backend
Example:
>>> pyproject = PyProjectTOML(Path("pyproject.toml"))
>>> build_sys = pyproject.build_system
>>> print(build_sys.build_backend)
poetry.core.masonry.api
>>> print(build_sys.requires)
['poetry-core']
"""
``` { .api }
### Methods
#### is_poetry_project
```python
def is_poetry_project(self) -> bool:
"""
Check if project is a valid Poetry project.
Returns:
True if project has [tool.poetry] section or valid [project] metadata
Note:
A project is considered a Poetry project if:
1. It has a [tool.poetry] section, OR
2. It has [project] section with name and version (no dynamic fields)
Example:
>>> pyproject = PyProjectTOML(Path("pyproject.toml"))
>>> if pyproject.is_poetry_project():
... print("This is a Poetry project")
... else:
... print("Not a Poetry project")
"""
``` { .api }
#### save
```python
def save(self) -> None:
"""
Save changes to the pyproject.toml file.
Raises:
IOError: If file cannot be written
Note:
This writes the current data dictionary back to the file.
Use with caution as it overwrites the entire file.
Example:
>>> pyproject = PyProjectTOML(Path("pyproject.toml"))
>>> data = pyproject.data
>>> data['tool']['poetry']['version'] = '2.0.0'
>>> pyproject.save() # Write changes back
"""
``` { .api }
## BuildSystem
Represents the [build-system] table configuration according to PEP 517/518.
### BuildSystem.__init__
```python
class BuildSystem:
def __init__(
self,
build_backend: str | None = None,
requires: list[str] | None = None
) -> None:
"""
Create build system configuration.
Args:
build_backend: Build backend module path
requires: List of build requirements
Defaults:
build_backend: "setuptools.build_meta:__legacy__" if None
requires: ["setuptools", "wheel"] if None
Example:
>>> build_sys = BuildSystem(
... build_backend="poetry.core.masonry.api",
... requires=["poetry-core"]
... )
"""
``` { .api }
### Properties
#### build_backend
```python
@property
def build_backend(self) -> str:
"""
Build backend module path.
Returns:
Module path to PEP 517 build backend
Example:
>>> build_sys.build_backend
'poetry.core.masonry.api'
"""
``` { .api }
#### requires
```python
@property
def requires(self) -> list[str]:
"""
Build requirements list.
Returns:
List of PEP 508 requirement strings needed for building
Example:
>>> build_sys.requires
['poetry-core']
"""
``` { .api }
#### dependencies
```python
@property
def dependencies(self) -> list[Dependency]:
"""
Build dependencies as Dependency objects.
Returns:
List of Dependency objects parsed from requires list
Note:
Automatically handles PEP 508 strings, local files, and directories
Example:
>>> deps = build_sys.dependencies
>>> for dep in deps:
... print(f"{dep.name}: {dep.constraint}")
poetry-core: *
"""
``` { .api }
## Configuration Examples
### Loading and Inspecting Configuration
```python
from pathlib import Path
from poetry.core.pyproject.toml import PyProjectTOML
def inspect_pyproject(project_path: Path):
"""Inspect pyproject.toml configuration."""
pyproject_file = project_path / "pyproject.toml"
try:
pyproject = PyProjectTOML(pyproject_file)
# Check if it's a Poetry project
if not pyproject.is_poetry_project():
print("❌ Not a Poetry project")
return
print("✅ Poetry project detected")
print(f"📁 Config file: {pyproject.path}")
# Poetry configuration
config = pyproject.poetry_config
print(f"\n📦 Package Information:")
print(f" Name: {config.get('name', 'Unknown')}")
print(f" Version: {config.get('version', 'Unknown')}")
print(f" Description: {config.get('description', 'No description')}")
# Authors
authors = config.get('authors', [])
if authors:
print(f" Authors: {', '.join(authors)}")
# Dependencies
dependencies = config.get('dependencies', {})
if dependencies:
print(f"\n🔗 Dependencies ({len(dependencies)}):")
for name, constraint in dependencies.items():
print(f" {name}: {constraint}")
# Development dependencies
group_deps = config.get('group', {})
if 'dev' in group_deps and 'dependencies' in group_deps['dev']:
dev_deps = group_deps['dev']['dependencies']
print(f"\n🛠️ Dev Dependencies ({len(dev_deps)}):")
for name, constraint in dev_deps.items():
print(f" {name}: {constraint}")
# Build system
build_sys = pyproject.build_system
print(f"\n🏗️ Build System:")
print(f" Backend: {build_sys.build_backend}")
print(f" Requires: {', '.join(build_sys.requires)}")
except Exception as e:
print(f"❌ Error reading configuration: {e}")
# Usage
inspect_pyproject(Path("./my-poetry-project"))
``` { .api }
### Creating Configuration Programmatically
```python
import toml
from pathlib import Path
from poetry.core.pyproject.toml import PyProjectTOML
def create_pyproject_config(project_dir: Path):
"""Create a new pyproject.toml configuration."""
config = {
"build-system": {
"requires": ["poetry-core"],
"build-backend": "poetry.core.masonry.api"
},
"tool": {
"poetry": {
"name": "my-new-package",
"version": "0.1.0",
"description": "A new Python package",
"authors": ["Your Name <you@example.com>"],
"readme": "README.md",
"packages": [{"include": "my_new_package"}],
"dependencies": {
"python": "^3.8"
},
"group": {
"dev": {
"dependencies": {
"pytest": "^7.0.0",
"black": "^22.0.0",
"isort": "^5.0.0"
}
}
}
}
}
}
# Write to file
pyproject_file = project_dir / "pyproject.toml"
with pyproject_file.open("w") as f:
toml.dump(config, f)
# Verify with PyProjectTOML
pyproject = PyProjectTOML(pyproject_file)
print(f"✅ Created Poetry project: {pyproject.poetry_config['name']}")
return pyproject
# Usage
project = create_pyproject_config(Path("./new-project"))
``` { .api }
### Configuration Validation
```python
from poetry.core.factory import Factory
from poetry.core.pyproject.toml import PyProjectTOML
def validate_pyproject_config(pyproject_path: Path):
"""Validate Poetry configuration against schemas."""
try:
pyproject = PyProjectTOML(pyproject_path)
if not pyproject.is_poetry_project():
print("❌ Not a Poetry project")
return False
# Validate using Factory
validation_result = Factory.validate(pyproject.data)
# Report errors
if validation_result["errors"]:
print("❌ Configuration Errors:")
for error in validation_result["errors"]:
print(f" • {error}")
# Report warnings
if validation_result["warnings"]:
print("⚠️ Configuration Warnings:")
for warning in validation_result["warnings"]:
print(f" • {warning}")
# Overall status
is_valid = not validation_result["errors"]
if is_valid:
print("✅ Configuration is valid")
else:
print("❌ Configuration has errors")
return is_valid
except Exception as e:
print(f"❌ Validation failed: {e}")
return False
# Usage
is_valid = validate_pyproject_config(Path("pyproject.toml"))
``` { .api }
### Working with Different Configuration Sections
```python
from poetry.core.pyproject.toml import PyProjectTOML
def explore_config_sections(pyproject_path: Path):
"""Explore different sections of pyproject.toml."""
pyproject = PyProjectTOML(pyproject_path)
data = pyproject.data
print("📋 PyProject Configuration Sections:")
print("=" * 50)
# Build system section
if "build-system" in data:
build_sys = data["build-system"]
print("🏗️ [build-system]")
print(f" requires = {build_sys.get('requires', [])}")
print(f" build-backend = '{build_sys.get('build-backend', '')}'")
print()
# Project section (PEP 621)
if "project" in data:
project = data["project"]
print("📦 [project]")
print(f" name = '{project.get('name', '')}'")
print(f" version = '{project.get('version', '')}'")
print(f" description = '{project.get('description', '')}'")
if "dependencies" in project:
print(f" dependencies = {project['dependencies']}")
print()
# Tool sections
if "tool" in data:
tool = data["tool"]
print("🔧 [tool] sections:")
for tool_name in tool.keys():
print(f" • tool.{tool_name}")
# Poetry-specific configuration
if "poetry" in tool:
poetry_config = tool["poetry"]
print(f"\n🎭 [tool.poetry]")
print(f" name = '{poetry_config.get('name', '')}'")
print(f" version = '{poetry_config.get('version', '')}'")
# Dependencies
if "dependencies" in poetry_config:
deps = poetry_config["dependencies"]
print(f" dependencies = {dict(list(deps.items())[:3])}...")
# Dependency groups
if "group" in poetry_config:
groups = poetry_config["group"]
print(f" dependency groups: {list(groups.keys())}")
# Scripts and entry points
if "scripts" in poetry_config:
scripts = poetry_config["scripts"]
print(f" scripts: {list(scripts.keys())}")
print("\n" + "=" * 50)
# Usage
explore_config_sections(Path("pyproject.toml"))
``` { .api }
### Dynamic Configuration Updates
```python
from poetry.core.pyproject.toml import PyProjectTOML
def update_pyproject_config(pyproject_path: Path):
"""Demonstrate dynamic configuration updates."""
pyproject = PyProjectTOML(pyproject_path)
# Get current data
data = pyproject.data
poetry_config = pyproject.poetry_config
print(f"Current version: {poetry_config.get('version')}")
# Update version
new_version = "2.0.0"
if "tool" not in data:
data["tool"] = {}
if "poetry" not in data["tool"]:
data["tool"]["poetry"] = {}
data["tool"]["poetry"]["version"] = new_version
# Add new dependency
if "dependencies" not in data["tool"]["poetry"]:
data["tool"]["poetry"]["dependencies"] = {}
data["tool"]["poetry"]["dependencies"]["requests"] = "^2.28.0"
# Add development dependency
if "group" not in data["tool"]["poetry"]:
data["tool"]["poetry"]["group"] = {}
if "dev" not in data["tool"]["poetry"]["group"]:
data["tool"]["poetry"]["group"]["dev"] = {"dependencies": {}}
data["tool"]["poetry"]["group"]["dev"]["dependencies"]["pytest"] = "^7.0.0"
# Update metadata
data["tool"]["poetry"]["description"] = "Updated package description"
data["tool"]["poetry"]["keywords"] = ["python", "package", "updated"]
print(f"✅ Updated configuration:")
print(f" Version: {data['tool']['poetry']['version']}")
print(f" Description: {data['tool']['poetry']['description']}")
print(f" New dependencies: {list(data['tool']['poetry']['dependencies'].keys())}")
# Save changes (uncomment to actually save)
# pyproject.save()
# print("💾 Changes saved to pyproject.toml")
# Usage
update_pyproject_config(Path("pyproject.toml"))
``` { .api }
## Error Handling
### Configuration Error Handling
```python
from poetry.core.pyproject.toml import PyProjectTOML
from poetry.core.pyproject.exceptions import PyProjectError
def safe_config_loading(pyproject_path: Path):
"""Safely load configuration with comprehensive error handling."""
try:
# Check if file exists
if not pyproject_path.exists():
print(f"❌ File not found: {pyproject_path}")
return None
pyproject = PyProjectTOML(pyproject_path)
# Try to access data (triggers parsing)
try:
data = pyproject.data
print(f"✅ Successfully loaded TOML data")
except PyProjectError as e:
print(f"❌ TOML parsing error: {e}")
return None
# Try to access Poetry config
try:
poetry_config = pyproject.poetry_config
print(f"✅ Found Poetry configuration")
except PyProjectError as e:
print(f"⚠️ No Poetry configuration: {e}")
# Could still be a valid project with [project] section
# Try to access build system
try:
build_sys = pyproject.build_system
print(f"✅ Build system: {build_sys.build_backend}")
except Exception as e:
print(f"⚠️ Build system error: {e}")
return pyproject
except Exception as e:
print(f"❌ Unexpected error: {e}")
return None
def validate_required_fields(pyproject: PyProjectTOML):
"""Validate required configuration fields."""
try:
config = pyproject.poetry_config
# Required fields
required_fields = ["name", "version"]
missing_fields = []
for field in required_fields:
if field not in config:
missing_fields.append(field)
if missing_fields:
print(f"❌ Missing required fields: {missing_fields}")
return False
# Validate field formats
name = config["name"]
version = config["version"]
if not isinstance(name, str) or not name.strip():
print(f"❌ Invalid name: '{name}'")
return False
if not isinstance(version, str) or not version.strip():
print(f"❌ Invalid version: '{version}'")
return False
print(f"✅ Required fields valid: {name} v{version}")
return True
except PyProjectError:
print("❌ Cannot validate - no Poetry configuration")
return False
# Usage
pyproject = safe_config_loading(Path("pyproject.toml"))
if pyproject:
validate_required_fields(pyproject)
``` { .api }
## Type Definitions
```python
from typing import Any, Dict
from pathlib import Path
# Configuration types
ConfigDict = Dict[str, Any]
TomlData = Dict[str, Any]
PoetryConfig = Dict[str, Any]
# Build system types
BuildBackend = str
BuildRequirements = List[str]
``` { .api }Install with Tessl CLI
npx tessl i tessl/pypi-poetry-core