Eli Bendersky | e0735d5 | 2011-09-08 20:12:44 +0300 | [diff] [blame^] | 1 | def recursion_lock(retval, lock_name = "__recursion_lock__"): |
| 2 | def decorator(func): |
| 3 | def wrapper(self, *args, **kw): |
| 4 | if getattr(self, lock_name, False): |
| 5 | return retval |
| 6 | setattr(self, lock_name, True) |
| 7 | try: |
| 8 | return func(self, *args, **kw) |
| 9 | finally: |
| 10 | setattr(self, lock_name, False) |
| 11 | wrapper.__name__ = func.__name__ |
| 12 | return wrapper |
| 13 | return decorator |
| 14 | |
| 15 | class Container(object): |
| 16 | """ |
| 17 | A generic container of attributes |
| 18 | """ |
| 19 | __slots__ = ["__dict__", "__attrs__"] |
| 20 | def __init__(self, **kw): |
| 21 | self.__dict__.update(kw) |
| 22 | object.__setattr__(self, "__attrs__", kw.keys()) |
| 23 | |
| 24 | def __eq__(self, other): |
| 25 | try: |
| 26 | return self.__dict__ == other.__dict__ |
| 27 | except AttributeError: |
| 28 | return False |
| 29 | def __ne__(self, other): |
| 30 | return not (self == other) |
| 31 | |
| 32 | def __delattr__(self, name): |
| 33 | object.__delattr__(self, name) |
| 34 | self.__attrs__.remove(name) |
| 35 | def __setattr__(self, name, value): |
| 36 | d = self.__dict__ |
| 37 | if name not in d: |
| 38 | self.__attrs__.append(name) |
| 39 | d[name] = value |
| 40 | def __getitem__(self, name): |
| 41 | return self.__dict__[name] |
| 42 | def __delitem__(self, name): |
| 43 | self.__delattr__(name) |
| 44 | def __setitem__(self, name, value): |
| 45 | self.__setattr__(name, value) |
| 46 | def __update__(self, obj): |
| 47 | for name in obj.__attrs__: |
| 48 | self[name] = obj[name] |
| 49 | def __copy__(self): |
| 50 | new = self.__class__() |
| 51 | new.__attrs__ = self.__attrs__[:] |
| 52 | new.__dict__ = self.__dict__.copy() |
| 53 | return new |
| 54 | |
| 55 | @recursion_lock("<...>") |
| 56 | def __repr__(self): |
| 57 | attrs = sorted("%s = %r" % (k, v) |
| 58 | for k, v in self.__dict__.iteritems() |
| 59 | if not k.startswith("_")) |
| 60 | return "%s(%s)" % (self.__class__.__name__, ", ".join(attrs)) |
| 61 | def __str__(self): |
| 62 | return self.__pretty_str__() |
| 63 | @recursion_lock("<...>") |
| 64 | def __pretty_str__(self, nesting = 1, indentation = " "): |
| 65 | attrs = [] |
| 66 | ind = indentation * nesting |
| 67 | for k in self.__attrs__: |
| 68 | v = self.__dict__[k] |
| 69 | if not k.startswith("_"): |
| 70 | text = [ind, k, " = "] |
| 71 | if hasattr(v, "__pretty_str__"): |
| 72 | text.append(v.__pretty_str__(nesting + 1, indentation)) |
| 73 | else: |
| 74 | text.append(repr(v)) |
| 75 | attrs.append("".join(text)) |
| 76 | if not attrs: |
| 77 | return "%s()" % (self.__class__.__name__,) |
| 78 | attrs.insert(0, self.__class__.__name__ + ":") |
| 79 | return "\n".join(attrs) |
| 80 | |
| 81 | class FlagsContainer(Container): |
| 82 | """ |
| 83 | A container providing pretty-printing for flags. Only set flags are |
| 84 | displayed. |
| 85 | """ |
| 86 | def __pretty_str__(self, nesting = 1, indentation = " "): |
| 87 | attrs = [] |
| 88 | ind = indentation * nesting |
| 89 | for k in self.__attrs__: |
| 90 | v = self.__dict__[k] |
| 91 | if not k.startswith("_") and v: |
| 92 | attrs.append(ind + k) |
| 93 | if not attrs: |
| 94 | return "%s()" % (self.__class__.__name__,) |
| 95 | attrs.insert(0, self.__class__.__name__+ ":") |
| 96 | return "\n".join(attrs) |
| 97 | |
| 98 | class ListContainer(list): |
| 99 | """ |
| 100 | A container for lists |
| 101 | """ |
| 102 | __slots__ = ["__recursion_lock__"] |
| 103 | def __str__(self): |
| 104 | return self.__pretty_str__() |
| 105 | @recursion_lock("[...]") |
| 106 | def __pretty_str__(self, nesting = 1, indentation = " "): |
| 107 | if not self: |
| 108 | return "[]" |
| 109 | ind = indentation * nesting |
| 110 | lines = ["["] |
| 111 | for elem in self: |
| 112 | lines.append("\n") |
| 113 | lines.append(ind) |
| 114 | if hasattr(elem, "__pretty_str__"): |
| 115 | lines.append(elem.__pretty_str__(nesting + 1, indentation)) |
| 116 | else: |
| 117 | lines.append(repr(elem)) |
| 118 | lines.append("\n") |
| 119 | lines.append(indentation * (nesting - 1)) |
| 120 | lines.append("]") |
| 121 | return "".join(lines) |
| 122 | |
| 123 | class AttrDict(object): |
| 124 | """ |
| 125 | A dictionary that can be accessed both using indexing and attributes, |
| 126 | i.e., |
| 127 | x = AttrDict() |
| 128 | x.foo = 5 |
| 129 | print x["foo"] |
| 130 | """ |
| 131 | __slots__ = ["__dict__"] |
| 132 | def __init__(self, **kw): |
| 133 | self.__dict__ = kw |
| 134 | def __contains__(self, key): |
| 135 | return key in self.__dict__ |
| 136 | def __nonzero__(self): |
| 137 | return bool(self.__dict__) |
| 138 | def __repr__(self): |
| 139 | return repr(self.__dict__) |
| 140 | def __str__(self): |
| 141 | return self.__pretty_str__() |
| 142 | def __pretty_str__(self, nesting = 1, indentation = " "): |
| 143 | if not self: |
| 144 | return "{}" |
| 145 | text = ["{\n"] |
| 146 | ind = nesting * indentation |
| 147 | for k in sorted(self.__dict__.keys()): |
| 148 | v = self.__dict__[k] |
| 149 | text.append(ind) |
| 150 | text.append(repr(k)) |
| 151 | text.append(" : ") |
| 152 | if hasattr(v, "__pretty_str__"): |
| 153 | try: |
| 154 | text.append(v.__pretty_str__(nesting+1, indentation)) |
| 155 | except Exception: |
| 156 | text.append(repr(v)) |
| 157 | else: |
| 158 | text.append(repr(v)) |
| 159 | text.append("\n") |
| 160 | text.append((nesting-1) * indentation) |
| 161 | text.append("}") |
| 162 | return "".join(text) |
| 163 | def __delitem__(self, key): |
| 164 | del self.__dict__[key] |
| 165 | def __getitem__(self, key): |
| 166 | return self.__dict__[key] |
| 167 | def __setitem__(self, key, value): |
| 168 | self.__dict__[key] = value |
| 169 | def __copy__(self): |
| 170 | new = self.__class__() |
| 171 | new.__dict__ = self.__dict__.copy() |
| 172 | return new |
| 173 | def __update__(self, other): |
| 174 | if isinstance(other, dict): |
| 175 | self.__dict__.update(other) |
| 176 | else: |
| 177 | self.__dict__.update(other.__dict__) |
| 178 | |
| 179 | class LazyContainer(object): |
| 180 | __slots__ = ["subcon", "stream", "pos", "context", "_value"] |
| 181 | def __init__(self, subcon, stream, pos, context): |
| 182 | self.subcon = subcon |
| 183 | self.stream = stream |
| 184 | self.pos = pos |
| 185 | self.context = context |
| 186 | self._value = NotImplemented |
| 187 | def __eq__(self, other): |
| 188 | try: |
| 189 | return self._value == other._value |
| 190 | except AttributeError: |
| 191 | return False |
| 192 | def __ne__(self, other): |
| 193 | return not (self == other) |
| 194 | def __str__(self): |
| 195 | return self.__pretty_str__() |
| 196 | def __pretty_str__(self, nesting = 1, indentation = " "): |
| 197 | if self._value is NotImplemented: |
| 198 | text = "<unread>" |
| 199 | elif hasattr(self._value, "__pretty_str__"): |
| 200 | text = self._value.__pretty_str__(nesting, indentation) |
| 201 | else: |
| 202 | text = repr(self._value) |
| 203 | return "%s: %s" % (self.__class__.__name__, text) |
| 204 | def read(self): |
| 205 | self.stream.seek(self.pos) |
| 206 | return self.subcon._parse(self.stream, self.context) |
| 207 | def dispose(self): |
| 208 | self.subcon = None |
| 209 | self.stream = None |
| 210 | self.context = None |
| 211 | self.pos = None |
| 212 | def _get_value(self): |
| 213 | if self._value is NotImplemented: |
| 214 | self._value = self.read() |
| 215 | return self._value |
| 216 | value = property(_get_value) |
| 217 | has_value = property(lambda self: self._value is not NotImplemented) |
| 218 | |
| 219 | |
| 220 | |
| 221 | |
| 222 | |
| 223 | |
| 224 | |
| 225 | |
| 226 | |
| 227 | |
| 228 | |
| 229 | |
| 230 | |
| 231 | |
| 232 | |
| 233 | |
| 234 | |
| 235 | |
| 236 | |
| 237 | |
| 238 | |
| 239 | |
| 240 | |
| 241 | |
| 242 | |
| 243 | |
| 244 | |
| 245 | |
| 246 | |
| 247 | |
| 248 | |
| 249 | |
| 250 | |
| 251 | |
| 252 | |
| 253 | |
| 254 | |
| 255 | |
| 256 | |
| 257 | |
| 258 | |