serenecode 0.1.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.
Files changed (39) hide show
  1. serenecode/__init__.py +281 -0
  2. serenecode/adapters/__init__.py +6 -0
  3. serenecode/adapters/coverage_adapter.py +1173 -0
  4. serenecode/adapters/crosshair_adapter.py +1069 -0
  5. serenecode/adapters/hypothesis_adapter.py +1824 -0
  6. serenecode/adapters/local_fs.py +169 -0
  7. serenecode/adapters/module_loader.py +492 -0
  8. serenecode/adapters/mypy_adapter.py +161 -0
  9. serenecode/checker/__init__.py +6 -0
  10. serenecode/checker/compositional.py +2216 -0
  11. serenecode/checker/coverage.py +186 -0
  12. serenecode/checker/properties.py +154 -0
  13. serenecode/checker/structural.py +1504 -0
  14. serenecode/checker/symbolic.py +178 -0
  15. serenecode/checker/types.py +148 -0
  16. serenecode/cli.py +478 -0
  17. serenecode/config.py +711 -0
  18. serenecode/contracts/__init__.py +6 -0
  19. serenecode/contracts/predicates.py +176 -0
  20. serenecode/core/__init__.py +6 -0
  21. serenecode/core/exceptions.py +38 -0
  22. serenecode/core/pipeline.py +807 -0
  23. serenecode/init.py +307 -0
  24. serenecode/models.py +308 -0
  25. serenecode/ports/__init__.py +6 -0
  26. serenecode/ports/coverage_analyzer.py +124 -0
  27. serenecode/ports/file_system.py +95 -0
  28. serenecode/ports/property_tester.py +69 -0
  29. serenecode/ports/symbolic_checker.py +70 -0
  30. serenecode/ports/type_checker.py +66 -0
  31. serenecode/reporter.py +346 -0
  32. serenecode/source_discovery.py +319 -0
  33. serenecode/templates/__init__.py +5 -0
  34. serenecode/templates/content.py +337 -0
  35. serenecode-0.1.0.dist-info/METADATA +298 -0
  36. serenecode-0.1.0.dist-info/RECORD +39 -0
  37. serenecode-0.1.0.dist-info/WHEEL +4 -0
  38. serenecode-0.1.0.dist-info/entry_points.txt +2 -0
  39. serenecode-0.1.0.dist-info/licenses/LICENSE +21 -0
serenecode/__init__.py ADDED
@@ -0,0 +1,281 @@
1
+ """Serenecode — formal verification framework for AI-generated Python code.
2
+
3
+ This module provides the public API surface for Serenecode. It enables
4
+ programmatic access to project initialization and all verification levels.
5
+ This is a composition root — it wires adapters to ports and delegates
6
+ to core logic via the pipeline.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import icontract
12
+
13
+ from serenecode.adapters.local_fs import LocalFileReader, LocalFileWriter
14
+ from serenecode.config import default_config, parse_serenecode_md
15
+ from serenecode.contracts.predicates import (
16
+ is_non_empty_string,
17
+ is_valid_file_path_string,
18
+ is_valid_template_name,
19
+ is_valid_verification_level,
20
+ )
21
+ from serenecode.core.exceptions import UnsafeCodeExecutionError
22
+ from serenecode.core.pipeline import run_pipeline
23
+ from serenecode.init import InitResult, initialize_project
24
+ from serenecode.models import CheckResult
25
+ from serenecode.source_discovery import build_source_files, find_serenecode_md
26
+
27
+ _TRUST_REQUIRED_MESSAGE = (
28
+ "Levels 3-6 import and execute project modules. "
29
+ "Pass allow_code_execution=True only for trusted code."
30
+ )
31
+
32
+
33
+ @icontract.require(lambda path: is_non_empty_string(path), "path must be a non-empty string")
34
+ @icontract.require(lambda path: is_valid_file_path_string(path), "path must be a valid path string")
35
+ @icontract.require(lambda template: is_valid_template_name(template), "template must be a valid template name")
36
+ @icontract.ensure(lambda result: isinstance(result, InitResult), "result must be an InitResult")
37
+ def init(path: str = ".", template: str = "default") -> InitResult:
38
+ """Initialize a Serenecode project.
39
+
40
+ Args:
41
+ path: Project root directory.
42
+ template: Template name ('default', 'strict', or 'minimal').
43
+
44
+ Returns:
45
+ An InitResult describing what was created.
46
+ """
47
+ reader = LocalFileReader()
48
+ writer = LocalFileWriter()
49
+ return initialize_project(
50
+ directory=path,
51
+ template=template,
52
+ file_reader=reader,
53
+ file_writer=writer,
54
+ )
55
+
56
+
57
+ @icontract.require(lambda path: is_non_empty_string(path), "path must be a non-empty string")
58
+ @icontract.require(lambda path: is_valid_file_path_string(path), "path must be a valid path string")
59
+ @icontract.require(lambda level: is_valid_verification_level(level), "level must be between 1 and 6")
60
+ @icontract.ensure(lambda result: isinstance(result, CheckResult), "result must be a CheckResult")
61
+ def check(
62
+ path: str = ".",
63
+ level: int = 6,
64
+ allow_code_execution: bool = False,
65
+ ) -> CheckResult:
66
+ """Run verification up to the specified level.
67
+
68
+ Uses the full pipeline (L1 through the requested level),
69
+ wiring adapters for each level that has a backend available.
70
+
71
+ Args:
72
+ path: File or directory to check.
73
+ level: Maximum verification level (1-6).
74
+ allow_code_execution: Explicit opt-in for Levels 3-6, which import
75
+ and execute project modules.
76
+
77
+ Returns:
78
+ A CheckResult with all findings.
79
+ """
80
+ return _run_check(path, level, allow_code_execution=allow_code_execution)
81
+
82
+
83
+ @icontract.require(lambda path: is_non_empty_string(path), "path must be a non-empty string")
84
+ @icontract.require(lambda path: is_valid_file_path_string(path), "path must be a valid path string")
85
+ @icontract.ensure(lambda result: isinstance(result, CheckResult), "result must be a CheckResult")
86
+ def check_structural(path: str = ".") -> CheckResult:
87
+ """Run only the structural checker (Level 1).
88
+
89
+ Args:
90
+ path: File or directory to check.
91
+
92
+ Returns:
93
+ A CheckResult with structural findings only.
94
+ """
95
+ return _run_check(path, level=1)
96
+
97
+
98
+ @icontract.require(lambda path: is_non_empty_string(path), "path must be a non-empty string")
99
+ @icontract.require(lambda path: is_valid_file_path_string(path), "path must be a valid path string")
100
+ @icontract.ensure(lambda result: isinstance(result, CheckResult), "result must be a CheckResult")
101
+ def check_types(path: str = ".") -> CheckResult:
102
+ """Run the Level 2 type checker."""
103
+ return _run_check(path, level=2)
104
+
105
+
106
+ @icontract.require(lambda path: is_non_empty_string(path), "path must be a non-empty string")
107
+ @icontract.require(lambda path: is_valid_file_path_string(path), "path must be a valid path string")
108
+ @icontract.ensure(lambda result: isinstance(result, CheckResult), "result must be a CheckResult")
109
+ def check_coverage(
110
+ path: str = ".",
111
+ allow_code_execution: bool = False,
112
+ ) -> CheckResult:
113
+ """Run verification through Level 3 (coverage analysis).
114
+
115
+ Args:
116
+ path: File or directory to check.
117
+ allow_code_execution: Explicit opt-in because Levels 3-6 import and
118
+ execute project modules.
119
+
120
+ Returns:
121
+ CheckResult through Level 3.
122
+ """
123
+ return _run_check(path, level=3, allow_code_execution=allow_code_execution)
124
+
125
+
126
+ @icontract.require(lambda path: is_non_empty_string(path), "path must be a non-empty string")
127
+ @icontract.require(lambda path: is_valid_file_path_string(path), "path must be a valid path string")
128
+ @icontract.ensure(lambda result: isinstance(result, CheckResult), "result must be a CheckResult")
129
+ def check_properties(
130
+ path: str = ".",
131
+ allow_code_execution: bool = False,
132
+ ) -> CheckResult:
133
+ """Run property-based verification through Level 4.
134
+
135
+ Args:
136
+ path: File or directory to check.
137
+ allow_code_execution: Explicit opt-in because Level 4 imports and
138
+ executes project modules.
139
+
140
+ Returns:
141
+ A CheckResult with findings through Level 4.
142
+ """
143
+ return _run_check(path, level=4, allow_code_execution=allow_code_execution)
144
+
145
+
146
+ @icontract.require(lambda path: is_non_empty_string(path), "path must be a non-empty string")
147
+ @icontract.require(lambda path: is_valid_file_path_string(path), "path must be a valid path string")
148
+ @icontract.ensure(lambda result: isinstance(result, CheckResult), "result must be a CheckResult")
149
+ def check_symbolic(
150
+ path: str = ".",
151
+ allow_code_execution: bool = False,
152
+ ) -> CheckResult:
153
+ """Run symbolic verification through Level 5.
154
+
155
+ Args:
156
+ path: File or directory to check.
157
+ allow_code_execution: Explicit opt-in because Levels 3-6 import and
158
+ execute project modules.
159
+
160
+ Returns:
161
+ A CheckResult with findings through Level 5.
162
+ """
163
+ return _run_check(path, level=5, allow_code_execution=allow_code_execution)
164
+
165
+
166
+ @icontract.require(lambda path: is_non_empty_string(path), "path must be a non-empty string")
167
+ @icontract.require(lambda path: is_valid_file_path_string(path), "path must be a valid path string")
168
+ @icontract.ensure(lambda result: isinstance(result, CheckResult), "result must be a CheckResult")
169
+ def check_compositional(
170
+ path: str = ".",
171
+ allow_code_execution: bool = False,
172
+ ) -> CheckResult:
173
+ """Run compositional verification through Level 6.
174
+
175
+ Args:
176
+ path: File or directory to check.
177
+ allow_code_execution: Explicit opt-in because Levels 3-6 import and
178
+ execute project modules during the full pipeline.
179
+
180
+ Returns:
181
+ A CheckResult with findings through Level 6.
182
+ """
183
+ return _run_check(path, level=6, allow_code_execution=allow_code_execution)
184
+
185
+
186
+ @icontract.require(lambda path: is_non_empty_string(path), "path must be a non-empty string")
187
+ @icontract.require(lambda path: is_valid_file_path_string(path), "path must be a valid path string")
188
+ @icontract.ensure(lambda result: isinstance(result, CheckResult), "result must be a CheckResult")
189
+ def status(path: str = ".") -> CheckResult:
190
+ """Show verification status of the codebase.
191
+
192
+ Args:
193
+ path: File or directory to check.
194
+
195
+ Returns:
196
+ A CheckResult showing current verification state.
197
+ """
198
+ return _run_check(path, level=1)
199
+
200
+
201
+ @icontract.require(lambda path: is_non_empty_string(path), "path must be a non-empty string")
202
+ @icontract.require(lambda path: is_valid_file_path_string(path), "path must be a valid path string")
203
+ @icontract.require(lambda level: is_valid_verification_level(level), "level must be between 1 and 6")
204
+ @icontract.ensure(lambda result: isinstance(result, CheckResult), "result must be a CheckResult")
205
+ def _run_check(
206
+ path: str,
207
+ level: int,
208
+ allow_code_execution: bool = False,
209
+ ) -> CheckResult:
210
+ """Internal helper to run checks via the real pipeline.
211
+
212
+ Args:
213
+ path: File or directory to check.
214
+ level: Maximum verification level (1-6).
215
+ allow_code_execution: Explicit opt-in for Levels 3-6, which import
216
+ and execute project modules.
217
+
218
+ Returns:
219
+ Aggregated CheckResult.
220
+ """
221
+ if level >= 3 and not allow_code_execution:
222
+ raise UnsafeCodeExecutionError(_TRUST_REQUIRED_MESSAGE)
223
+
224
+ reader = LocalFileReader()
225
+
226
+ # Load config
227
+ serenecode_path = find_serenecode_md(path, reader)
228
+ if serenecode_path:
229
+ config_content = reader.read_file(serenecode_path)
230
+ config = parse_serenecode_md(config_content)
231
+ else:
232
+ config = default_config()
233
+
234
+ # List and build source files
235
+ files = reader.list_python_files(path)
236
+ source_files = build_source_files(files, reader, path)
237
+
238
+ # Wire up adapters for higher levels
239
+ type_checker = None
240
+ coverage_analyzer = None
241
+ property_tester = None
242
+ symbolic_checker = None
243
+
244
+ if level >= 2:
245
+ try:
246
+ from serenecode.adapters.mypy_adapter import MypyTypeChecker
247
+ type_checker = MypyTypeChecker()
248
+ except ImportError:
249
+ pass
250
+
251
+ if level >= 3:
252
+ try:
253
+ from serenecode.adapters.coverage_adapter import CoverageAnalyzerAdapter
254
+ coverage_analyzer = CoverageAnalyzerAdapter(allow_code_execution=True)
255
+ except ImportError:
256
+ pass
257
+
258
+ if level >= 4:
259
+ try:
260
+ from serenecode.adapters.hypothesis_adapter import HypothesisPropertyTester
261
+ property_tester = HypothesisPropertyTester(allow_code_execution=True)
262
+ except ImportError:
263
+ pass
264
+
265
+ if level >= 5:
266
+ try:
267
+ from serenecode.adapters.crosshair_adapter import CrossHairSymbolicChecker
268
+ symbolic_checker = CrossHairSymbolicChecker(allow_code_execution=True)
269
+ except ImportError:
270
+ pass
271
+
272
+ return run_pipeline(
273
+ source_files=source_files,
274
+ level=level,
275
+ start_level=1,
276
+ config=config,
277
+ type_checker=type_checker,
278
+ coverage_analyzer=coverage_analyzer,
279
+ property_tester=property_tester,
280
+ symbolic_checker=symbolic_checker,
281
+ )
@@ -0,0 +1,6 @@
1
+ """Adapter implementations for Serenecode.
2
+
3
+ This package contains I/O implementations of the Protocol interfaces
4
+ defined in the ports package. Adapters handle file system access,
5
+ subprocess execution, and external tool integration.
6
+ """