src/pyams_utils/vocabulary.py
changeset 289 c8e21d7dd685
child 292 b338586588ad
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_utils/vocabulary.py	Wed Dec 05 12:45:56 2018 +0100
@@ -0,0 +1,95 @@
+#
+# 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.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+import logging
+logger = logging.getLogger('PyAMS (utils)')
+
+import venusian
+
+# import interfaces
+from zope.schema.interfaces import IVocabularyFactory
+
+# import packages
+from zope.interface import directlyProvides
+from zope.schema.vocabulary import getVocabularyRegistry
+
+
+class vocabulary_config:
+    """Class decorator to define a vocabulary
+
+    :param str name: name of the registered vocabulary
+
+    This is, for example, how a vocabulary of registered ZEO connections utilities is created:
+
+    .. code-block:: python
+
+        from pyams_utils.interfaces.zeo import IZEOConnection
+
+        from pyams_utils.registry import get_utilities_for
+        from pyams_utils.vocabulary import vocabulary_config
+        from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
+
+        @vocabulary_config(name='PyAMS ZEO connections')
+        class ZEOConnectionVocabulary(SimpleVocabulary):
+            '''ZEO connections vocabulary'''
+
+            def __init__(self, context=None):
+                terms = [SimpleTerm(name, title=util.name) for name, util in get_utilities_for(IZEOConnection)]
+                super(ZEOConnectionVocabulary, self).__init__(terms)
+
+    You can then use such a vocabulary in any schema field:
+
+    .. code-block:: python
+
+        from zope.interface import Interface
+        from zope.schema import Choice
+
+        class MySchema(Interface):
+            '''Custom schema interface'''
+
+            zeo_connection_name = Choice(title='ZEO connection name',
+                                         description='Please select a registered ZEO connection',
+                                         vocabulary='PyAMS ZEO connections',
+                                         required=False)
+    """
+
+    venusian = venusian
+
+    def __init__(self, name, **settings):
+        self.name = name
+        self.__dict__.update(settings)
+
+    def __call__(self, wrapped):
+        settings = self.__dict__.copy()
+        depth = settings.pop('_depth', 0)
+
+        def callback(context, name, ob):
+            logger.debug('Registering class {0} as vocabulary with name "{1}"'.format(str(ob), self.name))
+            directlyProvides(ob, IVocabularyFactory)
+            getVocabularyRegistry().register(self.name, ob)
+
+        info = self.venusian.attach(wrapped, callback, category='pyams_vocabulary',
+                                    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