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.
@@ -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
+