Avoid PD false positives on some non-DataFrame expressions (#1538)

This commit is contained in:
Charlie Marsh 2023-01-01 17:05:57 -05:00 committed by GitHub
parent 2cf6d05586
commit 31ce37dd8e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 25 additions and 2 deletions

View file

@ -212,6 +212,23 @@ pub fn is_constant_non_singleton(expr: &Expr) -> bool {
is_constant(expr) && !is_singleton(expr) is_constant(expr) && !is_singleton(expr)
} }
/// Return `true` if an `Expr` is not a reference to a variable (or something
/// that could resolve to a variable, like a function call).
pub fn is_non_variable(expr: &Expr) -> bool {
matches!(
expr.node,
ExprKind::Constant { .. }
| ExprKind::Tuple { .. }
| ExprKind::List { .. }
| ExprKind::Set { .. }
| ExprKind::Dict { .. }
| ExprKind::SetComp { .. }
| ExprKind::ListComp { .. }
| ExprKind::DictComp { .. }
| ExprKind::GeneratorExp { .. }
)
}
/// Return the `Keyword` with the given name, if it's present in the list of /// Return the `Keyword` with the given name, if it's present in the list of
/// `Keyword` arguments. /// `Keyword` arguments.
pub fn find_keyword<'a>(keywords: &'a [Keyword], keyword_name: &str) -> Option<&'a Keyword> { pub fn find_keyword<'a>(keywords: &'a [Keyword], keyword_name: &str) -> Option<&'a Keyword> {

View file

@ -1588,7 +1588,7 @@ where
pylint::plugins::used_prior_global_declaration(self, id, expr); pylint::plugins::used_prior_global_declaration(self, id, expr);
} }
} }
ExprKind::Attribute { attr, .. } => { ExprKind::Attribute { attr, value, .. } => {
// Ex) typing.List[...] // Ex) typing.List[...]
if !self.in_deferred_string_type_definition if !self.in_deferred_string_type_definition
&& self.settings.enabled.contains(&CheckCode::UP006) && self.settings.enabled.contains(&CheckCode::UP006)
@ -1629,11 +1629,16 @@ where
] { ] {
if self.settings.enabled.contains(&code) { if self.settings.enabled.contains(&code) {
if attr == name { if attr == name {
// Avoid flagging on function calls (e.g., `df.values()`).
if let Some(parent) = self.current_expr_parent() { if let Some(parent) = self.current_expr_parent() {
if matches!(parent.0.node, ExprKind::Call { .. }) { if matches!(parent.0.node, ExprKind::Call { .. }) {
continue; continue;
} }
} }
// Avoid flagging on non-DataFrames (e.g., `{"a": 1}.values`).
if helpers::is_non_variable(value) {
continue;
}
self.add_check(Check::new(code.kind(), Range::from_located(expr))); self.add_check(Check::new(code.kind(), Range::from_located(expr)));
}; };
} }

View file

@ -81,7 +81,8 @@ mod tests {
#[test_case("result = df.to_array()", &[]; "PD011_pass_to_array")] #[test_case("result = df.to_array()", &[]; "PD011_pass_to_array")]
#[test_case("result = df.array", &[]; "PD011_pass_array")] #[test_case("result = df.array", &[]; "PD011_pass_array")]
#[test_case("result = df.values", &[CheckCode::PD011]; "PD011_fail_values")] #[test_case("result = df.values", &[CheckCode::PD011]; "PD011_fail_values")]
#[test_case("result = {}.values()", &[]; "PD011_pass_values_call")] #[test_case("result = df.values()", &[]; "PD011_pass_values_call")]
#[test_case("result = {}.values", &[]; "PD011_pass_values_dict")]
#[test_case("result = values", &[]; "PD011_pass_node_name")] #[test_case("result = values", &[]; "PD011_pass_node_name")]
#[test_case("employees = pd.read_csv(input_file)", &[]; "PD012_pass_read_csv")] #[test_case("employees = pd.read_csv(input_file)", &[]; "PD012_pass_read_csv")]
#[test_case("employees = pd.read_table(input_file)", &[CheckCode::PD012]; "PD012_fail_read_table")] #[test_case("employees = pd.read_table(input_file)", &[CheckCode::PD012]; "PD012_fail_read_table")]