Source code for stelar.client.proxy.extras

from __future__ import annotations

from .fieldvalidation import AnyField, JSONField
from .property import Property
from .proxy import Proxy


[docs] class ExtrasProperty(Property): def __init__(self, **kwargs): super().__init__( updatable=False, optional=False, validator=AnyField(default={}), **kwargs ) self.isExtras = True self.item_validator = JSONField(nullable=True)
[docs] def autodoc(self, doc, repr_type, repr_constraints): return """\ The field holding extras (additional fields) for this entity. This field should not be accessed directly, instead its contents are available as normal attributes. """
[docs] def get(self, obj: Proxy): """Low-level getter""" if obj.proxy_attr is None: obj.proxy_sync() return obj.proxy_attr[self.name]
[docs] def touch(self, obj: Proxy) -> bool: """Mark the extras as changed. This overloads the default touch, because after the touch, the extras dict needs to be copied (else, changing the 'proxy_attr' instance will change the 'proxy_changed' instance, which is not what we want). """ if super().touch(obj): # Copy the extras dict to ensure that changes to the proxy_attr # do not affect the proxy_changed. obj.proxy_changed[self.name] = self.get(obj).copy() return True return False
def __get__(self, obj, obj_type=None): return self.get(obj).copy()
[docs] def convert_entity_to_proxy(self, proxy: Proxy, entity, **kwargs): entity_extras = entity.get(self.entity_name, {}) proxy_extras = entity_extras.copy() # Do we need a copy here? proxy.proxy_attr[self.name] = proxy_extras
[docs] def convert_proxy_to_entity(self, proxy: Proxy, entity: dict, **kwargs): proxy_extras = proxy.proxy_attr[self.name] if proxy_extras is ...: return entity_extras = proxy_extras.copy() # Do we need a copy here? entity[self.entity_name] = entity_extras
[docs] def convert_to_create(self, proxy_type, create_props, entity_props, **kwargs): schema = proxy_type.proxy_schema # Collect all entries that do not appear in the schema entity_extras = { key: self.item_validator.validate(value, **kwargs) for key, value in create_props.items() if key not in schema.all_fields and value is not None } entity_props[self.entity_name] = entity_extras
[docs] class ExtrasProxy(Proxy, entity=False): """The class implements a Proxy with an 'extras' field. There are two API variants for extra arguments in the CKAN data catalog: * Resource entities allow additional fields in their objects.:: "foo": "bar" * Packages, Groups and Organizations maintain a separate field called extras, whose format is a list of dicts.:: "extras": [{"key": "foo", "value": "bar"}] Our own implementation follows the resource approach, by utilizing dynamic attributes. """ def __getattr__(self, attr): try: return self.proxy_schema.extras.get(self)[attr] except KeyError as e: raise AttributeError(attr) from e def __setattr__(self, attr, value): if ( attr.startswith("proxy_") or attr in self.proxy_schema.all_fields or hasattr(self.__class__, attr) ): return object.__setattr__(self, attr, value) extras_property = self.proxy_schema.extras value = extras_property.item_validator.validate(value) extras_property.touch(self) extras_property.get(self)[attr] = value self.proxy_autocommit() def __delattr__(self, attr): if ( attr.startswith("proxy_") or attr in self.proxy_schema.all_fields or hasattr(self.__class__, attr) ): return object.__delattr__(self, attr) extras_property = self.proxy_schema.extras extras = extras_property.get(self) if attr in extras: extras_property.touch(self) # del extras[attr] del extras_property.get(self)[attr] self.proxy_autocommit() else: raise AttributeError(attr)