Skip to content

Conversation

@Czaki
Copy link
Collaborator

@Czaki Czaki commented Feb 5, 2026

Add testing napari repo against python 3.13

Summary by Sourcery

Update napari testing configuration to separate repo-based tests from pinned napari versions and expand CI coverage to Python 3.13.

Enhancements:

  • Split tox environments so napari widget tests run only against pinned napari versions while full test suite runs against the napari repo for selected Qt backends.
  • Stop running napari repo tests with the PySide2 backend in favor of PyQt5, PyQt6, and PySide6.

CI:

  • Extend the napari repo GitHub Actions workflow matrix to include Python 3.13.

Summary by CodeRabbit

  • Chores
    • Expanded CI to include Python 3.13 and reorganized test environments to separate repo-based runs for clearer, targeted testing and headless-display setup.
  • Bug Fixes
    • Viewer windows are now initialized hidden and only shown after setup completes to prevent premature rendering.
  • Tests
    • Updated tests to instantiate viewers without immediate rendering and adjusted test invocations to align with the new CI/testenv layout.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Feb 5, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Updates tox environments and CI workflow to stop running full napari-repo tests with PySide2 and to add Python 3.13 coverage for napari-repo, while keeping widget-only tests for released napari versions across all backends.

Flow diagram for updated tox environments and backend coverage

flowchart TD
    start["Start tox invocation"] --> select_env["Select tox environment"]

    subgraph released_napari["Widget tests for released napari"]
        select_env --> env_widgets
        env_widgets --> choose_py_version["Python version py39, py310, py311, py312, py313"]
        choose_py_version --> choose_backend["Qt backend PyQt5, PySide2, PyQt6, PySide6"]
        choose_backend --> choose_napari_version["napari version 0.4.19 or 0.5.4"]
        choose_napari_version --> install_released["Install napari from PyPI"]
        install_released --> run_widgets_pytest["Run pytest on test_napari_widgets with JSON report"]
    end

    subgraph napari_repo["Full tests for napari repo"]
        select_env --> env_repo
        env_repo --> choose_py_version_repo["Python version py310, py311, py312, py313"]
        choose_py_version_repo --> choose_backend_repo["Qt backend PyQt5, PyQt6, PySide6"]
        choose_backend_repo --> install_repo["Install napari from GitHub napari/napari"]
        install_repo --> run_full_pytest["Run pytest on package/tests with JSON report"]
    end

    run_widgets_pytest --> end_widgets["End widget test env"]
    run_full_pytest --> end_repo["End repo test env"]
Loading

File-Level Changes

Change Details Files
Refactor tox napari testenvs to separate napari-repo runs and drop PySide2 for napari-repo while keeping widget tests for released napari versions.
  • Remove napari_repo factor from the combined napari testenv matrix and restrict it to napari 0.4.19 and 0.5.4 only.
  • Simplify commands for the released napari testenvs to always run the widget-focused test module with verbose output.
  • Introduce a new dedicated napari_repo testenv limited to Python 3.10–3.13 and the Qt backends PyQt5, PyQt6, and PySide6, depending on napari from the GitHub repo and running the full test suite.
tox.ini
Extend GitHub Actions napari-repo workflow to test against Python 3.13.
  • Update the Python version matrix in the napari-repo workflow to include Python 3.13 alongside existing versions.
.github/workflows/test_napari_repo.yml

Possibly linked issues

  • #test-bot: PR updates napari repo tox and workflow matrix, likely addressing the failing napari repo workflow reported in issue.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 5, 2026

📝 Walkthrough

Walkthrough

Adds Python 3.13 to the napari CI matrix, reorganizes tox to isolate repo-based napari tests into dedicated envs, and defers displaying napari Viewer windows by creating them with show=False and calling viewer.show() after setup.

Changes

Cohort / File(s) Summary
CI matrix
.github/workflows/test_napari_repo.yml
Adds Python 3.13 to the test_napari job matrix (now: 3.10, 3.11, 3.12, 3.13).
Tox environment reorg
tox.ini
Removes napari_repo from the base napari envlist, narrows base napari targets to napari_{419,54}, and adds dedicated napari_repo testenvs (py{310,311,312,313}-{PyQt5,PyQt6,PySide6}-napari_repo) that install napari from git and run broader package/tests. Base napari envs now use posargs to target a single test file.
Viewer show timing
package/PartSeg/common_gui/main_window.py
Create Viewer instances with show=False, then call viewer.show() after layers/setup.
Tests updated for show flag
package/tests/test_PartSeg/test_viewer.py
Tests instantiate Viewer(show=False), remove/adjust qtbot usage and add/adjust fixtures to align with suppressed initial show.
CI workflow refactor (headless display & tox invocation)
.github/workflows/base_test_workflow.yml
Replaces prior apt/OpenGL steps with pyvista/setup-headless-display-action, exposes env vars (NAPARI, BACKEND, PYVISTA_OFF_SCREEN, TOX_ARGS, PYTEST_ARGS), and standardizes tox test invocations via shell scripts using those vars.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I nudged the matrix, added a new sky bright,
Kept windows sleeping till the layers were right.
Git-napari joins the parade of test-run friends,
CI hums softly as the rabbit hops and mends —
🥕 Quiet shows, small steps, delightful ends.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: dropping PySide2 from napari repo tests, fixing napari 0.7.0 compatibility, and adding Python 3.13 support.

✏️ 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 fix_napari_repo

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.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • In the new [testenv:py{310,311,312,313}-{PyQt5,PyQt6,PySide6}-napari_repo] you dropped the -v flag from the pytest invocation compared to the other napari testenvs; if that wasn’t intentional, consider adding -v for consistent output across environments.
  • The pytest command and JSON report options are now duplicated between the napari widget env and the new napari_repo env; consider extracting the common command into a factor or a shared commands snippet to keep the tox config DRY and easier to maintain.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the new `[testenv:py{310,311,312,313}-{PyQt5,PyQt6,PySide6}-napari_repo]` you dropped the `-v` flag from the pytest invocation compared to the other napari testenvs; if that wasn’t intentional, consider adding `-v` for consistent output across environments.
- The pytest command and JSON report options are now duplicated between the napari widget env and the new `napari_repo` env; consider extracting the common command into a factor or a shared `commands` snippet to keep the tox config DRY and easier to maintain.

## Individual Comments

### Comment 1
<location> `tox.ini:84` </location>
<code_context>
+    python -m pytest -v package/tests/test_PartSeg/test_napari_widgets.py --json-report --json-report-file={toxinidir}/report-{envname}-{sys_platform}.json {posargs}
+
+
+[testenv:py{310,311,312,313}-{PyQt5,PyQt6,PySide6}-napari_repo]
+deps =
+    {[testenv]deps}
</code_context>

<issue_to_address>
**question (testing):** The napari_repo env drops PySide2 and py39; confirm this reduction in matrix is intentional.

Previously, `napari_repo` ran on `py{39,310,311,312,313}-{PyQt5,PySide2,PyQt6,PySide6}`; now it’s only `py{310,311,312,313}-{PyQt5,PyQt6,PySide6}`. Unless this is due to an intentional upstream drop of py39/PySide2 support, consider keeping those combos so we don’t silently lose coverage for configurations users may still depend on.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

python -m pytest -v package/tests/test_PartSeg/test_napari_widgets.py --json-report --json-report-file={toxinidir}/report-{envname}-{sys_platform}.json {posargs}


[testenv:py{310,311,312,313}-{PyQt5,PyQt6,PySide6}-napari_repo]
Copy link
Contributor

Choose a reason for hiding this comment

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

question (testing): The napari_repo env drops PySide2 and py39; confirm this reduction in matrix is intentional.

Previously, napari_repo ran on py{39,310,311,312,313}-{PyQt5,PySide2,PyQt6,PySide6}; now it’s only py{310,311,312,313}-{PyQt5,PyQt6,PySide6}. Unless this is due to an intentional upstream drop of py39/PySide2 support, consider keeping those combos so we don’t silently lose coverage for configurations users may still depend on.

@Czaki Czaki changed the title Drop test against pyside2 in napari repo workflow chore:Drop test against pyside2 in napari repo workflow Feb 5, 2026
@Czaki Czaki changed the title chore:Drop test against pyside2 in napari repo workflow chore: Drop test against pyside2 in napari repo workflow Feb 5, 2026
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: 1

🤖 Fix all issues with AI agents
In `@tox.ini`:
- Around line 84-89: The napari git dependency in the testenv named
testenv:py{310,311,312,313}-{PyQt5,PyQt6,PySide6}-napari_repo is unpinned;
replace the deps entry git+https://github.com/napari/napari.git with a pinned
reference to a tag or commit (e.g.
git+https://github.com/napari/napari.git@vX.Y.Z or
git+https://github.com/napari/napari.git@<commit-hash>#egg=napari) to ensure CI
reproducibility, updating the deps line in that testenv block (keep the env name
and other deps identical).

tox.ini Outdated
Comment on lines 84 to 89
[testenv:py{310,311,312,313}-{PyQt5,PyQt6,PySide6}-napari_repo]
deps =
{[testenv]deps}
git+https://github.com/napari/napari.git
commands =
python -m pytest package/tests --json-report --json-report-file={toxinidir}/report-{envname}-{sys_platform}.json {posargs}
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 6, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

PyQt5 Python 3.13 wheels support

💡 Result:

PyQt5 does support Python 3.13 via wheels, but not as “cp313” wheels.

  • The latest PyQt5 release on PyPI (5.15.11, uploaded Jul 19, 2024) publishes cp38-abi3 wheels for Windows (x86/x64), macOS (x86_64/arm64), and Linux x86_64 (manylinux glibc ≥ 2.17). Because they’re abi3 wheels, they are usable on CPython 3.8+ (including 3.13). [1]
  • If your install pulls in Qt libraries via PyQt5-Qt5 / pyqt5-qt5, note that its wheels exist for Linux x86_64 and macOS, and (at least for recent versions like 5.15.18, Nov 9 2025) do not include Windows wheels. [2] (On Windows, PyQt5’s own wheel is the one you’ll typically rely on.)
  • On non-x86_64 Linux (e.g., Raspberry Pi), you may need platform-specific wheels from third parties (example: pyqt5-sip has cp313 wheels on piwheels), or build from source / use conda. [3]

What to do: on Python 3.13, try pip install PyQt5—if you’re on a supported platform above, pip should select the abi3 wheel automatically. [1]

Sources: PyQt5 on PyPI [1], pyqt5-qt5 on PyPI [2], piwheels pyqt5-sip builds [3].


🌐 Web query:

PyQt5 version 5.15 Python 3.13 compatibility

💡 Result:

PyQt5 5.15.x is compatible with Python 3.13, as long as you install a recent PyQt5 5.15 release and its current dependencies:

  • PyQt5 (latest 5.15.11) on PyPI declares “Requires: Python >=3.8” and ships cp38-abi3 wheels, which are intended to work on CPython 3.8+ (including 3.13). (pypi.org)
  • PyQt5-sip provides CPython 3.13 (cp313) wheels in recent releases (e.g., 12.18.0, uploaded Jan 13, 2026), which is important because PyQt5 depends on it. (pypi.org)

Practical guidance:

  • Use pip install PyQt5 (don’t pin an old PyQt5-sip), so pip can pick up a cp313 PyQt5-sip wheel automatically. (pypi.org)

Citations:


Pin the napari git dependency to a tag/commit for CI reproducibility.

Using an unpinned git+ dependency makes builds non-deterministic and can break on upstream changes. Pin to a specific tag or commit hash.

PyQt5 supports Python 3.13 via cp38-abi3 wheels (PyQt5 5.15.11+) and its pyqt5-sip dependency provides cp313 wheels, so py313-PyQt5 does not need exclusion on supported platforms (Windows x86/x64, macOS, Linux x86_64).

🔧 Suggested pinning for reproducibility
 deps =
     {[testenv]deps}
-    git+https://github.com/napari/napari.git
+    git+https://github.com/napari/napari.git@<tag-or-commit>
🤖 Prompt for AI Agents
In `@tox.ini` around lines 84 - 89, The napari git dependency in the testenv named
testenv:py{310,311,312,313}-{PyQt5,PyQt6,PySide6}-napari_repo is unpinned;
replace the deps entry git+https://github.com/napari/napari.git with a pinned
reference to a tag or commit (e.g.
git+https://github.com/napari/napari.git@vX.Y.Z or
git+https://github.com/napari/napari.git@<commit-hash>#egg=napari) to ensure CI
reproducibility, updating the deps line in that testenv block (keep the env name
and other deps identical).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This workflow is intentionally to check if there is some napari incompatibility before the release of the next version

Copy link
Contributor

Choose a reason for hiding this comment

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

@Czaki, thank you for the clarification! You're absolutely right—testing against the latest napari development version is a great practice for catching compatibility issues early. The unpinned dependency is appropriate for this pre-release testing workflow.

I appreciate the context and apologize for the noise on this one.


✏️ Learnings added
Learnt from: Czaki
Repo: 4DNucleome/PartSeg PR: 1352
File: tox.ini:84-89
Timestamp: 2026-02-06T10:35:06.033Z
Learning: In the PartSeg repository, the testenv:py{310,311,312,313}-{PyQt5,PyQt6,PySide6}-napari_repo in tox.ini intentionally uses an unpinned git dependency (git+https://github.com/napari/napari.git) to test against the latest napari development version and catch incompatibilities before napari releases.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: Czaki
Repo: 4DNucleome/PartSeg PR: 1206
File: .github/workflows/test_napari_widgets.yml:30-30
Timestamp: 2024-10-17T12:19:43.905Z
Learning: In the CI configuration, `napari54` corresponds to napari version `0.5.4`.

@Czaki Czaki added the skip check PR title skip spellcheking PR title label Feb 6, 2026
@codecov
Copy link

codecov bot commented Feb 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.15%. Comparing base (2e93a46) to head (5a0f4b4).
⚠️ Report is 2 commits behind head on develop.

Additional details and impacted files
@@           Coverage Diff            @@
##           develop    #1352   +/-   ##
========================================
  Coverage    93.15%   93.15%           
========================================
  Files          210      210           
  Lines        33253    33256    +3     
========================================
+ Hits         30978    30981    +3     
  Misses        2275     2275           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tox.ini (1)

7-7: ⚠️ Potential issue | 🟡 Minor

envlist is out of sync with the new napari_repo testenv.

The envlist still generates napari_repo combinations that no longer have a matching testenv:

  1. py39-{PyQt5,PyQt6}-napari_repo — py39 is not in the new napari_repo env (line 84).
  2. py{39,310}-PySide2-napari_repo — PySide2 is excluded from the new napari_repo env.
  3. PySide6-napari_repo combinations exist in the new testenv but are absent from the envlist.

These orphaned env names would silently fall through to the default [testenv] (no napari installed), producing misleading results if anyone runs the full envlist locally.

Suggested fix
-envlist = py{39,310,311,312,313}-{PyQt5,PySide2,PyQt6,PySide6}-all, py{39,310,311,312,313}-{PyQt5,PyQt6}-napari_{419,54,repo}, py{39,310}-PySide2-napari_{419,54,repo}
+envlist = py{39,310,311,312,313}-{PyQt5,PySide2,PyQt6,PySide6}-all, py{39,310,311,312,313}-{PyQt5,PySide2,PyQt6,PySide6}-napari_{419,54}, py{310,311,312,313}-{PyQt5,PyQt6,PySide6}-napari_repo, py{39,310}-PySide2-napari_{419,54}

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: 1

🤖 Fix all issues with AI agents
In `@tox.ini`:
- Line 7: The envlist includes py39 in napari_repo combos but there is no
matching testenv for Python 3.9; update either the envlist or the testenv to
align them: either remove py39 from the napari_repo patterns in the envlist (the
envlist variable) so napari_repo only targets py310+ or add py39 to the
napari_repo testenv definition (the
[testenv:py{310,311,312,313}-...-napari_repo] group) so py39-PyQt*-napari_repo
environments are covered; ensure the names in envlist and the napari_repo
testenv share the same Python set.

timeout-minutes: ${{ inputs.timeout }}
with:
run: python -m tox ${{ inputs.tox_args }} -- ${{ inputs.pytest_args }}
run: python -m tox ${{ inputs.tox_args }} ${{ inputs.pytest_args != '' && format('-- {0}', inputs.pytest_args) || '' }}
Copy link

@semgrep-app semgrep-app bot Feb 11, 2026

Choose a reason for hiding this comment

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

Using variable interpolation ${{...}} with github context data in a run: step could allow an attacker to inject their own code into the runner. This would allow them to steal secrets and code. github context data can have arbitrary user input and should be treated as untrusted. Instead, use an intermediate environment variable with env: to store the data and use the environment variable in the run: script. Be sure to use double-quotes the environment variable, like this: "$ENVVAR".

🌟 Fixed in commit ebecc1a 🌟

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@coderabbitai Did you agree with the semgrep check? What are possible solutions?

Copy link
Contributor

Choose a reason for hiding this comment

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

@Czaki, I agree with the Semgrep check. This is a legitimate security concern, even though the immediate risk may be low in your case.

The Risk:

The warning is valid because ${{ inputs.pytest_args }} and ${{ inputs.tox_args }} are directly interpolated into the shell command. If these inputs ever contain shell metacharacters (like ;, |, &&, etc.) or are derived from untrusted sources, an attacker could inject arbitrary commands.

Since this is a reusable workflow (workflow_call), the risk depends on:

  • Who controls the calling workflows
  • Whether those workflows pass sanitized or potentially untrusted data

Recommended Solution:

Use environment variables to pass the inputs, then reference them with proper quoting in the shell script:

- name: Test with tox all
  if: ${{ inputs.napari != 'latest' }}
  uses: aganders3/headless-gui@v2
  timeout-minutes: ${{ inputs.timeout }}
  with:
    run: |
      if [ -n "$PYTEST_ARGS" ]; then
        python -m tox $TOX_ARGS -- "$PYTEST_ARGS"
      else
        python -m tox $TOX_ARGS
      fi
  env:
    PYVISTA_OFF_SCREEN: True
    NAPARI: ${{ inputs.napari }}
    BACKEND: ${{ inputs.qt_backend }}
    PIP_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }}
    UV_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }}
    TOX_ARGS: ${{ inputs.tox_args }}
    PYTEST_ARGS: ${{ inputs.pytest_args }}

Note: You should apply this pattern consistently to the other test steps in the file (lines ~95, ~104, ~114) that also use inputs.tox_args and inputs.pytest_args.

Alternative (if you want to keep it simpler):

If you're certain the inputs are always controlled by maintainers and will never contain special characters, you could mark this as acceptable risk in Semgrep with /ar as suggested. However, using environment variables is the safer long-term approach.


🧠 Learnings used
Learnt from: Czaki
Repo: 4DNucleome/PartSeg PR: 1352
File: tox.ini:84-89
Timestamp: 2026-02-06T10:35:13.315Z
Learning: In the PartSeg repository, the testenv:py{310,311,312,313}-{PyQt5,PyQt6,PySide6}-napari_repo in tox.ini intentionally uses an unpinned git dependency (git+https://github.com/napari/napari.git) to test against the latest napari development version and catch incompatibilities before napari releases.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

But pytest args could contain multiple parameters, like file path, test selector, etc. So it cannot be passed as one argument that happens after quoting.

Copy link
Contributor

Choose a reason for hiding this comment

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

@Czaki, you're absolutely right! My apologies for the confusion. Quoting "$PYTEST_ARGS" would indeed treat it as a single argument, breaking the intended behavior.

Corrected Solution:

Use environment variables but leave them unquoted in the shell command to allow proper word splitting:

- name: Test with tox all
  if: ${{ inputs.napari != 'latest' }}
  uses: aganders3/headless-gui@v2
  timeout-minutes: ${{ inputs.timeout }}
  with:
    run: |
      if [ -n "$PYTEST_ARGS" ]; then
        python -m tox $TOX_ARGS -- $PYTEST_ARGS
      else
        python -m tox $TOX_ARGS
      fi
  env:
    PYVISTA_OFF_SCREEN: True
    NAPARI: ${{ inputs.napari }}
    BACKEND: ${{ inputs.qt_backend }}
    PIP_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }}
    UV_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }}
    TOX_ARGS: ${{ inputs.tox_args }}
    PYTEST_ARGS: ${{ inputs.pytest_args }}

Why this works:

  1. Prevents GitHub Actions expression injection: Moving ${{ inputs.* }} to environment variables prevents attackers from injecting malicious expressions through the GitHub Actions interpolation mechanism (which is what Semgrep warns about).

  2. Preserves word splitting: Using $PYTEST_ARGS unquoted allows the shell to properly split it into multiple arguments, so -k test_name /path/to/test.py --verbose becomes three separate arguments.

  3. Safe for your use case: Since this is a reusable workflow where inputs come from workflows you control, the content itself is trusted.

The same pattern should be applied to the other test steps at lines ~95, ~104, and ~114.


🧠 Learnings used
Learnt from: Czaki
Repo: 4DNucleome/PartSeg PR: 1352
File: tox.ini:84-89
Timestamp: 2026-02-06T10:35:13.315Z
Learning: In the PartSeg repository, the testenv:py{310,311,312,313}-{PyQt5,PyQt6,PySide6}-napari_repo in tox.ini intentionally uses an unpinned git dependency (git+https://github.com/napari/napari.git) to test against the latest napari development version and catch incompatibilities before napari releases.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So when the split happens, what prevents the usage of shell metacharacters? Is passing through the env variable is enough?

Copy link
Contributor

Choose a reason for hiding this comment

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

@Czaki, excellent question! You've identified an important distinction. Let me clarify:

Passing through environment variables does NOT prevent shell metacharacter exploitation. You're absolutely right that if PYTEST_ARGS contains something like --verbose; rm -rf /, the shell would still interpret the semicolon and execute malicious commands.

What it DOES prevent:

The environment variable approach specifically prevents GitHub Actions expression injection, which is what Semgrep is flagging. This is when an attacker injects malicious GitHub Actions expressions like ${{ }} that get evaluated by the workflow engine itself before the shell even sees them.

The two different attack vectors:

  1. GitHub Actions expression injection (what Semgrep warns about):

    • Attacker controls a GitHub context variable (e.g., github.event.issue.title)
    • That value contains }}; malicious code; echo ${{
    • Direct interpolation with ${{ }} allows the attacker to break out and inject commands at the workflow level
  2. Shell injection (still possible with env vars):

    • Input contains shell metacharacters like ;, |, &&, etc.
    • These are interpreted by the shell during execution

Why this is acceptable in your case:

The inputs to this reusable workflow come from workflow_call, meaning they're passed by other workflows you control in the same repository. They're not derived from untrusted sources like PR titles, issue comments, or external webhook data.

Since the calling workflows are maintained by you and the inputs are typed strings defined in the workflow schema, the threat model is significantly reduced. The environment variable approach satisfies Semgrep because it prevents the more dangerous GitHub Actions expression injection vector.

If you needed absolute protection against both:

You'd need to either:

  • Validate/sanitize inputs before passing them
  • Use a JSON array and parse it safely
  • Quote arguments individually (which breaks your use case)

But given your threat model, the env variable approach is a reasonable balance between security and functionality.


🧠 Learnings used
Learnt from: Czaki
Repo: 4DNucleome/PartSeg PR: 1352
File: tox.ini:84-89
Timestamp: 2026-02-06T10:35:13.315Z
Learning: In the PartSeg repository, the testenv:py{310,311,312,313}-{PyQt5,PyQt6,PySide6}-napari_repo in tox.ini intentionally uses an unpinned git dependency (git+https://github.com/napari/napari.git) to test against the latest napari development version and catch incompatibilities before napari releases.

@Czaki Czaki added this to the 0.16.5 milestone Feb 11, 2026
@Czaki
Copy link
Collaborator Author

Czaki commented Feb 11, 2026

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 11, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Czaki Czaki changed the title chore: Drop test against pyside2 in napari repo workflow chore: Drop test against pyside2, fix test for napari 0.7.0, add testing against python 3.13 Feb 11, 2026
@Czaki
Copy link
Collaborator Author

Czaki commented Feb 11, 2026

@coderabbitai How to change your configuration to not expect docstrings in test functions?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 11, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@Czaki I'll help you configure CodeRabbit to not expect docstrings in test functions. Let me fetch your current configuration to provide specific guidance.


🛠 Debug Information Gathered


@Czaki You can configure CodeRabbit to not expect docstrings in test functions by adding path-specific instructions. Here are the best approaches:

Option 1: Path-specific instructions (Recommended)

Add the following to your .coderabbit.yaml file:

reviews:
  path_instructions:
    - path: "**/test_*.py"
      instructions: "Docstrings are not required for test functions in this file."
    - path: "**/tests/**/*.py"
      instructions: "Docstrings are not required for test functions in test files."

This tells CodeRabbit to not expect docstrings specifically in test files that match these patterns.

Option 2: Adjust docstring coverage threshold

If you want to relax docstring requirements globally or turn off the pre-merge check:

reviews:
  pre_merge_checks:
    docstrings:
      mode: off  # Options: off, warning, error
      # OR keep it enabled but lower the threshold
      # mode: warning
      # threshold: 50  # Lower percentage if you want some coverage

Option 3: Customize docstring generation for tests

If you want to control how docstrings are generated for test files:

code_generation:
  docstrings:
    path_instructions:
      - path: "**/test_*.py"
        instructions: "Do not generate docstrings for test functions."
      - path: "**/tests/**/*.py"
        instructions: "Skip docstring generation for test functions."

Recommendation

I recommend Option 1 with path-specific instructions, as it gives you fine-grained control while keeping docstring checks active for non-test code. You can adjust the path patterns to match your test file structure.

You can find more details about path-based instructions in the CodeRabbit documentation.


🧠 Learnings used
Learnt from: Czaki
Repo: 4DNucleome/PartSeg PR: 1352
File: tox.ini:84-89
Timestamp: 2026-02-06T10:35:13.315Z
Learning: In the PartSeg repository, the testenv:py{310,311,312,313}-{PyQt5,PyQt6,PySide6}-napari_repo in tox.ini intentionally uses an unpinned git dependency (git+https://github.com/napari/napari.git) to test against the latest napari development version and catch incompatibilities before napari releases.

Czaki added a commit that referenced this pull request Feb 12, 2026
…test (#1355)

#1352 (comment)

## Summary by Sourcery

Build:
- Update CodeRabbit configuration to exempt Python test files from
requiring docstrings.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Updated code review configuration to clarify that docstrings are not
required for test functions in test files.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@sonarqubecloud
Copy link

@Czaki Czaki merged commit 059e195 into develop Feb 12, 2026
52 of 55 checks passed
@Czaki Czaki deleted the fix_napari_repo branch February 12, 2026 07:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip check PR title skip spellcheking PR title

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant