cdxcore 0.1.6__py3-none-any.whl → 0.1.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cdxcore might be problematic. Click here for more details.
- cdxcore/__init__.py +1 -9
- cdxcore/config.py +1188 -521
- cdxcore/crman.py +95 -25
- cdxcore/err.py +371 -0
- cdxcore/pretty.py +468 -0
- cdxcore/pretty.py_bak.py +750 -0
- cdxcore/subdir.py +2238 -1339
- cdxcore/uniquehash.py +515 -363
- cdxcore/util.py +358 -417
- cdxcore/verbose.py +683 -248
- cdxcore/version.py +399 -140
- cdxcore-0.1.10.dist-info/METADATA +27 -0
- cdxcore-0.1.10.dist-info/RECORD +35 -0
- {cdxcore-0.1.6.dist-info → cdxcore-0.1.10.dist-info}/top_level.txt +2 -1
- docs/source/conf.py +123 -0
- tests/test_config.py +500 -0
- tests/test_crman.py +54 -0
- tests/test_err.py +86 -0
- tests/test_pretty.py +404 -0
- tests/test_subdir.py +289 -0
- tests/test_uniquehash.py +159 -144
- tests/test_util.py +122 -83
- tests/test_verbose.py +119 -0
- tests/test_version.py +153 -0
- up/git_message.py +2 -2
- cdxcore/logger.py +0 -319
- cdxcore/prettydict.py +0 -388
- cdxcore/prettyobject.py +0 -64
- cdxcore-0.1.6.dist-info/METADATA +0 -1418
- cdxcore-0.1.6.dist-info/RECORD +0 -30
- conda/conda_exists.py +0 -10
- conda/conda_modify_yaml.py +0 -42
- tests/_cdxbasics.py +0 -1086
- {cdxcore-0.1.6.dist-info → cdxcore-0.1.10.dist-info}/WHEEL +0 -0
- {cdxcore-0.1.6.dist-info → cdxcore-0.1.10.dist-info}/licenses/LICENSE +0 -0
- {cdxcore → tmp}/deferred.py +0 -0
- {cdxcore → tmp}/dynaplot.py +0 -0
- {cdxcore → tmp}/filelock.py +0 -0
- {cdxcore → tmp}/np.py +0 -0
- {cdxcore → tmp}/npio.py +0 -0
- {cdxcore → tmp}/sharedarray.py +0 -0
cdxcore/pretty.py
ADDED
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Overview
|
|
3
|
+
--------
|
|
4
|
+
|
|
5
|
+
A simple :class:`cdxcore.pretty.PrettyObject` class which mimics directory access to its members.
|
|
6
|
+
The purpose is a functional-programming style pattern for generating complex objects::
|
|
7
|
+
|
|
8
|
+
from cdxbasics.prettydict import PrettyObject
|
|
9
|
+
pdct = PrettyObject(z=1)
|
|
10
|
+
|
|
11
|
+
pdct.num_samples = 1000
|
|
12
|
+
pdct.num_batches = 100
|
|
13
|
+
pdct.method = "signature"
|
|
14
|
+
|
|
15
|
+
The object allows accessing members via ``[]``:
|
|
16
|
+
|
|
17
|
+
print( pdct['num_samples'] ) # -> 1000
|
|
18
|
+
print( pdct['num_batches'] ) # -> 100
|
|
19
|
+
|
|
20
|
+
Features
|
|
21
|
+
^^^^^^^^
|
|
22
|
+
|
|
23
|
+
:class:`cdxcore.pretty.PrettyObject` implements all relevant dictionary protocols, so objects of type :class:`cdxcore.pretty.PrettyObject` can
|
|
24
|
+
(nearly always) be passed where dictionaries are expected:
|
|
25
|
+
|
|
26
|
+
* A :class:`cdxcore.pretty.PrettyObject` object supports standard dictionary semantics in addition to member attribute
|
|
27
|
+
access.
|
|
28
|
+
That means you can use ``pdct['num_samples']`` as well as ``pdc.num_samples``.
|
|
29
|
+
You can mix standard dictionary notation with member attribute notation::
|
|
30
|
+
|
|
31
|
+
print(pdct["num_samples"]) # -> prints "1000"
|
|
32
|
+
pdct["test"] = 1 # sets pdct.test to 1
|
|
33
|
+
|
|
34
|
+
* Iterations work just like for dictionaries; for example::
|
|
35
|
+
|
|
36
|
+
for k,v in pdct.items():
|
|
37
|
+
print( k, v)
|
|
38
|
+
|
|
39
|
+
* Applying ``str`` and ``repr`` to objects of type :class:`cdxcore.pretty.PrettyObject` will return dictionary-type
|
|
40
|
+
results, so for example ``print(pdct)`` of the above will return ``{'z': 1, 'num_samples': 1000, 'num_batches': 100, 'method': 'signature'}``.
|
|
41
|
+
|
|
42
|
+
The :attr:`cdxcore.pretty.PrettyObject.at_pos` attribute allows accessing elements of the ordered dictionary
|
|
43
|
+
by positon:
|
|
44
|
+
|
|
45
|
+
* ``cdxcore.pretty.PrettyObject.at_pos[i]`` returns the `i` th element.
|
|
46
|
+
|
|
47
|
+
* ``cdxcore.pretty.PrettyObject.at_pos.keys[i]`` returns the `i` th key.
|
|
48
|
+
|
|
49
|
+
* ``cdxcore.pretty.PrettyObject.at_pos.items[i]`` returns the `i` th item.
|
|
50
|
+
|
|
51
|
+
For example::
|
|
52
|
+
|
|
53
|
+
print(pdct.at_pos[3]) # -> prints "signature"
|
|
54
|
+
print(pdct.at_pos.keys[3]) # -> prints "method"
|
|
55
|
+
|
|
56
|
+
You can also assign member functions to a :class:`cdxcore.pretty.PrettyObject`.
|
|
57
|
+
The following works as expected::
|
|
58
|
+
|
|
59
|
+
pdct.f = lambda self, y: return self.y*x
|
|
60
|
+
|
|
61
|
+
(to assign a static function which does not refer to ``self``, use ``pdct['g'] = lambda z : return z``).
|
|
62
|
+
|
|
63
|
+
Dataclasses
|
|
64
|
+
^^^^^^^^^^^
|
|
65
|
+
|
|
66
|
+
:mod:`dataclasses` rely on default values of any member being "frozen" objects, which most user-defined objects and
|
|
67
|
+
:class:`cdxcore.pretty.PrettyObject` objects are not.
|
|
68
|
+
This limitation applies as well to `flax <https://flax-linen.readthedocs.io/en/latest/api_reference/flax.linen/module.html>`__ modules.
|
|
69
|
+
To use non-frozen default values, use the
|
|
70
|
+
:meth:`cdxcore.pretty.PrettyObject.as_field` function::
|
|
71
|
+
|
|
72
|
+
from cdxbasics.prettydict import PrettyObject
|
|
73
|
+
from dataclasses import dataclass
|
|
74
|
+
|
|
75
|
+
@dataclass
|
|
76
|
+
class Data:
|
|
77
|
+
data : PrettyObject = PrettyObject(x=2).as_field()
|
|
78
|
+
|
|
79
|
+
def f(self):
|
|
80
|
+
return self.data.x
|
|
81
|
+
|
|
82
|
+
d = Data() # default constructor used.
|
|
83
|
+
d.f() # -> returns 2
|
|
84
|
+
|
|
85
|
+
Import
|
|
86
|
+
------
|
|
87
|
+
.. code-block:: python
|
|
88
|
+
|
|
89
|
+
from cdxcore.pretty import PrettyObject as pdct
|
|
90
|
+
|
|
91
|
+
Documentation
|
|
92
|
+
-------------
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
from collections import OrderedDict
|
|
96
|
+
import dataclasses as dataclasses
|
|
97
|
+
from dataclasses import Field
|
|
98
|
+
import types as types
|
|
99
|
+
from collections.abc import Mapping, MutableMapping, Sequence
|
|
100
|
+
|
|
101
|
+
class __No_Default_dummy():
|
|
102
|
+
pass
|
|
103
|
+
no_default = __No_Default_dummy()
|
|
104
|
+
|
|
105
|
+
class PrettyObject(MutableMapping):
|
|
106
|
+
"""
|
|
107
|
+
Class mimicing an ordered dictionary.
|
|
108
|
+
|
|
109
|
+
Example::
|
|
110
|
+
|
|
111
|
+
from cdxcore.pretty import PrettyObject
|
|
112
|
+
pdct = PrettyObject()
|
|
113
|
+
pdct.x = 1
|
|
114
|
+
pdct['y'] = 2
|
|
115
|
+
print( pdct['x'], pdct.y ) # -> prints 1 2
|
|
116
|
+
|
|
117
|
+
The object mimics a dictionary::
|
|
118
|
+
|
|
119
|
+
print(pdct) # -> '{'x': 1, 'y': 2}'
|
|
120
|
+
|
|
121
|
+
u = dict( pdct )
|
|
122
|
+
print(u) # -> {'x': 1, 'y': 2}
|
|
123
|
+
|
|
124
|
+
u = { k: 2*v for k,v in pdct.items() }
|
|
125
|
+
print(u) # -> {'x': 2, 'y': 4}
|
|
126
|
+
|
|
127
|
+
l = list( pdct )
|
|
128
|
+
print(l) # -> ['x', 'y']
|
|
129
|
+
|
|
130
|
+
*Important:*
|
|
131
|
+
attributes starting with '__' cannot be accessed with item ``[]`` notation.
|
|
132
|
+
In other words::
|
|
133
|
+
|
|
134
|
+
pdct = PrettyObject()
|
|
135
|
+
pdct.__x = 1 # fine
|
|
136
|
+
_ = pdct['__x'] # <- throws an exception
|
|
137
|
+
|
|
138
|
+
**Access by Index Position**
|
|
139
|
+
|
|
140
|
+
:class:`cdxcore.pretty.PrettyObject` retains order of construction. To access its members
|
|
141
|
+
by index position, use the :attr:`cdxcore.pretty.PrettyObject.at_pos` attribute::
|
|
142
|
+
|
|
143
|
+
print(pdct.at_pos[1]) # -> prints "2"
|
|
144
|
+
print(pdct.at_pos.keys[1]) # -> prints "y"
|
|
145
|
+
print(list(pdct.at_pos.items[2])) # -> prints "[('x', 1), ('y', 2)]"
|
|
146
|
+
|
|
147
|
+
**Assigning Member Functions**
|
|
148
|
+
|
|
149
|
+
``PrettyObject`` objects also allow assigning bona fide member functions by a simple semantic of the form::
|
|
150
|
+
|
|
151
|
+
pdct = PrettyObject(b=2)
|
|
152
|
+
pdct.mult_b = lambda self, x: self.b*x
|
|
153
|
+
pdct.mult_b(3) # -> 6
|
|
154
|
+
|
|
155
|
+
Calling ``pdct.mult_b(3)`` with above ``pdct`` will return `6` as expected.
|
|
156
|
+
To assign static member functions, use the ``[]`` operator.
|
|
157
|
+
The reason for this is as follows: consider::
|
|
158
|
+
|
|
159
|
+
def mult( a, b ):
|
|
160
|
+
return a*b
|
|
161
|
+
pdct = PrettyObject()
|
|
162
|
+
pdct.mult = mult
|
|
163
|
+
pdct.mult(3,4) --> produces am error as three arguments must be passed: self, 3, and 4
|
|
164
|
+
|
|
165
|
+
In this case, use::
|
|
166
|
+
|
|
167
|
+
pdct = PrettyObject()
|
|
168
|
+
pdct['mult'] = mult
|
|
169
|
+
pdct.mult(3,4) --> 12
|
|
170
|
+
|
|
171
|
+
You can also pass member functions to the constructor::
|
|
172
|
+
|
|
173
|
+
p = PrettyObject( f=lambda self, x: self.y*x, y=2)
|
|
174
|
+
p.f(3) # -> 6
|
|
175
|
+
|
|
176
|
+
**Operators**
|
|
177
|
+
|
|
178
|
+
Objects of type :class:`cdxcore.pretty.PrettyObject` support the following operators:
|
|
179
|
+
|
|
180
|
+
* Comparison operator ``==`` and ``!=`` test for equality of keys and values. Unlike for dictionaries
|
|
181
|
+
comparisons are performed in *in order*. That means ``PrettyObject(x=1,y=2)`` and ``PrettyObject(y=2,x=1)``
|
|
182
|
+
are *not* equal.
|
|
183
|
+
|
|
184
|
+
* Super/subset operators ``>=`` and ``<=`` test for a super/sup set relationship, respectively.
|
|
185
|
+
|
|
186
|
+
* The ``a | b`` returns the union of two :class:`cdxcore.pretty.PrettyObject`. Elements of the ``b`` overwrite any elements of ``a``, if they
|
|
187
|
+
are present in both. The order of the new dictionary is determined by the order of appearance of keys in first ``a`` and then ``b``, that
|
|
188
|
+
means in all but trivial cases ``a|b != b|a``.
|
|
189
|
+
|
|
190
|
+
The ``|=`` operator is a short-cut for :meth:`cdxcore.pretty.PrettyObject.update`.
|
|
191
|
+
|
|
192
|
+
Parameters
|
|
193
|
+
----------
|
|
194
|
+
copy : Mapping, optional
|
|
195
|
+
If present, assign elements of ``copy`` to ``self``.
|
|
196
|
+
|
|
197
|
+
** kwargs:
|
|
198
|
+
Key/value pairs to be added to ``self``.
|
|
199
|
+
"""
|
|
200
|
+
def __init__(self, copy : Mapping = None, **kwargs):
|
|
201
|
+
"""
|
|
202
|
+
:meta private:
|
|
203
|
+
"""
|
|
204
|
+
if not copy is None:
|
|
205
|
+
self.update(copy)
|
|
206
|
+
for k, v in kwargs.items():
|
|
207
|
+
setattr(self, k, v)
|
|
208
|
+
|
|
209
|
+
def __getitem__(self, key):
|
|
210
|
+
try:
|
|
211
|
+
return getattr( self, key )
|
|
212
|
+
except AttributeError as e:
|
|
213
|
+
raise KeyError(key,*e.args)
|
|
214
|
+
|
|
215
|
+
def __setitem__(self,key,value):
|
|
216
|
+
"""
|
|
217
|
+
Route ``self[key] = value`` to the base class ``__setattr__`` method.
|
|
218
|
+
This way you can assign static functions using ``[]`` which assinging
|
|
219
|
+
functions using ``.`` will assign member functions.
|
|
220
|
+
"""
|
|
221
|
+
try:
|
|
222
|
+
super().__setattr__(key, value)
|
|
223
|
+
return self[key]
|
|
224
|
+
except AttributeError as e:
|
|
225
|
+
raise KeyError(key,*e.args)
|
|
226
|
+
|
|
227
|
+
def __delitem__(self,key):
|
|
228
|
+
try:
|
|
229
|
+
delattr(self, key)
|
|
230
|
+
except AttributeError as e:
|
|
231
|
+
raise KeyError(key,*e.args)
|
|
232
|
+
def __iter__(self):
|
|
233
|
+
return self.__dict__.__iter__()
|
|
234
|
+
def __reversed__(self):
|
|
235
|
+
return self.__dict__.__reversed__()
|
|
236
|
+
def __sizeof__(self):
|
|
237
|
+
return self.__dict__.__sizeof__()
|
|
238
|
+
def __contains__(self, key):
|
|
239
|
+
return self.__dict__.__contains__(key)
|
|
240
|
+
def __len__(self):
|
|
241
|
+
return self.__dict__.__len__()
|
|
242
|
+
|
|
243
|
+
# allow assigning functions with ``self``
|
|
244
|
+
def __setattr__(self, key, value):
|
|
245
|
+
"""
|
|
246
|
+
``__setattr__`` converts function assignments to member functions
|
|
247
|
+
"""
|
|
248
|
+
if key[:2] == "__":
|
|
249
|
+
super().__setattr__(key, value)
|
|
250
|
+
if isinstance(value,types.FunctionType):
|
|
251
|
+
# bind function to this object
|
|
252
|
+
value = types.MethodType(value,self)
|
|
253
|
+
elif isinstance(value,types.MethodType):
|
|
254
|
+
# re-point the method to the current instance
|
|
255
|
+
value = types.MethodType(value.__func__,self)
|
|
256
|
+
super().__setattr__(key, value)
|
|
257
|
+
|
|
258
|
+
# dictionary
|
|
259
|
+
def copy(self, **kwargs):
|
|
260
|
+
""" Return a shallow copy; optionally add further key/value pairs. """
|
|
261
|
+
return PrettyObject(self,**kwargs)
|
|
262
|
+
def clear(self):
|
|
263
|
+
""" Delete all elements. """
|
|
264
|
+
self.__dict__.clear()
|
|
265
|
+
|
|
266
|
+
def get(self, key, default = no_default ):
|
|
267
|
+
""" Equivalent to :meth:`dict.get`. """
|
|
268
|
+
try:
|
|
269
|
+
return getattr(self, key) if default == no_default else getattr(self, key, default)
|
|
270
|
+
except AttributeError as e:
|
|
271
|
+
raise KeyError(key,*e.args)
|
|
272
|
+
|
|
273
|
+
def pop(self, key, default = no_default ):
|
|
274
|
+
""" Equivalent to :meth:`dict.pop`. """
|
|
275
|
+
try:
|
|
276
|
+
v = getattr(self, key) if default == no_default else getattr(self, key, default)
|
|
277
|
+
delattr(self,key)
|
|
278
|
+
return v
|
|
279
|
+
except AttributeError as e:
|
|
280
|
+
raise KeyError(key,*e.args)
|
|
281
|
+
def setdefault( self, key, default=None ):
|
|
282
|
+
""" Equivalent to :meth:`dict.setdefault`. """
|
|
283
|
+
#return self.__dict__.setdefault(key,default)
|
|
284
|
+
if not hasattr(self, key):
|
|
285
|
+
self.__setattr__(key, default)
|
|
286
|
+
return getattr(self,key)
|
|
287
|
+
|
|
288
|
+
def update(self, other : Mapping = None, **kwargs):
|
|
289
|
+
""" Equivalent to :meth:`dict.update`. """
|
|
290
|
+
if not other is None:
|
|
291
|
+
for k, v in other.items():
|
|
292
|
+
setattr(self, k, v)
|
|
293
|
+
for k, v in kwargs.items():
|
|
294
|
+
setattr(self, k, v)
|
|
295
|
+
return self
|
|
296
|
+
|
|
297
|
+
# behave like a dictionary
|
|
298
|
+
def keys(self):
|
|
299
|
+
""" Equivalent to :meth:`dict.keys` """
|
|
300
|
+
return self.__dict__.keys()
|
|
301
|
+
def items(self):
|
|
302
|
+
""" Equivalent to :meth:`dict.items` """
|
|
303
|
+
return self.__dict__.items()
|
|
304
|
+
def values(self):
|
|
305
|
+
""" Equivalent to :meth:`dict.values` """
|
|
306
|
+
return self.__dict__.values()
|
|
307
|
+
|
|
308
|
+
# update
|
|
309
|
+
def __ior__(self, other):
|
|
310
|
+
return self.update(other)
|
|
311
|
+
def __or__(self, other):
|
|
312
|
+
copy = self.copy()
|
|
313
|
+
copy.update(other)
|
|
314
|
+
return copy
|
|
315
|
+
def __ror__(self, other):
|
|
316
|
+
copy = self.copy()
|
|
317
|
+
copy.update(other)
|
|
318
|
+
return copy
|
|
319
|
+
|
|
320
|
+
# dictionary comparison
|
|
321
|
+
def __eq__(self, other):
|
|
322
|
+
"""
|
|
323
|
+
Comparison operator. Unlike dictionary comparison, this comparision operator
|
|
324
|
+
preservers order.
|
|
325
|
+
"""
|
|
326
|
+
if len(self) != len(other):
|
|
327
|
+
return False
|
|
328
|
+
for k1, k2 in zip( self, other ):
|
|
329
|
+
if not k1==k2:
|
|
330
|
+
return False
|
|
331
|
+
for v1, v2 in zip( self.values(), other.values() ):
|
|
332
|
+
if not v1==v2:
|
|
333
|
+
return False
|
|
334
|
+
return True
|
|
335
|
+
def __le__(self, other):
|
|
336
|
+
"""
|
|
337
|
+
Subset operator i.e. if ``self`` is contained in ``other``, including values.
|
|
338
|
+
"""
|
|
339
|
+
for k, v in self.items():
|
|
340
|
+
if not k in other:
|
|
341
|
+
return False
|
|
342
|
+
if not v == other[k]:
|
|
343
|
+
return False
|
|
344
|
+
return True
|
|
345
|
+
def __ge__(self, other):
|
|
346
|
+
"""
|
|
347
|
+
Superset operator i.e. if ``self`` is a superset of ``other``, including values.
|
|
348
|
+
"""
|
|
349
|
+
return other <= self
|
|
350
|
+
|
|
351
|
+
def __neq__(self, other):
|
|
352
|
+
"""
|
|
353
|
+
Comparison operator. Unlike dictionary comparison, this comparision operator
|
|
354
|
+
preservers order.
|
|
355
|
+
"""
|
|
356
|
+
return not self == other
|
|
357
|
+
|
|
358
|
+
# print representation
|
|
359
|
+
def __repr__(self):
|
|
360
|
+
return f"PrettyObject({self.__dict__.__repr__()})"
|
|
361
|
+
def __str__(self):
|
|
362
|
+
return self.__dict__.__str__()
|
|
363
|
+
|
|
364
|
+
# data classes
|
|
365
|
+
def as_field(self) -> Field:
|
|
366
|
+
"""
|
|
367
|
+
This function provides support for :class:`dataclasses.dataclass` fields
|
|
368
|
+
with ``PrettyObject`` default values.
|
|
369
|
+
|
|
370
|
+
When adding
|
|
371
|
+
a `field <https://docs.python.org/3/library/dataclasses.html#dataclasses.field>`__
|
|
372
|
+
with a non-frozen default value to a ``@dataclass`` class,
|
|
373
|
+
a ``default_factory`` has to be provided.
|
|
374
|
+
The function ``as_field`` returns the corresponding :class:`dataclasses.Field`
|
|
375
|
+
element by returning simply::
|
|
376
|
+
|
|
377
|
+
def factory():
|
|
378
|
+
return self
|
|
379
|
+
return dataclasses.field( default_factory=factory )
|
|
380
|
+
|
|
381
|
+
Usage is as follows::
|
|
382
|
+
|
|
383
|
+
from dataclasses import dataclass
|
|
384
|
+
@dataclass
|
|
385
|
+
class A:
|
|
386
|
+
data : PrettyDict = PrettyDict(x=2).as_field()
|
|
387
|
+
|
|
388
|
+
a = A()
|
|
389
|
+
print(a.data.x) # -> "2"
|
|
390
|
+
a = A(data=PrettyDict(x=3))
|
|
391
|
+
print(a.data.x) # -> "3"
|
|
392
|
+
"""
|
|
393
|
+
def factory():
|
|
394
|
+
return self
|
|
395
|
+
return dataclasses.field( default_factory=factory )
|
|
396
|
+
|
|
397
|
+
@property
|
|
398
|
+
def at_pos(self):
|
|
399
|
+
"""
|
|
400
|
+
Elementary access to the data contained in ``self`` by ordinal position.
|
|
401
|
+
The ordinal
|
|
402
|
+
position of an element is determined by the order of addition to the dictionary.
|
|
403
|
+
|
|
404
|
+
* ``at_pos[position]`` returns an element or elements at an ordinal position:
|
|
405
|
+
|
|
406
|
+
* It returns a single element if ``position`` refers to only one field.
|
|
407
|
+
* If ``position`` is a slice then the respecitve list of fields is returned.
|
|
408
|
+
|
|
409
|
+
* ``at_pos.keys[position]`` returns the key or keys at ``position``.
|
|
410
|
+
|
|
411
|
+
* ``at_pos.items[position]`` returns the tuple ``(key, element)`` or a list thereof for ``position``.
|
|
412
|
+
|
|
413
|
+
You can also write data using the `attribute` notation:
|
|
414
|
+
|
|
415
|
+
* ``at_pos[position] = item`` assigns an item to an ordinal position:
|
|
416
|
+
|
|
417
|
+
* If ``position`` refers to a single element, ``item`` must be the value to be assigned to this element.
|
|
418
|
+
* If ``position`` is a slice then '``item`` must resolve to a list (or generator) of the required size.
|
|
419
|
+
"""
|
|
420
|
+
|
|
421
|
+
class Access(Sequence):
|
|
422
|
+
"""
|
|
423
|
+
Wrapper object to allow index access for at_pos
|
|
424
|
+
"""
|
|
425
|
+
def __init__(self):
|
|
426
|
+
self.__keys = None
|
|
427
|
+
|
|
428
|
+
def __getitem__(_, position):
|
|
429
|
+
key = _.keys[position]
|
|
430
|
+
return self[key] if not isinstance(key,list) else [ self[k] for k in key ]
|
|
431
|
+
def __setitem__(_, position, item ):
|
|
432
|
+
key = _.keys[position]
|
|
433
|
+
if not isinstance(key,list):
|
|
434
|
+
self[key] = item
|
|
435
|
+
else:
|
|
436
|
+
for k, i in zip(key, item):
|
|
437
|
+
self[k] = i
|
|
438
|
+
def __len__(_):
|
|
439
|
+
return len(self)
|
|
440
|
+
def __iter__(_):
|
|
441
|
+
for key in self:
|
|
442
|
+
yield self[key]
|
|
443
|
+
|
|
444
|
+
@property
|
|
445
|
+
def keys(_) -> list:
|
|
446
|
+
""" Returns the list of keys in construction order """
|
|
447
|
+
return list(self.keys())
|
|
448
|
+
@property
|
|
449
|
+
def values(_) -> list:
|
|
450
|
+
""" Returns the list of values in construction order """
|
|
451
|
+
return list(self.values())
|
|
452
|
+
@property
|
|
453
|
+
def items(_) -> Sequence:
|
|
454
|
+
""" Returns the sequence of key, value pairs of the original dictionary """
|
|
455
|
+
class ItemAccess(Sequence):
|
|
456
|
+
def __init__(_x):
|
|
457
|
+
_x.keys = list(self.keys())
|
|
458
|
+
def __getitem__(_x, position):
|
|
459
|
+
key = _x.keys[position]
|
|
460
|
+
return (key, self[key]) if not isinstance(key,(list,types.GeneratorType)) else [ (k,self[k]) for k in key ]
|
|
461
|
+
def __len__(_x):
|
|
462
|
+
return len(_x.keys)
|
|
463
|
+
def __iter__(_x):
|
|
464
|
+
for key in _x.keys:
|
|
465
|
+
yield key, self[key]
|
|
466
|
+
return ItemAccess()
|
|
467
|
+
|
|
468
|
+
return Access()
|