"""A more or less complete user-defined wrapper around dictionary objects."""



class UserDict:

    def __init__(self, dict=None, **kwargs):

        self.data = {}

        if dict is not None:

            self.update(dict)

        if len(kwargs):

            self.update(kwargs)

    def __repr__(self): return repr(self.data)

    def __cmp__(self, dict):

        if isinstance(dict, UserDict):

            return cmp(self.data, dict.data)

        else:

            return cmp(self.data, dict)

    def __len__(self): return len(self.data)

    def __getitem__(self, key):

        if key in self.data:

            return self.data[key]

        if hasattr(self.__class__, "__missing__"):

            return self.__class__.__missing__(self, key)

        raise KeyError(key)

    def __setitem__(self, key, item): self.data[key] = item

    def __delitem__(self, key): del self.data[key]

    def clear(self): self.data.clear()

    def copy(self):

        if self.__class__ is UserDict:

            return UserDict(self.data.copy())

        import copy

        data = self.data

        try:

            self.data = {}

            c = copy.copy(self)

        finally:

            self.data = data

        c.update(self)

        return c

    def keys(self): return self.data.keys()

    def items(self): return self.data.items()

    def iteritems(self): return self.data.iteritems()

    def iterkeys(self): return self.data.iterkeys()

    def itervalues(self): return self.data.itervalues()

    def values(self): return self.data.values()

    def has_key(self, key): return key in self.data

    def update(self, dict=None, **kwargs):

        if dict is None:

            pass

        elif isinstance(dict, UserDict):

            self.data.update(dict.data)

        elif isinstance(dict, type({})) or not hasattr(dict, 'items'):

            self.data.update(dict)

        else:

            for k, v in dict.items():

                self[k] = v

        if len(kwargs):

            self.data.update(kwargs)

    def get(self, key, failobj=None):

        if key not in self:

            return failobj

        return self[key]

    def setdefault(self, key, failobj=None):

        if key not in self:

            self[key] = failobj

        return self[key]

    def pop(self, key, *args):

        return self.data.pop(key, *args)

    def popitem(self):

        return self.data.popitem()

    def __contains__(self, key):

        return key in self.data

    @classmethod

    def fromkeys(cls, iterable, value=None):

        d = cls()

        for key in iterable:

            d[key] = value

        return d



class IterableUserDict(UserDict):

    def __iter__(self):

        return iter(self.data)



import _abcoll

_abcoll.MutableMapping.register(IterableUserDict)





class DictMixin:

    # Mixin defining all dictionary methods for classes that already have

    # a minimum dictionary interface including getitem, setitem, delitem,

    # and keys. Without knowledge of the subclass constructor, the mixin

    # does not define __init__() or copy().  In addition to the four base

    # methods, progressively more efficiency comes with defining

    # __contains__(), __iter__(), and iteritems().



    # second level definitions support higher levels

    def __iter__(self):

        for k in self.keys():

            yield k

    def has_key(self, key):

        try:

            value = self[key]

        except KeyError:

            return False

        return True

    def __contains__(self, key):

        return self.has_key(key)



    # third level takes advantage of second level definitions

    def iteritems(self):

        for k in self:

            yield (k, self[k])

    def iterkeys(self):

        return self.__iter__()



    # fourth level uses definitions from lower levels

    def itervalues(self):

        for _, v in self.iteritems():

            yield v

    def values(self):

        return [v for _, v in self.iteritems()]

    def items(self):

        return list(self.iteritems())

    def clear(self):

        for key in self.keys():

            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 = self.iteritems().next()

        except StopIteration:

            raise KeyError, 'container is empty'

        del self[k]

        return (k, v)

    def update(self, other=None, **kwargs):

        # Make progressively weaker assumptions about "other"

        if other is None:

            pass

        elif hasattr(other, 'iteritems'):  # iteritems saves memory and lookups

            for k, v in other.iteritems():

                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 get(self, key, default=None):

        try:

            return self[key]

        except KeyError:

            return default

    def __repr__(self):

        return repr(dict(self.iteritems()))

    def __cmp__(self, other):

        if other is None:

            return 1

        if isinstance(other, DictMixin):

            other = dict(other.iteritems())

        return cmp(dict(self.iteritems()), other)

    def __len__(self):

        return len(self.keys())

