diff --git a/src/zope/i18nmessageid/_zope_i18nmessageid_message.c b/src/zope/i18nmessageid/_zope_i18nmessageid_message.c index 66cd93d..450b9fe 100644 --- a/src/zope/i18nmessageid/_zope_i18nmessageid_message.c +++ b/src/zope/i18nmessageid/_zope_i18nmessageid_message.c @@ -100,8 +100,13 @@ Message_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (default_ != NULL) self->default_ = default_; - if (mapping != NULL) - self->mapping = mapping; + if (mapping == Py_None) { + self->mapping = Py_None; + Py_INCREF(Py_None); + } else if (mapping != NULL) { + self->mapping = PyDictProxy_New(mapping); + } else { + } if (value_plural != NULL) self->value_plural = value_plural; @@ -172,15 +177,27 @@ Message_dealloc(Message *self) static PyObject * Message_reduce(Message *self) { - PyObject *value, *result; + PyObject *value, *mapping, *result; value = PyObject_CallFunctionObjArgs((PyObject *)&PyUnicode_Type, self, NULL); if (value == NULL) return NULL; + if (self->mapping == NULL) { + mapping = Py_None; + } + else if (self->mapping == Py_None) { + mapping = Py_None; + } else { + mapping = PyObject_CallFunctionObjArgs( + (PyObject *)&PyDict_Type, self->mapping, NULL); + if (mapping == NULL) { + return NULL; + } + } result = Py_BuildValue("(O(OOOOOOO))", Py_TYPE(&(self->base)), value, self->domain ? self->domain : Py_None, self->default_ ? self->default_ : Py_None, - self->mapping ? self->mapping : Py_None, + mapping, self->value_plural ? self->value_plural : Py_None, self->default_plural ? self->default_plural : Py_None, self->number ? self->number : Py_None); diff --git a/src/zope/i18nmessageid/message.py b/src/zope/i18nmessageid/message.py index bd06e8d..122c14d 100644 --- a/src/zope/i18nmessageid/message.py +++ b/src/zope/i18nmessageid/message.py @@ -13,6 +13,8 @@ ############################################################################## """I18n Messages and factories. """ +import types + __docformat__ = "reStructuredText" _marker = object() @@ -54,8 +56,10 @@ def __new__(cls, ustr, domain=_marker, default=_marker, mapping=_marker, self.domain = domain if default is not _marker: self.default = default - if mapping is not _marker: - self.mapping = mapping + if mapping is None: + self.mapping = None + elif mapping is not _marker: + self.mapping = types.MappingProxyType(mapping) if msgid_plural is not _marker: self.msgid_plural = msgid_plural if default_plural is not _marker: @@ -81,9 +85,17 @@ def __setattr__(self, key, value): return str.__setattr__(self, key, value) def __getstate__(self): + # types.MappingProxyType is not picklable + mapping = None if self.mapping is None else dict(self.mapping) return ( - str(self), self.domain, self.default, self.mapping, - self.msgid_plural, self.default_plural, self.number) + str(self), + self.domain, + self.default, + mapping, + self.msgid_plural, + self.default_plural, + self.number, + ) def __reduce__(self): return self.__class__, self.__getstate__() diff --git a/src/zope/i18nmessageid/tests.py b/src/zope/i18nmessageid/tests.py index 1b2bbee..30ba48a 100644 --- a/src/zope/i18nmessageid/tests.py +++ b/src/zope/i18nmessageid/tests.py @@ -53,9 +53,20 @@ def test_values(self): self.assertEqual(message.msgid_plural, 'testings') self.assertEqual(message.default_plural, 'defaults') self.assertEqual(message.number, 2) + + with self.assertRaises(TypeError): + message.mapping['key'] = 'new value' + if self._TEST_READONLY: self.assertTrue(message._readonly) + def test_mapping_is_readonly(self): + mapping = {'key': 'value'} + message = self._makeOne('testing', 'domain', mapping=mapping) + + with self.assertRaises(TypeError): + message.mapping['key'] = 'new value' + def test_values_without_defaults(self): mapping = {'key': 'value'} message = self._makeOne( @@ -206,6 +217,16 @@ def test_unknown_immutable(self): with self.assertRaises((TypeError, AttributeError)): message.unknown = 'unknown' + def test___reduce___wo_values(self): + message = self._makeOne('testing') + klass, state = message.__reduce__() + self.assertTrue(klass is self._getTargetClass()) + self.assertTrue(message.mapping is None) + self.assertEqual( + state, + ('testing', None, None, None, None, None, None) + ) + def test___reduce__(self): mapping = {'key': 'value'} source = self._makeOne('testing')