ballpython 2.0.5__py3-none-any.whl → 2.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.
- ballpython/__init__.py +7 -3
- {ballpython-2.0.5.dist-info → ballpython-2.1.0.dist-info}/METADATA +1 -1
- ballpython-2.1.0.dist-info/RECORD +34 -0
- pycleaner/__init__.py +5 -0
- pycleaner/baseline.py +1 -1
- pycleaner/cache.py +3 -1
- pycleaner/cli.py +62 -37
- pycleaner/complexity_analyzer.py +1 -1
- pycleaner/dead_code_detector.py +561 -16
- pycleaner/dependency_auditor.py +190 -20
- pycleaner/frameworks/__init__.py +1 -1
- pycleaner/frameworks/plugins.py +0 -1
- pycleaner/import_resolver.py +32 -14
- pycleaner/linter_formatter.py +16 -14
- pycleaner/modernizer.py +86 -9
- pycleaner/security_scanner.py +5 -2
- pycleaner/syntax_healer.py +24 -10
- pycleaner/taint_engine.py +16 -5
- pycleaner/test_generator.py +1 -1
- pycleaner/type_checker.py +116 -19
- pycleaner/verifier.py +44 -17
- ballpython-2.0.5.dist-info/RECORD +0 -34
- {ballpython-2.0.5.dist-info → ballpython-2.1.0.dist-info}/WHEEL +0 -0
- {ballpython-2.0.5.dist-info → ballpython-2.1.0.dist-info}/entry_points.txt +0 -0
- {ballpython-2.0.5.dist-info → ballpython-2.1.0.dist-info}/top_level.txt +0 -0
ballpython/__init__.py
CHANGED
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
5
7
|
import pycleaner
|
|
6
|
-
from pycleaner import
|
|
8
|
+
from pycleaner import __all__ as __all__
|
|
9
|
+
from pycleaner import __version__ as __version__
|
|
10
|
+
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
def __getattr__(name: str) -> Any:
|
|
13
|
+
return getattr(pycleaner, name)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
ballpython/__init__.py,sha256=GSB-LHdou1Un6DS6Zmh8tv_zrwh6HQHO3Py2z8GO39o,304
|
|
2
|
+
ballpython/__main__.py,sha256=aCRr0vnZfKYHDrPHBsYSjX5xTxtFJpTTSPVUSZTBK-I,136
|
|
3
|
+
ballpython/cli.py,sha256=LllCLRAQQ-fRR_lIXwmd2e3Q7DLCIvf-5jHBXsAhJWs,125
|
|
4
|
+
ballpython/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
pycleaner/__init__.py,sha256=VzmbjUip6i0wpKh-s6XBwgBkIeUgI1duGyKDIiv7Pls,3042
|
|
6
|
+
pycleaner/__main__.py,sha256=8QBFrPyxId563F6cipPMNVEBTG8TIZUgxQfp8gk1VSI,135
|
|
7
|
+
pycleaner/baseline.py,sha256=ljED3x3akie0SB3fzhVno_1UHlI2id-9vta0NIGC0mw,3857
|
|
8
|
+
pycleaner/cache.py,sha256=T3JIxYg8ZezRbSpXYXPD-SyMUkjg4nfKsdO0IuNypOY,10774
|
|
9
|
+
pycleaner/cli.py,sha256=NswwV_3cbHiL63Pj6qDtSpV_98zXvGo7UuBHwNAxpQ8,73752
|
|
10
|
+
pycleaner/complexity_analyzer.py,sha256=j2ktzgR-isWE4xWMe56N3JFLLuYCi-S84BIlRBe0v-I,14975
|
|
11
|
+
pycleaner/config.py,sha256=XhloyaY_9pdwbQuc3QaAPCerLeXz-i1da4xT50h-pjY,8514
|
|
12
|
+
pycleaner/dead_code_detector.py,sha256=uufGt7EtGusQdtbX3rUCHIrF8IAu-S16jrViBJdnV-g,52092
|
|
13
|
+
pycleaner/dependency_auditor.py,sha256=EmSqr_ayUqmdSZ7Nm4QAT4qfikF9VToLL8eXVecIDpw,23395
|
|
14
|
+
pycleaner/discovery.py,sha256=dt3OmHg886hy_Z0p80ccMyDkITQxz4biaJY_zB3MU_g,6151
|
|
15
|
+
pycleaner/explanations.py,sha256=2OOU_l3YL5o_Br_6fP6MF8HlY75-1JRmw3eTYT2lu8M,14219
|
|
16
|
+
pycleaner/import_resolver.py,sha256=Wgrch335AQVsnDv5-7F0l4Zm2oQPkNebPEgTRa7ayAg,32855
|
|
17
|
+
pycleaner/linter_formatter.py,sha256=wdTjwguPWzkJ9KRMcQpFHEirb3ogc9qay69yiphz16Y,21356
|
|
18
|
+
pycleaner/modernizer.py,sha256=kd9_G5EzdW_O-9zm1hA6v33Sb__UbpMZzSvopF-UK5w,16820
|
|
19
|
+
pycleaner/pipeline.py,sha256=ZG7_9PNz0jBadFrZoiI7juLtUJXglGQhLgQhrLJqEac,20612
|
|
20
|
+
pycleaner/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
+
pycleaner/security_scanner.py,sha256=8nyRtoCjBYAB_sI5azlNsEMm2S0s2zE7fPhc7QCHWR0,23527
|
|
22
|
+
pycleaner/syntax_healer.py,sha256=5VYclqkI3WFnm6yfG0zRLMkHN4QMU4aN4ZYCcSUsmZk,27408
|
|
23
|
+
pycleaner/taint_engine.py,sha256=-bBq38YoQgbO2FHCDxDctZpXWQTqwuS56CEWFNOY000,26870
|
|
24
|
+
pycleaner/test_generator.py,sha256=MM3t0Dx5lyJB5s144IKXq2tXrE8ohKcSvicPR3c9MlU,19680
|
|
25
|
+
pycleaner/type_checker.py,sha256=zkUqi-BNLUzJT0tIECNh7_7GP29Y7_6fMXhVuguwMKg,37881
|
|
26
|
+
pycleaner/typeshed_resolver.py,sha256=N-kWU_CI5DG59lQMkt7wMW9sHYjNCBdfFS42b4y2Kr4,12742
|
|
27
|
+
pycleaner/verifier.py,sha256=QlBwnsrhWWRK3LzvJjpNix474drCNp6LP7FiUgJxMsg,19333
|
|
28
|
+
pycleaner/frameworks/__init__.py,sha256=SOuSRl39af86eoQx7Qm8rTJGD4jsnEvgyhhQ6cVmRVA,3462
|
|
29
|
+
pycleaner/frameworks/plugins.py,sha256=qunMzS3q7xp1GEGxqkuhrnm-raiSRhYQMzmDxsEhSUg,11452
|
|
30
|
+
ballpython-2.1.0.dist-info/METADATA,sha256=98OjKH0i9ih05fQ9JX2eriRljEboAEOVIgk4TwQ9hQ8,3466
|
|
31
|
+
ballpython-2.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
32
|
+
ballpython-2.1.0.dist-info/entry_points.txt,sha256=6ux-5UxoVSmlK5p9M-GDVt24sCdwTQq9wgRDkpMIYU8,81
|
|
33
|
+
ballpython-2.1.0.dist-info/top_level.txt,sha256=u6lmy8RWZ_saxCZAQTL7EijB4sunegOiVyk5fHjpwLk,21
|
|
34
|
+
ballpython-2.1.0.dist-info/RECORD,,
|
pycleaner/__init__.py
CHANGED
|
@@ -20,6 +20,8 @@ from pycleaner.discovery import (
|
|
|
20
20
|
|
|
21
21
|
__version__ = "2.0.5"
|
|
22
22
|
__all__ = [
|
|
23
|
+
"DEFAULT_IGNORED_DIRS",
|
|
24
|
+
"PROTECTED_FILE_PATTERNS",
|
|
23
25
|
"BaselineFingerprint",
|
|
24
26
|
"BaselineManager",
|
|
25
27
|
"CleanPipeline",
|
|
@@ -55,7 +57,10 @@ __all__ = [
|
|
|
55
57
|
"TypeReport",
|
|
56
58
|
"TypeshedResolver",
|
|
57
59
|
"VerificationTier",
|
|
60
|
+
"collect_project_python_files",
|
|
58
61
|
"get_explanation",
|
|
62
|
+
"is_ignored_directory",
|
|
63
|
+
"is_protected_file",
|
|
59
64
|
"list_rules",
|
|
60
65
|
"load_config",
|
|
61
66
|
]
|
pycleaner/baseline.py
CHANGED
|
@@ -54,7 +54,7 @@ class BaselineManager:
|
|
|
54
54
|
data = json.loads(self.baseline_path.read_text(encoding="utf-8"))
|
|
55
55
|
fingerprints = data.get("fingerprints", [])
|
|
56
56
|
return {
|
|
57
|
-
fp
|
|
57
|
+
str(fp["hash"])
|
|
58
58
|
for fp in fingerprints
|
|
59
59
|
if isinstance(fp, dict) and fp.get("hash")
|
|
60
60
|
}
|
pycleaner/cache.py
CHANGED
|
@@ -15,6 +15,7 @@ from __future__ import annotations
|
|
|
15
15
|
import ast
|
|
16
16
|
import hashlib
|
|
17
17
|
import json
|
|
18
|
+
import logging
|
|
18
19
|
import sqlite3
|
|
19
20
|
import time
|
|
20
21
|
from pathlib import Path
|
|
@@ -22,6 +23,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
22
23
|
|
|
23
24
|
if TYPE_CHECKING:
|
|
24
25
|
from pycleaner.pipeline import CleanResult
|
|
26
|
+
|
|
25
27
|
from pycleaner.verifier import VerificationTier
|
|
26
28
|
|
|
27
29
|
|
|
@@ -46,7 +48,7 @@ def extract_file_dependencies(source: str) -> list[str]:
|
|
|
46
48
|
elif isinstance(node, ast.ImportFrom) and node.module:
|
|
47
49
|
deps.append(node.module.split(".")[0])
|
|
48
50
|
except SyntaxError:
|
|
49
|
-
|
|
51
|
+
logging.getLogger(__name__).debug("Suppressed exception", exc_info=True)
|
|
50
52
|
return sorted(set(deps))
|
|
51
53
|
|
|
52
54
|
|
pycleaner/cli.py
CHANGED
|
@@ -40,7 +40,6 @@ from pycleaner.verifier import CounterExample, ProofReceipt, VerificationTier
|
|
|
40
40
|
|
|
41
41
|
try:
|
|
42
42
|
from rich.console import Console
|
|
43
|
-
from rich.panel import Panel
|
|
44
43
|
from rich.progress import (
|
|
45
44
|
BarColumn,
|
|
46
45
|
Progress,
|
|
@@ -158,6 +157,11 @@ def _register_fix_subparsers(subparsers: Any) -> None:
|
|
|
158
157
|
def _register_analysis_subparsers(subparsers: Any) -> None:
|
|
159
158
|
audit_p = subparsers.add_parser("audit", help="Audit project dependencies")
|
|
160
159
|
_add_common_args(audit_p)
|
|
160
|
+
audit_p.add_argument(
|
|
161
|
+
"--fix",
|
|
162
|
+
action="store_true",
|
|
163
|
+
help="Auto-fix requirements.txt and pyproject.toml and prune unused packages",
|
|
164
|
+
)
|
|
161
165
|
audit_p.add_argument(
|
|
162
166
|
"--fix-deps", action="store_true", help="Auto-fix requirements.txt"
|
|
163
167
|
)
|
|
@@ -714,17 +718,17 @@ def _report_file_modifications(
|
|
|
714
718
|
action = "Cleaned" if apply_changes else "Would modify"
|
|
715
719
|
print_msg(f"[green]{action}:[/green] {py_file.name}")
|
|
716
720
|
for repair in result.syntax_repairs:
|
|
717
|
-
print_msg(f"
|
|
721
|
+
print_msg(f" - Syntax: {repair}", style="cyan")
|
|
718
722
|
for mod in result.modernize_transforms:
|
|
719
|
-
print_msg(f"
|
|
723
|
+
print_msg(f" - Modernize: {mod}", style="green")
|
|
720
724
|
for dc in result.dead_code_pruned:
|
|
721
|
-
print_msg(f"
|
|
725
|
+
print_msg(f" - Dead-code: {dc}", style="yellow")
|
|
722
726
|
for imp in result.resolved_imports:
|
|
723
|
-
print_msg(f"
|
|
727
|
+
print_msg(f" - Import: {imp}", style="magenta")
|
|
724
728
|
if result.lint_changed:
|
|
725
|
-
print_msg("
|
|
729
|
+
print_msg(" - Lint: fixed errors and pruned unused imports", style="blue")
|
|
726
730
|
if result.format_changed:
|
|
727
|
-
print_msg("
|
|
731
|
+
print_msg(" - Format: applied PEP 8 formatting", style="blue")
|
|
728
732
|
|
|
729
733
|
|
|
730
734
|
def _accumulate_result(
|
|
@@ -750,11 +754,11 @@ def _accumulate_result(
|
|
|
750
754
|
elif result.verification_tier == VerificationTier.TIER_C_REFUSED:
|
|
751
755
|
if not is_json:
|
|
752
756
|
print_msg(
|
|
753
|
-
f"[bold red]Refused (Tier C):[/bold red] {py_file.name}
|
|
757
|
+
f"[bold red]Refused (Tier C):[/bold red] {py_file.name} - transformation falsified by differential fuzzing; rolled back!"
|
|
754
758
|
)
|
|
755
759
|
for ce in result.refused_changes:
|
|
756
760
|
print_msg(
|
|
757
|
-
f"
|
|
761
|
+
f" - {ce.callable_name} diverged on args={ce.arguments} kwargs={ce.keyword_arguments}"
|
|
758
762
|
)
|
|
759
763
|
print_msg(
|
|
760
764
|
f" original={ce.original_result or ce.original_error} vs transformed={ce.transformed_result or ce.transformed_error} (seed {ce.seed})"
|
|
@@ -898,15 +902,13 @@ def _render_audit_terminal_output(
|
|
|
898
902
|
f" [red]Missing:[/red] {', '.join(sorted(audit_report.missing_packages))}"
|
|
899
903
|
)
|
|
900
904
|
if not should_fix:
|
|
901
|
-
print_msg(
|
|
902
|
-
" [dim]Tip: Run with -a/--all or --fix-deps to append them[/dim]"
|
|
903
|
-
)
|
|
905
|
+
print_msg(" [dim]Tip: Run with -a/--all or --fix to append them[/dim]")
|
|
904
906
|
if audit_report.unused_packages:
|
|
905
907
|
print_msg(
|
|
906
908
|
f" [yellow]Unused:[/yellow] {', '.join(sorted(audit_report.unused_packages))}"
|
|
907
909
|
)
|
|
908
910
|
if audit_report.fixed_requirements:
|
|
909
|
-
print_msg(" [green]
|
|
911
|
+
print_msg(" [green]Project dependencies synchronized on disk.[/green]")
|
|
910
912
|
|
|
911
913
|
|
|
912
914
|
def _run_fix_audit(
|
|
@@ -919,8 +921,18 @@ def _run_fix_audit(
|
|
|
919
921
|
) -> DependencyAuditReport:
|
|
920
922
|
exclude_patterns = config.exclude if config else ()
|
|
921
923
|
auditor = DependencyAuditor(root_dir, exclude_patterns=exclude_patterns)
|
|
922
|
-
fix_any =
|
|
923
|
-
|
|
924
|
+
fix_any = (
|
|
925
|
+
getattr(args, "fix_deps", False)
|
|
926
|
+
or getattr(args, "fix_all", False)
|
|
927
|
+
or getattr(args, "fix", False)
|
|
928
|
+
or apply_changes
|
|
929
|
+
)
|
|
930
|
+
prune_any = (
|
|
931
|
+
getattr(args, "prune_deps", False)
|
|
932
|
+
or getattr(args, "fix_all", False)
|
|
933
|
+
or getattr(args, "fix", False)
|
|
934
|
+
or apply_changes
|
|
935
|
+
)
|
|
924
936
|
should_fix = apply_changes and fix_any
|
|
925
937
|
should_prune = apply_changes and prune_any
|
|
926
938
|
audit_report = auditor.audit(
|
|
@@ -1010,6 +1022,12 @@ def _cmd_fix(
|
|
|
1010
1022
|
config=config,
|
|
1011
1023
|
target_files=py_files,
|
|
1012
1024
|
)
|
|
1025
|
+
if opts.apply_changes and (
|
|
1026
|
+
audit_report.fixed_requirements
|
|
1027
|
+
or getattr(audit_report, "fixed_pyproject", False)
|
|
1028
|
+
):
|
|
1029
|
+
state.changed_count += 1
|
|
1030
|
+
|
|
1013
1031
|
print_msg(
|
|
1014
1032
|
f"\n[bold]Summary: {len(py_files)} inspected, {state.changed_count} updated, {state.error_count} errors.[/bold]"
|
|
1015
1033
|
)
|
|
@@ -1022,15 +1040,15 @@ def _cmd_fix(
|
|
|
1022
1040
|
if not is_json:
|
|
1023
1041
|
print_msg("\n[bold cyan]Verification Receipts (Trust Ladder):[/bold cyan]")
|
|
1024
1042
|
print_msg(
|
|
1025
|
-
f" [bold green]
|
|
1043
|
+
f" [bold green]- Proven (Tier A):[/bold green] {total_proven_callables} callable(s) invariant-preserving across {pipeline.proof_iterations} input(s)"
|
|
1026
1044
|
)
|
|
1027
1045
|
if state.suggested_count > 0:
|
|
1028
1046
|
print_msg(
|
|
1029
|
-
f" [yellow]
|
|
1047
|
+
f" [yellow]- Suggested (Tier B):[/yellow] {state.suggested_count} module(s) (not isolated for dynamic fuzzing)"
|
|
1030
1048
|
)
|
|
1031
1049
|
if total_refused_callables > 0:
|
|
1032
1050
|
print_msg(
|
|
1033
|
-
f" [bold red]
|
|
1051
|
+
f" [bold red]- Refused (Tier C):[/bold red] {total_refused_callables} transformation(s) diverged; rolled back"
|
|
1034
1052
|
)
|
|
1035
1053
|
|
|
1036
1054
|
output_report_path = getattr(
|
|
@@ -1098,20 +1116,20 @@ def _cmd_fix(
|
|
|
1098
1116
|
)
|
|
1099
1117
|
if tolerated:
|
|
1100
1118
|
print_msg(
|
|
1101
|
-
f"
|
|
1119
|
+
f" - Baseline tolerated: [yellow]{len(tolerated)}[/yellow] existing issue(s)"
|
|
1102
1120
|
)
|
|
1103
1121
|
if new_debt:
|
|
1104
1122
|
print_msg(
|
|
1105
|
-
f"
|
|
1123
|
+
f" - [bold red]Ratchet Violation:[/bold red] {len(new_debt)} new technical debt issue(s) detected!"
|
|
1106
1124
|
)
|
|
1107
1125
|
for nd in new_debt:
|
|
1108
1126
|
print_msg(
|
|
1109
|
-
f" [red]
|
|
1127
|
+
f" [red]- [X] {nd.rule} at {nd.file}:{nd.line} ({nd.symbol})[/red]"
|
|
1110
1128
|
)
|
|
1111
1129
|
return 1
|
|
1112
1130
|
else:
|
|
1113
1131
|
print_msg(
|
|
1114
|
-
"
|
|
1132
|
+
" - [bold green]Ratchet Passed:[/bold green] 0 new technical debt issues introduced."
|
|
1115
1133
|
)
|
|
1116
1134
|
elif new_debt:
|
|
1117
1135
|
return 1
|
|
@@ -1223,9 +1241,9 @@ def _cmd_baseline(
|
|
|
1223
1241
|
manager = BaselineManager(output_path)
|
|
1224
1242
|
saved_file = manager.save_baseline(fingerprints, root_dir)
|
|
1225
1243
|
|
|
1226
|
-
print_msg(
|
|
1227
|
-
print_msg(f"
|
|
1228
|
-
print_msg(f"
|
|
1244
|
+
print_msg("\n[bold green]Baseline successfully recorded![/bold green]")
|
|
1245
|
+
print_msg(f" - Issues snapshotted: [bold yellow]{len(fingerprints)}[/bold yellow]")
|
|
1246
|
+
print_msg(f" - Output file: [bold]{saved_file}[/bold]")
|
|
1229
1247
|
print_msg(
|
|
1230
1248
|
"\n[dim]Ratchet Guarantee: Technical debt in this repository is now locked. Run CI with:[/dim]"
|
|
1231
1249
|
)
|
|
@@ -1413,8 +1431,10 @@ def _render_audit_cli_output(
|
|
|
1413
1431
|
)
|
|
1414
1432
|
if not audit_report.missing_packages and not audit_report.unused_packages:
|
|
1415
1433
|
print_msg(" [green]All dependencies clean.[/green]")
|
|
1416
|
-
if audit_report.fixed_requirements
|
|
1417
|
-
|
|
1434
|
+
if audit_report.fixed_requirements or getattr(
|
|
1435
|
+
audit_report, "fixed_pyproject", False
|
|
1436
|
+
):
|
|
1437
|
+
print_msg(" [green]Project dependencies synchronized on disk.[/green]")
|
|
1418
1438
|
|
|
1419
1439
|
return (
|
|
1420
1440
|
1
|
|
@@ -1428,10 +1448,12 @@ def _cmd_audit(args: argparse.Namespace, config: PyCleanerConfig, print_msg) ->
|
|
|
1428
1448
|
root_dir = find_project_root(args.paths[0] if args.paths else None)
|
|
1429
1449
|
|
|
1430
1450
|
is_check = getattr(args, "check", False)
|
|
1451
|
+
fix_flag = getattr(args, "fix", False) or getattr(args, "fix_deps", False)
|
|
1452
|
+
prune_flag = getattr(args, "fix", False) or getattr(args, "prune_deps", False)
|
|
1431
1453
|
auditor = DependencyAuditor(root_dir, exclude_patterns=config.exclude)
|
|
1432
1454
|
audit_report = auditor.audit(
|
|
1433
|
-
fix=
|
|
1434
|
-
prune_unused=
|
|
1455
|
+
fix=fix_flag and not is_check,
|
|
1456
|
+
prune_unused=prune_flag and not is_check,
|
|
1435
1457
|
)
|
|
1436
1458
|
|
|
1437
1459
|
if getattr(args, "json", False):
|
|
@@ -1501,7 +1523,7 @@ def _render_scan_cli_summary(
|
|
|
1501
1523
|
print_msg(f"\n Findings by category: {cat_summary}")
|
|
1502
1524
|
|
|
1503
1525
|
print_msg(
|
|
1504
|
-
f"\n Total: {report.count} finding(s)
|
|
1526
|
+
f"\n Total: {report.count} finding(s) - "
|
|
1505
1527
|
f"[red]{report.critical_count} critical[/red], [red]{report.high_count} high[/red]"
|
|
1506
1528
|
)
|
|
1507
1529
|
return 1 if report.critical_count > 0 else 0
|
|
@@ -1604,7 +1626,7 @@ def _render_complexity_cli_output(
|
|
|
1604
1626
|
else:
|
|
1605
1627
|
for f in violations:
|
|
1606
1628
|
print_msg(
|
|
1607
|
-
f" {f.qualified_name}
|
|
1629
|
+
f" {f.qualified_name} - CC:{f.cyclomatic} Cog:{f.cognitive} Ln:{f.lines} Args:{f.args}"
|
|
1608
1630
|
)
|
|
1609
1631
|
print_msg(f"\n {len(violations)} function(s) exceed threshold(s).")
|
|
1610
1632
|
return 1
|
|
@@ -1661,7 +1683,7 @@ def _render_dead_code_kind_group(
|
|
|
1661
1683
|
rel_path = _try_relative(item.filepath, target_base)
|
|
1662
1684
|
conf_tag = f" [{item.confidence}]" if item.confidence != "high" else ""
|
|
1663
1685
|
print_msg(
|
|
1664
|
-
f" L{item.lineno} {rel_path}: {item.name}
|
|
1686
|
+
f" L{item.lineno} {rel_path}: {item.name} - {item.reason}{conf_tag}"
|
|
1665
1687
|
)
|
|
1666
1688
|
if len(kind_items) > 20:
|
|
1667
1689
|
print_msg(f" ... and {len(kind_items) - 20} more")
|
|
@@ -1781,7 +1803,7 @@ def _render_types_cli_summary(
|
|
|
1781
1803
|
for f in report.findings:
|
|
1782
1804
|
rel = _try_relative(f.filepath, target_base)
|
|
1783
1805
|
print_msg(
|
|
1784
|
-
f" [{f.severity.upper()}] {rel}:{f.lineno}
|
|
1806
|
+
f" [{f.severity.upper()}] {rel}:{f.lineno} - {f.message} (expected {f.expected_type}, got {f.actual_type})"
|
|
1785
1807
|
)
|
|
1786
1808
|
|
|
1787
1809
|
print_msg(f"\n Total: {report.count} type finding(s) detected.")
|
|
@@ -1965,6 +1987,8 @@ def _run_ultimate_phases(
|
|
|
1965
1987
|
print_msg(
|
|
1966
1988
|
"\n[bold blue]Phase 2: Project Dependency Audit & Reconciliation[/bold blue]"
|
|
1967
1989
|
)
|
|
1990
|
+
args.fix = True
|
|
1991
|
+
args.prune_deps = True
|
|
1968
1992
|
audit_rc = _cmd_audit(args, config, print_msg)
|
|
1969
1993
|
print_msg(
|
|
1970
1994
|
"\n[bold blue]Phase 3: Bidirectional Type Verification & Typeshed Resolution[/bold blue]"
|
|
@@ -1982,7 +2006,7 @@ def _run_ultimate_phases(
|
|
|
1982
2006
|
def _cmd_ultimate(
|
|
1983
2007
|
args: argparse.Namespace, config: PyCleanerConfig, print_msg: Any, console: Any
|
|
1984
2008
|
) -> int:
|
|
1985
|
-
"""The Ultimate Python Tool flagship runner: executes full multi-layer analysis."""
|
|
2009
|
+
"""The Ultimate Python Tool flagship runner: executes full multi-layer analysis and automated healing."""
|
|
1986
2010
|
print_msg(
|
|
1987
2011
|
"[bold magenta]=== PyCleaner Ultimate: Full Spectrum Analysis & Healing ===[/bold magenta]\n"
|
|
1988
2012
|
)
|
|
@@ -1990,14 +2014,15 @@ def _cmd_ultimate(
|
|
|
1990
2014
|
args, config, print_msg, console
|
|
1991
2015
|
)
|
|
1992
2016
|
print_msg(
|
|
1993
|
-
"\n[bold blue]Phase 6: Structural Complexity & Dead Code Discovery[/bold blue]"
|
|
2017
|
+
"\n[bold blue]Phase 6: Structural Complexity & Dead Code Discovery & Healing[/bold blue]"
|
|
1994
2018
|
)
|
|
1995
2019
|
_cmd_complexity(args, config, print_msg, console)
|
|
1996
|
-
|
|
2020
|
+
args.fix = True
|
|
2021
|
+
dead_rc = _cmd_dead_code(args, config, print_msg, console)
|
|
1997
2022
|
print_msg(
|
|
1998
2023
|
"\n[bold magenta]=== Ultimate Python Tool: Analysis Complete ===[/bold magenta]"
|
|
1999
2024
|
)
|
|
2000
|
-
return max(fix_rc, audit_rc, type_rc, taint_rc, scan_rc)
|
|
2025
|
+
return max(fix_rc, audit_rc, type_rc, taint_rc, scan_rc, dead_rc)
|
|
2001
2026
|
|
|
2002
2027
|
|
|
2003
2028
|
def _collect_file_mtimes(files: Sequence[Path]) -> dict[Path, float]:
|
pycleaner/complexity_analyzer.py
CHANGED
|
@@ -318,7 +318,7 @@ class _NestingDepthCounter(ast.NodeVisitor):
|
|
|
318
318
|
def visit_FunctionDef(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> None:
|
|
319
319
|
self._function_depth += 1
|
|
320
320
|
if self._function_depth == 1:
|
|
321
|
-
# Entry function being scored
|
|
321
|
+
# Entry function being scored - walk its body normally.
|
|
322
322
|
self.generic_visit(node)
|
|
323
323
|
# A nested def starts counting from its own zero depth in its own
|
|
324
324
|
# report entry; it must not extend the enclosing function's depth.
|