Lichen

lib/UserDict.py

90:c7ddfc4525da
2016-10-08 Paul Boddie Added some support for eliminating accessor class types where the provided attributes are invoked and are unbound methods. This uses a more sophisticated method involving usage observations that incorporate invocation information, permitting classes as accessors if paths through the code support them, even if other paths require instances as accessors to invoke methods.
     1 """A more or less complete user-defined wrapper around dictionary objects."""     2      3 class UserDict:     4     def __init__(self, dict=None, **kwargs):     5         self.data = {}     6         if dict is not None:     7             self.update(dict)     8         if len(kwargs):     9             self.update(kwargs)    10     def __repr__(self): return repr(self.data)    11     def __cmp__(self, dict):    12         if isinstance(dict, UserDict):    13             return cmp(self.data, dict.data)    14         else:    15             return cmp(self.data, dict)    16     __hash__ = None # Avoid Py3k warning    17     def __len__(self): return len(self.data)    18     def __getitem__(self, key):    19         if key in self.data:    20             return self.data[key]    21         if hasattr(self.__class__, "__missing__"):    22             return self.__class__.__missing__(self, key)    23         raise KeyError(key)    24     def __setitem__(self, key, item): self.data[key] = item    25     def __delitem__(self, key): del self.data[key]    26     def clear(self): self.data.clear()    27     def copy(self):    28         if self.__class__ is UserDict:    29             return UserDict(self.data.copy())    30         import copy    31         data = self.data    32         try:    33             self.data = {}    34             c = copy.copy(self)    35         finally:    36             self.data = data    37         c.update(self)    38         return c    39     def keys(self): return self.data.keys()    40     def items(self): return self.data.items()    41     def iteritems(self): return self.data.iteritems()    42     def iterkeys(self): return self.data.iterkeys()    43     def itervalues(self): return self.data.itervalues()    44     def values(self): return self.data.values()    45     def has_key(self, key): return key in self.data    46     def update(self, dict=None, **kwargs):    47         if dict is None:    48             pass    49         elif isinstance(dict, UserDict):    50             self.data.update(dict.data)    51         elif isinstance(dict, type({})) or not hasattr(dict, 'items'):    52             self.data.update(dict)    53         else:    54             for k, v in dict.items():    55                 self[k] = v    56         if len(kwargs):    57             self.data.update(kwargs)    58     def get(self, key, failobj=None):    59         if key not in self:    60             return failobj    61         return self[key]    62     def setdefault(self, key, failobj=None):    63         if key not in self:    64             self[key] = failobj    65         return self[key]    66     def pop(self, key, *args):    67         return self.data.pop(key, *args)    68     def popitem(self):    69         return self.data.popitem()    70     def __contains__(self, key):    71         return key in self.data    72     @classmethod    73     def fromkeys(cls, iterable, value=None):    74         d = cls()    75         for key in iterable:    76             d[key] = value    77         return d    78     79 class IterableUserDict(UserDict):    80     def __iter__(self):    81         return iter(self.data)    82     83 class DictMixin:    84     # Mixin defining all dictionary methods for classes that already have    85     # a minimum dictionary interface including getitem, setitem, delitem,    86     # and keys. Without knowledge of the subclass constructor, the mixin    87     # does not define __init__() or copy().  In addition to the four base    88     # methods, progressively more efficiency comes with defining    89     # __contains__(), __iter__(), and iteritems().    90     91     # second level definitions support higher levels    92     def __iter__(self):    93         for k in self.keys():    94             yield k    95     def has_key(self, key):    96         try:    97             self[key]    98         except KeyError:    99             return False   100         return True   101     def __contains__(self, key):   102         return self.has_key(key)   103    104     # third level takes advantage of second level definitions   105     def iteritems(self):   106         for k in self:   107             yield (k, self[k])   108     def iterkeys(self):   109         return self.__iter__()   110    111     # fourth level uses definitions from lower levels   112     def itervalues(self):   113         for _, v in self.iteritems():   114             yield v   115     def values(self):   116         return [v for _, v in self.iteritems()]   117     def items(self):   118         return list(self.iteritems())   119     def clear(self):   120         for key in self.keys():   121             del self[key]   122     def setdefault(self, key, default=None):   123         try:   124             return self[key]   125         except KeyError:   126             self[key] = default   127         return default   128     def pop(self, key, *args):   129         if len(args) > 1:   130             raise TypeError, "pop expected at most 2 arguments, got "\   131                               + repr(1 + len(args))   132         try:   133             value = self[key]   134         except KeyError:   135             if args:   136                 return args[0]   137             raise   138         del self[key]   139         return value   140     def popitem(self):   141         try:   142             k, v = self.iteritems().next()   143         except StopIteration:   144             raise KeyError, 'container is empty'   145         del self[k]   146         return (k, v)   147     def update(self, other=None, **kwargs):   148         # Make progressively weaker assumptions about "other"   149         if other is None:   150             pass   151         elif hasattr(other, 'iteritems'):  # iteritems saves memory and lookups   152             for k, v in other.iteritems():   153                 self[k] = v   154         elif hasattr(other, 'keys'):   155             for k in other.keys():   156                 self[k] = other[k]   157         else:   158             for k, v in other:   159                 self[k] = v   160         if kwargs:   161             self.update(kwargs)   162     def get(self, key, default=None):   163         try:   164             return self[key]   165         except KeyError:   166             return default   167     def __repr__(self):   168         return repr(dict(self.iteritems()))   169     def __cmp__(self, other):   170         if other is None:   171             return 1   172         if isinstance(other, DictMixin):   173             other = dict(other.iteritems())   174         return cmp(dict(self.iteritems()), other)   175     def __len__(self):   176         return len(self.keys())