1
+ # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2
+ # vi: set ft=python sts=4 ts=4 sw=4 et:
3
+ '''Containers for storing "elements" which have both a core data value as well
4
+ as some additional meta data. When indexing into these containers it is this
5
+ core value that is returned, which allows for much cleaner and more readable
6
+ access to nested structures.
7
+
8
+ Each object stored in these containers must have an attribute `value` which
9
+ provides the core data value for the element. To get the element object itself
10
+ the `get_elem` method must be used.
11
+ '''
12
+ from collections import MutableMapping , MutableSequence
13
+
14
+ from .externals import OrderedDict
15
+ from .externals .six import iteritems
16
+
17
+
18
+ class Elem (object ):
19
+ '''Basic element type has a `value` and a `meta` attribute.'''
20
+ def __init__ (self , value , meta = None ):
21
+ self .value = value
22
+ self .meta = {} if meta is None else meta
23
+
24
+
25
+ class InvalidElemError (Exception ):
26
+ '''Raised when trying to add an object without a `value` attribute to an
27
+ `ElemDict`.'''
28
+ def __init__ (self , invalid_val ):
29
+ self .invalid_val = invalid_val
30
+ message = ("Provided value '%s' of type %s does not have a 'value' "
31
+ "attribute" % (self .invalid_val , type (invalid_val )))
32
+ super (InvalidElemError , self ).__init__ (message )
33
+
34
+
35
+ class ElemDict (MutableMapping ):
36
+ '''Ordered dict-like where each value is an "element", which is defined as
37
+ any object which has a `value` attribute.
38
+
39
+ When looking up an item in the dict, it is this `value` attribute that
40
+ is returned. To get the element itself use the `get_elem` method.
41
+ '''
42
+
43
+ def __init__ (self , * args , ** kwargs ):
44
+ if len (args ) > 1 :
45
+ raise TypeError ("At most one arg expected, got %d" % len (args ))
46
+ self ._elems = OrderedDict ()
47
+ if len (args ) == 1 :
48
+ arg = args [0 ]
49
+ if hasattr (arg , 'get_elem' ):
50
+ it = ((k , arg .get_elem (k )) for k in arg )
51
+ elif hasattr (arg , 'items' ):
52
+ it = iteritems (arg )
53
+ else :
54
+ it = arg
55
+ for key , val in it :
56
+ self [key ] = val
57
+ for key , val in iteritems (kwargs ):
58
+ self [key ] = val
59
+
60
+ def __getitem__ (self , key ):
61
+ return self ._elems [key ].value
62
+
63
+ def __setitem__ (self , key , val ):
64
+ if not hasattr (val , 'value' ):
65
+ raise InvalidElemError (val )
66
+ self ._elems [key ] = val
67
+
68
+ def __delitem__ (self , key ):
69
+ del self ._elems [key ]
70
+
71
+ def __iter__ (self ):
72
+ return iter (self ._elems )
73
+
74
+ def __len__ (self ):
75
+ return len (self ._elems )
76
+
77
+ def __repr__ (self ):
78
+ return ('ElemDict(%s)' %
79
+ ', ' .join (['%r=%r' % x for x in self .items ()]))
80
+
81
+ def update (self , other ):
82
+ if hasattr (other , 'get_elem' ):
83
+ for key in other :
84
+ self [key ] = other .get_elem (key )
85
+ else :
86
+ for key , elem in iteritems (other ):
87
+ self [key ] = elem
88
+
89
+ def get_elem (self , key ):
90
+ return self ._elems [key ]
91
+
92
+
93
+ class ElemList (MutableSequence ):
94
+ '''A list-like container where each value is an "element", which is
95
+ defined as any object which has a `value` attribute.
96
+
97
+ When looking up an item in the list, it is this `value` attribute that
98
+ is returned. To get the element itself use the `get_elem` method.
99
+ '''
100
+ def __init__ (self , data = None ):
101
+ self ._elems = list ()
102
+ if data is not None :
103
+ if isinstance (data , self .__class__ ):
104
+ for idx in range (len (data )):
105
+ self .append (data .get_elem (idx ))
106
+ else :
107
+ for elem in data :
108
+ self .append (elem )
109
+
110
+ def _tuple_from_slice (self , slc ):
111
+ '''Get (start, end, step) tuple from slice object.
112
+ '''
113
+ (start , end , step ) = slc .indices (len (self ))
114
+ # Replace (0, -1, 1) with (0, 0, 1) (misfeature in .indices()).
115
+ if step == 1 :
116
+ if end < start :
117
+ end = start
118
+ step = None
119
+ if slc .step == None :
120
+ step = None
121
+ return (start , end , step )
122
+
123
+ def __getitem__ (self , idx ):
124
+ if isinstance (idx , slice ):
125
+ return ElemList (self ._elems [idx ])
126
+ else :
127
+ return self ._elems [idx ].value
128
+
129
+ def __setitem__ (self , idx , val ):
130
+ if isinstance (idx , slice ):
131
+ (start , end , step ) = self ._tuple_from_slice (idx )
132
+ if step != None :
133
+ # Extended slice
134
+ indices = range (start , end , step )
135
+ if len (val ) != len (indices ):
136
+ raise ValueError (('attempt to assign sequence of size %d' +
137
+ ' to extended slice of size %d' ) %
138
+ (len (value ), len (indices )))
139
+ for j , assign_val in enumerate (val ):
140
+ self .insert (indices [j ], assign_val )
141
+ else :
142
+ # Normal slice
143
+ for j , assign_val in enumerate (val ):
144
+ self .insert (start + j , assign_val )
145
+ else :
146
+ self .insert (idx , val )
147
+
148
+ def __delitem__ (self , idx ):
149
+ del self ._elems [idx ]
150
+
151
+ def __len__ (self ):
152
+ return len (self ._elems )
153
+
154
+ def __repr__ (self ):
155
+ return ('ElemList([%s])' % ', ' .join (['%r' % x for x in self ]))
156
+
157
+ def __add__ (self , other ):
158
+ result = self .__class__ (self )
159
+ if isinstance (other , self .__class__ ):
160
+ for idx in range (len (other )):
161
+ result .append (other .get_elem (idx ))
162
+ else :
163
+ for e in other :
164
+ result .append (e )
165
+ return result
166
+
167
+ def __radd__ (self , other ):
168
+ result = self .__class__ (other )
169
+ for idx in range (len (self )):
170
+ result .append (self .get_elem (idx ))
171
+ return result
172
+
173
+ def __iadd__ (self , other ):
174
+ if isinstance (other , self .__class__ ):
175
+ for idx in range (len (other )):
176
+ self .append (other .get_elem (idx ))
177
+ else :
178
+ for e in other :
179
+ self .append (e )
180
+ return self
181
+
182
+ def insert (self , idx , val ):
183
+ if not hasattr (val , 'value' ):
184
+ raise InvalidElemError (val )
185
+ self ._elems .insert (idx , val )
186
+
187
+ def get_elem (self , idx ):
188
+ return self ._elems [idx ]
0 commit comments