[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