Source code for actingweb.property

from typing import Any

from actingweb.db import get_property, get_property_list

from .property_list import ListProperty


[docs] class PropertyListStore: """ Explicit interface for managing list properties. Used when the application knows it's working with list data. """ def __init__(self, actor_id: str | None = None, config: Any | None = None) -> None: self._actor_id = actor_id self._config = config self.__initialised = True
[docs] def exists(self, name: str) -> bool: """Check if a list property exists without creating it.""" try: if self._config: db = get_property(self._config) meta = db.get(actor_id=self._actor_id, name=f"list:{name}-meta") return meta is not None except Exception: pass return False
[docs] def list_all(self) -> list[str]: """List all existing list property names.""" list_names = [] try: if self._config: db_list = get_property_list(self._config) all_props = ( db_list.fetch_all_including_lists(actor_id=self._actor_id) or {} ) for prop_name in all_props.keys(): if prop_name.startswith("list:") and prop_name.endswith("-meta"): # Extract list name: "list:name-meta" -> "name" list_name = prop_name[ 5:-5 ] # Remove 'list:' prefix and '-meta' suffix list_names.append(list_name) except Exception as e: import logging logger = logging.getLogger(__name__) logger.error(f"Error in list_all(): {e}") return list_names
def __getattr__(self, k: str) -> ListProperty: """Return a ListProperty for the requested list name.""" if k.startswith("_"): raise AttributeError( f"'{self.__class__.__name__}' object has no attribute '{k}'" ) # Validate actor_id is not None before creating ListProperty if self._actor_id is None: raise RuntimeError("Cannot create ListProperty without a valid actor_id") # Return a ListProperty - don't add "list:" prefix here, ListProperty will handle it return ListProperty(self._actor_id, k, self._config)
[docs] class PropertyStore: def __init__(self, actor_id: str | None = None, config: Any | None = None) -> None: self._actor_id = actor_id self._config = config self.__initialised = True def __getitem__(self, k: str) -> Any: # Block access to list: prefixed keys - use property_lists instead if k.startswith("list:"): raise ValueError( f"Cannot access list properties via [] operator. " f"Use property_lists.{k[5:]} instead." ) return self.__getattr__(k) def __setitem__(self, k: str, v: Any) -> None: # Block access to list: prefixed keys - use property_lists instead if k.startswith("list:"): raise ValueError( f"Cannot access list properties via [] operator. " f"Use property_lists.{k[5:]} instead." ) return self.__setattr__(k, v) def __setattr__(self, k: str, v: Any) -> None: if "_PropertyStore__initialised" not in self.__dict__: return object.__setattr__(self, k, v) if v is None: if k in self.__dict__: self.__delattr__(k) else: # Check for list collision - error if list exists if self.__dict__.get("_config"): list_store = PropertyListStore( actor_id=self.__dict__.get("_actor_id"), config=self.__dict__["_config"], ) if list_store.exists(k): raise ValueError( f"Cannot create property '{k}': a list with this name already exists. " f"Delete the list first or use a different name." ) self.__dict__[k] = v # Re-init property to avoid overwrite self.__dict__["_db"] = get_property(self.__dict__["_config"]) # set() will retrieve an attribute and delete it if value = None self.__dict__["_db"].set(actor_id=self.__dict__["_actor_id"], name=k, value=v) def __getattr__(self, k: str) -> Any: try: return self.__dict__[k] except KeyError: self.__dict__["_db"] = get_property(self.__dict__["_config"]) self.__dict__[k] = self.__dict__["_db"].get( actor_id=self.__dict__["_actor_id"], name=k ) return self.__dict__[k]
[docs] def get_all(self) -> dict[str, Any]: """Fetch all properties from the database and return as dictionary.""" if not self._actor_id or not self._config: return {} db_list = get_property_list(self._config) props = db_list.fetch(actor_id=self._actor_id) if isinstance(props, dict): return props return {}
[docs] class Property: """ property is the main entity keeping a property. It needs to be initalised at object creation time. """
[docs] def get(self) -> Any: """Retrieves the property from the database""" if not self.dbprop: # New property after a delete() if self.config: self.dbprop = get_property(self.config) else: self.dbprop = None self.value = None if self.dbprop: self.value = self.dbprop.get(actor_id=self.actor_id, name=self.name) else: self.value = None return self.value
[docs] def set(self, value: Any) -> bool: """Sets a new value for this property""" if not self.dbprop: # New property after a delete() if self.config: self.dbprop = get_property(self.config) else: self.dbprop = None if not self.actor_id or not self.name: return False # Make sure we have made a dip in db to avoid two properties # with same name if self.dbprop: db_value = self.dbprop.get(actor_id=self.actor_id, name=self.name) else: db_value = None if db_value == value: return True self.value = value if self.dbprop: return self.dbprop.set(actor_id=self.actor_id, name=self.name, value=value) return False
[docs] def delete(self) -> bool | None: """Deletes the property in the database""" if not self.dbprop: return if self.dbprop.delete(): self.value = None self.dbprop = None return True else: return False
[docs] def get_actor_id(self) -> str | None: return self.actor_id
def __init__( self, actor_id: str | None = None, name: str | None = None, value: Any | None = None, config: Any | None = None, ) -> None: """A property must be initialised with actor_id and name or name and value (to find an actor's property of a certain value) """ self.config = config if self.config: self.dbprop = get_property(self.config) else: self.dbprop = None self.name = name if not actor_id and name and len(name) > 0 and value and len(value) > 0: if self.dbprop: self.actor_id = self.dbprop.get_actor_id_from_property( name=name, value=value ) else: self.actor_id = None if not self.actor_id: return self.value = value else: self.actor_id = actor_id self.value = None if name and len(name) > 0: self.get()
[docs] class Properties: """Handles all properties of a specific actor_id Access the properties in .props as a dictionary """
[docs] def fetch(self) -> dict[str, Any] | bool: if not self.actor_id: return False if not self.list: return False if self.props is not None: return self.props self.props = self.list.fetch(actor_id=self.actor_id) return self.props if self.props is not None else False
[docs] def delete(self) -> bool: if not self.list: self.fetch() if not self.list: return False self.list.delete() return True
def __init__(self, actor_id: str | None = None, config: Any | None = None) -> None: """Properties must always be initialised with an actor_id""" self.config = config if not actor_id: self.list = None return if self.config: self.list = get_property_list(self.config) else: self.list = None self.actor_id = actor_id self.props = None self.fetch()