Property definitions¶
The semantic vocabulary of httk₂ is based on OPTIMADE property and entry-type
definitions: they are first-class, immutable Python objects in
httk.core.property_definitions, and pair the standard entry types httk-core
vendors with ready-to-use, stdlib-only data models in httk.core.entry_types.
The httk.core.EntryProvider implementations that serve those models live in
the httk-store module.
For the short practical overview, see Property definitions.
A property definition is a self-describing document: it carries a property’s
canonical $id, its OPTIMADE type and unit, its requirements, and a
human-readable description. An entry-type definition bundles the property
definitions of one entry type together with the entry type’s own description.
Vendoring policy¶
The authoritative, supported copies of the standard OPTIMADE entry-type
definitions are the JSON files checked in under
src/httk/registry/schemas/core/ (references, files, calculations). They
are distributed by the Materials-Consortia under the MIT License (see the
LICENSE next to them). httk-core supports exactly the checked-in versions;
the README.md in that directory records their provenance, and
make optimade-defs re-fetches them from the network. Ordinary builds and
tests read the committed copies offline.
Loading a standard definition¶
standard_entry_type returns the vendored definition for one of httk-core’s
standard entry types (references, files, calculations):
from httk.core import standard_entry_type
references = standard_entry_type("references")
print(references.description)
print(len(references.properties), "properties")
assert len(references.properties) == 30
Each property is a PropertyDefinition. Vendored definitions keep their
canonical $ids — note the mix of core (shared) and entry-scoped identifiers
— and every one carries the "1.2" definition-format stamp:
from httk.core import standard_entry_type
references = standard_entry_type("references")
assert references.properties["id"].definition_id.endswith("/properties/core/id")
assert references.properties["title"].definition_id.endswith("/optimade/references/title")
assert all(prop.format_version == "1.2" for prop in references.properties.values())
The "1.2" stamp reflects that httk-core’s generator emits only features that
already exist in format 1.2 of the OPTIMADE property-definition schema, and
the definition format is versioned in lockstep with the specification —
re-stamped only when a definition actually uses newer features. That is why
even calculations (a v1.3 entry type) keeps "1.2"-format property
definitions.
Generating a custom property¶
PropertyDefinition.from_simple generates an implementation-neutral definition
from a compact description. A database-specific property must carry a
registered prefix. The _httk_ prefix is pre-registered under the httk.org
base.
Machine-generated and on-the-fly names use a reserved custom_ sub-namespace
inside that prefix: _<prefix>_custom_<name>, where <prefix> is the token in
the registered prefix. Thus httk’s names are _httk_custom_<name>; they cannot
collide with curated _httk_* definitions outside that sub-namespace.
httk-store’s auto_definition follows the same convention by default.
from httk.core import PropertyDefinition
energy = PropertyDefinition.from_simple(
"_httk_custom_total_energy",
description="Total energy of the calculation.",
fulltype="float",
)
doc = energy.as_optimade()
assert doc["$id"] == "https://schemas.httk.org/ad-hoc/defs/properties/_httk_custom_total_energy"
assert doc["x-optimade-type"] == "float"
assert doc["type"] == ["number", "null"]
Registering a definition prefix¶
The recognized database-specific prefixes are held in a small registry.
_httk_ is pre-registered under the httk.org base. A database serving its
own custom properties registers its prefix once, giving the base URL under
which those properties’ $ids are minted; a prefix must be a lower-case
alphanumeric token wrapped in single underscores:
from httk.core import (
PropertyDefinition,
known_definition_prefixes,
register_definition_prefix,
standard_entry_type,
)
register_definition_prefix("_exmpl_", "https://schemas.example.org/ad-hoc/defs/properties")
assert "_exmpl_" in known_definition_prefixes()
# from_simple routes the prefixed name's $id under the registered base:
wave_class = PropertyDefinition.from_simple(
"_exmpl_wave_class", description="Altermagnetic wave class.", fulltype="string"
)
assert wave_class.as_optimade()["$id"] == "https://schemas.example.org/ad-hoc/defs/properties/_exmpl_wave_class"
# ...and extended() accepts the registered prefix as a custom property:
references = standard_entry_type("references").extended({"_exmpl_wave_class": wave_class})
assert "_exmpl_wave_class" in references.properties
An invalid prefix (e.g. "exmpl", "_Exmpl_", "exmpl_") raises a clear
ValueError, and re-registering an existing prefix overwrites its base.
Per-deployment sortable/response-default flags are layered on separately,
so the definition itself stays neutral:
from httk.core import PropertyDefinition
energy = PropertyDefinition.from_simple("_httk_custom_total_energy", description="E", fulltype="float")
served = energy.with_implementation(sortable=False, response_default=True)
assert served.as_optimade()["x-optimade-implementation"] == {"sortable": False, "response-default": True}
assert served.definition_id == energy.definition_id
# The original is untouched:
assert "x-optimade-implementation" not in energy.as_optimade()
Extending an entry type¶
EntryTypeDefinition.extended merges custom properties into a copy of a
standard definition. Unprefixed custom names are rejected (OPTIMADE reserves
them for standard properties):
from httk.core import PropertyDefinition, standard_entry_type
energy = PropertyDefinition.from_simple("_httk_custom_total_energy", description="E", fulltype="float")
standard = standard_entry_type("calculations")
calculations = standard.extended({"_httk_custom_total_energy": energy})
assert "_httk_custom_total_energy" in calculations.properties
assert calculations.definition_id is None
assert calculations.extends_id == standard.definition_id
try:
standard.extended(
{"cogwheels": PropertyDefinition.from_simple("cogwheels", description="w", fulltype="integer")}
)
except ValueError as exc:
assert "_httk_" in str(exc)
Extension and $id¶
EntryTypeDefinition.extended() creates a new entry-type document. It drops
the source document’s $id, because the extended document is no longer that
identified standard resource. Its extends_id retains the original standard
IRI; chained extensions keep that original IRI. Consequently, httk-serve
emits a describedby link only when definition_id exists, so an extended
entry type has no describedby link for the standard $id.
PropertyDefinition.with_implementation() has a different identity policy.
It keeps the standard $id and x-optimade-definition, because implementation
annotations do not change the definition’s meaning. The vendored OPTIMADE v1.2
Property Definitions meta-schema
says definitions “SHOULD be regarded as the same if they only differ by”
changes to x-optimade-implementation. The specification’s identity rule — a
redefinition “MUST change the $id” — therefore applies when the meaning
changes, not to these deployment annotations.
For authors: if a definition’s meaning changes, give it a new IRI and never
re-serve the modified definition under the standard $id.
Entry-type record models¶
httk.core.entry_types provides one frozen dataclass per standard entry type
(Reference, File, Calculation), each carrying a field for every non-core
property of its standard. id comes from a provider’s mapping key and type is
constant, so neither is a dataclass field. create accepts either an instance
or a plain mapping (unknown keys are rejected), which is how a provider ingests
records:
from httk.core import Reference
ref = Reference.create(
{"title": "A study of gallium titanium compounds", "doi": "10.1234/demo.2021.1"}
)
assert ref.title == "A study of gallium titanium compounds"
assert ref.year is None # every non-core property defaults to None
# create() is idempotent on an existing instance:
assert Reference.create(ref) is ref
These models use only the standard library. The httk.core.EntryProvider
implementations that map {id: record} mappings of them onto the neutral
provider contract — and self-register as store-references, store-files, and
store-calculations — live in the httk-store module, together with
property-definition validation built on the definitions above. That keeps
httk-core a dependency-free layer of contracts and models, with the concrete
capabilities provided by the modules built on top of it.
Dataset and service metadata records¶
Alongside the per-entry record models, httk-core carries neutral, DCAT-shaped
contracts for describing whole datasets and the services that serve them,
independently of any transport or provider. Dataset describes one published
dataset and carries an ordered tuple of DatasetDistribution values (one per
retrievable representation); Service describes an endpoint and the standards
it conforms to. DatasetRecord and ServiceRecord are the storable subclasses
that carry the core storage contract.
from httk.core import Dataset, DatasetDistribution, Service
dataset = Dataset(
id="https://example.org/datasets/demo",
title="Demo dataset",
description="A small demonstration dataset.",
publisher_id="https://example.org",
publisher_name="Example Org",
distributions=(
DatasetDistribution(
id="https://example.org/datasets/demo/sqlar",
access_url="/datasets/demo/data.sqlar", # root-relative: resolved by the serving app
),
),
)
service = Service(
id="https://example.org/optimade",
title="Demo OPTIMADE endpoint",
endpoint_url="https://example.org/optimade/v1",
conforms_to=("https://schemas.optimade.org/defs/v1.2/standard",),
serves_dataset_ids=("https://example.org/datasets/demo",),
)
Identifier and vocabulary fields must be well-formed absolute IRIs; a
distribution’s access_url may instead be a root-relative URL (a leading
/… path with no scheme or host) so that the serving application can resolve it
against its own deployment base. The validation behind these fields is exposed
as reusable predicates in httk.core.validation.iris —
is_absolute_iri, is_https_url, is_root_relative_url,
has_valid_percent_escapes, and urlsplit — for callers that need the same
URL/IRI checks elsewhere.