mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-24 13:33:50 +00:00
[ruff
] Ambiguous pattern passed to pytest.raises()
(RUF043
) (#14966)
This commit is contained in:
parent
c0b7c36d43
commit
ac81c72bf3
11 changed files with 575 additions and 3 deletions
91
crates/ruff_linter/resources/test/fixtures/ruff/RUF043.py
vendored
Normal file
91
crates/ruff_linter/resources/test/fixtures/ruff/RUF043.py
vendored
Normal file
|
@ -0,0 +1,91 @@
|
|||
import re
|
||||
import pytest
|
||||
|
||||
def test_foo():
|
||||
|
||||
### Errors
|
||||
|
||||
with pytest.raises(FooAtTheEnd, match="foo."): ...
|
||||
with pytest.raises(PackageExtraSpecifier, match="Install `foo[bar]` to enjoy all features"): ...
|
||||
with pytest.raises(InnocentQuestion, match="Did you mean to use `Literal` instead?"): ...
|
||||
|
||||
with pytest.raises(StringConcatenation, match="Huh"
|
||||
"?"): ...
|
||||
with pytest.raises(ManuallyEscapedWindowsPathToDotFile, match="C:\\\\Users\\\\Foo\\\\.config"): ...
|
||||
|
||||
with pytest.raises(MiddleDot, match="foo.bar"): ...
|
||||
with pytest.raises(EndDot, match="foobar."): ...
|
||||
with pytest.raises(EscapedFollowedByUnescaped, match="foo\\.*bar"): ...
|
||||
with pytest.raises(UnescapedFollowedByEscaped, match="foo.\\*bar"): ...
|
||||
|
||||
|
||||
## Metasequences
|
||||
|
||||
with pytest.raises(StartOfInput, match="foo\\Abar"): ...
|
||||
with pytest.raises(WordBoundary, match="foo\\bbar"): ...
|
||||
with pytest.raises(NonWordBoundary, match="foo\\Bbar"): ...
|
||||
with pytest.raises(Digit, match="foo\\dbar"): ...
|
||||
with pytest.raises(NonDigit, match="foo\\Dbar"): ...
|
||||
with pytest.raises(Whitespace, match="foo\\sbar"): ...
|
||||
with pytest.raises(NonWhitespace, match="foo\\Sbar"): ...
|
||||
with pytest.raises(WordCharacter, match="foo\\wbar"): ...
|
||||
with pytest.raises(NonWordCharacter, match="foo\\Wbar"): ...
|
||||
with pytest.raises(EndOfInput, match="foo\\zbar"): ...
|
||||
|
||||
with pytest.raises(StartOfInput2, match="foobar\\A"): ...
|
||||
with pytest.raises(WordBoundary2, match="foobar\\b"): ...
|
||||
with pytest.raises(NonWordBoundary2, match="foobar\\B"): ...
|
||||
with pytest.raises(Digit2, match="foobar\\d"): ...
|
||||
with pytest.raises(NonDigit2, match="foobar\\D"): ...
|
||||
with pytest.raises(Whitespace2, match="foobar\\s"): ...
|
||||
with pytest.raises(NonWhitespace2, match="foobar\\S"): ...
|
||||
with pytest.raises(WordCharacter2, match="foobar\\w"): ...
|
||||
with pytest.raises(NonWordCharacter2, match="foobar\\W"): ...
|
||||
with pytest.raises(EndOfInput2, match="foobar\\z"): ...
|
||||
|
||||
|
||||
### Acceptable false positives
|
||||
|
||||
with pytest.raises(NameEscape, match="\\N{EN DASH}"): ...
|
||||
|
||||
|
||||
### No errors
|
||||
|
||||
with pytest.raises(NoMatch): ...
|
||||
with pytest.raises(NonLiteral, match=pattern): ...
|
||||
with pytest.raises(FunctionCall, match=frobnicate("qux")): ...
|
||||
with pytest.raises(ReEscaped, match=re.escape("foobar")): ...
|
||||
with pytest.raises(RawString, match=r"fo()bar"): ...
|
||||
with pytest.raises(RawStringPart, match=r"foo" '\bar'): ...
|
||||
with pytest.raises(NoMetacharacters, match="foobar"): ...
|
||||
with pytest.raises(EndBackslash, match="foobar\\"): ...
|
||||
|
||||
with pytest.raises(ManuallyEscaped, match="some\\.fully\\.qualified\\.name"): ...
|
||||
with pytest.raises(ManuallyEscapedWindowsPath, match="C:\\\\Users\\\\Foo\\\\file\\.py"): ...
|
||||
|
||||
with pytest.raises(MiddleEscapedDot, match="foo\\.bar"): ...
|
||||
with pytest.raises(MiddleEscapedBackslash, match="foo\\\\bar"): ...
|
||||
with pytest.raises(EndEscapedDot, match="foobar\\."): ...
|
||||
with pytest.raises(EndEscapedBackslash, match="foobar\\\\"): ...
|
||||
|
||||
|
||||
## Not-so-special metasequences
|
||||
|
||||
with pytest.raises(Alert, match="\\f"): ...
|
||||
with pytest.raises(FormFeed, match="\\f"): ...
|
||||
with pytest.raises(Newline, match="\\n"): ...
|
||||
with pytest.raises(CarriageReturn, match="\\r"): ...
|
||||
with pytest.raises(Tab, match="\\t"): ...
|
||||
with pytest.raises(VerticalTab, match="\\v"): ...
|
||||
with pytest.raises(HexEscape, match="\\xFF"): ...
|
||||
with pytest.raises(_16BitUnicodeEscape, match="\\FFFF"): ...
|
||||
with pytest.raises(_32BitUnicodeEscape, match="\\0010FFFF"): ...
|
||||
|
||||
## Escaped metasequences
|
||||
|
||||
with pytest.raises(Whitespace, match="foo\\\\sbar"): ...
|
||||
with pytest.raises(NonWhitespace, match="foo\\\\Sbar"): ...
|
||||
|
||||
## Work by accident
|
||||
|
||||
with pytest.raises(OctalEscape, match="\\042"): ...
|
|
@ -1105,6 +1105,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
|||
if checker.enabled(Rule::BatchedWithoutExplicitStrict) {
|
||||
flake8_bugbear::rules::batched_without_explicit_strict(checker, call);
|
||||
}
|
||||
if checker.enabled(Rule::PytestRaisesAmbiguousPattern) {
|
||||
ruff::rules::pytest_raises_ambiguous_pattern(checker, call);
|
||||
}
|
||||
}
|
||||
Expr::Dict(dict) => {
|
||||
if checker.any_enabled(&[
|
||||
|
|
|
@ -985,6 +985,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
|||
(Ruff, "039") => (RuleGroup::Preview, rules::ruff::rules::UnrawRePattern),
|
||||
(Ruff, "040") => (RuleGroup::Preview, rules::ruff::rules::InvalidAssertMessageLiteralArgument),
|
||||
(Ruff, "041") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryNestedLiteral),
|
||||
(Ruff, "043") => (RuleGroup::Preview, rules::ruff::rules::PytestRaisesAmbiguousPattern),
|
||||
(Ruff, "046") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryCastToInt),
|
||||
(Ruff, "048") => (RuleGroup::Preview, rules::ruff::rules::MapIntVersionParsing),
|
||||
(Ruff, "051") => (RuleGroup::Preview, rules::ruff::rules::IfKeyInDictDel),
|
||||
|
|
|
@ -144,7 +144,7 @@ impl Violation for PytestAssertInExcept {
|
|||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// References
|
||||
/// ## References
|
||||
/// - [`pytest` documentation: `pytest.fail`](https://docs.pytest.org/en/latest/reference/reference.html#pytest-fail)
|
||||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct PytestAssertAlwaysFalse;
|
||||
|
|
|
@ -104,7 +104,6 @@ impl AlwaysFixableViolation for PytestIncorrectMarkParenthesesStyle {
|
|||
///
|
||||
/// ## References
|
||||
/// - [`pytest` documentation: `pytest.mark.usefixtures`](https://docs.pytest.org/en/latest/reference/reference.html#pytest-mark-usefixtures)
|
||||
|
||||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct PytestUseFixturesWithoutParameters;
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ impl Violation for PytestRaisesWithoutException {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_pytest_raises(func: &Expr, semantic: &SemanticModel) -> bool {
|
||||
pub(crate) fn is_pytest_raises(func: &Expr, semantic: &SemanticModel) -> bool {
|
||||
semantic
|
||||
.resolve_qualified_name(func)
|
||||
.is_some_and(|qualified_name| matches!(qualified_name.segments(), ["pytest", "raises"]))
|
||||
|
|
|
@ -415,6 +415,7 @@ mod tests {
|
|||
#[test_case(Rule::UnnecessaryRegularExpression, Path::new("RUF055_0.py"))]
|
||||
#[test_case(Rule::UnnecessaryRegularExpression, Path::new("RUF055_1.py"))]
|
||||
#[test_case(Rule::UnnecessaryCastToInt, Path::new("RUF046.py"))]
|
||||
#[test_case(Rule::PytestRaisesAmbiguousPattern, Path::new("RUF043.py"))]
|
||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
"preview__{}_{}",
|
||||
|
|
|
@ -23,6 +23,7 @@ pub(crate) use never_union::*;
|
|||
pub(crate) use none_not_at_end_of_union::*;
|
||||
pub(crate) use parenthesize_chained_operators::*;
|
||||
pub(crate) use post_init_default::*;
|
||||
pub(crate) use pytest_raises_ambiguous_pattern::*;
|
||||
pub(crate) use quadratic_list_summation::*;
|
||||
pub(crate) use redirected_noqa::*;
|
||||
pub(crate) use redundant_bool_literal::*;
|
||||
|
@ -71,6 +72,7 @@ mod never_union;
|
|||
mod none_not_at_end_of_union;
|
||||
mod parenthesize_chained_operators;
|
||||
mod post_init_default;
|
||||
mod pytest_raises_ambiguous_pattern;
|
||||
mod quadratic_list_summation;
|
||||
mod redirected_noqa;
|
||||
mod redundant_bool_literal;
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
use crate::checkers::ast::Checker;
|
||||
use crate::rules::flake8_pytest_style::rules::is_pytest_raises;
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||
use ruff_python_ast as ast;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for non-raw literal string arguments passed to the `match` parameter
|
||||
/// of `pytest.raises()` where the string contains at least one unescaped
|
||||
/// regex metacharacter.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// The `match` argument is implicitly converted to a regex under the hood.
|
||||
/// It should be made explicit whether the string is meant to be a regex or a "plain" pattern
|
||||
/// by prefixing the string with the `r` suffix, escaping the metacharacter(s)
|
||||
/// in the string using backslashes, or wrapping the entire string in a call to
|
||||
/// `re.escape()`.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```python
|
||||
/// import pytest
|
||||
///
|
||||
///
|
||||
/// with pytest.raises(Exception, match="A full sentence."):
|
||||
/// do_thing_that_raises()
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
///
|
||||
/// ```python
|
||||
/// import pytest
|
||||
///
|
||||
///
|
||||
/// with pytest.raises(Exception, match=r"A full sentence."):
|
||||
/// do_thing_that_raises()
|
||||
/// ```
|
||||
///
|
||||
/// Alternatively:
|
||||
///
|
||||
/// ```python
|
||||
/// import pytest
|
||||
/// import re
|
||||
///
|
||||
///
|
||||
/// with pytest.raises(Exception, match=re.escape("A full sentence.")):
|
||||
/// do_thing_that_raises()
|
||||
/// ```
|
||||
///
|
||||
/// or:
|
||||
///
|
||||
/// ```python
|
||||
/// import pytest
|
||||
/// import re
|
||||
///
|
||||
///
|
||||
/// with pytest.raises(Exception, "A full sentence\\."):
|
||||
/// do_thing_that_raises()
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `re.escape`](https://docs.python.org/3/library/re.html#re.escape)
|
||||
/// - [`pytest` documentation: `pytest.raises`](https://docs.pytest.org/en/latest/reference/reference.html#pytest-raises)
|
||||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct PytestRaisesAmbiguousPattern;
|
||||
|
||||
impl Violation for PytestRaisesAmbiguousPattern {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
"Pattern passed to `match=` contains metacharacters but is neither escaped nor raw"
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
Some("Use a raw string or `re.escape()` to make the intention explicit".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// RUF043
|
||||
pub(crate) fn pytest_raises_ambiguous_pattern(checker: &mut Checker, call: &ast::ExprCall) {
|
||||
if !is_pytest_raises(&call.func, checker.semantic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// It *can* be passed as a positional argument if you try very hard,
|
||||
// but pytest only documents it as a keyword argument, and it's quite hard pass it positionally
|
||||
let Some(ast::Keyword { value, .. }) = call.arguments.find_keyword("match") else {
|
||||
return;
|
||||
};
|
||||
|
||||
let ast::Expr::StringLiteral(string) = value else {
|
||||
return;
|
||||
};
|
||||
|
||||
let any_part_is_raw = string.value.iter().any(|part| part.flags.prefix().is_raw());
|
||||
|
||||
if any_part_is_raw || !string_has_unescaped_metacharacters(&string.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
let diagnostic = Diagnostic::new(PytestRaisesAmbiguousPattern, string.range);
|
||||
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
fn string_has_unescaped_metacharacters(value: &ast::StringLiteralValue) -> bool {
|
||||
let mut escaped = false;
|
||||
|
||||
for character in value.chars() {
|
||||
if escaped {
|
||||
if escaped_char_is_regex_metasequence(character) {
|
||||
return true;
|
||||
}
|
||||
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if character == '\\' {
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if char_is_regex_metacharacter(character) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Whether the sequence `\<c>` means anything special:
|
||||
///
|
||||
/// * `\A`: Start of input
|
||||
/// * `\b`, `\B`: Word boundary and non-word-boundary
|
||||
/// * `\d`, `\D`: Digit and non-digit
|
||||
/// * `\s`, `\S`: Whitespace and non-whitespace
|
||||
/// * `\w`, `\W`: Word and non-word character
|
||||
/// * `\z`: End of input
|
||||
///
|
||||
/// `\u`, `\U`, `\N`, `\x`, `\a`, `\f`, `\n`, `\r`, `\t`, `\v`
|
||||
/// are also valid in normal strings and thus do not count.
|
||||
/// `\b` means backspace only in character sets,
|
||||
/// while backreferences (e.g., `\1`) are not valid without groups,
|
||||
/// both of which should be caught in [`string_has_unescaped_metacharacters`].
|
||||
const fn escaped_char_is_regex_metasequence(c: char) -> bool {
|
||||
matches!(c, 'A' | 'b' | 'B' | 'd' | 'D' | 's' | 'S' | 'w' | 'W' | 'z')
|
||||
}
|
||||
|
||||
const fn char_is_regex_metacharacter(c: char) -> bool {
|
||||
matches!(
|
||||
c,
|
||||
'.' | '^' | '$' | '*' | '+' | '?' | '{' | '[' | '\\' | '|' | '('
|
||||
)
|
||||
}
|
|
@ -0,0 +1,319 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/ruff/mod.rs
|
||||
---
|
||||
RUF043.py:8:43: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
6 | ### Errors
|
||||
7 |
|
||||
8 | with pytest.raises(FooAtTheEnd, match="foo."): ...
|
||||
| ^^^^^^ RUF043
|
||||
9 | with pytest.raises(PackageExtraSpecifier, match="Install `foo[bar]` to enjoy all features"): ...
|
||||
10 | with pytest.raises(InnocentQuestion, match="Did you mean to use `Literal` instead?"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:9:53: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
8 | with pytest.raises(FooAtTheEnd, match="foo."): ...
|
||||
9 | with pytest.raises(PackageExtraSpecifier, match="Install `foo[bar]` to enjoy all features"): ...
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF043
|
||||
10 | with pytest.raises(InnocentQuestion, match="Did you mean to use `Literal` instead?"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:10:48: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
8 | with pytest.raises(FooAtTheEnd, match="foo."): ...
|
||||
9 | with pytest.raises(PackageExtraSpecifier, match="Install `foo[bar]` to enjoy all features"): ...
|
||||
10 | with pytest.raises(InnocentQuestion, match="Did you mean to use `Literal` instead?"): ...
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF043
|
||||
11 |
|
||||
12 | with pytest.raises(StringConcatenation, match="Huh"
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:12:51: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
10 | with pytest.raises(InnocentQuestion, match="Did you mean to use `Literal` instead?"): ...
|
||||
11 |
|
||||
12 | with pytest.raises(StringConcatenation, match="Huh"
|
||||
| ___________________________________________________^
|
||||
13 | | "?"): ...
|
||||
| |_____________________________________________________^ RUF043
|
||||
14 | with pytest.raises(ManuallyEscapedWindowsPathToDotFile, match="C:\\\\Users\\\\Foo\\\\.config"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:14:67: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
12 | with pytest.raises(StringConcatenation, match="Huh"
|
||||
13 | "?"): ...
|
||||
14 | with pytest.raises(ManuallyEscapedWindowsPathToDotFile, match="C:\\\\Users\\\\Foo\\\\.config"): ...
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF043
|
||||
15 |
|
||||
16 | with pytest.raises(MiddleDot, match="foo.bar"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:16:41: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
14 | with pytest.raises(ManuallyEscapedWindowsPathToDotFile, match="C:\\\\Users\\\\Foo\\\\.config"): ...
|
||||
15 |
|
||||
16 | with pytest.raises(MiddleDot, match="foo.bar"): ...
|
||||
| ^^^^^^^^^ RUF043
|
||||
17 | with pytest.raises(EndDot, match="foobar."): ...
|
||||
18 | with pytest.raises(EscapedFollowedByUnescaped, match="foo\\.*bar"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:17:38: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
16 | with pytest.raises(MiddleDot, match="foo.bar"): ...
|
||||
17 | with pytest.raises(EndDot, match="foobar."): ...
|
||||
| ^^^^^^^^^ RUF043
|
||||
18 | with pytest.raises(EscapedFollowedByUnescaped, match="foo\\.*bar"): ...
|
||||
19 | with pytest.raises(UnescapedFollowedByEscaped, match="foo.\\*bar"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:18:58: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
16 | with pytest.raises(MiddleDot, match="foo.bar"): ...
|
||||
17 | with pytest.raises(EndDot, match="foobar."): ...
|
||||
18 | with pytest.raises(EscapedFollowedByUnescaped, match="foo\\.*bar"): ...
|
||||
| ^^^^^^^^^^^^ RUF043
|
||||
19 | with pytest.raises(UnescapedFollowedByEscaped, match="foo.\\*bar"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:19:58: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
17 | with pytest.raises(EndDot, match="foobar."): ...
|
||||
18 | with pytest.raises(EscapedFollowedByUnescaped, match="foo\\.*bar"): ...
|
||||
19 | with pytest.raises(UnescapedFollowedByEscaped, match="foo.\\*bar"): ...
|
||||
| ^^^^^^^^^^^^ RUF043
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:24:44: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
22 | ## Metasequences
|
||||
23 |
|
||||
24 | with pytest.raises(StartOfInput, match="foo\\Abar"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
25 | with pytest.raises(WordBoundary, match="foo\\bbar"): ...
|
||||
26 | with pytest.raises(NonWordBoundary, match="foo\\Bbar"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:25:44: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
24 | with pytest.raises(StartOfInput, match="foo\\Abar"): ...
|
||||
25 | with pytest.raises(WordBoundary, match="foo\\bbar"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
26 | with pytest.raises(NonWordBoundary, match="foo\\Bbar"): ...
|
||||
27 | with pytest.raises(Digit, match="foo\\dbar"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:26:47: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
24 | with pytest.raises(StartOfInput, match="foo\\Abar"): ...
|
||||
25 | with pytest.raises(WordBoundary, match="foo\\bbar"): ...
|
||||
26 | with pytest.raises(NonWordBoundary, match="foo\\Bbar"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
27 | with pytest.raises(Digit, match="foo\\dbar"): ...
|
||||
28 | with pytest.raises(NonDigit, match="foo\\Dbar"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:27:37: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
25 | with pytest.raises(WordBoundary, match="foo\\bbar"): ...
|
||||
26 | with pytest.raises(NonWordBoundary, match="foo\\Bbar"): ...
|
||||
27 | with pytest.raises(Digit, match="foo\\dbar"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
28 | with pytest.raises(NonDigit, match="foo\\Dbar"): ...
|
||||
29 | with pytest.raises(Whitespace, match="foo\\sbar"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:28:40: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
26 | with pytest.raises(NonWordBoundary, match="foo\\Bbar"): ...
|
||||
27 | with pytest.raises(Digit, match="foo\\dbar"): ...
|
||||
28 | with pytest.raises(NonDigit, match="foo\\Dbar"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
29 | with pytest.raises(Whitespace, match="foo\\sbar"): ...
|
||||
30 | with pytest.raises(NonWhitespace, match="foo\\Sbar"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:29:42: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
27 | with pytest.raises(Digit, match="foo\\dbar"): ...
|
||||
28 | with pytest.raises(NonDigit, match="foo\\Dbar"): ...
|
||||
29 | with pytest.raises(Whitespace, match="foo\\sbar"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
30 | with pytest.raises(NonWhitespace, match="foo\\Sbar"): ...
|
||||
31 | with pytest.raises(WordCharacter, match="foo\\wbar"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:30:45: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
28 | with pytest.raises(NonDigit, match="foo\\Dbar"): ...
|
||||
29 | with pytest.raises(Whitespace, match="foo\\sbar"): ...
|
||||
30 | with pytest.raises(NonWhitespace, match="foo\\Sbar"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
31 | with pytest.raises(WordCharacter, match="foo\\wbar"): ...
|
||||
32 | with pytest.raises(NonWordCharacter, match="foo\\Wbar"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:31:45: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
29 | with pytest.raises(Whitespace, match="foo\\sbar"): ...
|
||||
30 | with pytest.raises(NonWhitespace, match="foo\\Sbar"): ...
|
||||
31 | with pytest.raises(WordCharacter, match="foo\\wbar"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
32 | with pytest.raises(NonWordCharacter, match="foo\\Wbar"): ...
|
||||
33 | with pytest.raises(EndOfInput, match="foo\\zbar"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:32:48: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
30 | with pytest.raises(NonWhitespace, match="foo\\Sbar"): ...
|
||||
31 | with pytest.raises(WordCharacter, match="foo\\wbar"): ...
|
||||
32 | with pytest.raises(NonWordCharacter, match="foo\\Wbar"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
33 | with pytest.raises(EndOfInput, match="foo\\zbar"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:33:42: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
31 | with pytest.raises(WordCharacter, match="foo\\wbar"): ...
|
||||
32 | with pytest.raises(NonWordCharacter, match="foo\\Wbar"): ...
|
||||
33 | with pytest.raises(EndOfInput, match="foo\\zbar"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
34 |
|
||||
35 | with pytest.raises(StartOfInput2, match="foobar\\A"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:35:45: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
33 | with pytest.raises(EndOfInput, match="foo\\zbar"): ...
|
||||
34 |
|
||||
35 | with pytest.raises(StartOfInput2, match="foobar\\A"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
36 | with pytest.raises(WordBoundary2, match="foobar\\b"): ...
|
||||
37 | with pytest.raises(NonWordBoundary2, match="foobar\\B"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:36:45: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
35 | with pytest.raises(StartOfInput2, match="foobar\\A"): ...
|
||||
36 | with pytest.raises(WordBoundary2, match="foobar\\b"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
37 | with pytest.raises(NonWordBoundary2, match="foobar\\B"): ...
|
||||
38 | with pytest.raises(Digit2, match="foobar\\d"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:37:48: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
35 | with pytest.raises(StartOfInput2, match="foobar\\A"): ...
|
||||
36 | with pytest.raises(WordBoundary2, match="foobar\\b"): ...
|
||||
37 | with pytest.raises(NonWordBoundary2, match="foobar\\B"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
38 | with pytest.raises(Digit2, match="foobar\\d"): ...
|
||||
39 | with pytest.raises(NonDigit2, match="foobar\\D"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:38:38: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
36 | with pytest.raises(WordBoundary2, match="foobar\\b"): ...
|
||||
37 | with pytest.raises(NonWordBoundary2, match="foobar\\B"): ...
|
||||
38 | with pytest.raises(Digit2, match="foobar\\d"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
39 | with pytest.raises(NonDigit2, match="foobar\\D"): ...
|
||||
40 | with pytest.raises(Whitespace2, match="foobar\\s"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:39:41: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
37 | with pytest.raises(NonWordBoundary2, match="foobar\\B"): ...
|
||||
38 | with pytest.raises(Digit2, match="foobar\\d"): ...
|
||||
39 | with pytest.raises(NonDigit2, match="foobar\\D"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
40 | with pytest.raises(Whitespace2, match="foobar\\s"): ...
|
||||
41 | with pytest.raises(NonWhitespace2, match="foobar\\S"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:40:43: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
38 | with pytest.raises(Digit2, match="foobar\\d"): ...
|
||||
39 | with pytest.raises(NonDigit2, match="foobar\\D"): ...
|
||||
40 | with pytest.raises(Whitespace2, match="foobar\\s"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
41 | with pytest.raises(NonWhitespace2, match="foobar\\S"): ...
|
||||
42 | with pytest.raises(WordCharacter2, match="foobar\\w"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:41:46: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
39 | with pytest.raises(NonDigit2, match="foobar\\D"): ...
|
||||
40 | with pytest.raises(Whitespace2, match="foobar\\s"): ...
|
||||
41 | with pytest.raises(NonWhitespace2, match="foobar\\S"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
42 | with pytest.raises(WordCharacter2, match="foobar\\w"): ...
|
||||
43 | with pytest.raises(NonWordCharacter2, match="foobar\\W"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:42:46: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
40 | with pytest.raises(Whitespace2, match="foobar\\s"): ...
|
||||
41 | with pytest.raises(NonWhitespace2, match="foobar\\S"): ...
|
||||
42 | with pytest.raises(WordCharacter2, match="foobar\\w"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
43 | with pytest.raises(NonWordCharacter2, match="foobar\\W"): ...
|
||||
44 | with pytest.raises(EndOfInput2, match="foobar\\z"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:43:49: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
41 | with pytest.raises(NonWhitespace2, match="foobar\\S"): ...
|
||||
42 | with pytest.raises(WordCharacter2, match="foobar\\w"): ...
|
||||
43 | with pytest.raises(NonWordCharacter2, match="foobar\\W"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
44 | with pytest.raises(EndOfInput2, match="foobar\\z"): ...
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:44:43: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
42 | with pytest.raises(WordCharacter2, match="foobar\\w"): ...
|
||||
43 | with pytest.raises(NonWordCharacter2, match="foobar\\W"): ...
|
||||
44 | with pytest.raises(EndOfInput2, match="foobar\\z"): ...
|
||||
| ^^^^^^^^^^^ RUF043
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
||||
|
||||
RUF043.py:49:42: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
|
||||
|
|
||||
47 | ### Acceptable false positives
|
||||
48 |
|
||||
49 | with pytest.raises(NameEscape, match="\\N{EN DASH}"): ...
|
||||
| ^^^^^^^^^^^^^^ RUF043
|
||||
|
|
||||
= help: Use a raw string or `re.escape()` to make the intention explicit
|
1
ruff.schema.json
generated
1
ruff.schema.json
generated
|
@ -3848,6 +3848,7 @@
|
|||
"RUF04",
|
||||
"RUF040",
|
||||
"RUF041",
|
||||
"RUF043",
|
||||
"RUF046",
|
||||
"RUF048",
|
||||
"RUF05",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue