Source code for openfactory.apps.attributefield

from typing import Any


[docs] class AttributeField: """ Base class for declarative OpenFactory attributes. Instances of this class (or its subclasses) are intended to be declared as class-level attributes inside an :class:`OpenFactoryApp <openfactory.apps.ofaapp.OpenFactoryApp>` class. They are automatically collected by :class:`OpenFactoryAppMeta` into the ``_declared_attributes`` dictionary. Each attribute represents a typed data field (e.g., ``Events`` or ``Samples``) with an associated tag used by OpenFactory. Args: value (Any, Optional): Default value of the attribute. Defaults to ``"UNAVAILABLE"``. type (str, Optional): Attribute type (e.g., ``Events``, ``Samples``). Defaults to ``"Events"``. tag (str): Tag used by OpenFactory. Notes: - ``name`` is assigned automatically when the class is created. - This class is typically not used directly; prefer subclasses like :class:`EventAttribute` or :class:`SampleAttribute`. """
[docs] def __init__(self, *, value: Any = "UNAVAILABLE", type: str = "Events", tag: str): self.value = value self.type = type self.tag = tag self.name = None # will be set by metaclass
[docs] class EventAttribute(AttributeField): """ Represents an ``Events``-type OpenFactory attribute. This is a convenience subclass of :class:`AttributeField` that automatically sets ``type="Events"``. Args: value (Any, Optional): Default value of the attribute. Defaults to ``"UNAVAILABLE"``. tag (str): Tag used by OpenFactory. .. admonition:: Example .. code-block:: python class MyApp(OpenFactoryApp): status = EventAttribute(value="idle", tag="App.Status") """
[docs] def __init__(self, *, value: Any = "UNAVAILABLE", tag: str): super().__init__(value=value, type="Events", tag=tag)
[docs] class SampleAttribute(AttributeField): """ Represents a ``Samples``-type OpenFactory attribute. This is a convenience subclass of :class:`AttributeField` that automatically sets ``type="Samples"``. Args: value (Any, Optional): Default value of the attribute. Defaults to ``"UNAVAILABLE"``. tag (str): External identifier used by OpenFactory. .. admonition:: Example .. code-block:: python class MyApp(OpenFactoryApp): temperature = SampleAttribute(tag="TEMP_SENSOR") """
[docs] def __init__(self, *, value: Any = "UNAVAILABLE", tag: str): super().__init__(value=value, type="Samples", tag=tag)
[docs] class OpenFactoryAppMeta(type): """ Metaclass that collects declarative :class:`AttributeField` instances. When a class is defined with this metaclass, all class-level attributes that are instances of :class:`AttributeField` are automatically collected into a ``_declared_attributes`` dictionary. This enables a declarative style for defining OpenFactory attributes. Behavior: - Attributes from base classes are inherited. - Attributes in subclasses override those from base classes. - Each attribute's ``name`` is automatically set to the variable name used in the class definition. Attributes: _declared_attributes (dict[str, AttributeField]): Mapping of attribute names to their corresponding instances. .. admonition:: Example .. code-block:: python class BaseApp(metaclass=OpenFactoryAppMeta): status = EventAttribute(tag="STATUS") class MyApp(BaseApp): temperature = SampleAttribute(tag="TEMP") status = EventAttribute(tag="OVERRIDE_STATUS") # overrides base MyApp._declared_attributes # { # "status": <EventAttribute tag="OVERRIDE_STATUS">, # "temperature": <SampleAttribute tag="TEMP"> # } MyApp._declared_attributes["status"].name # "status" .. admonition:: Advanced note The metaclass walks the MRO (via base classes) and merges attribute definitions before processing the current class body. This ensures predictable inheritance and override behavior. """ _declared_attributes: dict[str, "AttributeField"] def __new__(cls, name: str, bases: tuple[type, ...], attrs: dict[str, object]) -> "OpenFactoryAppMeta": """ Create a new class, collecting any AttributeField instances declared on the class or its base classes into a ``_declared_attributes`` dict. Args: cls (type): The metaclass itself. name (str): Name of the class being created. bases (tuple[type, ...]): Base classes of the class being created. attrs (dict[str, object]): Attributes defined in the class body. Returns: OpenFactoryAppMeta: A new class object with ``_declared_attributes`` set. Notes: - Base classes are scanned first, so their declarative attributes are inherited by subclasses. - Attributes in the current class override any with the same name from base classes. - ``value.name = key`` ensures the AttributeField knows the name it was assigned to, which can be used later for identification or serialization. """ declared_attributes: dict[str, "AttributeField"] = {} # Collect from base classes first for base in bases: if hasattr(base, "_declared_attributes"): declared_attributes.update(base._declared_attributes) # Collect from current class for key, value in attrs.items(): if isinstance(value, AttributeField): value.name = key declared_attributes[key] = value attrs["_declared_attributes"] = declared_attributes return super().__new__(cls, name, bases, attrs)