src/pyams_utils/adapter.py
branchdev-tf
changeset 408 cf2304af0fab
parent 395 5cb941e31c86
child 419 05ff71a02b2d
equal deleted inserted replaced
407:0037199881fb 408:cf2304af0fab
    10 # FOR A PARTICULAR PURPOSE.
    10 # FOR A PARTICULAR PURPOSE.
    11 #
    11 #
    12 
    12 
    13 """Adapters management package
    13 """Adapters management package
    14 
    14 
    15 This package provides a small set of standard base adapters for *context*, *context* and *request*, and
    15 This package provides a small set of standard base adapters for *context*, *context* and *request*,
    16 *context* and *request* and *view*.
    16 and *context* and *request* and *view*.
    17 
    17 
    18 See :ref:`zca` to see how PyAMS can help components management.
    18 See :ref:`zca` to see how PyAMS can help components management.
    19 """
    19 """
    20 
    20 
    21 import logging
    21 import logging
    25 from zope.interface import alsoProvides, implementedBy
    25 from zope.interface import alsoProvides, implementedBy
    26 from zope.lifecycleevent import ObjectCreatedEvent
    26 from zope.lifecycleevent import ObjectCreatedEvent
    27 from zope.location import locate as zope_locate
    27 from zope.location import locate as zope_locate
    28 
    28 
    29 from pyams_utils.factory import get_object_factory, is_interface
    29 from pyams_utils.factory import get_object_factory, is_interface
    30 from pyams_utils.registry import get_current_registry
    30 from pyams_utils.registry import get_current_registry, get_global_registry
    31 
    31 
    32 
    32 
    33 __docformat__ = 'restructuredtext'
    33 __docformat__ = 'restructuredtext'
    34 
    34 
    35 logger = logging.getLogger('PyAMS (utils)')
    35 LOGGER = logging.getLogger('PyAMS (utils)')
    36 
    36 
    37 
    37 
    38 class ContextAdapter(object):
    38 class ContextAdapter:
    39     """Context adapter"""
    39     """Context adapter"""
    40 
    40 
    41     def __init__(self, context):
    41     def __init__(self, context):
    42         self.context = context
    42         self.context = context
    43 
    43 
    44 
    44 
    45 class ContextRequestAdapter(object):
    45 class ContextRequestAdapter:
    46     """Context + request multi-adapter"""
    46     """Context + request multi-adapter"""
    47 
    47 
    48     def __init__(self, context, request):
    48     def __init__(self, context, request):
    49         self.context = context
    49         self.context = context
    50         self.request = request
    50         self.request = request
    51 
    51 
    52 
    52 
    53 class ContextRequestViewAdapter(object):
    53 class ContextRequestViewAdapter:
    54     """Context + request + view multi-adapter"""
    54     """Context + request + view multi-adapter"""
    55 
    55 
    56     def __init__(self, context, request, view):
    56     def __init__(self, context, request, view):
    57         self.context = context
    57         self.context = context
    58         self.request = request
    58         self.request = request
    59         self.view = view
    59         self.view = view
    60 
    60 
    61 
    61 
    62 class NullAdapter(object):
    62 class NullAdapter:
    63     """An adapter which always return None!
    63     """An adapter which always return None!
    64 
    64 
    65     Can be useful to override a default adapter...
    65     Can be useful to override a default adapter...
    66     """
    66     """
    67 
    67 
    68     def __new__(cls, *arsg, **kwargs):
    68     def __new__(cls, *args, **kwargs):  # pylint: disable=unused-argument
    69         return None
    69         return None
    70 
    70 
    71 
    71 
    72 class adapter_config(object):
    72 class adapter_config:    # pylint: disable=invalid-name
    73     """Function or class decorator to declare an adapter
    73     """Function or class decorator to declare an adapter
    74 
    74 
    75     Annotation parameters can be:
    75     Annotation parameters can be:
    76 
    76 
    77     :param str='' name: name of the adapter
    77     :param str='' name: name of the adapter
    78     :param [Interface...] context: an interface, or a tuple of interfaces, that the component adapts
    78     :param [Interface...] context: an interface, or a tuple of interfaces, that the component adapts
    79     :param Interface provides: the interface that the adapter provides
    79     :param Interface provides: the interface that the adapter provides
       
    80     :param registry: the registry into which adapter registration should be made
    80     """
    81     """
    81 
    82 
    82     venusian = venusian
    83     venusian = venusian
    83 
    84 
    84     def __init__(self, **settings):
    85     def __init__(self, **settings):
    89 
    90 
    90     def __call__(self, wrapped):
    91     def __call__(self, wrapped):
    91         settings = self.__dict__.copy()
    92         settings = self.__dict__.copy()
    92         depth = settings.pop('_depth', 0)
    93         depth = settings.pop('_depth', 0)
    93 
    94 
    94         def callback(context, name, ob):
    95         def callback(context, name, obj):
    95             adapts = settings.get('context')
    96             adapts = settings.get('context')
    96             if adapts is None:
    97             if adapts is None:
    97                 adapts = getattr(ob, '__component_adapts__', None)
    98                 adapts = getattr(obj, '__component_adapts__', None)
    98                 if adapts is None:
    99                 if adapts is None:
    99                     raise TypeError("No for argument was provided for %r and "
   100                     raise TypeError("No for argument was provided for %r and "
   100                                     "can't determine what the factory adapts." % ob)
   101                                     "can't determine what the factory adapts." % obj)
   101             if not isinstance(adapts, tuple):
   102             if not isinstance(adapts, tuple):
   102                 adapts = (adapts,)
   103                 adapts = (adapts,)
   103 
   104 
   104             provides = settings.get('provides')
   105             provides = settings.get('provides')
   105             if provides is None:
   106             if provides is None:
   106                 intfs = list(implementedBy(ob))
   107                 intfs = list(implementedBy(obj))
   107                 if len(intfs) == 1:
   108                 if len(intfs) == 1:
   108                     provides = intfs[0]
   109                     provides = intfs[0]
   109                 if provides is None:
   110                 if provides is None:
   110                     raise TypeError("Missing 'provides' argument")
   111                     raise TypeError("Missing 'provides' argument")
   111 
   112 
   112             config = context.config.with_package(info.module)
   113             config = context.config.with_package(info.module)  # pylint: disable=no-member
   113             logger.debug("Registering adapter {0} for {1} providing {2}".format(str(ob),
   114             LOGGER.debug("Registering adapter %s for %s providing %s",
   114                                                                                 str(adapts),
   115                          str(obj), str(adapts), str(provides))
   115                                                                                 str(provides)))
   116             registry = settings.get('registry', config.registry)
   116             config.registry.registerAdapter(ob, adapts, provides, settings.get('name', ''))
   117             registry.registerAdapter(obj, adapts, provides, settings.get('name', ''))
   117 
   118 
   118         info = self.venusian.attach(wrapped, callback, category='pyams_adapter',
   119         info = self.venusian.attach(wrapped, callback, category='pyams_adapter',
   119                                     depth=depth + 1)
   120                                     depth=depth + 1)
   120 
   121 
   121         if info.scope == 'class':
   122         if info.scope == 'class':  # pylint: disable=no-member
   122             # if the decorator was attached to a method in a class, or
   123             # if the decorator was attached to a method in a class, or
   123             # otherwise executed at class scope, we need to set an
   124             # otherwise executed at class scope, we need to set an
   124             # 'attr' into the settings if one isn't already in there
   125             # 'attr' into the settings if one isn't already in there
   125             if settings.get('attr') is None:
   126             if settings.get('attr') is None:
   126                 settings['attr'] = wrapped.__name__
   127                 settings['attr'] = wrapped.__name__
   127 
   128 
   128         settings['_info'] = info.codeinfo  # fbo "action_method"
   129         settings['_info'] = info.codeinfo  # pylint: disable=no-member
   129         return wrapped
   130         return wrapped
   130 
   131 
   131 
   132 
   132 def get_annotation_adapter(context, key, factory=None, markers=None, notify=True,
   133 def get_annotation_adapter(context, key, factory=None, markers=None, notify=True,
   133                            locate=True, parent=None, name=None, callback=None, **kwargs):
   134                            locate=True, parent=None, name=None, callback=None, **kwargs):
       
   135     # pylint: disable=too-many-arguments
   134     """Get an adapter via object's annotations, creating it if not existent
   136     """Get an adapter via object's annotations, creating it if not existent
   135     
   137 
   136     :param object context: context object which should be adapted
   138     :param object context: context object which should be adapted
   137     :param str key: annotations key to look for
   139     :param str key: annotations key to look for
   138     :param factory: if annotations key is not found, this is the factory which will be used to
   140     :param factory: if annotations key is not found, this is the factory which will be used to
   139         create a new object; factory can be a class or callable object, or an interface for which
   141         create a new object; factory can be a class or callable object, or an interface for which
   140         a factory has been registered; if factory is None and is requested object can't be found,
   142         a factory has been registered; if factory is None and is requested object can't be found,
   149     :param callback: if not None, callback function which will be called after object creation
   151     :param callback: if not None, callback function which will be called after object creation
   150     """
   152     """
   151     annotations = IAnnotations(context, None)
   153     annotations = IAnnotations(context, None)
   152     if annotations is None:
   154     if annotations is None:
   153         return None
   155         return None
   154     adapter = annotations.get(key)
   156     adapter = annotations.get(key)  # pylint: disable=assignment-from-no-return
   155     if adapter is None:
   157     if adapter is None:
   156         if 'default' in kwargs:
   158         if 'default' in kwargs:
   157             return kwargs['default']
   159             return kwargs['default']
   158         elif factory is None:
   160         if factory is None:
   159             return None
   161             return None
   160         else:
   162         if is_interface(factory):
   161             if is_interface(factory):
   163             factory = get_object_factory(factory)
   162                 factory = get_object_factory(factory)
   164             assert factory is not None, "Missing object factory"
   163                 assert factory is not None, "Missing object factory"
   165         adapter = annotations[key] = factory()
   164             adapter = annotations[key] = factory()
   166         if markers:
   165             if markers:
   167             if not isinstance(markers, (list, tuple, set)):
   166                 if not isinstance(markers, (list, tuple, set)):
   168                 markers = {markers}
   167                     markers = {markers}
   169             for marker in markers:
   168                 for marker in markers:
   170                 alsoProvides(adapter, marker)
   169                     alsoProvides(adapter, marker)
   171         if notify:
   170             if notify:
   172             get_current_registry().notify(ObjectCreatedEvent(adapter))
   171                 get_current_registry().notify(ObjectCreatedEvent(adapter))
   173         if locate:
   172             if locate:
   174             zope_locate(adapter, context if parent is None else parent, name)
   173                 zope_locate(adapter, context if parent is None else parent, name)
   175         if callback:
   174             if callback:
   176             callback(adapter)
   175                 callback(adapter)
       
   176     return adapter
   177     return adapter
   177 
   178 
   178 
   179 
   179 def get_adapter_weight(item):
   180 def get_adapter_weight(item):
   180     """Get adapters weight sort key"""
   181     """Get adapters weight sort key"""