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
@@ -0,0 +1,161 @@
1
+ """Mypy adapter for static type checking (Level 2).
2
+
3
+ This adapter implements the TypeChecker protocol by running mypy
4
+ as a subprocess and parsing its output into structured TypeIssue
5
+ objects.
6
+
7
+ This is an adapter module — it handles I/O (subprocess execution)
8
+ and is exempt from full contract requirements.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import re
14
+ import subprocess
15
+ import sys
16
+ import os
17
+
18
+ import icontract
19
+
20
+ from serenecode.contracts.predicates import is_positive_int
21
+ from serenecode.core.exceptions import ToolNotInstalledError
22
+ from serenecode.ports.type_checker import TypeIssue
23
+
24
+ _MYPY_OUTPUT_PATTERN = re.compile(
25
+ r"^(.+?):(\d+)(?::(\d+))?: (error|warning|note): (.+?)(?:\s+\[(.+?)\])?$"
26
+ )
27
+
28
+
29
+ @icontract.invariant(lambda self: is_positive_int(self._timeout), "timeout must be positive")
30
+ class MypyTypeChecker:
31
+ """Type checker implementation using mypy.
32
+
33
+ Runs mypy as a subprocess with strict mode and parses
34
+ its output into structured TypeIssue objects.
35
+ """
36
+
37
+ @icontract.require(lambda timeout: is_positive_int(timeout), "timeout must be positive")
38
+ @icontract.ensure(lambda result: result is None, "result must be None")
39
+ def __init__(self, timeout: int = 120) -> None:
40
+ """Initialize the checker.
41
+
42
+ Args:
43
+ timeout: Maximum seconds for mypy to run.
44
+ """
45
+ self._timeout = timeout
46
+
47
+ @icontract.require(lambda file_paths: isinstance(file_paths, list), "file_paths must be a list")
48
+ @icontract.ensure(lambda result: isinstance(result, list), "result must be a list")
49
+ def check(
50
+ self,
51
+ file_paths: list[str],
52
+ strict: bool = True,
53
+ search_paths: tuple[str, ...] = (),
54
+ ) -> list[TypeIssue]:
55
+ """Run mypy type checking on the given files.
56
+
57
+ Args:
58
+ file_paths: Paths to Python files to check.
59
+ strict: Whether to use strict mode.
60
+ search_paths: Import roots needed to resolve project-local modules.
61
+
62
+ Returns:
63
+ List of type issues found.
64
+
65
+ Raises:
66
+ ToolNotInstalledError: If mypy is not installed.
67
+ """
68
+ if not file_paths:
69
+ return []
70
+
71
+ cmd = [sys.executable, "-m", "mypy"]
72
+ if strict:
73
+ cmd.append("--strict")
74
+ cmd.extend([
75
+ "--no-error-summary",
76
+ "--show-error-codes",
77
+ "--no-color",
78
+ ])
79
+ cmd.extend(file_paths)
80
+
81
+ try:
82
+ env = dict(os.environ)
83
+ existing = env.get("MYPYPATH", "")
84
+ if search_paths:
85
+ combined = list(search_paths)
86
+ if existing:
87
+ combined.append(existing)
88
+ env["MYPYPATH"] = os.pathsep.join(combined)
89
+ result = subprocess.run(
90
+ cmd,
91
+ capture_output=True,
92
+ text=True,
93
+ timeout=self._timeout,
94
+ env=env,
95
+ )
96
+ except FileNotFoundError as exc:
97
+ raise ToolNotInstalledError(
98
+ "mypy is not installed. Install with: pip install mypy"
99
+ ) from exc
100
+ except subprocess.TimeoutExpired:
101
+ return [TypeIssue(
102
+ file="<timeout>",
103
+ line=0,
104
+ column=0,
105
+ severity="error",
106
+ message=f"mypy timed out after {self._timeout}s",
107
+ )]
108
+ combined_output = "\n".join(
109
+ chunk for chunk in (result.stdout, result.stderr) if chunk
110
+ )
111
+ issues = self._parse_output(combined_output)
112
+ if result.returncode == 0:
113
+ return issues
114
+
115
+ stderr_text = result.stderr.strip()
116
+ if "No module named mypy" in stderr_text:
117
+ raise ToolNotInstalledError(
118
+ "mypy is not installed. Install with: pip install mypy"
119
+ )
120
+
121
+ if issues:
122
+ return issues
123
+
124
+ fallback_message = stderr_text or result.stdout.strip() or (
125
+ f"mypy failed with exit code {result.returncode}"
126
+ )
127
+ return [TypeIssue(
128
+ file="<mypy>",
129
+ line=0,
130
+ column=0,
131
+ severity="error",
132
+ message=fallback_message,
133
+ )]
134
+
135
+ @icontract.require(lambda output: isinstance(output, str), "output must be a string")
136
+ @icontract.ensure(lambda result: isinstance(result, list), "result must be a list")
137
+ def _parse_output(self, output: str) -> list[TypeIssue]:
138
+ """Parse mypy stdout into TypeIssue objects.
139
+
140
+ Args:
141
+ output: mypy stdout content.
142
+
143
+ Returns:
144
+ List of parsed type issues.
145
+ """
146
+ issues: list[TypeIssue] = []
147
+
148
+ # Loop invariant: issues contains parsed results for lines[0..i]
149
+ for line in output.splitlines():
150
+ match = _MYPY_OUTPUT_PATTERN.match(line.strip())
151
+ if match:
152
+ issues.append(TypeIssue(
153
+ file=match.group(1),
154
+ line=int(match.group(2)),
155
+ column=int(match.group(3)) if match.group(3) else 0,
156
+ severity=match.group(4),
157
+ message=match.group(5),
158
+ code=match.group(6),
159
+ ))
160
+
161
+ return issues
@@ -0,0 +1,6 @@
1
+ """Verification engines for Serenecode.
2
+
3
+ This package contains the checker modules that implement each level
4
+ of the verification pipeline. Checker modules are pure — they receive
5
+ source code as strings and return structured results.
6
+ """