Source code for vivarium.artifact.entity_key

"""
==========
Entity Key
==========

Provides the :class:`EntityKey` class and :func:`is_entity_key` helper for
working with artifact entity keys formatted as ``"type.name.measure"`` or
``"type.measure"``.

"""

from __future__ import annotations


[docs] def is_entity_key(key: str) -> bool: """Check whether a string is a valid artifact entity key. A valid entity key has the format ``"type.name.measure"`` or ``"type.measure"`` - that is, 2 or 3 non-empty dot-separated elements with no leading, trailing, or consecutive dots. Parameters ---------- key The string to check. Returns ------- True if the string matches the entity key format, False otherwise. """ parts = key.split(".") return len(parts) in (2, 3) and all(parts)
[docs] class EntityKey(str): """A convenience wrapper that translates artifact keys. This class provides several representations of the artifact keys that are useful when working with the :mod:`pandas` and `tables <https://www.pytables.org>`_ HDF interfaces. """ def __init__(self, key: str) -> None: """ Parameters ---------- key The string representation of the entity key. Must be formatted as ``"type.name.measure"`` or ``"type.measure"``. Raises ------ ValueError If the key is improperly formatted. """ if not is_entity_key(key): raise ValueError( f"Invalid format for HDF key: {key}. " 'Acceptable formats are "type.name.measure" and "type.measure"' ) super().__init__() @property def type(self) -> str: """The type of the entity represented by the key.""" return self.split(".")[0] @property def name(self) -> str: """The name of the entity represented by the key.""" return self.split(".")[1] if len(self.split(".")) == 3 else "" @property def measure(self) -> str: """The measure associated with the data represented by the key.""" return self.split(".")[-1] @property def group_prefix(self) -> str: """The HDF group prefix for the key.""" return "/" + self.type if self.name else "/" @property def group_name(self) -> str: """The HDF group name for the key.""" return self.name if self.name else self.type @property def group(self) -> str: """The full path to the group for this key.""" return ( self.group_prefix + "/" + self.group_name if self.name else self.group_prefix + self.group_name ) @property def path(self) -> str: """The full HDF path associated with this key.""" return self.group + "/" + self.measure
[docs] def with_measure(self, measure: str) -> "EntityKey": """Replace this key's measure with the provided one. Parameters ---------- measure The measure to replace this key's measure with. Returns ------- A new EntityKey with the updated measure. """ if self.name: return EntityKey(f"{self.type}.{self.name}.{measure}") else: return EntityKey(f"{self.type}.{measure}")
def __eq__(self, other: object) -> bool: return isinstance(other, str) and str(self) == str(other) def __ne__(self, other: object) -> bool: return not self == other def __hash__(self) -> int: return hash(str(self)) def __repr__(self) -> str: return f"EntityKey({str(self)})"