changeset 4780:c13bce051efb

Groups2009: MelitaMihaljevic's wiki_backend is used instead of one based on the GroupDict. Group pages are created in the setup_class in tests.
author Dmitrijs Milajevs <dimazest@gmail.com>
date Tue, 02 Jun 2009 14:32:40 +0200
parents c45db1dc0a4b
children cee722c2097c
files MoinMoin/groups/__init__.py MoinMoin/groups/backends/_tests/test_wiki_group.py MoinMoin/groups/backends/wiki_group.py
diffstat 3 files changed, 171 insertions(+), 283 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/groups/__init__.py	Tue Jun 02 09:50:39 2009 +0200
+++ b/MoinMoin/groups/__init__.py	Tue Jun 02 14:32:40 2009 +0200
@@ -98,7 +98,7 @@
         for backend in self._backends:
             if group_name in backend:
                 return backend[group_name]
-        raise KeyError("There is no such group")
+        raise KeyError("There is no such group %s" % group_name)
 
     def __iter__(self):
         """
--- a/MoinMoin/groups/backends/_tests/test_wiki_group.py	Tue Jun 02 09:50:39 2009 +0200
+++ b/MoinMoin/groups/backends/_tests/test_wiki_group.py	Tue Jun 02 14:32:40 2009 +0200
@@ -4,114 +4,148 @@
 
     @copyright: 2003-2004 by Juergen Hermann <jh@web.de>,
                 2007 by MoinMoin:ThomasWaldmann
+                2009 by MoinMoin:DmitrijsMilajevs
     @license: GNU GPL, see COPYING for details.
 
-    XXX Update docstrings
 """
 
 import py
 import re
 import shutil
 
+from py.test import raises
+
 from MoinMoin.groups.backends import wiki_group
 from MoinMoin import Page
 from MoinMoin.PageEditor import PageEditor
 from MoinMoin.user import User
-from MoinMoin._tests import append_page, become_trusted, create_page, create_random_string_list, nuke_page, nuke_user
+from MoinMoin._tests import append_page, become_trusted, create_page, create_random_string_list, nuke_page, nuke_user, wikiconfig
 from MoinMoin.groups import GroupManager
 
 class TestWikiGroupPage:
+    """
+    Test what backend extracts from a group page and what is ignored.
+    """
+
+    class Config(wikiconfig.Config):
+        def group_manager_init(self, request):
+            return GroupManager(backends=[wiki_group.Backend(request)])
 
     def testCamelCase(self):
-        """ wikidicts: initFromText: CamelCase links """
         text = """
  * CamelCase
 """
-        assert self.getMembers(text) == ['CamelCase']
+        assert u'CamelCase' in self.getGroup(text)
 
     def testExtendedName(self):
-        """ wikidicts: initFromText: extended names """
         text = """
  * extended name
 """
-        assert self.getMembers(text) == ['extended name']
+        assert u'extended name' in self.getGroup(text)
 
     def testExtendedLink(self):
-        """ wikidicts: initFromText: extended link """
         text = """
  * [[extended link]]
 """
-        assert self.getMembers(text) == ['extended link']
+        assert u'extended link' in self.getGroup(text)
 
     def testIgnoreSecondLevelList(self):
-        """ wikidicts: initFromText: ignore non first level items """
         text = """
   * second level
    * third level
     * forth level
      * and then some...
 """
-        assert self.getMembers(text) == []
+        assert len([x for x in self.getGroup(text)]) == 0
 
     def testIgnoreOther(self):
-        """ wikidicts: initFromText: ignore anything but first level list itmes """
         text = """
 = ignore this =
  * take this
 
 Ignore previous line and this text.
 """
-        assert self.getMembers(text) == ['take this']
+        assert u'take this' in self.getGroup(text)
 
     def testStripWhitespace(self):
-        """ wikidicts: initFromText: strip whitespace around items """
         text = """
  *   take this
 """
-        assert self.getMembers(text) == ['take this']
+        assert u'take this' in self.getGroup(text)
 
-    def getMembers(self, text):
-        group = wiki_group.Group(self.request, '')
-        group.initFromText(text)
-        return group.members()
+    def getGroup(self, text):
+        request = self.request
+        become_trusted(request)
+        create_page(request, u'SomeTestGroup', text)
+        group = request.groups[u'SomeTestGroup']
+        nuke_page(request, u'SomeTestGroup')
+        return group
 
 
 class TestWikiGroupBackend:
 
-    from MoinMoin._tests import wikiconfig
     class Config(wikiconfig.Config):
         def group_manager_init(self, request):
             return GroupManager(backends=[wiki_group.Backend(request)])
 
-    def testSystemPagesGroupInDicts(self):
-        """ wikidict: names in SystemPagesGroup should be in request.dicts
-
-        Get a list of all pages, and check that the dicts list all of them.
+    def setup_method(self, method):
 
-        Assume that the SystemPagesGroup is in the data or the underlay dir.
-        """
-        assert Page.Page(self.request, 'SystemPagesGroup').exists(), "SystemPagesGroup is missing, Can't run test"
-        systemPages = wiki_group.Group(self.request, 'SystemPagesGroup')
-        #print repr(systemPages)
-        #print repr(self.request.dicts['SystemPagesGroup'])
-        for member in systemPages.members():
-            assert member in self.request.groups[u'SystemPagesGroup'], '%s should be in request.dict' % member
+        become_trusted(self.request)
 
-        assert 'SystemPagesInEnglishGroup' in self.request.groups
-        assert 'RecentChanges' in self.request.groups[u'SystemPagesGroup']
-        assert 'HelpContents' in self.request.groups[u'SystemPagesGroup']
+        self.wiki_group_page_name = u'TestWikiGroup'
+        wiki_group_page_text = u"""
+ * Apple
+ * Banana
+ * OtherGroup"""
+        create_page(self.request, self.wiki_group_page_name, wiki_group_page_text)
+
+        self.other_group_page_name = u'OtherGroup'
+        other_group_page_text = u"""
+ * Admin
+ * Editor
+ * Apple"""
+        create_page(self.request, self.other_group_page_name, other_group_page_text)
+
+        self.third_group_page_name = u'ThirdGroup'
+        third_group_page_text = u' * Other'
+        create_page(self.request, self.third_group_page_name, third_group_page_text)
+
+    def teardown_method(self, method):
+        become_trusted(self.request)
+        nuke_page(self.request, self.wiki_group_page_name)
+        nuke_page(self.request, self.other_group_page_name)
+        nuke_page(self.request, self.third_group_page_name)
+
+    def testContainment(self):
+        groups = self.request.groups
+
+        assert u'Banana' in groups[u'TestWikiGroup']
+        assert u'Apple' in groups[u'TestWikiGroup']
+
+        assert u'Apple' in groups[u'OtherGroup']
+        assert u'Admin' in groups[u'OtherGroup']
+
+        apple_groups = groups.membergroups(u'Apple')
+        assert 2 == len(apple_groups), 'Groups must be automatically expanded'
+        assert u'TestWikiGroup' in apple_groups
+        assert u'OtherGroup' in apple_groups
+        assert u'ThirdGroup' not in apple_groups
+
+        raises(KeyError, lambda: groups[u'NotExistingssssGroup'])
+
+        assert u'ThirdGroup' in groups
 
     def testRenameGroupPage(self):
         """
-         tests if the dict cache for groups is refreshed after renaming a Group page
+         tests if the dict cache for groups is refreshed after
+         renaming a Group page
         """
         request = self.request
         become_trusted(request)
+
         page = create_page(request, u'SomeGroup', u" * ExampleUser")
         page.renamePage('AnotherGroup')
-        group = wiki_group.Group(request, '')
-        isgroup = request.cfg.cache.page_group_regexact.search
-        grouppages = request.rootpage.getPageList(user='', filter=isgroup)
+
         result = u'ExampleUser' in request.groups[u'AnotherGroup']
         nuke_page(request, u'AnotherGroup')
 
@@ -123,12 +157,12 @@
         """
         request = self.request
         become_trusted(request)
+
         page = create_page(request, u'SomeGroup', u" * ExampleUser")
-        page.copyPage(u'OtherGroup')
-        group = wiki_group.Group(request, '')
-        isgroup = request.cfg.cache.page_group_regexact.search
-        grouppages = request.rootpage.getPageList(user='', filter=isgroup)
-        result = u'ExampleUser' in request.groups[u'OtherGroup']
+        page.copyPage(u'SomeOtherGroup')
+
+        result = u'ExampleUser' in request.groups[u'SomeOtherGroup']
+
         nuke_page(request, u'OtherGroup')
         nuke_page(request, u'SomeGroup')
 
@@ -136,7 +170,8 @@
 
     def testAppendingGroupPage(self):
         """
-         tests scalability by appending a name to a large list of group members
+         tests scalability by appending a name to a large list of
+         group members
         """
         # long list of users
         page_content = [u" * %s" % member for member in create_random_string_list(length=15, count=30000)]
@@ -152,7 +187,8 @@
 
     def testUserAppendingGroupPage(self):
         """
-         tests appending a username to a large list of group members and user creation
+         tests appending a username to a large list of group members
+         and user creation
         """
         # long list of users
         page_content = [u" * %s" % member for member in create_random_string_list()]
@@ -175,7 +211,8 @@
 
     def testMemberRemovedFromGroupPage(self):
         """
-         tests appending a member to a large list of group members and recreating the page without the member
+         tests appending a member to a large list of group members and
+         recreating the page without the member
         """
         # long list of users
         page_content = [u" * %s" % member for member in create_random_string_list()]
--- a/MoinMoin/groups/backends/wiki_group.py	Tue Jun 02 09:50:39 2009 +0200
+++ b/MoinMoin/groups/backends/wiki_group.py	Tue Jun 02 14:32:40 2009 +0200
@@ -1,255 +1,106 @@
 # -*- coding: iso-8859-1 -*-
 """
-    MoinMoin - wiki_group backend provides access to the group definitions on a wiki pages.
-
-    @copyright: 2003-2007 MoinMoin:ThomasWaldmann,
-                2003 by Gustavo Niemeyer
-                2009 MoinMoin:DmitrijsMilajevs
-    @license: GNU GPL, see COPYING for details.
-"""
-import re, time
-
-from MoinMoin import caching, Page
-from MoinMoin.wikidicts import DictBase, DictDict
-
-# Version of the internal data structure which is pickled.
-# Please increment if you have changed the structure.
-DICTS_PICKLE_VERSION = 6
-
-class Group(DictBase):
-    """ Group of users, of pages, of whatever.
-
-    How a Group definition page should look like:
+MoinMoin - wiki group backend
 
-    any text ignored
-     * member1
-      * ignored, too
-     * member2
-     * ....
-     * memberN
-    any text ignored
+The wiki group backend enables you to define groups on wiki pages.  To
+find group pages, request.cfg.cache.page_group_regexact pattern is
+used.  To find group members, it parses theses pages and extracts the
+first level list (wiki markup).
 
-    If there are any free links using [[free link]] notation, the markup
-    is stripped from the member.
-    """
-    # * Member - ignore all but first level list items, strip whitespace, strip free links markup
-    regex = re.compile(ur'^ \* +(?:\[\[)?(?P<member>.+?)(?:\]\])? *$', re.MULTILINE | re.UNICODE)
+@copyright: 2008 MoinMoin:ThomasWaldmann,
+            2008 MoinMoin:MelitaMihaljevic
+@license: GPL, see COPYING for details
+"""
 
-    def __init__(self, request=None, pagename=None):
-        self._list = []
-        DictBase.__init__(self, request, pagename)
+import re
 
-    def initFromText(self, text):
-        for match in self.regex.finditer(text):
-            member = match.group('member')
-            self.addmember(member)
+from MoinMoin import caching, wikiutil
+from MoinMoin.Page import Page
 
-    def update(self, members):
-        self.addmembers(members.keys())
+class Group(object):
+    name = 'wiki'
+
+    # * Member - ignore all but first level list items, strip
+    # whitespace, strip free links markup. This is used for parsing
+    # pages in order to find group page members
+    group_page_parse_re = re.compile(ur'^ \* +(?:\[\[)?(?P<member>.+?)(?:\]\])? *$', re.MULTILINE | re.UNICODE)
+
+    def __init__(self, request, group_name):
+        """
+        Initialize a wiki group.
+
+        @parm request: request object
+        @parm group_name: group name (== group page name)
+        """
+        page = Page(request, group_name)
+        if page.exists():
+            arena = 'pagegroups'
+            key = wikiutil.quoteWikinameFS(group_name)
+            cache = caching.CacheEntry(request, arena, key, scope='wiki', use_pickle=True)
+            try:
+                cache_mtime = cache.mtime()
+                page_mtime = wikiutil.version2timestamp(page.mtime_usecs())
+                # TODO: fix up-to-date check mtime granularity problems
+                if cache_mtime > page_mtime:
+                    # cache is uptodate
+                    self.group = 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)
+        else:
+            # we have no such group
+            raise KeyError
+
+    def _parse_page(self, text):
+        """
+        Parse <group_name> page and return members.
+        """
+        return [match.group('member') for match in self.group_page_parse_re.finditer(text)]
+
+    def __contains__(self, name):
+        """
+        Check if name is a member of this group.
+        """
+        return name in self.group
 
     def __iter__(self):
-        return iter(self._list)
-
-    def members(self):
-        """ return the group's members """
-        return self._list[:]
-
-    def addmembers(self, members):
-        """ add a list of members to the group """
-        for m in members:
-            self.addmember(m)
-
-    def addmember(self, member):
-        """ add a member to the group """
-        self[member] = 1
-        self._list.append(member)
-
-    def has_member(self, member):
-        """ check if the group has member <member> """
-        return self.has_key(member)
+        """
+        Iterate over group members.
+        """
+        return iter(self.group)
 
-    def __repr__(self):
-        return "<Group name=%r items=%r>" % (self.name, self._list)
-
-class Backend(DictDict):
-    """
-    a dictionary of Group objects
 
-    Config:
-         cfg.page_group_regex
-            Default: ".*Group$"
-
-    XXX Group expanding. Groups should be expanded in the
-        initialization, of the backend (which probably bad idea,
-        because all groups may take a lot of memory, or expanded when
-        needed. This must be transparent outside of the class.
-
-    XXX Some methods were deleted (has_member, hasgroup), load_dicts
-        was renamed to load_cache, scan_dicts to update_cache methods
-        of super-class may be used somewhere in the code.
-    """
+class Backend(object):
+    name = 'wiki'
 
     def __init__(self, request):
-        self.cfg = request.cfg
-        self.request = request
-
-        # XXX probably, not the best place
-        self.update_cache()
-
-    def reset(self):
-        self.dictdict = {}
-        self.groupdict = {} # unexpanded groups
-        self.picklever = DICTS_PICKLE_VERSION
-        self.disk_cache_id = None
-
-    def members(self, groupname):
-        """ get members of group <groupname> """
-        try:
-            group = self.dictdict[groupname]
-        except KeyError:
-            return []
-        return group.members()
-
-    def addgroup(self, request, groupname):
-        """ add a new group (will be read from the wiki page) """
-        grp = Group(request, groupname)
-        self.groupdict[groupname] = grp
-        self.expand_groups()
-
-    def __contains__(self, groupname):
-        """
-        Check if group groupname is defined in some wikipage.
-        """
-        return self.groupdict.has_key(groupname)
-
-    def __getitem__(self, name):
-        return self.groupdict[name]
-
-    def membergroups(self, member):
-        """ list all groups where member is a member of """
-        grouplist = []
-        for group in self.dictdict.values():
-            if group.has_member(member):
-                grouplist.append(group.name)
-        return grouplist
-
-    def expand_groups(self):
-        """ copy expanded groups to self.dictdict """
-        for name in self.groupdict:
-            members, groups = self.expand_group(name)
-            members.update(groups)
-            grp = Group()
-            grp.update(members)
-            self.dictdict[name] = grp
-
-    def expand_group(self, name):
-        """ Recursively expand group <name>, using the groupdict (which is a not expanded
-            dict of all group names -> group dicts). We return a flat list of group member
-            names and group names.
-
-        Given a groupdict (self) with two groups:
-
-            MainGroup: [A, SubGroup]
-            SubGroup: [B, C]
-
-        MainGroup is expanded to:
-
-            self.expand_group('MainGroup') -> [A, B, C], [MainGroup, SubGroup]
         """
-        groups = {name: 1}
-        members = {}
-        groupmembers = self[name].keys()
-        for member in groupmembers:
-            # Skip duplicates
-            if member in groups:
-                continue
-            # Add member and its children
-            if member in self:
-                new_members, new_groups = self.expand_group(member)
-                groups.update(new_groups)
-                members.update(new_members)
-            else:
-                members[member] = 1
-        return members, groups
-
-    def load_cache(self):
-        """ load the dict from the cache """
-        request = self.request
-        rescan = False
-        arena = 'wikidicts'
-        key = 'dicts_groups'
-        cache = caching.CacheEntry(request, arena, key, scope='wiki', use_pickle=True)
-        current_disk_cache_id = cache.uid()
-        try:
-            self.__dict__.update(self.cfg.cache.DICTS_DATA)
-            if (current_disk_cache_id is None or
-                current_disk_cache_id != self.disk_cache_id):
-                self.reset()
-                raise AttributeError # not fresh, force load from disk
-            else:
-                return
-        except AttributeError:
-            try:
-                data = cache.content()
-                self.__dict__.update(data)
-                self.disk_cache_id = current_disk_cache_id
-
-                # invalidate the cache if the pickle version changed
-                if self.picklever != DICTS_PICKLE_VERSION:
-                    raise # force rescan
-            except:
-                self.reset()
-                rescan = True
+        Create a group manager backend object.
+        """
+        self.request = request
+        self.page_group_regex = request.cfg.cache.page_group_regexact
 
-        if rescan:
-            self.scan_dicts()
-            self.load_dicts() # try again
-            return
-
-        data = {
-            "disk_cache_id": self.disk_cache_id,
-            "dictdict": self.dictdict,
-            "groupdict": self.groupdict,
-            "picklever": self.picklever
-        }
-
-        # remember it (persistent environments)
-        self.cfg.cache.DICTS_DATA = data
-
-    def update_cache(self):
-        """ scan all pages matching the dict / group regex and
-            cache the results on disk
+    def __contains__(self, group_name):
         """
-        request = self.request
-        self.reset()
-
-        # XXX get cache write lock here
-        scan_begin_time = time.time()
+        Check if we have a Page <groupname>.
+        """
+        page = Page(self.request, group_name)
+        return page.exists()
 
-        # Get all pages in the wiki - without user filtering using filter
-        # function - this makes the page list about 10 times faster.
-        isdict = self.cfg.cache.page_dict_regexact.search
-        dictpages = request.rootpage.getPageList(user='', filter=isdict)
-        for pagename in dictpages:
-            self.adddict(request, pagename)
-
-        isgroup = self.cfg.cache.page_group_regexact.search
-        grouppages = request.rootpage.getPageList(user='', filter=isgroup)
-        for pagename in grouppages:
-            self.addgroup(request, pagename)
+    def __iter__(self):
+        """
+        Iterate over group names of groups available in the wiki.
+        """
+        grouppages = self.request.rootpage.getPageList(user='', filter=self.page_group_regex.search)
+        return iter(grouppages)
 
-        scan_end_time = time.time()
-        self.expand_groups()
+    def __getitem__(self, group_name):
+        """
+        Return wiki group backend object.
+        """
+        return Group(self.request, group_name)
 
-        arena = 'wikidicts'
-        key = 'dicts_groups'
-        cache = caching.CacheEntry(request, arena, key, scope='wiki', use_pickle=True)
-        data = {
-            "scan_begin_time": scan_begin_time,
-            "scan_end_time": scan_end_time,
-            "dictdict": self.dictdict,
-            "groupdict": self.groupdict,
-            "picklever": self.picklever
-        }
-        cache.update(data)
-        # XXX release cache write lock here