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.
Files changed (30) hide show
  1. {codecoco-3.5.1 → codecoco-3.6.0}/PKG-INFO +14 -9
  2. {codecoco-3.5.1 → codecoco-3.6.0}/README.md +13 -8
  3. {codecoco-3.5.1 → codecoco-3.6.0}/codecoco.egg-info/PKG-INFO +14 -9
  4. codecoco-3.6.0/cognitive_complexity/__init__.py +1 -0
  5. {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/cli.py +68 -18
  6. {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/report.py +5 -2
  7. {codecoco-3.5.1 → codecoco-3.6.0}/setup.py +3 -5
  8. {codecoco-3.5.1 → codecoco-3.6.0}/tests/test_cli.py +45 -2
  9. codecoco-3.5.1/cognitive_complexity/__init__.py +0 -1
  10. {codecoco-3.5.1 → codecoco-3.6.0}/LICENSE +0 -0
  11. {codecoco-3.5.1 → codecoco-3.6.0}/codecoco.egg-info/SOURCES.txt +0 -0
  12. {codecoco-3.5.1 → codecoco-3.6.0}/codecoco.egg-info/dependency_links.txt +0 -0
  13. {codecoco-3.5.1 → codecoco-3.6.0}/codecoco.egg-info/entry_points.txt +0 -0
  14. {codecoco-3.5.1 → codecoco-3.6.0}/codecoco.egg-info/not-zip-safe +0 -0
  15. {codecoco-3.5.1 → codecoco-3.6.0}/codecoco.egg-info/top_level.txt +0 -0
  16. {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/api.py +0 -0
  17. {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/autofix.py +0 -0
  18. {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/common_types.py +0 -0
  19. {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/discovery.py +0 -0
  20. {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/refactor.py +0 -0
  21. {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/utils/__init__.py +0 -0
  22. {codecoco-3.5.1 → codecoco-3.6.0}/cognitive_complexity/utils/ast.py +0 -0
  23. {codecoco-3.5.1 → codecoco-3.6.0}/pyproject.toml +0 -0
  24. {codecoco-3.5.1 → codecoco-3.6.0}/setup.cfg +0 -0
  25. {codecoco-3.5.1 → codecoco-3.6.0}/tests/test_autofix.py +0 -0
  26. {codecoco-3.5.1 → codecoco-3.6.0}/tests/test_cognitive_complexity.py +0 -0
  27. {codecoco-3.5.1 → codecoco-3.6.0}/tests/test_edge_cases.py +0 -0
  28. {codecoco-3.5.1 → codecoco-3.6.0}/tests/test_explain.py +0 -0
  29. {codecoco-3.5.1 → codecoco-3.6.0}/tests/test_properties.py +0 -0
  30. {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.5.1
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 every function, worst first
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 show functions scoring >= 10
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 on a failing gate
95
+ ### Refactor suggestions
95
96
 
96
- When `--max` is exceeded, each offending function is reported on stderr together
97
- with a few concrete, mechanical refactors and an estimated complexity drop so
98
- a human (or an agent) reading the failure knows what to do next:
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
- cococo: 1 function(s) exceed cognitive complexity 5
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 every function, worst first
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 show functions scoring >= 10
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 on a failing gate
60
+ ### Refactor suggestions
60
61
 
61
- When `--max` is exceeded, each offending function is reported on stderr together
62
- with a few concrete, mechanical refactors and an estimated complexity drop so
63
- a human (or an agent) reading the failure knows what to do next:
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
- cococo: 1 function(s) exceed cognitive complexity 5
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.5.1
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 every function, worst first
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 show functions scoring >= 10
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 on a failing gate
95
+ ### Refactor suggestions
95
96
 
96
- When `--max` is exceeded, each offending function is reported on stderr together
97
- with a few concrete, mechanical refactors and an estimated complexity drop so
98
- a human (or an agent) reading the failure knows what to do next:
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
- cococo: 1 function(s) exceed cognitive complexity 5
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. With ``--max`` it doubles as a gate: it exits non-zero when any
6
- function exceeds the ceiling, reporting each offender with concrete refactor
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 every function, worst first
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, skipped, scanned, args.max, args.as_json, args.min, baseline, baseline_root
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(functions, skipped, scanned, max_, min_, baseline, baseline_root)
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
- for f in _shown(functions, max_, min_):
298
- print(f"{f.score:4d} {f.path}:{f.lineno} {f.qualname}")
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
- fix = " [--fix]" if s.autofixable else ""
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
- # PyPI distribution name. The importable package stays `cognitive_complexity`
19
- # (the parent data_pipeline imports it by that name) and the CLI is `cococo`;
20
- # both `cognitive_complexity` and `cococo` are already taken on PyPI, so the
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
- quals = [line.split()[-1] for line in capsys.readouterr().out.splitlines() if line]
289
- assert quals == ["f", "g"] # f (10) ranks above g (0)
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