Modern, extensible Python build backend implementing PEP 517/660 standards with plugin architecture.
—
Extensible plugin architecture supporting custom builders, build hooks, version sources, version schemes, and metadata hooks. The plugin system enables third-party extensions and customization of hatchling's functionality.
Central plugin management system that discovers, loads, and manages all plugin types.
class PluginManager:
def __init__(self):
"""
Initialize plugin manager.
Note:
The PluginManager uses dynamic initialization and does not
require parameters during construction.
"""
@property
def builders(self) -> dict[str, type[BuilderInterface]]:
"""Dictionary of available builder plugins."""
@property
def build_hooks(self) -> dict[str, type[BuildHookInterface]]:
"""Dictionary of available build hook plugins."""
@property
def version_sources(self) -> dict[str, type[VersionSourceInterface]]:
"""Dictionary of available version source plugins."""
@property
def version_schemes(self) -> dict[str, type[VersionSchemeInterface]]:
"""Dictionary of available version scheme plugins."""
@property
def metadata_hooks(self) -> dict[str, type[MetadataHookInterface]]:
"""Dictionary of available metadata hook plugins."""Abstract base class for all builder plugins.
class BuilderInterface(ABC, Generic[BuilderConfigBound, PluginManagerBound]):
def build(
self, *,
directory: str | None = None,
versions: list[str] | None = None,
hooks_only: bool | None = None,
clean: bool | None = None,
clean_hooks_after: bool | None = None,
clean_only: bool | None = False
) -> Generator[str, None, None]:
"""
Build distributions and yield paths to built files.
Args:
directory: Output directory for built distributions (default: "dist")
versions: List of version types to build
hooks_only: Whether to run only build hooks
clean: Whether to clean build directory before building
clean_hooks_after: Whether to clean hooks after building
clean_only: Whether to only clean without building
Yields:
str: Paths to built distribution files
"""
def get_version_api(self) -> dict[str, Callable]:
"""
Get version API information for this builder.
Returns:
dict: Version API information
"""
def iter_build_targets(self, versions: list[str]) -> Iterator[tuple[str, str]]:
"""
Iterate over build targets for given versions.
Args:
versions: List of version types
Yields:
tuple[str, str]: (version, target_name) pairs
"""Abstract base class for build hooks that customize the build process.
class BuildHookInterface(Generic[BuilderConfigBound]):
def clean(self, versions: list[str]) -> None:
"""
Clean up build artifacts for specified versions.
Args:
versions: List of version types to clean
"""
def initialize(self, version: str, build_data: dict[str, Any]) -> None:
"""
Initialize hook before building.
Args:
version: Version being built
build_data: Build data dictionary
"""
def finalize(self, version: str, build_data: dict[str, Any], artifact_path: str) -> None:
"""
Finalize hook after building.
Args:
version: Version that was built
build_data: Build data dictionary
artifact_path: Path to built artifact
"""Abstract base class for version source plugins that determine project versions.
class VersionSourceInterface(ABC):
def get_version_data(self) -> dict:
"""
Get the current version data.
Returns:
dict: Dictionary with 'version' key and current version string
"""
def set_version(self, version: str, version_data: dict) -> None:
"""
Set the version.
Args:
version: Version string to set
"""Abstract base class for version scheme plugins that manipulate version strings.
class VersionSchemeInterface(ABC):
def update(self, version: str, **kwargs) -> str:
"""
Update a version string.
Args:
version: Version string to update
**kwargs: Additional update parameters
Returns:
str: Updated version string
"""
def parse(self, version: str) -> dict:
"""
Parse a version string into components.
Args:
version: Version string to parse
Returns:
dict: Version components
"""
def normalize(self, version: str) -> str:
"""
Normalize a version string.
Args:
version: Version string to normalize
Returns:
str: Normalized version string
"""Abstract base class for metadata hook plugins that modify project metadata.
class MetadataHookInterface(ABC):
def update(self, metadata: dict[str, Any]) -> None:
"""
Update project metadata.
Args:
metadata: Metadata dictionary to modify
"""Built-in version source implementations for common version management patterns.
class CodeSource(VersionSourceInterface):
"""Extract version from Python code using AST parsing."""
class RegexSource(VersionSourceInterface):
"""Extract version using regular expression patterns."""
class EnvSource(VersionSourceInterface):
"""Get version from environment variables."""class StandardScheme(VersionSchemeInterface):
"""Standard PEP 440 compliant version scheme."""class VersionBuildHook(BuildHookInterface):
"""Hook for managing version information during builds."""
class CustomBuildHook:
"""Custom user-defined build hook loaded from scripts."""class CustomMetadataHook:
"""Custom user-defined metadata hook loaded from scripts."""Functions for registering built-in plugins.
def hatch_register_builder() -> list[type[BuilderInterface]]:
"""Register built-in builder plugins."""
def hatch_register_build_hook() -> list[type[BuildHookInterface]]:
"""Register built-in build hook plugins."""Plugins are discovered through entry points in setup.py or pyproject.toml:
[project.entry-points.hatch_builder]
my_builder = "my_package.builders:MyBuilder"
[project.entry-points.hatch_build_hook]
my_hook = "my_package.hooks:MyBuildHook"
[project.entry-points.hatch_version_source]
my_source = "my_package.version:MyVersionSource"
[project.entry-points.hatch_version_scheme]
my_scheme = "my_package.version:MyVersionScheme"
[project.entry-points.hatch_metadata_hook]
my_metadata = "my_package.metadata:MyMetadataHook"from hatchling.builders.plugin.interface import BuilderInterface
class MyBuilder(BuilderInterface):
PLUGIN_NAME = "my-builder"
def build(self, *, directory="dist", versions=None):
# Custom build logic
yield self.create_my_distribution(directory)
def get_version_api(self):
return {"my-version": "1.0"}from hatchling.builders.hooks.plugin.interface import BuildHookInterface
class MyBuildHook(BuildHookInterface):
PLUGIN_NAME = "my-hook"
def initialize(self, version, build_data):
# Pre-build customization
build_data["custom_setting"] = "value"
def finalize(self, version, build_data, artifact_path):
# Post-build customization
print(f"Built {artifact_path}")from hatchling.version.source.plugin.interface import VersionSourceInterface
class MyVersionSource(VersionSourceInterface):
PLUGIN_NAME = "my-source"
def get_version_data(self):
# Custom version retrieval logic
return {"version": "1.0.0"}
def set_version(self, version, version_data):
# Custom version setting logic
passPlugins are configured in pyproject.toml:
[tool.hatch.build]
builder = "my-builder"
hooks = ["my-hook"]
[tool.hatch.version]
source = "my-source"
scheme = "my-scheme"
[tool.hatch.metadata.hooks.my-metadata]
setting = "value"def load_plugin_from_script(
path: str,
script_name: str,
plugin_class: type[T],
plugin_id: str
) -> type[T]:
"""
Load a plugin class from a Python script.
Args:
path: Path to script file
script_name: Name of the script
plugin_class: Expected plugin base class
plugin_id: Plugin identifier
Returns:
type[T]: Loaded plugin class
"""class ClassRegister:
"""Registry for plugin classes with validation."""
class ThirdPartyPlugins:
"""Manager for third-party plugin discovery and loading."""class UnknownPluginError(ValueError):
"""Raised when referencing an unknown plugin."""from hatchling.metadata.core import ProjectMetadata
from hatchling.plugin.manager import PluginManager
# Create plugin manager
plugin_manager = PluginManager()
# List available plugins
print("Builders:", list(plugin_manager.builders.keys()))
print("Build hooks:", list(plugin_manager.build_hooks.keys()))
print("Version sources:", list(plugin_manager.version_sources.keys()))
print("Version schemes:", list(plugin_manager.version_schemes.keys()))
print("Metadata hooks:", list(plugin_manager.metadata_hooks.keys()))
# Get specific plugin
wheel_builder = plugin_manager.builders["wheel"]
standard_scheme = plugin_manager.version_schemes["standard"]# Configure builder with plugins
from hatchling.builders.wheel import WheelBuilder
builder = WheelBuilder(
root=os.getcwd(),
plugin_manager=plugin_manager,
config={
"hooks": ["version", "my-custom-hook"],
"version-source": "code",
"version-scheme": "standard"
}
)Third-party plugins can be registered using hatchling's plugin system.
from hatchling.plugin import hookimpl
@hookimpl
def hatch_register_builder():
"""Register a custom builder plugin."""
return MyBuilderClass
@hookimpl
def hatch_register_version_source():
"""Register a custom version source plugin."""
return MyVersionSourceClass
@hookimpl
def hatch_register_version_scheme():
"""Register a custom version scheme plugin."""
return MyVersionSchemeClass
@hookimpl
def hatch_register_metadata_hook():
"""Register a custom metadata hook plugin."""
return MyMetadataHookClass
@hookimpl
def hatch_register_build_hook():
"""Register a custom build hook plugin."""
return MyBuildHookClass# plugin.py
from hatchling.plugin import hookimpl
from hatchling.builders.plugin.interface import BuilderInterface
class CustomBuilder(BuilderInterface):
PLUGIN_NAME = 'custom'
def get_version_api(self):
return {'standard': self.build_standard}
def build_standard(self, directory, **build_data):
# Custom build logic
pass
@hookimpl
def hatch_register_builder():
return CustomBuilderThe plugin system makes hatchling highly extensible, allowing custom build logic, version management, and metadata processing to be implemented as reusable plugins.
Install with Tessl CLI
npx tessl i tessl/pypi-hatchling