Skip to content

flake8-executable: allow global flags in uv shebangs (EXE003)#22582

Merged
amyreese merged 4 commits intoastral-sh:mainfrom
Jkhall81:fix/exe003-uv-global-args-21753
Feb 13, 2026
Merged

flake8-executable: allow global flags in uv shebangs (EXE003)#22582
amyreese merged 4 commits intoastral-sh:mainfrom
Jkhall81:fix/exe003-uv-global-args-21753

Conversation

@Jkhall81
Copy link
Contributor

Summary

This PR allows the use of global flags (e.g., --quiet, --offline) in uv shebangs for rule EXE003. Previously, flags placed between uv and run caused a false positive "missing python" error. The logic has been updated to independently check for the presence of uv and run within the shebang directive.

Fixes #21753

Test Plan

  • Added new test cases to crates/ruff_linter/resources/test/fixtures/flake8_executable/EXE003_uv.py covering uv --quiet run and uv --offline run.

@astral-sh-bot
Copy link

astral-sh-bot bot commented Jan 14, 2026

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

if shebang.contains("python")
|| shebang.contains("pytest")
|| shebang.contains("uv run")
|| (shebang.contains("uv") && shebang.contains("run"))
Copy link
Member

Choose a reason for hiding this comment

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

I'd prefer a stricter regex as mentioned in the original issue. We should also make the same change for uvx and uv tool run.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I used switched out contains() for a regex check. It's 'stricter' than the contains() check but I didn't want to make it too strict. I initially got caught up in trying to account for every possible flag a user might use.... that turned into a monster. I chopped it down to this. Added some tests and included uvx and uv tool run.

@Jkhall81 Jkhall81 force-pushed the fix/exe003-uv-global-args-21753 branch from 8cfe319 to 71c144b Compare January 15, 2026 15:41
use crate::comments::shebang::ShebangDirective;

static UV_RUN_REGEX: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"(?x) \b(?:uv|uvx|uv\s+tool)\b .* \s run \b").unwrap());
Copy link
Member

Choose a reason for hiding this comment

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

uvx doesn't require run. So I don't thinkw e need to change anything for uvx.

For uv and uv tool, would a regex like this work uv\s+(?:--?[a-zA-Z][\w-]*(?:[=\s]\S+)?\s+)*run(?:\s+.*)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My bad on the uvx run thing. I took the run out of the test cases with uvx. I tried your suggested regex. When using uv tool, the regex didn't recognize tool, and run is still mandatory.

So, I updated the regex to use your flag logic the uv cases (requiring run) and uvx can stand alone. I thought about just making run optional, but that would cause problems.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, and I left comments in the regex, as you can see. I can take those out, if you think it looks cleaner without them.

@Jkhall81 Jkhall81 force-pushed the fix/exe003-uv-global-args-21753 branch from 71c144b to 8d049ba Compare January 16, 2026 13:24
Copy link
Member

@amyreese amyreese left a comment

Choose a reason for hiding this comment

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

Probably needs a rebase onto latest.

Comment on lines 4 to 11
EXE005 Shebang should be at the beginning of the file
--> EXE003_uv.py:4:1
|
2 | print("hello world")
3 |
4 | #!/usr/bin/env uv --offline run
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5 | print("offline")
Copy link
Member

Choose a reason for hiding this comment

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

Can you update the config for this snapshot test to exclude EXE005 since the test fixture is only testing EXE003?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, sure thing. I can do this a little later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

rebased, and got rid of that EXE005 noise.

@Jkhall81 Jkhall81 force-pushed the fix/exe003-uv-global-args-21753 branch from 8d049ba to 324737c Compare February 11, 2026 16:41
@amyreese amyreese force-pushed the fix/exe003-uv-global-args-21753 branch from 324737c to 1e552c3 Compare February 13, 2026 18:27
@amyreese amyreese requested a review from ntBre February 13, 2026 18:27
@amyreese
Copy link
Member

@ntBre I updated the PR to split the EXE00x test cases to clean up the EXE005 errors from snapshots without needing #noqa, and to restore the better regex.

Copy link
Contributor

@ntBre ntBre left a comment

Choose a reason for hiding this comment

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

Looks good to me, just a couple of testing suggestions.

@amyreese amyreese force-pushed the fix/exe003-uv-global-args-21753 branch from 1e552c3 to b39c2d9 Compare February 13, 2026 22:32
@amyreese amyreese merged commit 5011b25 into astral-sh:main Feb 13, 2026
42 checks passed
carljm added a commit that referenced this pull request Feb 14, 2026
* main: (209 commits)
  [ty] Defer base inference for functional `type(...)` classes (#22792)
  flake8-executable: allow global flags in uv shebangs (EXE003) (#22582)
  [ty] Add `replace-imports-with-any` option (#23122)
  Update html comments in mdtests (#23269)
  Apply ruff formatting to mdtests (#22935)
  [ty] Exclude test-related symbols from non-first-party packages in auto-import completions
  [ty] Refactor `CursorTest` helper to support site-packages
  [ty] Qualify inlay hint edit symbol when possibly referencing another variable (#23265)
  [ty] Avoid `UnionBuilder` overhead when creating a new union from the filtered elements of an existing union (#22352)
  [ty] Refactor TypedDict key assignment validation (#23262)
  [ty] Improve Python environment path documentation (#23256)
  [ty] loop control flow analysis using loop header definitions
  Prepare for 0.15.1 (#23253)
  Remove docker-run-action (#23254)
  [ty] Allow discovering dependencies in system Python environments (#22994)
  Ensure pending suppression diagnostics are reported (#23242)
  [`isort`] support for configurable import section heading comments (#23151)
  [ty] Fix method calls on subclasses of `Any` (#23248)
  [ty] Fix bound method access on `None` (#23246)
  Make range suppression test snapshot actually useful (#23251)
  ...
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.

EXE003 - allows "uv run" but errors on "uv --quiet run"

4 participants