sdata 1.5.0__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.
Files changed (98) hide show
  1. sdata/__init__.py +140 -0
  2. sdata/base.py +684 -0
  3. sdata/contrib/__init__.py +0 -0
  4. sdata/contrib/piexif/__init__.py +12 -0
  5. sdata/contrib/piexif/_common.py +94 -0
  6. sdata/contrib/piexif/_dump.py +346 -0
  7. sdata/contrib/piexif/_exceptions.py +2 -0
  8. sdata/contrib/piexif/_exif.py +638 -0
  9. sdata/contrib/piexif/_insert.py +60 -0
  10. sdata/contrib/piexif/_load.py +270 -0
  11. sdata/contrib/piexif/_remove.py +56 -0
  12. sdata/contrib/piexif/_transplant.py +45 -0
  13. sdata/contrib/piexif/_webp.py +240 -0
  14. sdata/contrib/piexif/helper.py +66 -0
  15. sdata/contrib/ranger/__init__.py +8 -0
  16. sdata/contrib/ranger/collections/__init__.py +0 -0
  17. sdata/contrib/ranger/collections/rangebucketmap.py +346 -0
  18. sdata/contrib/ranger/collections/rangemap.py +396 -0
  19. sdata/contrib/ranger/collections/rangeset.py +483 -0
  20. sdata/contrib/ranger/range/__init__.py +0 -0
  21. sdata/contrib/ranger/range/cut.py +285 -0
  22. sdata/contrib/ranger/range/range.py +644 -0
  23. sdata/contrib/sobol_seq.py +394 -0
  24. sdata/contrib/sortedcontainers/LICENSE +13 -0
  25. sdata/contrib/sortedcontainers/__init__.py +74 -0
  26. sdata/contrib/sortedcontainers/sorteddict.py +789 -0
  27. sdata/contrib/sortedcontainers/sortedlist.py +2646 -0
  28. sdata/contrib/sortedcontainers/sortedset.py +733 -0
  29. sdata/contrib/sqlitedict.py +598 -0
  30. sdata/contrib/timeflake/__init__.py +40 -0
  31. sdata/contrib/timeflake/flake.py +69 -0
  32. sdata/contrib/timeflake/utils.py +47 -0
  33. sdata/deprecated/__init__.py +10 -0
  34. sdata/deprecated/data.py +1503 -0
  35. sdata/did/__init__.py +129 -0
  36. sdata/did/_http.py +67 -0
  37. sdata/did/base58btc.py +49 -0
  38. sdata/did/did_github.py +152 -0
  39. sdata/did/did_peer4.py +155 -0
  40. sdata/did/did_web.py +222 -0
  41. sdata/did/diddoc.py +75 -0
  42. sdata/did/ed25519.py +225 -0
  43. sdata/did/eddsa.py +107 -0
  44. sdata/did/errors.py +40 -0
  45. sdata/did/issue_vc.py +193 -0
  46. sdata/did/jose.py +126 -0
  47. sdata/did/keys.py +104 -0
  48. sdata/did/utils_didkey.py +161 -0
  49. sdata/did/verify_vp.py +158 -0
  50. sdata/doe.py +101 -0
  51. sdata/dtypes.py +510 -0
  52. sdata/experiments/__init__.py +331 -0
  53. sdata/experiments/blob0.py +131 -0
  54. sdata/experiments/file.py +42 -0
  55. sdata/experiments/filegroup.py +144 -0
  56. sdata/experiments/image_old.py +313 -0
  57. sdata/experiments/ks2.py +15 -0
  58. sdata/experiments/lapshear.py +8 -0
  59. sdata/experiments/material.py +7 -0
  60. sdata/experiments/parameters_deprecated.py +394 -0
  61. sdata/experiments/workbook.py +226 -0
  62. sdata/format.py +125 -0
  63. sdata/imagemeta.py +533 -0
  64. sdata/interactive.py +118 -0
  65. sdata/iolib/__init__.py +51 -0
  66. sdata/iolib/hashtree.py +264 -0
  67. sdata/iolib/json1sqlitestore.py +554 -0
  68. sdata/iolib/jsonsqlitestore.py +704 -0
  69. sdata/iolib/pud.py +242 -0
  70. sdata/iolib/reader.py +262 -0
  71. sdata/iolib/writer.py +419 -0
  72. sdata/metadata.py +882 -0
  73. sdata/parameter.py +414 -0
  74. sdata/schema.py +273 -0
  75. sdata/sclass/__init__.py +111 -0
  76. sdata/sclass/bfo_classes.py +362 -0
  77. sdata/sclass/blob.py +362 -0
  78. sdata/sclass/content.py +87 -0
  79. sdata/sclass/dataframe.py +1251 -0
  80. sdata/sclass/dataframegroup.py +187 -0
  81. sdata/sclass/dependency_graph.py +85 -0
  82. sdata/sclass/filereference.py +124 -0
  83. sdata/sclass/image.py +218 -0
  84. sdata/sclass/lazy_namespace.py +155 -0
  85. sdata/sclass/process.py +383 -0
  86. sdata/sclass/process_graph.py +221 -0
  87. sdata/sclass/zipfile.py +135 -0
  88. sdata/semantic.py +346 -0
  89. sdata/suuid.py +136 -0
  90. sdata/timestamp.py +336 -0
  91. sdata/tools.py +14 -0
  92. sdata/units.py +602 -0
  93. sdata/vocab.py +149 -0
  94. sdata-1.5.0.dist-info/METADATA +264 -0
  95. sdata-1.5.0.dist-info/RECORD +98 -0
  96. sdata-1.5.0.dist-info/WHEEL +5 -0
  97. sdata-1.5.0.dist-info/licenses/LICENSE-MIT +21 -0
  98. sdata-1.5.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,396 @@
1
+ from bisect import bisect_left
2
+ from sdata.contrib.ranger.range.range import Range
3
+ from collections import deque
4
+
5
+
6
+ class RangeMap(object):
7
+ """ Class used to represent a mapping of disjoint ranges to some objects.
8
+ Ranges do not coalesce. If a new Range is added over an existing Range,
9
+ it overwrites the overlapping part of the existing Range
10
+ """
11
+
12
+ def __init__(self, range_dict=None):
13
+ """ Instantiates a RangeMap
14
+
15
+ Parameters
16
+ ----------
17
+ range_dict : Dictionary of Range -> object
18
+ Dictionary to start off the RangeMap with. Note that this will
19
+ not be traversed in any particular order, so it may result in
20
+ unexpected behavior if instantiated with any overlapping ranges
21
+ """
22
+ # Holds lower and upper cut points of ranges
23
+ self.lower_cuts = []
24
+ self.upper_cuts = []
25
+ # Holds the actual range objects that are the keys
26
+ self.ranges = []
27
+ # Holds items mapping to each range
28
+ self.items = []
29
+ if range_dict is not None:
30
+ for range_key, val in range_dict.iteritems():
31
+ self.put(range_key, val)
32
+
33
+ def __getitem__(self, key):
34
+ return self.get(key)
35
+
36
+ def __setitem__(self, key, value):
37
+ self.put(key, value)
38
+
39
+ def __delitem__(self, key):
40
+ self.remove(key)
41
+
42
+ def __iter__(self):
43
+ return iter(self.ranges)
44
+
45
+ def __eq__(self, other):
46
+ if not isinstance(other, RangeMap):
47
+ return False
48
+ elif len(self) != len(other):
49
+ return False
50
+ for k1, v1, k2, v2 in zip(self.ranges, self.items,
51
+ other.ranges, other.items):
52
+ if (k1 != k2 or v1 != v2):
53
+ return False
54
+ return True
55
+
56
+ def __ne__(self, other):
57
+ return not self.__eq__(other)
58
+
59
+ def __len__(self):
60
+ return len(self.ranges)
61
+
62
+ def __repr__(self):
63
+ if len(self) < 5:
64
+ return_str = "{%s}" % ", ".join([
65
+ "%s : %s" % (k, v) for k, v in zip(self.ranges, self.items)
66
+ ])
67
+ else:
68
+ return_str = "{%s, ..., %s}" % (", ".join([
69
+ "%s : %s" % (k, v) for k, v in zip(self.ranges[:2], self.items[:2])
70
+ ]), ", ".join([
71
+ "%s : %s" % (k, v) for k, v in zip(self.ranges[-2:], self.items[-2:])
72
+ ])
73
+ )
74
+ return return_str
75
+
76
+ def __missing__(self, key):
77
+ raise KeyError(str(key))
78
+
79
+ def contains(self, val):
80
+ """ Returns true if any of the ranges fully enclose the given
81
+ value, which can be a single value or a Range object
82
+
83
+ Parameters
84
+ ----------
85
+ val : A single value or a Range object
86
+
87
+ Raises
88
+ ------
89
+ ValueError
90
+ If the value type not compatible with the ranges
91
+
92
+ Returns
93
+ -------
94
+ true if any of the ranges fully enclose the given value
95
+ """
96
+ if len(self) == 0: return False
97
+ # Get the index+1 of the highest lower cut <= to the value or its
98
+ # lower cutpoint and check if the value contained
99
+ if isinstance(val, Range):
100
+ lower_ind = max(bisect_left(self.lower_cuts, val.lower_cut) - 1, 0)
101
+ return self.ranges[lower_ind].encloses(val)
102
+ else:
103
+ lower_ind = max(bisect_left(self.lower_cuts, val) - 1, 0)
104
+ return self.ranges[lower_ind].contains(val)
105
+
106
+ def get(self, key):
107
+ """ Get the item(s) corresponding to a given key. The key can be a
108
+ Range or a single value that is within a Range
109
+
110
+ Parameters
111
+ ----------
112
+ key : A single value or Range object
113
+
114
+ Raises
115
+ ------
116
+ KeyError
117
+ If there is no overlap with the key
118
+ ValueError
119
+ If the key type not compatible with the ranges
120
+
121
+ Returns
122
+ -------
123
+ A set containing all overlapping items
124
+ """
125
+ if not self.overlaps(key):
126
+ self.__missing__(key)
127
+ elif isinstance(key, Range):
128
+ # If this is a single value
129
+ return_set = set()
130
+ # Get the bounding indices
131
+ ovlap_lower_ind = max(bisect_left(self.lower_cuts, key.lower_cut) - 1, 0)
132
+ ovlap_upper_ind = bisect_left(self.lower_cuts, key.upper_cut)
133
+ for i in range(ovlap_lower_ind, ovlap_upper_ind):
134
+ try:
135
+ # Get intersection of the ranges
136
+ intersect = key.intersection(self.ranges[i])
137
+ if not intersect.is_empty():
138
+ # If overlapping with this range, put its
139
+ # item in the return set
140
+ return_set.add(self.items[i])
141
+ except ValueError:
142
+ # Continue if no overlap with this range
143
+ continue
144
+ # Return the set of items
145
+ return return_set
146
+ else:
147
+ # If this is a single value
148
+ # Get the index of the range containing the value
149
+ lower_ind = max(bisect_left(self.lower_cuts, key) - 1, 0)
150
+ # Return the item at that value
151
+ return set([self.items[lower_ind]])
152
+
153
+ def overlaps(self, val):
154
+ """ Returns true if any of the ranges at least partially overlap
155
+ the given value, which can be a single value or a Range object
156
+
157
+ Parameters
158
+ ----------
159
+ val : A single value or a Range object
160
+
161
+ Raises:
162
+ -------
163
+ ValueError
164
+ If the value type not compatible with the ranges
165
+
166
+ Returns
167
+ -------
168
+ true if any of the ranges fully enclose the given value
169
+ """
170
+ if len(self) == 0: return False
171
+ # Get the index+1 of the highest lower cut <= to the value or its
172
+ # lower cutpoint and check if the value overlaps
173
+ if isinstance(val, Range):
174
+ lower_ind = bisect_left(self.lower_cuts, val.lower_cut) - 1
175
+ upper_ind = bisect_left(self.lower_cuts, val.upper_cut)
176
+ for i in range(lower_ind, upper_ind):
177
+ if val.is_connected(self.ranges[i]):
178
+ if not self.ranges[i].intersection(val).is_empty():
179
+ return True
180
+ return False
181
+ else:
182
+ lower_ind = bisect_left(self.lower_cuts, val) - 1
183
+ return self.ranges[lower_ind].contains(val)
184
+
185
+ def put(self, key, val):
186
+ """ Creates a mapping from a Range to a value. Note that if the
187
+ key Range overlaps any existing ranges, it will replace those
188
+ Range(s) over the intersection
189
+
190
+ Parameters
191
+ ----------
192
+ key : Range object
193
+ A Range to serve as a key
194
+ val : value
195
+ Some value that the Range should map to
196
+
197
+ Raises
198
+ ------
199
+ TypeError
200
+ If the key is not a Range object
201
+ """
202
+ if not isinstance(key, Range):
203
+ raise TypeError("key is not a Range")
204
+ elif key.is_empty():
205
+ # Skip if this is an empty range
206
+ return
207
+ # Figure out where to the key/value
208
+ if not self.overlaps(key):
209
+ # If this range is completely on its own, just insert
210
+ insert_ind = bisect_left(self.lower_cuts, key.lower_cut)
211
+ self.ranges.insert(insert_ind, key)
212
+ self.lower_cuts.insert(insert_ind, key.lower_cut)
213
+ self.upper_cuts.insert(insert_ind, key.upper_cut)
214
+ self.items.insert(insert_ind, val)
215
+ return
216
+ else:
217
+ # If this range has some overlap with existing ranges
218
+ ovlap_lower_ind = max(bisect_left(self.lower_cuts, key.lower_cut) - 1, 0)
219
+ ovlap_upper_ind = bisect_left(self.lower_cuts, key.upper_cut)
220
+ # Create queue or indices marked for removal
221
+ remove_ranges = deque()
222
+ # Create queue ranges to add
223
+ add_ranges = deque()
224
+ # Create queue of items to add
225
+ add_items = deque()
226
+ for i in range(ovlap_lower_ind, ovlap_upper_ind):
227
+ try:
228
+ # Get intersection of the ranges
229
+ intersect = key.intersection(self.ranges[i])
230
+ if not intersect.is_empty():
231
+ if intersect == self.ranges[i]:
232
+ # Mark range for removal
233
+ remove_ranges.append(i)
234
+ elif self.lower_cuts[i] == intersect.lower_cut:
235
+ # If equal on left cutpoint, subtract out left
236
+ # part
237
+ self.lower_cuts[i] = intersect.upper_cut
238
+ self.ranges[i] = Range(intersect.upper_cut,
239
+ self.upper_cuts[i])
240
+ elif self.upper_cuts[i] == intersect.upper_cut:
241
+ # If equal on right cutpoint, subtract out
242
+ # right part
243
+ self.upper_cuts[i] = intersect.lower_cut
244
+ self.ranges[i] = Range(self.lower_cuts[i],
245
+ intersect.lower_cut)
246
+ else:
247
+ # If in the middle, split into two parts, putting
248
+ # both in add queue and placing the old range index
249
+ # in the remove queue
250
+ add_ranges.append(Range(self.lower_cuts[i],
251
+ intersect.lower_cut))
252
+ add_ranges.append(Range(intersect.upper_cut,
253
+ self.upper_cuts[i]))
254
+ add_items.append(self.items[i])
255
+ add_items.append(self.items[i])
256
+ remove_ranges.append(i)
257
+ except ValueError:
258
+ # Continue if no overlap with this range
259
+ continue
260
+ # Remove any ranges that are marked for removal
261
+ while len(remove_ranges) > 0:
262
+ remove_ind = remove_ranges.pop()
263
+ self.ranges.pop(remove_ind)
264
+ self.lower_cuts.pop(remove_ind)
265
+ self.upper_cuts.pop(remove_ind)
266
+ self.items.pop(remove_ind)
267
+ add_items.append(val)
268
+ add_ranges.append(key)
269
+ # Use recursive call to place the pairs, which now
270
+ # should not overlap with any other ranges
271
+ while len(add_ranges) > 0:
272
+ self.put(add_ranges.pop(), add_items.pop())
273
+
274
+ def remove(self, a_range):
275
+ """ Removes a range and its value from the range set
276
+
277
+ Parameters
278
+ ----------
279
+ a_range : A Range object
280
+ The Range to remove
281
+
282
+ Raises
283
+ ------
284
+ ValueError
285
+ If removing range of type not compatible with previously
286
+ added ranges
287
+ TypeError
288
+ If not a Range
289
+ """
290
+ if not isinstance(a_range, Range):
291
+ raise TypeError("a_range is not a Range")
292
+ elif a_range.is_empty():
293
+ # Skip if this is an empty range
294
+ return
295
+ # Check for compatibility of types if necessary
296
+ if len(self) > 0:
297
+ if not (issubclass(a_range.lower_cut.the_type,
298
+ self.ranges[0].lower_cut.the_type) or \
299
+ issubclass(self.ranges[0].lower_cut.the_type,
300
+ a_range.lower_cut.the_type)):
301
+ raise ValueError("Range not compatible with previously added ranges")
302
+ # Check if the range actually overlaps with the key set
303
+ if not self.overlaps(a_range):
304
+ return
305
+ else:
306
+ # There's some overlap, so deal with that
307
+ # Determine where overlap occurs
308
+ ovlap_lower_ind = max(bisect_left(self.lower_cuts,
309
+ a_range.lower_cut) - 1, 0)
310
+ ovlap_upper_ind = bisect_left(self.lower_cuts, a_range.upper_cut)
311
+ # Create queue of indices marked for removal
312
+ remove_ranges = deque()
313
+ # Create queue of ranges to add
314
+ add_ranges = deque()
315
+ # Create queue of items to add with the add_ranges
316
+ add_items = deque()
317
+ for i in range(ovlap_lower_ind, ovlap_upper_ind):
318
+ try:
319
+ # Get intersection of the ranges
320
+ intersect = a_range.intersection(self.ranges[i])
321
+ if not intersect.is_empty():
322
+ if intersect == self.ranges[i]:
323
+ # Mark range for removal
324
+ remove_ranges.append(i)
325
+ elif self.lower_cuts[i] == intersect.lower_cut:
326
+ # If equal on the left cutpoint, subtract
327
+ # out left part
328
+ self.lower_cuts[i] = intersect.upper_cut
329
+ self.ranges[i] = Range(intersect.upper_cut,
330
+ self.upper_cuts[i])
331
+ elif self.upper_cuts[i] == intersect.upper_cut:
332
+ # If equal on right cutpoint, subtract out
333
+ # right part
334
+ self.upper_cuts[i] = intersect.lower_cut
335
+ self.ranges[i] = Range(self.lower_cuts[i],
336
+ intersect.lower_cut)
337
+ else:
338
+ # If in the middle, split into two parts, putting
339
+ # both in add queue and placing the old range index
340
+ # into the remove queue
341
+ add_ranges.append(Range(self.lower_cuts[i],
342
+ intersect.lower_cut))
343
+ add_items.append(self.items[i])
344
+ add_ranges.append(Range(intersect.upper_cut,
345
+ self.upper_cuts[i]))
346
+ add_items.append(self.items[i])
347
+ remove_ranges.append(i)
348
+ except ValueError:
349
+ # Continue if no overlap with this range
350
+ continue
351
+ # Remove any ranges that are marked for removal
352
+ while len(remove_ranges) > 0:
353
+ remove_ind = remove_ranges.pop()
354
+ self.ranges.pop(remove_ind)
355
+ self.lower_cuts.pop(remove_ind)
356
+ self.upper_cuts.pop(remove_ind)
357
+ self.items.pop(remove_ind)
358
+ # Add any pairs that need to be added
359
+ while len(add_ranges) > 0:
360
+ self.put(add_ranges.pop(), add_items.pop())
361
+
362
+ def which_overlaps(self, val):
363
+ """ Returns which of the Ranges overlap with a single value or
364
+ Range object
365
+
366
+ Parameters
367
+ ----------
368
+ val : A single value or a Range object
369
+
370
+ Raises:
371
+ -------
372
+ ValueError
373
+ If the value type not compatible with the ranges
374
+
375
+ Returns
376
+ -------
377
+ set of ranges overlapping with the value
378
+ """
379
+ if len(self) == 0: return False
380
+ # Get the index+1 of the highest lower cut <= to the value or its
381
+ # lower cutpoint and check if the value overlaps. If it does, add
382
+ # to set
383
+ overlap_set = set()
384
+ if isinstance(val, Range):
385
+ lower_ind = bisect_left(self.lower_cuts, val.lower_cut) - 1
386
+ upper_ind = bisect_left(self.lower_cuts, val.upper_cut)
387
+ for i in range(lower_ind, upper_ind):
388
+ if val.is_connected(self.ranges[i]):
389
+ if not self.ranges[i].intersection(val).is_empty():
390
+ overlap_set.add(self.ranges[i])
391
+ return overlap_set
392
+ else:
393
+ lower_ind = bisect_left(self.lower_cuts, val) - 1
394
+ if self.ranges[lower_ind].contains(val):
395
+ overlap_set.add(self.ranges[lower_ind])
396
+ return overlap_set