Source code for haive.core.common.mixins.identifier
"""Identifier mixin for unique identification of objects.This module provides a mixin class that adds UUID-based identification andhuman-readable naming to Pydantic models. The mixin handles validation,generation, and utility methods for working with identifiers.Uses Pydantic v2 patterns with field_validator and computed fields.Usage: from haive.core.common.mixins.identifier import IdentifierMixin class MyComponent(IdentifierMixin, BaseModel): # Other fields content: str def __init__(self, **data): super().__init__(**data) # Now the component has an ID and optional name # Create with auto-generated ID component = MyComponent(content="Hello") print(component.id) # UUID string print(component.short_id) # First 8 chars of UUID # Create with custom name named_component = MyComponent(content="Hello", name="GreetingComponent") print(named_component.display_name) # "GreetingComponent""""importuuidfromtypingimportSelffromuuidimportUUIDfrompydanticimport(BaseModel,Field,PrivateAttr,computed_field,field_validator,model_validator,)
[docs]classIdentifierMixin(BaseModel):"""Mixin that adds unique identification to any Pydantic model. This mixin provides both UUID-based identification and human-readable naming capabilities. It automatically generates UUIDs, validates provided IDs, and offers convenience methods for working with the identifiers. Attributes: id: A UUID string that uniquely identifies the object. name: An optional human-readable name for the object. short_id: First 8 characters of the UUID (computed). display_name: User-friendly name for display (computed). uuid_obj: UUID object representation of the ID (computed). has_custom_name: Whether a custom name is set (computed). """id:str=Field(default_factory=lambda:str(uuid.uuid4()),description="Unique identifier for this object",)name:str|None=Field(default=None,description="Human-readable name for this object")# Private attribute for UUID object_uuid_obj:UUID|None=PrivateAttr(default=None)
[docs]@field_validator("id")@classmethoddefvalidate_id(cls,v:str)->str:"""Ensure ID is a valid UUID string. Args: v: The ID string to validate. Returns: The validated ID string, or a new UUID if invalid. """try:# Validate that it's a proper UUIDUUID(v)returnvexceptValueError:# If not valid, generate a new UUIDreturnstr(uuid.uuid4())
[docs]@field_validator("name")@classmethoddefvalidate_name(cls,v:str|None)->str|None:"""Validate and clean the name field. Args: v: The name string to validate. Returns: The cleaned name string, or None if empty after cleaning. """ifvisnotNone:# Strip whitespace and ensure non-emptycleaned=v.strip()returncleanedifcleanedelseNonereturnv
[docs]@model_validator(mode="after")definitialize_uuid_obj(self)->Self:"""Initialize UUID object after model validation. Returns: Self, with the _uuid_obj private attribute initialized. """try:self._uuid_obj=UUID(self.id)exceptValueError:# If id is somehow not a valid UUID, regenerateself._uuid_obj=uuid.uuid4()self.id=str(self._uuid_obj)returnself
@computed_field@propertydefshort_id(self)->str:"""Short version of the ID (first 8 characters). Returns: The first 8 characters of the UUID string. """returnself.id[:8]@computed_field@propertydefdisplay_name(self)->str:"""Display name (uses name if available, otherwise short_id). Returns: The human-readable name if set, otherwise "Object-{short_id}". """returnself.nameorf"Object-{self.short_id}"@computed_field@propertydefuuid_obj(self)->UUID:"""UUID object representation of the ID. Returns: The UUID object corresponding to the ID string. """ifself._uuid_objisNone:self._uuid_obj=UUID(self.id)returnself._uuid_obj@computed_field@propertydefhas_custom_name(self)->bool:"""Whether this object has a custom name (not auto-generated). Returns: True if a non-empty name is set, False otherwise. """returnself.nameisnotNoneandself.name.strip()!=""
[docs]defregenerate_id(self)->str:"""Generate a new ID and return it. This method creates a new UUID, updates the ID field, and returns the new ID string. Returns: The newly generated UUID string. """self._uuid_obj=uuid.uuid4()self.id=str(self._uuid_obj)returnself.id
[docs]defset_name(self,name:str)->None:"""Set the name with validation. Args: name: The new name to set. """ifnameandname.strip():self.name=name.strip()else:self.name=None
[docs]defclear_name(self)->None:"""Clear the name."""self.name=None
[docs]defmatches_id(self,id_or_name:str)->bool:"""Check if this object matches the given ID or name. This method checks if the provided string matches this object's full ID, short ID, or name (case-insensitive). Args: id_or_name: The ID or name string to check against. Returns: True if there's a match, False otherwise. """ifnotid_or_name:returnFalse# Check full IDifself.id==id_or_name:returnTrue# Check short IDifself.short_id==id_or_name:returnTrue# Check name (case-insensitive)returnbool(self.nameandself.name.lower()==id_or_name.lower())
[docs]defidentifier_info(self)->dict[str,str]:"""Get comprehensive identifier information. Returns: A dictionary containing all identifier-related information. """return{"id":self.id,"short_id":self.short_id,"name":self.nameor"unnamed","display_name":self.display_name,"has_custom_name":self.has_custom_name,}