--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_utils/adapter.py Wed Dec 05 12:45:56 2018 +0100
@@ -0,0 +1,185 @@
+#
+# Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+
+"""Adapters management package
+
+This package provides a small set of standard base adapters for *context*, *context* and *request*, and
+*context* and *request* and *view*.
+
+See :ref:`zca` to see how PyAMS can help components management.
+"""
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+import logging
+logger = logging.getLogger('PyAMS (utils)')
+
+import venusian
+
+# import interfaces
+from zope.annotation.interfaces import IAnnotations
+
+# import packages
+from pyams_utils.factory import get_object_factory, is_interface
+from pyams_utils.registry import get_current_registry
+from zope.interface import implementedBy, alsoProvides, Interface
+from zope.lifecycleevent import ObjectCreatedEvent
+from zope.location import locate as zope_locate
+
+
+class ContextAdapter(object):
+ """Context adapter"""
+
+ def __init__(self, context):
+ self.context = context
+
+
+class ContextRequestAdapter(object):
+ """Context + request multi-adapter"""
+
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+
+
+class ContextRequestViewAdapter(object):
+ """Context + request + view multi-adapter"""
+
+ def __init__(self, context, request, view):
+ self.context = context
+ self.request = request
+ self.view = view
+
+
+class NullAdapter(object):
+ """An adapter which always return None!
+
+ Can be useful to override a default adapter...
+ """
+
+ def __new__(cls, *arsg, **kwargs):
+ return None
+
+
+class adapter_config(object):
+ """Function or class decorator to declare an adapter
+
+ Annotation parameters can be:
+
+ :param str='' name: name of the adapter
+ :param [Interface...] context: an interface, or a tuple of interfaces, that the component adapts
+ :param Interface provides: the interface that the adapter provides
+ """
+
+ venusian = venusian
+
+ def __init__(self, **settings):
+ if 'for_' in settings:
+ if settings.get('context') is None:
+ settings['context'] = settings.pop('for_')
+ self.__dict__.update(settings)
+
+ def __call__(self, wrapped):
+ settings = self.__dict__.copy()
+ depth = settings.pop('_depth', 0)
+
+ def callback(context, name, ob):
+ adapts = settings.get('context')
+ if adapts is None:
+ adapts = getattr(ob, '__component_adapts__', None)
+ if adapts is None:
+ raise TypeError("No for argument was provided for %r and "
+ "can't determine what the factory adapts." % ob)
+ if not isinstance(adapts, tuple):
+ adapts = (adapts,)
+
+ provides = settings.get('provides')
+ if provides is None:
+ intfs = list(implementedBy(ob))
+ if len(intfs) == 1:
+ provides = intfs[0]
+ if provides is None:
+ raise TypeError("Missing 'provides' argument")
+
+ config = context.config.with_package(info.module)
+ logger.debug("Registering adapter {0} for {1} providing {2}".format(str(ob),
+ str(adapts),
+ str(provides)))
+ config.registry.registerAdapter(ob, adapts, provides, settings.get('name', ''))
+
+ info = self.venusian.attach(wrapped, callback, category='pyams_adapter',
+ depth=depth + 1)
+
+ if info.scope == 'class':
+ # if the decorator was attached to a method in a class, or
+ # otherwise executed at class scope, we need to set an
+ # 'attr' into the settings if one isn't already in there
+ if settings.get('attr') is None:
+ settings['attr'] = wrapped.__name__
+
+ settings['_info'] = info.codeinfo # fbo "action_method"
+ return wrapped
+
+
+def get_annotation_adapter(context, key, factory=None, markers=None, notify=True,
+ locate=True, parent=None, name=None, callback=None, **kwargs):
+ """Get an adapter via object's annotations, creating it if not existent
+
+ :param object context: context object which should be adapted
+ :param str key: annotations key to look for
+ :param factory: if annotations key is not found, this is the factory which will be used to
+ create a new object; if factory is None and is requested object can't be found, None is returned
+ :param markers: if not None, list of marker interfaces which created adapter should provide
+ :param bool=True notify: if 'False', no notification event will be sent on object creation
+ :param bool=True locate: if 'False', the new object is not attached to any parent
+ :param object=None parent: parent to which new object is attached
+ :param str=None name: if locate is not False, this is the name with which the new object is attached
+ to it's parent.
+ :param callback: if not None, callback function which will be called after
+ """
+ annotations = IAnnotations(context, None)
+ if annotations is None:
+ return None
+ adapter = annotations.get(key)
+ if adapter is None:
+ if 'default' in kwargs:
+ return kwargs['default']
+ elif factory is None:
+ return None
+ else:
+ if is_interface(factory):
+ factory = get_object_factory(factory)
+ assert factory is not None, "Missing object factory"
+ adapter = annotations[key] = factory()
+ if markers:
+ if not isinstance(markers, (list, tuple, set)):
+ markers = {markers}
+ for marker in markers:
+ alsoProvides(adapter, marker)
+ if notify:
+ get_current_registry().notify(ObjectCreatedEvent(adapter))
+ if locate:
+ zope_locate(adapter, context if parent is None else parent, name)
+ if callback:
+ callback(adapter)
+ return adapter
+
+
+def get_adapter_weight(item):
+ """Get adapters weight sort key"""
+ name, adapter = item
+ try:
+ return int(adapter.weight), name
+ except (TypeError, AttributeError):
+ return 0, name