tarang 4.4.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.
@@ -0,0 +1,184 @@
1
+ """
2
+ Shadow Linter - Run linting after applying changes.
3
+
4
+ Auto-detects project type and runs appropriate linters.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import shutil
9
+ import subprocess
10
+ from dataclasses import dataclass, field
11
+ from pathlib import Path
12
+ from typing import Dict, List, Optional
13
+
14
+
15
+ @dataclass
16
+ class LintResult:
17
+ """Result of linting."""
18
+ success: bool
19
+ errors: List[str] = field(default_factory=list)
20
+ warnings: List[str] = field(default_factory=list)
21
+ tool: str = "none"
22
+
23
+
24
+ class ShadowLinter:
25
+ """
26
+ Run linting in the background after applying changes.
27
+
28
+ Auto-detects project type and runs appropriate linter.
29
+ """
30
+
31
+ LINTER_CONFIGS: Dict[str, Dict] = {
32
+ "python": {
33
+ "detect": ["pyproject.toml", "setup.py", "requirements.txt"],
34
+ "commands": [
35
+ ["python", "-m", "py_compile", "{file}"], # Syntax check
36
+ ["ruff", "check", "{file}"], # Fast linter
37
+ ]
38
+ },
39
+ "javascript": {
40
+ "detect": ["package.json"],
41
+ "commands": [
42
+ ["npx", "eslint", "{file}"],
43
+ ]
44
+ },
45
+ "typescript": {
46
+ "detect": ["tsconfig.json"],
47
+ "commands": [
48
+ ["npx", "tsc", "--noEmit"],
49
+ ]
50
+ },
51
+ "rust": {
52
+ "detect": ["Cargo.toml"],
53
+ "commands": [
54
+ ["cargo", "check"],
55
+ ]
56
+ },
57
+ "go": {
58
+ "detect": ["go.mod"],
59
+ "commands": [
60
+ ["go", "vet", "{file}"],
61
+ ]
62
+ },
63
+ }
64
+
65
+ def __init__(self, project_root: Path):
66
+ self.project_root = project_root
67
+ self.project_type = self._detect_project_type()
68
+
69
+ def lint_file(self, file_path: str) -> LintResult:
70
+ """
71
+ Run linters on a modified file.
72
+
73
+ Args:
74
+ file_path: Path to file (relative to project root)
75
+
76
+ Returns:
77
+ LintResult with errors/warnings
78
+ """
79
+ if not self.project_type:
80
+ return LintResult(success=True, tool="none")
81
+
82
+ config = self.LINTER_CONFIGS.get(self.project_type, {})
83
+ commands = config.get("commands", [])
84
+
85
+ errors = []
86
+ warnings = []
87
+
88
+ for cmd_template in commands:
89
+ cmd = [
90
+ part.replace("{file}", file_path)
91
+ for part in cmd_template
92
+ ]
93
+
94
+ # Check if command exists
95
+ if not shutil.which(cmd[0]):
96
+ continue
97
+
98
+ try:
99
+ result = subprocess.run(
100
+ cmd,
101
+ cwd=self.project_root,
102
+ capture_output=True,
103
+ timeout=60
104
+ )
105
+
106
+ if result.returncode != 0:
107
+ output = result.stderr.decode() or result.stdout.decode()
108
+ errors.append(f"{cmd[0]}: {output}")
109
+
110
+ except subprocess.TimeoutExpired:
111
+ warnings.append(f"{cmd[0]} timed out")
112
+ except FileNotFoundError:
113
+ continue
114
+
115
+ return LintResult(
116
+ success=len(errors) == 0,
117
+ errors=errors,
118
+ warnings=warnings,
119
+ tool=self.project_type,
120
+ )
121
+
122
+ def lint_build(self) -> LintResult:
123
+ """
124
+ Run full project build/check.
125
+
126
+ Returns:
127
+ LintResult with build errors/warnings
128
+ """
129
+ build_commands = {
130
+ "python": ["python", "-m", "py_compile"],
131
+ "javascript": ["npm", "run", "build"],
132
+ "typescript": ["npm", "run", "build"],
133
+ "rust": ["cargo", "build"],
134
+ "go": ["go", "build", "./..."],
135
+ }
136
+
137
+ if self.project_type not in build_commands:
138
+ return LintResult(success=True, tool="none")
139
+
140
+ cmd = build_commands[self.project_type]
141
+
142
+ # Check if command exists
143
+ if not shutil.which(cmd[0]):
144
+ return LintResult(success=True, tool="none")
145
+
146
+ try:
147
+ result = subprocess.run(
148
+ cmd,
149
+ cwd=self.project_root,
150
+ capture_output=True,
151
+ timeout=300
152
+ )
153
+
154
+ errors = []
155
+ if result.returncode != 0:
156
+ errors = [result.stderr.decode() or result.stdout.decode()]
157
+
158
+ return LintResult(
159
+ success=result.returncode == 0,
160
+ errors=errors,
161
+ warnings=[],
162
+ tool=cmd[0],
163
+ )
164
+
165
+ except subprocess.TimeoutExpired:
166
+ return LintResult(
167
+ success=False,
168
+ errors=["Build timed out"],
169
+ tool=cmd[0],
170
+ )
171
+ except Exception as e:
172
+ return LintResult(
173
+ success=False,
174
+ errors=[str(e)],
175
+ tool="build",
176
+ )
177
+
178
+ def _detect_project_type(self) -> Optional[str]:
179
+ """Detect project type from marker files."""
180
+ for project_type, config in self.LINTER_CONFIGS.items():
181
+ for marker in config.get("detect", []):
182
+ if (self.project_root / marker).exists():
183
+ return project_type
184
+ return None