codecoco 3.5.1__tar.gz → 3.6.0__tar.gz
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.
- {codecoco-3.5.1 → codecoco-3.6.0}/PKG-INFO +14 -9
- {codecoco-3.5.1 → codecoco-3.6.0}/README.md +13 -8
- {codecoco-3.5.1 → codecoco-3.6.0}/codecoco.egg-info/PKG-INFO +14 -9
- codecoco-3.6.0/cognitive_complexity/__init__.py +1 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/cli.py +68 -18
- {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/report.py +5 -2
- {codecoco-3.5.1 → codecoco-3.6.0}/setup.py +3 -5
- {codecoco-3.5.1 → codecoco-3.6.0}/tests/test_cli.py +45 -2
- codecoco-3.5.1/cognitive_complexity/__init__.py +0 -1
- {codecoco-3.5.1 → codecoco-3.6.0}/LICENSE +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/codecoco.egg-info/SOURCES.txt +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/codecoco.egg-info/dependency_links.txt +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/codecoco.egg-info/entry_points.txt +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/codecoco.egg-info/not-zip-safe +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/codecoco.egg-info/top_level.txt +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/api.py +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/autofix.py +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/common_types.py +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/discovery.py +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/refactor.py +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/utils/__init__.py +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/utils/ast.py +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/pyproject.toml +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/setup.cfg +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/tests/test_autofix.py +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/tests/test_cognitive_complexity.py +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/tests/test_edge_cases.py +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/tests/test_explain.py +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/tests/test_properties.py +0 -0
- {codecoco-3.5.1 → codecoco-3.6.0}/tests/test_refactor.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codecoco
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.6.0
|
|
4
4
|
Summary: Library and CLI to compute the cognitive complexity of Python functions
|
|
5
5
|
Home-page: https://github.com/qwhex/cococo
|
|
6
6
|
Author: Mice Pápai
|
|
@@ -65,9 +65,10 @@ pip install git+https://github.com/qwhex/cococo
|
|
|
65
65
|
### Command line
|
|
66
66
|
|
|
67
67
|
```bash
|
|
68
|
-
cococo src/ # score
|
|
68
|
+
cococo src/ # score worst-first, with refactor suggestions inline
|
|
69
69
|
cococo src/ --max 20 # gate: exit non-zero if any function exceeds 20
|
|
70
|
-
cococo a.py b.py --min 10 # only
|
|
70
|
+
cococo a.py b.py --min 10 # only list functions scoring >= 10
|
|
71
|
+
cococo src/ --suggest-min 10 # only attach suggestions to functions scoring >= 10
|
|
71
72
|
cococo src/ --max 20 --json # machine-readable report for a pipeline
|
|
72
73
|
cococo src/ --fix # apply safe guard-clause rewrites in place
|
|
73
74
|
cococo src/ --nested fold # pre-2.0.0 scoring: fold nested defs into the parent
|
|
@@ -91,19 +92,23 @@ scored by its inner function) as a migration aid; the same is available in the
|
|
|
91
92
|
library as `get_cognitive_complexity(funcdef, fold_nested=True)`. See
|
|
92
93
|
[CHANGELOG.md](CHANGELOG.md).
|
|
93
94
|
|
|
94
|
-
### Refactor suggestions
|
|
95
|
+
### Refactor suggestions
|
|
95
96
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
Beyond reporting a score, cococo points at what to *do* about a complex
|
|
98
|
+
function: the default listing prints concrete, mechanical refactors inline —
|
|
99
|
+
each with the lines it touches and an estimated complexity drop:
|
|
99
100
|
|
|
100
101
|
```text
|
|
101
|
-
|
|
102
|
-
src/load.py:42 load = 14 (>5)
|
|
102
|
+
14 src/load.py:42 load
|
|
103
103
|
- Extract this block into a helper function (lines 50-61, ~-7 -> 7)
|
|
104
104
|
- Flatten nested block with a guard clause (lines 45-61, ~-3 -> 11) [--fix]
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
+
`--suggest-min N` attaches suggestions only to functions scoring at least `N`
|
|
108
|
+
(it defaults to `--min`), to focus the output on the worst offenders. Under a
|
|
109
|
+
`--max` gate the offending functions are reported on stderr with the same
|
|
110
|
+
suggestions instead, so a failing CI step says exactly what to fix.
|
|
111
|
+
|
|
107
112
|
Suggestions tagged `[--fix]` can be applied automatically. `--fix` rewrites only
|
|
108
113
|
transforms it can prove keep behavior identical (an `if` with no `else` that is
|
|
109
114
|
the last statement of a function or loop body becomes an early
|
|
@@ -30,9 +30,10 @@ pip install git+https://github.com/qwhex/cococo
|
|
|
30
30
|
### Command line
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
cococo src/ # score
|
|
33
|
+
cococo src/ # score worst-first, with refactor suggestions inline
|
|
34
34
|
cococo src/ --max 20 # gate: exit non-zero if any function exceeds 20
|
|
35
|
-
cococo a.py b.py --min 10 # only
|
|
35
|
+
cococo a.py b.py --min 10 # only list functions scoring >= 10
|
|
36
|
+
cococo src/ --suggest-min 10 # only attach suggestions to functions scoring >= 10
|
|
36
37
|
cococo src/ --max 20 --json # machine-readable report for a pipeline
|
|
37
38
|
cococo src/ --fix # apply safe guard-clause rewrites in place
|
|
38
39
|
cococo src/ --nested fold # pre-2.0.0 scoring: fold nested defs into the parent
|
|
@@ -56,19 +57,23 @@ scored by its inner function) as a migration aid; the same is available in the
|
|
|
56
57
|
library as `get_cognitive_complexity(funcdef, fold_nested=True)`. See
|
|
57
58
|
[CHANGELOG.md](CHANGELOG.md).
|
|
58
59
|
|
|
59
|
-
### Refactor suggestions
|
|
60
|
+
### Refactor suggestions
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
Beyond reporting a score, cococo points at what to *do* about a complex
|
|
63
|
+
function: the default listing prints concrete, mechanical refactors inline —
|
|
64
|
+
each with the lines it touches and an estimated complexity drop:
|
|
64
65
|
|
|
65
66
|
```text
|
|
66
|
-
|
|
67
|
-
src/load.py:42 load = 14 (>5)
|
|
67
|
+
14 src/load.py:42 load
|
|
68
68
|
- Extract this block into a helper function (lines 50-61, ~-7 -> 7)
|
|
69
69
|
- Flatten nested block with a guard clause (lines 45-61, ~-3 -> 11) [--fix]
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
`--suggest-min N` attaches suggestions only to functions scoring at least `N`
|
|
73
|
+
(it defaults to `--min`), to focus the output on the worst offenders. Under a
|
|
74
|
+
`--max` gate the offending functions are reported on stderr with the same
|
|
75
|
+
suggestions instead, so a failing CI step says exactly what to fix.
|
|
76
|
+
|
|
72
77
|
Suggestions tagged `[--fix]` can be applied automatically. `--fix` rewrites only
|
|
73
78
|
transforms it can prove keep behavior identical (an `if` with no `else` that is
|
|
74
79
|
the last statement of a function or loop body becomes an early
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codecoco
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.6.0
|
|
4
4
|
Summary: Library and CLI to compute the cognitive complexity of Python functions
|
|
5
5
|
Home-page: https://github.com/qwhex/cococo
|
|
6
6
|
Author: Mice Pápai
|
|
@@ -65,9 +65,10 @@ pip install git+https://github.com/qwhex/cococo
|
|
|
65
65
|
### Command line
|
|
66
66
|
|
|
67
67
|
```bash
|
|
68
|
-
cococo src/ # score
|
|
68
|
+
cococo src/ # score worst-first, with refactor suggestions inline
|
|
69
69
|
cococo src/ --max 20 # gate: exit non-zero if any function exceeds 20
|
|
70
|
-
cococo a.py b.py --min 10 # only
|
|
70
|
+
cococo a.py b.py --min 10 # only list functions scoring >= 10
|
|
71
|
+
cococo src/ --suggest-min 10 # only attach suggestions to functions scoring >= 10
|
|
71
72
|
cococo src/ --max 20 --json # machine-readable report for a pipeline
|
|
72
73
|
cococo src/ --fix # apply safe guard-clause rewrites in place
|
|
73
74
|
cococo src/ --nested fold # pre-2.0.0 scoring: fold nested defs into the parent
|
|
@@ -91,19 +92,23 @@ scored by its inner function) as a migration aid; the same is available in the
|
|
|
91
92
|
library as `get_cognitive_complexity(funcdef, fold_nested=True)`. See
|
|
92
93
|
[CHANGELOG.md](CHANGELOG.md).
|
|
93
94
|
|
|
94
|
-
### Refactor suggestions
|
|
95
|
+
### Refactor suggestions
|
|
95
96
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
Beyond reporting a score, cococo points at what to *do* about a complex
|
|
98
|
+
function: the default listing prints concrete, mechanical refactors inline —
|
|
99
|
+
each with the lines it touches and an estimated complexity drop:
|
|
99
100
|
|
|
100
101
|
```text
|
|
101
|
-
|
|
102
|
-
src/load.py:42 load = 14 (>5)
|
|
102
|
+
14 src/load.py:42 load
|
|
103
103
|
- Extract this block into a helper function (lines 50-61, ~-7 -> 7)
|
|
104
104
|
- Flatten nested block with a guard clause (lines 45-61, ~-3 -> 11) [--fix]
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
+
`--suggest-min N` attaches suggestions only to functions scoring at least `N`
|
|
108
|
+
(it defaults to `--min`), to focus the output on the worst offenders. Under a
|
|
109
|
+
`--max` gate the offending functions are reported on stderr with the same
|
|
110
|
+
suggestions instead, so a failing CI step says exactly what to fix.
|
|
111
|
+
|
|
107
112
|
Suggestions tagged `[--fix]` can be applied automatically. `--fix` rewrites only
|
|
108
113
|
transforms it can prove keep behavior identical (an `if` with no `else` that is
|
|
109
114
|
the last statement of a function or loop body becomes an early
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "3.6.0"
|
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
Scores every function and method in the given Python files/directories with
|
|
4
4
|
:func:`cognitive_complexity.api.get_cognitive_complexity` and prints them
|
|
5
|
-
worst-first
|
|
6
|
-
|
|
7
|
-
suggestions.
|
|
5
|
+
worst-first, with concrete refactor suggestions inline by default. With ``--max``
|
|
6
|
+
it doubles as a gate: it exits non-zero when any function exceeds the ceiling,
|
|
7
|
+
reporting each offender (with the same suggestions) on stderr.
|
|
8
8
|
|
|
9
9
|
Usage::
|
|
10
10
|
|
|
11
|
-
cococo src/ # list
|
|
11
|
+
cococo src/ # list worst-first, with suggestions inline
|
|
12
12
|
cococo src/ --max 20 # gate: fail (with suggestions) above 20
|
|
13
13
|
cococo a.py b.py --min 10 # only show functions scoring >= 10
|
|
14
|
+
cococo src/ --suggest-min 10 # only suggest on functions scoring >= 10
|
|
14
15
|
cococo src/ --max 20 --json # machine-readable report for a pipeline
|
|
15
16
|
cococo src/ --fix # apply safe guard-clause rewrites in place
|
|
16
17
|
cococo src/ --nested fold # pre-2.0.0 scoring (fold nested defs into parent)
|
|
@@ -35,7 +36,7 @@ from cognitive_complexity.api import Contribution, get_cognitive_complexity_brea
|
|
|
35
36
|
from cognitive_complexity.autofix import atomic_write, fix_source
|
|
36
37
|
from cognitive_complexity.common_types import AnyFuncdef, ScoredFunction, SkippedFile
|
|
37
38
|
from cognitive_complexity.discovery import find_function, iter_python_files, parse_target, scan
|
|
38
|
-
from cognitive_complexity.refactor import suggest_refactors
|
|
39
|
+
from cognitive_complexity.refactor import Suggestion, suggest_refactors
|
|
39
40
|
from cognitive_complexity.report import build_report, func_key, is_over, to_json
|
|
40
41
|
|
|
41
42
|
|
|
@@ -90,6 +91,14 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
90
91
|
parser.add_argument(
|
|
91
92
|
"--min", type=int, default=0, help="only list functions scoring at least this much"
|
|
92
93
|
)
|
|
94
|
+
parser.add_argument(
|
|
95
|
+
"--suggest-min",
|
|
96
|
+
type=int,
|
|
97
|
+
default=None,
|
|
98
|
+
metavar="N",
|
|
99
|
+
help="show inline refactor suggestions for functions scoring at least N "
|
|
100
|
+
"(default: same as --min). Applies to the default listing, not the --max gate.",
|
|
101
|
+
)
|
|
93
102
|
parser.add_argument(
|
|
94
103
|
"--explain",
|
|
95
104
|
metavar="FILE::QUAL",
|
|
@@ -123,6 +132,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
123
132
|
)
|
|
124
133
|
args = parser.parse_args(argv)
|
|
125
134
|
fold_nested = args.nested == "fold"
|
|
135
|
+
suggest_min = _resolve_suggest_min(args.suggest_min, args.min)
|
|
126
136
|
|
|
127
137
|
if args.explain is not None:
|
|
128
138
|
return explain(args.explain, fold_nested)
|
|
@@ -141,7 +151,15 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
141
151
|
return 2
|
|
142
152
|
_warn_unused_ignores(functions, args.max)
|
|
143
153
|
scan_code = _scan_exit_code(
|
|
144
|
-
functions,
|
|
154
|
+
functions,
|
|
155
|
+
skipped,
|
|
156
|
+
scanned,
|
|
157
|
+
args.max,
|
|
158
|
+
args.as_json,
|
|
159
|
+
args.min,
|
|
160
|
+
suggest_min,
|
|
161
|
+
baseline,
|
|
162
|
+
baseline_root,
|
|
145
163
|
)
|
|
146
164
|
# A failed --fix write, or a file skipped under a gate, is a hard failure (2)
|
|
147
165
|
# regardless of whether the functions that *did* scan stayed within --max.
|
|
@@ -207,6 +225,11 @@ def _warn_unused_ignores(functions: list[ScoredFunction], max_: int | None) -> N
|
|
|
207
225
|
)
|
|
208
226
|
|
|
209
227
|
|
|
228
|
+
def _resolve_suggest_min(suggest_min: int | None, min_: int) -> int:
|
|
229
|
+
"""The suggestion threshold defaults to ``--min`` but can be tuned separately."""
|
|
230
|
+
return suggest_min if suggest_min is not None else min_
|
|
231
|
+
|
|
232
|
+
|
|
210
233
|
def _scan_exit_code(
|
|
211
234
|
functions: list[ScoredFunction],
|
|
212
235
|
skipped: list[SkippedFile],
|
|
@@ -214,14 +237,17 @@ def _scan_exit_code(
|
|
|
214
237
|
max_: int | None,
|
|
215
238
|
as_json: bool,
|
|
216
239
|
min_: int,
|
|
240
|
+
suggest_min: int,
|
|
217
241
|
baseline: dict[str, int] | None,
|
|
218
242
|
baseline_root: Path | None,
|
|
219
243
|
) -> int:
|
|
220
244
|
if as_json:
|
|
221
|
-
return _report_json(
|
|
245
|
+
return _report_json(
|
|
246
|
+
functions, skipped, scanned, max_, min_, suggest_min, baseline, baseline_root
|
|
247
|
+
)
|
|
222
248
|
if not functions:
|
|
223
249
|
return _empty_scan_exit(max_)
|
|
224
|
-
return _report(functions, max_, min_, baseline, baseline_root)
|
|
250
|
+
return _report(functions, max_, min_, suggest_min, baseline, baseline_root)
|
|
225
251
|
|
|
226
252
|
|
|
227
253
|
def _empty_scan_exit(max_: int | None) -> int:
|
|
@@ -290,12 +316,18 @@ def _report(
|
|
|
290
316
|
functions: list[ScoredFunction],
|
|
291
317
|
max_: int | None,
|
|
292
318
|
min_: int,
|
|
319
|
+
suggest_min: int,
|
|
293
320
|
baseline: dict[str, int] | None,
|
|
294
321
|
baseline_root: Path | None,
|
|
295
322
|
) -> int:
|
|
296
|
-
"""Print the scored functions and, when ``max_`` is set, gate on it.
|
|
297
|
-
|
|
298
|
-
|
|
323
|
+
"""Print the scored functions and, when ``max_`` is set, gate on it.
|
|
324
|
+
|
|
325
|
+
In the default listing (no ``--max``) each function scoring at least
|
|
326
|
+
``suggest_min`` carries its refactor suggestions inline, so the actionable
|
|
327
|
+
advice is the default output rather than a gate-only diagnostic. Under a gate
|
|
328
|
+
the listing stays terse and suggestions are reported with the offenders.
|
|
329
|
+
"""
|
|
330
|
+
_print_listing(_shown(functions, max_, min_), max_ is None, suggest_min)
|
|
299
331
|
|
|
300
332
|
if max_ is None:
|
|
301
333
|
return 0
|
|
@@ -313,6 +345,7 @@ def _report_json(
|
|
|
313
345
|
scanned: int,
|
|
314
346
|
max_: int | None,
|
|
315
347
|
min_: int,
|
|
348
|
+
suggest_min: int,
|
|
316
349
|
baseline: dict[str, int] | None,
|
|
317
350
|
baseline_root: Path | None,
|
|
318
351
|
) -> int:
|
|
@@ -320,6 +353,7 @@ def _report_json(
|
|
|
320
353
|
_shown(functions, max_, min_),
|
|
321
354
|
max_,
|
|
322
355
|
min_,
|
|
356
|
+
suggest_min,
|
|
323
357
|
skipped,
|
|
324
358
|
scanned,
|
|
325
359
|
baseline,
|
|
@@ -331,6 +365,28 @@ def _report_json(
|
|
|
331
365
|
return 1 if max_ is not None and report["exceeded"] else 0
|
|
332
366
|
|
|
333
367
|
|
|
368
|
+
def _suggestion_line(s: Suggestion) -> str:
|
|
369
|
+
fix = " [--fix]" if s.autofixable else ""
|
|
370
|
+
return (
|
|
371
|
+
f" - {s.title} "
|
|
372
|
+
f"(lines {s.line_start}-{s.line_end}, ~-{s.estimated_reduction} "
|
|
373
|
+
f"-> {s.estimated_complexity_after}){fix}"
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def _print_listing(shown: list[ScoredFunction], with_suggestions: bool, suggest_min: int) -> None:
|
|
378
|
+
"""Print each function's score line to stdout, with suggestions inline when asked.
|
|
379
|
+
|
|
380
|
+
Listing mode shows only real suggestions; unlike the gate it stays silent when
|
|
381
|
+
none apply (no "no mechanical refactor found" line) so a clean listing is quiet.
|
|
382
|
+
"""
|
|
383
|
+
for f in shown:
|
|
384
|
+
print(f"{f.score:4d} {f.path}:{f.lineno} {f.qualname}")
|
|
385
|
+
if with_suggestions and f.score >= suggest_min:
|
|
386
|
+
for s in suggest_refactors(f.funcdef, f.breakdown):
|
|
387
|
+
print(_suggestion_line(s))
|
|
388
|
+
|
|
389
|
+
|
|
334
390
|
def _print_gate_failure(over: list[ScoredFunction], max_: int) -> None:
|
|
335
391
|
print(
|
|
336
392
|
f"\ncococo: {len(over)} function(s) exceed cognitive complexity {max_}",
|
|
@@ -347,13 +403,7 @@ def _print_suggestions(f: ScoredFunction, max_: int) -> None:
|
|
|
347
403
|
print(" (no mechanical refactor found; split it by responsibility)", file=sys.stderr)
|
|
348
404
|
return
|
|
349
405
|
for s in suggestions:
|
|
350
|
-
|
|
351
|
-
print(
|
|
352
|
-
f" - {s.title} "
|
|
353
|
-
f"(lines {s.line_start}-{s.line_end}, ~-{s.estimated_reduction} "
|
|
354
|
-
f"-> {s.estimated_complexity_after}){fix}",
|
|
355
|
-
file=sys.stderr,
|
|
356
|
-
)
|
|
406
|
+
print(_suggestion_line(s), file=sys.stderr)
|
|
357
407
|
|
|
358
408
|
|
|
359
409
|
if __name__ == "__main__": # pragma: no cover
|
|
@@ -57,6 +57,7 @@ def build_report(
|
|
|
57
57
|
funcs: list[ScoredFunction],
|
|
58
58
|
max_: int | None,
|
|
59
59
|
min_: int,
|
|
60
|
+
suggest_min: int,
|
|
60
61
|
skipped: list[SkippedFile],
|
|
61
62
|
files_scanned: int,
|
|
62
63
|
baseline: dict[str, int] | None = None,
|
|
@@ -68,8 +69,9 @@ def build_report(
|
|
|
68
69
|
can tell a clean scan from a partial one: ``"exceeded": 0`` over a tree where
|
|
69
70
|
files failed to parse is no longer indistinguishable from a genuinely clean
|
|
70
71
|
tree. ``over``/``exceeded`` honor ``# cococo: ignore`` and the baseline.
|
|
72
|
+
Suggestions are attached to functions scoring at least ``suggest_min``.
|
|
71
73
|
"""
|
|
72
|
-
entries = [_func_entry(func, max_, baseline, baseline_root) for func in funcs]
|
|
74
|
+
entries = [_func_entry(func, max_, suggest_min, baseline, baseline_root) for func in funcs]
|
|
73
75
|
return {
|
|
74
76
|
"max": max_,
|
|
75
77
|
"min": min_,
|
|
@@ -83,11 +85,12 @@ def build_report(
|
|
|
83
85
|
def _func_entry(
|
|
84
86
|
func: ScoredFunction,
|
|
85
87
|
max_: int | None,
|
|
88
|
+
suggest_min: int,
|
|
86
89
|
baseline: dict[str, int] | None,
|
|
87
90
|
baseline_root: Path | None,
|
|
88
91
|
) -> dict[str, object]:
|
|
89
92
|
breakdown = func.breakdown
|
|
90
|
-
suggestions = suggest_refactors(func.funcdef, breakdown)
|
|
93
|
+
suggestions = suggest_refactors(func.funcdef, breakdown) if func.score >= suggest_min else []
|
|
91
94
|
return {
|
|
92
95
|
"path": str(func.path),
|
|
93
96
|
"lineno": func.lineno,
|
|
@@ -15,11 +15,9 @@ def get_long_description() -> str:
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
setup(
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
# distribution is published as `codecoco`. The three names differing is
|
|
22
|
-
# intentional and documented in the README.
|
|
18
|
+
# Import package stays `cognitive_complexity` and the CLI is `cococo`; both
|
|
19
|
+
# names were already taken on PyPI, so the distribution ships as `codecoco`
|
|
20
|
+
# (the three differ by design — see README).
|
|
23
21
|
name="codecoco",
|
|
24
22
|
description="Library and CLI to compute the cognitive complexity of Python functions",
|
|
25
23
|
classifiers=[
|
|
@@ -281,12 +281,55 @@ def test_baseline_key_falls_back_to_absolute_when_file_outside_baseline_dir(tmp_
|
|
|
281
281
|
assert key.startswith("/") and key.endswith("m.py::f")
|
|
282
282
|
|
|
283
283
|
|
|
284
|
+
def _listed_quals(out: str) -> list[str]:
|
|
285
|
+
"""Qualnames from the score lines, ignoring inline suggestion lines (` - ...`)."""
|
|
286
|
+
return [
|
|
287
|
+
line.split()[-1] for line in out.splitlines() if line and not line.lstrip().startswith("-")
|
|
288
|
+
]
|
|
289
|
+
|
|
290
|
+
|
|
284
291
|
def test_main_lists_all_functions_worst_first(tmp_path, capsys):
|
|
285
292
|
# Plain listing mode (no --max): every function is printed, worst first.
|
|
286
293
|
_write(tmp_path, "m.py", NESTED + FLAT)
|
|
287
294
|
assert main([str(tmp_path)]) == 0
|
|
288
|
-
|
|
289
|
-
|
|
295
|
+
assert _listed_quals(capsys.readouterr().out) == ["f", "g"] # f (10) ranks above g (0)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def test_default_listing_shows_inline_suggestions(tmp_path, capsys):
|
|
299
|
+
# Suggestions are the default output, not a gate-only diagnostic: a plain run
|
|
300
|
+
# (no --max) prints them inline on stdout for the complex function.
|
|
301
|
+
_write(tmp_path, "m.py", NESTED + FLAT)
|
|
302
|
+
assert main([str(tmp_path)]) == 0
|
|
303
|
+
out = capsys.readouterr().out
|
|
304
|
+
assert "guard clause" in out.lower()
|
|
305
|
+
assert "[--fix]" in out
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def test_default_listing_stays_silent_when_no_suggestion_applies(tmp_path, capsys):
|
|
309
|
+
# Unlike the gate, listing mode does not print the "no mechanical refactor"
|
|
310
|
+
# line — a function with no applicable refactor just shows its score.
|
|
311
|
+
_write(tmp_path, "m.py", NO_SUGGESTION)
|
|
312
|
+
assert main([str(tmp_path)]) == 0
|
|
313
|
+
out = capsys.readouterr().out
|
|
314
|
+
assert "busy" in out
|
|
315
|
+
assert "no mechanical refactor" not in out
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def test_suggest_min_filters_inline_suggestions(tmp_path, capsys):
|
|
319
|
+
# --suggest-min raises the bar for inline suggestions independently of --min:
|
|
320
|
+
# the function is still listed, but above its score it carries no suggestions.
|
|
321
|
+
_write(tmp_path, "m.py", NESTED)
|
|
322
|
+
assert main([str(tmp_path), "--suggest-min", "999"]) == 0
|
|
323
|
+
out = capsys.readouterr().out
|
|
324
|
+
assert "f" in _listed_quals(out)
|
|
325
|
+
assert "guard clause" not in out.lower()
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def test_suggest_min_filters_json_suggestions(tmp_path, capsys):
|
|
329
|
+
_write(tmp_path, "m.py", NESTED)
|
|
330
|
+
assert main([str(tmp_path), "--json", "--suggest-min", "999"]) == 0
|
|
331
|
+
[func] = json.loads(capsys.readouterr().out)["functions"]
|
|
332
|
+
assert func["suggestions"] == []
|
|
290
333
|
|
|
291
334
|
|
|
292
335
|
# --- refactor suggestions, JSON output, and --fix (added with the refactor feature) ---
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "3.5.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|