cyvest 0.1.0__py3-none-any.whl → 5.1.3__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.
cyvest/builder.py DELETED
@@ -1,182 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import threading
4
- from collections.abc import Iterable, Sequence
5
- from contextlib import AbstractContextManager
6
- from dataclasses import dataclass
7
- from typing import Any
8
-
9
- from .models import ContainableSLM, Container, Level, ResultCheck, Scope
10
- from .visitors import Report
11
-
12
-
13
- @dataclass
14
- class _ContainerNode:
15
- container: Container
16
-
17
-
18
- class _ContainerContext(AbstractContextManager["BuilderContainerProxy"]):
19
- def __init__(self, builder: ReportBuilder, node: _ContainerNode) -> None:
20
- self._builder = builder
21
- self._node = node
22
-
23
- def __enter__(self) -> BuilderContainerProxy:
24
- self._builder._enter_container(self._node)
25
- return BuilderContainerProxy(self._builder, self._node)
26
-
27
- def __exit__(self, exc_type, exc, exc_tb) -> None:
28
- self._builder._exit_container(self._node)
29
-
30
-
31
- class BuilderContainerProxy:
32
- def __init__(self, builder: ReportBuilder, node: _ContainerNode) -> None:
33
- self._builder = builder
34
- self._node = node
35
-
36
- def add_check(
37
- self,
38
- path: str,
39
- *,
40
- identifier: str | None = None,
41
- description: str | None = None,
42
- level: Level = Level.INFO,
43
- score: float = 0.0,
44
- details: dict | None = None,
45
- observable_chain: Sequence[dict[str, Any]] | None = None,
46
- ) -> ResultCheck:
47
- return self._builder.add_check(
48
- path,
49
- scope=self._node.container.scope,
50
- identifier=identifier,
51
- description=description,
52
- level=level,
53
- score=score,
54
- details=details,
55
- observable_chain=observable_chain,
56
- )
57
-
58
- def add_existing(self, node: ContainableSLM) -> ContainableSLM:
59
- return self._builder.add_existing(node)
60
-
61
- def container(
62
- self,
63
- path: str,
64
- *,
65
- scope: Scope | None = None,
66
- description: str | None = None,
67
- identifier: str | None = None,
68
- level: Level = Level.INFO,
69
- details: dict | None = None,
70
- ) -> _ContainerContext:
71
- container_scope = scope or self._node.container.scope
72
- return self._builder.container(
73
- path,
74
- scope=container_scope,
75
- description=description,
76
- identifier=identifier,
77
- level=level,
78
- details=details,
79
- )
80
-
81
-
82
- class ReportBuilder:
83
- """Helper for constructing reports with nested containers and checks."""
84
-
85
- def __init__(self, report: Report | None = None, *, graph: bool = False) -> None:
86
- self.report: Report = report or Report(graph=graph)
87
- self._stack: list[_ContainerNode] = []
88
- self._roots: list[ContainableSLM] = []
89
- self._lock = threading.RLock()
90
-
91
- def container(
92
- self,
93
- path: str,
94
- *,
95
- scope: Scope,
96
- description: str | None = None,
97
- identifier: str | None = None,
98
- level: Level = Level.INFO,
99
- details: dict | None = None,
100
- ) -> _ContainerContext:
101
- container = Container(
102
- path,
103
- scope=scope,
104
- identifier=identifier,
105
- description=description,
106
- score=0.0,
107
- level=level,
108
- details=details,
109
- )
110
- node = _ContainerNode(container)
111
- return _ContainerContext(self, node)
112
-
113
- def add_check(
114
- self,
115
- path: str,
116
- *,
117
- scope: Scope | None = None,
118
- identifier: str | None = None,
119
- description: str | None = None,
120
- level: Level = Level.INFO,
121
- score: float = 0.0,
122
- details: dict | None = None,
123
- observable_chain: Sequence[dict[str, Any]] | None = None,
124
- ) -> ResultCheck:
125
- with self._lock:
126
- check_scope = scope
127
- if check_scope is None:
128
- if not self._stack:
129
- raise ValueError("Scope must be provided when adding a check outside a container context")
130
- check_scope = self._stack[-1].container.scope
131
- result_check = ResultCheck.create(
132
- path,
133
- scope=check_scope,
134
- identifier=identifier,
135
- description=description,
136
- level=level,
137
- score=score,
138
- details=details,
139
- )
140
- if self._stack:
141
- self._stack[-1].container.contain(result_check)
142
- else:
143
- self._roots.append(result_check)
144
- if observable_chain:
145
- result_check.add_observable_chain(observable_chain)
146
- return result_check
147
-
148
- def build(self) -> Report:
149
- with self._lock:
150
- roots = list(self._roots)
151
- self._roots.clear()
152
- for root in roots:
153
- root.accept(self.report)
154
- return self.report
155
-
156
- def add_existing(self, node: ContainableSLM) -> ContainableSLM:
157
- with self._lock:
158
- if self._stack:
159
- self._stack[-1].container.contain(node)
160
- else:
161
- self._roots.append(node)
162
- return node
163
-
164
- def extend_existing(self, nodes: Iterable[ContainableSLM]) -> None:
165
- for node in nodes:
166
- self.add_existing(node)
167
-
168
- # Internal helpers -------------------------------------------------
169
- def _enter_container(self, node: _ContainerNode) -> None:
170
- with self._lock:
171
- if self._stack:
172
- parent = self._stack[-1].container
173
- parent.contain(node.container)
174
- else:
175
- self._roots.append(node.container)
176
- self._stack.append(node)
177
-
178
- def _exit_container(self, node: _ContainerNode) -> None:
179
- with self._lock:
180
- if not self._stack or self._stack[-1] is not node:
181
- raise RuntimeError("Container context stack is inconsistent")
182
- self._stack.pop()
cyvest/check_tree.py DELETED
@@ -1,117 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from collections import OrderedDict, defaultdict
4
- from collections.abc import Iterable
5
-
6
- from .models import ContainableSLM, Container, Level, ResultCheck
7
-
8
-
9
- class CheckTree:
10
- """Canonical structure that stores containers/result checks and keeps their scores in sync."""
11
-
12
- def __init__(self) -> None:
13
- self._nodes: dict[str, ContainableSLM] = {}
14
- self._roots: dict[str, OrderedDict[str, ContainableSLM]] = defaultdict(OrderedDict)
15
-
16
- def integrate_container(self, container: Container) -> Container:
17
- return self._integrate_container(container, seen=set())
18
-
19
- def integrate_result_check(self, result_check: ResultCheck) -> ResultCheck:
20
- return self._integrate_result_check(result_check, seen=set())
21
-
22
- def _integrate_container(self, container: Container, seen: set[int]) -> Container:
23
- if id(container) in seen:
24
- existing = self._nodes.get(container.full_key)
25
- if isinstance(existing, Container):
26
- return existing
27
- seen.add(id(container))
28
- parent = container.parent
29
- if parent is not None:
30
- parent = self._integrate_container(parent, seen)
31
- container.set_parent(parent)
32
- key = container.full_key
33
- existing = self._nodes.get(key)
34
- if isinstance(existing, Container):
35
- existing.merge_from(container)
36
- target = existing
37
- else:
38
- target = container
39
- self._nodes[key] = target
40
- if target.parent is not None:
41
- target.parent.attach_child(target)
42
- else:
43
- root_scope = target.scope.name if target.scope else "GLOBAL"
44
- self._roots[root_scope][key] = target
45
- # integrate children
46
- for child in list(container.children):
47
- if isinstance(child, Container):
48
- child.set_parent(target)
49
- self._integrate_container(child, seen)
50
- else:
51
- child.set_parent(target)
52
- self._integrate_result_check(child, seen)
53
- target.recompute()
54
- self._recompute_upwards(target.parent)
55
- return target
56
-
57
- def _integrate_result_check(self, result_check: ResultCheck, seen: set[int]) -> ResultCheck:
58
- if id(result_check) in seen:
59
- existing = self._nodes.get(result_check.full_key)
60
- if isinstance(existing, ResultCheck):
61
- return existing
62
- seen.add(id(result_check))
63
- parent = result_check.parent
64
- if parent is not None:
65
- parent = self._integrate_container(parent, seen)
66
- result_check.set_parent(parent)
67
- key = result_check.full_key
68
- existing = self._nodes.get(key)
69
- if isinstance(existing, ResultCheck):
70
- existing.merge_from(result_check)
71
- target = existing
72
- else:
73
- target = result_check
74
- self._nodes[key] = target
75
- if target.parent is not None:
76
- target.parent.attach_child(target)
77
- else:
78
- scope_name = target.scope.name if target.scope else "GLOBAL"
79
- self._roots[scope_name][key] = target
80
- self._recompute_upwards(target.parent)
81
- return target
82
-
83
- def _recompute_upwards(self, node: Container | None) -> None:
84
- current = node
85
- while current is not None:
86
- current.recompute()
87
- current = current.parent
88
-
89
- def get(self, full_key: str) -> ContainableSLM | None:
90
- return self._nodes.get(full_key)
91
-
92
- def total_score(self) -> float:
93
- return sum(node.score for node in self._nodes.values() if isinstance(node, ResultCheck))
94
-
95
- def highest_level(self) -> Level:
96
- highest = Level.INFO
97
- for node in self._nodes.values():
98
- if node.level > highest:
99
- highest = node.level
100
- return highest
101
-
102
- def result_checks(self) -> Iterable[ResultCheck]:
103
- for node in self._nodes.values():
104
- if isinstance(node, ResultCheck):
105
- yield node
106
-
107
- def containers(self) -> Iterable[Container]:
108
- for node in self._nodes.values():
109
- if isinstance(node, Container):
110
- yield node
111
-
112
- def roots_for_scope(self, scope_name: str) -> list[ContainableSLM]:
113
- return list(self._roots.get(scope_name, {}).values())
114
-
115
- def iter_roots(self) -> Iterable[tuple[str, list[ContainableSLM]]]:
116
- for scope_name, nodes in self._roots.items():
117
- yield scope_name, list(nodes.values())