httk.data¶
httk-data: the data-management capability layer for httk v2.
Built on the stdlib-only contracts and models in httk-core, httk-data supplies capabilities:
in-memory
EntryProviderimplementations for the standard OPTIMADE entry types (ReferenceEntryProvider,FileEntryProvider,CalculationEntryProvider), serving httk-core’s record models through the neutral provider contract; andproperty-definition validation (
validate_property(),validate_record(),PropertyValidationError) built onjsonschema(Draft 2020-12), checking record values against their OPTIMADE property definitions fully offline; andthe store/searcher query protocols (
httk.data.query) — the backend-agnostic query contract implemented by httk data stores and consumed by serving modules; andthe generic OPTIMADE filter translation (
httk.data.optimade_query) — turning filter syntax trees parsed byhttk.core.parse_optimade_filter()into search expressions over the query protocols (translate_filter_ast(),filter_searcher()), with neutralFilterTranslationErrorcategories; andthe database storage layer (
httk.data.db, requiring thehttk-data[db]extra) — relational storage and querying of plain frozen dataclasses (SqlStoreover SQLite or DuckDB), served through the provider contract byStoreEntryProvider.
The providers self-register (under httk.handlers.data, as
data-references/data-files/data-calculations/data-db-store)
when httk.core discovers the module, so a serving module (such as
httk-optimade) can find them through the registry.
Submodules¶
Attributes¶
Why a filter could not be translated (see |
|
Per-property translation callables, keyed by property name. |
|
Resolve a depth-1 relationship filter to the matching related-entry ids. |
|
Exceptions¶
A filter cannot be translated into a search expression. |
|
A value did not conform to its OPTIMADE property definition. |
Classes¶
Serves OPTIMADE |
|
Serves OPTIMADE |
|
Serves OPTIMADE |
|
A single query under construction, and its results once iterated. |
|
Base class for protocol classes. |
|
A queryable field of a search variable. |
|
One match: the declared output values, and the names they were declared under. |
|
A query variable bound to a target type; attribute access yields fields. |
|
Base class for protocol classes. |
Functions¶
|
Build a |
|
Convert a filter constant node to a Python value of the property's |
|
A |
|
Build a filter handler table for an entry type from a property-key map. |
|
Translate one filter syntax-tree node into a search expression. |
|
Validate a single |
|
Validate every property present in |
Package Contents¶
- class httk.data.CalculationEntryProvider(entries: collections.abc.Mapping[str, httk.core.Calculation | collections.abc.Mapping[str, Any]], *, relationships: collections.abc.Mapping[str, collections.abc.Iterable[httk.core.RelatedEntry]] | None = None)[source]¶
Bases:
httk.core.EntryProviderServes OPTIMADE
calculationsfrom a mapping of id toCalculation.relationshipsoptionally maps a calculation id to its related entries (RelatedEntryvalues, served flat per id) — e.g. itsinput/outputfiles, expressed via therolemetadata.- entry_types() collections.abc.Mapping[str, httk.core.EntryTypeDefinition][source]¶
Return the served entry types keyed by name.
Each value is an
EntryTypeDefinitiondescribing the entry type and its properties. The subset a provider actually serves is named byproperty_keys(); a definition may describe more properties than are served.
- property_keys(entry_type: str) collections.abc.Mapping[str, str][source]¶
Return the served-property-name to record-key map for
entry_type.The mapping MUST include entries for at least
idandtype. Every key names a property described byentry_types(); every value names the key under which that property’s value is found in a record fromrecords().
- records(entry_type: str) collections.abc.Iterable[collections.abc.Mapping[str, Any]][source]¶
Yield the records for
entry_typeas plain JSON-able mappings.Each record is a mapping keyed by the record keys named in
property_keys(); values are JSON-able (strings, numbers, booleans,None, or nested lists/dicts of the same).
- relationships(entry_type: str) collections.abc.Mapping[str, tuple[httk.core.RelatedEntry, Ellipsis]][source]¶
Return the related entries for each record of
entry_type.The result maps an entry id to a flat tuple of
RelatedEntryvalues, e.g.{"struct-1": (RelatedEntry("references", "ref-1"), RelatedEntry("references", "ref-2", description="Cites the method"))}. Grouping the related entries by related entry type is the serving layer’s concern (JSON:API groups them at render time). This is the neutral source of an OPTIMADE relationships block: a consumer turns each related entry into a resource identifier under its entry type (carrying thedescription/rolemetadata when present), and aninclude=<type>request then embeds those related resources. The default implementation returns an empty mapping (no relationships); a provider overrides it to declare them. Ids referring to records this provider (or a sibling provider serving the related type) does not supply are simply not resolvable by the consumer.
- class httk.data.FileEntryProvider(entries: collections.abc.Mapping[str, httk.core.File | collections.abc.Mapping[str, Any]], *, relationships: collections.abc.Mapping[str, collections.abc.Iterable[httk.core.RelatedEntry]] | None = None)[source]¶
Bases:
httk.core.EntryProviderServes OPTIMADE
filesfrom a mapping of id toFile.relationshipsoptionally maps a file id to its related entries (RelatedEntryvalues, served flat per id) — e.g. the calculations a file isinput/outputof.- entry_types() collections.abc.Mapping[str, httk.core.EntryTypeDefinition][source]¶
Return the served entry types keyed by name.
Each value is an
EntryTypeDefinitiondescribing the entry type and its properties. The subset a provider actually serves is named byproperty_keys(); a definition may describe more properties than are served.
- property_keys(entry_type: str) collections.abc.Mapping[str, str][source]¶
Return the served-property-name to record-key map for
entry_type.The mapping MUST include entries for at least
idandtype. Every key names a property described byentry_types(); every value names the key under which that property’s value is found in a record fromrecords().
- records(entry_type: str) collections.abc.Iterable[collections.abc.Mapping[str, Any]][source]¶
Yield the records for
entry_typeas plain JSON-able mappings.Each record is a mapping keyed by the record keys named in
property_keys(); values are JSON-able (strings, numbers, booleans,None, or nested lists/dicts of the same).
- relationships(entry_type: str) collections.abc.Mapping[str, tuple[httk.core.RelatedEntry, Ellipsis]][source]¶
Return the related entries for each record of
entry_type.The result maps an entry id to a flat tuple of
RelatedEntryvalues, e.g.{"struct-1": (RelatedEntry("references", "ref-1"), RelatedEntry("references", "ref-2", description="Cites the method"))}. Grouping the related entries by related entry type is the serving layer’s concern (JSON:API groups them at render time). This is the neutral source of an OPTIMADE relationships block: a consumer turns each related entry into a resource identifier under its entry type (carrying thedescription/rolemetadata when present), and aninclude=<type>request then embeds those related resources. The default implementation returns an empty mapping (no relationships); a provider overrides it to declare them. Ids referring to records this provider (or a sibling provider serving the related type) does not supply are simply not resolvable by the consumer.
- class httk.data.ReferenceEntryProvider(entries: collections.abc.Mapping[str, httk.core.Reference | collections.abc.Mapping[str, Any]], *, relationships: collections.abc.Mapping[str, collections.abc.Iterable[httk.core.RelatedEntry]] | None = None)[source]¶
Bases:
httk.core.EntryProviderServes OPTIMADE
referencesfrom a mapping of id toReference.relationshipsoptionally maps a reference id to its related entries (RelatedEntryvalues, served flat per id).- entry_types() collections.abc.Mapping[str, httk.core.EntryTypeDefinition][source]¶
Return the served entry types keyed by name.
Each value is an
EntryTypeDefinitiondescribing the entry type and its properties. The subset a provider actually serves is named byproperty_keys(); a definition may describe more properties than are served.
- property_keys(entry_type: str) collections.abc.Mapping[str, str][source]¶
Return the served-property-name to record-key map for
entry_type.The mapping MUST include entries for at least
idandtype. Every key names a property described byentry_types(); every value names the key under which that property’s value is found in a record fromrecords().
- records(entry_type: str) collections.abc.Iterable[collections.abc.Mapping[str, Any]][source]¶
Yield the records for
entry_typeas plain JSON-able mappings.Each record is a mapping keyed by the record keys named in
property_keys(); values are JSON-able (strings, numbers, booleans,None, or nested lists/dicts of the same).
- relationships(entry_type: str) collections.abc.Mapping[str, tuple[httk.core.RelatedEntry, Ellipsis]][source]¶
Return the related entries for each record of
entry_type.The result maps an entry id to a flat tuple of
RelatedEntryvalues, e.g.{"struct-1": (RelatedEntry("references", "ref-1"), RelatedEntry("references", "ref-2", description="Cites the method"))}. Grouping the related entries by related entry type is the serving layer’s concern (JSON:API groups them at render time). This is the neutral source of an OPTIMADE relationships block: a consumer turns each related entry into a resource identifier under its entry type (carrying thedescription/rolemetadata when present), and aninclude=<type>request then embeds those related resources. The default implementation returns an empty mapping (no relationships); a provider overrides it to declare them. Ids referring to records this provider (or a sibling provider serving the related type) does not supply are simply not resolvable by the consumer.
- type httk.data.FilterTranslationCategory = Literal['unrecognized-property', 'not-implemented', 'type-mismatch', 'internal'][source]¶
Why a filter could not be translated (see
FilterTranslationError).
- exception httk.data.FilterTranslationError(message: str, category: FilterTranslationCategory, detail: str | None = None)[source]¶
Bases:
ExceptionA filter cannot be translated into a search expression.
The exception message describes the failure;
categoryclassifies it neutrally (this module knows nothing about transports, so consumers map each category onto their own error codes):"unrecognized-property"— the filter names an unknown property carrying a recognized prefix (a caller error);"type-mismatch"— a filter constant does not match the property’s declared type (a caller error);"not-implemented"— the filter uses a construct this translation (or the supplied handler table) does not support;"internal"— an inconsistency in the translation itself.
detailoptionally carries extra machine-readable context.- category: FilterTranslationCategory¶
- detail = None¶
- type httk.data.HandlerTable = Mapping[str, Mapping[str, Callable[..., Any]]][source]¶
Per-property translation callables, keyed by property name.
The inner mapping’s keys name the filter-operation families:
'comparison'(=,!=,<,<=,>,>=),'stringmatching'(CONTAINS/STARTS/ENDS),'HAS'(the set operations),'length'(LENGTH), and'unknown'(IS KNOWN/IS UNKNOWN). A'HAS'handler is called ashandler(property, ops, values, search_variable, has_type)and returns a plainSearchExpression. The caller appliesNOTas~; handlers do not receive negation state or report post-filter placement. Dotted'<type>.id'entries provide relationship-id filtering (seerelationship_id_handler()).
- type httk.data.RelatedPropertyResolver = Callable[[str, FilterAst], tuple[str, ...]][source]¶
Resolve a depth-1 relationship filter to the matching related-entry ids.
Called as
resolver(related_type, sub_ast)wheresub_astis the filter node with the<related_type>.prefix stripped from its identifier (and without any surroundingNOT); returns the ids of the related entries matching the sub-filter, evaluated against the related type’s own properties.
- httk.data.filter_searcher(store: httk.data.query.Store, target: Any, filter_string: str | httk.core.FilterAst, *, entry_type: str, property_fulltypes: collections.abc.Mapping[str, str], property_keys: collections.abc.Mapping[str, str] | None = None, handlers: HandlerTable | None = None, recognized_prefixes: tuple[str, Ellipsis] = (), relationship_targets: tuple[str, Ellipsis] = (), related_property_resolver: RelatedPropertyResolver | None = None) httk.data.query.Searcher[source]¶
Build a
Searcheroverstoreapplying an OPTIMADE filter.filter_stringis an OPTIMADE filter string (parsed withhttk.core.parse_optimade_filter()) or an already-parsedFilterAst. The searcher binds one search variable totarget(the store-specific query target, declared as the searcher output namedentry_type) and applies the translated filter. Whenhandlersis not supplied, a default table is built withsimple_property_handlers()fromproperty_keys(or, whenproperty_keysis also None, from an identity map overproperty_fulltypes). The remaining keyword arguments are passed through totranslate_filter_ast().- Raises:
FilterTranslationError – When the filter cannot be translated.
httk.core.ParserSyntaxError – When a filter string does not parse.
- httk.data.format_value(fulltype: str, val: tuple[Any, Ellipsis], allow_null: bool = False) Any[source]¶
Convert a filter constant node to a Python value of the property’s
fulltype.- Raises:
FilterTranslationError – With category
"type-mismatch"when the constant does not fit the declared type, or"not-implemented"for dictionary-typed properties.
- httk.data.relationship_id_handler(rel_key: str) collections.abc.Mapping[str, collections.abc.Callable[Ellipsis, Any]][source]¶
A
'<type>.id'handler-table entry matching related ids over a list-valued field.rel_keynames a list-valued backend field holding the related entry ids; the returned{'HAS': ...}mapping serves<type>.id HAS ...filters (and the semi-join rewrites of other dotted filters) with the standard set-operation semantics ofset_handler().
- httk.data.simple_property_handlers(entry_type: str, property_keys: collections.abc.Mapping[str, str], property_fulltypes: collections.abc.Mapping[str, str]) dict[str, collections.abc.Mapping[str, collections.abc.Callable[Ellipsis, Any]]][source]¶
Build a filter handler table for an entry type from a property-key map.
Always provides the standard
id(matched against the__idfield) andtype(a constant equal toentry_type) handlers. For every property named inproperty_keys(which maps property names to backend field names), handlers are generated from the property’s fulltype inproperty_fulltypes(default"string"): string properties get comparison and stringmatching handlers; integer and float properties get a numeric comparison handler;list of ...properties get a HAS (set membership) handler. Every generated property also gets aknownunknown handler.
- httk.data.translate_filter_ast(node: httk.core.FilterAst, search_variable: httk.data.query.SearchVariable, entry_type: str, property_fulltypes: collections.abc.Mapping[str, str], handlers: HandlerTable, recognized_prefixes: tuple[str, Ellipsis], *, recursion: int = 0, relationship_targets: tuple[str, Ellipsis] = (), related_property_resolver: RelatedPropertyResolver | None = None) httk.data.query.SearchExpression[source]¶
Translate one filter syntax-tree node into a search expression.
nodeis aFilterAstnode (as produced byhttk.core.parse_optimade_filter());search_variableis the backend search variable the expression is built against;property_fulltypes,handlers, andrecognized_prefixesdrive the translation as described in the module docstring;recursioncounts the nesting depth.Returns the translated expression, ready to hand to
add().NOTtranslates to the backend’s~; a backend whose set predicates need a second (post-filter) evaluation position derives that from the expression itself, so the caller never has to.Relationship filtering: an identifier dotted with a name in
relationship_targets(e.g.('Identifier', 'references', 'doi')) filters on the properties of related entries.<type>.id HAS ...is served directly by the'<type>.id'handler-table entry. Every other depth-1 dotted filter — comparisons (including<type>.id = ...), string matching,IS KNOWN/IS UNKNOWN, and the HAS family over related list properties — is resolved by a two-phase semi-join: the<type>.prefix is stripped from the node, the resulting sub-filter is passed torelated_property_resolver(without any surroundingNOT— inversion applies to the resulting id-set membership), and the returned ids are substituted back as<type>.id HAS ANY <ids>(an empty id set translates to a constant-false expression). Each dotted node is resolved independently:references.doi CONTAINS "x" AND references.year >= 2000matches entries where some related reference matches the doi condition and some — possibly different — related reference matches the year condition. Without a resolver, dotted filters other than<type>.id HAS ...are not implemented; nested (deeper than depth-1) paths and dottedLENGTHfilters are never supported.- Raises:
FilterTranslationError – See
FilterTranslationErrorfor the failure categories.
- class httk.data.Searcher[source]¶
Bases:
ProtocolA single query under construction, and its results once iterated.
Iteration yields one
SearchResultper match, soitem[0][0]is the first declared output of the match (typically the matched row object). The expressions received byaddare always ones produced by this same backend’s search variables, so implementations may type them as their own expression class; a backend that needs a second (post-filter) evaluation position decides that from the expression itself, not from the caller.
- class httk.data.SearchExpression[source]¶
Bases:
ProtocolBase class for protocol classes.
Protocol classes are defined as:
class Proto(Protocol): def meth(self) -> int: ...
Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).
For example:
class C: def meth(self) -> int: return 0 def func(x: Proto) -> int: return x.meth() func(C()) # Passes static type check
See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as:
class GenProto[T](Protocol): def meth(self) -> T: ...
- class httk.data.SearchField[source]¶
Bases:
ProtocolA queryable field of a search variable.
In addition to the methods below, fields support the rich comparison operators (
==,!=,<,<=,>,>=), returningSearchExpression. The handlers invoke those viagetattr(field, '__eq__')(value)since the comparison dunders cannot be typed as expression-returning.The three string-matching methods take literal text: no wildcard or pattern syntax whatsoever crosses this contract, so
%and_(and any other metacharacter) match themselves. A backend is therefore free to implement them with SQLLIKEover an escaped pattern, with a regular expression, or with a full-text index — the choice is invisible here.- has_any(*values: Any) SearchExpression[source]¶
- has_only(*values: Any) SearchExpression[source]¶
- contains(text: str) SearchExpression[source]¶
Match values containing
textas a literal substring.
- startswith(prefix: str) SearchExpression[source]¶
Match values beginning with the literal
prefix.
- endswith(suffix: str) SearchExpression[source]¶
Match values ending with the literal
suffix.
- class httk.data.SearchResult[source]¶
Bases:
NamedTupleOne match: the declared output values, and the names they were declared under.
valuesholds one entry perSearcher.output()call in declaration order; it is a tuple, sovalues, names = resultandresult[0][0]both work.
- class httk.data.SearchVariable[source]¶
Bases:
ProtocolA query variable bound to a target type; attribute access yields fields.
always_true/always_falseare reserved names: they are real methods of the variable, never stored fields resolved through__getattr__. They exist so a translation layer can express a constant truth value without inventing a probe field. Afield == fieldprobe is NULL-unsound, since it yields NULL (not true) for a NULL field.- always_true() SearchExpression[source]¶
An expression that matches every row.
- always_false() SearchExpression[source]¶
An expression that matches no row.
- class httk.data.Store[source]¶
Bases:
ProtocolBase class for protocol classes.
Protocol classes are defined as:
class Proto(Protocol): def meth(self) -> int: ...
Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).
For example:
class C: def meth(self) -> int: return 0 def func(x: Proto) -> int: return x.meth() func(C()) # Passes static type check
See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as:
class GenProto[T](Protocol): def meth(self) -> T: ...
- exception httk.data.PropertyValidationError(name: str, message: str)[source]¶
Bases:
ValueErrorA value did not conform to its OPTIMADE property definition.
Carries the offending property
nameand a human-readablemessage. For single-value failures the message wraps the underlyingjsonschemaerror message, and thatjsonschema.exceptions.ValidationErroris preserved as the chained__cause__.- name¶
- message¶
- httk.data.validate_property(definition: httk.core.PropertyDefinition, value: Any) None[source]¶
Validate a single
valueagainstdefinition’s JSON-Schema payload.Builds a
jsonschema.Draft202012Validatordirectly from the definition’s document (with the$schemameta-schema reference removed) and validatesvalueagainst it. ReturnsNoneon success; raisesPropertyValidationErroron failure, chaining the underlyingjsonschema.exceptions.ValidationErroras the cause. No network access or registry lookup ever happens.
- httk.data.validate_record(entry_type: httk.core.EntryTypeDefinition, record: collections.abc.Mapping[str, Any]) None[source]¶
Validate every property present in
recordagainstentry_type.Each key in
recordmust be described byentry_type; unknown property names are rejected with aPropertyValidationErrornaming them and the entry type.idandtypemust both be present. Properties described by the definition but absent fromrecordare simply not checked (serving a subset of the described properties is normal). The value of every property that is present is validated viavalidate_property(). ReturnsNoneon success.