[Checkins] SVN: zope.i18nmessageid/branches/c-extension-less/ Make sure messages are immutable.

Malthe Borch mborch at gmail.com
Fri Dec 12 10:58:24 EST 2008


Log message for revision 93992:
  Make sure messages are immutable.

Changed:
  U   zope.i18nmessageid/branches/c-extension-less/CHANGES.txt
  U   zope.i18nmessageid/branches/c-extension-less/src/zope/i18nmessageid/message.py
  U   zope.i18nmessageid/branches/c-extension-less/src/zope/i18nmessageid/messages.txt

-=-
Modified: zope.i18nmessageid/branches/c-extension-less/CHANGES.txt
===================================================================
--- zope.i18nmessageid/branches/c-extension-less/CHANGES.txt	2008-12-12 15:46:49 UTC (rev 93991)
+++ zope.i18nmessageid/branches/c-extension-less/CHANGES.txt	2008-12-12 15:58:24 UTC (rev 93992)
@@ -4,6 +4,8 @@
 Version 3.5.0 (unreleased)
 -------------------------
 
+- Make sure messages are immutable.
+
 - Removed C-extension.
 
 - Added support to bootstrap on Jython.

Modified: zope.i18nmessageid/branches/c-extension-less/src/zope/i18nmessageid/message.py
===================================================================
--- zope.i18nmessageid/branches/c-extension-less/src/zope/i18nmessageid/message.py	2008-12-12 15:46:49 UTC (rev 93991)
+++ zope.i18nmessageid/branches/c-extension-less/src/zope/i18nmessageid/message.py	2008-12-12 15:58:24 UTC (rev 93992)
@@ -17,14 +17,19 @@
 """
 __docformat__ = "reStructuredText"
 
+from weakref import WeakKeyDictionary
+registry = WeakKeyDictionary()
+
 class Message(unicode):
     """Message (Python implementation)
 
-    This is a string used as a message.  It has a domain attribute that is
-    its source domain, and a default attribute that is its default text to
-    display when there is no translation.  domain may be None meaning there is
-    no translation domain.  default may also be None, in which case the
-    message id itself implicitly serves as the default text.
+    This is a string used as a translation message. It has a
+    ``domain`` attribute that is its translation domain, and a
+    ``default`` attribute that is its default text to display when
+    there is no translation. ``domain`` may be ``None`` meaning there
+    is no translation domain; ``default`` may also be ``None``, in
+    which case the message id itself implicitly serves as the default
+    text.
 
     >>> from zope.i18nmessageid.message import Message
     >>> robot = Message(u"robot-message", 'futurama', u"${name} is a robot.")
@@ -41,55 +46,76 @@
     >>> robot.domain = "planetexpress"
     Traceback (most recent call last):
     ...
-    TypeError: readonly attribute
+    AttributeError: can't set attribute
 
     >>> robot.default = u"${name} is not a robot."
     Traceback (most recent call last):
     ...
-    TypeError: readonly attribute
+    AttributeError: can't set attribute
 
     >>> robot.mapping = {u'name': u'Bender'}
     Traceback (most recent call last):
     ...
-    TypeError: readonly attribute
+    AttributeError: can't set attribute
 
     >>> new_robot = Message(robot, mapping={u'name': u'Bender'})
+    
     >>> new_robot
     u'robot-message'
+    
     >>> new_robot.domain
     'futurama'
+    
     >>> new_robot.default
     u'${name} is a robot.'
+    
     >>> new_robot.mapping
     {u'name': u'Bender'}
 
+    Verify that messages reduces to their arguments.
+
     >>> callable, args = new_robot.__reduce__()
     >>> callable is Message
     True
+    
     >>> args
     (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'})
 
     >>> fembot = Message(u'fembot')
+    
     >>> callable, args = fembot.__reduce__()
     >>> callable is Message
     True
+    
     >>> args
     (u'fembot', None, None, None)
-
-    Verify pickling.
     
+    Verify pickling. First, pickle the message.
+    
     >>> from pickle import dumps, loads
     >>> pystate = dumps(new_robot)
+    
+    Verify that we have an empty registry when there are no hard
+    references to the messages.
+    
+    >>> from zope.i18nmessageid.message import registry
+    >>> del robot, fembot, new_robot
+    >>> len(registry)
+    0
+    
+    Load the pickle and verify registry.
+    
     >>> pickle_bot = loads(pystate)
+    >>> len(registry)
+    1
+
+    We expect the message properties to be available.
+    
     >>> pickle_bot, pickle_bot.domain, pickle_bot.default, pickle_bot.mapping
     (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'})
-    >>> pickle_bot.__reduce__()[0] is Message
-    True
     
     """
 
-    __slots__ = ('domain', 'default', 'mapping', '_readonly')
-
     def __new__(cls, ustr, domain=None, default=None, mapping=None):
         self = unicode.__new__(cls, ustr)
         if isinstance(ustr, self.__class__):
@@ -101,27 +127,28 @@
         # make sure a non-trivial default value is a unicode string
         if default is not None:
             default = unicode(default)
-            
-        unicode.__setattr__(self, 'domain', domain)
-        unicode.__setattr__(self, 'default', default)
-        unicode.__setattr__(self, 'mapping', mapping)
-        
+
+        registry[self] = (domain, default, mapping)
         return self
 
-    def __setattr__(self, key, value):
-        """Message is immutable.
-
-        It cannot be changed once the message id is created.
-        """
-
-        raise TypeError('readonly attribute')
-    
     def __reduce__(self):
-        return self.__class__, self.__getstate__()
+        return type(self), self.__getstate__()
 
     def __getstate__(self):
         return unicode(self), self.domain, self.default, self.mapping
 
+    @property
+    def domain(self):
+        return registry[self][0]
+
+    @property
+    def default(self):
+        return registry[self][1]
+
+    @property
+    def mapping(self):
+        return registry[self][2]
+
 class MessageFactory(object):
     """Factory for creating i18n messages."""
 

Modified: zope.i18nmessageid/branches/c-extension-less/src/zope/i18nmessageid/messages.txt
===================================================================
--- zope.i18nmessageid/branches/c-extension-less/src/zope/i18nmessageid/messages.txt	2008-12-12 15:46:49 UTC (rev 93991)
+++ zope.i18nmessageid/branches/c-extension-less/src/zope/i18nmessageid/messages.txt	2008-12-12 15:58:24 UTC (rev 93992)
@@ -27,13 +27,14 @@
 ZopeMessageFactory
 ------------------
 
-  >>> from zope.i18nmessageid import ZopeMessageFactory as _z_
-  >>> foo = _z_('foo')
-  >>> foo.domain
+A message factory for the 'zope' translation domain is available for
+direct import.
+
+  >>> from zope.i18nmessageid import ZopeMessageFactory as _
+  >>> msg = _('foo')
+  >>> msg.domain
   'zope'
-  
 
-
 Example
 -------
 
@@ -42,14 +43,16 @@
 translatable string extraction tools such as xgettext.  We then call _
 with a string that needs to be translatable:
 
-  >>> from zope.i18nmessageid import MessageFactory, Message
+  >>> from zope.i18nmessageid import MessageFactory  
   >>> _ = MessageFactory("futurama")
+  
   >>> robot = _(u"robot-message", u"${name} is a robot.")
 
 Messages at first seem like they are unicode strings:
 
   >>> robot
   u'robot-message'
+  
   >>> isinstance(robot, unicode)
   True
 
@@ -58,57 +61,13 @@
 
   >>> robot.default
   u'${name} is a robot.'
-  >>> robot.mapping
+  
   >>> robot.domain
   'futurama'
 
 The message's attributes are considered part of the immutable message
-object.  They cannot be changed once the message id is created:
+object. They cannot be changed once the message id is created.
 
-  >>> robot.domain = "planetexpress"
-  Traceback (most recent call last):
-  ...
-  TypeError: readonly attribute
-
-  >>> robot.default = u"${name} is not a robot."
-  Traceback (most recent call last):
-  ...
-  TypeError: readonly attribute
-
-  >>> robot.mapping = {u'name': u'Bender'}
-  Traceback (most recent call last):
-  ...
-  TypeError: readonly attribute
-
-If you need to change their information, you'll have to make a new
-message id object:
-
-  >>> new_robot = Message(robot, mapping={u'name': u'Bender'})
-  >>> new_robot
-  u'robot-message'
-  >>> new_robot.domain
-  'futurama'
-  >>> new_robot.default
-  u'${name} is a robot.'
-  >>> new_robot.mapping
-  {u'name': u'Bender'}
-
-Last but not least, messages are reduceable for pickling:
-
-  >>> callable, args = new_robot.__reduce__()
-  >>> callable is Message
-  True
-  >>> args
-  (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'})
-
-  >>> fembot = Message(u'fembot')
-  >>> callable, args = fembot.__reduce__()
-  >>> callable is Message
-  True
-  >>> args
-  (u'fembot', None, None, None)
-
-
 Message IDs and backward compatability
 --------------------------------------
 
@@ -132,3 +91,6 @@
   Zope 3.2: Mutable message ids are deprecated.
 
   Zope 3.3: Mutable message ids are removed.
+
+
+  



More information about the Checkins mailing list