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,66 @@
1
+ class UserComment:
2
+ #
3
+ # Names of encodings that we publicly support.
4
+ #
5
+ ASCII = 'ascii'
6
+ JIS = 'jis'
7
+ UNICODE = 'unicode'
8
+ ENCODINGS = (ASCII, JIS, UNICODE)
9
+
10
+ #
11
+ # The actual encodings accepted by the standard library differ slightly from
12
+ # the above.
13
+ #
14
+ _JIS = 'shift_jis'
15
+ _UNICODE = 'utf_16_be'
16
+
17
+ _PREFIX_SIZE = 8
18
+ #
19
+ # From Table 9: Character Codes and their Designation
20
+ #
21
+ _ASCII_PREFIX = b'\x41\x53\x43\x49\x49\x00\x00\x00'
22
+ _JIS_PREFIX = b'\x4a\x49\x53\x00\x00\x00\x00\x00'
23
+ _UNICODE_PREFIX = b'\x55\x4e\x49\x43\x4f\x44\x45\x00'
24
+ _UNDEFINED_PREFIX = b'\x00\x00\x00\x00\x00\x00\x00\x00'
25
+
26
+ @classmethod
27
+ def load(cls, data):
28
+ """
29
+ Convert "UserComment" value in exif format to str.
30
+
31
+ :param bytes data: "UserComment" value from exif
32
+ :return: u"foobar"
33
+ :rtype: str(Unicode)
34
+ :raises: ValueError if the data does not conform to the EXIF specification,
35
+ or the encoding is unsupported.
36
+ """
37
+ if len(data) < cls._PREFIX_SIZE:
38
+ raise ValueError('not enough data to decode UserComment')
39
+ prefix = data[:cls._PREFIX_SIZE]
40
+ body = data[cls._PREFIX_SIZE:]
41
+ if prefix == cls._UNDEFINED_PREFIX:
42
+ raise ValueError('prefix is UNDEFINED, unable to decode UserComment')
43
+ try:
44
+ encoding = {
45
+ cls._ASCII_PREFIX: cls.ASCII, cls._JIS_PREFIX: cls._JIS, cls._UNICODE_PREFIX: cls._UNICODE,
46
+ }[prefix]
47
+ except KeyError:
48
+ raise ValueError('unable to determine appropriate encoding')
49
+ return body.decode(encoding, errors='replace')
50
+
51
+ @classmethod
52
+ def dump(cls, data, encoding="ascii"):
53
+ """
54
+ Convert str to appropriate format for "UserComment".
55
+
56
+ :param data: Like u"foobar"
57
+ :param str encoding: "ascii", "jis", or "unicode"
58
+ :return: b"ASCII\x00\x00\x00foobar"
59
+ :rtype: bytes
60
+ :raises: ValueError if the encoding is unsupported.
61
+ """
62
+ if encoding not in cls.ENCODINGS:
63
+ raise ValueError('encoding {!r} must be one of {!r}'.format(encoding, cls.ENCODINGS))
64
+ prefix = {cls.ASCII: cls._ASCII_PREFIX, cls.JIS: cls._JIS_PREFIX, cls.UNICODE: cls._UNICODE_PREFIX}[encoding]
65
+ internal_encoding = {cls.UNICODE: cls._UNICODE, cls.JIS: cls._JIS}.get(encoding, encoding)
66
+ return prefix + data.encode(internal_encoding, errors='replace')
@@ -0,0 +1,8 @@
1
+ __author__ = 'Eli Rodgers-Melnick'
2
+ __license__ = 'BSD'
3
+
4
+ # Make imports
5
+ from sdata.contrib.ranger.range.range import Range
6
+ from sdata.contrib.ranger.collections.rangeset import RangeSet
7
+ from sdata.contrib.ranger.collections.rangemap import RangeMap
8
+ from sdata.contrib.ranger.collections.rangebucketmap import RangeBucketMap
File without changes
@@ -0,0 +1,346 @@
1
+ from bisect import bisect_left
2
+ from collections import deque
3
+ try: # Python 3.10+ moved ABCs to collections.abc
4
+ from collections.abc import Hashable
5
+ except ImportError: # pragma: no cover - Python < 3.10
6
+ from collections import Hashable
7
+ from sdata.contrib.ranger.collections.rangemap import RangeMap
8
+ from sdata.contrib.ranger.range.range import Range
9
+ from sdata.contrib.ranger.range.cut import Cut
10
+
11
+ class RangeBucketMap(RangeMap):
12
+ """ Class used to represent a mapping of disjoint ranges to sets of items. Ranges
13
+ do not coalesce. However, if a new Range is added over an existing Range, items
14
+ belonging to the existing Range are retained in that Range
15
+ """
16
+
17
+ def __init__(self, range_dict=None):
18
+ """ Instantiates a RangeBucketMap
19
+
20
+ Parameters
21
+ ----------
22
+ range_dict : Dictionary of Range -> object
23
+ Dictionary to start off the RangeBucketMap with
24
+ """
25
+ self.recurse_add = False
26
+ super(RangeBucketMap, self).__init__(range_dict)
27
+
28
+ def iteritems(self, start=None, end=None):
29
+ """ Iterates over pairs of (Range, value)
30
+
31
+ Parameters
32
+ ----------
33
+ start : comparable, optional
34
+ The starting point for iterating, inclusive
35
+ end : comparable, optional
36
+ The ending point for iterating, inclusive
37
+
38
+ Returns
39
+ -------
40
+ Generator of (Range intersecting [start,end], value), ordered by start point
41
+ """
42
+ if start is None:
43
+ start = self.lower_cuts[0]
44
+ else:
45
+ start = Cut.below_value(start)
46
+ if end is None:
47
+ end = self.upper_cuts[-1]
48
+ else:
49
+ end = Cut.above_value(end)
50
+ bounding_range = Range(start, end)
51
+ # Get the bounding indices
52
+ ovlapLowerInd = max(bisect_left(self.lower_cuts, start) - 1, 0)
53
+ ovlapUpperInd = bisect_left(self.lower_cuts, end)
54
+ # Create queue of values that need to be generated
55
+ yield_vals = deque()
56
+ # Create dictionary of values to be generated -> indices containing them
57
+ vals_inds_dict = {}
58
+ for i in range(ovlapLowerInd, ovlapUpperInd):
59
+ # Check if anything can be released from the queue
60
+ while len(yield_vals) > 0:
61
+ if vals_inds_dict[yield_vals[0]][-1] < i - 1:
62
+ # Yield the full range, value. Remove value from queue
63
+ val = yield_vals.popleft()
64
+ yield Range(max(self.lower_cuts[vals_inds_dict[val][0]], start),
65
+ min(self.upper_cuts[vals_inds_dict[val][-1]], end)), val
66
+ # Remove value from dict
67
+ del vals_inds_dict[val]
68
+ else:
69
+ break
70
+ try:
71
+ # Get intersection of the ranges
72
+ intersect = bounding_range.intersection(self.ranges[i])
73
+ if not intersect.is_empty():
74
+ # If overlapping with this range, put into queue
75
+ for val in self.items[i]:
76
+ if val not in vals_inds_dict:
77
+ yield_vals.append(val)
78
+ vals_inds_dict[val] = deque()
79
+ vals_inds_dict[val].append(i)
80
+ except ValueError:
81
+ # Continue if no overlap with this range
82
+ continue
83
+ ## Yield remaining values
84
+ while len(yield_vals) > 0:
85
+ # Yield the full range, value. Remove value from queue
86
+ val = yield_vals.popleft()
87
+ yield Range(max(self.lower_cuts[vals_inds_dict[val][0]], start),
88
+ min(self.upper_cuts[vals_inds_dict[val][-1]], end)), val
89
+ # Remove value from dict
90
+ del vals_inds_dict[val]
91
+
92
+ def get(self, key):
93
+ """ Get the item(s) corresponding to a given key. The key can be a
94
+ Range or a single value that is within a Range
95
+
96
+ Parameters
97
+ ----------
98
+ key : comparable
99
+ A single value or Range object
100
+
101
+ Raises
102
+ ------
103
+ KeyError
104
+ If there is no overlap with the key
105
+ ValueError
106
+ If the key type not compatible with the ranges
107
+
108
+ Returns
109
+ -------
110
+ A set containing all overlapping items
111
+ """
112
+ if not self.overlaps(key):
113
+ self.__missing__(key)
114
+ elif isinstance(key, Range):
115
+ # If this is a single value
116
+ return_set = set()
117
+ # Get the bounding indices
118
+ ovlapLowerInd = max(bisect_left(self.lower_cuts, key.lower_cut) - 1, 0)
119
+ ovlapUpperInd = bisect_left(self.lower_cuts, key.upper_cut)
120
+ for i in range(ovlapLowerInd, ovlapUpperInd):
121
+ try:
122
+ # Get intersection of the ranges
123
+ intersect = key.intersection(self.ranges[i])
124
+ if not intersect.is_empty():
125
+ # If overlapping with this range, put its
126
+ # item in the return set
127
+ return_set = return_set.union(self.items[i])
128
+ except ValueError:
129
+ # Continue if no overlap with this range
130
+ continue
131
+ # Return the set of items
132
+ return return_set
133
+ else:
134
+ # If this is a single value
135
+ # Get the index of the range containing the value
136
+ lower_ind = max(bisect_left(self.lower_cuts, key) - 1, 0)
137
+ # Return the item at that value
138
+ return self.items[lower_ind]
139
+
140
+ def put(self, key, val):
141
+ """ Creates a mapping from a Range to a value, adding to
142
+ any existing values over that Range
143
+
144
+ Parameters
145
+ ----------
146
+ key : Range object
147
+ A Range to serve as a key
148
+ val : value, hashable
149
+ Some value that the Range should map to
150
+
151
+ Raises
152
+ ------
153
+ TypeError
154
+ If the key is not a Range object or value is not hashable
155
+ """
156
+ if not isinstance(key, Range):
157
+ raise TypeError("key is not a Range")
158
+ elif not any((isinstance(val, Hashable), self.recurse_add)):
159
+ raise TypeError("value not hashable")
160
+ elif key.is_empty():
161
+ # Skip if this is an empty range
162
+ return
163
+ # Figure out where to the key/value
164
+ if not self.overlaps(key):
165
+ # If this range is completely on its own, just insert
166
+ insert_ind = bisect_left(self.lower_cuts, key.lower_cut)
167
+ self.ranges.insert(insert_ind, key)
168
+ self.lower_cuts.insert(insert_ind, key.lower_cut)
169
+ self.upper_cuts.insert(insert_ind, key.upper_cut)
170
+ if not isinstance(val, set):
171
+ self.items.insert(insert_ind, set([val]))
172
+ else:
173
+ self.items.insert(insert_ind, val)
174
+ return
175
+ else:
176
+ # If this range has some overlap with existing ranges
177
+ ovlap_lower_ind = max(bisect_left(self.lower_cuts, key.lower_cut) - 1, 0)
178
+ ovlap_upper_ind = bisect_left(self.lower_cuts, key.upper_cut)
179
+ # Create queue ranges to add
180
+ add_ranges = deque()
181
+ # Create queue of items to add
182
+ add_items = deque()
183
+ # Keep track of next lower cutpoint to add
184
+ next_lower_cut = key.lower_cut
185
+ for i in range(ovlap_lower_ind, ovlap_upper_ind):
186
+ try:
187
+ # Get intersection of the ranges
188
+ intersect = key.intersection(self.ranges[i])
189
+ if not intersect.is_empty():
190
+ # Add in a Range between the next LowerCut and
191
+ # the beginning of this intersection if necessary
192
+ if next_lower_cut < intersect.lower_cut:
193
+ add_ranges.append(Range(next_lower_cut, intersect.lower_cut))
194
+ add_items.append(val)
195
+ next_lower_cut = intersect.lower_cut
196
+ if intersect == self.ranges[i]:
197
+ ## If key encompassing existing Range ##
198
+ # Add item to this range
199
+ self.items[i].add(val)
200
+ # Change the next lower cut
201
+ next_lower_cut = intersect.upper_cut
202
+ elif self.lower_cuts[i] == intersect.lower_cut:
203
+ ## If key upper cutpoint enclosed by existing Range ##
204
+ # Add in the rest of the original Range
205
+ if self.upper_cuts[i] > intersect.upper_cut:
206
+ add_ranges.append(Range(intersect.upper_cut,
207
+ self.upper_cuts[i]))
208
+ add_items.append(set(self.items[i]))
209
+ # Define original part to be shorter
210
+ self.upper_cuts[i] = intersect.upper_cut
211
+ self.ranges[i] = Range(self.lower_cuts[i],
212
+ intersect.upper_cut)
213
+ self.items[i].add(val)
214
+ # Change the next lower cut
215
+ next_lower_cut = intersect.upper_cut
216
+ elif self.upper_cuts[i] == intersect.upper_cut:
217
+ ## If key lower cutpoint enclosed by existing Range ##
218
+ # Add in the rest of the original Range
219
+ if intersect.lower_cut > self.lower_cuts[i]:
220
+ add_ranges.append(Range(self.lower_cuts[i], intersect.lower_cut))
221
+ add_items.append(set(self.items[i]))
222
+ # Define original part to be shorter
223
+ self.lower_cuts[i] = intersect.lower_cut
224
+ self.ranges[i] = Range(self.lower_cuts[i],
225
+ intersect.upper_cut)
226
+ self.items[i].add(val)
227
+ # Change the next lower cut
228
+ next_lower_cut = intersect.upper_cut
229
+ else:
230
+ # If entire key enclosed by existing Range
231
+ # Add in lower part of original Range
232
+ add_ranges.append(Range(self.lower_cuts[i], intersect.lower_cut))
233
+ add_items.append(set(self.items[i]))
234
+ # Add in upper part of original Range
235
+ add_ranges.append(Range(intersect.upper_cut, self.upper_cuts[i]))
236
+ add_items.append(set(self.items[i]))
237
+ # Define original part to be middle
238
+ self.lower_cuts[i] = intersect.lower_cut
239
+ self.upper_cuts[i] = intersect.upper_cut
240
+ self.ranges[i] = Range(intersect.lower_cut, intersect.upper_cut)
241
+ self.items[i].add(val)
242
+ # Change the next lower cut
243
+ next_lower_cut = intersect.upper_cut
244
+ except ValueError:
245
+ # Continue if no overlap with this range
246
+ continue
247
+ # Put in a last range if necessary
248
+ if next_lower_cut < key.upper_cut:
249
+ add_ranges.append(Range(next_lower_cut, key.upper_cut))
250
+ add_items.append(val)
251
+ # Use recursive call to place the pairs, which now
252
+ # should not overlap with any other ranges
253
+ self.recurse_add = True
254
+ while len(add_ranges) > 0:
255
+ self.put(add_ranges.pop(), add_items.pop())
256
+ self.recurse_add = False
257
+
258
+ def remove(self, a_range):
259
+ """ Removes a range and its value(s) from the range set
260
+
261
+ Parameters
262
+ ----------
263
+ a_range : A Range object
264
+ The Range to remove
265
+
266
+ Raises
267
+ ------
268
+ ValueError
269
+ If removing range of type not compatible with previously
270
+ added ranges
271
+ TypeError
272
+ If not a Range
273
+ """
274
+ if not isinstance(a_range, Range):
275
+ raise TypeError("a_range is not a Range")
276
+ elif a_range.is_empty():
277
+ # Skip if this is an empty range
278
+ return
279
+ # Check for compatibility of types if necessary
280
+ if len(self) > 0:
281
+ if not (issubclass(a_range.lower_cut.the_type,
282
+ self.ranges[0].lower_cut.the_type) or \
283
+ issubclass(self.ranges[0].lower_cut.the_type,
284
+ a_range.lower_cut.the_type)):
285
+ raise ValueError("Range not compatible with previously added ranges")
286
+ # Check if the range actually overlaps with the key set
287
+ if not self.overlaps(a_range):
288
+ return
289
+ else:
290
+ # There's some overlap, so deal with that
291
+ # Determine where overlap occurs
292
+ ovlap_lower_ind = max(bisect_left(self.lower_cuts,
293
+ a_range.lower_cut) - 1, 0)
294
+ ovlap_upper_ind = bisect_left(self.lower_cuts, a_range.upper_cut)
295
+ # Create queue of indices marked for removal
296
+ remove_ranges = deque()
297
+ # Create queue of ranges to add
298
+ add_ranges = deque()
299
+ # Create queue of items to add with the add_ranges
300
+ add_items = deque()
301
+ for i in range(ovlap_lower_ind, ovlap_upper_ind):
302
+ try:
303
+ # Get intersection of the ranges
304
+ intersect = a_range.intersection(self.ranges[i])
305
+ if not intersect.is_empty():
306
+ if intersect == self.ranges[i]:
307
+ # Mark range for removal
308
+ remove_ranges.append(i)
309
+ elif self.lower_cuts[i] == intersect.lower_cut:
310
+ # If equal on the left cutpoint, subtract
311
+ # out left part
312
+ self.lower_cuts[i] = intersect.upper_cut
313
+ self.ranges[i] = Range(intersect.upper_cut,
314
+ self.upper_cuts[i])
315
+ elif self.upper_cuts[i] == intersect.upper_cut:
316
+ # If equal on right cutpoint, subtract out
317
+ # right part
318
+ self.upper_cuts[i] = intersect.lower_cut
319
+ self.ranges[i] = Range(self.lower_cuts[i],
320
+ intersect.lower_cut)
321
+ else:
322
+ # If in the middle, split into two parts, putting
323
+ # both in add queue and placing the old range index
324
+ # into the remove queue
325
+ add_ranges.append(Range(self.lower_cuts[i],
326
+ intersect.lower_cut))
327
+ add_items.append(set(self.items[i]))
328
+ add_ranges.append(Range(intersect.upper_cut,
329
+ self.upper_cuts[i]))
330
+ add_items.append(set(self.items[i]))
331
+ remove_ranges.append(i)
332
+ except ValueError:
333
+ # Continue if no overlap with this range
334
+ continue
335
+ # Remove any ranges that are marked for removal
336
+ while len(remove_ranges) > 0:
337
+ remove_ind = remove_ranges.pop()
338
+ self.ranges.pop(remove_ind)
339
+ self.lower_cuts.pop(remove_ind)
340
+ self.upper_cuts.pop(remove_ind)
341
+ self.items.pop(remove_ind)
342
+ # Add any pairs that need to be added
343
+ self.recurse_add = True
344
+ while len(add_ranges) > 0:
345
+ self.put(add_ranges.pop(), add_items.pop())
346
+ self.recurse_add = False