Skip to content

Commit b39c2d9

Browse files
Jkhall81amyreese
authored andcommitted
fix(executable): stricter regex for uv/uvx/uv tool run
1 parent 5b4887e commit b39c2d9

3 files changed

Lines changed: 43 additions & 9 deletions

File tree

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
#!/usr/bin/env -S uv run
22
print("hello world")
33

4-
#!/usr/bin/env uv --quiet run
54
#!/usr/bin/env uv --offline run
5+
print("offline")
6+
7+
#!/usr/bin/env uvx
8+
print("uvx")
9+
10+
#!/usr/bin/env uv tool run
11+
print("uv tool")
12+
13+
#!/usr/bin/env uvx --quiet
14+
print("uvx quiet")
15+
16+
#!/usr/bin/env uv_not_really_run
17+
print("this should fail")

crates/ruff_linter/src/rules/flake8_executable/rules/shebang_missing_python.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,30 @@
1-
use ruff_text_size::TextRange;
1+
use std::sync::LazyLock;
22

3+
use regex::Regex;
34
use ruff_macros::{ViolationMetadata, derive_message_formats};
5+
use ruff_text_size::TextRange;
46

57
use crate::Violation;
68
use crate::checkers::ast::LintContext;
79
use crate::comments::shebang::ShebangDirective;
810

11+
static UV_RUN_REGEX: LazyLock<Regex> = LazyLock::new(|| {
12+
Regex::new(
13+
r"(?x)
14+
\b
15+
(?:
16+
# Part A: uv or uv tool (these MUST be followed by run)
17+
(?:uv|uv\s+tool) \s+ (?:--?[a-zA-Z][\w-]*(?:[=\s]\S+)?\s+)* run
18+
|
19+
# Part B: uvx (stands alone, run is optional/redundant)
20+
uvx (?: \s+ .* )?
21+
)
22+
\b
23+
",
24+
)
25+
.unwrap()
26+
});
27+
928
/// ## What it does
1029
/// Checks for a shebang directive in `.py` files that does not contain `python`,
1130
/// `pytest`, or `uv run`.
@@ -48,12 +67,7 @@ pub(crate) fn shebang_missing_python(
4867
shebang: &ShebangDirective,
4968
context: &LintContext,
5069
) {
51-
if shebang.contains("python")
52-
|| shebang.contains("pytest")
53-
|| (shebang.contains("uv") && shebang.contains("run"))
54-
|| shebang.contains("uvx")
55-
|| shebang.contains("uv tool run")
56-
{
70+
if shebang.contains("python") || shebang.contains("pytest") || UV_RUN_REGEX.is_match(shebang) {
5771
return;
5872
}
5973

Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
---
22
source: crates/ruff_linter/src/rules/flake8_executable/mod.rs
33
---
4-
4+
EXE003 Shebang should contain `python`, `pytest`, or `uv run`
5+
--> EXE003_uv.py:16:1
6+
|
7+
14 | print("uvx quiet")
8+
15 |
9+
16 | #!/usr/bin/env uv_not_really_run
10+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11+
17 | print("this should fail")
12+
|

0 commit comments

Comments
 (0)