# HG changeset patch # User Thierry Florac # Date 1527228494 -7200 # Node ID 17f98772891ecd3ec6ab21a404e930032e9d116b # Parent 1bb9673c3be19817a0c916cccf02e97d2e6c7543 Added default object factory for a given interface diff -r 1bb9673c3be1 -r 17f98772891e src/pyams_utils/factory.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_utils/factory.py Fri May 25 08:08:14 2018 +0200 @@ -0,0 +1,103 @@ +# +# Copyright (c) 2008-2018 Thierry Florac +# 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. +# +from pyams_utils.registry import get_global_registry + +__docformat__ = 'restructuredtext' + + +# import standard library +import logging +logger = logging.getLogger('PyAMS (utils)') + +import venusian + +# import interfaces +from pyams_utils.interfaces import IObjectFactory + +# import packages +from zope.component import adapter, queryAdapter +from zope.interface import implementer, Interface + + +@adapter(Interface) +@implementer(IObjectFactory) +class ObjectFactoryAdapter(object): + """Most basic-default object factory adapter""" + + factory = None + + def __init__(self, context): + self.context = context + + def __call__(self): + return self.factory() + + +def get_interface_name(iface): + """Get interface name""" + return iface.__module__ + '.' + iface.__name__ + + +def register_factory(interface, klass, registry=None): + """Register factory for a given interface""" + + class Temp(ObjectFactoryAdapter): + factory = klass + + name = get_interface_name(interface) + if registry is None: + registry = get_global_registry() + registry.registerAdapter(Temp, name=name) + + +class factory_config(object): + """Class decorator to declare a default object factory""" + + venusian = venusian + + def __init__(self, **settings): + self.__dict__.update(settings) + + def __call__(self, wrapped): + settings = self.__dict__.copy() + depth = settings.pop('_depth', 0) + + def callback(context, name, ob): + provides = settings.get('provided') + if not provides: + raise TypeError("No provided interface(s) was given for registered factory %r" % ob) + if not isinstance(provides, tuple): + provides = (provides,) + + config = context.config.with_package(info.module) + for provided in provides: + logger.debug("Registering factory {0} for interface {1}".format(str(ob), str(provided))) + register_factory(provided, ob, config.registry) + + info = self.venusian.attach(wrapped, callback, category='pyams_factory', 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_object_factory(interface, registry=None): + """Get registered factory for given interface""" + name = get_interface_name(interface) + if registry is None: + registry = get_global_registry() + return registry.queryAdapter(interface, IObjectFactory, name=name) diff -r 1bb9673c3be1 -r 17f98772891e src/pyams_utils/interfaces/__init__.py --- a/src/pyams_utils/interfaces/__init__.py Thu May 24 13:54:24 2018 +0200 +++ b/src/pyams_utils/interfaces/__init__.py Fri May 25 08:08:14 2018 +0200 @@ -101,5 +101,9 @@ """Error raised when no request is available""" +class IObjectFactory(Interface): + """Object factory interface""" + + class ICacheKeyValue(Interface): """Interface used to get string representation of a given object as cache key"""