Skip to content

Handle function usage with unpacking dictionaries#264

Merged
myakove merged 2 commits intomainfrom
keyword_unpacking
Feb 18, 2026
Merged

Handle function usage with unpacking dictionaries#264
myakove merged 2 commits intomainfrom
keyword_unpacking

Conversation

@dbasunag
Copy link
Collaborator

@dbasunag dbasunag commented Feb 17, 2026

Summary by CodeRabbit

  • New Features

    • Enhanced function usage detection to identify keyword unpacking patterns that were previously missed.
    • Improved documentation pattern classification to reduce false positives.
  • Tests

    • Added comprehensive test suite validating keyword unpacking detection across function definitions, comments, and documentation strings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 17, 2026

Walkthrough

Adds keyword unpacking function usage detection to the unused code analyzer. A new helper function generates portable regex patterns for matching syntax like **func() across grep modes, process_file now uses this pattern to identify keyword-unpacked function usage, and a "*" character was removed from documentation detection patterns.

Changes

Cohort / File(s) Summary
Keyword unpacking detection
apps/unused_code/unused_code.py
Added _build_keyword_unpacking_pattern() helper to generate regex patterns for detecting keyword unpacking syntax across PCRE and basic grep modes. Updated process_file to detect keyword unpacking usage with documentation and comment filters. Removed "*" from doc_starters to reduce documentation misclassification.
Keyword unpacking tests
tests/unused_code/test_unused_code_keyword_unpacking.py
New test module with five tests validating keyword unpacking detection across scenarios: general usage patterns, function definitions, comment exclusions, documentation exclusions, and CLI integration with exit status verification.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Handle function usage with unpacking dictionaries' accurately describes the main change: adding keyword unpacking detection to the unused code analyzer.
Docstring Coverage ✅ Passed Docstring coverage is 90.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch keyword_unpacking

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@redhat-qe-bot1
Copy link
Collaborator

Report bugs in Issues

Welcome! 🎉

This pull request will be automatically processed with the following features:

🔄 Automatic Actions

  • Reviewer Assignment: Reviewers are automatically assigned based on the OWNERS file in the repository root
  • Size Labeling: PR size labels (XS, S, M, L, XL, XXL) are automatically applied based on changes
  • Issue Creation: Disabled for this repository
  • Branch Labeling: Branch-specific labels are applied to track the target branch
  • Auto-verification: Auto-verified users have their PRs automatically marked as verified
  • Labels: All label categories are enabled (default configuration)

📋 Available Commands

PR Status Management

  • /wip - Mark PR as work in progress (adds WIP: prefix to title)
  • /wip cancel - Remove work in progress status
  • /hold - Block PR merging (approvers only)
  • /hold cancel - Unblock PR merging
  • /verified - Mark PR as verified
  • /verified cancel - Remove verification status
  • /reprocess - Trigger complete PR workflow reprocessing (useful if webhook failed or configuration changed)
  • /regenerate-welcome - Regenerate this welcome message

Review & Approval

  • /lgtm - Approve changes (looks good to me)
  • /approve - Approve PR (approvers only)
  • /automerge - Enable automatic merging when all requirements are met (maintainers and approvers only)
  • /assign-reviewers - Assign reviewers based on OWNERS file
  • /assign-reviewer @username - Assign specific reviewer
  • /check-can-merge - Check if PR meets merge requirements

Testing & Validation

  • /retest tox - Run Python test suite with tox
  • /retest python-module-install - Test Python package installation
  • /retest all - Run all available tests

Cherry-pick Operations

  • /cherry-pick <branch> - Schedule cherry-pick to target branch when PR is merged
    • Multiple branches: /cherry-pick branch1 branch2 branch3

Label Management

  • /<label-name> - Add a label to the PR
  • /<label-name> cancel - Remove a label from the PR

✅ Merge Requirements

This PR will be automatically approved when the following conditions are met:

  1. Approval: /approve from at least one approver
  2. LGTM Count: Minimum 0 /lgtm from reviewers
  3. Status Checks: All required status checks must pass
  4. No Blockers: No WIP, hold, conflict labels
  5. Verified: PR must be marked as verified (if verification is enabled)

📊 Review Process

Approvers and Reviewers

Approvers:

  • dbasunag
  • myakove
  • rnetser

Reviewers:

  • dbasunag
  • myakove
  • rnetser
Available Labels
  • hold
  • verified
  • wip
  • lgtm
  • approve
  • automerge

💡 Tips

  • WIP Status: Use /wip when your PR is not ready for review
  • Verification: The verified label is automatically removed on each new commit
  • Cherry-picking: Cherry-pick labels are processed when the PR is merged
  • Permission Levels: Some commands require approver permissions
  • Auto-verified Users: Certain users have automatic verification and merge privileges

For more information, please refer to the project documentation or contact the maintainers.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
apps/unused_code/unused_code.py (1)

353-353: Removing "*" from doc_starters could regress markdown-style bullet detection for the general usage check.

The removal is needed to prevent **func() lines from being falsely classified as documentation. However, it also means that markdown-style bullet lines like * func_name(arg) in docstrings are no longer caught as documentation by Pattern 4, potentially causing false positives in the existing general-usage detection path.

If this is an acceptable trade-off, a brief comment explaining the rationale would help future maintainers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/unused_code/unused_code.py` at line 353, Update the doc_starters change
by adding a brief inline comment near the doc_starters declaration explaining
why "*" was removed (to avoid false positives like "**func()") and explicitly
call out the trade-off that markdown-style bullets ("* item") will no longer be
treated as documentation by Pattern 4; if you want to preserve markdown bullets
instead, add a separate markdown-bullet check or a more specific regex for
bolded-function false positives rather than reintroducing "*" into doc_starters.
tests/polarion/test_polarion_verify_tc_requirements_coverage.py (1)

54-71: Consider letting CliRunner handle sys.exit instead of mocking it.

Patching sys.exit prevents CliRunner from capturing the actual exit code, making the assertion mock_exit.call_count >= 1 more fragile than checking result.exit_code == 1. If code is later added after sys.exit(1), it would execute unexpectedly under this mock. The same pattern appears at lines 279–296, 328–348, and 365–375.

Without the sys.exit patch, CliRunner will catch the SystemExit and populate result.exit_code directly:

♻️ Suggested simplification (example for one test)
     `@patch`("apps.polarion.polarion_verify_tc_requirements.get_polarion_project_id")
     `@patch`("apps.polarion.polarion_verify_tc_requirements.find_polarion_ids")
     `@patch`("apps.polarion.polarion_verify_tc_requirements.validate_polarion_requirements")
-    `@patch`("sys.exit")
-    def test_command_fails_with_missing_requirements(self, mock_exit, mock_validate, mock_find, mock_get_project):
+    def test_command_fails_with_missing_requirements(self, mock_validate, mock_find, mock_get_project):
         """Test command exits with code 1 when there are missing requirements"""
         # Arrange
         mock_get_project.return_value = "TEST_PROJECT"
         mock_find.return_value = ["TEST-001", "TEST-002"]
         mock_validate.return_value = ["TEST-001"]  # Has missing requirements
 
         # Act
-        self.runner.invoke(
+        result = self.runner.invoke(
             has_verify,
             ["--project-id", "TEST_PROJECT"],
         )
 
         # Assert
-        # sys.exit(1) should be called due to missing requirements
-        assert mock_exit.call_count >= 1
-        assert 1 in [call.args[0] for call in mock_exit.call_args_list]
+        assert result.exit_code == 1
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/polarion/test_polarion_verify_tc_requirements_coverage.py` around lines
54 - 71, Remove the patching of sys.exit in
test_command_fails_with_missing_requirements and let CliRunner handle
SystemExit: call result = self.runner.invoke(has_verify, ["--project-id",
"TEST_PROJECT"]) and assert result.exit_code == 1 instead of inspecting
mock_exit; also remove mock_exit from the test signature and any assertions on
mock_exit.call_args_list. Apply the same change to the other tests that patch
sys.exit (the ones around the later blocks) so they all use result =
self.runner.invoke(...) and assert result.exit_code for exit behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/unused_code/unused_code.py`:
- Around line 506-528: The keyword-unpacking usage check wrongly treats lines
with inline comments like "x = 1  # **func_name()" as real usage because it only
tests _line.strip().startswith("#"); update the block that iterates _git_grep
(the loop using _build_keyword_unpacking_pattern, _is_documentation_pattern and
variable _line) to reuse the same inline-comment stripping logic as the general
check: split _line on "#" once (e.g., _line.split("#", 1)[0]), assign the code
portion, skip if empty or if _is_documentation_pattern still matches, and then
test for the keyword-unpacking pattern/function_name in the code portion before
marking used = True.

In `@tests/unused_code/test_unused_code_keyword_unpacking.py`:
- Around line 126-160: The test test_keyword_unpacking_with_cli creates a temp
file outside the repo so git grep won't find usages; update the test to mock the
_git_grep call (the same way other tests do) so
process_file/get_unused_functions sees the temp file's symbol usages (or
alternatively write the temp file into the repo working tree); specifically
patch/mock the module function _git_grep used by process_file (and ensure
get_cli_runner()/get_unused_functions still invoke the patched behavior) so
get_config and get_defaults are not reported as unused.

---

Nitpick comments:
In `@apps/unused_code/unused_code.py`:
- Line 353: Update the doc_starters change by adding a brief inline comment near
the doc_starters declaration explaining why "*" was removed (to avoid false
positives like "**func()") and explicitly call out the trade-off that
markdown-style bullets ("* item") will no longer be treated as documentation by
Pattern 4; if you want to preserve markdown bullets instead, add a separate
markdown-bullet check or a more specific regex for bolded-function false
positives rather than reintroducing "*" into doc_starters.

In `@tests/polarion/test_polarion_verify_tc_requirements_coverage.py`:
- Around line 54-71: Remove the patching of sys.exit in
test_command_fails_with_missing_requirements and let CliRunner handle
SystemExit: call result = self.runner.invoke(has_verify, ["--project-id",
"TEST_PROJECT"]) and assert result.exit_code == 1 instead of inspecting
mock_exit; also remove mock_exit from the test signature and any assertions on
mock_exit.call_args_list. Apply the same change to the other tests that patch
sys.exit (the ones around the later blocks) so they all use result =
self.runner.invoke(...) and assert result.exit_code for exit behavior.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/unused_code/test_unused_code_keyword_unpacking.py (1)

25-25: Consider using an explicit file_path parameter instead of **kwargs in mock functions.

The **kwargs silently absorbs the file_path keyword argument. Using an explicit parameter improves readability and silences the Ruff ARG001 warning.

Example for one mock (apply similarly to others)
-    def _mock_grep(pattern: str, **kwargs):
+    def _mock_grep(pattern: str, file_path=None):

Also applies to: 52-52, 78-78, 108-108

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unused_code/test_unused_code_keyword_unpacking.py` at line 25, The mock
function _mock_grep currently uses **kwargs which swallows the file_path kwarg
and triggers Ruff ARG001; change its signature to accept file_path explicitly
(e.g., def _mock_grep(pattern: str, file_path: str):) and update its body to use
that parameter, and apply the same change to the other mock functions in this
file that currently use **kwargs (the mocks at the other comment locations) so
the tests read file_path directly and the ARG001 warning is silenced.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@apps/unused_code/unused_code.py`:
- Around line 522-524: The check that skips commented lines only tests
_line.strip().startswith("#"), which misses inline comments and allows lines
like "x = 1  # func.name()" to be treated as real usage; update the logic (used
alongside the general usage check that already does _line.split("#", 1)[0]) to
strip inline comments before deciding to continue — i.e., derive the code
portion via _line.split("#", 1)[0].strip() and if that code portion is empty
continue, and use that same code portion when checking for func.name to ensure
inline comments are not counted as real usage.

In `@tests/unused_code/test_unused_code_keyword_unpacking.py`:
- Around line 122-155: The CLI test test_keyword_unpacking_with_cli creates a
temp file outside the repo so the real _git_grep (which falls back to
os.getcwd()) scans the repository and makes the assertions flaky; update the
test to mock _git_grep (as in the other four tests) to return expected grep
results for get_config/get_defaults (or alternatively create the temp file
inside the repository working tree) so
get_cli_runner().invoke(get_unused_functions, ...) sees deterministic results
and the assertions about result.output become reliable.

---

Nitpick comments:
In `@tests/unused_code/test_unused_code_keyword_unpacking.py`:
- Line 25: The mock function _mock_grep currently uses **kwargs which swallows
the file_path kwarg and triggers Ruff ARG001; change its signature to accept
file_path explicitly (e.g., def _mock_grep(pattern: str, file_path: str):) and
update its body to use that parameter, and apply the same change to the other
mock functions in this file that currently use **kwargs (the mocks at the other
comment locations) so the tests read file_path directly and the ARG001 warning
is silenced.

@myakove
Copy link
Collaborator

myakove commented Feb 18, 2026

/lgtm
/approve

@dbasunag
Copy link
Collaborator Author

/verified

@myakove myakove merged commit 9b1cb34 into main Feb 18, 2026
6 checks passed
@myakove myakove deleted the keyword_unpacking branch February 18, 2026 14:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

Comments