mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-18 03:26:25 +00:00
[pylint] Implement missing-maxsplit-arg (PLC0207) (#17454)
## Summary Implements `use-maxsplit-arg` (`PLC0207`) https://pylint.readthedocs.io/en/latest/user_guide/messages/convention/use-maxsplit-arg.html > Emitted when accessing only the first or last element of str.split(). The first and last element can be accessed by using str.split(sep, maxsplit=1)[0] or str.rsplit(sep, maxsplit=1)[-1] instead. This is part of https://github.com/astral-sh/ruff/issues/970 ## Test Plan `cargo test` Additionally compared Ruff output to Pylint: ``` pylint --disable=all --enable=use-maxsplit-arg crates/ruff_linter/resources/test/fixtures/pylint/missing_maxsplit_arg.py cargo run -p ruff -- check crates/ruff_linter/resources/test/fixtures/pylint/missing_maxsplit_arg.py --no-cache --select PLC0207 ``` --------- Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
This commit is contained in:
parent
c60b4d7f30
commit
27743efa1b
8 changed files with 650 additions and 0 deletions
184
crates/ruff_linter/resources/test/fixtures/pylint/missing_maxsplit_arg.py
vendored
Normal file
184
crates/ruff_linter/resources/test/fixtures/pylint/missing_maxsplit_arg.py
vendored
Normal file
|
|
@ -0,0 +1,184 @@
|
||||||
|
SEQ = "1,2,3"
|
||||||
|
|
||||||
|
class Foo(str):
|
||||||
|
class_str = "1,2,3"
|
||||||
|
|
||||||
|
def split(self, sep=None, maxsplit=-1) -> list[str]:
|
||||||
|
return super().split(sep, maxsplit)
|
||||||
|
|
||||||
|
class Bar():
|
||||||
|
split = "1,2,3"
|
||||||
|
|
||||||
|
# Errors
|
||||||
|
## Test split called directly on string literal
|
||||||
|
"1,2,3".split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
"1,2,3".split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
"1,2,3".rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
"1,2,3".rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
||||||
|
## Test split called on string variable
|
||||||
|
SEQ.split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
SEQ.split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
SEQ.rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
SEQ.rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
||||||
|
## Test split called on class attribute
|
||||||
|
Foo.class_str.split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
Foo.class_str.split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
Foo.class_str.rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
Foo.class_str.rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
||||||
|
## Test split called on sliced string
|
||||||
|
"1,2,3"[::-1].split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
"1,2,3"[::-1][::-1].split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
SEQ[:3].split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
Foo.class_str[1:3].split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
"1,2,3"[::-1].rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
SEQ[:3].rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
Foo.class_str[1:3].rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
||||||
|
## Test sep given as named argument
|
||||||
|
"1,2,3".split(sep=",")[0] # [missing-maxsplit-arg]
|
||||||
|
"1,2,3".split(sep=",")[-1] # [missing-maxsplit-arg]
|
||||||
|
"1,2,3".rsplit(sep=",")[0] # [missing-maxsplit-arg]
|
||||||
|
"1,2,3".rsplit(sep=",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
||||||
|
## Special cases
|
||||||
|
"1,2,3".split("\n")[0] # [missing-maxsplit-arg]
|
||||||
|
"1,2,3".split("split")[-1] # [missing-maxsplit-arg]
|
||||||
|
"1,2,3".rsplit("rsplit")[0] # [missing-maxsplit-arg]
|
||||||
|
|
||||||
|
## Test class attribute named split
|
||||||
|
Bar.split.split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
Bar.split.split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
Bar.split.rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
Bar.split.rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
||||||
|
## Test unpacked dict literal kwargs
|
||||||
|
"1,2,3".split(**{"sep": ","})[0] # [missing-maxsplit-arg]
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
## Test not accessing the first or last element
|
||||||
|
### Test split called directly on string literal
|
||||||
|
"1,2,3".split(",")[1]
|
||||||
|
"1,2,3".split(",")[-2]
|
||||||
|
"1,2,3".rsplit(",")[1]
|
||||||
|
"1,2,3".rsplit(",")[-2]
|
||||||
|
|
||||||
|
### Test split called on string variable
|
||||||
|
SEQ.split(",")[1]
|
||||||
|
SEQ.split(",")[-2]
|
||||||
|
SEQ.rsplit(",")[1]
|
||||||
|
SEQ.rsplit(",")[-2]
|
||||||
|
|
||||||
|
### Test split called on class attribute
|
||||||
|
Foo.class_str.split(",")[1]
|
||||||
|
Foo.class_str.split(",")[-2]
|
||||||
|
Foo.class_str.rsplit(",")[1]
|
||||||
|
Foo.class_str.rsplit(",")[-2]
|
||||||
|
|
||||||
|
### Test split called on sliced string
|
||||||
|
"1,2,3"[::-1].split(",")[1]
|
||||||
|
SEQ[:3].split(",")[1]
|
||||||
|
Foo.class_str[1:3].split(",")[-2]
|
||||||
|
"1,2,3"[::-1].rsplit(",")[1]
|
||||||
|
SEQ[:3].rsplit(",")[1]
|
||||||
|
Foo.class_str[1:3].rsplit(",")[-2]
|
||||||
|
|
||||||
|
### Test sep given as named argument
|
||||||
|
"1,2,3".split(sep=",")[1]
|
||||||
|
"1,2,3".split(sep=",")[-2]
|
||||||
|
"1,2,3".rsplit(sep=",")[1]
|
||||||
|
"1,2,3".rsplit(sep=",")[-2]
|
||||||
|
|
||||||
|
## Test varying maxsplit argument
|
||||||
|
### str.split() tests
|
||||||
|
"1,2,3".split(sep=",", maxsplit=1)[-1]
|
||||||
|
"1,2,3".split(sep=",", maxsplit=1)[0]
|
||||||
|
"1,2,3".split(sep=",", maxsplit=2)[-1]
|
||||||
|
"1,2,3".split(sep=",", maxsplit=2)[0]
|
||||||
|
"1,2,3".split(sep=",", maxsplit=2)[1]
|
||||||
|
|
||||||
|
### str.rsplit() tests
|
||||||
|
"1,2,3".rsplit(sep=",", maxsplit=1)[-1]
|
||||||
|
"1,2,3".rsplit(sep=",", maxsplit=1)[0]
|
||||||
|
"1,2,3".rsplit(sep=",", maxsplit=2)[-1]
|
||||||
|
"1,2,3".rsplit(sep=",", maxsplit=2)[0]
|
||||||
|
"1,2,3".rsplit(sep=",", maxsplit=2)[1]
|
||||||
|
|
||||||
|
## Test user-defined split
|
||||||
|
Foo("1,2,3").split(",")[0]
|
||||||
|
Foo("1,2,3").split(",")[-1]
|
||||||
|
Foo("1,2,3").rsplit(",")[0]
|
||||||
|
Foo("1,2,3").rsplit(",")[-1]
|
||||||
|
|
||||||
|
## Test split called on sliced list
|
||||||
|
["1", "2", "3"][::-1].split(",")[0]
|
||||||
|
|
||||||
|
## Test class attribute named split
|
||||||
|
Bar.split[0]
|
||||||
|
Bar.split[-1]
|
||||||
|
Bar.split[0]
|
||||||
|
Bar.split[-1]
|
||||||
|
|
||||||
|
## Test unpacked dict literal kwargs
|
||||||
|
"1,2,3".split(",", **{"maxsplit": 1})[0]
|
||||||
|
"1,2,3".split(**{"sep": ",", "maxsplit": 1})[0]
|
||||||
|
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
## Test variable split result index
|
||||||
|
## TODO: These require the ability to resolve a variable name to a value
|
||||||
|
# Errors
|
||||||
|
result_index = 0
|
||||||
|
"1,2,3".split(",")[result_index] # TODO: [missing-maxsplit-arg]
|
||||||
|
result_index = -1
|
||||||
|
"1,2,3".split(",")[result_index] # TODO: [missing-maxsplit-arg]
|
||||||
|
# OK
|
||||||
|
result_index = 1
|
||||||
|
"1,2,3".split(",")[result_index]
|
||||||
|
result_index = -2
|
||||||
|
"1,2,3".split(",")[result_index]
|
||||||
|
|
||||||
|
|
||||||
|
## Test split result index modified in loop
|
||||||
|
## TODO: These require the ability to recognize being in a loop where:
|
||||||
|
## - the result of split called on a string is indexed by a variable
|
||||||
|
## - the variable index above is modified
|
||||||
|
# OK
|
||||||
|
result_index = 0
|
||||||
|
for j in range(3):
|
||||||
|
print(SEQ.split(",")[result_index])
|
||||||
|
result_index = result_index + 1
|
||||||
|
|
||||||
|
|
||||||
|
## Test accessor
|
||||||
|
## TODO: These require the ability to get the return type of a method
|
||||||
|
## (possibly via `typing::is_string`)
|
||||||
|
class Baz():
|
||||||
|
def __init__(self):
|
||||||
|
self.my_str = "1,2,3"
|
||||||
|
|
||||||
|
def get_string(self) -> str:
|
||||||
|
return self.my_str
|
||||||
|
|
||||||
|
# Errors
|
||||||
|
Baz().get_string().split(",")[0] # TODO: [missing-maxsplit-arg]
|
||||||
|
Baz().get_string().split(",")[-1] # TODO: [missing-maxsplit-arg]
|
||||||
|
# OK
|
||||||
|
Baz().get_string().split(",")[1]
|
||||||
|
Baz().get_string().split(",")[-2]
|
||||||
|
|
||||||
|
|
||||||
|
## Test unpacked dict instance kwargs
|
||||||
|
## TODO: These require the ability to resolve a dict variable name to a value
|
||||||
|
# Errors
|
||||||
|
kwargs_without_maxsplit = {"seq": ","}
|
||||||
|
"1,2,3".split(**kwargs_without_maxsplit)[0] # TODO: [missing-maxsplit-arg]
|
||||||
|
# OK
|
||||||
|
kwargs_with_maxsplit = {"maxsplit": 1}
|
||||||
|
"1,2,3".split(",", **kwargs_with_maxsplit)[0] # TODO: false positive
|
||||||
|
kwargs_with_maxsplit = {"sep": ",", "maxsplit": 1}
|
||||||
|
"1,2,3".split(**kwargs_with_maxsplit)[0] # TODO: false positive
|
||||||
|
|
@ -176,6 +176,9 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||||
if checker.enabled(Rule::Airflow3Removal) {
|
if checker.enabled(Rule::Airflow3Removal) {
|
||||||
airflow::rules::airflow_3_removal_expr(checker, expr);
|
airflow::rules::airflow_3_removal_expr(checker, expr);
|
||||||
}
|
}
|
||||||
|
if checker.enabled(Rule::MissingMaxsplitArg) {
|
||||||
|
pylint::rules::missing_maxsplit_arg(checker, value, slice, expr);
|
||||||
|
}
|
||||||
pandas_vet::rules::subscript(checker, value, expr);
|
pandas_vet::rules::subscript(checker, value, expr);
|
||||||
}
|
}
|
||||||
Expr::Tuple(ast::ExprTuple {
|
Expr::Tuple(ast::ExprTuple {
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||||
(Pylint, "C0132") => (RuleGroup::Stable, rules::pylint::rules::TypeParamNameMismatch),
|
(Pylint, "C0132") => (RuleGroup::Stable, rules::pylint::rules::TypeParamNameMismatch),
|
||||||
(Pylint, "C0205") => (RuleGroup::Stable, rules::pylint::rules::SingleStringSlots),
|
(Pylint, "C0205") => (RuleGroup::Stable, rules::pylint::rules::SingleStringSlots),
|
||||||
(Pylint, "C0206") => (RuleGroup::Stable, rules::pylint::rules::DictIndexMissingItems),
|
(Pylint, "C0206") => (RuleGroup::Stable, rules::pylint::rules::DictIndexMissingItems),
|
||||||
|
(Pylint, "C0207") => (RuleGroup::Preview, rules::pylint::rules::MissingMaxsplitArg),
|
||||||
(Pylint, "C0208") => (RuleGroup::Stable, rules::pylint::rules::IterationOverSet),
|
(Pylint, "C0208") => (RuleGroup::Stable, rules::pylint::rules::IterationOverSet),
|
||||||
(Pylint, "C0414") => (RuleGroup::Stable, rules::pylint::rules::UselessImportAlias),
|
(Pylint, "C0414") => (RuleGroup::Stable, rules::pylint::rules::UselessImportAlias),
|
||||||
(Pylint, "C0415") => (RuleGroup::Preview, rules::pylint::rules::ImportOutsideTopLevel),
|
(Pylint, "C0415") => (RuleGroup::Preview, rules::pylint::rules::ImportOutsideTopLevel),
|
||||||
|
|
|
||||||
|
|
@ -231,6 +231,7 @@ mod tests {
|
||||||
Path::new("bad_staticmethod_argument.py")
|
Path::new("bad_staticmethod_argument.py")
|
||||||
)]
|
)]
|
||||||
#[test_case(Rule::LenTest, Path::new("len_as_condition.py"))]
|
#[test_case(Rule::LenTest, Path::new("len_as_condition.py"))]
|
||||||
|
#[test_case(Rule::MissingMaxsplitArg, Path::new("missing_maxsplit_arg.py"))]
|
||||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
||||||
let diagnostics = test_path(
|
let diagnostics = test_path(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
|
use ruff_python_ast::{
|
||||||
|
DictItem, Expr, ExprAttribute, ExprCall, ExprDict, ExprNumberLiteral, ExprStringLiteral,
|
||||||
|
ExprSubscript, ExprUnaryOp, Keyword, Number, UnaryOp,
|
||||||
|
};
|
||||||
|
use ruff_python_semantic::{SemanticModel, analyze::typing};
|
||||||
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
use crate::Violation;
|
||||||
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
/// ## What it does
|
||||||
|
/// Checks for access to the first or last element of `str.split()` without
|
||||||
|
/// `maxsplit=1`
|
||||||
|
///
|
||||||
|
/// ## Why is this bad?
|
||||||
|
/// Calling `str.split()` without `maxsplit` set splits on every delimiter in the
|
||||||
|
/// string. When accessing only the first or last element of the result, it
|
||||||
|
/// would be more efficient to only split once.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```python
|
||||||
|
/// url = "www.example.com"
|
||||||
|
/// prefix = url.split(".")[0]
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Use instead:
|
||||||
|
/// ```python
|
||||||
|
/// url = "www.example.com"
|
||||||
|
/// prefix = url.split(".", maxsplit=1)[0]
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
#[derive(ViolationMetadata)]
|
||||||
|
pub(crate) struct MissingMaxsplitArg;
|
||||||
|
|
||||||
|
impl Violation for MissingMaxsplitArg {
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
"Accessing only the first or last element of `str.split()` without setting `maxsplit=1`"
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_string(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||||
|
if let Expr::Name(name) = expr {
|
||||||
|
semantic
|
||||||
|
.only_binding(name)
|
||||||
|
.is_some_and(|binding_id| typing::is_string(semantic.binding(binding_id), semantic))
|
||||||
|
} else if let Some(binding_id) = semantic.lookup_attribute(expr) {
|
||||||
|
typing::is_string(semantic.binding(binding_id), semantic)
|
||||||
|
} else {
|
||||||
|
expr.is_string_literal_expr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PLC0207
|
||||||
|
pub(crate) fn missing_maxsplit_arg(checker: &Checker, value: &Expr, slice: &Expr, expr: &Expr) {
|
||||||
|
// Check the sliced expression is a function
|
||||||
|
let Expr::Call(ExprCall {
|
||||||
|
func, arguments, ..
|
||||||
|
}) = value
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check the slice index is either 0 or -1 (first or last value)
|
||||||
|
let index = match slice {
|
||||||
|
Expr::NumberLiteral(ExprNumberLiteral {
|
||||||
|
value: Number::Int(number_value),
|
||||||
|
..
|
||||||
|
}) => number_value.as_i64(),
|
||||||
|
Expr::UnaryOp(ExprUnaryOp {
|
||||||
|
op: UnaryOp::USub,
|
||||||
|
operand,
|
||||||
|
..
|
||||||
|
}) => match operand.as_ref() {
|
||||||
|
Expr::NumberLiteral(ExprNumberLiteral {
|
||||||
|
value: Number::Int(number_value),
|
||||||
|
..
|
||||||
|
}) => number_value.as_i64().map(|number| -number),
|
||||||
|
_ => return,
|
||||||
|
},
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
if !matches!(index, Some(0 | -1)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Expr::Attribute(ExprAttribute { attr, value, .. }) = func.as_ref() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check the function is "split" or "rsplit"
|
||||||
|
let attr = attr.as_str();
|
||||||
|
if !matches!(attr, "split" | "rsplit") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut target_instance = value;
|
||||||
|
// a subscripted value could technically be subscripted further ad infinitum, so we
|
||||||
|
// recurse into the subscript expressions until we find the value being subscripted
|
||||||
|
while let Expr::Subscript(ExprSubscript { value, .. }) = target_instance.as_ref() {
|
||||||
|
target_instance = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the function is called on a string
|
||||||
|
if !is_string(target_instance, checker.semantic()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the function does not have maxsplit set
|
||||||
|
if arguments.find_argument_value("maxsplit", 1).is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check maxsplit kwarg not set via unpacked dict literal
|
||||||
|
for keyword in &*arguments.keywords {
|
||||||
|
let Keyword { value, .. } = keyword;
|
||||||
|
|
||||||
|
if let Expr::Dict(ExprDict { items, .. }) = value {
|
||||||
|
for item in items {
|
||||||
|
let DictItem { key, .. } = item;
|
||||||
|
if let Some(Expr::StringLiteral(ExprStringLiteral { value, .. })) = key {
|
||||||
|
if value.to_str() == "maxsplit" {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checker.report_diagnostic(MissingMaxsplitArg, expr.range());
|
||||||
|
}
|
||||||
|
|
@ -46,6 +46,7 @@ pub(crate) use logging::*;
|
||||||
pub(crate) use magic_value_comparison::*;
|
pub(crate) use magic_value_comparison::*;
|
||||||
pub(crate) use manual_import_from::*;
|
pub(crate) use manual_import_from::*;
|
||||||
pub(crate) use misplaced_bare_raise::*;
|
pub(crate) use misplaced_bare_raise::*;
|
||||||
|
pub(crate) use missing_maxsplit_arg::*;
|
||||||
pub(crate) use modified_iterating_set::*;
|
pub(crate) use modified_iterating_set::*;
|
||||||
pub(crate) use named_expr_without_context::*;
|
pub(crate) use named_expr_without_context::*;
|
||||||
pub(crate) use nan_comparison::*;
|
pub(crate) use nan_comparison::*;
|
||||||
|
|
@ -155,6 +156,7 @@ mod logging;
|
||||||
mod magic_value_comparison;
|
mod magic_value_comparison;
|
||||||
mod manual_import_from;
|
mod manual_import_from;
|
||||||
mod misplaced_bare_raise;
|
mod misplaced_bare_raise;
|
||||||
|
mod missing_maxsplit_arg;
|
||||||
mod modified_iterating_set;
|
mod modified_iterating_set;
|
||||||
mod named_expr_without_context;
|
mod named_expr_without_context;
|
||||||
mod nan_comparison;
|
mod nan_comparison;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,324 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/pylint/mod.rs
|
||||||
|
---
|
||||||
|
missing_maxsplit_arg.py:14:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
12 | # Errors
|
||||||
|
13 | ## Test split called directly on string literal
|
||||||
|
14 | "1,2,3".split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
15 | "1,2,3".split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
16 | "1,2,3".rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:15:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
13 | ## Test split called directly on string literal
|
||||||
|
14 | "1,2,3".split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
15 | "1,2,3".split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
16 | "1,2,3".rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
17 | "1,2,3".rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:16:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
14 | "1,2,3".split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
15 | "1,2,3".split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
16 | "1,2,3".rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
17 | "1,2,3".rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:17:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
15 | "1,2,3".split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
16 | "1,2,3".rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
17 | "1,2,3".rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
18 |
|
||||||
|
19 | ## Test split called on string variable
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:20:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
19 | ## Test split called on string variable
|
||||||
|
20 | SEQ.split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
21 | SEQ.split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
22 | SEQ.rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:21:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
19 | ## Test split called on string variable
|
||||||
|
20 | SEQ.split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
21 | SEQ.split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
22 | SEQ.rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
23 | SEQ.rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:22:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
20 | SEQ.split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
21 | SEQ.split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
22 | SEQ.rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
23 | SEQ.rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:23:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
21 | SEQ.split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
22 | SEQ.rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
23 | SEQ.rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
24 |
|
||||||
|
25 | ## Test split called on class attribute
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:26:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
25 | ## Test split called on class attribute
|
||||||
|
26 | Foo.class_str.split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
27 | Foo.class_str.split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
28 | Foo.class_str.rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:27:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
25 | ## Test split called on class attribute
|
||||||
|
26 | Foo.class_str.split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
27 | Foo.class_str.split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
28 | Foo.class_str.rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
29 | Foo.class_str.rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:28:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
26 | Foo.class_str.split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
27 | Foo.class_str.split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
28 | Foo.class_str.rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
29 | Foo.class_str.rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:29:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
27 | Foo.class_str.split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
28 | Foo.class_str.rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
29 | Foo.class_str.rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
30 |
|
||||||
|
31 | ## Test split called on sliced string
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:32:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
31 | ## Test split called on sliced string
|
||||||
|
32 | "1,2,3"[::-1].split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
33 | "1,2,3"[::-1][::-1].split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
34 | SEQ[:3].split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:33:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
31 | ## Test split called on sliced string
|
||||||
|
32 | "1,2,3"[::-1].split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
33 | "1,2,3"[::-1][::-1].split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
34 | SEQ[:3].split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
35 | Foo.class_str[1:3].split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:34:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
32 | "1,2,3"[::-1].split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
33 | "1,2,3"[::-1][::-1].split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
34 | SEQ[:3].split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
35 | Foo.class_str[1:3].split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
36 | "1,2,3"[::-1].rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:35:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
33 | "1,2,3"[::-1][::-1].split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
34 | SEQ[:3].split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
35 | Foo.class_str[1:3].split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
36 | "1,2,3"[::-1].rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
37 | SEQ[:3].rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:36:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
34 | SEQ[:3].split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
35 | Foo.class_str[1:3].split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
36 | "1,2,3"[::-1].rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
37 | SEQ[:3].rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
38 | Foo.class_str[1:3].rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:37:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
35 | Foo.class_str[1:3].split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
36 | "1,2,3"[::-1].rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
37 | SEQ[:3].rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
38 | Foo.class_str[1:3].rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:38:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
36 | "1,2,3"[::-1].rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
37 | SEQ[:3].rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
38 | Foo.class_str[1:3].rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
39 |
|
||||||
|
40 | ## Test sep given as named argument
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:41:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
40 | ## Test sep given as named argument
|
||||||
|
41 | "1,2,3".split(sep=",")[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
42 | "1,2,3".split(sep=",")[-1] # [missing-maxsplit-arg]
|
||||||
|
43 | "1,2,3".rsplit(sep=",")[0] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:42:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
40 | ## Test sep given as named argument
|
||||||
|
41 | "1,2,3".split(sep=",")[0] # [missing-maxsplit-arg]
|
||||||
|
42 | "1,2,3".split(sep=",")[-1] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
43 | "1,2,3".rsplit(sep=",")[0] # [missing-maxsplit-arg]
|
||||||
|
44 | "1,2,3".rsplit(sep=",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:43:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
41 | "1,2,3".split(sep=",")[0] # [missing-maxsplit-arg]
|
||||||
|
42 | "1,2,3".split(sep=",")[-1] # [missing-maxsplit-arg]
|
||||||
|
43 | "1,2,3".rsplit(sep=",")[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
44 | "1,2,3".rsplit(sep=",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:44:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
42 | "1,2,3".split(sep=",")[-1] # [missing-maxsplit-arg]
|
||||||
|
43 | "1,2,3".rsplit(sep=",")[0] # [missing-maxsplit-arg]
|
||||||
|
44 | "1,2,3".rsplit(sep=",")[-1] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
45 |
|
||||||
|
46 | ## Special cases
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:47:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
46 | ## Special cases
|
||||||
|
47 | "1,2,3".split("\n")[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
48 | "1,2,3".split("split")[-1] # [missing-maxsplit-arg]
|
||||||
|
49 | "1,2,3".rsplit("rsplit")[0] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:48:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
46 | ## Special cases
|
||||||
|
47 | "1,2,3".split("\n")[0] # [missing-maxsplit-arg]
|
||||||
|
48 | "1,2,3".split("split")[-1] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
49 | "1,2,3".rsplit("rsplit")[0] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:49:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
47 | "1,2,3".split("\n")[0] # [missing-maxsplit-arg]
|
||||||
|
48 | "1,2,3".split("split")[-1] # [missing-maxsplit-arg]
|
||||||
|
49 | "1,2,3".rsplit("rsplit")[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
50 |
|
||||||
|
51 | ## Test class attribute named split
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:52:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
51 | ## Test class attribute named split
|
||||||
|
52 | Bar.split.split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
53 | Bar.split.split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
54 | Bar.split.rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:53:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
51 | ## Test class attribute named split
|
||||||
|
52 | Bar.split.split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
53 | Bar.split.split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
54 | Bar.split.rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
55 | Bar.split.rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:54:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
52 | Bar.split.split(",")[0] # [missing-maxsplit-arg]
|
||||||
|
53 | Bar.split.split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
54 | Bar.split.rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
55 | Bar.split.rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:55:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
53 | Bar.split.split(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
54 | Bar.split.rsplit(",")[0] # [missing-maxsplit-arg]
|
||||||
|
55 | Bar.split.rsplit(",")[-1] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
56 |
|
||||||
|
57 | ## Test unpacked dict literal kwargs
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:58:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
57 | ## Test unpacked dict literal kwargs
|
||||||
|
58 | "1,2,3".split(**{"sep": ","})[0] # [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:179:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
177 | # Errors
|
||||||
|
178 | kwargs_without_maxsplit = {"seq": ","}
|
||||||
|
179 | "1,2,3".split(**kwargs_without_maxsplit)[0] # TODO: [missing-maxsplit-arg]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
180 | # OK
|
||||||
|
181 | kwargs_with_maxsplit = {"maxsplit": 1}
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:182:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
180 | # OK
|
||||||
|
181 | kwargs_with_maxsplit = {"maxsplit": 1}
|
||||||
|
182 | "1,2,3".split(",", **kwargs_with_maxsplit)[0] # TODO: false positive
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
183 | kwargs_with_maxsplit = {"sep": ",", "maxsplit": 1}
|
||||||
|
184 | "1,2,3".split(**kwargs_with_maxsplit)[0] # TODO: false positive
|
||||||
|
|
|
||||||
|
|
||||||
|
missing_maxsplit_arg.py:184:1: PLC0207 Accessing only the first or last element of `str.split()` without setting `maxsplit=1`
|
||||||
|
|
|
||||||
|
182 | "1,2,3".split(",", **kwargs_with_maxsplit)[0] # TODO: false positive
|
||||||
|
183 | kwargs_with_maxsplit = {"sep": ",", "maxsplit": 1}
|
||||||
|
184 | "1,2,3".split(**kwargs_with_maxsplit)[0] # TODO: false positive
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0207
|
||||||
|
|
|
||||||
1
ruff.schema.json
generated
1
ruff.schema.json
generated
|
|
@ -3588,6 +3588,7 @@
|
||||||
"PLC020",
|
"PLC020",
|
||||||
"PLC0205",
|
"PLC0205",
|
||||||
"PLC0206",
|
"PLC0206",
|
||||||
|
"PLC0207",
|
||||||
"PLC0208",
|
"PLC0208",
|
||||||
"PLC04",
|
"PLC04",
|
||||||
"PLC041",
|
"PLC041",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue