bigraph-schema 1.0.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.
- bigraph_schema/__init__.py +112 -0
- bigraph_schema/core.py +759 -0
- bigraph_schema/edge.py +138 -0
- bigraph_schema/methods/__init__.py +12 -0
- bigraph_schema/methods/apply.py +276 -0
- bigraph_schema/methods/check.py +213 -0
- bigraph_schema/methods/default.py +204 -0
- bigraph_schema/methods/generalize.py +309 -0
- bigraph_schema/methods/handle_parameters.py +182 -0
- bigraph_schema/methods/infer.py +217 -0
- bigraph_schema/methods/jump.py +432 -0
- bigraph_schema/methods/merge.py +405 -0
- bigraph_schema/methods/realize.py +527 -0
- bigraph_schema/methods/resolve.py +692 -0
- bigraph_schema/methods/serialize.py +491 -0
- bigraph_schema/methods/validate.py +249 -0
- bigraph_schema/package/__init__.py +1 -0
- bigraph_schema/package/discover.py +122 -0
- bigraph_schema/parse.py +183 -0
- bigraph_schema/protocols.py +57 -0
- bigraph_schema/schema.py +370 -0
- bigraph_schema/units.py +133 -0
- bigraph_schema-1.0.0.dist-info/METADATA +66 -0
- bigraph_schema-1.0.0.dist-info/RECORD +28 -0
- bigraph_schema-1.0.0.dist-info/WHEEL +5 -0
- bigraph_schema-1.0.0.dist-info/licenses/AUTHORS.md +6 -0
- bigraph_schema-1.0.0.dist-info/licenses/LICENSE +201 -0
- bigraph_schema-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
from plum import dispatch
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
from dataclasses import replace, dataclass
|
|
5
|
+
|
|
6
|
+
from bigraph_schema.schema import (
|
|
7
|
+
Node,
|
|
8
|
+
Atom,
|
|
9
|
+
Empty,
|
|
10
|
+
Union,
|
|
11
|
+
Tuple,
|
|
12
|
+
Boolean,
|
|
13
|
+
Number,
|
|
14
|
+
Integer,
|
|
15
|
+
Float,
|
|
16
|
+
Delta,
|
|
17
|
+
Nonnegative,
|
|
18
|
+
String,
|
|
19
|
+
Enum,
|
|
20
|
+
Wrap,
|
|
21
|
+
Maybe,
|
|
22
|
+
Overwrite,
|
|
23
|
+
List,
|
|
24
|
+
Map,
|
|
25
|
+
Tree,
|
|
26
|
+
Array,
|
|
27
|
+
Key,
|
|
28
|
+
Path,
|
|
29
|
+
Wires,
|
|
30
|
+
Schema,
|
|
31
|
+
Link,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
from bigraph_schema.methods.check import check
|
|
35
|
+
from bigraph_schema.methods.default import default
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dispatch
|
|
39
|
+
def merge(schema: Empty, current, update, path=()):
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dispatch
|
|
44
|
+
def merge(schema: Maybe, current, update, path=()):
|
|
45
|
+
if update is None:
|
|
46
|
+
return current
|
|
47
|
+
elif current is None:
|
|
48
|
+
return update
|
|
49
|
+
else:
|
|
50
|
+
return merge(
|
|
51
|
+
schema._value,
|
|
52
|
+
current,
|
|
53
|
+
update,
|
|
54
|
+
path=path)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dispatch
|
|
58
|
+
def merge(schema: Wrap, current, update, path=()):
|
|
59
|
+
return merge(schema._value, current, update, path=path)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dispatch
|
|
63
|
+
def merge(schema: Union, current, update, path=()):
|
|
64
|
+
current_option = None
|
|
65
|
+
update_option = None
|
|
66
|
+
for option in schema._options:
|
|
67
|
+
if not current_option and check(option, current):
|
|
68
|
+
current_option = option
|
|
69
|
+
if not update_option and check(option, update):
|
|
70
|
+
update_option = option
|
|
71
|
+
if current_option and update_option:
|
|
72
|
+
break
|
|
73
|
+
|
|
74
|
+
if current_option == update_option:
|
|
75
|
+
return merge(current_option, current, update, path=path)
|
|
76
|
+
else:
|
|
77
|
+
return update
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dispatch
|
|
81
|
+
def merge(schema: Tuple, current, update, path=()):
|
|
82
|
+
if path:
|
|
83
|
+
head = path[0]
|
|
84
|
+
if head >= len(schema._values):
|
|
85
|
+
raise Exception(f'trying to merge at path {path} but index {head} is out of range:\n\n{schema}\n\n{current}\n\n{update}\n\n')
|
|
86
|
+
|
|
87
|
+
subschema = schema._values[head]
|
|
88
|
+
field = list(current)
|
|
89
|
+
field[head] = merge(subschema, current[head], update, path[1:])
|
|
90
|
+
return tuple(field)
|
|
91
|
+
|
|
92
|
+
else:
|
|
93
|
+
if update is None:
|
|
94
|
+
return current
|
|
95
|
+
if current is None:
|
|
96
|
+
return update
|
|
97
|
+
|
|
98
|
+
return tuple([
|
|
99
|
+
merge(schema_value, current_value, update_value)
|
|
100
|
+
for schema_value, current_value, update_value, index in zip(schema._values, current, update, range(len(schema._values)))])
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dispatch
|
|
104
|
+
def merge(schema: List, current, update, path=()):
|
|
105
|
+
if path:
|
|
106
|
+
head = path[0]
|
|
107
|
+
subschema = schema._element
|
|
108
|
+
current[head] = merge(subschema, current[head], update, path[1:])
|
|
109
|
+
return current
|
|
110
|
+
|
|
111
|
+
elif current is None:
|
|
112
|
+
return update
|
|
113
|
+
|
|
114
|
+
elif update is None:
|
|
115
|
+
return current
|
|
116
|
+
|
|
117
|
+
else:
|
|
118
|
+
return current + update
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@dispatch
|
|
122
|
+
def merge(schema: Map, current, update, path=()):
|
|
123
|
+
if path:
|
|
124
|
+
head = path[0]
|
|
125
|
+
|
|
126
|
+
if head == '*':
|
|
127
|
+
current = current or {}
|
|
128
|
+
for key, value in update.items():
|
|
129
|
+
current[key] = merge(
|
|
130
|
+
schema._value,
|
|
131
|
+
current.get(key),
|
|
132
|
+
value,
|
|
133
|
+
path[1:])
|
|
134
|
+
|
|
135
|
+
return current
|
|
136
|
+
|
|
137
|
+
else:
|
|
138
|
+
subschema = schema._value
|
|
139
|
+
current[head] = merge(subschema, current[head], update, path[1:])
|
|
140
|
+
return current
|
|
141
|
+
|
|
142
|
+
result = {}
|
|
143
|
+
if current is None:
|
|
144
|
+
return update
|
|
145
|
+
|
|
146
|
+
elif update is None:
|
|
147
|
+
return current
|
|
148
|
+
|
|
149
|
+
else:
|
|
150
|
+
for key in current.keys() | update.keys():
|
|
151
|
+
if key in update:
|
|
152
|
+
if key in current:
|
|
153
|
+
if not key.startswith('_'):
|
|
154
|
+
result[key] = merge(
|
|
155
|
+
schema._value,
|
|
156
|
+
current[key],
|
|
157
|
+
update[key])
|
|
158
|
+
else:
|
|
159
|
+
result[key] = update[key]
|
|
160
|
+
else:
|
|
161
|
+
result[key] = current[key]
|
|
162
|
+
|
|
163
|
+
return result
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@dispatch
|
|
167
|
+
def merge(schema: Tree, current, update, path=()):
|
|
168
|
+
if path:
|
|
169
|
+
head = path[0]
|
|
170
|
+
current[head] = merge(schema, current[head], update, path[1:])
|
|
171
|
+
return current
|
|
172
|
+
|
|
173
|
+
current_leaf = check(schema._leaf, current)
|
|
174
|
+
update_leaf = check(schema._leaf, update)
|
|
175
|
+
|
|
176
|
+
if current_leaf and update_leaf:
|
|
177
|
+
return merge(schema._leaf, current, update)
|
|
178
|
+
elif not current_leaf and not update_leaf:
|
|
179
|
+
result = {}
|
|
180
|
+
for key in current.keys() | update.keys():
|
|
181
|
+
if key in update:
|
|
182
|
+
if key in current:
|
|
183
|
+
result[key] = merge(
|
|
184
|
+
schema,
|
|
185
|
+
current[key],
|
|
186
|
+
update[key])
|
|
187
|
+
else:
|
|
188
|
+
result[key] = update[key]
|
|
189
|
+
else:
|
|
190
|
+
result[key] = current[key]
|
|
191
|
+
return result
|
|
192
|
+
else:
|
|
193
|
+
return update
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@dispatch
|
|
197
|
+
def merge(schema: Array, current, update, path=()):
|
|
198
|
+
if path:
|
|
199
|
+
if current is None:
|
|
200
|
+
current = default(schema)
|
|
201
|
+
|
|
202
|
+
head = path[0]
|
|
203
|
+
if head == '*':
|
|
204
|
+
for index, row, subupdate in zip(range(current.shape[0]), current, update):
|
|
205
|
+
subschema = replace(schema, **{'_shape': schema._shape[1:]})
|
|
206
|
+
current[index] = merge(subschema, row, subupdate, path=path[1:])
|
|
207
|
+
|
|
208
|
+
else:
|
|
209
|
+
row = current[head]
|
|
210
|
+
subschema = replace(schema, **{'_shape': schema._shape[1:]})
|
|
211
|
+
subvalue = merge(subschema, row, update, path=path[1:])
|
|
212
|
+
current[head] = subvalue
|
|
213
|
+
|
|
214
|
+
return current
|
|
215
|
+
|
|
216
|
+
# TODO: more sophisticated merge for arrays?
|
|
217
|
+
if update is None:
|
|
218
|
+
if current is None:
|
|
219
|
+
return default(schema)
|
|
220
|
+
else:
|
|
221
|
+
return current
|
|
222
|
+
else:
|
|
223
|
+
return update
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@dispatch
|
|
227
|
+
def merge(schema: Atom, current, update, path=()):
|
|
228
|
+
result = None
|
|
229
|
+
if update and update is not None:
|
|
230
|
+
result = update
|
|
231
|
+
elif current and current is not None:
|
|
232
|
+
result = current
|
|
233
|
+
# if update and update is not None:
|
|
234
|
+
# result = update
|
|
235
|
+
# elif current and current is not None:
|
|
236
|
+
# result = current
|
|
237
|
+
else:
|
|
238
|
+
result = default(schema)
|
|
239
|
+
|
|
240
|
+
return result
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
@dispatch
|
|
244
|
+
def merge(schema: Wires, current, update, path=()):
|
|
245
|
+
return update
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@dispatch
|
|
249
|
+
def merge(schema: Link, current, update, path=()):
|
|
250
|
+
if not current:
|
|
251
|
+
return update
|
|
252
|
+
if not update:
|
|
253
|
+
return current
|
|
254
|
+
|
|
255
|
+
result = {}
|
|
256
|
+
for key in schema.__dataclass_fields__:
|
|
257
|
+
if not key.startswith('_'):
|
|
258
|
+
down = getattr(schema, key)
|
|
259
|
+
result[key] = merge(
|
|
260
|
+
down,
|
|
261
|
+
current.get(key),
|
|
262
|
+
update.get(key))
|
|
263
|
+
|
|
264
|
+
return result
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
@dispatch
|
|
268
|
+
def merge(schema: Node, current, update, path=()):
|
|
269
|
+
if path:
|
|
270
|
+
head = path[0]
|
|
271
|
+
subschema = None
|
|
272
|
+
if hasattr(schema, head):
|
|
273
|
+
subschema = getattr(schema, head)
|
|
274
|
+
|
|
275
|
+
if isinstance(current, dict):
|
|
276
|
+
substate = current.get(head)
|
|
277
|
+
current[head] = merge(subschema, substate, update, path[1:])
|
|
278
|
+
return current
|
|
279
|
+
else:
|
|
280
|
+
substate = None
|
|
281
|
+
if hasattr(current, head):
|
|
282
|
+
substate = getattr(current, head)
|
|
283
|
+
submerge = merge(subschema, substate, update, path[1:])
|
|
284
|
+
setattr(current, head, submerge)
|
|
285
|
+
return current
|
|
286
|
+
|
|
287
|
+
elif isinstance(current, dict) and isinstance(update, dict):
|
|
288
|
+
down = {}
|
|
289
|
+
for key in schema.__dataclass_fields__:
|
|
290
|
+
down[key] = getattr(schema, key)
|
|
291
|
+
return merge(down, current, update)
|
|
292
|
+
|
|
293
|
+
else:
|
|
294
|
+
# result = merge(
|
|
295
|
+
# Atom(),
|
|
296
|
+
# current,
|
|
297
|
+
# update)
|
|
298
|
+
|
|
299
|
+
result = None
|
|
300
|
+
if update is not None:
|
|
301
|
+
result = update
|
|
302
|
+
elif current is not None:
|
|
303
|
+
result = current
|
|
304
|
+
|
|
305
|
+
if result is None:
|
|
306
|
+
result = default(schema)
|
|
307
|
+
|
|
308
|
+
return result
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
@dispatch
|
|
312
|
+
def merge(schema, current, update, path=()):
|
|
313
|
+
return update
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def is_empty(value):
|
|
317
|
+
if isinstance(value, np.ndarray):
|
|
318
|
+
return False
|
|
319
|
+
else:
|
|
320
|
+
return not value
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def tuplify_dict(d):
|
|
324
|
+
if isinstance(d, dict):
|
|
325
|
+
tulip = []
|
|
326
|
+
for key, value in d.items():
|
|
327
|
+
tulip.append((
|
|
328
|
+
key,
|
|
329
|
+
tuplify_dict(value)))
|
|
330
|
+
else:
|
|
331
|
+
return d
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
@dispatch
|
|
335
|
+
def merge(schema: dict, current, update, path=()):
|
|
336
|
+
if path:
|
|
337
|
+
head = path[0]
|
|
338
|
+
if head == '*':
|
|
339
|
+
import ipdb; ipdb.set_trace()
|
|
340
|
+
else:
|
|
341
|
+
current = current or {}
|
|
342
|
+
subschema = schema.get(head)
|
|
343
|
+
substate = current.get(head)
|
|
344
|
+
current[head] = merge(subschema, substate, update, path[1:])
|
|
345
|
+
return current
|
|
346
|
+
|
|
347
|
+
result = {}
|
|
348
|
+
if is_empty(current):
|
|
349
|
+
return update
|
|
350
|
+
if is_empty(update):
|
|
351
|
+
return current
|
|
352
|
+
|
|
353
|
+
if isinstance(update, np.ndarray):
|
|
354
|
+
return update
|
|
355
|
+
elif not isinstance(update, dict):
|
|
356
|
+
return current
|
|
357
|
+
|
|
358
|
+
for key in schema.keys() | current.keys() | update.keys():
|
|
359
|
+
if key in schema:
|
|
360
|
+
result[key] = merge(
|
|
361
|
+
schema[key],
|
|
362
|
+
current.get(key),
|
|
363
|
+
update.get(key))
|
|
364
|
+
elif key in update:
|
|
365
|
+
result[key] = update[key]
|
|
366
|
+
elif key in current:
|
|
367
|
+
result[key] = current[key]
|
|
368
|
+
else:
|
|
369
|
+
raise Exception('logically impossible')
|
|
370
|
+
|
|
371
|
+
if key in schema and schema[key] and result[key] is None:
|
|
372
|
+
if key.startswith('_'):
|
|
373
|
+
result[key] = schema[key]
|
|
374
|
+
else:
|
|
375
|
+
result[key] = default(
|
|
376
|
+
schema[key])
|
|
377
|
+
|
|
378
|
+
if result[key] is None:
|
|
379
|
+
del result[key]
|
|
380
|
+
|
|
381
|
+
return result
|
|
382
|
+
|
|
383
|
+
def merge_update(schema, current, update, path=()):
|
|
384
|
+
if path:
|
|
385
|
+
import ipdb; ipdb.set_trace()
|
|
386
|
+
|
|
387
|
+
current_state = default(current)
|
|
388
|
+
update_state = default(update)
|
|
389
|
+
state = current_state
|
|
390
|
+
|
|
391
|
+
if update_state:
|
|
392
|
+
if current_state:
|
|
393
|
+
state = merge(schema, current_state, update_state)
|
|
394
|
+
else:
|
|
395
|
+
state = update_state
|
|
396
|
+
|
|
397
|
+
if isinstance(schema, Node):
|
|
398
|
+
schema = replace(schema, _default=state)
|
|
399
|
+
elif isinstance(schema, dict):
|
|
400
|
+
schema['_default'] = state
|
|
401
|
+
else:
|
|
402
|
+
raise Exception(f'do not recognize schema: {schema}')
|
|
403
|
+
return schema
|
|
404
|
+
|
|
405
|
+
|