cdxcore 0.1.5__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.

@@ -0,0 +1,64 @@
1
+ """
2
+ Prettydict
3
+ Objects like dictionaries
4
+ Hans Buehler 2025
5
+ """
6
+
7
+ from collections.abc import Mapping
8
+
9
+ class PrettyObject(Mapping):
10
+ """
11
+ Object base class to minimc an unordered dictionary.
12
+ Explicit object version of the implied PrettyDict.
13
+
14
+ Usage pattern:
15
+
16
+ class M( PrettyObject ):
17
+ pass
18
+
19
+ m = M()
20
+ m.x = 1 # standard object handling
21
+ m['y'] = 1 # mimic dictionary
22
+ print( m['x'] ) # mimic dictionary
23
+ print( m.y ) # standard object handling
24
+
25
+ Mimics a dictionary:
26
+
27
+ u = dict( m )
28
+ print(u) --> {'x': 1, 'y': 2}
29
+
30
+ u = { k: 2*v for k,v in m.items() }
31
+ print(u) --> {'x': 2, 'y': 4}
32
+
33
+ l = list( m )
34
+ print(l) --> ['x', 'y']
35
+ """
36
+ def __init__(self, **kwargs):
37
+ for k, v in kwargs.items():
38
+ setattr(self, k, v)
39
+
40
+ def __getitem__(self, key):
41
+ return getattr( self, key )
42
+ def __setitem__(self,key,value):
43
+ setattr(self, key, value)
44
+ return self[key]
45
+ def __delitem__(self,key):
46
+ delattr(self, key)
47
+ def __iter__(self):
48
+ return self.__dict__.__iter__()
49
+ def __contains__(self, key):
50
+ return self.__dict__.__contains__(key)
51
+ def __len__(self):
52
+ return self.__dict__.__len__()
53
+
54
+ def keys(self):
55
+ return self.__dict__.keys()
56
+ def items(self):
57
+ return self.__dict__.items()
58
+ def values(self):
59
+ return self.__dict__.values()
60
+
61
+ def __repr__(self):
62
+ return f"PrettyObject({self.__dict__.__repr__()})"
63
+ def __str__(self):
64
+ return self.__dict__.__str__()
cdxcore/sharedarray.py ADDED
@@ -0,0 +1,285 @@
1
+ """
2
+ Shared named numpy arrays
3
+ Hans Buehler 2023
4
+ """
5
+
6
+ from .logger import Logger
7
+ from .version import version
8
+ from .verbose import Context
9
+ from .util import fmt_digits
10
+ import numpy as np
11
+ import gc as gc
12
+ from multiprocessing import shared_memory
13
+ _log = Logger(__file__)
14
+
15
+ @version("0.0.1")
16
+ class ndsharedarray( object ):
17
+ """
18
+ Wrapper around https://docs.python.org/3/library/multiprocessing.shared_memory.html
19
+ Use sharedarray() to create objects of this type
20
+
21
+ This array vaguley behaves like a numpy array, but it is usally better to use ndsharedarray.array
22
+ to access the actual underlying numpy array.
23
+ """
24
+
25
+ serial_number = 0
26
+
27
+ def __init__(self, name : str,
28
+ shape : tuple,
29
+ create : bool,
30
+ dtype = np.float32,
31
+ full = 0.,
32
+ *,
33
+ verbose : Context = None ):
34
+ """
35
+ Create a new shared memory array.
36
+ See sharedarray().
37
+
38
+ Recall that Python objects are not freed until they are garbage collected.
39
+ Make sure you use gc.collect() whenever you want to make sure to delete
40
+ a particular shared memory.
41
+
42
+ Parameters
43
+ ----------
44
+ name : globally unique name accross all processes.
45
+ Note that the name used for the shared memory itself will be id'd by dtype and shape of the array to avoid collisions.
46
+ Use shared_id to obtain this name.
47
+ shape : numpy shape
48
+ create : whether to create a new shared memory block or not.
49
+ dtype : numpy dtype
50
+ full : if not None: value used to fill newly created array. Not used when create is False
51
+ verbose : None or a Context. If the latter is present, the object provides logging information on when objects are created/delted
52
+ """
53
+ dtype = dtype() if isinstance(dtype, type) else dtype
54
+ size = int( np.uint64( dtype.itemsize ) * np.product( [ np.uint64(i) for i in shape ], dtype=np.uint64 ) + 32 )
55
+ shape = tuple(shape)
56
+ self._name = name
57
+ self._id = name + str(shape) + "[" + str(type(dtype).__name__) + "]"
58
+ self._serial = ndsharedarray.serial_number # for debugging purposes
59
+ self._verbose = verbose
60
+ ndsharedarray.serial_number += 1
61
+
62
+ assert size>0, "Cannot have zero size"
63
+
64
+ if create:
65
+ self._shared = shared_memory.SharedMemory(name=self._id, create=True, size=size )
66
+ self._array = np.ndarray( shape, dtype=dtype, buffer = self._shared.buf )
67
+ assert self._array.dtype == dtype, ("Dtype mismatch", self._array.dtype , dtype )
68
+ assert self._array.nbytes < size, ("size mismatch", self._array.nbytes , size )
69
+ if not full is None:
70
+ self._array[:] = full
71
+ else:
72
+ self._shared = shared_memory.SharedMemory(name=self._id, create=False, size=size )
73
+ self._array = np.ndarray( shape, dtype=dtype, buffer = self._shared.buf )
74
+ assert self.array.dtype == dtype, ("Dtype mismatch", self.array.dtype , dtype )
75
+ assert self._array.nbytes < size, ("size mismatch", self._array.nbytes , size )
76
+
77
+ if not self._verbose is None:
78
+ self._verbose.write("Initialized %s #%ld size %ld %s", self._id, self._serial, size, "(created)" if create else "(referenced)")
79
+
80
+ def __del__(self):
81
+ """ Ensure shared memory is released """
82
+ self.close(unlink=False)
83
+
84
+ def close(self, unlink : bool = False):
85
+ """
86
+ Closes the shared memory file.
87
+ Optionally calls unlink()
88
+ NOTE: unlink destroys the file and should be called after all procssess called close() ... don't ask.
89
+ c.f. https://docs.python.org/3/library/multiprocessing.shared_memory.html
90
+ """
91
+ self._array = None
92
+ if '_shared' in self.__dict__:
93
+ if not self._verbose is None:
94
+ self._verbose.write("Closing %s #%ld (do %sunlink)", self._id, self._serial, "" if unlink else "not " )
95
+ try:
96
+ self._shared.close()
97
+ except FileNotFoundError:
98
+ pass
99
+ if unlink:
100
+ try:
101
+ self._shared.unlink()
102
+ except FileNotFoundError:
103
+ pass
104
+ del self._shared
105
+
106
+ def __str__(self) -> str: #NOQA
107
+ return "ndsharedarray( " + self._id + ")" + str(self._array)
108
+
109
+ # Basic
110
+ # -----
111
+
112
+ @property
113
+ def name(self) -> str:
114
+ """ User-specified name, without shape and dtype qualification. Use shared_id() for the latter """
115
+ return self._name
116
+ @property
117
+ def shared_id(self) -> str:
118
+ """ Return fully qualified name of shared memory, e.g. including dtype and shape information """
119
+ return self._id
120
+ @property
121
+ def shared_size(self) -> int:
122
+ """ Return fully qualified name of shared memory, e.g. including dtype and shape information """
123
+ return self._shared.size
124
+ @property
125
+ def shared_buf(self):
126
+ """ binary buffer of the shared stream """
127
+ return self._shared.buf
128
+
129
+ @property
130
+ def array(self) -> np.ndarray:
131
+ """ Return underlying numpy array """
132
+ return self._array
133
+ @property
134
+ def shape(self) -> tuple:
135
+ """ Shape of the underlying array """
136
+ return self._array.shape
137
+ @property
138
+ def dtype(self):
139
+ """ Dtype """
140
+ return self._array.dtype
141
+ @property
142
+ def data(self):
143
+ """ binary buffer of the underlying numpy array """
144
+ return self._array.data
145
+ @property
146
+ def nbytes(self):
147
+ """ Size in bytes of the numpy array. Note that the internal buffer might be larger """
148
+ return self._array.nbytes
149
+ @property
150
+ def itemsize(self):
151
+ """ itemsize """
152
+ return self._array.itemsize
153
+
154
+ # mimic numpy array
155
+ # -----------------
156
+
157
+ def __getitem__(self, k, *kargs):
158
+ return self._array.__getitem__(k, *kargs)
159
+ def __setitem__(self, k, v, *kargs):
160
+ return self._array.__setitem__(k,v,*kargs)
161
+ # def __getattr__(self, name):
162
+ # return getattr(self._array,name)
163
+ @property
164
+ def __array_interface__(self):
165
+ return self._array.__array_interface__
166
+ def __array__(self, *kargs, **kwargs):
167
+ return self._array.__array__(*kargs, **kwargs)
168
+
169
+ # pickling
170
+ # --------
171
+
172
+ @staticmethod
173
+ def from_state( state ):
174
+ """ Restore object from disk """
175
+ return ndsharedarray( name=state['name'], shape=state['shape'], create=True, dtype=state['dtype'], full=state['data'] )
176
+
177
+ def __reduce__(self):
178
+ """
179
+ Pickling this object explicitly
180
+ See https://docs.python.org/3/library/pickle.html#object.__reduce__
181
+ """
182
+ state = dict( name=self._name,
183
+ dtype=self._dtype,
184
+ shape=self.shape,
185
+ data=self._array
186
+ )
187
+ return (self.from_state, (state,) )
188
+
189
+
190
+ @version("0.0.1", dependencies=[ndsharedarray] )
191
+ def sharedarray( name : str,
192
+ shape : tuple,
193
+ create : bool,
194
+ dtype = np.float32,
195
+ full = None,
196
+ *,
197
+ raiseOnError : bool = False,
198
+ verbose : Context = None ):
199
+ """
200
+ Create a new shared memory array.
201
+
202
+ Recall that Python objects are not freed until they are garbage collected.
203
+ Make sure you use gc.collect() whenever you want to make sure to delete
204
+ a particular shared memory.
205
+
206
+ Parameters
207
+ ----------
208
+ name : globally unique name accross all processes.
209
+ shape : numpy shape
210
+ create : whether to create a new shared memory block or not.
211
+ If True, and if a block with the given name already exists, this function returns None or raises FileExistsError (if raiseOnError is True)
212
+ Otherwise it will return a new block with the specified name.
213
+ If False, and no block with the given name exists, this function returns None or raises FileNotFoundError (if raiseOnError is True)
214
+ If the block has different total size, the function will raise an IncompatibleSharedSizeError
215
+ Otherwise the function will return an array pointing to the shared block
216
+ If None, then the function first attempts to create a new block with the given name.
217
+ If this is successful, the function will return (array, True).
218
+ If creating a new memory block fails, it will attempt to read a block with the given name.
219
+ and return (array, False) where array is pointing to the shared block.
220
+ dtype : dtype
221
+ full : If not None, fill a newly created object with this data.
222
+ Ignored if a shared object is used.
223
+ raiseOnError : if False, fail by returning None, else throw FileExistsError or FileNotFoundError, respectively
224
+ verbose : None or a Context. If the latter is present, the object provides logging information on when objects are created/delted
225
+
226
+ Returns
227
+ -------
228
+ If 'create' is a boolean: returns ndsharedarray or None
229
+ If 'create' is None: return ndsharedarray, created where 'created' is a boolean indicating whether the array was newly created.
230
+
231
+ Raises
232
+ ------
233
+ May raise FileExistsError or FileNotFoundError if raiseOnError is True
234
+ """
235
+ name = str(name)
236
+ create = bool(create) if not create is None else None
237
+
238
+ assert len(name) > 0, "Must specifiy name"
239
+
240
+ if create is None or create:
241
+ try:
242
+ array = ndsharedarray( name=name, shape=shape, create=True, dtype=dtype, full=full, verbose=verbose )
243
+ if not create is None:
244
+ return array
245
+ else:
246
+ return array, True
247
+ except FileExistsError as e:
248
+ if not create is None:
249
+ if raiseOnError:
250
+ raise e
251
+ return None
252
+
253
+ try:
254
+ array = ndsharedarray( name=name, shape=shape, create=False, dtype=dtype, full=None, verbose=verbose )
255
+ if not create is None:
256
+ return array
257
+ else:
258
+ return array, False
259
+ except FileNotFoundError as e:
260
+ if not create is None:
261
+ if raiseOnError:
262
+ raise e
263
+ return None
264
+
265
+ raise Exception("Cannot create or read shared memory block '%s'", name)
266
+
267
+ from .npio import *
268
+ def shared_fromfile( file, name, dtype=np.float32 ):
269
+ """
270
+ Read array from disk into a new named sharedarray.
271
+
272
+ Parameters
273
+ ----------
274
+ file : file name passed to open()
275
+ name : memory name
276
+ dtype : target dtype
277
+
278
+ Returns
279
+ -------
280
+ Newly created numpy array
281
+ """
282
+ def construct(shape):
283
+ return ndsharedarray( name=name, shape=shape, dtype=dtype, create=True, raiseOnError=True )
284
+ return _fromfile( file, construct=construct )
285
+