query-cascade 0.3.1__tar.gz → 0.3.2__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.
- {query_cascade-0.3.1 → query_cascade-0.3.2}/PKG-INFO +1 -1
- {query_cascade-0.3.1 → query_cascade-0.3.2}/pyproject.toml +1 -1
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/query_cascade.egg-info/PKG-INFO +1 -1
- {query_cascade-0.3.1 → query_cascade-0.3.2}/tests/test_disk_cache.py +169 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/README.md +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/setup.cfg +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/cascade/__init__.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/cascade/_canonical.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/cascade/_disk_cache.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/cascade/_errors.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/cascade/_evaluator.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/cascade/_graph_export.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/cascade/_persistence.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/cascade/_runtime.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/cascade/_scheduler.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/cascade/_serde.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/cascade/_state.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/cascade/_store.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/cascade/_synthetic_graph.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/cascade/engine.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/query_cascade.egg-info/SOURCES.txt +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/query_cascade.egg-info/dependency_links.txt +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/query_cascade.egg-info/requires.txt +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/src/query_cascade.egg-info/top_level.txt +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/tests/test_dependency_parallelism.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/tests/test_engine.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/tests/test_engine_compute_many.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/tests/test_engine_concurrency.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/tests/test_engine_persistence_and_tracing.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/tests/test_engine_private.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/tests/test_engine_private2.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/tests/test_evaluator_and_synthetic_coverage.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/tests/test_examples.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/tests/test_internal_invariants.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/tests/test_performance.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/tests/test_repro_touch_memo_keyerror.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/tests/test_scale_behavior.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/tests/test_serde.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/tests/test_stateful_engine_invariants.py +0 -0
- {query_cascade-0.3.1 → query_cascade-0.3.2}/tests/test_subgraph_stats_export.py +0 -0
|
@@ -674,3 +674,172 @@ def test_disk_cache_prune_vacuum_edge_cases(tmp_path: Path) -> None:
|
|
|
674
674
|
engine.prune([("query", "missing:fid", ())], vacuum_disk=True)
|
|
675
675
|
|
|
676
676
|
engine.shutdown()
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
def test_dynamic_graph_topology_cross_session(tmp_path: Path) -> None:
|
|
680
|
+
data = tmp_path / "cond.txt"
|
|
681
|
+
data.write_text("A")
|
|
682
|
+
cache = tmp_path / "cache"
|
|
683
|
+
runs: dict[str, int] = {}
|
|
684
|
+
|
|
685
|
+
def build() -> tuple[Engine, Any]:
|
|
686
|
+
engine = Engine(cache_dir=cache)
|
|
687
|
+
|
|
688
|
+
@engine.input
|
|
689
|
+
def condition() -> str:
|
|
690
|
+
return data.read_text().strip()
|
|
691
|
+
|
|
692
|
+
@engine.query
|
|
693
|
+
def branch_a() -> str:
|
|
694
|
+
runs["branch_a"] = runs.get("branch_a", 0) + 1
|
|
695
|
+
return "A_out"
|
|
696
|
+
|
|
697
|
+
@engine.query
|
|
698
|
+
def branch_b() -> str:
|
|
699
|
+
runs["branch_b"] = runs.get("branch_b", 0) + 1
|
|
700
|
+
return "B_out"
|
|
701
|
+
|
|
702
|
+
@engine.query
|
|
703
|
+
def root() -> str:
|
|
704
|
+
runs["root"] = runs.get("root", 0) + 1
|
|
705
|
+
if condition() == "A":
|
|
706
|
+
return branch_a()
|
|
707
|
+
else:
|
|
708
|
+
return branch_b()
|
|
709
|
+
|
|
710
|
+
return engine, root
|
|
711
|
+
|
|
712
|
+
# Session 1: cond is A
|
|
713
|
+
engine_1, root_1 = build()
|
|
714
|
+
assert root_1() == "A_out"
|
|
715
|
+
assert runs == {"root": 1, "branch_a": 1}
|
|
716
|
+
engine_1.shutdown()
|
|
717
|
+
|
|
718
|
+
# Session 2: cond is still A, should hit
|
|
719
|
+
runs.clear()
|
|
720
|
+
engine_2, root_2 = build()
|
|
721
|
+
assert root_2() == "A_out"
|
|
722
|
+
assert runs == {}
|
|
723
|
+
engine_2.shutdown()
|
|
724
|
+
|
|
725
|
+
# Session 3: cond is B, must recompute root and branch_b
|
|
726
|
+
data.write_text("B")
|
|
727
|
+
runs.clear()
|
|
728
|
+
engine_3, root_3 = build()
|
|
729
|
+
assert root_3() == "B_out"
|
|
730
|
+
assert runs == {"root": 1, "branch_b": 1}
|
|
731
|
+
engine_3.shutdown()
|
|
732
|
+
|
|
733
|
+
# Session 4: cond is B, should hit
|
|
734
|
+
runs.clear()
|
|
735
|
+
engine_4, root_4 = build()
|
|
736
|
+
assert root_4() == "B_out"
|
|
737
|
+
assert runs == {}
|
|
738
|
+
engine_4.shutdown()
|
|
739
|
+
|
|
740
|
+
# Session 5: cond is A again. root's fingerprint was overwritten by Session 3 (which depended on B),
|
|
741
|
+
# so root must recompute. But branch_a will hit the cache from Session 1!
|
|
742
|
+
data.write_text("A")
|
|
743
|
+
runs.clear()
|
|
744
|
+
engine_5, root_5 = build()
|
|
745
|
+
assert root_5() == "A_out"
|
|
746
|
+
assert runs == {"root": 1}
|
|
747
|
+
engine_5.shutdown()
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
def test_errors_are_not_cached_across_sessions(tmp_path: Path) -> None:
|
|
751
|
+
data = tmp_path / "state.txt"
|
|
752
|
+
data.write_text("fail")
|
|
753
|
+
cache = tmp_path / "cache"
|
|
754
|
+
runs: dict[str, int] = {}
|
|
755
|
+
|
|
756
|
+
def build() -> tuple[Engine, Any]:
|
|
757
|
+
engine = Engine(cache_dir=cache)
|
|
758
|
+
|
|
759
|
+
@engine.input
|
|
760
|
+
def state() -> str:
|
|
761
|
+
return data.read_text().strip()
|
|
762
|
+
|
|
763
|
+
@engine.query
|
|
764
|
+
def process() -> str:
|
|
765
|
+
runs["process"] = runs.get("process", 0) + 1
|
|
766
|
+
val = state()
|
|
767
|
+
if val == "fail":
|
|
768
|
+
raise ValueError("Oops")
|
|
769
|
+
return "Success"
|
|
770
|
+
|
|
771
|
+
return engine, process
|
|
772
|
+
|
|
773
|
+
# Session 1: fails
|
|
774
|
+
engine_1, process_1 = build()
|
|
775
|
+
with pytest.raises(ValueError, match="Oops"):
|
|
776
|
+
process_1()
|
|
777
|
+
assert runs == {"process": 1}
|
|
778
|
+
engine_1.shutdown()
|
|
779
|
+
|
|
780
|
+
# Session 2: still failing, must re-run because errors aren't cached
|
|
781
|
+
runs.clear()
|
|
782
|
+
engine_2, process_2 = build()
|
|
783
|
+
with pytest.raises(ValueError, match="Oops"):
|
|
784
|
+
process_2()
|
|
785
|
+
assert runs == {"process": 1}
|
|
786
|
+
engine_2.shutdown()
|
|
787
|
+
|
|
788
|
+
# Session 3: state fixed, runs and caches
|
|
789
|
+
data.write_text("ok")
|
|
790
|
+
runs.clear()
|
|
791
|
+
engine_3, process_3 = build()
|
|
792
|
+
assert process_3() == "Success"
|
|
793
|
+
assert runs == {"process": 1}
|
|
794
|
+
engine_3.shutdown()
|
|
795
|
+
|
|
796
|
+
# Session 4: state still ok, hits cache
|
|
797
|
+
runs.clear()
|
|
798
|
+
engine_4, process_4 = build()
|
|
799
|
+
assert process_4() == "Success"
|
|
800
|
+
assert runs == {}
|
|
801
|
+
engine_4.shutdown()
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
def test_deep_dependency_chain_cross_session(tmp_path: Path) -> None:
|
|
805
|
+
data = tmp_path / "val.txt"
|
|
806
|
+
data.write_text("0")
|
|
807
|
+
cache = tmp_path / "cache"
|
|
808
|
+
runs: dict[str, int] = {}
|
|
809
|
+
|
|
810
|
+
def build() -> tuple[Engine, Any]:
|
|
811
|
+
engine = Engine(cache_dir=cache)
|
|
812
|
+
|
|
813
|
+
@engine.input
|
|
814
|
+
def base() -> int:
|
|
815
|
+
return int(data.read_text().strip())
|
|
816
|
+
|
|
817
|
+
@engine.query
|
|
818
|
+
def chain(idx: int) -> int:
|
|
819
|
+
runs[f"q_{idx}"] = runs.get(f"q_{idx}", 0) + 1
|
|
820
|
+
if idx == 0:
|
|
821
|
+
return base() + 1
|
|
822
|
+
return chain(idx - 1) + 1
|
|
823
|
+
|
|
824
|
+
return engine, chain
|
|
825
|
+
|
|
826
|
+
# Session 1: compute the chain
|
|
827
|
+
engine_1, chain_1 = build()
|
|
828
|
+
assert chain_1(49) == 50
|
|
829
|
+
assert len(runs) == 50
|
|
830
|
+
engine_1.shutdown()
|
|
831
|
+
|
|
832
|
+
# Session 2: hit the chain
|
|
833
|
+
runs.clear()
|
|
834
|
+
engine_2, chain_2 = build()
|
|
835
|
+
assert chain_2(49) == 50
|
|
836
|
+
assert len(runs) == 0
|
|
837
|
+
engine_2.shutdown()
|
|
838
|
+
|
|
839
|
+
# Session 3: update base, recomputes all
|
|
840
|
+
data.write_text("10")
|
|
841
|
+
runs.clear()
|
|
842
|
+
engine_3, chain_3 = build()
|
|
843
|
+
assert chain_3(49) == 60
|
|
844
|
+
assert len(runs) == 50
|
|
845
|
+
engine_3.shutdown()
|
|
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
|
|
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
|