Source code for haive.core.utils.haive_collections
"""from typing import AnyCollection utilities for Haive Core.This module includes advanced collection types and utilities that extendstandard Python collections with additional functionality."""importuuidfromcollections.abcimportIterablefromtypingimportAny,Generic,TypeVarfrompydanticimportBaseModel,Field,model_validatorfromhaive.core.utils.getter_mixinimportGetterMixinT=TypeVar("T")
[docs]classNamedDict(BaseModel,Generic[T],GetterMixin[T]):"""A dictionary that automatically builds keys from object names. This class combines dictionary-like access with attributes extraction and rich lookup capabilities from GetterMixin. """values:dict[str,T]=Field(default_factory=dict)name_attrs:list[str]=Field(default=["name","__name__"])model_config={"arbitrary_types_allowed":True}
[docs]@model_validator(mode="before")@classmethoddefconvert_input(cls,data:Any)->Any:"""Process input data into the expected format."""# Already a dictionary with valuesifisinstance(data,dict)and"values"indata:returndata# Plain dictionary without 'values'ifisinstance(data,dict)and"values"notindata:return{"values":data}# Convert list/iterable to dictionaryifisinstance(data,list|tuple|set):# Get name attributes from data if availablename_attrs=(data.get("name_attrs",["name","__name__"])ifisinstance(data,dict)else["name","__name__"])# Convert each item to name-based entryresult={}foritemindata:# Extract key from objectkey=cls._extract_key(item,name_attrs)# If key found, add to resultifkey:result[key]=itemreturn{"values":result}returndata
@staticmethoddef_extract_key(obj:Any,attrs:list[str])->str|None:"""Extract a key from an object using the specified attributes. Args: obj: Object to extract key from attrs: Attribute names to try Returns: Extracted key as string or None if no key found """# Handle direct string valueifisinstance(obj,str):returnobj# Dictionary lookupifisinstance(obj,dict):forattrinattrs:ifattrinobj:returnstr(obj[attr])returnNone# Object attribute lookupforattrinattrs:ifhasattr(obj,attr):value=getattr(obj,attr)# Skip methods (but allow __name__)ifcallable(value)andattr!="__name__":continuereturnstr(value)returnNone# Dictionary-like access methodsdef__getitem__(self,key:str)->T:"""Get item by key."""returnself.values[key]def__setitem__(self,key:str,value:T)->None:"""Set item by key."""self.values[key]=valuedef__delitem__(self,key:str)->None:"""Delete item by key."""delself.values[key]def__contains__(self,key:str)->bool:"""Check if key exists."""returnkeyinself.valuesdef__len__(self)->int:"""Get number of items."""returnlen(self.values)def__iter__(self):"""Iterate through values."""returniter(self.values.values())# Implement GetterMixin abstract methoddef_get_items(self)->list[T]:"""Get all items in the dictionary."""returnlist(self.values.values())# Enhanced accessor methods
[docs]defget(self,key:str,default:Any=None)->Any:"""Get an item with a default value."""ifkeyinself.values:returnself.values[key]returndefault
[docs]defadd(self,item:T,key:str|None=None)->str:"""Add an item with automatic or explicit key. Args: item: Item to add key: Optional explicit key Returns: Key used to store the item """ifkeyisNone:# Try to extract keykey=self._extract_key(item,self.name_attrs)# If no key found, generate UUIDifnotkey:key=str(uuid.uuid4())self.values[key]=itemreturnkey
[docs]defupdate(self,items:dict[str,T]|Iterable[T])->None:"""Update with dictionary or iterable. Args: items: Dictionary or iterable of items to add """ifisinstance(items,dict):self.values.update(items)else:foriteminitems:self.add(item)
[docs]defkeys(self)->list[str]:"""Get all keys."""returnlist(self.values.keys())
[docs]defitems(self)->Any:"""Get all key-value pairs."""returnself.values.items()
[docs]defvalues_list(self)->list[T]:"""Get all values as a list."""returnlist(self.values.values())
[docs]defpop(self,key:str,default:Any=None)->Any:"""Remove and return an item with default value."""returnself.values.pop(key,default)
[docs]defclear(self)->None:"""Clear all items."""self.values.clear()
# Attribute accessdef__getattr__(self,name:str)->Any:"""Allow attribute access for keys. Also supports dynamic get_by_X methods. """ifnameinself.values:returnself.values[name]# Dynamic get_by_X methodsifname.startswith("get_by_"):attr_name=name[7:]# Remove "get_by_"defgetter_method(value:str,default=None):returnself.get_by_attr(attr_name,value,default)returngetter_methodraiseAttributeError(f"{self.__class__.__name__} has no attribute '{name}'")
[docs]defto_dict(self)->dict[str,T]:"""Convert to plain dictionary."""returndict(self.values)