[Zope3-dev] Re: Make message ids into "rocks", was Re: Re: SVN: Zope3/trunk/src/zope/ Internationalized apidoc modules description.

Jim Fulton jim at zope.com
Fri Sep 3 10:12:06 EDT 2004


Philipp von Weitershausen wrote:
> Jim,
> 
>>>> I would suggest to
>>>>
>>>> 1.) make the 'default' attribute a read-only attribute. 'default' 
>>>> and message-id are to be seen as one set of information, always tied 
>>>> together.
>>>
>>>
>>> The 'domain' attribute should be a read-only attribute too.
>>
>>
>> Yup. All of the attributes must be read only.
> 
> 
> Yes, and the mapping attribute should be returned as a copy.
> 
>>>> 2.) use a different syntax for updating the mapping:
>>>>
>>>>   >>> msg = msg % {'foo':'bar'}
>>>>
>>>> Note how the __mod__ (method responsible for the % operator) returns 
>>>> a new message id object.
>>>>
>>>> This is an analogy to
>>>>
>>>>   >>> "Insert %(text)s" % {'text': 'here'}
>>>>   Insert here
>>>
>>>
>>>
>>>
>>> Maybe the 'mapping' attribute should be a read-only attribute which 
>>> returns the mapping copy?
>>
>>
>> But that doesn't prevent the mapping from being changed in the
>> message id. We need to prevent the messaage id's data from being
>> mutated.
> 
> 
> Yes, which is why I proposed that whenever you make an attempt to change 
> the mapping, which in the future will only be possible using the % 
> operator, a new message id is created. That effectively makes the 
> combination of msgid, default and mapping immutable.

That's not enough, see below.

>> I suggest we use Python dict proxies, which are proxies
>> used by new-style classes to prevent modification of their
>> dictionaries.  Unfortunately, creation of these is not
>> exposed to Python code. Someone will have to whip up a small
>> C extension to make these creatable from Python.  I've proposed
>> on python-dev to make these creatable from Python, but that wouldn't
>> happen until Python 2.4 at the soonest.
> 
> 
> This sounds complicated and I don't see why we need it.

It's not complicated at all.

The good news is that it's not strictly necessary.
Recently, we changed the implementation of untrusted code
so that getattr operations by untrusted code always try to
proxy the result.  So, if a message id is given to
untrusted code and the untrusted code gets the mapping,
the mapping will be proxied by the untrusted code interpreter
and thus not mutable.  We don't even need to pre-unicodify
the keys and values, since untrusted code they can get to them
through a proxied dictionary.

The bad news is that we will have to rewrite the message ids
in C to make their attributes immutable. See below.

>>  > And the MessageID constructor should accept
>>
>>> optional 'mapping' argument and save copy of the passed mapping in 
>>> private attribute, like this:
>>>
>>> class MessageID(unicode):
>>>
>>>     def __new__(cls, ustr, domain=None, default=None, mapping={}):
>>>         self = super(MessageID, cls).__new__(cls, ustr)
>>>         self.__mapping = mapping.copy()
>>>         ...
>>
>>
>>
>> I propose to use dictproxies instead, however, we should make a copy
>> anyway and convert all of the keys and values to unicode as we make
>> the copy. Otherwise, some of the data might be mutable.
> 
> 
> I don't see the need for these dictproxies. Otherwise I agree. 
> Specifically, I have something like this in mind:
> 
> def keys_and_values_to_unicode(mapping):
>     items = [(unicode(key), unicode(value) for key, value
>              in mapping.items()]
>     return dict(items)
> 
> class MessageID(unicode):
> 
>     def __new__(cls, ustr, domain=None, default=None, mapping={}):
>         self = super(MessageID, cls).__new__(cls, ustr)
>         self._domain = domain
>         self._default = default
>         self._mapping = keys_and_values_to_unicode(mapping)
> 
>     def domain(self):
>         return self._domain
>     domain = property(domain)
> 
>     def default(self):
>         return self._default
>     default = property(default)
> 
>     def mapping(self):
>         return self._mapping.copy()
>     mapping = property(mapping)
> 
>     def __mod__(self, mapping):
>         new_mapping = self.mapping
>         new_mapping.update(keys_and_values_to_unicode(mapping))
>         return self.__class__(
>             self,
>             domain=self._domain,
>             default=self._default,
>             mapping=new_mapping
>         )
> 
> This class's instances would be immutable since their attributes can't 
> be changed. Changing the mapping causes a new isntance to be created.

Unless I say:

   foo._domain = 'myfakedomain'

or

   foo._default = 'hee hee hee'

or maybe:

   foo._mapping = {'this is': 'what you deserve ;)'}

I don't think that it is possible to implement
truly read-only attributes in Python.

Jim

-- 
Jim Fulton           mailto:jim at zope.com       Python Powered!
CTO                  (540) 361-1714            http://www.python.org
Zope Corporation     http://www.zope.com       http://www.zope.org


More information about the Zope3-dev mailing list