diff options
author | Chris Larson <chris_larson@mentor.com> | 2011-05-05 16:53:44 -0700 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2011-06-08 20:38:23 +0100 |
commit | 83708e43c2cd647dfded66338bfa61ea2000d030 (patch) | |
tree | aeb788a6f87041c2662d54eefbc33545d2c8b74a /bitbake/lib/bb/namedtuple_with_abc.py | |
parent | a702c3dbf5fdc88eefdadd1f6c4cd6e04811c1e1 (diff) | |
download | poky-83708e43c2cd647dfded66338bfa61ea2000d030.tar.gz |
bb.namedtuple_with_abc: add useful util from activestate
(Bitbake rev: bf9391c60d8dd9eeaca87783cb874c56fa7a4e91)
Signed-off-by: Chris Larson <chris_larson@mentor.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/bb/namedtuple_with_abc.py')
-rw-r--r-- | bitbake/lib/bb/namedtuple_with_abc.py | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/bitbake/lib/bb/namedtuple_with_abc.py b/bitbake/lib/bb/namedtuple_with_abc.py new file mode 100644 index 0000000000..f5e0a3f3d5 --- /dev/null +++ b/bitbake/lib/bb/namedtuple_with_abc.py | |||
@@ -0,0 +1,255 @@ | |||
1 | # http://code.activestate.com/recipes/577629-namedtupleabc-abstract-base-class-mix-in-for-named/ | ||
2 | #!/usr/bin/env python | ||
3 | # Copyright (c) 2011 Jan Kaliszewski (zuo). Available under the MIT License. | ||
4 | |||
5 | """ | ||
6 | namedtuple_with_abc.py: | ||
7 | * named tuple mix-in + ABC (abstract base class) recipe, | ||
8 | * works under Python 2.6, 2.7 as well as 3.x. | ||
9 | |||
10 | Import this module to patch collections.namedtuple() factory function | ||
11 | -- enriching it with the 'abc' attribute (an abstract base class + mix-in | ||
12 | for named tuples) and decorating it with a wrapper that registers each | ||
13 | newly created named tuple as a subclass of namedtuple.abc. | ||
14 | |||
15 | How to import: | ||
16 | import collections, namedtuple_with_abc | ||
17 | or: | ||
18 | import namedtuple_with_abc | ||
19 | from collections import namedtuple | ||
20 | # ^ in this variant you must import namedtuple function | ||
21 | # *after* importing namedtuple_with_abc module | ||
22 | or simply: | ||
23 | from namedtuple_with_abc import namedtuple | ||
24 | |||
25 | Simple usage example: | ||
26 | class Credentials(namedtuple.abc): | ||
27 | _fields = 'username password' | ||
28 | def __str__(self): | ||
29 | return ('{0.__class__.__name__}' | ||
30 | '(username={0.username}, password=...)'.format(self)) | ||
31 | print(Credentials("alice", "Alice's password")) | ||
32 | |||
33 | For more advanced examples -- see below the "if __name__ == '__main__':". | ||
34 | """ | ||
35 | |||
36 | import collections | ||
37 | from abc import ABCMeta, abstractproperty | ||
38 | from functools import wraps | ||
39 | from sys import version_info | ||
40 | |||
41 | __all__ = ('namedtuple',) | ||
42 | _namedtuple = collections.namedtuple | ||
43 | |||
44 | |||
45 | class _NamedTupleABCMeta(ABCMeta): | ||
46 | '''The metaclass for the abstract base class + mix-in for named tuples.''' | ||
47 | def __new__(mcls, name, bases, namespace): | ||
48 | fields = namespace.get('_fields') | ||
49 | for base in bases: | ||
50 | if fields is not None: | ||
51 | break | ||
52 | fields = getattr(base, '_fields', None) | ||
53 | if not isinstance(fields, abstractproperty): | ||
54 | basetuple = _namedtuple(name, fields) | ||
55 | bases = (basetuple,) + bases | ||
56 | namespace.pop('_fields', None) | ||
57 | namespace.setdefault('__doc__', basetuple.__doc__) | ||
58 | namespace.setdefault('__slots__', ()) | ||
59 | return ABCMeta.__new__(mcls, name, bases, namespace) | ||
60 | |||
61 | |||
62 | exec( | ||
63 | # Python 2.x metaclass declaration syntax | ||
64 | """class _NamedTupleABC(object): | ||
65 | '''The abstract base class + mix-in for named tuples.''' | ||
66 | __metaclass__ = _NamedTupleABCMeta | ||
67 | _fields = abstractproperty()""" if version_info[0] < 3 else | ||
68 | # Python 3.x metaclass declaration syntax | ||
69 | """class _NamedTupleABC(metaclass=_NamedTupleABCMeta): | ||
70 | '''The abstract base class + mix-in for named tuples.''' | ||
71 | _fields = abstractproperty()""" | ||
72 | ) | ||
73 | |||
74 | |||
75 | _namedtuple.abc = _NamedTupleABC | ||
76 | #_NamedTupleABC.register(type(version_info)) # (and similar, in the future...) | ||
77 | |||
78 | @wraps(_namedtuple) | ||
79 | def namedtuple(*args, **kwargs): | ||
80 | '''Named tuple factory with namedtuple.abc subclass registration.''' | ||
81 | cls = _namedtuple(*args, **kwargs) | ||
82 | _NamedTupleABC.register(cls) | ||
83 | return cls | ||
84 | |||
85 | collections.namedtuple = namedtuple | ||
86 | |||
87 | |||
88 | |||
89 | |||
90 | if __name__ == '__main__': | ||
91 | |||
92 | '''Examples and explanations''' | ||
93 | |||
94 | # Simple usage | ||
95 | |||
96 | class MyRecord(namedtuple.abc): | ||
97 | _fields = 'x y z' # such form will be transformed into ('x', 'y', 'z') | ||
98 | def _my_custom_method(self): | ||
99 | return list(self._asdict().items()) | ||
100 | # (the '_fields' attribute belongs to the named tuple public API anyway) | ||
101 | |||
102 | rec = MyRecord(1, 2, 3) | ||
103 | print(rec) | ||
104 | print(rec._my_custom_method()) | ||
105 | print(rec._replace(y=222)) | ||
106 | print(rec._replace(y=222)._my_custom_method()) | ||
107 | |||
108 | # Custom abstract classes... | ||
109 | |||
110 | class MyAbstractRecord(namedtuple.abc): | ||
111 | def _my_custom_method(self): | ||
112 | return list(self._asdict().items()) | ||
113 | |||
114 | try: | ||
115 | MyAbstractRecord() # (abstract classes cannot be instantiated) | ||
116 | except TypeError as exc: | ||
117 | print(exc) | ||
118 | |||
119 | class AnotherAbstractRecord(MyAbstractRecord): | ||
120 | def __str__(self): | ||
121 | return '<<<{0}>>>'.format(super(AnotherAbstractRecord, | ||
122 | self).__str__()) | ||
123 | |||
124 | # ...and their non-abstract subclasses | ||
125 | |||
126 | class MyRecord2(MyAbstractRecord): | ||
127 | _fields = 'a, b' | ||
128 | |||
129 | class MyRecord3(AnotherAbstractRecord): | ||
130 | _fields = 'p', 'q', 'r' | ||
131 | |||
132 | rec2 = MyRecord2('foo', 'bar') | ||
133 | print(rec2) | ||
134 | print(rec2._my_custom_method()) | ||
135 | print(rec2._replace(b=222)) | ||
136 | print(rec2._replace(b=222)._my_custom_method()) | ||
137 | |||
138 | rec3 = MyRecord3('foo', 'bar', 'baz') | ||
139 | print(rec3) | ||
140 | print(rec3._my_custom_method()) | ||
141 | print(rec3._replace(q=222)) | ||
142 | print(rec3._replace(q=222)._my_custom_method()) | ||
143 | |||
144 | # You can also subclass non-abstract ones... | ||
145 | |||
146 | class MyRecord33(MyRecord3): | ||
147 | def __str__(self): | ||
148 | return '< {0!r}, ..., {0!r} >'.format(self.p, self.r) | ||
149 | |||
150 | rec33 = MyRecord33('foo', 'bar', 'baz') | ||
151 | print(rec33) | ||
152 | print(rec33._my_custom_method()) | ||
153 | print(rec33._replace(q=222)) | ||
154 | print(rec33._replace(q=222)._my_custom_method()) | ||
155 | |||
156 | # ...and even override the magic '_fields' attribute again | ||
157 | |||
158 | class MyRecord345(MyRecord3): | ||
159 | _fields = 'e f g h i j k' | ||
160 | |||
161 | rec345 = MyRecord345(1, 2, 3, 4, 3, 2, 1) | ||
162 | print(rec345) | ||
163 | print(rec345._my_custom_method()) | ||
164 | print(rec345._replace(f=222)) | ||
165 | print(rec345._replace(f=222)._my_custom_method()) | ||
166 | |||
167 | # Mixing-in some other classes is also possible: | ||
168 | |||
169 | class MyMixIn(object): | ||
170 | def method(self): | ||
171 | return "MyMixIn.method() called" | ||
172 | def _my_custom_method(self): | ||
173 | return "MyMixIn._my_custom_method() called" | ||
174 | def count(self, item): | ||
175 | return "MyMixIn.count({0}) called".format(item) | ||
176 | def _asdict(self): # (cannot override a namedtuple method, see below) | ||
177 | return "MyMixIn._asdict() called" | ||
178 | |||
179 | class MyRecord4(MyRecord33, MyMixIn): # mix-in on the right | ||
180 | _fields = 'j k l x' | ||
181 | |||
182 | class MyRecord5(MyMixIn, MyRecord33): # mix-in on the left | ||
183 | _fields = 'j k l x y' | ||
184 | |||
185 | rec4 = MyRecord4(1, 2, 3, 2) | ||
186 | print(rec4) | ||
187 | print(rec4.method()) | ||
188 | print(rec4._my_custom_method()) # MyRecord33's | ||
189 | print(rec4.count(2)) # tuple's | ||
190 | print(rec4._replace(k=222)) | ||
191 | print(rec4._replace(k=222).method()) | ||
192 | print(rec4._replace(k=222)._my_custom_method()) # MyRecord33's | ||
193 | print(rec4._replace(k=222).count(8)) # tuple's | ||
194 | |||
195 | rec5 = MyRecord5(1, 2, 3, 2, 1) | ||
196 | print(rec5) | ||
197 | print(rec5.method()) | ||
198 | print(rec5._my_custom_method()) # MyMixIn's | ||
199 | print(rec5.count(2)) # MyMixIn's | ||
200 | print(rec5._replace(k=222)) | ||
201 | print(rec5._replace(k=222).method()) | ||
202 | print(rec5._replace(k=222)._my_custom_method()) # MyMixIn's | ||
203 | print(rec5._replace(k=222).count(2)) # MyMixIn's | ||
204 | |||
205 | # None that behavior: the standard namedtuple methods cannot be | ||
206 | # overriden by a foreign mix-in -- even if the mix-in is declared | ||
207 | # as the leftmost base class (but, obviously, you can override them | ||
208 | # in the defined class or its subclasses): | ||
209 | |||
210 | print(rec4._asdict()) # (returns a dict, not "MyMixIn._asdict() called") | ||
211 | print(rec5._asdict()) # (returns a dict, not "MyMixIn._asdict() called") | ||
212 | |||
213 | class MyRecord6(MyRecord33): | ||
214 | _fields = 'j k l x y z' | ||
215 | def _asdict(self): | ||
216 | return "MyRecord6._asdict() called" | ||
217 | rec6 = MyRecord6(1, 2, 3, 1, 2, 3) | ||
218 | print(rec6._asdict()) # (this returns "MyRecord6._asdict() called") | ||
219 | |||
220 | # All that record classes are real subclasses of namedtuple.abc: | ||
221 | |||
222 | assert issubclass(MyRecord, namedtuple.abc) | ||
223 | assert issubclass(MyAbstractRecord, namedtuple.abc) | ||
224 | assert issubclass(AnotherAbstractRecord, namedtuple.abc) | ||
225 | assert issubclass(MyRecord2, namedtuple.abc) | ||
226 | assert issubclass(MyRecord3, namedtuple.abc) | ||
227 | assert issubclass(MyRecord33, namedtuple.abc) | ||
228 | assert issubclass(MyRecord345, namedtuple.abc) | ||
229 | assert issubclass(MyRecord4, namedtuple.abc) | ||
230 | assert issubclass(MyRecord5, namedtuple.abc) | ||
231 | assert issubclass(MyRecord6, namedtuple.abc) | ||
232 | |||
233 | # ...but abstract ones are not subclasses of tuple | ||
234 | # (and this is what you probably want): | ||
235 | |||
236 | assert not issubclass(MyAbstractRecord, tuple) | ||
237 | assert not issubclass(AnotherAbstractRecord, tuple) | ||
238 | |||
239 | assert issubclass(MyRecord, tuple) | ||
240 | assert issubclass(MyRecord2, tuple) | ||
241 | assert issubclass(MyRecord3, tuple) | ||
242 | assert issubclass(MyRecord33, tuple) | ||
243 | assert issubclass(MyRecord345, tuple) | ||
244 | assert issubclass(MyRecord4, tuple) | ||
245 | assert issubclass(MyRecord5, tuple) | ||
246 | assert issubclass(MyRecord6, tuple) | ||
247 | |||
248 | # Named tuple classes created with namedtuple() factory function | ||
249 | # (in the "traditional" way) are registered as "virtual" subclasses | ||
250 | # of namedtuple.abc: | ||
251 | |||
252 | MyTuple = namedtuple('MyTuple', 'a b c') | ||
253 | mt = MyTuple(1, 2, 3) | ||
254 | assert issubclass(MyTuple, namedtuple.abc) | ||
255 | assert isinstance(mt, namedtuple.abc) | ||