src/pyams_utils/factory.py
changeset 181 17f98772891e
child 182 71a6c885b336
--- /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 <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.
+#
+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)