[Checkins] SVN: zope.configuration/trunk/ - Action structures changed from tuples to dictionaries to allow for action

Chris McDonough chrism at plope.com
Mon Dec 5 19:59:37 UTC 2011


Log message for revision 123574:
  - Action structures changed from tuples to dictionaries to allow for action
    structure extensibility (merged chrism-dictactions branch).
  
  

Changed:
  U   zope.configuration/trunk/CHANGES.txt
  U   zope.configuration/trunk/setup.py
  U   zope.configuration/trunk/src/zope/configuration/config.py
  U   zope.configuration/trunk/src/zope/configuration/tests/test_config.py
  U   zope.configuration/trunk/src/zope/configuration/tests/test_xmlconfig.py
  U   zope.configuration/trunk/src/zope/configuration/xmlconfig.py

-=-
Modified: zope.configuration/trunk/CHANGES.txt
===================================================================
--- zope.configuration/trunk/CHANGES.txt	2011-12-04 20:11:28 UTC (rev 123573)
+++ zope.configuration/trunk/CHANGES.txt	2011-12-05 19:59:35 UTC (rev 123574)
@@ -2,9 +2,11 @@
 Changes
 =======
 
-3.7.5 (unreleased)
+3.8.0 (unreleased)
 ------------------
 
+- Action structures changed from tuples to dictionaries to allow for action
+  structure extensibility (merged chrism-dictactions branch).
 
 3.7.4 (2011-04-03)
 ------------------

Modified: zope.configuration/trunk/setup.py
===================================================================
--- zope.configuration/trunk/setup.py	2011-12-04 20:11:28 UTC (rev 123573)
+++ zope.configuration/trunk/setup.py	2011-12-05 19:59:35 UTC (rev 123574)
@@ -57,7 +57,7 @@
     return suite
 
 setup(name='zope.configuration',
-      version = '3.7.5dev',
+      version = '3.8.0dev',
       author='Zope Foundation and Contributors',
       author_email='zope-dev at zope.org',
       description='Zope Configuration Markup Language (ZCML)',

Modified: zope.configuration/trunk/src/zope/configuration/config.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/config.py	2011-12-04 20:11:28 UTC (rev 123573)
+++ zope.configuration/trunk/src/zope/configuration/config.py	2011-12-05 19:59:35 UTC (rev 123574)
@@ -17,6 +17,7 @@
 """
 __docformat__ = 'restructuredtext'
 import __builtin__
+import operator
 import os.path
 import sys
 
@@ -64,25 +65,23 @@
     The ``info`` attribute contains descriptive information helpful
     when reporting errors.  If not set, it defaults to an empty string.
 
-    The actions attribute is a sequence of tuples with items:
+    The actions attribute is a sequence of dictionaries where each dictionary
+    has the following keys:
 
-      - discriminator, a value that identifies the action. Two actions
+      - ``discriminator``, a value that identifies the action. Two actions
         that have the same (non None) discriminator conflict.
 
-      - an object that is called to execute the action,
+      - ``callable``, an object that is called to execute the action,
 
-      - positional arguments for the action
+      - ``args``, positional arguments for the action
 
-      - keyword arguments for the action
+      - ``kw`, keyword arguments for the action
 
-      - a tuple of include file names (defaults to ())
+      - ``includepath``, a tuple of include file names (defaults to ())
 
-      - an object that has descriptive information about
+      - ``info``, an object that has descriptive information about
         the action (defaults to '')
 
-    For brevity, trailing items after the callable in the tuples are
-    ommitted if they are empty.
-
     """
 
     def __init__(self):
@@ -317,8 +316,8 @@
         self._seen_files.add(path)
         return True
 
-    def action(self, discriminator, callable=None, args=(), kw={}, order=0,
-               includepath=None, info=None):
+    def action(self, discriminator, callable=None, args=(), kw=None, order=0,
+               includepath=None, info=None, **extra):
         """Add an action with the given discriminator, callable and arguments
 
         For testing purposes, the callable and arguments may be omitted.
@@ -341,57 +340,113 @@
         >>> from zope.configuration.tests.directives import f
 
         >>> c.action(1, f, (1, ), {'x': 1})
-        >>> c.actions
-        [(1, f, (1,), {'x': 1})]
+        >>> from pprint import PrettyPrinter
+        >>> pprint=PrettyPrinter(width=60).pprint
+        >>> pprint(c.actions)
+        [{'args': (1,),
+          'callable': f,
+          'discriminator': 1,
+          'includepath': (),
+          'info': '',
+          'kw': {'x': 1},
+          'order': 0}]
 
         >>> c.action(None)
-        >>> c.actions
-        [(1, f, (1,), {'x': 1}), (None, None)]
+        >>> pprint(c.actions)
+        [{'args': (1,),
+          'callable': f,
+          'discriminator': 1,
+          'includepath': (),
+          'info': '',
+          'kw': {'x': 1},
+          'order': 0},
+         {'args': (),
+          'callable': None,
+          'discriminator': None,
+          'includepath': (),
+          'info': '',
+          'kw': {},
+          'order': 0}]
 
         Now set the include path and info:
 
         >>> c.includepath = ('foo.zcml',)
         >>> c.info = "?"
         >>> c.action(None)
-        >>> c.actions[-1]
-        (None, None, (), {}, ('foo.zcml',), '?')
+        >>> pprint(c.actions[-1])
+        {'args': (),
+         'callable': None,
+         'discriminator': None,
+         'includepath': ('foo.zcml',),
+         'info': '?',
+         'kw': {},
+         'order': 0}
 
         We can add an order argument to crudely control the order
         of execution:
 
         >>> c.action(None, order=99999)
-        >>> c.actions[-1]
-        (None, None, (), {}, ('foo.zcml',), '?', 99999)
+        >>> pprint(c.actions[-1])
+        {'args': (),
+         'callable': None,
+         'discriminator': None,
+         'includepath': ('foo.zcml',),
+         'info': '?',
+         'kw': {},
+         'order': 99999}
 
         We can also pass an includepath argument, which will be used as the the
         includepath for the action.  (if includepath is None, self.includepath
         will be used):
 
         >>> c.action(None, includepath=('abc',))
-        >>> c.actions[-1]
-        (None, None, (), {}, ('abc',), '?')
+        >>> pprint(c.actions[-1])
+        {'args': (),
+         'callable': None,
+         'discriminator': None,
+         'includepath': ('abc',),
+         'info': '?',
+         'kw': {},
+         'order': 0}
 
         We can also pass an info argument, which will be used as the the
         source line info for the action.  (if info is None, self.info will be
         used):
 
         >>> c.action(None, info='abc')
-        >>> c.actions[-1]
-        (None, None, (), {}, ('foo.zcml',), 'abc')
+        >>> pprint(c.actions[-1])
+        {'args': (),
+         'callable': None,
+         'discriminator': None,
+         'includepath': ('foo.zcml',),
+         'info': 'abc',
+         'kw': {},
+         'order': 0}
         
         """
+        if kw is None:
+            kw = {}
+
+        action = extra
+
         if info is None:
             info = getattr(self, 'info', '')
 
         if includepath is None:
             includepath = getattr(self, 'includepath', ())
             
-        action = (discriminator, callable, args, kw, includepath, info, order)
+        action.update(
+            dict(
+                discriminator=discriminator,
+                callable=callable,
+                args=args,
+                kw=kw,
+                includepath=includepath,
+                info=info,
+                order=order,
+                )
+            )
 
-        # remove trailing false items
-        while (len(action) > 2) and not action[-1]:
-            action = action[:-1]
-
         self.actions.append(action)
 
     def hasFeature(self, feature):
@@ -524,10 +579,17 @@
     and try it out:
 
     >>> machine((ns, "simple"), a=u"aa", c=u"cc")
+    >>> from pprint import PrettyPrinter
+    >>> pprint=PrettyPrinter(width=60).pprint
+    >>> pprint(machine.actions)
+    [{'args': (u'aa', u'xxx', 'cc'),
+      'callable': f,
+      'discriminator': ('simple', u'aa', u'xxx', 'cc'),
+      'includepath': (),
+      'info': None,
+      'kw': {},
+      'order': 0}]
 
-    >>> machine.actions
-    [(('simple', u'aa', u'xxx', 'cc'), f, (u'aa', u'xxx', 'cc'))]
-
     A more extensive example can be found in the unit tests.
     """
 
@@ -616,26 +678,32 @@
 
 
         """
+
         try:
             for action in resolveConflicts(self.actions):
-                (discriminator, callable, args, kw, includepath, info, order
-                 ) = expand_action(*action)
+                callable = action['callable']
                 if callable is None:
                     continue
+                args = action['args']
+                kw = action['kw']
+                info = action['info']
                 try:
                     callable(*args, **kw)
-                except (KeyboardInterrupt, SystemExit):
+                except (KeyboardInterrupt, SystemExit): # pragma: no cover
                     raise
                 except:
                     if testing:
                         raise
                     t, v, tb = sys.exc_info()
-                    raise ConfigurationExecutionError(t, v, info), None, tb
+                    try:
+                        raise ConfigurationExecutionError(t, v, info), None, tb
+                    finally:
+                       del t, v, tb
+                
         finally:
             if clear:
                 del self.actions[:]
-
-
+        
 class ConfigurationExecutionError(ConfigurationError):
     """An error occurred during execution of a configuration action
     """
@@ -707,7 +775,9 @@
         if actions:
             # we allow the handler to return nothing
             for action in actions:
-                context.action(*action)
+                if not isinstance(action, dict):
+                    action = expand_action(*action) # b/c
+                context.action(**action)
 
 class RootStackItem(object):
 
@@ -812,19 +882,48 @@
     >>> pprint=PrettyPrinter(width=60).pprint
 
     >>> pprint(context.actions)
-    [(('before', 1, 2), f),
-     (('simple', 1, 2, {'z': 'zope'}), f)]
+    [{'args': (),
+      'callable': f,
+      'discriminator': ('before', 1, 2),
+      'includepath': (),
+      'info': '',
+      'kw': {},
+      'order': 0},
+     {'args': (),
+      'callable': f,
+      'discriminator': ('simple', 1, 2, {'z': 'zope'}),
+      'includepath': (),
+      'info': '',
+      'kw': {},
+      'order': 0}]
 
     Finally, we call finish, which calls the decorator after method:
 
     >>> item.finish()
 
     >>> pprint(context.actions)
-    [(('before', 1, 2), f),
-     (('simple', 1, 2, {'z': 'zope'}), f),
-     ('after', f)]
+    [{'args': (),
+      'callable': f,
+      'discriminator': ('before', 1, 2),
+      'includepath': (),
+      'info': '',
+      'kw': {},
+      'order': 0},
+     {'args': (),
+      'callable': f,
+      'discriminator': ('simple', 1, 2, {'z': 'zope'}),
+      'includepath': (),
+      'info': '',
+      'kw': {},
+      'order': 0},
+     {'args': (),
+      'callable': f,
+      'discriminator': 'after',
+      'includepath': (),
+      'info': '',
+      'kw': {},
+      'order': 0}]
 
-
     If there were no nested directives:
 
     >>> context = ConfigurationMachine()
@@ -835,8 +934,20 @@
     Then before will be when we call finish:
 
     >>> pprint(context.actions)
-    [(('before', 1, 2), f), ('after', f)]
-
+    [{'args': (),
+      'callable': f,
+      'discriminator': ('before', 1, 2),
+      'includepath': (),
+      'info': '',
+      'kw': {},
+      'order': 0},
+     {'args': (),
+      'callable': f,
+      'discriminator': 'after',
+      'includepath': (),
+      'info': '',
+      'kw': {},
+      'order': 0}]
     """
 
     implements(IStackItem)
@@ -848,7 +959,9 @@
         actions = self.context.before()
         if actions:
             for action in actions:
-                self.context.action(*action)
+                if not isinstance(action, dict):
+                    action = expand_action(*action)
+                self.context.action(**action)
         self.__callBefore = noop
 
     def contained(self, name, data, info):
@@ -860,7 +973,9 @@
         actions = self.context.after()
         if actions:
             for action in actions:
-                self.context.action(*action)
+                if not isinstance(action, dict):
+                    action = expand_action(*action)
+                self.context.action(**action)
 
 def noop():
     pass
@@ -919,8 +1034,16 @@
 
     When we created the definition, the handler (factory) was called.
 
-    >>> context.actions
-    [('init', f, (), {}, (), 'foo')]
+    >>> from pprint import PrettyPrinter
+    >>> pprint=PrettyPrinter(width=60).pprint
+    >>> pprint(context.actions)
+    [{'args': (),
+      'callable': f,
+      'discriminator': 'init',
+      'includepath': (),
+      'info': 'foo',
+      'kw': {},
+      'order': 0}]
 
     If a subdirective is provided, the ``contained`` method of the stack item
     is called. It will lookup the subdirective schema and call the
@@ -937,8 +1060,20 @@
     >>> pprint=PrettyPrinter(width=60).pprint
 
     >>> pprint(context.actions)
-    [('init', f, (), {}, (), 'foo'),
-     (('sub', u'av', u'bv'), f, (), {}, (), 'baz')]
+    [{'args': (),
+      'callable': f,
+      'discriminator': 'init',
+      'includepath': (),
+      'info': 'foo',
+      'kw': {},
+      'order': 0},
+     {'args': (),
+      'callable': f,
+      'discriminator': ('sub', u'av', u'bv'),
+      'includepath': (),
+      'info': 'baz',
+      'kw': {},
+      'order': 0}]
 
     The new stack item returned by contained is one that doesn't allow
     any more subdirectives,
@@ -951,11 +1086,27 @@
     The stack item will call the handler if it is callable.
 
     >>> pprint(context.actions)
-    [('init', f, (), {}, (), 'foo'),
-     (('sub', u'av', u'bv'), f, (), {}, (), 'baz'),
-     (('call', u'xv', u'yv'), f, (), {}, (), 'foo')]
-
-
+    [{'args': (),
+      'callable': f,
+      'discriminator': 'init',
+      'includepath': (),
+      'info': 'foo',
+      'kw': {},
+      'order': 0},
+     {'args': (),
+      'callable': f,
+      'discriminator': ('sub', u'av', u'bv'),
+      'includepath': (),
+      'info': 'baz',
+      'kw': {},
+      'order': 0},
+     {'args': (),
+      'callable': f,
+      'discriminator': ('call', u'xv', u'yv'),
+      'includepath': (),
+      'info': 'foo',
+      'kw': {},
+      'order': 0}]
     """
 
     implements(IStackItem)
@@ -1001,7 +1152,9 @@
         if actions:
             # we allow the handler to return nothing
             for action in actions:
-                self.context.action(*action)
+                if not isinstance(action, dict):
+                    action = expand_action(*action)
+                self.context.action(**action)
 
 ##############################################################################
 # Helper classes
@@ -1118,8 +1271,16 @@
     >>> defineSimpleDirective(context, 's', Ixy, s, testns)
 
     >>> context((testns, "s"), x=u"vx", y=u"vy")
-    >>> context.actions
-    [(('s', u'vx', u'vy'), f)]
+    >>> from pprint import PrettyPrinter
+    >>> pprint=PrettyPrinter(width=60).pprint
+    >>> pprint(context.actions)
+    [{'args': (),
+      'callable': f,
+      'discriminator': ('s', u'vx', u'vy'),
+      'includepath': (),
+      'info': None,
+      'kw': {},
+      'order': 0}]
 
     >>> context(('http://www.zope.com/t1', "s"), x=u"vx", y=u"vy")
     Traceback (most recent call last):
@@ -1130,8 +1291,14 @@
     >>> defineSimpleDirective(context, 's', Ixy, s, "*")
 
     >>> context(('http://www.zope.com/t1', "s"), x=u"vx", y=u"vy")
-    >>> context.actions
-    [(('s', u'vx', u'vy'), f)]
+    >>> pprint(context.actions)
+    [{'args': (),
+      'callable': f,
+      'discriminator': ('s', u'vx', u'vy'),
+      'includepath': (),
+      'info': None,
+      'kw': {},
+      'order': 0}]
 
     """
 
@@ -1424,119 +1591,103 @@
 ##############################################################################
 # Conflict resolution
 
-def expand_action(discriminator, callable=None, args=(), kw={},
-                   includepath=(), info='', order=0):
-    return (discriminator, callable, args, kw,
-            includepath, info, order)
+def expand_action(discriminator, callable=None, args=(), kw=None,
+                  includepath=(), info=None, order=0, **extra):
+    if kw is None:
+        kw = {}
+    action = extra
+    action.update(
+        dict(
+            discriminator=discriminator,
+            callable=callable,
+            args=args,
+            kw=kw,
+            includepath=includepath,
+            info=info,
+            order=order,
+            )
+        )
+    return action
 
 def resolveConflicts(actions):
     """Resolve conflicting actions
 
     Given an actions list, identify and try to resolve conflicting actions.
-    Actions conflict if they have the same non-null discriminator.
+    Actions conflict if they have the same non-None discriminator.
     Conflicting actions can be resolved if the include path of one of
     the actions is a prefix of the includepaths of the other
     conflicting actions and is unequal to the include paths in the
     other conflicting actions.
-
-    Here are some examples to illustrate how this works:
-
-    >>> from zope.configuration.tests.directives import f
-    >>> from pprint import PrettyPrinter
-    >>> pprint=PrettyPrinter(width=60).pprint
-    >>> pprint(resolveConflicts([
-    ...    (None, f),
-    ...    (1, f, (1,), {}, (), 'first'),
-    ...    (1, f, (2,), {}, ('x',), 'second'),
-    ...    (1, f, (3,), {}, ('y',), 'third'),
-    ...    (4, f, (4,), {}, ('y',), 'should be last', 99999),
-    ...    (3, f, (3,), {}, ('y',)),
-    ...    (None, f, (5,), {}, ('y',)),
-    ... ]))
-    [(None, f),
-     (1, f, (1,), {}, (), 'first'),
-     (3, f, (3,), {}, ('y',)),
-     (None, f, (5,), {}, ('y',)),
-     (4, f, (4,), {}, ('y',), 'should be last')]
-
-    >>> try:
-    ...     v = resolveConflicts([
-    ...        (None, f),
-    ...        (1, f, (2,), {}, ('x',), 'eek'),
-    ...        (1, f, (3,), {}, ('y',), 'ack'),
-    ...        (4, f, (4,), {}, ('y',)),
-    ...        (3, f, (3,), {}, ('y',)),
-    ...        (None, f, (5,), {}, ('y',)),
-    ...     ])
-    ... except ConfigurationConflictError, v:
-    ...    pass
-    >>> print v
-    Conflicting configuration actions
-      For: 1
-        eek
-        ack
-
     """
 
     # organize actions by discriminators
     unique = {}
     output = []
-    for i in range(len(actions)):
-        (discriminator, callable, args, kw, includepath, info, order
-         ) = expand_action(*(actions[i]))
+    for i, action in enumerate(actions):
+        if not isinstance(action, dict):
+            # old-style tuple action
+            action = expand_action(*action)
 
-        order = order or i
+        # "order" is an integer grouping. Actions in a lower order will be
+        # executed before actions in a higher order.  Within an order,
+        # actions are executed sequentially based on original action ordering
+        # ("i").
+        order = action['order'] or 0
+        discriminator = action['discriminator']
+
+        # "ainfo" is a tuple of (order, i, action) where "order" is a
+        # user-supplied grouping, "i" is an integer expressing the relative
+        # position of this action in the action list being resolved, and
+        # "action" is an action dictionary.  The purpose of an ainfo is to
+        # associate an "order" and an "i" with a particular action; "order"
+        # and "i" exist for sorting purposes after conflict resolution.
+        ainfo = (order, i, action)
+
         if discriminator is None:
-            # The discriminator is None, so this directive can
-            # never conflict. We can add it directly to the
-            # configuration actions.
-            output.append(
-                (order, discriminator, callable, args, kw, includepath, info)
-                )
+            # The discriminator is None, so this action can never conflict.
+            # We can add it directly to the result.
+            output.append(ainfo)
             continue
 
+        L = unique.setdefault(discriminator, [])
+        L.append(ainfo)
 
-        a = unique.setdefault(discriminator, [])
-        a.append(
-            (includepath, order, callable, args, kw, info)
-            )
-
     # Check for conflicts
     conflicts = {}
-    for discriminator, dups in unique.items():
 
-        # We need to sort the actions by the paths so that the shortest
-        # path with a given prefix comes first:
-        dups.sort()
-        (basepath, i, callable, args, kw, baseinfo) = dups[0]
-        output.append(
-            (i, discriminator, callable, args, kw, basepath, baseinfo)
-            )
-        for includepath, i, callable, args, kw, info in dups[1:]:
+    for discriminator, ainfos in unique.items():
+
+        # We use (includepath, order, i) as a sort key because we need to
+        # sort the actions by the paths so that the shortest path with a
+        # given prefix comes first.  The "first" action is the one with the
+        # shortest include path.  We break sorting ties using "order", then
+        # "i".
+        def bypath(ainfo):
+            path, order, i = ainfo[2]['includepath'], ainfo[0], ainfo[1]
+            return path, order, i
+
+        ainfos.sort(key=bypath)
+        ainfo, rest = ainfos[0], ainfos[1:]
+        output.append(ainfo)
+        _, _, action = ainfo
+        basepath, baseinfo, discriminator = (action['includepath'],
+                                             action['info'],
+                                             action['discriminator'])
+
+        for _, _, action in rest:
+            includepath = action['includepath']
             # Test whether path is a prefix of opath
             if (includepath[:len(basepath)] != basepath # not a prefix
-                or
-                (includepath == basepath)
-                ):
-                if discriminator not in conflicts:
-                    conflicts[discriminator] = [baseinfo]
-                conflicts[discriminator].append(info)
+                or includepath == basepath):
+                L = conflicts.setdefault(discriminator, [baseinfo])
+                L.append(action['info'])
 
-
     if conflicts:
         raise ConfigurationConflictError(conflicts)
 
-    # Now put the output back in the original order, and return it:
-    output.sort()
-    r = []
-    for o in output:
-        action = o[1:]
-        while len(action) > 2 and not action[-1]:
-            action = action[:-1]
-        r.append(action)
+    # sort conflict-resolved actions by (order, i) and return them
+    return [ x[2] for x in sorted(output, key=operator.itemgetter(0, 1))]
 
-    return r
-
 class ConfigurationConflictError(ConfigurationError):
 
     def __init__(self, conflicts):

Modified: zope.configuration/trunk/src/zope/configuration/tests/test_config.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/test_config.py	2011-12-04 20:11:28 UTC (rev 123573)
+++ zope.configuration/trunk/src/zope/configuration/tests/test_config.py	2011-12-05 19:59:35 UTC (rev 123574)
@@ -68,20 +68,27 @@
     >>> pprint=PrettyPrinter(width=50).pprint
 
     >>> pprint(machine.actions)
-    [(('simple', u'aa', u'xxx', 'cc'),
-      f,
-      (u'aa', u'xxx', 'cc'),
-      {},
-      (),
-      'first'),
-     (('newsimple', u'naa', u'nbb', 'ncc'),
-      f,
-      (u'naa', u'nbb', 'ncc'),
-      {},
-      (),
-      'second')]
+    [{'args': (u'aa', u'xxx', 'cc'),
+      'callable': f,
+      'discriminator': ('simple',
+                        u'aa',
+                        u'xxx',
+                        'cc'),
+      'includepath': (),
+      'info': 'first',
+      'kw': {},
+      'order': 0},
+     {'args': (u'naa', u'nbb', 'ncc'),
+      'callable': f,
+      'discriminator': ('newsimple',
+                        u'naa',
+                        u'nbb',
+                        'ncc'),
+      'includepath': (),
+      'info': 'second',
+      'kw': {},
+      'order': 0}]
 
-
     Define and try a simple directive that uses a component:
 
     >>> machine((metans, "directive"),
@@ -91,7 +98,13 @@
 
     >>> machine((ns, "factory"), factory=u".f")
     >>> pprint(machine.actions[-1:])
-    [(('factory', 1, 2), f)]
+    [{'args': (),
+      'callable': f,
+      'discriminator': ('factory', 1, 2),
+      'includepath': (),
+      'info': None,
+      'kw': {},
+      'order': 0}]
 
     Define and try a complex directive:
 
@@ -118,32 +131,54 @@
 
     >>> machine.end()
     >>> pprint(machine.actions)
-    [(('simple', u'aa', u'xxx', 'cc'),
-      f,
-      (u'aa', u'xxx', 'cc'),
-      {},
-      (),
-      'first'),
-     (('newsimple', u'naa', u'nbb', 'ncc'),
-      f,
-      (u'naa', u'nbb', 'ncc'),
-      {},
-      (),
-      'second'),
-     (('factory', 1, 2), f),
-     ('Complex.__init__', None, (), {}, (), 'third'),
-     (('Complex.factory', 1, 2),
-      f,
-      (u'ca',),
-      {},
-      (),
-      'fourth'),
-     (('Complex', 1, 2),
-      f,
-      (u'xxx', 'cc'),
-      {},
-      (),
-      'third')]
+    [{'args': (u'aa', u'xxx', 'cc'),
+      'callable': f,
+      'discriminator': ('simple',
+                        u'aa',
+                        u'xxx',
+                        'cc'),
+      'includepath': (),
+      'info': 'first',
+      'kw': {},
+      'order': 0},
+     {'args': (u'naa', u'nbb', 'ncc'),
+      'callable': f,
+      'discriminator': ('newsimple',
+                        u'naa',
+                        u'nbb',
+                        'ncc'),
+      'includepath': (),
+      'info': 'second',
+      'kw': {},
+      'order': 0},
+     {'args': (),
+      'callable': f,
+      'discriminator': ('factory', 1, 2),
+      'includepath': (),
+      'info': None,
+      'kw': {},
+      'order': 0},
+     {'args': (),
+      'callable': None,
+      'discriminator': 'Complex.__init__',
+      'includepath': (),
+      'info': 'third',
+      'kw': {},
+      'order': 0},
+     {'args': (u'ca',),
+      'callable': f,
+      'discriminator': ('Complex.factory', 1, 2),
+      'includepath': (),
+      'info': 'fourth',
+      'kw': {},
+      'order': 0},
+     {'args': (u'xxx', 'cc'),
+      'callable': f,
+      'discriminator': ('Complex', 1, 2),
+      'includepath': (),
+      'info': 'third',
+      'kw': {},
+      'order': 0}]
 
     Done with the package
 
@@ -164,35 +199,64 @@
        """ "Can't use leading dots in dotted names, no package has been set.")
 
     >>> pprint(machine.actions)
-    [(('simple', u'aa', u'xxx', 'cc'),
-      f,
-      (u'aa', u'xxx', 'cc'),
-      {},
-      (),
-      'first'),
-     (('newsimple', u'naa', u'nbb', 'ncc'),
-      f,
-      (u'naa', u'nbb', 'ncc'),
-      {},
-      (),
-      'second'),
-     (('factory', 1, 2), f),
-     ('Complex.__init__', None, (), {}, (), 'third'),
-     (('Complex.factory', 1, 2),
-      f,
-      (u'ca',),
-      {},
-      (),
-      'fourth'),
-     (('Complex', 1, 2),
-      f,
-      (u'xxx', 'cc'),
-      {},
-      (),
-      'third'),
-     (('simple', u'oaa', u'obb', 'occ'),
-      f,
-      (u'oaa', u'obb', 'occ'))]
+    [{'args': (u'aa', u'xxx', 'cc'),
+      'callable': f,
+      'discriminator': ('simple',
+                        u'aa',
+                        u'xxx',
+                        'cc'),
+      'includepath': (),
+      'info': 'first',
+      'kw': {},
+      'order': 0},
+     {'args': (u'naa', u'nbb', 'ncc'),
+      'callable': f,
+      'discriminator': ('newsimple',
+                        u'naa',
+                        u'nbb',
+                        'ncc'),
+      'includepath': (),
+      'info': 'second',
+      'kw': {},
+      'order': 0},
+     {'args': (),
+      'callable': f,
+      'discriminator': ('factory', 1, 2),
+      'includepath': (),
+      'info': None,
+      'kw': {},
+      'order': 0},
+     {'args': (),
+      'callable': None,
+      'discriminator': 'Complex.__init__',
+      'includepath': (),
+      'info': 'third',
+      'kw': {},
+      'order': 0},
+     {'args': (u'ca',),
+      'callable': f,
+      'discriminator': ('Complex.factory', 1, 2),
+      'includepath': (),
+      'info': 'fourth',
+      'kw': {},
+      'order': 0},
+     {'args': (u'xxx', 'cc'),
+      'callable': f,
+      'discriminator': ('Complex', 1, 2),
+      'includepath': (),
+      'info': 'third',
+      'kw': {},
+      'order': 0},
+     {'args': (u'oaa', u'obb', 'occ'),
+      'callable': f,
+      'discriminator': ('simple',
+                        u'oaa',
+                        u'obb',
+                        'occ'),
+      'includepath': (),
+      'info': None,
+      'kw': {},
+      'order': 0}]
 
     """
     #'
@@ -227,8 +291,16 @@
 
     >>> machine((ns, "k"), "yee ha", **{"for": u"f", "class": u"c", "x": u"x"})
 
-    >>> machine.actions
-    [(('k', 'f'), f, ('f', 'c', 'x'), {}, (), 'yee ha')]
+    >>> from pprint import PrettyPrinter
+    >>> pprint=PrettyPrinter(width=60).pprint
+    >>> pprint(machine.actions)
+    [{'args': ('f', 'c', 'x'),
+      'callable': f,
+      'discriminator': ('k', 'f'),
+      'includepath': (),
+      'info': 'yee ha',
+      'kw': {},
+      'order': 0}]
     """
 
 def test_basepath_absolute():

Modified: zope.configuration/trunk/src/zope/configuration/tests/test_xmlconfig.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/test_xmlconfig.py	2011-12-04 20:11:28 UTC (rev 123573)
+++ zope.configuration/trunk/src/zope/configuration/tests/test_xmlconfig.py	2011-12-05 19:59:35 UTC (rev 123574)
@@ -321,12 +321,11 @@
 
 def clean_actions(actions):
     return [
-      {'discriminator': discriminator,
-       'info': clean_info_path(`info`),
-       'includepath': [clean_path(p) for p in includepath],
+      {'discriminator': action['discriminator'],
+       'info': clean_info_path(`action['info']`),
+       'includepath': [clean_path(p) for p in action['includepath']],
        }
-      for (discriminator, callable, args, kw, includepath, info, order)
-      in [config.expand_action(*action) for action in actions]
+      for action in actions
       ]
 
 def clean_text_w_paths(error):

Modified: zope.configuration/trunk/src/zope/configuration/xmlconfig.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/xmlconfig.py	2011-12-04 20:11:28 UTC (rev 123573)
+++ zope.configuration/trunk/src/zope/configuration/xmlconfig.py	2011-12-05 19:59:35 UTC (rev 123574)
@@ -606,14 +606,11 @@
     # Now we'll grab the new actions, resolve conflicts,
     # and munge the includepath:
     newactions = []
+
     for action in config.resolveConflicts(_context.actions[nactions:]):
-        (discriminator, callable, args, kw, oldincludepath, info, order
-         ) = config.expand_action(*action)
-        newactions.append(
-            (discriminator, callable, args, kw, includepath, info, order)
-            )
+        action['includepath'] = includepath
+        newactions.append(action)
 
-    # and replace the new actions with the munched new actions:
     _context.actions[nactions:] = newactions
 
 def registerCommonDirectives(context):



More information about the checkins mailing list