0
# Metadata System
1
2
Project metadata parsing, validation, and core metadata generation following Python packaging standards. The metadata system handles project configuration, dependency management, and metadata validation.
3
4
## Capabilities
5
6
### Project Metadata
7
8
Core project metadata container that parses and validates project configuration.
9
10
```python { .api }
11
class ProjectMetadata(Generic[PluginManagerBound]):
12
def __init__(
13
self,
14
root: str,
15
plugin_manager: PluginManager | None = None,
16
config: dict | None = None
17
):
18
"""
19
Initialize project metadata.
20
21
Args:
22
root: Project root directory path
23
plugin_manager: Optional plugin manager instance
24
config: Optional metadata configuration override
25
"""
26
27
@property
28
def name(self) -> str:
29
"""Project name."""
30
31
@property
32
def version(self) -> str:
33
"""Project version."""
34
35
@property
36
def description(self) -> str:
37
"""Project description."""
38
39
@property
40
def readme(self) -> str | None:
41
"""Project readme content."""
42
43
@property
44
def authors(self) -> list[dict[str, str]]:
45
"""Project authors list."""
46
47
@property
48
def maintainers(self) -> list[dict[str, str]]:
49
"""Project maintainers list."""
50
51
@property
52
def license(self) -> str | None:
53
"""Project license."""
54
55
@property
56
def keywords(self) -> list[str]:
57
"""Project keywords."""
58
59
@property
60
def classifiers(self) -> list[str]:
61
"""Project classifiers."""
62
63
@property
64
def urls(self) -> dict[str, str]:
65
"""Project URLs."""
66
67
@property
68
def dependencies(self) -> list[str]:
69
"""Project runtime dependencies."""
70
71
@property
72
def optional_dependencies(self) -> dict[str, list[str]]:
73
"""Project optional dependencies (extras)."""
74
75
@property
76
def requires_python(self) -> str | None:
77
"""Required Python version specification."""
78
79
@property
80
def dynamic(self) -> list[str]:
81
"""List of dynamic metadata fields."""
82
```
83
84
### Build Metadata
85
86
Build-time metadata configuration and validation.
87
88
```python { .api }
89
class BuildMetadata:
90
def __init__(self, root: str, config: dict):
91
"""
92
Initialize build metadata.
93
94
Args:
95
root: Project root directory
96
config: Build metadata configuration
97
"""
98
99
@property
100
def build_requires(self) -> list[str]:
101
"""Build-time dependencies."""
102
103
@property
104
def build_backend(self) -> str:
105
"""Build backend specification."""
106
107
@property
108
def backend_path(self) -> list[str] | None:
109
"""Backend path specification."""
110
```
111
112
### Core Metadata
113
114
Core metadata following Python packaging standards (PEP 314, PEP 566, etc.).
115
116
```python { .api }
117
class CoreMetadata:
118
def __init__(self, metadata: ProjectMetadata, config: dict):
119
"""
120
Initialize core metadata.
121
122
Args:
123
metadata: Project metadata instance
124
config: Core metadata configuration
125
"""
126
127
def as_string(self) -> str:
128
"""
129
Generate core metadata as string.
130
131
Returns:
132
str: Core metadata in email header format
133
"""
134
135
def as_bytes(self) -> bytes:
136
"""
137
Generate core metadata as bytes.
138
139
Returns:
140
bytes: Core metadata in email header format
141
"""
142
```
143
144
### Hatch-Specific Metadata
145
146
Extended metadata configuration specific to Hatch projects.
147
148
```python { .api }
149
class HatchMetadata(Generic[PluginManagerBound]):
150
def __init__(
151
self,
152
root: str,
153
config: dict,
154
plugin_manager: PluginManager
155
):
156
"""
157
Initialize Hatch metadata.
158
159
Args:
160
root: Project root directory
161
config: Hatch metadata configuration
162
plugin_manager: Plugin manager instance
163
"""
164
165
@property
166
def version_config(self) -> HatchVersionConfig:
167
"""Version configuration."""
168
169
@property
170
def metadata_settings(self) -> HatchMetadataSettings:
171
"""Metadata settings."""
172
173
@property
174
def build_config(self) -> dict:
175
"""Build configuration."""
176
177
class HatchVersionConfig(Generic[PluginManagerBound]):
178
def __init__(
179
self,
180
root: str,
181
config: dict,
182
plugin_manager: PluginManager
183
):
184
"""
185
Initialize version configuration.
186
187
Args:
188
root: Project root directory
189
config: Version configuration
190
plugin_manager: Plugin manager instance
191
"""
192
193
@property
194
def source(self) -> str:
195
"""Version source plugin name."""
196
197
@property
198
def scheme(self) -> str:
199
"""Version scheme plugin name."""
200
201
@property
202
def source_config(self) -> dict:
203
"""Version source configuration."""
204
205
@property
206
def scheme_config(self) -> dict:
207
"""Version scheme configuration."""
208
209
class HatchMetadataSettings(Generic[PluginManagerBound]):
210
def __init__(
211
self,
212
root: str,
213
config: dict,
214
plugin_manager: PluginManager
215
):
216
"""
217
Initialize metadata settings.
218
219
Args:
220
root: Project root directory
221
config: Metadata settings configuration
222
plugin_manager: Plugin manager instance
223
"""
224
225
@property
226
def hooks(self) -> dict[str, dict]:
227
"""Metadata hook configurations."""
228
```
229
230
## Metadata Parsing
231
232
### Configuration Sources
233
234
Hatchling reads metadata from multiple sources with precedence:
235
236
1. `pyproject.toml` - Primary configuration file
237
2. `setup.py` - Legacy setup script (limited support)
238
3. `setup.cfg` - Legacy configuration file (limited support)
239
240
### pyproject.toml Structure
241
242
```toml
243
[project]
244
name = "my-package"
245
version = "1.0.0"
246
description = "My package description"
247
readme = "README.md"
248
license = {text = "MIT"}
249
authors = [
250
{name = "Author Name", email = "author@example.com"}
251
]
252
dependencies = [
253
"requests>=2.25.0",
254
"click>=8.0.0"
255
]
256
optional-dependencies = {
257
dev = ["pytest", "black"],
258
docs = ["sphinx", "myst-parser"]
259
}
260
requires-python = ">=3.8"
261
keywords = ["example", "package"]
262
classifiers = [
263
"Development Status :: 4 - Beta",
264
"Programming Language :: Python :: 3"
265
]
266
267
[project.urls]
268
Homepage = "https://example.com"
269
Documentation = "https://example.com/docs"
270
Repository = "https://github.com/user/repo"
271
272
[project.scripts]
273
my-cli = "my_package.cli:main"
274
275
[project.gui-scripts]
276
my-gui = "my_package.gui:main"
277
278
[project.entry-points."my.group"]
279
plugin = "my_package.plugin:MyPlugin"
280
281
[build-system]
282
requires = ["hatchling"]
283
build-backend = "hatchling.build"
284
285
[tool.hatch.version]
286
source = "code"
287
path = "src/my_package/__about__.py"
288
289
[tool.hatch.build]
290
include = ["src/**/*.py"]
291
exclude = ["tests/"]
292
293
[tool.hatch.metadata]
294
allow-direct-references = true
295
```
296
297
## Metadata Validation
298
299
### Core Metadata Constructors
300
301
```python { .api }
302
def get_core_metadata_constructors() -> dict[str, Callable]:
303
"""
304
Get available core metadata constructor functions.
305
306
Returns:
307
dict: Mapping of version to constructor function
308
"""
309
```
310
311
### Dynamic Metadata
312
313
Some metadata fields can be marked as dynamic and resolved at build time:
314
315
```python
316
# Dynamic fields are resolved through plugins or build hooks
317
dynamic = ["version", "description", "dependencies"]
318
```
319
320
### Metadata Validation Rules
321
322
- Project name must be valid Python package name
323
- Version must follow PEP 440 specification
324
- Dependencies must use valid requirement specifications
325
- Classifiers must be from the official classifier list
326
- URLs must be valid URIs
327
328
## Usage Examples
329
330
### Basic Metadata Loading
331
332
```python
333
import os
334
from hatchling.metadata.core import ProjectMetadata
335
from hatchling.plugin.manager import PluginManager
336
337
# Load project metadata
338
root = os.getcwd()
339
metadata = ProjectMetadata(root)
340
341
# Access metadata properties
342
print(f"Name: {metadata.name}")
343
print(f"Version: {metadata.version}")
344
print(f"Description: {metadata.description}")
345
print(f"Dependencies: {metadata.dependencies}")
346
print(f"Optional dependencies: {metadata.optional_dependencies}")
347
```
348
349
### With Plugin Manager
350
351
```python
352
# Create plugin manager first
353
plugin_manager = PluginManager(metadata)
354
355
# Metadata with plugin support
356
metadata = ProjectMetadata(root, plugin_manager=plugin_manager)
357
358
# Access dynamic metadata resolved through plugins
359
print(f"Dynamic version: {metadata.version}")
360
```
361
362
### Core Metadata Generation
363
364
```python
365
from hatchling.metadata.core import CoreMetadata
366
367
# Generate core metadata
368
core_metadata = CoreMetadata(metadata, config={})
369
metadata_string = core_metadata.as_string()
370
371
# Write to METADATA file
372
with open("PKG-INFO", "w") as f:
373
f.write(metadata_string)
374
```
375
376
### Custom Metadata Configuration
377
378
```python
379
# Override metadata configuration
380
custom_config = {
381
"name": "custom-name",
382
"version": "2.0.0",
383
"dependencies": ["requests", "click"]
384
}
385
386
metadata = ProjectMetadata(root, config=custom_config)
387
```
388
389
### Accessing Hatch-Specific Metadata
390
391
```python
392
from hatchling.metadata.core import HatchMetadata
393
394
# Load Hatch-specific metadata
395
hatch_metadata = HatchMetadata(root, config, plugin_manager)
396
397
# Access version configuration
398
version_config = hatch_metadata.version_config
399
print(f"Version source: {version_config.source}")
400
print(f"Version scheme: {version_config.scheme}")
401
402
# Access metadata settings
403
metadata_settings = hatch_metadata.metadata_settings
404
print(f"Metadata hooks: {metadata_settings.hooks}")
405
```
406
407
## Error Handling
408
409
Common metadata errors and exceptions:
410
411
- `ValueError`: Invalid metadata values or configurations
412
- `FileNotFoundError`: Missing configuration files
413
- `KeyError`: Missing required metadata fields
414
- `TypeError`: Incorrect metadata field types
415
416
## Metadata Hooks
417
418
Custom metadata hooks can modify metadata at build time:
419
420
```python
421
from hatchling.metadata.plugin.interface import MetadataHookInterface
422
423
class CustomMetadataHook(MetadataHookInterface):
424
def update(self, metadata):
425
# Custom metadata modifications
426
metadata["custom_field"] = "custom_value"
427
```
428
429
## Integration with Build System
430
431
The metadata system integrates closely with the build system:
432
433
1. **Builder Configuration**: Metadata informs builder settings and dependencies
434
2. **Version Management**: Dynamic version resolution through version sources
435
3. **Dependency Resolution**: Build and runtime dependency specifications
436
4. **Distribution Metadata**: Core metadata generation for wheels and sdists
437
438
This metadata system provides the foundation for hatchling's standards-compliant packaging functionality while maintaining flexibility through its plugin architecture.