Source code for zope.container.ordered

##############################################################################
#
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Ordered container implementation.
"""
__docformat__ = 'restructuredtext'
import six
from persistent import Persistent
from persistent.dict import PersistentDict
from persistent.list import PersistentList
from zope.container.interfaces import IOrderedContainer
from zope.interface import implementer
from zope.container.contained import Contained, setitem, uncontained
from zope.container.contained import notifyContainerModified

@implementer(IOrderedContainer)
[docs]class OrderedContainer(Persistent, Contained): """ `OrderedContainer` maintains entries' order as added and moved. >>> oc = OrderedContainer() >>> int(IOrderedContainer.providedBy(oc)) 1 >>> len(oc) 0 """ def __init__(self): self._data = PersistentDict() self._order = PersistentList()
[docs] def keys(self): """ See `IOrderedContainer`. >>> oc = OrderedContainer() >>> oc.keys() [] >>> oc['foo'] = 'bar' >>> oc.keys() ['foo'] >>> oc['baz'] = 'quux' >>> oc.keys() ['foo', 'baz'] >>> int(len(oc._order) == len(oc._data)) 1 """ return self._order[:]
def __iter__(self): """ See `IOrderedContainer`. >>> oc = OrderedContainer() >>> oc.keys() [] >>> oc['foo'] = 'bar' >>> oc['baz'] = 'quux' >>> [i for i in oc] ['foo', 'baz'] >>> int(len(oc._order) == len(oc._data)) 1 """ return iter(self.keys()) def __getitem__(self, key): """ See `IOrderedContainer`. >>> oc = OrderedContainer() >>> oc['foo'] = 'bar' >>> oc['foo'] 'bar' """ return self._data[key]
[docs] def get(self, key, default=None): """ See `IOrderedContainer`. >>> oc = OrderedContainer() >>> oc['foo'] = 'bar' >>> oc.get('foo') 'bar' >>> oc.get('funky', 'No chance, dude.') 'No chance, dude.' """ return self._data.get(key, default)
[docs] def values(self): """ See `IOrderedContainer`. >>> oc = OrderedContainer() >>> oc.keys() [] >>> oc['foo'] = 'bar' >>> oc.values() ['bar'] >>> oc['baz'] = 'quux' >>> oc.values() ['bar', 'quux'] >>> int(len(oc._order) == len(oc._data)) 1 """ return [self._data[i] for i in self._order]
def __len__(self): """ See `IOrderedContainer`. >>> oc = OrderedContainer() >>> int(len(oc) == 0) 1 >>> oc['foo'] = 'bar' >>> int(len(oc) == 1) 1 """ return len(self._data)
[docs] def items(self): """ See `IOrderedContainer`. >>> oc = OrderedContainer() >>> oc.keys() [] >>> oc['foo'] = 'bar' >>> oc.items() [('foo', 'bar')] >>> oc['baz'] = 'quux' >>> oc.items() [('foo', 'bar'), ('baz', 'quux')] >>> int(len(oc._order) == len(oc._data)) 1 """ return [(i, self._data[i]) for i in self._order]
def __contains__(self, key): """ See `IOrderedContainer`. >>> oc = OrderedContainer() >>> oc['foo'] = 'bar' >>> int('foo' in oc) 1 >>> int('quux' in oc) 0 """ return key in self._data has_key = __contains__ def __setitem__(self, key, object): """ See `IOrderedContainer`. >>> oc = OrderedContainer() >>> oc.keys() [] >>> oc['foo'] = 'bar' >>> oc._order ['foo'] >>> oc['baz'] = 'quux' >>> oc._order ['foo', 'baz'] >>> int(len(oc._order) == len(oc._data)) 1 >>> oc['foo'] = 'baz' Traceback (most recent call last): ... KeyError: u'foo' >>> oc._order ['foo', 'baz'] """ existed = key in self._data bad = False if isinstance(key, six.string_types): try: key.decode() except AttributeError: # Py3 str cannot decode. pass except UnicodeError: bad = True else: bad = True if bad: raise TypeError("'%s' is invalid, the key must be an " "ascii or unicode string" % key) if len(key) == 0: raise ValueError("The key cannot be an empty string") # We have to first update the order, so that the item is available, # otherwise most API functions will lie about their available values # when an event subscriber tries to do something with the container. if not existed: self._order.append(key) # This function creates a lot of events that other code listens to. try: setitem(self, self._data.__setitem__, key, object) except Exception: if not existed: self._order.remove(key) raise return key def __delitem__(self, key): """ See `IOrderedContainer`. >>> oc = OrderedContainer() >>> oc.keys() [] >>> oc['foo'] = 'bar' >>> oc['baz'] = 'quux' >>> oc['zork'] = 'grue' >>> oc.items() [('foo', 'bar'), ('baz', 'quux'), ('zork', 'grue')] >>> int(len(oc._order) == len(oc._data)) 1 >>> del oc['baz'] >>> oc.items() [('foo', 'bar'), ('zork', 'grue')] >>> int(len(oc._order) == len(oc._data)) 1 """ uncontained(self._data[key], self, key) del self._data[key] self._order.remove(key)
[docs] def updateOrder(self, order): """ See `IOrderedContainer`. >>> oc = OrderedContainer() >>> oc['foo'] = 'bar' >>> oc['baz'] = 'quux' >>> oc['zork'] = 'grue' >>> oc.keys() ['foo', 'baz', 'zork'] >>> oc.updateOrder(['baz', 'foo', 'zork']) >>> oc.keys() ['baz', 'foo', 'zork'] >>> oc.updateOrder(['baz', 'zork', 'foo']) >>> oc.keys() ['baz', 'zork', 'foo'] >>> oc.updateOrder(['baz', 'zork', 'foo']) >>> oc.keys() ['baz', 'zork', 'foo'] >>> oc.updateOrder(('zork', 'foo', 'baz')) >>> oc.keys() ['zork', 'foo', 'baz'] >>> oc.updateOrder(['baz', 'zork']) Traceback (most recent call last): ... ValueError: Incompatible key set. >>> oc.updateOrder(['foo', 'bar', 'baz', 'quux']) Traceback (most recent call last): ... ValueError: Incompatible key set. >>> oc.updateOrder(1) Traceback (most recent call last): ... TypeError: order must be a tuple or a list. >>> oc.updateOrder('bar') Traceback (most recent call last): ... TypeError: order must be a tuple or a list. >>> oc.updateOrder(['baz', 'zork', 'quux']) Traceback (most recent call last): ... ValueError: Incompatible key set. >>> del oc['baz'] >>> del oc['zork'] >>> del oc['foo'] >>> len(oc) 0 """ if not isinstance(order, list) and \ not isinstance(order, tuple): raise TypeError('order must be a tuple or a list.') if len(order) != len(self._order): raise ValueError("Incompatible key set.") was_dict = {} will_be_dict = {} new_order = PersistentList() for i in range(len(order)): was_dict[self._order[i]] = 1 will_be_dict[order[i]] = 1 new_order.append(order[i]) if will_be_dict != was_dict: raise ValueError("Incompatible key set.") self._order = new_order notifyContainerModified(self)