Poetry PEP 517 Build Backend for building Python packages with lightweight, compliant, self-contained build system
—
Poetry Core provides a modern package building system supporting wheels, source distributions (sdists), and editable installs. The builders handle file collection, metadata generation, and package creation according to Python packaging standards.
from poetry.core.masonry.builders.builder import Builder
from poetry.core.masonry.builders.wheel import WheelBuilder
from poetry.core.masonry.builders.sdist import SdistBuilder
from poetry.core.masonry.metadata import Metadata
``` { .api }
## Base Builder Class
### Builder
Abstract base class for all package builders providing common functionality.
```python
class Builder:
"""
Abstract base class for package builders.
Provides common functionality for file discovery, exclusion handling,
and metadata generation across different package formats.
"""
format: str | None = None # Format identifier ("wheel", "sdist", etc.)
def __init__(
self,
poetry: Poetry,
executable: Path | None = None,
config_settings: dict[str, Any] | None = None,
) -> None:
"""
Initialize builder.
Args:
poetry: Poetry instance representing the project
executable: Python executable to use (defaults to current)
config_settings: Build configuration settings
Raises:
RuntimeError: If project is not in package mode
Example:
>>> from poetry.core.factory import Factory
>>> poetry = Factory().create_poetry()
>>> builder = SdistBuilder(poetry)
"""
@property
def executable(self) -> Path:
"""Python executable used for building."""
@property
def default_target_dir(self) -> Path:
"""Default directory for build outputs (typically ./dist)."""
def build(self, target_dir: Path | None) -> Path:
"""
Build package to target directory.
Args:
target_dir: Directory to write package file
Returns:
Path to built package file
Note:
Must be implemented by subclasses
"""
raise NotImplementedError
def find_files_to_add(self) -> list[BuildIncludeFile]:
"""
Find all files to include in the package.
Returns:
List of files to add to package with their metadata
Note:
Handles package discovery, file includes/excludes,
and VCS integration for determining files.
"""
def find_excluded_files(self, fmt: str | None = None) -> set[str]:
"""
Find files excluded from packaging.
Args:
fmt: Format to check exclusions for
Returns:
Set of excluded file paths
Note:
Uses VCS ignore files, Poetry configuration excludes,
and format-specific exclusion rules.
"""
``` { .api }
## Wheel Builder
### WheelBuilder
Builds wheel packages according to PEP 427.
```python
class WheelBuilder(Builder):
"""
Wheel package builder implementing PEP 427.
Creates .whl files containing compiled Python packages
with proper metadata and installation support.
"""
format = "wheel"
def __init__(
self,
poetry: Poetry,
original: Path | None = None,
executable: Path | None = None,
editable: bool = False,
metadata_directory: Path | None = None,
config_settings: dict[str, Any] | None = None,
) -> None:
"""
Initialize wheel builder.
Args:
poetry: Poetry project instance
original: Original project path (for advanced scenarios)
executable: Python executable for building
editable: Whether to build editable wheel
metadata_directory: Pre-built metadata directory
config_settings: Build configuration settings
Example:
>>> builder = WheelBuilder(poetry, editable=True)
>>> builder = WheelBuilder(poetry, config_settings={"--build-option": ["--plat-name", "linux_x86_64"]})
"""
``` { .api }
### Class Methods
#### make_in
```python
@classmethod
def make_in(
cls,
poetry: Poetry,
directory: Path | None = None,
original: Path | None = None,
executable: Path | None = None,
editable: bool = False,
metadata_directory: Path | None = None,
config_settings: dict[str, Any] | None = None,
) -> str:
"""
Build wheel in specified directory (class method).
Args:
poetry: Poetry project instance
directory: Target directory for wheel file
original: Original project path
executable: Python executable for building
editable: Whether to build editable wheel
metadata_directory: Pre-built metadata directory
config_settings: Build configuration settings
Returns:
Filename of built wheel
Example:
>>> filename = WheelBuilder.make_in(poetry, Path("./dist"))
>>> print(f"Built: {filename}")
Built: mypackage-1.0.0-py3-none-any.whl
>>> # Editable wheel
>>> filename = WheelBuilder.make_in(poetry, Path("./dist"), editable=True)
>>> # With configuration
>>> filename = WheelBuilder.make_in(
... poetry,
... Path("./dist"),
... config_settings={"--build-option": ["--verbose"]}
... )
"""
``` { .api }
#### make
```python
@classmethod
def make(cls, poetry: Poetry, executable: Path | None = None) -> None:
"""
Build wheel in default location (./dist).
Args:
poetry: Poetry project instance
executable: Python executable for building
Example:
>>> WheelBuilder.make(poetry)
# Creates wheel in ./dist/ directory
"""
``` { .api }
### Properties
```python
@property
def wheel_filename(self) -> str:
"""
Generated wheel filename.
Returns:
Wheel filename following PEP 427 naming convention
Format: {name}-{version}-{python tag}-{abi tag}-{platform tag}.whl
Example:
>>> builder.wheel_filename
'mypackage-1.0.0-py3-none-any.whl'
"""
@property
def tag(self) -> packaging.tags.Tag:
"""
Wheel compatibility tag.
Returns:
Tag object describing Python/ABI/platform compatibility
"""
@property
def supports_tags(self) -> list[packaging.tags.Tag]:
"""List of all compatible tags for this wheel."""
``` { .api }
### Methods
```python
def build(self, target_dir: Path | None = None) -> Path:
"""
Build wheel package.
Args:
target_dir: Directory to write wheel file
Returns:
Path to built wheel file
Example:
>>> wheel_path = builder.build(Path("./dist"))
>>> print(f"Wheel created: {wheel_path}")
"""
def prepare_metadata(self, metadata_dir: Path) -> Path:
"""
Prepare wheel metadata without building full wheel.
Args:
metadata_dir: Directory to write metadata
Returns:
Path to .dist-info directory containing metadata
Note:
Used by PEP 517 prepare_metadata_for_build_wheel hook
for faster installation planning.
"""
``` { .api }
## Source Distribution Builder
### SdistBuilder
Builds source distribution packages according to PEP 518.
```python
class SdistBuilder(Builder):
"""
Source distribution builder implementing PEP 518.
Creates .tar.gz files containing source code and metadata
for distribution and installation from source.
"""
format = "sdist"
def __init__(
self,
poetry: Poetry,
executable: Path | None = None,
config_settings: dict[str, Any] | None = None,
) -> None:
"""
Initialize sdist builder.
Args:
poetry: Poetry project instance
executable: Python executable for building
config_settings: Build configuration settings
Example:
>>> builder = SdistBuilder(poetry)
>>> builder = SdistBuilder(poetry, config_settings={"--formats": ["gztar"]})
"""
``` { .api }
### Methods
```python
def build(self, target_dir: Path | None = None) -> Path:
"""
Build source distribution package.
Args:
target_dir: Directory to write sdist file
Returns:
Path to built sdist file (.tar.gz)
Example:
>>> sdist_path = builder.build(Path("./dist"))
>>> print(f"Sdist created: {sdist_path}")
Sdist created: ./dist/mypackage-1.0.0.tar.gz
"""
@property
def sdist_filename(self) -> str:
"""
Generated sdist filename.
Returns:
Sdist filename in format: {name}-{version}.tar.gz
Example:
>>> builder.sdist_filename
'mypackage-1.0.0.tar.gz'
"""
``` { .api }
## Metadata Handling
### Metadata
Handles package metadata generation for distributions.
```python
class Metadata:
"""
Package metadata generator for distributions.
Creates standard metadata files (METADATA, WHEEL, entry_points.txt)
according to packaging specifications.
"""
@classmethod
def from_package(cls, package: ProjectPackage) -> Metadata:
"""
Create metadata from package information.
Args:
package: Project package instance
Returns:
Metadata instance ready for distribution generation
"""
def write_to_directory(
self,
directory: Path,
fmt: str | None = None
) -> None:
"""
Write metadata files to directory.
Args:
directory: Target directory (typically .dist-info or .egg-info)
fmt: Format context ("wheel" or "sdist")
Note:
Creates standard metadata files:
- METADATA (core metadata)
- WHEEL (wheel-specific, for wheels only)
- entry_points.txt (if entry points defined)
- RECORD (for wheels, file hashes and sizes)
"""
``` { .api }
## Build Configuration
### Configuration Options
```python
# Common config_settings for builders
config_settings = {
# Build options
"--build-option": ["--verbose", "--plat-name", "linux_x86_64"],
# Global options
"--global-option": ["--quiet"],
# Format-specific options
"--formats": ["gztar"], # For sdist
# Local version label
"local-version": "dev123",
# Environment variables
"env": {
"POETRY_CORE_DEBUG": "1",
"MAKEFLAGS": "-j4"
}
}
``` { .api }
## Usage Examples
### Basic Package Building
```python
from pathlib import Path
from poetry.core.factory import Factory
from poetry.core.masonry.builders.wheel import WheelBuilder
from poetry.core.masonry.builders.sdist import SdistBuilder
def build_project(project_path: Path, output_dir: Path):
"""Build both wheel and sdist for a project."""
# Create Poetry instance
factory = Factory()
poetry = factory.create_poetry(project_path)
# Create output directory
output_dir.mkdir(exist_ok=True, parents=True)
print(f"Building {poetry.package.name} v{poetry.package.version}")
try:
# Build wheel
print("Building wheel...")
wheel_builder = WheelBuilder(poetry)
wheel_path = wheel_builder.build(output_dir)
print(f"✅ Wheel: {wheel_path.name}")
# Build source distribution
print("Building sdist...")
sdist_builder = SdistBuilder(poetry)
sdist_path = sdist_builder.build(output_dir)
print(f"✅ Sdist: {sdist_path.name}")
return wheel_path, sdist_path
except Exception as e:
print(f"❌ Build failed: {e}")
return None, None
# Usage
wheel, sdist = build_project(Path("./my-project"), Path("./dist"))
``` { .api }
### Advanced Wheel Building
```python
from poetry.core.masonry.builders.wheel import WheelBuilder
def build_specialized_wheels(poetry):
"""Build wheels with different configurations."""
dist_dir = Path("./dist")
dist_dir.mkdir(exist_ok=True)
# Regular wheel
print("Building regular wheel...")
regular_wheel = WheelBuilder.make_in(poetry, dist_dir)
print(f"Regular wheel: {regular_wheel}")
# Editable wheel (for development)
print("Building editable wheel...")
editable_wheel = WheelBuilder.make_in(
poetry,
dist_dir,
editable=True
)
print(f"Editable wheel: {editable_wheel}")
# Platform-specific wheel
print("Building platform-specific wheel...")
platform_config = {
"--build-option": ["--plat-name", "linux_x86_64"]
}
platform_wheel = WheelBuilder.make_in(
poetry,
dist_dir,
config_settings=platform_config
)
print(f"Platform wheel: {platform_wheel}")
# Development version wheel
print("Building development wheel...")
dev_config = {
"local-version": f"dev{datetime.now().strftime('%Y%m%d')}"
}
dev_wheel = WheelBuilder.make_in(
poetry,
dist_dir,
config_settings=dev_config
)
print(f"Dev wheel: {dev_wheel}")
# Usage
build_specialized_wheels(poetry)
``` { .api }
### Metadata Preparation
```python
from pathlib import Path
from poetry.core.masonry.builders.wheel import WheelBuilder
def prepare_metadata_only(poetry, metadata_dir: Path):
"""Prepare wheel metadata without building full wheel."""
metadata_dir.mkdir(exist_ok=True, parents=True)
# Create wheel builder
builder = WheelBuilder(poetry)
# Prepare metadata
dist_info_dir = builder.prepare_metadata(metadata_dir)
print(f"Metadata prepared in: {dist_info_dir}")
# List metadata files
print("Metadata files:")
for file in dist_info_dir.iterdir():
print(f" {file.name}")
# Read core metadata
metadata_file = dist_info_dir / "METADATA"
if metadata_file.exists():
print(f"\nCore metadata preview:")
print(metadata_file.read_text()[:500] + "...")
return dist_info_dir
# Usage
metadata_dir = prepare_metadata_only(poetry, Path("./metadata"))
``` { .api }
### Custom Build Process
```python
from poetry.core.masonry.builders.wheel import WheelBuilder
from poetry.core.masonry.builders.sdist import SdistBuilder
class CustomBuilder:
"""Custom builder with additional processing."""
def __init__(self, poetry):
self.poetry = poetry
def build_with_checks(self, target_dir: Path):
"""Build packages with pre/post checks."""
# Pre-build validation
print("🔍 Pre-build validation...")
if not self._validate_project():
print("❌ Validation failed")
return
# Build packages
print("🏗️ Building packages...")
results = {}
try:
# Build wheel
wheel_builder = WheelBuilder(self.poetry)
wheel_path = wheel_builder.build(target_dir)
results['wheel'] = wheel_path
print(f"✅ Wheel: {wheel_path.name}")
# Build sdist
sdist_builder = SdistBuilder(self.poetry)
sdist_path = sdist_builder.build(target_dir)
results['sdist'] = sdist_path
print(f"✅ Sdist: {sdist_path.name}")
# Post-build verification
print("🔍 Post-build verification...")
if self._verify_builds(results):
print("✅ Build verification passed")
else:
print("⚠️ Build verification issues found")
return results
except Exception as e:
print(f"❌ Build failed: {e}")
return None
def _validate_project(self) -> bool:
"""Validate project before building."""
package = self.poetry.package
# Check required metadata
if not package.name or not package.version:
print("Missing name or version")
return False
# Check for README
if not package.readmes:
print("⚠️ No README file found")
# Check for license
if not package.license:
print("⚠️ No license specified")
return True
def _verify_builds(self, results: dict) -> bool:
"""Verify built packages."""
issues = []
for build_type, path in results.items():
if not path.exists():
issues.append(f"{build_type} file not found: {path}")
continue
# Check file size
size = path.stat().st_size
if size < 1024: # Less than 1KB seems suspicious
issues.append(f"{build_type} seems too small: {size} bytes")
if issues:
for issue in issues:
print(f"⚠️ {issue}")
return False
return True
# Usage
custom_builder = CustomBuilder(poetry)
results = custom_builder.build_with_checks(Path("./dist"))
``` { .api }
### Build Monitoring and Progress
```python
import time
from pathlib import Path
from poetry.core.masonry.builders.wheel import WheelBuilder
def build_with_progress(poetry, target_dir: Path):
"""Build with progress monitoring."""
print(f"🎯 Target directory: {target_dir}")
target_dir.mkdir(exist_ok=True, parents=True)
# Track build time
start_time = time.time()
try:
print("📦 Initializing wheel builder...")
builder = WheelBuilder(poetry)
print("📝 Package information:")
print(f" Name: {poetry.package.name}")
print(f" Version: {poetry.package.version}")
print(f" Dependencies: {len(poetry.package.requires)}")
print("🔍 Discovering files...")
files = builder.find_files_to_add()
print(f" Found {len(files)} files to include")
print("🚫 Finding excluded files...")
excluded = builder.find_excluded_files()
print(f" Excluding {len(excluded)} files")
print("🏗️ Building wheel...")
wheel_path = builder.build(target_dir)
# Build statistics
build_time = time.time() - start_time
wheel_size = wheel_path.stat().st_size
print("✅ Build completed!")
print(f" Wheel: {wheel_path.name}")
print(f" Size: {wheel_size:,} bytes ({wheel_size/1024:.1f} KB)")
print(f" Time: {build_time:.2f} seconds")
return wheel_path
except Exception as e:
build_time = time.time() - start_time
print(f"❌ Build failed after {build_time:.2f} seconds: {e}")
raise
# Usage
wheel_path = build_with_progress(poetry, Path("./dist"))
``` { .api }
## Error Handling
### Build Error Handling
```python
from poetry.core.masonry.builders.wheel import WheelBuilder
from poetry.core.masonry.builders.sdist import SdistBuilder
def safe_build_packages(poetry, target_dir: Path):
"""Build packages with comprehensive error handling."""
results = {"success": [], "failed": []}
builders = [
("wheel", WheelBuilder),
("sdist", SdistBuilder)
]
for build_type, builder_class in builders:
try:
print(f"Building {build_type}...")
builder = builder_class(poetry)
package_path = builder.build(target_dir)
results["success"].append({
"type": build_type,
"path": package_path,
"size": package_path.stat().st_size
})
print(f"✅ {build_type.title()}: {package_path.name}")
except RuntimeError as e:
if "non-package mode" in str(e):
print(f"❌ Cannot build {build_type}: Project not in package mode")
results["failed"].append({"type": build_type, "error": "non-package mode"})
else:
print(f"❌ {build_type.title()} build failed: {e}")
results["failed"].append({"type": build_type, "error": str(e)})
except PermissionError as e:
print(f"❌ Permission error building {build_type}: {e}")
results["failed"].append({"type": build_type, "error": f"permission: {e}"})
except OSError as e:
print(f"❌ OS error building {build_type}: {e}")
results["failed"].append({"type": build_type, "error": f"os: {e}"})
except Exception as e:
print(f"❌ Unexpected error building {build_type}: {e}")
results["failed"].append({"type": build_type, "error": f"unexpected: {e}"})
# Summary
print(f"\n📊 Build Summary:")
print(f" Successful: {len(results['success'])}")
print(f" Failed: {len(results['failed'])}")
if results["success"]:
print(f" Built packages:")
for pkg in results["success"]:
print(f" {pkg['type']}: {pkg['path'].name} ({pkg['size']:,} bytes)")
return results
# Usage
results = safe_build_packages(poetry, Path("./dist"))
``` { .api }
## Type Definitions
```python
from typing import Any, Dict, List, Tuple
from pathlib import Path
# Build configuration types
BuildConfig = Dict[str, Any]
ConfigSettings = Dict[str, Any] | None
# File handling types
BuildIncludeFile = Any # Internal build file representation
ExcludedFiles = set[str]
# Builder result types
BuildResult = Path
WheelTag = str
MetadataPath = Path
``` { .api }Install with Tessl CLI
npx tessl i tessl/pypi-poetry-core