graphddb-runtime 1.0.7__tar.gz → 1.0.9__tar.gz
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.
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/PKG-INFO +2 -2
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime/behavior_exec.py +70 -46
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime/runtime.py +11 -31
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime/transactions.py +0 -30
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime.egg-info/PKG-INFO +2 -2
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime.egg-info/requires.txt +1 -1
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/pyproject.toml +2 -2
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/README.md +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime/__init__.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime/async_runtime.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime/batch.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime/concurrency.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime/cursor.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime/errors.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime/filters.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime/handler_registry.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime/hydration.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime/limits.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime/middleware.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime/per_key_cursor.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime/templates.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime.egg-info/SOURCES.txt +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime.egg-info/dependency_links.txt +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime.egg-info/top_level.txt +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/setup.cfg +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_atomic_add_command.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_concurrency.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_contract_runtime.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_crosslang_python_cells.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_description.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_description_followups.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_description_jsdoc.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_error_policy.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_generated_module.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_guard_eval.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_handler_registry.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_integration.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_integration_batch_write.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_integration_binding_contract.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_integration_command.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_integration_compose.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_integration_contract.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_integration_edge_derive.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_integration_edge_write.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_integration_events.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_integration_maintain.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_integration_middleware.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_integration_referential.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_integration_relations.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_integration_unique.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_maintain.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_middleware.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_relations.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_spec_version_guard.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_ttl.py +0 -0
- {graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_unit.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: graphddb-runtime
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.9
|
|
4
4
|
Summary: Thin DynamoDB executor for GraphDDB-generated Python repositories (single-operation core, issue #44).
|
|
5
5
|
License: MIT
|
|
6
6
|
Requires-Python: >=3.9
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
Requires-Dist: boto3>=1.26
|
|
9
|
-
Requires-Dist: behavior-contracts==0.7.
|
|
9
|
+
Requires-Dist: behavior-contracts==0.7.3
|
|
10
10
|
Provides-Extra: test
|
|
11
11
|
Requires-Dist: pytest>=7.0; extra == "test"
|
|
12
12
|
|
|
@@ -122,16 +122,22 @@ _READ_META_PORTS = frozenset(
|
|
|
122
122
|
"filter",
|
|
123
123
|
"projection",
|
|
124
124
|
"cardinality",
|
|
125
|
-
"implicitSourceField",
|
|
126
125
|
)
|
|
127
126
|
)
|
|
128
127
|
|
|
129
128
|
|
|
130
129
|
def _node_parts(node: Mapping[str, Any]) -> Tuple[str, Optional[Mapping[str, Any]], Mapping[str, Any]]:
|
|
131
|
-
"""``(id, map|None, ports)`` of a body node (componentRef
|
|
130
|
+
"""``(id, map|None, ports)`` of a body node (componentRef / map / fanout)."""
|
|
132
131
|
m = node.get("map")
|
|
133
132
|
if m is not None:
|
|
134
133
|
return node["id"], m, m["ports"]
|
|
134
|
+
fo = node.get("fanout")
|
|
135
|
+
if fo is not None:
|
|
136
|
+
# A `refs` fan-out (bc 0.7.3 v3): its per-element BatchGet ports drive the
|
|
137
|
+
# SAME batched handler a batched map did. bc's `run_behavior` owns the
|
|
138
|
+
# dedupe/drop/wrap (fanout_dedup_drop) — graphddb only supplies the aligned
|
|
139
|
+
# bodies. Return its ports for the structural/config consumers.
|
|
140
|
+
return node["id"], None, fo["ports"]
|
|
135
141
|
return node["id"], None, node["ports"]
|
|
136
142
|
|
|
137
143
|
|
|
@@ -240,11 +246,6 @@ def execute_read_component(
|
|
|
240
246
|
"is_root": node_id == "root",
|
|
241
247
|
}
|
|
242
248
|
|
|
243
|
-
# Batched fan-out bookkeeping for the refs fold: node id → the per-element
|
|
244
|
-
# dedupe markers, ALIGNED to the map's driver elements (first-seen dedupe /
|
|
245
|
-
# missing-drop needs the element→key identity the handler computed).
|
|
246
|
-
refs_markers: Dict[str, List[str]] = {}
|
|
247
|
-
|
|
248
249
|
def _key_of(ports: Mapping[str, Any], key_attrs: List[str]) -> Dict[str, Any]:
|
|
249
250
|
return {attr: ports[attr] for attr in key_attrs}
|
|
250
251
|
|
|
@@ -381,8 +382,12 @@ def execute_read_component(
|
|
|
381
382
|
ports["items"] if batched else [ports] # type: ignore[assignment]
|
|
382
383
|
)
|
|
383
384
|
|
|
384
|
-
#
|
|
385
|
-
#
|
|
385
|
+
# PHYSICAL dedupe INSIDE the handler (CONSUMER) — the ONE deduped/chunked
|
|
386
|
+
# BatchGet the request needs to avoid over-fetch. The RESULT-level dedupe /
|
|
387
|
+
# dangling-drop / connection-wrap is NO LONGER graphddb's: bc's `fanout`
|
|
388
|
+
# node (v3, run_behavior → fanout_dedup_drop) owns it, keyed off the result
|
|
389
|
+
# body's `dedupeKey` field. The handler returns the list ALIGNED to the
|
|
390
|
+
# input element order (null for a missing body) — the fanout's contract.
|
|
386
391
|
plain_keys: List[Dict[str, Any]] = []
|
|
387
392
|
markers: List[str] = []
|
|
388
393
|
seen: set = set()
|
|
@@ -394,8 +399,6 @@ def execute_read_component(
|
|
|
394
399
|
continue
|
|
395
400
|
seen.add(marker)
|
|
396
401
|
plain_keys.append(plain_key)
|
|
397
|
-
if batched:
|
|
398
|
-
refs_markers[ctx["nodeId"]] = markers
|
|
399
402
|
|
|
400
403
|
if len(plain_keys) > rt._limits.max_items:
|
|
401
404
|
raise LimitExceededError(
|
|
@@ -491,25 +494,30 @@ def execute_read_component(
|
|
|
491
494
|
if exc.code == "OP_FAILED" and ddb_error:
|
|
492
495
|
raise ddb_error[-1]
|
|
493
496
|
raise
|
|
494
|
-
return _fold_read_output(component, out
|
|
497
|
+
return _fold_read_output(component, out)
|
|
495
498
|
|
|
496
499
|
|
|
497
500
|
def _fold_read_output(
|
|
498
501
|
component: Mapping[str, Any],
|
|
499
502
|
out: Any,
|
|
500
|
-
refs_markers: Mapping[str, List[str]],
|
|
501
503
|
) -> Any:
|
|
502
|
-
"""The documented mechanical, id-driven fold from the
|
|
504
|
+
"""The documented mechanical, id-driven fold from the v3 ``run_behavior``
|
|
503
505
|
output to the old assembled result shape (components.ts module doc; proven
|
|
504
506
|
golden-equal by ``__tests__/spec/components.test.ts``):
|
|
505
507
|
|
|
506
508
|
- a chained connection's flat per-element ``into`` keys re-nest along the
|
|
507
509
|
node-id path (``groups.items.group.permissions.items`` → move each
|
|
508
510
|
element's ``permissions`` under its ``group``);
|
|
509
|
-
- a
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
511
|
+
- a ``refs`` fan-out node's connection ``{items, cursor: None}`` is produced
|
|
512
|
+
ENTIRELY by bc's ``fanout`` node (v3, ``fanout_dedup_drop``): first-seen
|
|
513
|
+
dedupe (by the result body's ``dedupeKey`` field), dangling drop, and the
|
|
514
|
+
connection wrap all happen inside ``run_behavior`` now — graphddb folds it
|
|
515
|
+
through unchanged (the dedup/drop is no longer here);
|
|
516
|
+
- the implicitly-projected ``refs`` SOURCE id-list (``tagRefs``) is stripped
|
|
517
|
+
from the assembled root: it is a bare-return field the fanout ``over`` read,
|
|
518
|
+
absent from the caller-visible ``outputType`` (the annotation omits it), so
|
|
519
|
+
any root field NOT in ``outputType.root`` is dropped (the old
|
|
520
|
+
``_strip_implicit_sources``, now derived from the type-notation).
|
|
513
521
|
"""
|
|
514
522
|
output = component["output"]
|
|
515
523
|
body: List[Mapping[str, Any]] = list(component["body"])
|
|
@@ -535,23 +543,10 @@ def _fold_read_output(
|
|
|
535
543
|
)
|
|
536
544
|
val = out_obj[prop]
|
|
537
545
|
if "ref" in spec_node:
|
|
538
|
-
|
|
539
|
-
node
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
# Batched refs fan-out: aligned list → dedupe/drop → connection.
|
|
543
|
-
markers = refs_markers.get(node_id, [])
|
|
544
|
-
seen: set = set()
|
|
545
|
-
items: List[Any] = []
|
|
546
|
-
for i, item_body in enumerate(val or []):
|
|
547
|
-
marker = markers[i]
|
|
548
|
-
if item_body is None or marker in seen:
|
|
549
|
-
continue
|
|
550
|
-
seen.add(marker)
|
|
551
|
-
items.append(item_body)
|
|
552
|
-
result[prop] = {"items": items, "cursor": None}
|
|
553
|
-
else:
|
|
554
|
-
result[prop] = val
|
|
546
|
+
# A `refs` fan-out node result is ALREADY the deduped/dropped
|
|
547
|
+
# `{items, cursor: None}` connection (bc's fanout node owns it); a
|
|
548
|
+
# plain relation ref is its value verbatim. Either way: pass through.
|
|
549
|
+
result[prop] = val
|
|
555
550
|
elif "obj" in spec_node:
|
|
556
551
|
# Chained connection: {cursor, items:<augmented tail>} — re-nest the
|
|
557
552
|
# per-element flat `into` keys along each chain map's id.
|
|
@@ -565,13 +560,38 @@ def _fold_read_output(
|
|
|
565
560
|
f"components[]: unsupported output node for '{prop}' (fail-closed)"
|
|
566
561
|
)
|
|
567
562
|
|
|
568
|
-
# Implicit-strip
|
|
569
|
-
#
|
|
570
|
-
#
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
563
|
+
# Implicit refs-source strip (gap 5): a `refs` fan-out's SOURCE id-list
|
|
564
|
+
# (`tagRefs`) is physically projected onto the root ONLY to feed the fanout
|
|
565
|
+
# `over`; it is a bare-return field the caller never sees. The type-notation
|
|
566
|
+
# annotation encodes exactly this: the root NODE's `outType` carries the
|
|
567
|
+
# source, while the component `outputType.root` OMITS it. So strip precisely
|
|
568
|
+
# the field(s) a root-level `fanout.over` reads that are ABSENT from
|
|
569
|
+
# `outputType.root` (a field the query SELECTS stays in `outputType.root` and
|
|
570
|
+
# is kept). Untyped IR (legacy/hand-built, no annotation) strips nothing —
|
|
571
|
+
# the pre-#347 behavior; nested/non-root fanout over is emitter-rejected.
|
|
572
|
+
output_type = component.get("outputType")
|
|
573
|
+
root_out = (
|
|
574
|
+
output_type.get("obj", {}).get("root")
|
|
575
|
+
if isinstance(output_type, Mapping)
|
|
576
|
+
else None
|
|
577
|
+
)
|
|
578
|
+
kept = (
|
|
579
|
+
set(root_out["obj"].keys())
|
|
580
|
+
if isinstance(root_out, Mapping) and isinstance(root_out.get("obj"), Mapping)
|
|
581
|
+
else None
|
|
582
|
+
)
|
|
583
|
+
if kept is not None:
|
|
584
|
+
for node in body:
|
|
585
|
+
fo = node.get("fanout")
|
|
586
|
+
if fo is None:
|
|
587
|
+
continue
|
|
588
|
+
over = fo.get("over")
|
|
589
|
+
ref = over.get("ref") if isinstance(over, Mapping) else None
|
|
590
|
+
# `over: {ref: ["root", "<source>"]}` — the implicit driver field.
|
|
591
|
+
if isinstance(ref, list) and len(ref) == 2 and ref[0] == "root":
|
|
592
|
+
source = ref[1]
|
|
593
|
+
if source not in kept:
|
|
594
|
+
result.pop(source, None)
|
|
575
595
|
|
|
576
596
|
return result
|
|
577
597
|
|
|
@@ -735,10 +755,14 @@ def collect_transaction_items(
|
|
|
735
755
|
|
|
736
756
|
if component_name == "PutItem":
|
|
737
757
|
plain = _group(ports, "item")
|
|
738
|
-
# A
|
|
739
|
-
#
|
|
740
|
-
|
|
741
|
-
|
|
758
|
+
# A modeled Put's PK/SK/GSI key attributes now ride as DECLARATIVE
|
|
759
|
+
# `key.*` ports (attribute → evaluated `concat`/`ref` template — the
|
|
760
|
+
# same symbolic derivation reads/updates use, issue #350), so fold
|
|
761
|
+
# them into the written item directly. NO runtime manifest key
|
|
762
|
+
# derivation. A raw marker-row write (literalKey) carries its PK/SK
|
|
763
|
+
# verbatim in `item` and emits no `key.*` group, so the fold is a
|
|
764
|
+
# no-op for it.
|
|
765
|
+
plain.update(_group(ports, "key"))
|
|
742
766
|
serialized = {k: rt._serializer.serialize(v) for k, v in plain.items()}
|
|
743
767
|
put: Dict[str, Any] = {"TableName": table, "Item": serialized}
|
|
744
768
|
expander._apply_condition(cond_spec, put, cond_params, None)
|
|
@@ -1951,7 +1951,12 @@ class GraphDDBRuntime:
|
|
|
1951
1951
|
field: self._serializer.serialize(resolve_value_template(tmpl, params))
|
|
1952
1952
|
for field, tmpl in spec.get("item", {}).items()
|
|
1953
1953
|
}
|
|
1954
|
-
|
|
1954
|
+
# A put's PK/SK/GSI key attributes ride as DECLARATIVE key
|
|
1955
|
+
# templates on `keyCondition` (the same symbolic derivation
|
|
1956
|
+
# read/update/delete use, issue #350); resolve + fold — NO runtime
|
|
1957
|
+
# manifest key derivation.
|
|
1958
|
+
for attr, tmpl in spec.get("keyCondition", {}).items():
|
|
1959
|
+
item[attr] = self._serializer.serialize(resolve_template(tmpl, params))
|
|
1955
1960
|
requests.append({"PutRequest": {"Item": item}})
|
|
1956
1961
|
else: # DeleteItem
|
|
1957
1962
|
dynamo_key = {
|
|
@@ -2269,7 +2274,11 @@ class GraphDDBRuntime:
|
|
|
2269
2274
|
field: self._serializer.serialize(resolve_value_template(tmpl, params))
|
|
2270
2275
|
for field, tmpl in item_tmpl.items()
|
|
2271
2276
|
}
|
|
2272
|
-
|
|
2277
|
+
# A put's PK/SK/GSI key attributes ride as DECLARATIVE key templates on
|
|
2278
|
+
# `keyCondition` (the same symbolic derivation read/update/delete use,
|
|
2279
|
+
# issue #350); resolve + fold — NO runtime manifest key derivation.
|
|
2280
|
+
for attr, tmpl in spec.get("keyCondition", {}).items():
|
|
2281
|
+
item[attr] = self._serializer.serialize(resolve_template(tmpl, params))
|
|
2273
2282
|
|
|
2274
2283
|
request: Dict[str, Any] = {
|
|
2275
2284
|
"TableName": self._physical_table(spec["tableName"]),
|
|
@@ -2549,35 +2558,6 @@ class GraphDDBRuntime:
|
|
|
2549
2558
|
values[alias] = self._serializer.serialize(resolved)
|
|
2550
2559
|
request["ConditionExpression"] = condition["expression"]
|
|
2551
2560
|
|
|
2552
|
-
def _add_key_attributes(
|
|
2553
|
-
self, entity_name: str, item: Dict[str, Any], params: Mapping[str, Any]
|
|
2554
|
-
) -> None:
|
|
2555
|
-
entity_meta = self._entities.get(entity_name, {})
|
|
2556
|
-
plain = {
|
|
2557
|
-
field: self._deserializer.deserialize(av) for field, av in item.items()
|
|
2558
|
-
}
|
|
2559
|
-
key = entity_meta.get("key")
|
|
2560
|
-
if key:
|
|
2561
|
-
# pkTemplate is self-contained (segment key model #51); no prefix.
|
|
2562
|
-
pk = self._fill(key.get("pkTemplate") or "", plain)
|
|
2563
|
-
item["PK"] = self._serializer.serialize(pk)
|
|
2564
|
-
sk_tmpl = key.get("skTemplate")
|
|
2565
|
-
if sk_tmpl is not None:
|
|
2566
|
-
item["SK"] = self._serializer.serialize(self._fill(sk_tmpl, plain))
|
|
2567
|
-
|
|
2568
|
-
for gsi in entity_meta.get("gsis", []):
|
|
2569
|
-
index = gsi["indexName"]
|
|
2570
|
-
pk_tmpl = gsi.get("pkTemplate")
|
|
2571
|
-
sk_tmpl = gsi.get("skTemplate")
|
|
2572
|
-
if pk_tmpl is not None and self._fillable(pk_tmpl, plain):
|
|
2573
|
-
item[f"{index}PK"] = self._serializer.serialize(
|
|
2574
|
-
self._fill(pk_tmpl, plain)
|
|
2575
|
-
)
|
|
2576
|
-
if sk_tmpl is not None and self._fillable(sk_tmpl, plain):
|
|
2577
|
-
item[f"{index}SK"] = self._serializer.serialize(
|
|
2578
|
-
self._fill(sk_tmpl, plain)
|
|
2579
|
-
)
|
|
2580
|
-
|
|
2581
2561
|
@staticmethod
|
|
2582
2562
|
def _fillable(template: str, values: Mapping[str, Any]) -> bool:
|
|
2583
2563
|
return all(
|
|
@@ -145,18 +145,6 @@ def _resolve_condition_tree(
|
|
|
145
145
|
return out
|
|
146
146
|
|
|
147
147
|
|
|
148
|
-
def _fill_key(template: str, item: Mapping[str, Any]) -> str:
|
|
149
|
-
def repl(match: "re.Match[str]") -> str:
|
|
150
|
-
name = match.group(0)[1:-1]
|
|
151
|
-
return str(item.get(name, ""))
|
|
152
|
-
|
|
153
|
-
return _PLACEHOLDER_RE.sub(repl, template)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
def _key_fillable(template: str, item: Mapping[str, Any]) -> bool:
|
|
157
|
-
return all(m.group(0)[1:-1] in item for m in _PLACEHOLDER_RE.finditer(template))
|
|
158
|
-
|
|
159
|
-
|
|
160
148
|
# ── SCP guard evaluation seam (issue #266, Phase 3 S6) ────────────────────────
|
|
161
149
|
#
|
|
162
150
|
# The branch-for-branch port of the TS ``evaluateGuard`` seam
|
|
@@ -450,24 +438,6 @@ class TransactionExpander:
|
|
|
450
438
|
raise KeyError(f"unknown entity '{name}' in manifest")
|
|
451
439
|
return entity
|
|
452
440
|
|
|
453
|
-
def _add_key_attributes(self, entity_name: str, plain: Dict[str, Any]) -> None:
|
|
454
|
-
entity = self._entity(entity_name)
|
|
455
|
-
key = entity.get("key")
|
|
456
|
-
if key:
|
|
457
|
-
# pkTemplate is self-contained (segment key model #51); no prefix.
|
|
458
|
-
plain["PK"] = _fill_key(key.get("pkTemplate") or "", plain)
|
|
459
|
-
sk_tmpl = key.get("skTemplate")
|
|
460
|
-
if sk_tmpl is not None:
|
|
461
|
-
plain["SK"] = _fill_key(sk_tmpl, plain)
|
|
462
|
-
for gsi in entity.get("gsis", []):
|
|
463
|
-
index = gsi["indexName"]
|
|
464
|
-
pk_tmpl = gsi.get("pkTemplate")
|
|
465
|
-
sk_tmpl = gsi.get("skTemplate")
|
|
466
|
-
if pk_tmpl is not None and _key_fillable(pk_tmpl, plain):
|
|
467
|
-
plain[f"{index}PK"] = _fill_key(pk_tmpl, plain)
|
|
468
|
-
if sk_tmpl is not None and _key_fillable(sk_tmpl, plain):
|
|
469
|
-
plain[f"{index}SK"] = _fill_key(sk_tmpl, plain)
|
|
470
|
-
|
|
471
441
|
def _apply_condition(
|
|
472
442
|
self,
|
|
473
443
|
condition: Optional[Mapping[str, Any]],
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: graphddb-runtime
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.9
|
|
4
4
|
Summary: Thin DynamoDB executor for GraphDDB-generated Python repositories (single-operation core, issue #44).
|
|
5
5
|
License: MIT
|
|
6
6
|
Requires-Python: >=3.9
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
Requires-Dist: boto3>=1.26
|
|
9
|
-
Requires-Dist: behavior-contracts==0.7.
|
|
9
|
+
Requires-Dist: behavior-contracts==0.7.3
|
|
10
10
|
Provides-Extra: test
|
|
11
11
|
Requires-Dist: pytest>=7.0; extra == "test"
|
|
12
12
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "graphddb-runtime"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.9"
|
|
8
8
|
description = "Thin DynamoDB executor for GraphDDB-generated Python repositories (single-operation core, issue #44)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -14,7 +14,7 @@ license = { text = "MIT" }
|
|
|
14
14
|
# canonicalisation) are delegated to the extracted shared package
|
|
15
15
|
# ``behavior-contracts`` (import name ``behavior_contracts``). The runtime
|
|
16
16
|
# imports it unconditionally, so it is a HARD dependency.
|
|
17
|
-
dependencies = ["boto3>=1.26", "behavior-contracts==0.7.
|
|
17
|
+
dependencies = ["boto3>=1.26", "behavior-contracts==0.7.3"]
|
|
18
18
|
|
|
19
19
|
[project.optional-dependencies]
|
|
20
20
|
test = ["pytest>=7.0"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/graphddb_runtime.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{graphddb_runtime-1.0.7 → graphddb_runtime-1.0.9}/tests/test_integration_binding_contract.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|