blob: 2f89b2dc400ade5389a1a08e48bfd3ff990f6942 [file] [log] [blame]
Eli Benderskybd1a09f2012-01-26 06:49:19 +02001"""
2Various containers.
3"""
4
5from collections import MutableMapping
6from pprint import pformat
7
Eli Benderskye0735d52011-09-08 20:12:44 +03008def recursion_lock(retval, lock_name = "__recursion_lock__"):
9 def decorator(func):
10 def wrapper(self, *args, **kw):
11 if getattr(self, lock_name, False):
12 return retval
13 setattr(self, lock_name, True)
14 try:
15 return func(self, *args, **kw)
16 finally:
17 setattr(self, lock_name, False)
18 wrapper.__name__ = func.__name__
19 return wrapper
20 return decorator
21
Eli Benderskybd1a09f2012-01-26 06:49:19 +020022class Container(MutableMapping):
Eli Benderskye0735d52011-09-08 20:12:44 +030023 """
Eli Benderskybd1a09f2012-01-26 06:49:19 +020024 A generic container of attributes.
25
26 Containers are the common way to express parsed data.
Eli Benderskye0735d52011-09-08 20:12:44 +030027 """
Eli Benderskybd1a09f2012-01-26 06:49:19 +020028
Eli Benderskye0735d52011-09-08 20:12:44 +030029 def __init__(self, **kw):
Eli Benderskybd1a09f2012-01-26 06:49:19 +020030 self.__dict__ = kw
31
32 # The core dictionary interface.
33
34 def __getitem__(self, name):
35 return self.__dict__[name]
36
37 def __delitem__(self, name):
38 del self.__dict__[name]
39
40 def __setitem__(self, name, value):
41 self.__dict__[name] = value
42
43 def keys(self):
44 return self.__dict__.keys()
45
46 def __len__(self):
47 return len(self.__dict__.keys())
48
49 # Extended dictionary interface.
50
51 def update(self, other):
52 self.__dict__.update(other)
53
54 __update__ = update
55
56 def __contains__(self, value):
57 return value in self.__dict__
58
59 # Rich comparisons.
60
Eli Benderskye0735d52011-09-08 20:12:44 +030061 def __eq__(self, other):
62 try:
63 return self.__dict__ == other.__dict__
64 except AttributeError:
65 return False
Eli Benderskybd1a09f2012-01-26 06:49:19 +020066
Eli Benderskye0735d52011-09-08 20:12:44 +030067 def __ne__(self, other):
Eli Benderskybd1a09f2012-01-26 06:49:19 +020068 return not self == other
69
70 # Copy interface.
71
72 def copy(self):
73 return self.__class__(**self.__dict__)
74
75 __copy__ = copy
76
77 # Iterator interface.
78
79 def __iter__(self):
80 return iter(self.__dict__)
81
Eli Benderskye0735d52011-09-08 20:12:44 +030082 def __repr__(self):
Eli Benderskybd1a09f2012-01-26 06:49:19 +020083 return "%s(%s)" % (self.__class__.__name__, repr(self.__dict__))
84
Eli Benderskye0735d52011-09-08 20:12:44 +030085 def __str__(self):
Eli Benderskybd1a09f2012-01-26 06:49:19 +020086 return "%s(%s)" % (self.__class__.__name__, str(self.__dict__))
Eli Benderskye0735d52011-09-08 20:12:44 +030087
88class FlagsContainer(Container):
89 """
Eli Benderskybd1a09f2012-01-26 06:49:19 +020090 A container providing pretty-printing for flags.
91
92 Only set flags are displayed.
Eli Benderskye0735d52011-09-08 20:12:44 +030093 """
Eli Benderskybd1a09f2012-01-26 06:49:19 +020094
95 @recursion_lock("<...>")
96 def __str__(self):
97 d = dict((k, self[k]) for k in self
98 if self[k] and not k.startswith("_"))
99 return "%s(%s)" % (self.__class__.__name__, pformat(d))
Eli Benderskye0735d52011-09-08 20:12:44 +0300100
101class ListContainer(list):
102 """
Eli Benderskybd1a09f2012-01-26 06:49:19 +0200103 A container for lists.
Eli Benderskye0735d52011-09-08 20:12:44 +0300104 """
Eli Benderskye0735d52011-09-08 20:12:44 +0300105
Eli Benderskybd1a09f2012-01-26 06:49:19 +0200106 __slots__ = ["__recursion_lock__"]
107
108 @recursion_lock("[...]")
Eli Benderskye0735d52011-09-08 20:12:44 +0300109 def __str__(self):
Eli Benderskybd1a09f2012-01-26 06:49:19 +0200110 return pformat(self)
Eli Benderskye0735d52011-09-08 20:12:44 +0300111
112class LazyContainer(object):
Eli Benderskybd1a09f2012-01-26 06:49:19 +0200113
Eli Benderskye0735d52011-09-08 20:12:44 +0300114 __slots__ = ["subcon", "stream", "pos", "context", "_value"]
Eli Benderskybd1a09f2012-01-26 06:49:19 +0200115
Eli Benderskye0735d52011-09-08 20:12:44 +0300116 def __init__(self, subcon, stream, pos, context):
117 self.subcon = subcon
118 self.stream = stream
119 self.pos = pos
120 self.context = context
121 self._value = NotImplemented
Eli Benderskybd1a09f2012-01-26 06:49:19 +0200122
Eli Benderskye0735d52011-09-08 20:12:44 +0300123 def __eq__(self, other):
124 try:
125 return self._value == other._value
126 except AttributeError:
127 return False
Eli Benderskybd1a09f2012-01-26 06:49:19 +0200128
Eli Benderskye0735d52011-09-08 20:12:44 +0300129 def __ne__(self, other):
130 return not (self == other)
Eli Benderskybd1a09f2012-01-26 06:49:19 +0200131
Eli Benderskye0735d52011-09-08 20:12:44 +0300132 def __str__(self):
133 return self.__pretty_str__()
Eli Benderskybd1a09f2012-01-26 06:49:19 +0200134
Eli Benderskye0735d52011-09-08 20:12:44 +0300135 def __pretty_str__(self, nesting = 1, indentation = " "):
136 if self._value is NotImplemented:
137 text = "<unread>"
138 elif hasattr(self._value, "__pretty_str__"):
139 text = self._value.__pretty_str__(nesting, indentation)
140 else:
Eli Benderskybd1a09f2012-01-26 06:49:19 +0200141 text = str(self._value)
Eli Benderskye0735d52011-09-08 20:12:44 +0300142 return "%s: %s" % (self.__class__.__name__, text)
Eli Benderskybd1a09f2012-01-26 06:49:19 +0200143
Eli Benderskye0735d52011-09-08 20:12:44 +0300144 def read(self):
145 self.stream.seek(self.pos)
146 return self.subcon._parse(self.stream, self.context)
Eli Benderskybd1a09f2012-01-26 06:49:19 +0200147
Eli Benderskye0735d52011-09-08 20:12:44 +0300148 def dispose(self):
149 self.subcon = None
150 self.stream = None
151 self.context = None
152 self.pos = None
Eli Benderskybd1a09f2012-01-26 06:49:19 +0200153
Eli Benderskye0735d52011-09-08 20:12:44 +0300154 def _get_value(self):
155 if self._value is NotImplemented:
156 self._value = self.read()
157 return self._value
Eli Benderskybd1a09f2012-01-26 06:49:19 +0200158
Eli Benderskye0735d52011-09-08 20:12:44 +0300159 value = property(_get_value)
Eli Benderskybd1a09f2012-01-26 06:49:19 +0200160
Eli Benderskye0735d52011-09-08 20:12:44 +0300161 has_value = property(lambda self: self._value is not NotImplemented)