Poetry PEP 517 Build Backend for building Python packages with lightweight, compliant, self-contained build system
—
Poetry Core provides a comprehensive package system for managing package specifications, dependencies, and various dependency types. This system supports standard Python packages, VCS dependencies, local file/directory dependencies, and URL-based dependencies.
# Base package classes
from poetry.core.packages.specification import PackageSpecification
from poetry.core.packages.package import Package
from poetry.core.packages.project_package import ProjectPackage
# Dependency classes
from poetry.core.packages.dependency import Dependency
from poetry.core.packages.dependency_group import DependencyGroup, MAIN_GROUP
# Specialized dependency types
from poetry.core.packages.directory_dependency import DirectoryDependency
from poetry.core.packages.file_dependency import FileDependency
from poetry.core.packages.url_dependency import URLDependency
from poetry.core.packages.vcs_dependency import VCSDependency
# Utility functions
from poetry.core.packages.utils import dependency_from_pep_508
``` { .api }
## PackageSpecification
Base class for package specifications providing source information and feature management.
### PackageSpecification.__init__
```python
class PackageSpecification:
def __init__(
self,
name: str,
source_type: str | None = None,
source_url: str | None = None,
source_reference: str | None = None,
source_resolved_reference: str | None = None,
source_subdirectory: str | None = None,
features: Iterable[str] | None = None,
) -> None:
"""
Initialize package specification.
Args:
name: Package name (will be normalized)
source_type: Source type ("git", "file", "directory", "url", None)
source_url: Source URL or path
source_reference: Source reference (branch, tag, commit for VCS)
source_resolved_reference: Resolved reference (full commit hash)
source_subdirectory: Subdirectory within source
features: Package features/extras to enable
Example:
>>> spec = PackageSpecification("requests")
>>> spec = PackageSpecification(
... "mypackage",
... source_type="git",
... source_url="https://github.com/user/repo.git",
... source_reference="main"
... )
"""
``` { .api }
### Properties
#### Name Properties
```python
@property
def name(self) -> NormalizedName:
"""Normalized package name (PEP 503 compliant)."""
@property
def pretty_name(self) -> str:
"""Original case-preserved package name."""
@property
def complete_name(self) -> str:
"""
Name including features in normalized form.
Returns:
Package name with features like "requests[security,socks]"
Example:
>>> spec = PackageSpecification("requests", features=["security"])
>>> print(spec.complete_name)
requests[security]
"""
@property
def complete_pretty_name(self) -> str:
"""Name including features in pretty form."""
``` { .api }
#### Source Properties
```python
@property
def source_type(self) -> str | None:
"""
Source type identifier.
Returns:
One of: "git", "file", "directory", "url", or None for PyPI
"""
@property
def source_url(self) -> str | None:
"""Source URL or local path."""
@property
def source_reference(self) -> str | None:
"""Source reference (branch, tag, commit for VCS)."""
@property
def source_resolved_reference(self) -> str | None:
"""Resolved reference (full commit hash for VCS)."""
@property
def source_subdirectory(self) -> str | None:
"""Subdirectory within source."""
@property
def features(self) -> frozenset[NormalizedName]:
"""Package features/extras enabled."""
``` { .api }
### Methods
```python
def is_direct_origin(self) -> bool:
"""
Check if package comes from direct source (not PyPI).
Returns:
True if source_type is not None (VCS, file, directory, URL)
"""
def provides(self, other: PackageSpecification) -> bool:
"""
Check if this specification provides another specification.
Args:
other: Another package specification
Returns:
True if this spec can satisfy the other spec
"""
def is_same_source_as(self, other: PackageSpecification) -> bool:
"""Check if two specs have the same source."""
def is_same_package_as(self, other: PackageSpecification) -> bool:
"""Check if two specs represent the same package."""
def clone(self: T) -> T:
"""Create a shallow copy of this specification."""
def with_features(self: T, features: Iterable[str]) -> T:
"""
Create copy with additional features.
Args:
features: Features/extras to add
Returns:
New specification with combined features
"""
def without_features(self: T) -> T:
"""Create copy without any features."""
``` { .api }
## Dependency
Represents a package dependency with version constraints and metadata.
### Dependency.__init__
```python
class Dependency(PackageSpecification):
def __init__(
self,
name: str,
constraint: str | VersionConstraint,
optional: bool = False,
groups: Iterable[str] | None = None,
allows_prereleases: bool | None = None,
extras: Iterable[str] | None = None,
source_type: str | None = None,
source_url: str | None = None,
source_reference: str | None = None,
source_resolved_reference: str | None = None,
source_subdirectory: str | None = None,
) -> None:
"""
Create a package dependency.
Args:
name: Package name
constraint: Version constraint (string or VersionConstraint object)
optional: Whether dependency is optional
groups: Dependency groups (defaults to ["main"])
allows_prereleases: Whether to allow prerelease versions
extras: Package extras/features to enable
source_type: Source type for non-PyPI dependencies
source_url: Source URL for non-PyPI dependencies
source_reference: VCS reference (branch, tag, commit)
source_resolved_reference: Resolved VCS reference
source_subdirectory: Subdirectory within source
Example:
>>> dep = Dependency("requests", "^2.25.0")
>>> dep = Dependency("pytest", ">=6.0.0", groups=["dev"])
>>> dep = Dependency("mypackage", "*", optional=True, extras=["extra1"])
"""
``` { .api }
### Properties
#### Core Dependency Properties
```python
@property
def constraint(self) -> VersionConstraint:
"""Version constraint for this dependency."""
@constraint.setter
def constraint(self, constraint: str | VersionConstraint) -> None:
"""Set version constraint from string or VersionConstraint object."""
@property
def pretty_constraint(self) -> str:
"""Human-readable constraint string."""
@property
def optional(self) -> bool:
"""Whether dependency is optional."""
@property
def groups(self) -> frozenset[str]:
"""
Dependency groups.
Returns:
Set of group names like {"main", "dev", "test"}
"""
``` { .api }
#### Python and Marker Properties
```python
@property
def python_versions(self) -> str:
"""
Required Python versions.
Returns:
Python version constraint string (e.g., ">=3.8,<4.0")
"""
@python_versions.setter
def python_versions(self, value: str) -> None:
"""Set Python version requirements."""
@property
def marker(self) -> BaseMarker:
"""
PEP 508 environment marker.
Returns:
Environment marker for conditional installation
"""
@marker.setter
def marker(self, marker: str | BaseMarker) -> None:
"""Set environment marker from string or BaseMarker."""
@property
def allows_prereleases(self) -> bool | None:
"""Whether to allow prerelease versions."""
``` { .api }
### Static Methods
#### create_from_pep_508
```python
@classmethod
def create_from_pep_508(
cls,
requirement_string: str,
relative_to: Path | None = None
) -> Dependency:
"""
Create dependency from PEP 508 requirement string.
Args:
requirement_string: PEP 508 requirement specification
relative_to: Base path for resolving relative file/directory paths
Returns:
Appropriate Dependency subclass instance
Raises:
InvalidRequirementError: If requirement string is malformed
Examples:
>>> dep = Dependency.create_from_pep_508("requests>=2.25.0")
>>> dep = Dependency.create_from_pep_508("pytest>=6.0; python_version>='3.8'")
>>> dep = Dependency.create_from_pep_508("mypackage[extra1,extra2]>=1.0")
>>> dep = Dependency.create_from_pep_508("./local-package", Path("/base/dir"))
"""
``` { .api }
## Package
Represents an installed or installable package with metadata.
### Package.__init__
```python
class Package(PackageSpecification):
def __init__(
self,
name: str,
version: str | Version,
description: str = "",
category: str = "main",
optional: bool = False,
develop: bool = False,
source_type: str | None = None,
source_url: str | None = None,
source_reference: str | None = None,
source_resolved_reference: str | None = None,
source_subdirectory: str | None = None,
features: Iterable[str] | None = None,
) -> None:
"""
Create package instance.
Args:
name: Package name
version: Package version (string or Version object)
description: Package description
category: Package category ("main", "dev", etc.)
optional: Whether package is optional
develop: Whether this is a development package
source_type: Source type for non-PyPI packages
source_url: Source URL for non-PyPI packages
source_reference: VCS reference
source_resolved_reference: Resolved VCS reference
source_subdirectory: Subdirectory within source
features: Enabled package features
"""
``` { .api }
### Properties
```python
@property
def version(self) -> Version:
"""Package version object."""
@property
def description(self) -> str:
"""Package description."""
@property
def category(self) -> str:
"""Package category (main, dev, etc.)."""
@property
def optional(self) -> bool:
"""Whether package is optional."""
@property
def files(self) -> list[dict[str, Any]]:
"""
Package files information.
Returns:
List of file info dictionaries with hashes and paths
"""
@files.setter
def files(self, files: list[dict[str, Any]]) -> None:
"""Set package files information."""
``` { .api }
## ProjectPackage
Represents the project package being built with additional Poetry-specific metadata.
### ProjectPackage Properties
```python
class ProjectPackage(Package):
# Additional metadata properties
@property
def authors(self) -> list[str]:
"""Package authors list."""
@property
def maintainers(self) -> list[str]:
"""Package maintainers list."""
@property
def homepage(self) -> str | None:
"""Homepage URL."""
@property
def repository_url(self) -> str | None:
"""Repository URL."""
@property
def documentation_url(self) -> str | None:
"""Documentation URL."""
@property
def keywords(self) -> list[str]:
"""Package keywords."""
@property
def classifiers(self) -> list[str]:
"""Trove classifiers."""
@property
def license(self) -> License | None:
"""Package license object."""
@property
def readmes(self) -> tuple[Path, ...]:
"""README files."""
@property
def packages(self) -> list[dict[str, Any]]:
"""Package includes configuration."""
@property
def include(self) -> list[dict[str, Any]]:
"""File includes configuration."""
@property
def exclude(self) -> list[str]:
"""File excludes patterns."""
@property
def custom_urls(self) -> dict[str, str]:
"""Custom project URLs."""
@property
def entry_points(self) -> dict[str, dict[str, str]]:
"""Entry points configuration."""
@property
def extras(self) -> dict[NormalizedName, list[Dependency]]:
"""Optional extras and their dependencies."""
@property
def build_config(self) -> dict[str, Any]:
"""Build configuration settings."""
``` { .api }
### Dependency Management
```python
def add_dependency(self, dependency: Dependency) -> None:
"""
Add dependency to project.
Args:
dependency: Dependency to add
Note:
Dependencies are organized by groups. Main dependencies
go to the "main" group, dev dependencies to "dev", etc.
"""
def remove_dependency(self, name: str, group: str = MAIN_GROUP) -> None:
"""Remove dependency from specified group."""
@property
def requires(self) -> list[Dependency]:
"""All main dependencies."""
@property
def dev_requires(self) -> list[Dependency]:
"""Development dependencies."""
def dependency_group(self, name: str) -> DependencyGroup:
"""Get dependency group by name."""
def has_dependency_group(self, name: str) -> bool:
"""Check if dependency group exists."""
``` { .api }
## Specialized Dependency Types
### DirectoryDependency
```python
class DirectoryDependency(Dependency):
"""Dependency on local directory containing a Python package."""
def __init__(
self,
name: str,
path: Path,
groups: Iterable[str] | None = None,
optional: bool = False,
develop: bool = False,
extras: Iterable[str] | None = None,
) -> None:
"""
Create directory dependency.
Args:
name: Package name
path: Path to directory containing package
groups: Dependency groups
optional: Whether dependency is optional
develop: Whether to install in development mode
extras: Package extras to enable
"""
@property
def path(self) -> Path:
"""Path to directory."""
@property
def develop(self) -> bool:
"""Whether to install in development mode."""
``` { .api }
### FileDependency
```python
class FileDependency(Dependency):
"""Dependency on local file (wheel or sdist)."""
def __init__(
self,
name: str,
path: Path,
groups: Iterable[str] | None = None,
optional: bool = False,
extras: Iterable[str] | None = None,
) -> None:
"""
Create file dependency.
Args:
name: Package name
path: Path to wheel or sdist file
groups: Dependency groups
optional: Whether dependency is optional
extras: Package extras to enable
"""
@property
def path(self) -> Path:
"""Path to file."""
``` { .api }
### URLDependency
```python
class URLDependency(Dependency):
"""Dependency on package downloadable from URL."""
def __init__(
self,
name: str,
url: str,
groups: Iterable[str] | None = None,
optional: bool = False,
extras: Iterable[str] | None = None,
) -> None:
"""
Create URL dependency.
Args:
name: Package name
url: URL to package archive
groups: Dependency groups
optional: Whether dependency is optional
extras: Package extras to enable
"""
@property
def url(self) -> str:
"""Package URL."""
``` { .api }
### VCSDependency
```python
class VCSDependency(Dependency):
"""Dependency on Version Control System repository."""
def __init__(
self,
name: str,
vcs: str,
source: str,
branch: str | None = None,
tag: str | None = None,
rev: str | None = None,
resolved_rev: str | None = None,
directory: str | None = None,
groups: Iterable[str] | None = None,
optional: bool = False,
develop: bool = False,
extras: Iterable[str] | None = None,
) -> None:
"""
Create VCS dependency.
Args:
name: Package name
vcs: Version control system ("git", "hg", "svn", "bzr")
source: Repository URL
branch: Branch name
tag: Tag name
rev: Specific revision/commit
resolved_rev: Resolved full revision hash
directory: Subdirectory within repository
groups: Dependency groups
optional: Whether dependency is optional
develop: Whether to install in development mode
extras: Package extras to enable
"""
@property
def vcs(self) -> str:
"""Version control system type."""
@property
def branch(self) -> str | None:
"""Branch name."""
@property
def tag(self) -> str | None:
"""Tag name."""
@property
def rev(self) -> str | None:
"""Revision/commit."""
@property
def develop(self) -> bool:
"""Whether to install in development mode."""
``` { .api }
## Dependency Groups
### DependencyGroup
```python
class DependencyGroup:
"""Container for related dependencies."""
def __init__(
self,
name: str,
dependencies: list[Dependency] | None = None,
optional: bool = False
) -> None:
"""
Create dependency group.
Args:
name: Group name (e.g., "main", "dev", "test")
dependencies: Initial dependencies
optional: Whether entire group is optional
"""
@property
def name(self) -> str:
"""Group name."""
@property
def dependencies(self) -> list[Dependency]:
"""Dependencies in this group."""
@property
def optional(self) -> bool:
"""Whether group is optional."""
def add_dependency(self, dependency: Dependency) -> None:
"""Add dependency to group."""
def remove_dependency(self, name: str) -> None:
"""Remove dependency by name."""
``` { .api }
### Group Constants
```python
MAIN_GROUP = "main" # Main dependency group identifier
``` { .api }
## Usage Examples
### Creating Various Dependency Types
```python
from pathlib import Path
from poetry.core.packages.dependency import Dependency
from poetry.core.constraints.version import parse_constraint
def create_dependencies():
"""Demonstrate creating different dependency types."""
# Regular PyPI dependency
requests_dep = Dependency("requests", "^2.25.0")
print(f"Regular: {requests_dep.name} {requests_dep.constraint}")
# Development dependency
pytest_dep = Dependency(
"pytest",
">=6.0.0",
groups=["dev"],
allows_prereleases=True
)
print(f"Dev: {pytest_dep.name} in {pytest_dep.groups}")
# Optional dependency with extras
optional_dep = Dependency(
"requests",
"^2.25.0",
optional=True,
extras=["security", "socks"]
)
print(f"Optional: {optional_dep.complete_name}")
# Dependency with environment marker
marker_dep = Dependency("pywin32", "*")
marker_dep.marker = "sys_platform == 'win32'"
print(f"Conditional: {marker_dep.name} when {marker_dep.marker}")
create_dependencies()
``` { .api }
### Creating from PEP 508 Strings
```python
from poetry.core.packages.dependency import Dependency
def pep508_examples():
"""Create dependencies from PEP 508 requirement strings."""
examples = [
"requests>=2.25.0",
"pytest>=6.0; python_version>='3.8'",
"sphinx[docs]>=3.0",
"mypackage[extra1,extra2]>=1.0,<2.0",
"pywin32>=200; sys_platform=='win32'",
]
for requirement in examples:
try:
dep = Dependency.create_from_pep_508(requirement)
print(f"✓ {requirement}")
print(f" -> {dep.name} {dep.constraint}")
if not dep.marker.is_any():
print(f" -> Marker: {dep.marker}")
if dep.features:
print(f" -> Features: {sorted(dep.features)}")
except Exception as e:
print(f"✗ {requirement}: {e}")
pep508_examples()
``` { .api }
### Working with Project Packages
```python
from poetry.core.packages.project_package import ProjectPackage
from poetry.core.packages.dependency import Dependency
def create_project():
"""Create and configure a project package."""
# Create project package
project = ProjectPackage("my-awesome-app", "1.0.0")
# Set metadata
project.description = "An awesome Python application"
project.authors = ["Developer <dev@example.com>"]
project.homepage = "https://github.com/user/my-awesome-app"
project.keywords = ["cli", "tool", "automation"]
# Add main dependencies
requests_dep = Dependency("requests", "^2.25.0")
click_dep = Dependency("click", ">=7.0")
project.add_dependency(requests_dep)
project.add_dependency(click_dep)
# Add development dependencies
pytest_dep = Dependency("pytest", "^6.0.0", groups=["dev"])
black_dep = Dependency("black", "^21.0.0", groups=["dev"])
project.add_dependency(pytest_dep)
project.add_dependency(black_dep)
# Print summary
print(f"Project: {project.name} v{project.version}")
print(f"Description: {project.description}")
print(f"Main dependencies: {len(project.requires)}")
print(f"Dev dependencies: {len(project.dev_requires)}")
# List dependencies
print("\nMain Dependencies:")
for dep in project.requires:
print(f" {dep.name}: {dep.constraint}")
print("\nDev Dependencies:")
for dep in project.dev_requires:
print(f" {dep.name}: {dep.constraint}")
create_project()
``` { .api }
### Specialized Dependencies
```python
from pathlib import Path
from poetry.core.packages.directory_dependency import DirectoryDependency
from poetry.core.packages.file_dependency import FileDependency
from poetry.core.packages.url_dependency import URLDependency
from poetry.core.packages.vcs_dependency import VCSDependency
def create_specialized_dependencies():
"""Create various specialized dependency types."""
# Local directory dependency
dir_dep = DirectoryDependency(
"local-lib",
Path("./libs/local-lib"),
develop=True
)
print(f"Directory: {dir_dep.name} from {dir_dep.path}")
# Local file dependency
file_dep = FileDependency(
"custom-wheel",
Path("./wheels/custom-1.0.0-py3-none-any.whl")
)
print(f"File: {file_dep.name} from {file_dep.path}")
# URL dependency
url_dep = URLDependency(
"archive-package",
"https://example.com/packages/archive-1.0.0.tar.gz"
)
print(f"URL: {url_dep.name} from {url_dep.url}")
# Git dependency
git_dep = VCSDependency(
"git-package",
"git",
"https://github.com/user/repo.git",
branch="develop",
directory="subpackage"
)
print(f"Git: {git_dep.name} from {git_dep.source_url}@{git_dep.branch}")
create_specialized_dependencies()
``` { .api }
### Dependency Analysis
```python
from poetry.core.packages.project_package import ProjectPackage
def analyze_dependencies(project: ProjectPackage):
"""Analyze project dependencies."""
print(f"Dependency Analysis for {project.name}")
print("=" * 50)
# Group analysis
all_groups = set()
for dep in project.all_requires:
all_groups.update(dep.groups)
print(f"Dependency groups: {sorted(all_groups)}")
# Count by group
for group in sorted(all_groups):
group_deps = [dep for dep in project.all_requires if group in dep.groups]
print(f" {group}: {len(group_deps)} dependencies")
# Source type analysis
source_types = {}
for dep in project.all_requires:
source_type = dep.source_type or "pypi"
source_types[source_type] = source_types.get(source_type, 0) + 1
print(f"\nBy source type:")
for source_type, count in source_types.items():
print(f" {source_type}: {count} dependencies")
# Optional dependencies
optional_deps = [dep for dep in project.all_requires if dep.optional]
print(f"\nOptional dependencies: {len(optional_deps)}")
# Dependencies with markers
marker_deps = [dep for dep in project.all_requires if not dep.marker.is_any()]
print(f"Conditional dependencies: {len(marker_deps)}")
# Dependencies with extras
extra_deps = [dep for dep in project.all_requires if dep.features]
print(f"Dependencies with extras: {len(extra_deps)}")
# Usage with a project
# analyze_dependencies(my_project)
``` { .api }Install with Tessl CLI
npx tessl i tessl/pypi-poetry-core