changeset 4792:7a826f946da3

Groups2009: wikidicts were refactored. request.dict provides access only to WikiDicts. DictBase class was merged with Dict. Group class was removed. DictDict was merged with GroupDict removing methods related to the group functionality. The cache key for dicts changed from 'dicts_groups' to 'dicts'. wikidicts test was refined to capture new functionality. Changes in the other code to use DictDict.__contains__ instead of has_dict and DictDict.__getitem__ in place of dict.
author Dmitrijs Milajevs <dimazest@gmail.com>
date Sun, 07 Jun 2009 16:04:05 +0200
parents 1b84b35fbe91
children cab105f9fd55
files MoinMoin/PageEditor.py MoinMoin/_tests/test_PageEditor.py MoinMoin/_tests/test_wikidicts.py MoinMoin/events/wikidictsrescan.py MoinMoin/i18n/__init__.py MoinMoin/macro/__init__.py MoinMoin/packages.py MoinMoin/script/maint/cleancache.py MoinMoin/web/contexts.py MoinMoin/wikidicts.py docs/CHANGES.dmilajevs
diffstat 11 files changed, 67 insertions(+), 426 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/PageEditor.py	Sun Jun 07 15:25:28 2009 +0200
+++ b/MoinMoin/PageEditor.py	Sun Jun 07 16:04:05 2009 +0200
@@ -782,8 +782,8 @@
             # Users can define their own variables via
             # UserHomepage/MyDict, which override the default variables.
             userDictPage = u.name + "/MyDict"
-            if request.dicts.has_dict(userDictPage):
-                variables.update(request.dicts.dict(userDictPage))
+            if userDictPage in request.dicts:
+                variables.update(request.dicts[userDictPage])
 
         for name in variables:
             text = text.replace('@%s@' % name, variables[name])
--- a/MoinMoin/_tests/test_PageEditor.py	Sun Jun 07 15:25:28 2009 +0200
+++ b/MoinMoin/_tests/test_PageEditor.py	Sun Jun 07 16:04:05 2009 +0200
@@ -148,7 +148,7 @@
     def deleteCaches(self):
         """ Force the wiki to scan the test page into the dicts """
         from MoinMoin import caching
-        caching.CacheEntry(self.request, 'wikidicts', 'dicts_groups', scope='wiki').remove()
+        caching.CacheEntry(self.request, 'wikidicts', 'dicts', scope='wiki').remove()
         if hasattr(self.request, 'dicts'):
             del self.request.dicts
         if hasattr(self.request.cfg, 'DICTS_DATA'):
--- a/MoinMoin/_tests/test_wikidicts.py	Sun Jun 07 15:25:28 2009 +0200
+++ b/MoinMoin/_tests/test_wikidicts.py	Sun Jun 07 16:04:05 2009 +0200
@@ -7,76 +7,15 @@
     @license: GNU GPL, see COPYING for details.
 """
 
-import py
-import re
-import shutil
-
 from MoinMoin import wikidicts
-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
-
-class TestGroupPage:
-
-    def testCamelCase(self):
-        """ wikidicts: initFromText: CamelCase links """
-        text = """
- * CamelCase
-"""
-        assert self.getMembers(text) == ['CamelCase']
-
-    def testExtendedName(self):
-        """ wikidicts: initFromText: extended names """
-        text = """
- * extended name
-"""
-        assert self.getMembers(text) == ['extended name']
-
-    def testExtendedLink(self):
-        """ wikidicts: initFromText: extended link """
-        text = """
- * [[extended link]]
-"""
-        assert self.getMembers(text) == ['extended link']
+from MoinMoin._tests import become_trusted, create_page, nuke_page
 
-    def testIgnoreSecondLevelList(self):
-        """ wikidicts: initFromText: ignore non first level items """
-        text = """
-  * second level
-   * third level
-    * forth level
-     * and then some...
-"""
-        assert self.getMembers(text) == []
-
-    def testIgnoreOther(self):
-        """ wikidicts: initFromText: ignore anything but first level list itmes """
-        text = """
-= ignore this =
- * take this
+class TestDictDict:
 
-Ignore previous line and this text.
-"""
-        assert self.getMembers(text) == ['take this']
+    def setup_class(self):
+        request = self.request
+        become_trusted(request)
 
-    def testStripWhitespace(self):
-        """ wikidicts: initFromText: strip whitespace around items """
-        text = """
- *   take this
-"""
-        assert self.getMembers(text) == ['take this']
-
-    def getMembers(self, text):
-        group = wikidicts.Group(self.request, '')
-        group.initFromText(text)
-        return group.members()
-
-
-class TestDictPage:
-
-    def testGroupMembers(self):
-        """ wikidicts: create dict from keys and values in text """
         text = '''
 Text ignored
  * list items ignored
@@ -89,143 +28,29 @@
  Empty string::\x20
  Last:: last item
 '''
-        d = wikidicts.Dict(self.request, '')
-        d.initFromText(text)
-        assert d['First'] == 'first item'
-        assert d['text with spaces'] == 'second item'
-        assert d['Empty string'] == '' # XXX fails if trailing blank is missing
-        assert d['Last'] == 'last item'
-        assert len(d) == 4
-
-class TestGroupDicts:
-
-    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.
-
-        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 = wikidicts.Group(self.request, 'SystemPagesGroup')
-        #print repr(systemPages)
-        #print repr(self.request.dicts['SystemPagesGroup'])
-        for member in systemPages.members():
-            assert self.request.dicts.has_member('SystemPagesGroup', member), '%s should be in request.dict' % member
-
-        members, groups = self.request.dicts.expand_group('SystemPagesGroup')
-        assert 'SystemPagesInEnglishGroup' in groups
-        assert 'RecentChanges' in members
-        assert 'HelpContents' in members
-
-    def testRenameGroupPage(self):
-        """
-         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 = wikidicts.Group(request, '')
-        isgroup = request.cfg.cache.page_group_regexact.search
-        grouppages = request.rootpage.getPageList(user='', filter=isgroup)
-        result = request.dicts.has_member(u'AnotherGroup', u'ExampleUser')
-        nuke_page(request, u'AnotherGroup')
-
-        assert result is True
-
-    def testCopyGroupPage(self):
-        """
-         tests if the dict cache for groups is refreshed after copying a Group page
-        """
-        request = self.request
-        become_trusted(request)
-        page = create_page(request, u'SomeGroup', u" * ExampleUser")
-        page.copyPage(u'OtherGroup')
-        group = wikidicts.Group(request, '')
-        isgroup = request.cfg.cache.page_group_regexact.search
-        grouppages = request.rootpage.getPageList(user='', filter=isgroup)
-        result = request.dicts.has_member(u'OtherGroup', u'ExampleUser')
-        nuke_page(request, u'OtherGroup')
-        nuke_page(request, u'SomeGroup')
-
-        assert result is True
 
-    def testAppendingGroupPage(self):
-        """
-         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)]
-        request = self.request
-        become_trusted(request)
-        test_user = create_random_string_list(length=15, count=1)[0]
-        page = create_page(request, u'UserGroup', "\n".join(page_content))
-        page = append_page(request, u'UserGroup', u' * %s' % test_user)
-        result = request.dicts.has_member('UserGroup', test_user)
-        nuke_page(request, u'UserGroup')
-
-        assert result is True
-
-    def testUserAppendingGroupPage(self):
-        """
-         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()]
-        request = self.request
-        become_trusted(request)
-        test_user = create_random_string_list(length=15, count=1)[0]
-        page = create_page(request, u'UserGroup', "\n".join(page_content))
-        page = append_page(request, u'UserGroup', u' * %s' % test_user)
-
-        # now shortly later we create a user object
-        user = User(request, name=test_user)
-        if not user.exists():
-            User(request, name=test_user, password=test_user).save()
-
-        result = request.dicts.has_member('UserGroup', test_user)
-        nuke_page(request, u'UserGroup')
-        nuke_user(request, test_user)
+        create_page(request, u'SomeTestDict', text)
 
-        assert result is True
-
-    def testMemberRemovedFromGroupPage(self):
-        """
-         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()]
-        page_content = "\n".join(page_content)
-        request = self.request
-        become_trusted(request)
-        test_user = create_random_string_list(length=15, count=1)[0]
-        page = create_page(request, u'UserGroup', page_content)
-        page = append_page(request, u'UserGroup', u' * %s' % test_user)
-        # saves the text without test_user
-        page.saveText(page_content, 0)
-        result = request.dicts.has_member('UserGroup', test_user)
-        nuke_page(request, u'UserGroup')
+    def teardown_class(self):
+        become_trusted(self.request)
+        nuke_page(self.request, u'SomeTestDict')
 
-        assert result is False
+    def test_getitem(self):
+        dicts = self.request.dicts
 
-    def testGroupPageTrivialChange(self):
-        """
-         tests appending a username to a group page by trivial change
-        """
-        request = self.request
-        become_trusted(request)
-        test_user = create_random_string_list(length=15, count=1)[0]
-        member = u" * %s\n" % test_user
-        page = create_page(request, u'UserGroup', member)
-        # next member saved  as trivial change
-        test_user = create_random_string_list(length=15, count=1)[0]
-        member = u" * %s\n" % test_user
-        page.saveText(member, 0, trivial=1)
-        result = request.dicts.has_member('UserGroup', test_user)
-        nuke_page(request, u'UserGroup')
+        some_test_dict = dicts['SomeTestDict']
+        assert len(some_test_dict) == 4
+        assert some_test_dict['First'] == 'first item'
+        assert some_test_dict['text with spaces'] == 'second item'
+        assert some_test_dict['Empty string'] == '' # XXX fails if trailing blank is missing
+        assert some_test_dict['Last'] == 'last item'
 
-        assert result is True
+    def test_contains(self):
+        dicts = self.request.dicts
+
+        assert  u'SomeTestDict' in dicts
+        assert u'SomeNotExistingDict' not in  dicts
+
 
 coverage_modules = ['MoinMoin.wikidicts']
 
--- a/MoinMoin/events/wikidictsrescan.py	Sun Jun 07 15:25:28 2009 +0200
+++ b/MoinMoin/events/wikidictsrescan.py	Sun Jun 07 16:04:05 2009 +0200
@@ -36,7 +36,7 @@
 
     logging.debug("groupsdicts changed: %r, scan_dicts started", page.page_name)
     del request.dicts
-    gd = wikidicts.GroupDict(request)
+    gd = wikidicts.DictDict(request)
     gd.scan_dicts()
     logging.debug("groupsdicts changed: scan_dicts finished")
 
--- a/MoinMoin/i18n/__init__.py	Sun Jun 07 15:25:28 2009 +0200
+++ b/MoinMoin/i18n/__init__.py	Sun Jun 07 16:04:05 2009 +0200
@@ -304,8 +304,8 @@
             language = languages[lang]['x-language-in-english']
             dictpagename = "%sDict" % language.replace(' ', '')
             dicts = request.dicts
-            if dicts.has_dict(dictpagename):
-                userdict = dicts.dict(dictpagename)
+            if dictpagename in dicts:
+                userdict = dicts[dictpagename]
                 translated = userdict[original]
             else:
                 raise KeyError
--- a/MoinMoin/macro/__init__.py	Sun Jun 07 15:25:28 2009 +0200
+++ b/MoinMoin/macro/__init__.py	Sun Jun 07 16:04:05 2009 +0200
@@ -385,7 +385,7 @@
         key = wikiutil.get_unicode(self.request, key, 'key')
         if page is None or key is None:
             raise ValueError("You need to give: pagename, key")
-        d = self.request.dicts.dict(page)
+        d = self.request.dicts[page]
         result = d.get(key, '')
         return self.formatter.text(result)
 
--- a/MoinMoin/packages.py	Sun Jun 07 15:25:28 2009 +0200
+++ b/MoinMoin/packages.py	Sun Jun 07 16:04:05 2009 +0200
@@ -376,7 +376,7 @@
         except AttributeError:
             pass
         self.request.pages = {}
-        caching.CacheEntry(self.request, 'wikidicts', 'dicts_groups', scope='wiki').remove()
+        caching.CacheEntry(self.request, 'wikidicts', 'dicts', scope='wiki').remove()
         page.clean_acl_cache()
 
     def runScript(self, commands):
--- a/MoinMoin/script/maint/cleancache.py	Sun Jun 07 15:25:28 2009 +0200
+++ b/MoinMoin/script/maint/cleancache.py	Sun Jun 07 16:04:05 2009 +0200
@@ -56,7 +56,7 @@
             ('charts', 'pagehits'),
             ('charts', 'useragents'),
             ('user', 'name2id'),
-            ('wikidicts', 'dicts_groups'),
+            ('wikidicts', 'dicts'),
         ]
         for arena, key in arena_key_list:
             caching.CacheEntry(request, arena, key, scope='wiki').remove()
--- a/MoinMoin/web/contexts.py	Sun Jun 07 15:25:28 2009 +0200
+++ b/MoinMoin/web/contexts.py	Sun Jun 07 16:04:05 2009 +0200
@@ -372,7 +372,7 @@
     def dicts(self):
         """ Lazy initialize the dicts on the first access """
         from MoinMoin import wikidicts
-        dicts = wikidicts.GroupDict(self)
+        dicts = wikidicts.DictDict(self)
         dicts.load_dicts()
         return dicts
     dicts = EnvironProxy(dicts)
--- a/MoinMoin/wikidicts.py	Sun Jun 07 15:25:28 2009 +0200
+++ b/MoinMoin/wikidicts.py	Sun Jun 07 16:04:05 2009 +0200
@@ -1,9 +1,10 @@
 # -*- coding: iso-8859-1 -*-
 """
-    MoinMoin - Dictionary / Group Functions
+    MoinMoin - WikiDict functions.
 
     @copyright: 2003-2007 MoinMoin:ThomasWaldmann,
                 2003 by Gustavo Niemeyer
+                2009 MoinMoin:DmitrijsMilajevs
     @license: GNU GPL, see COPYING for details.
 """
 import re, time
@@ -12,35 +13,10 @@
 
 # Version of the internal data structure which is pickled.
 # Please increment if you have changed the structure.
-DICTS_PICKLE_VERSION = 6
+DICTS_PICKLE_VERSION = 7
 
 
-class DictBase(dict):
-    """ Base class for wiki dicts
-
-    To use this class, subclass it and override regex and initFromText.
-    """
-    def __init__(self, request=None, pagename=None):
-        dict.__init__(self)
-        self.name = None
-        if request is not None and pagename is not None:
-            self.loadFromPage(request, pagename)
-
-    # Regular expression used to parse text - subclass must override this
-    regex = None  # re.compile(u'...', re.MULTILINE | re.UNICODE)
-
-    def loadFromPage(self, request, name):
-        """ load the dict from wiki page <name>'s content """
-        self.name = name
-        text = Page.Page(request, name).get_raw_body()
-        self.initFromText(text)
-
-    def initFromText(self, text):
-        """ parse the wiki page text and init the dict from it """
-        raise NotImplementedError('subclasses should override this')
-
-
-class Dict(DictBase):
+class Dict(dict):
     """ Mapping of keys to values in a wiki page.
 
        How a Dict definition page should look like:
@@ -56,7 +32,19 @@
     # Key:: Value - ignore all but key:: value pairs, strip whitespace, exactly one space after the :: is required
     regex = re.compile(ur'^ (?P<key>.+?):: (?P<val>.*?) *$', re.MULTILINE | re.UNICODE)
 
-    def initFromText(self, text):
+    def __init__(self, request=None, pagename=None):
+        dict.__init__(self)
+        self.name = None
+        if request is not None and pagename is not None:
+            self._loadFromPage(request, pagename)
+
+    def _loadFromPage(self, request, name):
+        """ load the dict from wiki page <name>'s content """
+        self.name = name
+        text = Page.Page(request, name).get_raw_body()
+        self._initFromText(text)
+
+    def _initFromText(self, text):
         for match in self.regex.finditer(text):
             key, val = match.groups()
             self[key] = val
@@ -65,62 +53,6 @@
         return "<Dict name=%r items=%r>" % (self.name, self.items())
 
 
-class Group(DictBase):
-    """ Group of users, of pages, of whatever.
-
-    How a Group definition page should look like:
-
-    any text ignored
-     * member1
-      * ignored, too
-     * member2
-     * ....
-     * memberN
-    any text ignored
-
-    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)
-
-    def __init__(self, request=None, pagename=None):
-        self._list = []
-        DictBase.__init__(self, request, pagename)
-
-    def initFromText(self, text):
-        for match in self.regex.finditer(text):
-            member = match.group('member')
-            self.addmember(member)
-
-    def update(self, members):
-        self.addmembers(members.keys())
-
-    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)
-
-    def __repr__(self):
-        return "<Group name=%r items=%r>" % (self.name, self._list)
-
-
 class DictDict:
     """ a dictionary of Dict objects
 
@@ -129,8 +61,9 @@
                Default: ".*Dict$"  Defs$ Vars$ ???????????????????
     """
 
-    def __init__(self):
-        self.reset()
+    def __init__(self, request):
+        self.cfg = request.cfg
+        self.request = request
 
     def reset(self):
         self.dictdict = {}
@@ -138,19 +71,7 @@
         self.pageupdate_timestamp = 0
         self.base_timestamp = 0
         self.picklever = DICTS_PICKLE_VERSION
-
-    def has_key(self, dictname, key):
-        """ check if we have key <key> in dict <dictname> """
-        d = self.dictdict.get(dictname)
-        return d and d.has_key(key)
-
-    def keys(self, dictname):
-        """ get keys of dict <dictname> """
-        try:
-            d = self.dictdict[dictname]
-        except KeyError:
-            return []
-        return d.keys()
+        self.disk_cache_id = None
 
     def values(self, dictname):
         """ get values of dict <dictname> """
@@ -160,128 +81,26 @@
             return []
         return d.values()
 
-    def dict(self, dictname):
-        """ get dict <dictname> """
+    def __getitem__(self, dictname):
         try:
             d = self.dictdict[dictname]
         except KeyError:
             return {}
         return d
 
-    def adddict(self, request, dictname):
+    def _adddict(self, request, dictname):
         """ add a new dict (will be read from the wiki page) """
         self.dictdict[dictname] = Dict(request, dictname)
 
-    def has_dict(self, dictname):
-        """ check if we have a dict <dictname> """
+    def __contains__(self, dictname):
         return self.dictdict.has_key(dictname)
 
-    def keydict(self, key):
-        """ list all dicts that contain key """
-        dictlist = []
-        for d in self.dictdict.values():
-            if d.has_key(key):
-                dictlist.append(d.name)
-        return dictlist
-
-
-class GroupDict(DictDict):
-    """ a dictionary of Group objects
-
-       Config:
-           cfg.page_group_regex
-               Default: ".*Group$"
-    """
-
-    def __init__(self, request):
-        self.cfg = request.cfg
-        self.request = request
-
-    def reset(self):
-        self.dictdict = {}
-        self.groupdict = {} # unexpanded groups
-        self.picklever = DICTS_PICKLE_VERSION
-        self.disk_cache_id = None
-
-    def has_member(self, groupname, member):
-        """ check if we have <member> as a member of group <groupname> """
-        group = self.dictdict.get(groupname)
-        return group and group.has_member(member)
-
-    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 hasgroup(self, groupname):
-        """ check if we have a dict <dictname> """
-        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 self.hasgroup(member):
-                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_dicts(self):
         """ load the dict from the cache """
         request = self.request
         rescan = False
         arena = 'wikidicts'
-        key = 'dicts_groups'
+        key = 'dicts'
         cache = caching.CacheEntry(request, arena, key, scope='wiki', use_pickle=True)
         current_disk_cache_id = cache.uid()
         try:
@@ -313,7 +132,6 @@
         data = {
             "disk_cache_id": self.disk_cache_id,
             "dictdict": self.dictdict,
-            "groupdict": self.groupdict,
             "picklever": self.picklever
         }
 
@@ -321,8 +139,8 @@
         self.cfg.cache.DICTS_DATA = data
 
     def scan_dicts(self):
-        """ scan all pages matching the dict / group regex and
-            cache the results on disk
+        """ scan all pages matching the dict regex and cache the
+            results on disk
         """
         request = self.request
         self.reset()
@@ -335,25 +153,20 @@
         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)
+            self._adddict(request, pagename)
 
         scan_end_time = time.time()
-        self.expand_groups()
 
         arena = 'wikidicts'
-        key = 'dicts_groups'
+        key = 'dicts'
         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
+
+
--- a/docs/CHANGES.dmilajevs	Sun Jun 07 15:25:28 2009 +0200
+++ b/docs/CHANGES.dmilajevs	Sun Jun 07 16:04:05 2009 +0200
@@ -3,4 +3,7 @@
    New features:
    * Group backends. Group definitions can be stored outside of MoinMoin.
    * MoinMoin.security.AccessControlList works with the new GroupManager.
+   * wikidicts provide only WikiDicts related functionality.
+   * Use request.dicts to access WikiDict and request.groups to access groups.
+   * request.dicts __contains__ must be used instead of has_dict and __getitem__ instead of dict.