changeset 4793:cab105f9fd55

Groups2009: wiki_page.Group doesn't expand itself during initialization, instead it iterates over subgroups during __iter__ and checks subgroups during __contains__. __repr__ for Group. Tests for recursive groups.
author Dmitrijs Milajevs <dimazest@gmail.com>
date Mon, 08 Jun 2009 19:49:39 +0200
parents 7a826f946da3
children d9151c006aab
files MoinMoin/groups/backends/_tests/test_wiki_group.py MoinMoin/groups/backends/wiki_group.py
diffstat 2 files changed, 98 insertions(+), 37 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/groups/backends/_tests/test_wiki_group.py	Sun Jun 07 16:04:05 2009 +0200
+++ b/MoinMoin/groups/backends/_tests/test_wiki_group.py	Mon Jun 08 19:49:39 2009 +0200
@@ -90,13 +90,19 @@
         become_trusted(self.request)
 
         test_groups = {u'EditorGroup': [u'AdminGroup', u'John', u'JoeDoe', u'Editor'],
-                            u'AdminGroup': [u'Admin', u'Admin2', u'John'],
-                            u'OtherGroup': [u'SomethingOther']}
+                       u'AdminGroup': [u'Admin', u'Admin2', u'John'],
+                       u'OtherGroup': [u'SomethingOther'],
+                       u'RecursiveGroup': [u'Something', u'OtherRecursiveGroup'],
+                       u'OtherRecursiveGroup': [u'RecursiveGroup', u'Anything'],
+                       u'ThirdRecursiveGroup': [u'ThirdRecursiveGroup', u'Banana']}
 
         self.expanded_groups = {u'EditorGroup': [u'Admin', u'Admin2', u'John',
                                                  u'JoeDoe', u'Editor'],
                                 u'AdminGroup': [u'Admin', u'Admin2', u'John'],
-                                u'OtherGroup': [u'SomethingOther']}
+                                u'OtherGroup': [u'SomethingOther'],
+                                u'RecursiveGroup': [u'Anything', u'Something'],
+                                u'OtherRecursiveGroup': [u'Anything', u'Something'],
+                                u'ThirdRecursiveGroup': [u'Banana']}
 
         for (group, members) in test_groups.iteritems():
             page_text = ' * %s' % '\n * '.join(members)
--- a/MoinMoin/groups/backends/wiki_group.py	Sun Jun 07 16:04:05 2009 +0200
+++ b/MoinMoin/groups/backends/wiki_group.py	Mon Jun 08 19:49:39 2009 +0200
@@ -8,7 +8,8 @@
 first level list (wiki markup).
 
 @copyright: 2008 MoinMoin:ThomasWaldmann,
-            2008 MoinMoin:MelitaMihaljevic
+            2008 MoinMoin:MelitaMihaljevic,
+            2009 MoinMoin:DmitrijsMilajevs
 @license: GPL, see COPYING for details
 """
 
@@ -17,8 +18,14 @@
 from MoinMoin import caching, wikiutil
 from MoinMoin.Page import Page
 
+
+backend_type = "wiki"
+backend_name= "wiki_group"
+
+
 class Group(object):
-    name = 'wiki'
+    backend_type = backend_type
+    backend_name = backend_name
 
     # * Member - ignore all but first level list items, strip
     # whitespace, strip free links markup. This is used for parsing
@@ -33,6 +40,7 @@
         @parm group_name: group name (== group page name)
         """
         self.request = request
+        self.group_name = group_name
 
         page = Page(request, group_name)
         if page.exists():
@@ -45,63 +53,110 @@
                 # TODO: fix up-to-date check mtime granularity problems
                 if cache_mtime > page_mtime:
                     # cache is uptodate
-                    self.group = cache.content()
+                    self.members, self.member_groups = cache.content()
                 else:
                     raise caching.CacheError
             except caching.CacheError:
                 # either cache does not exist, is erroneous or not uptodate: recreate it
                 text = page.get_raw_body()
-                self.group = set(self._parse_page(text))
-                cache.update(self.group)
+                self.members, self.member_groups = self._parse_page(text)
+                cache.update((self.members, self. member_groups))
         else:
             # we have no such group
             raise KeyError
 
-    def _expand_group(self, group_name):
-        groups = self.request.groups
-        expanded_groups = set(group_name)
-        members = set()
-
-        for member in groups[group_name]:
-            if member not in expanded_groups and member in groups:
-                expanded_groups.add(member)
-                members.update(self._expand_group(member))
-            else:
-                members.add(member)
-        return members
-
     def _parse_page(self, text):
         """
-        Parse <group_name> page and return members.
+        Parse <text> and return members and groups defined in the <text>
         """
         groups = self.request.groups
-        unexpanded_members = [match.group('member') for match in self.group_page_parse_re.finditer(text)]
 
-        members = set()
-        # Expand possible recursive groups
-        for member in unexpanded_members:
-            if member in groups:
-                members.update(self._expand_group(member))
+        text_members = (match.group('member') for match in self.group_page_parse_re.finditer(text))
+        members_final = set()
+        member_groups = set()
+
+        for member in text_members:
+            if self.request.cfg.cache.page_group_regexact.match(member):
+                member_groups.add(member)
             else:
-                members.add(member)
-
-        return members
+                members_final.add(member)
 
-    def __contains__(self, name):
+        return members_final, member_groups
+
+    def _contains(self, member, processed_groups):
         """
-        Check if name is a member of this group.
+        First check if <member> is part of this group and then check
+        for every subgroup in this group.
+
+        <processed_groups> is needed to avoid infinite recursion, if
+        groups are defined recursively.
+
+        @param member: member name [unicode]
+        @param processed_groups: groups which were checked for containment before [set]
         """
-        return name in self.group
+        processed_groups.add(self.group_name)
+
+        if member in self.members:
+            return True
+        else:
+            groups = self.request.groups
+            for group_name in self.member_groups:
+                if group_name not in processed_groups and groups[group_name]._contains(member, processed_groups):
+                    processed_groups.add(group_name)
+                    return True
+
+        return False
+
+    def __contains__(self, member):
+        """
+        Check if <member> is defined in this group. Checks also for subgroups.
+        """
+        return self._contains(member, set())
+
+    def _iter(self, yielded_members, processed_groups):
+        """
+        Iterate first over members of this group, then over subgroups of this group.
+
+        <yielded_members> and <processed_groups> are needed to avoid infinite recursion.
+        This can happen if there are two groups like these:
+           OneGroup: Something, OtherGroup
+           OtherGroup: OneGroup, SomethingOther
+
+        @param yielded_members: members which have been already yielded before [set]
+        @param processed_groups: group names which have been iterated before [set]
+        """
+        processed_groups.add(self.group_name)
+
+        for member in self.members:
+            if member not in yielded_members:
+                yield member
+                yielded_members.add(member)
+
+        groups = self.request.groups
+        for group_name in self.member_groups:
+            if group_name not in processed_groups:
+                for member in groups[group_name]._iter(yielded_members, processed_groups):
+                    processed_groups.add(group_name)
+                    if member not in yielded_members:
+                        yield member
+                        yielded_members.add(member)
+
 
     def __iter__(self):
         """
-        Iterate over group members.
+        Iterate over members of this group. Iterates also over subgroups if any.
         """
-        return iter(self.group)
+        return self._iter(set(), set())
+
+    def __repr__(self):
+        return "<Group group_name=%s members=%s member_groups=%s>" %(self.group_name,
+                                                                     self.members,
+                                                                     self.member_groups)
 
 
 class Backend(object):
-    name = 'wiki'
+    backend_type = backend_type
+    backend_name = backend_name
 
     def __init__(self, request):
         """