upxo.material.registry module

registry.py

Extensible, typed, provenance-tracking container for material data.

Replaces upxo._sup.ODDict.ObjectDataDictionary, which flattened every appended dataclass to a plain dict via dataclasses.asdict() the instant it was stored, discarding all type information immediately and offering no validation. MaterialRegistry keeps typed instances and lets new data categories be declared via register_category() rather than requiring a central build() function to be edited (import + append) every time a category is added.

Validation is deliberately soft: unrecognized field values warn but never block, so the registry stays usable for exploratory work and materials/ processing routes not yet on any curated list – the point is catching likely typos, not gatekeeping legitimate but unanticipated input.

Usage

>>> from dataclasses import dataclass
>>> from upxo.material.registry import MaterialRegistry
>>>
>>> @dataclass
... class MaterialIdentity:
...     name: str = "cu"
...     alloy: str = "value"
>>>
>>> registry = MaterialRegistry()
>>> registry.register_category(
...     "MaterialIdentity", MaterialIdentity,
...     known_values={"name": {"OFHC-Cu", "CuCrZr"}})
>>> registry.ingest(
...     "MaterialIdentity", MaterialIdentity(name="OFHC-Cu"),
...     source="user input")
>>> registry.get("MaterialIdentity").name
'OFHC-Cu'
>>> registry.get_provenance("MaterialIdentity").source
'user input'
class upxo.material.registry.MaterialRegistry[source]

Bases: object

Typed, extensible container for material data categories.

Register dataclass types with register_category(), store instances with ingest() (soft-validates known field values), and retrieve data / provenance via get() / get_provenance(). Built by upxo.material.Material.build() with default category set.

Validation is soft: unknown values warn but never block, so exploratory materials and processing routes remain usable.

register_category(name: str, dataclass_type: Type, known_values: Dict[str, Set[Any]] | None = None) → None[source]

Declare that name maps to dataclass_type.

Parameters:
  • name (str) – Category name (e.g. "MaterialIdentity"). Conventionally the dataclass’s own class name, but not required to be – multiple categories of the same underlying type can be registered under different names if ever needed.

  • dataclass_type (type) – The dataclass every instance ingested under this category must be an instance of.

  • known_values (dict, optional) – Maps field name -> a set of recognized values for that field. Used only for soft-validation warnings on ingest() (unknown values warn, never block).

Notes

Calling this again for an already-registered name replaces its type/known_values – it does not merge with a prior registration.

ingest(category: str, instance: Any, *, source: str | None = None, method: str | None = None, params: dict | None = None) → None[source]

Store instance under category, with provenance.

Parameters:
  • category (str) – Must already be registered via register_category().

  • instance (Any) – Must be an instance of that category’s registered dataclass type.

  • source – Forwarded into a new Provenance record attached to this ingestion – see that class for what each means.

  • method – Forwarded into a new Provenance record attached to this ingestion – see that class for what each means.

  • params – Forwarded into a new Provenance record attached to this ingestion – see that class for what each means.

Raises:
  • KeyError – If category hasn’t been registered yet.

  • TypeError – If instance isn’t an instance of the registered dataclass type for category.

Warns:

UserWarning – For every field named in this category’s known_values (from registration) whose current value on instance isn’t in the recognized set. The value is still stored – this is soft validation, not a block.

get(category: str) → Any[source]

Return the stored instance for category.

Raises:

KeyError – If nothing has been ingested yet for category.

get_provenance(category: str) → Provenance | None[source]

Return the Provenance record for category, or None if nothing has been ingested yet for it.

categories() → List[str][source]

Return every registered category name, in registration order.