Skip to content

Commit 31447f9

Browse files
committed
Use script to improve error reporting
1 parent 4e1b0db commit 31447f9

2 files changed

Lines changed: 155 additions & 29 deletions

File tree

‎.github/workflows/build.yml‎

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,37 +26,11 @@ jobs:
2626
pip install -r requirements.txt
2727
2828
# Static analysis tools
29-
- name: Install static analysis tools
29+
- name: Static Code Analysis
3030
if: runner.os == 'Linux'
3131
run: |
32-
pip install mypy==1.8.0
33-
pip install flake8==7.0.0
34-
pip install black==24.1.1
35-
36-
- name: Run static code analysis
37-
if: runner.os == 'Linux'
38-
run: |
39-
# Critical checks
40-
mypy_output=$(mypy lean/ \
41-
--ignore-missing-imports \
42-
--check-untyped-defs \
43-
--show-error-codes \
44-
--no-error-summary 2>&1 || true)
45-
46-
if echo "$mypy_output" | grep -q -E "Missing positional argument|\[call-arg\]"; then
47-
echo "ERROR: Missing function arguments detected:"
48-
echo ""
49-
echo "$mypy_output" | grep -E "Missing positional argument|\[call-arg\]"
50-
echo ""
51-
echo "When adding/removing parameters from methods, ensure all call sites are updated."
52-
exit 1
53-
fi
54-
55-
flake8 lean/ --select=F821 --ignore=ALL
56-
57-
# Warning checks (don't fail the build)
58-
flake8 lean/ --select=F401 --ignore=ALL --exit-zero
59-
black --check lean/ --quiet || true
32+
pip install mypy==1.15.0 flake8==7.0.0
33+
python static_analysis.py
6034
6135
- name: Run tests
6236
run: python -m pytest -s -rs

‎static_analysis.py‎

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import subprocess
2+
import sys
3+
4+
def display_warning_summary(warnings):
5+
print("\nWarnings:")
6+
unused_count = sum(1 for e in warnings if e.startswith('F401:'))
7+
if unused_count > 0:
8+
print(f" - Unused imports: {unused_count}")
9+
10+
print(" Consider addressing warnings in future updates.")
11+
12+
def run_analysis():
13+
print("Running static analysis...")
14+
print("=" * 60)
15+
16+
all_critical_errors = []
17+
all_warnings = []
18+
19+
# Check for missing arguments with mypy - CRITICAL
20+
print("\n1. Checking for missing function arguments...")
21+
print("-" * 40)
22+
23+
result = subprocess.run(
24+
["python", "-m", "mypy", "lean/",
25+
"--show-error-codes",
26+
"--no-error-summary",
27+
"--ignore-missing-imports",
28+
"--check-untyped-defs"],
29+
capture_output=True,
30+
text=True
31+
)
32+
33+
# Filter for critical call argument mismatches
34+
call_arg_errors = []
35+
36+
for line in (result.stdout + result.stderr).split('\n'):
37+
if not line.strip():
38+
continue
39+
40+
# Look for call-arg errors (this covers both "too many" and "missing" arguments)
41+
if '[call-arg]' in line:
42+
# Skip false positives
43+
if any(pattern in line for pattern in
44+
['click.', 'subprocess.', 'Module "', 'has incompatible type "Optional',
45+
'validator', 'pydantic', '__call__', 'OnlyValueValidator', 'V1Validator',
46+
'QCParameter', 'QCBacktest']):
47+
continue
48+
call_arg_errors.append(line.strip())
49+
50+
# Display call argument mismatches
51+
if call_arg_errors:
52+
print("CRITICAL: Missing function arguments found:")
53+
for error in call_arg_errors:
54+
# Clean path for better display
55+
clean_error = error.replace('/home/runner/work/lean-cli/lean-cli/', '')
56+
print(f" {clean_error}")
57+
58+
all_critical_errors.extend(call_arg_errors)
59+
else:
60+
print("No argument mismatch errors found")
61+
62+
# Check for undefined variables with flake8 - CRITICAL
63+
print("\n2. Checking for undefined variables...")
64+
print("-" * 40)
65+
66+
result = subprocess.run(
67+
["python", "-m", "flake8", "lean/",
68+
"--select=F821",
69+
"--ignore=ALL",
70+
"--count"],
71+
capture_output=True,
72+
text=True
73+
)
74+
75+
if result.stdout.strip() and result.stdout.strip() != "0":
76+
detail = subprocess.run(
77+
["python", "-m", "flake8", "lean/", "--select=F821", "--ignore=ALL"],
78+
capture_output=True,
79+
text=True
80+
)
81+
82+
undefined_errors = [e.strip() for e in detail.stdout.split('\n') if e.strip()]
83+
print(f"CRITICAL: {len(undefined_errors)} undefined variable(s) found:")
84+
85+
for error in undefined_errors:
86+
print(f" {error}")
87+
88+
all_critical_errors.extend([f"F821: {e}" for e in undefined_errors])
89+
else:
90+
print("No undefined variables found")
91+
92+
# Check for unused imports with flake8 - WARNING
93+
print("\n3. Checking for unused imports...")
94+
print("-" * 40)
95+
96+
result = subprocess.run(
97+
["python", "-m", "flake8", "lean/",
98+
"--select=F401",
99+
"--ignore=ALL",
100+
"--count",
101+
"--exit-zero"],
102+
capture_output=True,
103+
text=True
104+
)
105+
106+
if result.stdout.strip() and result.stdout.strip() != "0":
107+
detail = subprocess.run(
108+
["python", "-m", "flake8", "lean/", "--select=F401", "--ignore=ALL", "--exit-zero"],
109+
capture_output=True,
110+
text=True
111+
)
112+
113+
unused_imports = [e.strip() for e in detail.stdout.split('\n') if e.strip()]
114+
if unused_imports:
115+
print(f"WARNING: {len(unused_imports)} unused import(s) found:")
116+
117+
for error in unused_imports:
118+
print(f" {error}")
119+
120+
all_warnings.extend([f"F401: {e}" for e in unused_imports])
121+
else:
122+
print("No unused imports found")
123+
else:
124+
print("No unused imports found")
125+
126+
print("\n" + "=" * 60)
127+
128+
# Summary
129+
if all_critical_errors:
130+
total_errors = len(all_critical_errors)
131+
print(f"BUILD FAILED: Found {total_errors} critical error(s)")
132+
133+
print("\nSummary of critical errors:")
134+
print(f" - Function call argument mismatches: {len(call_arg_errors)}")
135+
undefined_count = sum(1 for e in all_critical_errors if e.startswith('F821:'))
136+
print(f" - Undefined variables: {undefined_count}")
137+
138+
if all_warnings:
139+
display_warning_summary(all_warnings)
140+
141+
return 1
142+
143+
if all_warnings:
144+
print(f"BUILD PASSED with {len(all_warnings)} warning(s)")
145+
display_warning_summary(all_warnings)
146+
return 0
147+
148+
print("SUCCESS: All checks passed with no warnings")
149+
return 0
150+
151+
if __name__ == "__main__":
152+
sys.exit(run_analysis())

0 commit comments

Comments
 (0)