blob: cd39a6ea166a61e90be36cdd64a691fa056f673c [file] [log] [blame]
# Copyright: 2005-2009 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# Author(s): Brian Harring (ferringb@gentoo.org)
__all__ = ["Mapping", "MutableMapping", "UserDict", "ProtectedDict",
"LazyLoad", "slot_dict_class"]
import sys
import weakref
class Mapping(object):
"""
In python-3.0, the UserDict.DictMixin class has been replaced by
Mapping and MutableMapping from the collections module, but 2to3
doesn't currently account for this change:
http://bugs.python.org/issue2876
As a workaround for the above issue, use this class as a substitute
for UserDict.DictMixin so that code converted via 2to3 will run.
"""
__slots__ = ()
def __iter__(self):
return iter(self.keys())
def keys(self):
return list(self.__iter__())
def __contains__(self, key):
try:
value = self[key]
except KeyError:
return False
return True
def iteritems(self):
for k in self:
yield (k, self[k])
def iterkeys(self):
return self.__iter__()
def itervalues(self):
for _, v in self.items():
yield v
def values(self):
return [v for _, v in self.iteritems()]
def items(self):
return list(self.iteritems())
def get(self, key, default=None):
try:
return self[key]
except KeyError:
return default
def __repr__(self):
return repr(dict(self.items()))
def __len__(self):
return len(list(self))
if sys.hexversion >= 0x3000000:
items = iteritems
keys = __iter__
values = itervalues
class MutableMapping(Mapping):
"""
A mutable vesion of the Mapping class.
"""
__slots__ = ()
def clear(self):
for key in list(self):
del self[key]
def setdefault(self, key, default=None):
try:
return self[key]
except KeyError:
self[key] = default
return default
def pop(self, key, *args):
if len(args) > 1:
raise TypeError("pop expected at most 2 arguments, got " + \
repr(1 + len(args)))
try:
value = self[key]
except KeyError:
if args:
return args[0]
raise
del self[key]
return value
def popitem(self):
try:
k, v = next(iter(self.items()))
except StopIteration:
raise KeyError('container is empty')
del self[k]
return (k, v)
def update(self, *args, **kwargs):
if len(args) > 1:
raise TypeError(
"expected at most 1 positional argument, got " + \
repr(len(args)))
other = None
if args:
other = args[0]
if other is None:
pass
elif hasattr(other, 'iteritems'):
# Use getattr to avoid interference from 2to3.
for k, v in getattr(other, 'iteritems')():
self[k] = v
elif hasattr(other, 'items'):
# Use getattr to avoid interference from 2to3.
for k, v in getattr(other, 'items')():
self[k] = v
elif hasattr(other, 'keys'):
for k in other.keys():
self[k] = other[k]
else:
for k, v in other:
self[k] = v
if kwargs:
self.update(kwargs)
class UserDict(MutableMapping):
"""
Use this class as a substitute for UserDict.UserDict so that
code converted via 2to3 will run:
http://bugs.python.org/issue2876
"""
__slots__ = ('data',)
def __init__(self, *args, **kwargs):
self.data = {}
if len(args) > 1:
raise TypeError(
"expected at most 1 positional argument, got " + \
repr(len(args)))
if args:
self.update(args[0])
if kwargs:
self.update(kwargs)
def __repr__(self):
return repr(self.data)
def __contains__(self, key):
return key in self.data
def __iter__(self):
return iter(self.data)
def __len__(self):
return len(self.data)
def __getitem__(self, key):
return self.data[key]
def __setitem__(self, key, item):
self.data[key] = item
def __delitem__(self, key):
del self.data[key]
def clear(self):
self.data.clear()
if sys.hexversion >= 0x3000000:
keys = __iter__
class OrderedDict(UserDict):
__slots__ = ('_order',)
def __init__(self, *args, **kwargs):
self._order = []
UserDict.__init__(self, *args, **kwargs)
def __iter__(self):
return iter(self._order)
def __setitem__(self, key, item):
new_key = key not in self
UserDict.__setitem__(self, key, item)
if new_key:
self._order.append(key)
def __delitem__(self, key):
UserDict.__delitem__(self, key)
self._order.remove(key)
def clear(self):
UserDict.clear(self)
del self._order[:]
if sys.hexversion >= 0x3000000:
keys = __iter__
class ProtectedDict(MutableMapping):
"""
given an initial dict, this wraps that dict storing changes in a secondary dict, protecting
the underlying dict from changes
"""
__slots__=("orig","new","blacklist")
def __init__(self, orig):
self.orig = orig
self.new = {}
self.blacklist = {}
def __setitem__(self, key, val):
self.new[key] = val
if key in self.blacklist:
del self.blacklist[key]
def __getitem__(self, key):
if key in self.new:
return self.new[key]
if key in self.blacklist:
raise KeyError(key)
return self.orig[key]
def __delitem__(self, key):
if key in self.new:
del self.new[key]
elif key in self.orig:
if key not in self.blacklist:
self.blacklist[key] = True
return
raise KeyError(key)
def __iter__(self):
for k in self.new:
yield k
for k in self.orig:
if k not in self.blacklist and k not in self.new:
yield k
def __contains__(self, key):
return key in self.new or (key not in self.blacklist and key in self.orig)
if sys.hexversion >= 0x3000000:
keys = __iter__
class LazyLoad(Mapping):
"""
Lazy loading of values for a dict
"""
__slots__=("pull", "d")
def __init__(self, pull_items_func, initial_items=[]):
self.d = {}
for k, v in initial_items:
self.d[k] = v
self.pull = pull_items_func
def __getitem__(self, key):
if key in self.d:
return self.d[key]
elif self.pull != None:
self.d.update(self.pull())
self.pull = None
return self.d[key]
def __iter__(self):
if self.pull is not None:
self.d.update(self.pull())
self.pull = None
return iter(self.d)
def __contains__(self, key):
if key in self.d:
return True
elif self.pull != None:
self.d.update(self.pull())
self.pull = None
return key in self.d
if sys.hexversion >= 0x3000000:
keys = __iter__
_slot_dict_classes = weakref.WeakValueDictionary()
def slot_dict_class(keys, prefix="_val_"):
"""
Generates mapping classes that behave similar to a dict but store values
as object attributes that are allocated via __slots__. Instances of these
objects have a smaller memory footprint than a normal dict object.
@param keys: Fixed set of allowed keys
@type keys: Iterable
@param prefix: a prefix to use when mapping
attribute names from keys
@type prefix: String
@rtype: SlotDict
@return: A class that constructs SlotDict instances
having the specified keys.
"""
if isinstance(keys, frozenset):
keys_set = keys
else:
keys_set = frozenset(keys)
v = _slot_dict_classes.get((keys_set, prefix))
if v is None:
class SlotDict(object):
allowed_keys = keys_set
_prefix = prefix
__slots__ = ("__weakref__",) + \
tuple(prefix + k for k in allowed_keys)
def __init__(self, *args, **kwargs):
if len(args) > 1:
raise TypeError(
"expected at most 1 positional argument, got " + \
repr(len(args)))
if args:
self.update(args[0])
if kwargs:
self.update(kwargs)
def __iter__(self):
for k, v in self.iteritems():
yield k
def __len__(self):
l = 0
for i in self.iteritems():
l += 1
return l
def keys(self):
return list(self)
def iteritems(self):
prefix = self._prefix
for k in self.allowed_keys:
try:
yield (k, getattr(self, prefix + k))
except AttributeError:
pass
def items(self):
return list(self.iteritems())
def itervalues(self):
for k, v in self.iteritems():
yield v
def values(self):
return list(self.itervalues())
def __delitem__(self, k):
try:
delattr(self, self._prefix + k)
except AttributeError:
raise KeyError(k)
def __setitem__(self, k, v):
setattr(self, self._prefix + k, v)
def setdefault(self, key, default=None):
try:
return self[key]
except KeyError:
self[key] = default
return default
def update(self, *args, **kwargs):
if len(args) > 1:
raise TypeError(
"expected at most 1 positional argument, got " + \
repr(len(args)))
other = None
if args:
other = args[0]
if other is None:
pass
elif hasattr(other, 'iteritems'):
# Use getattr to avoid interference from 2to3.
for k, v in getattr(other, 'iteritems')():
self[k] = v
elif hasattr(other, 'items'):
# Use getattr to avoid interference from 2to3.
for k, v in getattr(other, 'items')():
self[k] = v
elif hasattr(other, 'keys'):
for k in other.keys():
self[k] = other[k]
else:
for k, v in other:
self[k] = v
if kwargs:
self.update(kwargs)
def __getitem__(self, k):
try:
return getattr(self, self._prefix + k)
except AttributeError:
raise KeyError(k)
def get(self, key, default=None):
try:
return self[key]
except KeyError:
return default
def __contains__(self, k):
return hasattr(self, self._prefix + k)
def pop(self, key, *args):
if len(args) > 1:
raise TypeError(
"pop expected at most 2 arguments, got " + \
repr(1 + len(args)))
try:
value = self[key]
except KeyError:
if args:
return args[0]
raise
del self[key]
return value
def popitem(self):
try:
k, v = self.iteritems().next()
except StopIteration:
raise KeyError('container is empty')
del self[k]
return (k, v)
def copy(self):
c = self.__class__()
c.update(self)
return c
def clear(self):
for k in self.allowed_keys:
try:
delattr(self, self._prefix + k)
except AttributeError:
pass
def __str__(self):
return str(dict(self.iteritems()))
def __repr__(self):
return repr(dict(self.iteritems()))
if sys.hexversion >= 0x3000000:
items = iteritems
keys = __iter__
values = itervalues
v = SlotDict
_slot_dict_classes[v.allowed_keys] = v
return v