speclogician 0.0.0b1__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.
- speclogician/__init__.py +0 -0
- speclogician/commands/__init__.py +15 -0
- speclogician/commands/cmd_ch.py +616 -0
- speclogician/commands/cmd_find.py +256 -0
- speclogician/commands/cmd_view.py +202 -0
- speclogician/commands/runner.py +149 -0
- speclogician/commands/utils.py +101 -0
- speclogician/data/__init__.py +0 -0
- speclogician/data/artifact.py +63 -0
- speclogician/data/container.py +402 -0
- speclogician/data/mapping.py +88 -0
- speclogician/data/refs.py +24 -0
- speclogician/data/traces.py +26 -0
- speclogician/demos/.DS_Store +0 -0
- speclogician/demos/cmd_demo.py +278 -0
- speclogician/demos/loader.py +135 -0
- speclogician/demos/model.py +27 -0
- speclogician/demos/runner.py +51 -0
- speclogician/logic/__init__.py +11 -0
- speclogician/logic/api/__init__.py +29 -0
- speclogician/logic/api/client.py +606 -0
- speclogician/logic/api/decomp.py +67 -0
- speclogician/logic/api/scenario.py +102 -0
- speclogician/logic/api/traces.py +59 -0
- speclogician/logic/lib/__init__.py +19 -0
- speclogician/logic/lib/complement.py +107 -0
- speclogician/logic/lib/domain_model.py +59 -0
- speclogician/logic/lib/predicates.py +151 -0
- speclogician/logic/lib/scenarios.py +369 -0
- speclogician/logic/lib/traces.py +114 -0
- speclogician/logic/lib/transitions.py +104 -0
- speclogician/logic/main.py +246 -0
- speclogician/logic/strings.py +194 -0
- speclogician/logic/utils.py +135 -0
- speclogician/main.py +139 -0
- speclogician/modeling/__init__.py +31 -0
- speclogician/modeling/complement.py +104 -0
- speclogician/modeling/component.py +71 -0
- speclogician/modeling/conflict.py +26 -0
- speclogician/modeling/domain.py +349 -0
- speclogician/modeling/predicates.py +59 -0
- speclogician/modeling/scenario.py +162 -0
- speclogician/modeling/spec.py +306 -0
- speclogician/modeling/spec_stats.py +39 -0
- speclogician/presentation/__init__.py +0 -0
- speclogician/presentation/api.py +244 -0
- speclogician/presentation/builders/__init__.py +0 -0
- speclogician/presentation/builders/_links.py +44 -0
- speclogician/presentation/builders/container.py +53 -0
- speclogician/presentation/builders/data_artifact.py +42 -0
- speclogician/presentation/builders/domain.py +54 -0
- speclogician/presentation/builders/instances_list.py +38 -0
- speclogician/presentation/builders/predicate.py +51 -0
- speclogician/presentation/builders/recommendations.py +41 -0
- speclogician/presentation/builders/scenario.py +41 -0
- speclogician/presentation/builders/scenario_complement.py +82 -0
- speclogician/presentation/builders/smart_find.py +39 -0
- speclogician/presentation/builders/spec.py +39 -0
- speclogician/presentation/builders/state_diff.py +150 -0
- speclogician/presentation/builders/state_instance.py +42 -0
- speclogician/presentation/builders/state_instance_summary.py +84 -0
- speclogician/presentation/builders/trace.py +58 -0
- speclogician/presentation/ctx.py +38 -0
- speclogician/presentation/models/__init__.py +0 -0
- speclogician/presentation/models/container.py +44 -0
- speclogician/presentation/models/data_artifact.py +33 -0
- speclogician/presentation/models/domain.py +50 -0
- speclogician/presentation/models/instances_list.py +23 -0
- speclogician/presentation/models/predicate.py +60 -0
- speclogician/presentation/models/recommendations.py +34 -0
- speclogician/presentation/models/scenario.py +31 -0
- speclogician/presentation/models/scenario_complement.py +40 -0
- speclogician/presentation/models/smart_find.py +34 -0
- speclogician/presentation/models/spec.py +32 -0
- speclogician/presentation/models/state_diff.py +34 -0
- speclogician/presentation/models/state_instance.py +31 -0
- speclogician/presentation/models/state_instance_summary.py +102 -0
- speclogician/presentation/models/trace.py +42 -0
- speclogician/presentation/preview/__init__.py +13 -0
- speclogician/presentation/preview/cli.py +50 -0
- speclogician/presentation/preview/fixtures/__init__.py +205 -0
- speclogician/presentation/preview/fixtures/artifact_container.py +150 -0
- speclogician/presentation/preview/fixtures/data_artifact.py +144 -0
- speclogician/presentation/preview/fixtures/domain_model.py +162 -0
- speclogician/presentation/preview/fixtures/instances_list.py +162 -0
- speclogician/presentation/preview/fixtures/predicate.py +184 -0
- speclogician/presentation/preview/fixtures/scenario.py +84 -0
- speclogician/presentation/preview/fixtures/scenario_complement.py +81 -0
- speclogician/presentation/preview/fixtures/smart_find.py +140 -0
- speclogician/presentation/preview/fixtures/spec.py +95 -0
- speclogician/presentation/preview/fixtures/state_diff.py +158 -0
- speclogician/presentation/preview/fixtures/state_instance.py +128 -0
- speclogician/presentation/preview/fixtures/state_instance_summary.py +80 -0
- speclogician/presentation/preview/fixtures/trace.py +206 -0
- speclogician/presentation/preview/registry.py +42 -0
- speclogician/presentation/renderers/__init__.py +24 -0
- speclogician/presentation/renderers/container.py +136 -0
- speclogician/presentation/renderers/data_artifact.py +144 -0
- speclogician/presentation/renderers/domain.py +123 -0
- speclogician/presentation/renderers/instances_list.py +120 -0
- speclogician/presentation/renderers/predicate.py +180 -0
- speclogician/presentation/renderers/recommendations.py +90 -0
- speclogician/presentation/renderers/scenario.py +94 -0
- speclogician/presentation/renderers/scenario_complement.py +59 -0
- speclogician/presentation/renderers/smart_find.py +307 -0
- speclogician/presentation/renderers/spec.py +105 -0
- speclogician/presentation/renderers/state_diff.py +102 -0
- speclogician/presentation/renderers/state_instance.py +82 -0
- speclogician/presentation/renderers/state_instance_summary.py +143 -0
- speclogician/presentation/renderers/trace.py +122 -0
- speclogician/py.typed +0 -0
- speclogician/shell/app.py +170 -0
- speclogician/shell/shell_ch.py +263 -0
- speclogician/shell/shell_view.py +153 -0
- speclogician/state/__init__.py +0 -0
- speclogician/state/change.py +428 -0
- speclogician/state/change_result.py +32 -0
- speclogician/state/diff.py +191 -0
- speclogician/state/inst.py +574 -0
- speclogician/state/recommendation.py +13 -0
- speclogician/state/recommender.py +577 -0
- speclogician/state/state.py +465 -0
- speclogician/state/state_stats.py +133 -0
- speclogician/tui/__init__.py +0 -0
- speclogician/tui/app.py +257 -0
- speclogician/tui/app.tcss +160 -0
- speclogician/tui/demo.py +45 -0
- speclogician/tui/images/speclogician-full.png +0 -0
- speclogician/tui/images/speclogician-minimal.png +0 -0
- speclogician/tui/main_screen.py +454 -0
- speclogician/tui/splash_screen.py +51 -0
- speclogician/tui/stats_screen.py +125 -0
- speclogician/utils/__init__.py +78 -0
- speclogician/utils/load.py +166 -0
- speclogician/utils/prompt.md +325 -0
- speclogician/utils/testing.py +151 -0
- speclogician-0.0.0b1.dist-info/METADATA +116 -0
- speclogician-0.0.0b1.dist-info/RECORD +139 -0
- speclogician-0.0.0b1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Imandra Inc.
|
|
3
|
+
#
|
|
4
|
+
# speclogician/presentation/builders/data_artifact.py
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from speclogician.presentation.ctx import RenderCtx
|
|
10
|
+
from speclogician.presentation.models.data_artifact import (
|
|
11
|
+
DataArtifactCorePM,
|
|
12
|
+
DocRefPM,
|
|
13
|
+
SrcCodeRefPM,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from speclogician.data.refs import DocRef, SrcCodeRef # adjust if needed
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _build_core(art_id: str) -> DataArtifactCorePM:
|
|
20
|
+
return DataArtifactCorePM(art_id=art_id)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def build_doc_ref_pm(d: DocRef, ctx: RenderCtx) -> DocRefPM:
|
|
24
|
+
# Text is the artifact; keep it unless you want a separate ctx flag for "heavy text"
|
|
25
|
+
text = d.text if getattr(ctx, "show_code", True) else d.text
|
|
26
|
+
return DocRefPM(
|
|
27
|
+
core=_build_core(d.art_id),
|
|
28
|
+
text=text,
|
|
29
|
+
meta=d.meta,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def build_src_code_ref_pm(s: SrcCodeRef, ctx: RenderCtx) -> SrcCodeRefPM:
|
|
34
|
+
show = getattr(ctx, "show_code", True)
|
|
35
|
+
return SrcCodeRefPM(
|
|
36
|
+
core=_build_core(s.art_id),
|
|
37
|
+
src_code=s.src_code if show else "",
|
|
38
|
+
language=s.language,
|
|
39
|
+
file_path=s.file_path,
|
|
40
|
+
iml_code=s.iml_code if show else None,
|
|
41
|
+
meta=s.meta,
|
|
42
|
+
)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Imandra Inc.
|
|
3
|
+
#
|
|
4
|
+
# speclogician/presentation/builders/domain.py
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from speclogician.modeling.domain import DomainModel
|
|
10
|
+
from speclogician.presentation.ctx import RenderCtx
|
|
11
|
+
from speclogician.presentation.models.domain import (
|
|
12
|
+
DomainModelPM,
|
|
13
|
+
DomainCountsPM,
|
|
14
|
+
DomainCodePM,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def build_domain_model_pm(dm: DomainModel, ctx: RenderCtx) -> DomainModelPM:
|
|
19
|
+
counts = DomainCountsPM(
|
|
20
|
+
base_status=dm.base_status,
|
|
21
|
+
base_has_state=dm.base_has_state,
|
|
22
|
+
base_has_action=dm.base_has_action,
|
|
23
|
+
num_state_preds_total=dm.num_state_preds_total,
|
|
24
|
+
num_state_preds_valid_logic=dm.num_state_preds_valid_logic,
|
|
25
|
+
num_state_preds_matched=dm.num_state_preds_matched,
|
|
26
|
+
num_action_preds_total=dm.num_action_preds_total,
|
|
27
|
+
num_action_preds_valid_logic=dm.num_action_preds_valid_logic,
|
|
28
|
+
num_action_preds_matched=dm.num_action_preds_matched,
|
|
29
|
+
num_preds_total=dm.num_preds_total,
|
|
30
|
+
num_preds_valid_logic=dm.num_preds_valid_logic,
|
|
31
|
+
num_preds_matched=dm.num_preds_matched,
|
|
32
|
+
num_trans_total=dm.num_trans_total,
|
|
33
|
+
num_trans_valid_logic=dm.num_trans_valid_logic,
|
|
34
|
+
num_trans_matched=dm.num_trans_matched,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Always construct code PM for stable JSON; keep fields empty unless requested.
|
|
38
|
+
code = DomainCodePM()
|
|
39
|
+
|
|
40
|
+
if not ctx.show_stats_only and ctx.show_code:
|
|
41
|
+
# Base
|
|
42
|
+
if getattr(ctx, "show_base", True):
|
|
43
|
+
code.base = dm.base or ""
|
|
44
|
+
|
|
45
|
+
# Predicates
|
|
46
|
+
if getattr(ctx, "show_predicates", True):
|
|
47
|
+
code.state_preds_iml = "\n\n".join(p.to_iml() for p in dm.state_preds).strip()
|
|
48
|
+
code.action_preds_iml = "\n\n".join(p.to_iml() for p in dm.action_preds).strip()
|
|
49
|
+
|
|
50
|
+
# Transitions
|
|
51
|
+
if getattr(ctx, "show_transitions", True):
|
|
52
|
+
code.transitions_iml = "\n\n".join(t.to_iml() for t in dm.transitions).strip()
|
|
53
|
+
|
|
54
|
+
return DomainModelPM(counts=counts, code=code)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Imandra Inc.
|
|
3
|
+
#
|
|
4
|
+
# speclogician/presentation/builders/instances_list.py
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from speclogician.presentation.ctx import RenderCtx
|
|
10
|
+
from speclogician.presentation.models.instances_list import InstancesListPM, InstancesListRowPM
|
|
11
|
+
from speclogician.presentation.builders.state_instance_summary import build_state_instance_summary_pm
|
|
12
|
+
from speclogician.state.inst import StateInstance
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _changes_str(si: StateInstance) -> str:
|
|
16
|
+
if not (si.changes or []):
|
|
17
|
+
return "-"
|
|
18
|
+
|
|
19
|
+
parts: list[str] = []
|
|
20
|
+
for ch in si.changes:
|
|
21
|
+
short = getattr(ch, "short_str", None)
|
|
22
|
+
parts.append(short() if callable(short) else ch.__class__.__name__)
|
|
23
|
+
return ", ".join(parts)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def build_instances_list_pm(states: list[StateInstance], ctx: RenderCtx) -> InstancesListPM:
|
|
27
|
+
rows: list[InstancesListRowPM] = []
|
|
28
|
+
for idx, si in enumerate(states):
|
|
29
|
+
state_label = "latest" if idx == 0 else str(idx)
|
|
30
|
+
rows.append(
|
|
31
|
+
InstancesListRowPM(
|
|
32
|
+
state_label=state_label,
|
|
33
|
+
changes=_changes_str(si),
|
|
34
|
+
summary=build_state_instance_summary_pm(si, ctx=ctx),
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return InstancesListPM(rows=rows)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Imandra Inc.
|
|
3
|
+
#
|
|
4
|
+
# speclogician/presentation/builders/predicate.py
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from speclogician.modeling.predicates import ActionPredicate, PredicateType, StatePredicate, Transition
|
|
10
|
+
from speclogician.presentation.ctx import RenderCtx
|
|
11
|
+
from speclogician.presentation.builders._links import build_links_pm
|
|
12
|
+
from speclogician.presentation.models.predicate import PredicatePM, TransitionPM
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def build_predicate_pm(p: StatePredicate | ActionPredicate, ctx: RenderCtx) -> PredicatePM:
|
|
16
|
+
pred_type = PredicateType.STATE if isinstance(p, StatePredicate) else PredicateType.ACTION
|
|
17
|
+
signature = f"{p.name} s" if pred_type == PredicateType.STATE else f"{p.name} s a"
|
|
18
|
+
|
|
19
|
+
# Prefer to_iml if present (yours is)
|
|
20
|
+
iml = p.to_iml() if hasattr(p, "to_iml") else ""
|
|
21
|
+
|
|
22
|
+
return PredicatePM(
|
|
23
|
+
kind=p.kind, # "state_predicate" | "action_predicate"
|
|
24
|
+
comp_id=p.comp_id,
|
|
25
|
+
name=p.name,
|
|
26
|
+
last_updated=p.last_updated,
|
|
27
|
+
pred_type=pred_type,
|
|
28
|
+
signature=signature,
|
|
29
|
+
src_code=p.src_code or "",
|
|
30
|
+
iml=iml,
|
|
31
|
+
is_iml_valid=p.is_iml_valid,
|
|
32
|
+
iml_error=p.iml_error,
|
|
33
|
+
links=build_links_pm(p.comp_id, ctx),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def build_transition_pm(t: Transition, ctx: RenderCtx) -> TransitionPM:
|
|
38
|
+
signature = f"{t.name} s a"
|
|
39
|
+
iml = t.to_iml() if hasattr(t, "to_iml") else ""
|
|
40
|
+
|
|
41
|
+
return TransitionPM(
|
|
42
|
+
comp_id=t.comp_id,
|
|
43
|
+
name=t.name,
|
|
44
|
+
last_updated=t.last_updated,
|
|
45
|
+
signature=signature,
|
|
46
|
+
src_code=t.src_code or "",
|
|
47
|
+
iml=iml,
|
|
48
|
+
is_iml_valid=t.is_iml_valid,
|
|
49
|
+
iml_error=t.iml_error,
|
|
50
|
+
links=build_links_pm(t.comp_id, ctx),
|
|
51
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Imandra Inc.
|
|
3
|
+
#
|
|
4
|
+
# speclogician/presentation/builders/recommendations.py
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from speclogician.presentation.ctx import RenderCtx
|
|
10
|
+
from speclogician.presentation.models.recommendations import (
|
|
11
|
+
RecommendationsPM,
|
|
12
|
+
RecommendationPM,
|
|
13
|
+
)
|
|
14
|
+
from speclogician.state.recommender import Recommendation
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def build_recommendations_pm(
|
|
18
|
+
recs: list[Recommendation] | None,
|
|
19
|
+
ctx: RenderCtx,
|
|
20
|
+
) -> RecommendationsPM:
|
|
21
|
+
rs = list(recs or [])
|
|
22
|
+
|
|
23
|
+
# UI wants stable ordering: priority asc, then kind, then text
|
|
24
|
+
rs.sort(key=lambda r: (r.priority, r.kind, r.text))
|
|
25
|
+
|
|
26
|
+
items = [RecommendationPM(kind=r.kind, priority=r.priority, text=r.text) for r in rs]
|
|
27
|
+
|
|
28
|
+
# counts
|
|
29
|
+
n_error = sum(1 for r in items if r.kind == "error")
|
|
30
|
+
n_warn = sum(1 for r in items if r.kind == "warning")
|
|
31
|
+
n_next = sum(1 for r in items if r.kind == "next")
|
|
32
|
+
n_info = sum(1 for r in items if r.kind == "info")
|
|
33
|
+
|
|
34
|
+
return RecommendationsPM(
|
|
35
|
+
count=len(items),
|
|
36
|
+
items=items,
|
|
37
|
+
num_error=n_error,
|
|
38
|
+
num_warning=n_warn,
|
|
39
|
+
num_next=n_next,
|
|
40
|
+
num_info=n_info,
|
|
41
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Imandra Inc.
|
|
3
|
+
#
|
|
4
|
+
# speclogician/presentation/builders/scenario.py
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from speclogician.presentation.ctx import RenderCtx
|
|
10
|
+
from speclogician.modeling.scenario import Scenario, Missing, Inconsistent
|
|
11
|
+
from speclogician.presentation.models.scenario import (
|
|
12
|
+
ScenarioPM,
|
|
13
|
+
ScenarioStatusPM,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def build_scenario_pm(sc: Scenario, ctx: RenderCtx) -> ScenarioPM:
|
|
18
|
+
# status
|
|
19
|
+
st = sc.component_status
|
|
20
|
+
if isinstance(st, Missing):
|
|
21
|
+
status = ScenarioStatusPM(
|
|
22
|
+
kind="missing",
|
|
23
|
+
missing_preds=list(st.missing_preds),
|
|
24
|
+
missing_trans=list(st.missing_trans),
|
|
25
|
+
)
|
|
26
|
+
elif isinstance(st, Inconsistent):
|
|
27
|
+
status = ScenarioStatusPM(kind="inconsistent")
|
|
28
|
+
else:
|
|
29
|
+
status = ScenarioStatusPM(kind="valid")
|
|
30
|
+
|
|
31
|
+
return ScenarioPM(
|
|
32
|
+
comp_id=sc.comp_id,
|
|
33
|
+
name=sc.name,
|
|
34
|
+
status=status,
|
|
35
|
+
given=list(sc.given),
|
|
36
|
+
when=list(sc.when),
|
|
37
|
+
then=list(sc.then),
|
|
38
|
+
given_preds_consistent=sc.given_preds_consistent,
|
|
39
|
+
when_preds_consistent=sc.when_preds_consistent,
|
|
40
|
+
all_preds_consistent=sc.all_preds_consistent,
|
|
41
|
+
)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Imandra Inc.
|
|
3
|
+
#
|
|
4
|
+
# speclogician/presentation/builders/scenario_complement.py
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from speclogician.presentation.ctx import RenderCtx
|
|
10
|
+
from speclogician.presentation.models.scenario_complement import (
|
|
11
|
+
ScenarioComplementPM,
|
|
12
|
+
ScenarioComplementRegionPM,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from speclogician.modeling.complement import ScenarioComplement, _fingerprint_region
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _safe_list(x) -> list:
|
|
19
|
+
try:
|
|
20
|
+
return list(x or [])
|
|
21
|
+
except Exception:
|
|
22
|
+
return []
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def build_scenario_complement_pm(
|
|
26
|
+
comp: ScenarioComplement | None,
|
|
27
|
+
ctx: RenderCtx,
|
|
28
|
+
*,
|
|
29
|
+
preview_limit: int = 10,
|
|
30
|
+
constraints_preview_limit: int = 3,
|
|
31
|
+
) -> ScenarioComplementPM:
|
|
32
|
+
if comp is None:
|
|
33
|
+
return ScenarioComplementPM(count_regions=0, regions_preview=[])
|
|
34
|
+
|
|
35
|
+
regs = _safe_list(getattr(comp, "regions", None))
|
|
36
|
+
count = len(regs)
|
|
37
|
+
|
|
38
|
+
# show only a few regions in the UI
|
|
39
|
+
preview_regs = regs[: max(0, int(preview_limit))]
|
|
40
|
+
|
|
41
|
+
items: list[ScenarioComplementRegionPM] = []
|
|
42
|
+
for r in preview_regs:
|
|
43
|
+
fp = _fingerprint_region(r)
|
|
44
|
+
|
|
45
|
+
constraints = _safe_list(getattr(r, "constraints_str", None))
|
|
46
|
+
invariant = (getattr(r, "invariant_str", None) or None)
|
|
47
|
+
model_eval = (getattr(r, "model_eval_str", None) or None)
|
|
48
|
+
|
|
49
|
+
# model_str can be dict-like; stringify for display
|
|
50
|
+
model_obj = getattr(r, "model_str", None)
|
|
51
|
+
if model_obj is None:
|
|
52
|
+
model_s = None
|
|
53
|
+
else:
|
|
54
|
+
try:
|
|
55
|
+
model_s = str(model_obj)
|
|
56
|
+
except Exception:
|
|
57
|
+
model_s = "<unprintable>"
|
|
58
|
+
|
|
59
|
+
# If ctx.show_code is False, keep it extra light.
|
|
60
|
+
if not ctx.show_code:
|
|
61
|
+
invariant = None
|
|
62
|
+
model_s = None
|
|
63
|
+
model_eval = None
|
|
64
|
+
constraints_preview: list[str] = []
|
|
65
|
+
else:
|
|
66
|
+
constraints_preview = [str(c) for c in constraints[: max(0, int(constraints_preview_limit))]]
|
|
67
|
+
|
|
68
|
+
items.append(
|
|
69
|
+
ScenarioComplementRegionPM(
|
|
70
|
+
fp=fp,
|
|
71
|
+
num_constraints=len(constraints),
|
|
72
|
+
invariant=invariant,
|
|
73
|
+
model=model_s,
|
|
74
|
+
model_eval=model_eval,
|
|
75
|
+
constraints_preview=constraints_preview,
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
return ScenarioComplementPM(
|
|
80
|
+
count_regions=count,
|
|
81
|
+
regions_preview=items,
|
|
82
|
+
)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Imandra Inc.
|
|
3
|
+
#
|
|
4
|
+
# speclogician/presentation/builders/smart_find.py
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from speclogician.data.container import SmartFindResults
|
|
10
|
+
from speclogician.presentation.ctx import RenderCtx
|
|
11
|
+
from speclogician.presentation.models.smart_find import (
|
|
12
|
+
SmartFindPM,
|
|
13
|
+
SmartFindCountsPM,
|
|
14
|
+
SmartFindItemsPM,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from speclogician.presentation.builders.trace import build_test_trace_pm, build_log_trace_pm
|
|
18
|
+
from speclogician.presentation.builders.data_artifact import build_doc_ref_pm, build_src_code_ref_pm
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def build_smart_find_pm(res: SmartFindResults, ctx: RenderCtx) -> SmartFindPM:
|
|
22
|
+
counts = SmartFindCountsPM(
|
|
23
|
+
needle=res.needle,
|
|
24
|
+
num_test_traces=len(res.test_traces),
|
|
25
|
+
num_log_traces=len(res.log_traces),
|
|
26
|
+
num_doc_refs=len(res.doc_refs),
|
|
27
|
+
num_src_code_refs=len(res.src_code_refs),
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
items = SmartFindItemsPM()
|
|
31
|
+
|
|
32
|
+
# If stats-only, skip the heavy lists.
|
|
33
|
+
if not ctx.show_stats_only:
|
|
34
|
+
items.test_traces = [build_test_trace_pm(t, ctx) for t in res.test_traces]
|
|
35
|
+
items.log_traces = [build_log_trace_pm(t, ctx) for t in res.log_traces]
|
|
36
|
+
items.doc_refs = [build_doc_ref_pm(d, ctx) for d in res.doc_refs]
|
|
37
|
+
items.src_code_refs = [build_src_code_ref_pm(s, ctx) for s in res.src_code_refs]
|
|
38
|
+
|
|
39
|
+
return SmartFindPM(counts=counts, items=items)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Imandra Inc.
|
|
3
|
+
#
|
|
4
|
+
# speclogician/presentation/builders/spec.py
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from speclogician.modeling.spec import Spec
|
|
10
|
+
from speclogician.presentation.ctx import RenderCtx
|
|
11
|
+
from speclogician.presentation.builders.domain import build_domain_model_pm
|
|
12
|
+
from speclogician.presentation.builders.scenario_complement import (
|
|
13
|
+
build_scenario_complement_pm,
|
|
14
|
+
)
|
|
15
|
+
from speclogician.presentation.models.spec import SpecPM, SpecCountsPM
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def build_spec_pm(spec: Spec, ctx: RenderCtx) -> SpecPM:
|
|
19
|
+
dm_pm = build_domain_model_pm(spec.domain_model, ctx)
|
|
20
|
+
|
|
21
|
+
counts = SpecCountsPM(
|
|
22
|
+
num_sc_total=spec.num_sc_total,
|
|
23
|
+
num_sc_missing=spec.num_sc_missing,
|
|
24
|
+
num_sc_matched=spec.num_sc_matched,
|
|
25
|
+
num_sc_inconsistent=spec.num_sc_inconsistent,
|
|
26
|
+
num_sc_conflicted=spec.num_sc_conflicted,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
scenario_names = [s.name for s in (spec.scenarios or [])]
|
|
30
|
+
|
|
31
|
+
return SpecPM(
|
|
32
|
+
domain=dm_pm,
|
|
33
|
+
counts=counts,
|
|
34
|
+
scenario_names=scenario_names,
|
|
35
|
+
scenario_complement=build_scenario_complement_pm(
|
|
36
|
+
getattr(spec, "scenario_comp", None),
|
|
37
|
+
ctx,
|
|
38
|
+
),
|
|
39
|
+
)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Imandra Inc.
|
|
3
|
+
#
|
|
4
|
+
# speclogician/presentation/builders/state_diff.py
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from speclogician.presentation.ctx import RenderCtx
|
|
12
|
+
from speclogician.presentation.models.state_diff import (
|
|
13
|
+
StateDiffPM,
|
|
14
|
+
DiffSectionPM,
|
|
15
|
+
DiffRowPM,
|
|
16
|
+
)
|
|
17
|
+
from speclogician.state.diff import StateDiff, ValueDiff, ComparisonOutcome
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _outcome(vd: ValueDiff[Any]) -> ComparisonOutcome:
|
|
21
|
+
if vd.comp_func:
|
|
22
|
+
return vd.comp_func(vd.before, vd.after)
|
|
23
|
+
if vd.before == vd.after:
|
|
24
|
+
return ComparisonOutcome.NO_CHANGE
|
|
25
|
+
return ComparisonOutcome.UNKNOWN
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _message(vd: ValueDiff[Any]) -> str:
|
|
29
|
+
oc = _outcome(vd)
|
|
30
|
+
b = vd.before
|
|
31
|
+
a = vd.after
|
|
32
|
+
|
|
33
|
+
match oc:
|
|
34
|
+
case ComparisonOutcome.UNKNOWN:
|
|
35
|
+
return f"{vd.label}: Unknown: {b} -> {a}"
|
|
36
|
+
case ComparisonOutcome.NO_CHANGE:
|
|
37
|
+
return f"{vd.label}: No change: {b} -> {a}"
|
|
38
|
+
case ComparisonOutcome.NO_CHANGE_GOOD:
|
|
39
|
+
return f"{vd.label}: No change (good): {b} -> {a}"
|
|
40
|
+
case ComparisonOutcome.NO_CHANGE_BAD:
|
|
41
|
+
return f"{vd.label}: No change (bad): {b} -> {a}"
|
|
42
|
+
case ComparisonOutcome.IMPROVED:
|
|
43
|
+
return f"{vd.label}: Improvement: {b} -> {a}"
|
|
44
|
+
case ComparisonOutcome.DECLINED:
|
|
45
|
+
return f"{vd.label}: Decline: {b} -> {a}"
|
|
46
|
+
case _:
|
|
47
|
+
return f"{vd.label}: {b} -> {a}"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _row(vd: ValueDiff[Any]) -> DiffRowPM:
|
|
51
|
+
return DiffRowPM(
|
|
52
|
+
label=vd.label,
|
|
53
|
+
before=vd.before,
|
|
54
|
+
after=vd.after,
|
|
55
|
+
outcome=_outcome(vd),
|
|
56
|
+
message=_message(vd),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def build_state_diff_pm(sd: StateDiff, ctx: RenderCtx) -> StateDiffPM:
|
|
61
|
+
sections: list[DiffSectionPM] = []
|
|
62
|
+
|
|
63
|
+
def add(title: str, vds: list[ValueDiff[Any]]) -> None:
|
|
64
|
+
rows = [_row(vd) for vd in vds if vd.is_different()]
|
|
65
|
+
if rows:
|
|
66
|
+
sections.append(DiffSectionPM(title=title, rows=rows))
|
|
67
|
+
|
|
68
|
+
# ---- Domain model ----
|
|
69
|
+
add("Domain Model: Base", [sd.base_status, sd.base_has_state, sd.base_has_action])
|
|
70
|
+
|
|
71
|
+
add(
|
|
72
|
+
"Domain Model: State predicates",
|
|
73
|
+
[
|
|
74
|
+
sd.num_state_preds_total,
|
|
75
|
+
sd.num_state_preds_matched,
|
|
76
|
+
sd.num_state_preds_valid_logic,
|
|
77
|
+
sd.num_state_preds_errored,
|
|
78
|
+
],
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
add(
|
|
82
|
+
"Domain Model: Action predicates",
|
|
83
|
+
[
|
|
84
|
+
sd.num_action_preds_total,
|
|
85
|
+
sd.num_action_preds_matched,
|
|
86
|
+
sd.num_action_preds_valid_logic,
|
|
87
|
+
sd.num_action_preds_errored,
|
|
88
|
+
],
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
add(
|
|
92
|
+
"Domain Model: Predicates (total)",
|
|
93
|
+
[
|
|
94
|
+
sd.num_preds_total,
|
|
95
|
+
sd.num_preds_matched,
|
|
96
|
+
sd.num_preds_valid_logic,
|
|
97
|
+
sd.num_preds_errored,
|
|
98
|
+
],
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
add(
|
|
102
|
+
"Domain Model: Transitions",
|
|
103
|
+
[
|
|
104
|
+
sd.num_trans_total,
|
|
105
|
+
sd.num_trans_matched,
|
|
106
|
+
sd.num_trans_valid_logic,
|
|
107
|
+
sd.num_trans_errored,
|
|
108
|
+
],
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# ---- Scenarios + conflicts ----
|
|
112
|
+
add(
|
|
113
|
+
"Scenarios",
|
|
114
|
+
[
|
|
115
|
+
sd.num_sc_total,
|
|
116
|
+
sd.num_sc_missing,
|
|
117
|
+
sd.num_sc_matched,
|
|
118
|
+
sd.num_sc_inconsistent,
|
|
119
|
+
],
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
add(
|
|
123
|
+
"Scenario conflicts",
|
|
124
|
+
[
|
|
125
|
+
sd.num_sc_conflicted,
|
|
126
|
+
sd.num_sc_overlap,
|
|
127
|
+
sd.num_sc_consumed,
|
|
128
|
+
],
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# ---- Artifacts ----
|
|
132
|
+
add(
|
|
133
|
+
"Test traces",
|
|
134
|
+
[
|
|
135
|
+
sd.num_test_traces_total,
|
|
136
|
+
sd.num_test_traces_logic_good,
|
|
137
|
+
sd.num_test_traces_matched,
|
|
138
|
+
],
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
add(
|
|
142
|
+
"Log traces",
|
|
143
|
+
[
|
|
144
|
+
sd.num_log_traces_total,
|
|
145
|
+
sd.num_log_traces_logic_good,
|
|
146
|
+
sd.num_log_traces_matched,
|
|
147
|
+
],
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
return StateDiffPM(has_changes=bool(sections), sections=sections)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Imandra Inc.
|
|
3
|
+
#
|
|
4
|
+
# speclogician/presentation/builders/state_instance.py
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from speclogician.presentation.ctx import RenderCtx
|
|
10
|
+
from speclogician.presentation.models.state_instance import StateInstancePM
|
|
11
|
+
from speclogician.presentation.builders.state_instance_summary import (
|
|
12
|
+
build_state_instance_summary_pm,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from speclogician.presentation.builders.spec import build_spec_pm
|
|
16
|
+
from speclogician.presentation.builders.container import build_artifact_container_pm
|
|
17
|
+
from speclogician.presentation.builders.state_diff import build_state_diff_pm
|
|
18
|
+
from speclogician.presentation.builders.recommendations import build_recommendations_pm
|
|
19
|
+
|
|
20
|
+
from speclogician.state.inst import StateInstance
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def build_state_instance_pm(si: StateInstance, ctx: RenderCtx) -> StateInstancePM:
|
|
24
|
+
summary_pm = build_state_instance_summary_pm(si, ctx)
|
|
25
|
+
|
|
26
|
+
recommendations_pm = build_recommendations_pm(si.recommendations, ctx)
|
|
27
|
+
|
|
28
|
+
pm = StateInstancePM(summary=summary_pm, recommendations=recommendations_pm)
|
|
29
|
+
|
|
30
|
+
# -----------------
|
|
31
|
+
# Heavy parts (optional)
|
|
32
|
+
# -----------------
|
|
33
|
+
if not ctx.show_stats_only:
|
|
34
|
+
pm.spec = build_spec_pm(si.spec, ctx)
|
|
35
|
+
pm.artifacts = build_artifact_container_pm(si.art_container, ctx)
|
|
36
|
+
|
|
37
|
+
if si.state_diff is not None:
|
|
38
|
+
pm.state_diff = build_state_diff_pm(si.state_diff, ctx)
|
|
39
|
+
else:
|
|
40
|
+
pm.state_diff = None
|
|
41
|
+
|
|
42
|
+
return pm
|