Source code for nti.externalization.interfaces

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Externalization Interfaces

"""

from typing import Any

import zope.interface.common.builtins as ibuiltins
from zope import interface
from zope.component.interfaces import IFactory
from zope.deprecation import deprecated
from zope.interface.common import collections as icollections
from zope.interface.common import mapping as legacy_imapping
from zope.interface.common import sequence as legacy_isequence
from zope.interface.interfaces import IObjectEvent
from zope.interface.interfaces import ObjectEvent
from zope.lifecycleevent import IObjectModifiedEvent
from zope.lifecycleevent import ObjectModifiedEvent
from zope.location import ILocation

from ._base_interfaces import MINIMAL_SYNTHETIC_EXTERNAL_KEYS
from ._base_interfaces import ExternalizationPolicy
from ._base_interfaces import LocatedExternalDict
from ._base_interfaces import get_default_externalization_policy
from ._base_interfaces import get_standard_external_fields
from ._base_interfaces import get_standard_internal_fields

# pylint:disable=inherit-non-class,no-method-argument,no-self-argument
# pylint:disable=too-many-ancestors


StandardExternalFields = get_standard_external_fields()
StandardInternalFields = get_standard_internal_fields()
DEFAULT_EXTERNALIZATION_POLICY = get_default_externalization_policy()


class IExternalizationPolicy(interface.Interface):
    """
    This isn't public, it's a marker for internal use.
    """
interface.classImplements(ExternalizationPolicy, IExternalizationPolicy)


[docs] class IInternalObjectExternalizer(interface.Interface): """ Implemented by, or adapted from, an object that can be externalized. """ __external_can_create__ = interface.Attribute( """This must be set to true, generally at the class level, for objects that can be created by specifying their Class name.""") __external_class_name__ = interface.Attribute( """If present, the value is a string that is used for the 'Class' key in the external dictionary. If not present, the local name of the object's class is used instead.""") def toExternalObject(**kwargs): """ Optional, see this :func:`~nti.externalization.externalization.to_external_object`. """
[docs] class INonExternalizableReplacement(interface.Interface): """ This interface may be applied to objects that serve as a replacement for a non-externalized object. """
[docs] class INonExternalizableReplacementFactory(interface.Interface): """ An factory object called to make a replacement when some object cannot be externalized. """ def __call__(obj): # pylint:disable=signature-differs """ :return: An externalized object to replace the given object. Possibly the given object itself if some higher level will handle it. The returned object *may* have the ``INonExternalizableReplacement`` interface. """
[docs] class IExternalObjectDecorator(interface.Interface): """ Used as a subscription adapter (of the object or the object and request) to provide additional information to the externalization of an object after it has been externalized by the primary implementation of :class:`~nti.externalization.interfaces.IInternalObjectExternalizer`. Allows for a separation of concerns. These are called in no specific order, and so must operate by mutating the external object. These are called *after* :class:`.IExternalStandardDictionaryDecorator`. """ def decorateExternalObject(origial, external): """ Decorate the externalized object (which is almost certainly a mapping, though this is not guaranteed). :param original: The object that is being externalized. Passed to facilitate using non-classes as decorators. :param external: The externalization of that object, produced by an implementation of :class:`~nti.externalization.interfaces.IInternalObjectExternalizer` or default rules. :return: Undefined. """
[docs] class IExternalStandardDictionaryDecorator(interface.Interface): """ Used as a subscription adapter (of the object or the object and request) to provide additional information to the externalization of an object after it has been externalized by the primary implementation of :class:`~nti.externalization.interfaces.IInternalObjectExternalizer` (which in turn *must* have invoked :func:`~.to_standard_external_dictionary`). Allows for a separation of concerns. These are called in no specific order, and so must operate by mutating the external object. These are called *before* :class:`.IExternalObjectDecorator`. .. versionchanged:: 2.3.0 Previously this was called ``IExternalMappingDecorator``; that name remains as a backward compatibility alias. """ def decorateExternalMapping(original, external): """ Decorate the externalized object mapping. :param original: The object that is being externalized. Passed to facilitate using non-classes as decorators. :param external: The externalization of that object, an :class:`~nti.externalization.interfaces.ILocatedExternalMapping`, produced by an implementation of :class:`~nti.externalization.interfaces.IInternalObjectExternalizer` or default rules. :return: Undefined. """
[docs] class IExternalizedObject(interface.Interface): """ An object that has already been externalized and needs no further transformation. """
[docs] class ILocatedExternalMapping(IExternalizedObject, ILocation, icollections.IMapping): """ The externalization of an object as a dictionary, maintaining its location information. """
[docs] class ILocatedExternalSequence(IExternalizedObject, ILocation, icollections.ISequence): """ The externalization of an object as a sequence, maintaining its location information. """
# This is defined in _base_interfaces for bootstrap reasons. interface.classImplements(LocatedExternalDict, ILocatedExternalMapping) # BWC: Also make the concrete class implement the legacy IFullMapping; the ILocatedExternalMapping # used to extend this. interface.classImplements(LocatedExternalDict, legacy_imapping.IFullMapping)
[docs] @interface.implementer(ILocatedExternalSequence) class LocatedExternalList(list): """ A list that implements :class:`~nti.externalization.interfaces.ILocatedExternalSequence`. Returned by :func:`~nti.externalization.externalization.to_external_object`. This class is not :class:`.IContentTypeAware`, and it indicates so explicitly by declaring a `mimeType` value of None. """ __name__ = '' __parent__ = None __acl__ = () mimeType = None
# BWC: Also make the concrete class implement as much of the legacy # ISequence as possible (which ILocatedExternalSequence used to # extend). We cannot actually implement it, or the legacy IReadSequence, # because of interface resolution order conflicts. interface.classImplements(LocatedExternalList, legacy_isequence.IWriteSequence) interface.classImplements(LocatedExternalList, legacy_isequence.IFiniteSequence) # Representations as strings
[docs] class IExternalObjectRepresenter(interface.Interface): """ Something that can represent an external object as a sequence of bytes. These will be registered as named utilities and may each have slightly different representation characteristics. """ def dump(obj, fp=None): """ Write the given object. If `fp` is None, then the string representation will be returned, otherwise, fp specifies a writeable object to which the representation will be written. Implementations should accept arbitrary keyword arguments, ignoring any they don't understand. """
[docs] class IExternalRepresentationReader(interface.Interface): """ Something that can read an external string, as produced by :class:`.IExternalObjectRepresenter` and return an equivalent external value.` """ def load(stream): """ Load from the stream an external value. String values should be read as unicode. All objects must support the stream being a sequence of bytes, some may support an open file object. """
[docs] class IExternalObjectIO(IExternalObjectRepresenter, IExternalRepresentationReader): """ Something that can read and write external values. """
#: Constant requesting JSON format data EXT_REPR_JSON = 'json' #: Constant requesting YAML format data EXT_REPR_YAML = 'yaml' # Creating and updating new and existing objects given external forms
[docs] class IMimeObjectFactory(IFactory): """ A factory named for the external mime-type of objects it works with. """
[docs] class IClassObjectFactory(IFactory): """ A factory named for the external class name of objects it works with. """
[docs] class IAnonymousObjectFactory(IFactory): """ A factory for external data that doesn't identify its object type. This data is not produced by this library but comes from external sources. When these are registered as factories (utilities) care must be taken to avoid name clashes (since there are no "natural" unique names). See the ZCML directive :class:`~nti.externalization.zcml.IAnonymousObjectFactoryDirective` for a simple way to do this. .. versionadded:: 1.0a3 """
[docs] class IExternalizedObjectFactoryFinder(interface.Interface): """ An adapter from an externalized object to something that can find factories. """ def find_factory(externalized_object): """ Given an externalized object, return a :class:`zope.component.interfaces.IFactory` to create the proper internal types. :return: An :class:`zope.component.interfaces.IFactory`, or :const:`None`. """
[docs] class IExternalReferenceResolver(interface.Interface): """ Used as a multi-adapter from an *internal* object and an external reference to something that can resolve the reference. """ def resolve(reference): """ Resolve the external reference and return it. """
[docs] class INamedExternalizedObjectFactoryFinder(interface.Interface): """ An object that can find factories for particular named external objects. This is registered as an adapter for particular internal objects, so that internal object, and any schema it implements, can be used to choose the factory for nested objects being updated into it. """ def find_factory_for_named_value(name, value): """ Find a factory for the external object *value* when it is the value with the name *name*. This function has three key pieces of information to work with. First, it is an adapter from an internal object, so it knows the ultimate destination object (the context) where the results of the factory will be set. Second, it knows the incoming name of the external value. Third, it knows the actual incoming external value. For example, if the external data looked like ``{'key': 'value'}`` a call to ``update_from_external_object(internal_object, data)`` would conceptually result in a call that looked like this:: adapter = INamedExternalizedObjectFactoryFinder(internal_object) factory = adapter.find_factory_for_named_value('key', 'value') When the value for the external data is a mutable sequence, this function will be called once for each item in the sequence. So external data of ``{'key': [1, 2, 3]}`` would result in calls ``('key', 1)``, ``('key', 2)`` and ``('key', 3)``. This function can return actual factories that produce fresh objects, or it can return the current object assigned to the ultimate attribute in the context to update that exact object in place. This can be beneficial for persistent objects. """
[docs] class IInternalObjectUpdater(interface.Interface): """ An adapter that can be used to update an internal object from its externalized representation. """ __external_oids__ = interface.Attribute( """For objects whose external form includes object references (OIDs), this attribute is a list of key paths that should be resolved. The values for the key paths may be singleton items or mutable sequences. Resolution may involve placing a None value for a key.""") __external_resolvers__ = interface.Attribute( """For objects who need to perform arbitrary resolution from external forms to internal forms, this attribute is a map from key path to a function of three arguments, the dataserver, the parsed object, and the value to resolve. It should return the new value. Note that the function here is at most a class or static method, not an instance method.""") def updateFromExternalObject(externalObject, context, **kwargs): """ Update the object this is adapting from the external object. Alternately, the signature can be ``updateFromExternalObject(externalObject)`` or simply ``updateFromExternalObject(externalObject, **kwargs)``. In this last case, ``context`` will be passed as a value in ``**kwargs``. :return: If not ``None``, a value that can be interpreted as a boolean, indicating whether or not the internal object actually underwent updates. If ``None``, the caller should assume that the object was updated (to allow older code that doesn't return at all.) """
[docs] class IInternalObjectIO(IInternalObjectExternalizer, IInternalObjectUpdater): """ A single object responsible for both reading and writing internal objects in external forms. This is convenient for keeping code organized. """
[docs] class IInternalObjectIOFinder(INamedExternalizedObjectFactoryFinder, # pylint:disable=too-many-ancestors IInternalObjectIO): """ Like `IInternalObjectIO`, but this object also gets the chance to find factories for objects. The class `~.InterfaceObjectIO` implements this interface. """
[docs] class IObjectWillUpdateFromExternalEvent(IObjectEvent): """ An object will be updated from an external value. """ external_value = interface.Attribute( "The external value. " "This is not necessarily a pristine object as decoded from, e.g., JSON. " "It will be mutated as sub-objects get updated and parsed. For example, strings " "may get replaced with datetimes, and so on. " "The consequences of modifying this object in an event subscriber are undefined. " ) root = interface.Attribute( "The object initially passed to update_from_external_object. " "For nested objects, this will be some ancestor of the object this event is for. " "For updaters that manually update sub-objects, this isn't guaranteed to be the actual " "true root object being updated." )
@interface.implementer(IObjectWillUpdateFromExternalEvent) class ObjectWillUpdateFromExternalEvent(ObjectEvent): external_value = None root = None def __init__(self, it, external_value=None, root=None): ObjectEvent.__init__(self, it) self.external_value = external_value self.root = root
[docs] class IObjectModifiedFromExternalEvent(IObjectModifiedEvent): """ An object has been updated from an external value. """ kwargs = interface.Attribute("The keyword arguments") external_value = interface.Attribute("The external value")
[docs] @interface.implementer(IObjectModifiedFromExternalEvent) class ObjectModifiedFromExternalEvent(ObjectModifiedEvent): """ Default implementation of `IObjectModifiedFromExternalEvent`. """ kwargs:dict[str,Any]|None = None external_value = None def __init__(self, obj, *descriptions, **kwargs): super().__init__(obj, *descriptions) self.kwargs = kwargs
#### # Deprecated backwards compatibility aliases. # Do *NOT* list these in __all__; *do* list them # in interfaces.rst and *do* add them to the call to # zope.deprecation. ### #: Base interface for iterable types. #: #: .. deprecated:: 2.1.0 #: Use :class:`zope.interface.common.collections.IIterable` directly. #: This is just an alias. IIterable = icollections.IIterable #: Marker interface for lists. #: #: .. deprecated:: 2.1.0 #: Use :class:`zope.interface.common.builtins.IList` directly. #: This is just an alias. IList = ibuiltins.IList #: Backwards compatibility alias. #: #: .. deprecated:: 2.0.0 #: Use `IInternalObjectExternalizer` directly. IExternalObject = IInternalObjectExternalizer #: Backwards compatibility alias. #: #: .. deprecated:: 2.0.0 #: Use `INonExternalizableReplacement` directly. INonExternalizableReplacer = INonExternalizableReplacementFactory #: Backwards compatibility alias. #: #: .. deprecated:: 2.3.0 #: Use `IExternalStandardDictionaryDecorator` directly. IExternalMappingDecorator = IExternalStandardDictionaryDecorator deprecated(('IIterable', 'IList', 'IExternalObject', 'INonExternalizableReplacer', 'IExternalMappingDecorator',), "This name is deprecated; see the documentation for replacement." ) #### # Internal use only; do not document or list in __all__. #### class _ILegacySearchModuleFactory(interface.Interface): def __call__(*args, **kwargs): # pylint:disable=no-method-argument,arguments-differ,signature-differs """ Create and return the object. """ __all__ = [ 'ExternalizationPolicy', 'DEFAULT_EXTERNALIZATION_POLICY', 'LocatedExternalDict', 'MINIMAL_SYNTHETIC_EXTERNAL_KEYS', 'StandardExternalFields', 'StandardInternalFields', 'IInternalObjectExternalizer', 'INonExternalizableReplacement', 'INonExternalizableReplacementFactory', 'IExternalObjectDecorator', 'IExternalStandardDictionaryDecorator', 'IExternalizedObject', 'ILocatedExternalMapping', 'ILocatedExternalSequence', 'LocatedExternalList', 'IExternalObjectRepresenter', 'IExternalRepresentationReader', 'IExternalObjectIO', 'EXT_REPR_JSON', 'EXT_REPR_YAML', 'IMimeObjectFactory', 'IClassObjectFactory', 'IAnonymousObjectFactory', 'IExternalizedObjectFactoryFinder', 'IExternalReferenceResolver', 'INamedExternalizedObjectFactoryFinder', 'IInternalObjectUpdater', 'IInternalObjectIO', 'IInternalObjectIOFinder', 'IObjectWillUpdateFromExternalEvent', 'ObjectWillUpdateFromExternalEvent', 'IObjectModifiedFromExternalEvent', 'ObjectModifiedFromExternalEvent', ]