Poetry PEP 517 Build Backend for building Python packages with lightweight, compliant, self-contained build system
—
Poetry Core provides various utility functions for JSON schema validation, SPDX license handling, file type detection, and other helper functions that support the core functionality.
# JSON validation
from poetry.core.json import validate_object, ValidationError
# SPDX license support
from poetry.core.spdx.license import License
from poetry.core.spdx.helpers import license_by_id
# Helper utilities
from poetry.core.utils.helpers import (
combine_unicode,
readme_content_type,
temporary_directory
)
# Version helpers
from poetry.core.version.helpers import format_python_constraint, PYTHON_VERSION
# VCS support
from poetry.core.vcs.git import Git, GitConfig, ParsedUrl, GitError
# Compatibility utilities
from poetry.core.utils._compat import WINDOWS, tomllib
``` { .api }
## JSON Schema Validation
### validate_object
```python
def validate_object(obj: dict[str, Any], schema_name: str) -> list[str]:
"""
Validate object against JSON schema.
Args:
obj: Dictionary object to validate
schema_name: Name of schema to validate against
Returns:
List of validation error messages (empty if valid)
Raises:
ValidationError: If validation fails with detailed error information
Available Schemas:
- "poetry-schema": Main Poetry configuration schema
- "poetry-plugins-schema": Poetry plugins configuration
Example:
>>> config = {"name": "my-package", "version": "1.0.0"}
>>> errors = validate_object(config, "poetry-schema")
>>> if errors:
... print("Validation errors:", errors)
... else:
... print("Configuration is valid")
"""
``` { .api }
### ValidationError
```python
class ValidationError(ValueError):
"""
JSON schema validation error.
Raised when object validation against schema fails,
providing detailed information about validation issues.
"""
def __init__(self, message: str, errors: list[str] | None = None) -> None:
"""
Create validation error.
Args:
message: Error message
errors: List of specific validation errors
"""
@property
def errors(self) -> list[str]:
"""List of specific validation errors."""
``` { .api }
## SPDX License Support
### License
```python
class License:
"""
SPDX license information.
Named tuple containing license metadata from SPDX license list.
"""
id: str # SPDX license identifier (e.g., "MIT", "Apache-2.0")
name: str # Full license name
is_osi_approved: bool # Whether OSI approved
is_deprecated: bool # Whether license is deprecated
def __init__(
self,
id: str,
name: str,
is_osi_approved: bool = False,
is_deprecated: bool = False
) -> None:
"""
Create license information.
Args:
id: SPDX license identifier
name: Full license name
is_osi_approved: Whether license is OSI approved
is_deprecated: Whether license is deprecated
Example:
>>> license = License(
... id="MIT",
... name="MIT License",
... is_osi_approved=True
... )
"""
``` { .api }
### license_by_id
```python
def license_by_id(license_id: str) -> License:
"""
Get license information by SPDX identifier.
Args:
license_id: SPDX license identifier (case-insensitive)
Returns:
License object with metadata
Raises:
ValueError: If license ID is not found
Example:
>>> mit = license_by_id("MIT")
>>> print(f"{mit.name} (OSI: {mit.is_osi_approved})")
MIT License (OSI: True)
>>> apache = license_by_id("apache-2.0") # Case insensitive
>>> print(apache.name)
Apache License 2.0
>>> # Check if license exists
>>> try:
... license = license_by_id("UNKNOWN")
... except ValueError:
... print("License not found")
"""
``` { .api }
## Helper Utilities
### combine_unicode
```python
def combine_unicode(string: str) -> str:
"""
Normalize Unicode string using NFC normalization.
Args:
string: String to normalize
Returns:
Normalized Unicode string
Note:
Uses Unicode NFC (Canonical Decomposition + Canonical Composition)
normalization to ensure consistent string representation.
Example:
>>> # Combining characters
>>> combined = combine_unicode("café") # e + ́ (combining acute)
>>> print(repr(combined)) # Single é character
>>> # Already normalized
>>> normal = combine_unicode("café") # Single é character
>>> combined == normal # True
"""
``` { .api }
### readme_content_type
```python
def readme_content_type(readme_path: Path) -> str:
"""
Detect README content type from file extension.
Args:
readme_path: Path to README file
Returns:
MIME content type string
Supported Extensions:
- .md, .markdown -> "text/markdown"
- .rst -> "text/x-rst"
- .txt -> "text/plain"
- Other -> "text/plain"
Example:
>>> content_type = readme_content_type(Path("README.md"))
>>> print(content_type)
text/markdown
>>> content_type = readme_content_type(Path("README.rst"))
>>> print(content_type)
text/x-rst
>>> content_type = readme_content_type(Path("README.txt"))
>>> print(content_type)
text/plain
"""
``` { .api }
### temporary_directory
```python
@contextmanager
def temporary_directory() -> Iterator[Path]:
"""
Context manager for temporary directory creation.
Yields:
Path to temporary directory
Note:
Directory is automatically cleaned up when context exits.
Example:
>>> from poetry.core.utils.helpers import temporary_directory
>>>
>>> with temporary_directory() as tmp_dir:
... print(f"Temp dir: {tmp_dir}")
... # Use temporary directory
... temp_file = tmp_dir / "test.txt"
... temp_file.write_text("Hello")
... # Directory automatically cleaned up here
"""
``` { .api }
## Version Helpers
### format_python_constraint
Formats Python version constraints into proper constraint strings, transforming disjunctive constraints into readable forms.
```python { .api }
def format_python_constraint(constraint: VersionConstraint) -> str:
"""
Format Python version constraint for display.
Transforms disjunctive version constraints into proper constraint strings
suitable for Python version specifications.
Args:
constraint: Version constraint to format
Returns:
Formatted constraint string (e.g., ">=3.8, !=3.9.*")
Examples:
- Version("3.8") -> "~3.8"
- Version("3") -> "^3.0"
- Complex unions -> ">=3.8, !=3.9.*, !=3.10.*"
"""Comprehensive list of supported Python version patterns for constraint formatting.
PYTHON_VERSION: list[str] = [
"2.7.*",
"3.0.*", "3.1.*", "3.2.*", "3.3.*", "3.4.*", "3.5.*",
"3.6.*", "3.7.*", "3.8.*", "3.9.*", "3.10.*", "3.11.*",
"3.12.*", "3.13.*"
]class Git:
"""
Git operations wrapper.
Provides interface for common Git operations needed
for VCS dependency handling and project management.
"""
def __init__(self, work_dir: Path | None = None) -> None:
"""
Initialize Git wrapper.
Args:
work_dir: Working directory for Git operations
"""
@classmethod
def clone(
cls,
repository: str,
dest: Path,
branch: str | None = None,
tag: str | None = None,
) -> Git:
"""
Clone Git repository.
Args:
repository: Repository URL
dest: Destination directory
branch: Branch to checkout
tag: Tag to checkout
Returns:
Git instance for cloned repository
Raises:
GitError: If clone operation fails
"""
def checkout(self, rev: str) -> None:
"""
Checkout specific revision.
Args:
rev: Revision (branch, tag, commit hash)
Raises:
GitError: If checkout fails
"""
def rev_parse(self, rev: str) -> str:
"""
Get full commit hash for revision.
Args:
rev: Revision to resolve
Returns:
Full commit hash
Raises:
GitError: If revision cannot be resolved
"""
@property
def head(self) -> str:
"""Current HEAD commit hash."""
@property
def is_clean(self) -> bool:
"""Whether working directory is clean (no uncommitted changes)."""
``` { .api }
### GitConfig
```python
class GitConfig:
"""
Git configuration management.
Provides access to Git configuration values
with fallback to global and system configs.
"""
def get(self, key: str, default: str | None = None) -> str | None:
"""
Get Git configuration value.
Args:
key: Configuration key (e.g., "user.name", "user.email")
default: Default value if key not found
Returns:
Configuration value or default
Example:
>>> config = GitConfig()
>>> name = config.get("user.name")
>>> email = config.get("user.email")
>>> print(f"Git user: {name} <{email}>")
"""
``` { .api }
### ParsedUrl
```python
class ParsedUrl:
"""
Parsed Git URL representation.
Handles various Git URL formats and provides
normalized access to URL components.
"""
@classmethod
def parse(cls, url: str) -> ParsedUrl:
"""
Parse Git URL into components.
Args:
url: Git URL in various formats
Returns:
ParsedUrl instance with normalized components
Supported Formats:
- HTTPS: https://github.com/user/repo.git
- SSH: git@github.com:user/repo.git
- GitHub shorthand: github:user/repo
Example:
>>> parsed = ParsedUrl.parse("git@github.com:user/repo.git")
>>> print(parsed.url) # Normalized URL
>>> print(parsed.hostname) # github.com
"""
@property
def url(self) -> str:
"""Normalized Git URL."""
@property
def hostname(self) -> str:
"""Git server hostname."""
@property
def pathname(self) -> str:
"""Repository path."""
``` { .api }
### GitError
```python
class GitError(RuntimeError):
"""
Git operation error.
Raised when Git operations fail, providing
context about the specific operation and error.
"""
def __init__(self, message: str, return_code: int | None = None) -> None:
"""
Create Git error.
Args:
message: Error message
return_code: Git command return code
"""
``` { .api }
## Compatibility Utilities
### Platform Detection
```python
WINDOWS: bool
"""
Platform detection constant.
True if running on Windows, False otherwise.
Used for platform-specific code paths.
Example:
>>> from poetry.core.utils._compat import WINDOWS
>>> if WINDOWS:
... print("Running on Windows")
... else:
... print("Running on Unix-like system")
"""
``` { .api }
### TOML Support
```python
tomllib: ModuleType
"""
TOML parsing library compatibility layer.
Uses tomllib (Python 3.11+) or tomli (older Python versions).
Provides consistent TOML parsing interface across Python versions.
Example:
>>> from poetry.core.utils._compat import tomllib
>>>
>>> # Parse TOML file
>>> with open("pyproject.toml", "rb") as f:
... data = tomllib.load(f)
>>> # Parse TOML string
>>> toml_string = '''
... [tool.poetry]
... name = "my-package"
... '''
>>> data = tomllib.loads(toml_string)
"""
``` { .api }
## Usage Examples
### Configuration Validation
```python
from poetry.core.json import validate_object, ValidationError
from poetry.core.pyproject.toml import PyProjectTOML
def validate_poetry_config(pyproject_path: Path):
"""Validate Poetry configuration with detailed error reporting."""
try:
# Load configuration
pyproject = PyProjectTOML(pyproject_path)
poetry_config = pyproject.poetry_config
# Validate against schema
errors = validate_object(poetry_config, "poetry-schema")
if not errors:
print("✅ Configuration is valid")
return True
print("❌ Configuration validation failed:")
for error in errors:
print(f" • {error}")
return False
except ValidationError as e:
print(f"❌ Validation error: {e}")
if hasattr(e, 'errors') and e.errors:
for error in e.errors:
print(f" • {error}")
return False
except Exception as e:
print(f"❌ Unexpected error: {e}")
return False
# Usage
is_valid = validate_poetry_config(Path("pyproject.toml"))
``` { .api }
### License Information
```python
from poetry.core.spdx.helpers import license_by_id
def show_license_info(license_ids: list[str]):
"""Display information about SPDX licenses."""
print("📜 License Information:")
print("=" * 50)
for license_id in license_ids:
try:
license_info = license_by_id(license_id)
print(f"\n🔖 {license_info.id}")
print(f" Name: {license_info.name}")
print(f" OSI Approved: {'✅' if license_info.is_osi_approved else '❌'}")
print(f" Deprecated: {'⚠️' if license_info.is_deprecated else '✅'}")
except ValueError:
print(f"\n❌ {license_id}: License not found")
# Common licenses
common_licenses = [
"MIT", "Apache-2.0", "GPL-3.0-or-later",
"BSD-3-Clause", "ISC", "LGPL-2.1"
]
show_license_info(common_licenses)
``` { .api }
### File Type Detection
```python
from pathlib import Path
from poetry.core.utils.helpers import readme_content_type
def analyze_readme_files(project_dir: Path):
"""Analyze README files in project directory."""
readme_patterns = ["README*", "readme*", "Readme*"]
readme_files = []
for pattern in readme_patterns:
readme_files.extend(project_dir.glob(pattern))
if not readme_files:
print("❌ No README files found")
return
print("📄 README Files Analysis:")
print("=" * 40)
for readme_file in readme_files:
if readme_file.is_file():
content_type = readme_content_type(readme_file)
size = readme_file.stat().st_size
print(f"\n📝 {readme_file.name}")
print(f" Content-Type: {content_type}")
print(f" Size: {size:,} bytes")
# Preview content
try:
with readme_file.open('r', encoding='utf-8') as f:
preview = f.read(200).strip()
if len(preview) == 200:
preview += "..."
print(f" Preview: {preview[:50]}...")
except UnicodeDecodeError:
print(" Preview: <Binary content>")
# Usage
analyze_readme_files(Path("./my-project"))
``` { .api }
### VCS Operations
```python
from pathlib import Path
from poetry.core.vcs.git import Git, GitError, ParsedUrl
def clone_and_analyze_repo(repo_url: str, target_dir: Path):
"""Clone repository and analyze its structure."""
try:
# Parse repository URL
parsed_url = ParsedUrl.parse(repo_url)
print(f"🔗 Repository: {parsed_url.url}")
print(f" Host: {parsed_url.hostname}")
print(f" Path: {parsed_url.pathname}")
# Clone repository
print(f"\n📥 Cloning to {target_dir}...")
git = Git.clone(repo_url, target_dir)
# Get repository information
head_commit = git.head
is_clean = git.is_clean
print(f"✅ Clone successful")
print(f" HEAD: {head_commit[:8]}")
print(f" Clean: {'✅' if is_clean else '❌'}")
# Check for pyproject.toml
pyproject_file = target_dir / "pyproject.toml"
if pyproject_file.exists():
print(f"📋 Found pyproject.toml")
# Quick analysis
from poetry.core.pyproject.toml import PyProjectTOML
try:
pyproject = PyProjectTOML(pyproject_file)
if pyproject.is_poetry_project():
config = pyproject.poetry_config
print(f" Poetry project: {config.get('name', 'Unknown')}")
else:
print(f" Not a Poetry project")
except Exception as e:
print(f" Error reading config: {e}")
else:
print(f"❌ No pyproject.toml found")
return git
except GitError as e:
print(f"❌ Git error: {e}")
return None
except Exception as e:
print(f"❌ Unexpected error: {e}")
return None
# Usage
git = clone_and_analyze_repo(
"https://github.com/python-poetry/poetry-core.git",
Path("./temp-clone")
)
``` { .api }
### Temporary File Operations
```python
from poetry.core.utils.helpers import temporary_directory
from pathlib import Path
import shutil
def process_with_temp_workspace(source_files: list[Path]):
"""Process files using temporary workspace."""
with temporary_directory() as temp_dir:
print(f"🛠️ Working in: {temp_dir}")
# Copy source files to temp directory
temp_files = []
for source_file in source_files:
if source_file.exists():
temp_file = temp_dir / source_file.name
shutil.copy2(source_file, temp_file)
temp_files.append(temp_file)
print(f" Copied: {source_file.name}")
# Process files in temp directory
print(f"🔄 Processing {len(temp_files)} files...")
results = []
for temp_file in temp_files:
try:
# Example processing: count lines
with temp_file.open('r') as f:
lines = len(f.readlines())
results.append({
"file": temp_file.name,
"lines": lines,
"size": temp_file.stat().st_size
})
print(f" {temp_file.name}: {lines} lines")
except Exception as e:
print(f" ❌ Error processing {temp_file.name}: {e}")
# Temporary directory is automatically cleaned up
print(f"✅ Processing complete, temp directory cleaned")
return results
# Usage
source_files = [
Path("README.md"),
Path("pyproject.toml"),
Path("src/mypackage/__init__.py")
]
results = process_with_temp_workspace(source_files)
``` { .api }
### Unicode Normalization
```python
from poetry.core.utils.helpers import combine_unicode
import unicodedata
def normalize_project_strings(config: dict):
"""Normalize Unicode strings in project configuration."""
print("🔤 Unicode Normalization:")
string_fields = ["name", "description", "authors", "maintainers"]
for field in string_fields:
if field in config:
original = config[field]
if isinstance(original, str):
normalized = combine_unicode(original)
# Check if normalization changed anything
if original != normalized:
print(f" {field}: Normalized")
print(f" Before: {repr(original)}")
print(f" After: {repr(normalized)}")
config[field] = normalized
else:
print(f" {field}: Already normalized")
elif isinstance(original, list):
# Handle list of strings (authors, maintainers)
normalized_list = []
changed = False
for item in original:
if isinstance(item, str):
normalized_item = combine_unicode(item)
normalized_list.append(normalized_item)
if item != normalized_item:
changed = True
else:
normalized_list.append(item)
if changed:
print(f" {field}: Normalized list items")
config[field] = normalized_list
else:
print(f" {field}: List already normalized")
return config
# Example usage
project_config = {
"name": "café-package", # May contain combining characters
"description": "A café management system",
"authors": ["José García <jose@example.com>"]
}
normalized_config = normalize_project_strings(project_config)
``` { .api }
## Error Handling Utilities
### Safe Operations
```python
from poetry.core.json import ValidationError
from poetry.core.spdx.helpers import license_by_id
from poetry.core.vcs.git import GitError
def safe_validation_operations():
"""Demonstrate safe utility operations with error handling."""
# Safe license lookup
def safe_license_lookup(license_id: str):
try:
return license_by_id(license_id)
except ValueError:
print(f"❌ Unknown license: {license_id}")
return None
# Safe validation
def safe_validation(obj: dict, schema: str):
try:
from poetry.core.json import validate_object
errors = validate_object(obj, schema)
return errors
except ValidationError as e:
print(f"❌ Validation failed: {e}")
return [str(e)]
except Exception as e:
print(f"❌ Unexpected validation error: {e}")
return [f"Unexpected error: {e}"]
# Safe Git operations
def safe_git_operation(func, *args, **kwargs):
try:
return func(*args, **kwargs)
except GitError as e:
print(f"❌ Git operation failed: {e}")
return None
except Exception as e:
print(f"❌ Unexpected Git error: {e}")
return None
# Usage examples
license_info = safe_license_lookup("MIT")
if license_info:
print(f"✅ License found: {license_info.name}")
validation_errors = safe_validation({"name": "test"}, "poetry-schema")
if not validation_errors:
print("✅ Validation passed")
else:
print(f"❌ Validation errors: {validation_errors}")
safe_validation_operations()
``` { .api }
## Type Definitions
```python
from typing import Any, Dict, List, Iterator
from pathlib import Path
# Validation types
ValidationErrors = List[str]
SchemaName = str
# License types
LicenseId = str
# VCS types
GitUrl = str
GitRevision = str
GitBranch = str
GitTag = str
# Utility types
ContentType = str
UnicodeString = str
``` { .api }Install with Tessl CLI
npx tessl i tessl/pypi-poetry-core