Skip to content

Round 3 follow-up: hoist to_string()'s loop body out in activity/models.py - #560

Merged
mzeier merged 2 commits into
stage-eksfrom
450/py-r3-activity-followup
Oct 10, 2026
Merged

mzeier merged 2 commits into
stage-eksfrom
450/py-r3-activity-followup

Conversation

@mzeier

@mzeier mzeier commented Oct 10, 2026

Copy link
Copy Markdown

Part of #450 (round 3).

Sonar issue: python:S3776, activity/models.py:556, complexity 30.

My earlier split in thunderbird/addons-server#551 (merged) extracted the per-argument-type formatting into _format_*_arg() helpers, but the 8 isinstance-based if-checks in to_string()'s main loop stayed inline, nested inside a for loop. Sonar's cognitive complexity model multiplies nested-structure penalties, so to_string() was re-flagged at complexity 30 (even though each individual helper is now simple) once re-analyzed on stage-eks after merge.

Extracted the entire loop body into _apply_arg_to_accumulators(arg, arguments, acc), called once per arg from to_string()'s loop. Moving it out of the for loop removes Sonar's nesting multiplier for each of the independent if-checks, since the method itself has no loop around them.

Body moved verbatim, including some subtle existing behavior I deliberately did not "fix" as part of this complexity-only change:

  • the asymmetric not X guards (only the first matching arg of a given type is kept; the Tag branch is the only one that does not remove its arg from arguments, which was already true before this change);
  • the CHANGE_STATUS branch's condition, which can independently match the same arg that already matched an isinstance branch above it (e.g. a File arg during a CHANGE_STATUS action) and call arguments.remove(arg) a second time on it. This existing behavior (including its risk of raising ValueError at runtime in that edge case) is preserved exactly.

addon/rating/version/collection/tag/group/file_/status move from local variables into a single acc dict threaded through the helper and back into to_string()'s kw dict construction; no value or key changes.

Local Greptile review: org free-tier quota exhausted, skipped per coordinator guidance; the Greptile GitHub check on this PR still runs independently.

…ls.py

My earlier split in #551 (merged) extracted the per-argument-type
formatting into _format_*_arg() helpers, but the 8 isinstance-based
if-checks in to_string()'s main loop stayed inline, nested inside a for
loop. Sonar's cognitive complexity model multiplies nested-structure
penalties, so to_string() was re-flagged at complexity 30 (even though
each individual helper is now simple) once re-analyzed on stage-eks
after merge.

Extracted the entire loop body into _apply_arg_to_accumulators(arg,
arguments, acc), called once per arg from to_string()'s loop. Moving it
out of the for loop removes Sonar's nesting multiplier for each of the
independent if-checks, since the method itself has no loop around them.

Body moved verbatim, including:
- the asymmetric "not X" guards (only the first matching arg of a given
  type is kept; the Tag branch is the only one that does not remove its
  arg from `arguments`, which was already true before this change);
- the CHANGE_STATUS branch's condition, which can independently match
  the same arg that already matched an isinstance branch above it (e.g.
  a File arg during a CHANGE_STATUS action) and call
  arguments.remove(arg) a second time on it. This existing behavior
  (including its risk of raising ValueError at runtime) is preserved
  exactly rather than silently fixed, since that's out of scope for a
  complexity-only refactor.

addon/rating/version/collection/tag/group/file_/status move from local
variables into a single `acc` dict threaded through the helper and back
into to_string()'s kw dict construction; no value or key changes.
Sonar's PR scan flagged the newly-extracted _apply_arg_to_accumulators()
itself at complexity 16/15 - hoisting the loop body out of to_string()
wasn't enough on its own, since 8 independent if-checks in one
non-nested method was still over the threshold.

Split it into three sub-helpers, keeping branch order and the
arguments.remove() calls exactly where they were:
- _apply_arg_isinstance_checks_1(): Addon/Rating/Version/Collection (4)
- _apply_arg_isinstance_checks_2(): Tag/Group/File (3)
- _apply_arg_change_status(): the CHANGE_STATUS branch (1)

_apply_arg_to_accumulators() now just calls the three in sequence.
Updated its docstring to describe the two-stage extraction accurately.
@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

Python Test Results

5 763 tests  +5 763   5 709 ✅ +5 709   11m 15s ⏱️ + 11m 15s
    1 suites +    1      54 💤 +   54 
    1 files   +    1       0 ❌ ±    0 

Results for commit 346a71d. ± Comparison against base commit 97c947c.

@mzeier
mzeier merged commit 7273c51 into stage-eks Oct 10, 2026
3 checks passed
@mzeier
mzeier deleted the 450/py-r3-activity-followup branch October 10, 2026 02:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant