Modern, extensible Python build backend implementing PEP 517/660 standards with plugin architecture.
—
Project metadata parsing, validation, and core metadata generation following Python packaging standards. The metadata system handles project configuration, dependency management, and metadata validation.
Core project metadata container that parses and validates project configuration.
class ProjectMetadata(Generic[PluginManagerBound]):
def __init__(
self,
root: str,
plugin_manager: PluginManager | None = None,
config: dict | None = None
):
"""
Initialize project metadata.
Args:
root: Project root directory path
plugin_manager: Optional plugin manager instance
config: Optional metadata configuration override
"""
@property
def name(self) -> str:
"""Project name."""
@property
def version(self) -> str:
"""Project version."""
@property
def description(self) -> str:
"""Project description."""
@property
def readme(self) -> str | None:
"""Project readme content."""
@property
def authors(self) -> list[dict[str, str]]:
"""Project authors list."""
@property
def maintainers(self) -> list[dict[str, str]]:
"""Project maintainers list."""
@property
def license(self) -> str | None:
"""Project license."""
@property
def keywords(self) -> list[str]:
"""Project keywords."""
@property
def classifiers(self) -> list[str]:
"""Project classifiers."""
@property
def urls(self) -> dict[str, str]:
"""Project URLs."""
@property
def dependencies(self) -> list[str]:
"""Project runtime dependencies."""
@property
def optional_dependencies(self) -> dict[str, list[str]]:
"""Project optional dependencies (extras)."""
@property
def requires_python(self) -> str | None:
"""Required Python version specification."""
@property
def dynamic(self) -> list[str]:
"""List of dynamic metadata fields."""Build-time metadata configuration and validation.
class BuildMetadata:
def __init__(self, root: str, config: dict):
"""
Initialize build metadata.
Args:
root: Project root directory
config: Build metadata configuration
"""
@property
def build_requires(self) -> list[str]:
"""Build-time dependencies."""
@property
def build_backend(self) -> str:
"""Build backend specification."""
@property
def backend_path(self) -> list[str] | None:
"""Backend path specification."""Core metadata following Python packaging standards (PEP 314, PEP 566, etc.).
class CoreMetadata:
def __init__(self, metadata: ProjectMetadata, config: dict):
"""
Initialize core metadata.
Args:
metadata: Project metadata instance
config: Core metadata configuration
"""
def as_string(self) -> str:
"""
Generate core metadata as string.
Returns:
str: Core metadata in email header format
"""
def as_bytes(self) -> bytes:
"""
Generate core metadata as bytes.
Returns:
bytes: Core metadata in email header format
"""Extended metadata configuration specific to Hatch projects.
class HatchMetadata(Generic[PluginManagerBound]):
def __init__(
self,
root: str,
config: dict,
plugin_manager: PluginManager
):
"""
Initialize Hatch metadata.
Args:
root: Project root directory
config: Hatch metadata configuration
plugin_manager: Plugin manager instance
"""
@property
def version_config(self) -> HatchVersionConfig:
"""Version configuration."""
@property
def metadata_settings(self) -> HatchMetadataSettings:
"""Metadata settings."""
@property
def build_config(self) -> dict:
"""Build configuration."""
class HatchVersionConfig(Generic[PluginManagerBound]):
def __init__(
self,
root: str,
config: dict,
plugin_manager: PluginManager
):
"""
Initialize version configuration.
Args:
root: Project root directory
config: Version configuration
plugin_manager: Plugin manager instance
"""
@property
def source(self) -> str:
"""Version source plugin name."""
@property
def scheme(self) -> str:
"""Version scheme plugin name."""
@property
def source_config(self) -> dict:
"""Version source configuration."""
@property
def scheme_config(self) -> dict:
"""Version scheme configuration."""
class HatchMetadataSettings(Generic[PluginManagerBound]):
def __init__(
self,
root: str,
config: dict,
plugin_manager: PluginManager
):
"""
Initialize metadata settings.
Args:
root: Project root directory
config: Metadata settings configuration
plugin_manager: Plugin manager instance
"""
@property
def hooks(self) -> dict[str, dict]:
"""Metadata hook configurations."""Hatchling reads metadata from multiple sources with precedence:
pyproject.toml - Primary configuration filesetup.py - Legacy setup script (limited support)setup.cfg - Legacy configuration file (limited support)[project]
name = "my-package"
version = "1.0.0"
description = "My package description"
readme = "README.md"
license = {text = "MIT"}
authors = [
{name = "Author Name", email = "author@example.com"}
]
dependencies = [
"requests>=2.25.0",
"click>=8.0.0"
]
optional-dependencies = {
dev = ["pytest", "black"],
docs = ["sphinx", "myst-parser"]
}
requires-python = ">=3.8"
keywords = ["example", "package"]
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python :: 3"
]
[project.urls]
Homepage = "https://example.com"
Documentation = "https://example.com/docs"
Repository = "https://github.com/user/repo"
[project.scripts]
my-cli = "my_package.cli:main"
[project.gui-scripts]
my-gui = "my_package.gui:main"
[project.entry-points."my.group"]
plugin = "my_package.plugin:MyPlugin"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.version]
source = "code"
path = "src/my_package/__about__.py"
[tool.hatch.build]
include = ["src/**/*.py"]
exclude = ["tests/"]
[tool.hatch.metadata]
allow-direct-references = truedef get_core_metadata_constructors() -> dict[str, Callable]:
"""
Get available core metadata constructor functions.
Returns:
dict: Mapping of version to constructor function
"""Some metadata fields can be marked as dynamic and resolved at build time:
# Dynamic fields are resolved through plugins or build hooks
dynamic = ["version", "description", "dependencies"]import os
from hatchling.metadata.core import ProjectMetadata
from hatchling.plugin.manager import PluginManager
# Load project metadata
root = os.getcwd()
metadata = ProjectMetadata(root)
# Access metadata properties
print(f"Name: {metadata.name}")
print(f"Version: {metadata.version}")
print(f"Description: {metadata.description}")
print(f"Dependencies: {metadata.dependencies}")
print(f"Optional dependencies: {metadata.optional_dependencies}")# Create plugin manager first
plugin_manager = PluginManager(metadata)
# Metadata with plugin support
metadata = ProjectMetadata(root, plugin_manager=plugin_manager)
# Access dynamic metadata resolved through plugins
print(f"Dynamic version: {metadata.version}")from hatchling.metadata.core import CoreMetadata
# Generate core metadata
core_metadata = CoreMetadata(metadata, config={})
metadata_string = core_metadata.as_string()
# Write to METADATA file
with open("PKG-INFO", "w") as f:
f.write(metadata_string)# Override metadata configuration
custom_config = {
"name": "custom-name",
"version": "2.0.0",
"dependencies": ["requests", "click"]
}
metadata = ProjectMetadata(root, config=custom_config)from hatchling.metadata.core import HatchMetadata
# Load Hatch-specific metadata
hatch_metadata = HatchMetadata(root, config, plugin_manager)
# Access version configuration
version_config = hatch_metadata.version_config
print(f"Version source: {version_config.source}")
print(f"Version scheme: {version_config.scheme}")
# Access metadata settings
metadata_settings = hatch_metadata.metadata_settings
print(f"Metadata hooks: {metadata_settings.hooks}")Common metadata errors and exceptions:
ValueError: Invalid metadata values or configurationsFileNotFoundError: Missing configuration filesKeyError: Missing required metadata fieldsTypeError: Incorrect metadata field typesCustom metadata hooks can modify metadata at build time:
from hatchling.metadata.plugin.interface import MetadataHookInterface
class CustomMetadataHook(MetadataHookInterface):
def update(self, metadata):
# Custom metadata modifications
metadata["custom_field"] = "custom_value"The metadata system integrates closely with the build system:
This metadata system provides the foundation for hatchling's standards-compliant packaging functionality while maintaining flexibility through its plugin architecture.
Install with Tessl CLI
npx tessl i tessl/pypi-hatchling