Architecture and validation¶
From schema to source¶
The generator reads a full STAC Extension JSON Schema and follows references from
STAC object locations. Item metadata lives under properties; Collection and
Catalog metadata lives at the top level. Assets, item asset definitions, Links,
and Collection summaries are discovered through their respective containers.
Root type constraints restrict applicable branches. Unused definitions are not
turned into extension fields just because they exist.
The title supplies the extension class name, and $id supplies the registration
URI. Field names and constraints come from the schema, with no separate class,
URI, field-pointer, or target overrides.
The package separates these responsibilities:
| Component | Responsibility |
|---|---|
cli.py |
Load inputs, handle options, report diagnostics, and write output. |
bundle.py |
Copy and embed explicitly supplied or fetched external resources. |
schema.py |
Resolve local pointers and discover fields and STAC targets. |
models.py |
Build internal rendering metadata for nested classes and conversion plans. |
generator.py |
Validate the schema, render templates, and check output syntax. |
| Jinja2 templates | Emit the standalone extension and nested value classes. |
Change the input schema and regenerate to change an extension contract. Generated files are artifacts; manual edits will be lost during regeneration.
Runtime integration¶
A generated extension wraps an existing PySTAC object and writes its normal
metadata dictionaries. PySTAC handles serialization through to_dict() and
from_dict(). Nested generated values encode as plain JSON objects and arrays.
Call the generated class's ext() method directly. Generation does not install
into pystac.extensions, register an item.ext accessor, or provide migration
hooks. Extension membership checks the exact canonical schema version.
Lean output contains PySTAC wrappers and only the required conversion helpers.
Themes needs no jsonschema dependency or embedded schema. --include-validation
adds an offline extension validator. Nested object unions also embed the schema
and use jsonschema when matching a representation requires validation. Wide
factory signatures use typing-extensions. The generator, Ruff, and the template
engine are not dependencies of emitted modules.
Validation boundaries¶
Generation validates the schema itself, parses the generated Python source, normalizes imports, and formats it with Ruff. It does not execute the emitted module or validate future data values.
At runtime, property annotations describe expected values. Setters and apply()
do not enforce every schema constraint. apply() checks all field names and
targets before mutation, then assigns values; it is not a general transactional
update if serialization of a later value fails. Nested factories enforce the
presence of unconditional required arguments.
By default, use PySTAC's item.validate() or the equivalent owning-object method
for schema validation. This uses PySTAC's normal registered-schema resolver.
When generated with --include-validation, extension.validate() checks the
complete object against the embedded extension schema, including bounds, formats,
and conditional constraints, without retrieving the extension schema.
For Assets, Links, and ItemAssetDefinitions, validation uses the owning STAC
object and requires an owner. Serialization alone does not validate metadata.
The optional embedded validator checks the extension schema, not STAC core.
PySTAC's object-level validate() also covers core validation; its schema
resolution and network behavior are separate from the embedded validator.
Limitations¶
- Conditional applicability is approximated and produces a generation warning. Accessor availability does not establish that every combination is schema-valid.
- Standalone field-definition fragments do not identify STAC targets. Supply the complete extension schema and reference fields from their intended locations.
- Unprefixed STAC core/common metadata names are reserved during field discovery.
Declared extension fields outside that set, such as
renders, are supported. - Recursive records are supported. Recursive non-object values can use
Anyat recursion boundaries and produce warnings; complex intersections can also have conservative annotations without losing validation constraints. - Ambiguous nested object unions raise an error when no unique representation can be selected. Inspect raw properties or fix the data before decoding again.
- Heterogeneous pattern-property maps and tuple arrays remain raw JSON values with warnings. Dynamic property names do not become named accessors.
- Nested
$idscopes, named anchors,$dynamicRef,$recursiveRef, and mixed schema dialects in bundles are rejected. - Domain-specific calculations, migrations, and custom semantic helpers are not generated. Nested Band records can be modeled, but Band-level attachment helpers are not generated.
Use --strict when generation warnings should stop your build. Keep schema
sources under version control and review regenerated modules alongside schema changes.