-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Avoid triggering
pd#at
and friends on non-subscripts (#4474)
- Loading branch information
1 parent
39fb2cc
commit cd82b83
Showing
8 changed files
with
207 additions
and
151 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,51 @@ | ||
use ruff_python_semantic::binding::{BindingKind, Importation}; | ||
use ruff_python_semantic::context::Context; | ||
use rustpython_parser::ast; | ||
use rustpython_parser::ast::Expr; | ||
|
||
/// Return `true` if an `Expr` _could_ be a `DataFrame`. This rules out | ||
/// obviously-wrong cases, like constants and literals. | ||
pub(crate) const fn is_dataframe_candidate(expr: &Expr) -> bool { | ||
!matches!( | ||
expr, | ||
pub(crate) enum Resolution { | ||
/// The expression resolves to an irrelevant expression type (e.g., a constant). | ||
IrrelevantExpression, | ||
/// The expression resolves to an irrelevant binding (e.g., a function definition). | ||
IrrelevantBinding, | ||
/// The expression resolves to a relevant local binding (e.g., a variable). | ||
RelevantLocal, | ||
/// The expression resolves to the `pandas` module itself. | ||
PandasModule, | ||
} | ||
|
||
/// Test an [`Expr`] for relevance to Pandas-related operations. | ||
pub(crate) fn test_expression(expr: &Expr, context: &Context) -> Resolution { | ||
match expr { | ||
Expr::Constant(_) | ||
| Expr::Tuple(_) | ||
| Expr::List(_) | ||
| Expr::Set(_) | ||
| Expr::Dict(_) | ||
| Expr::SetComp(_) | ||
| Expr::ListComp(_) | ||
| Expr::DictComp(_) | ||
| Expr::GeneratorExp(_) | ||
) | ||
| Expr::Tuple(_) | ||
| Expr::List(_) | ||
| Expr::Set(_) | ||
| Expr::Dict(_) | ||
| Expr::SetComp(_) | ||
| Expr::ListComp(_) | ||
| Expr::DictComp(_) | ||
| Expr::GeneratorExp(_) => Resolution::IrrelevantExpression, | ||
Expr::Name(ast::ExprName { id, .. }) => { | ||
context | ||
.find_binding(id) | ||
.map_or(Resolution::IrrelevantBinding, |binding| { | ||
match binding.kind { | ||
BindingKind::Annotation | ||
| BindingKind::Argument | ||
| BindingKind::Assignment | ||
| BindingKind::NamedExprAssignment | ||
| BindingKind::Binding | ||
| BindingKind::LoopVar | ||
| BindingKind::Global | ||
| BindingKind::Nonlocal => Resolution::RelevantLocal, | ||
BindingKind::Importation(Importation { | ||
full_name: module, .. | ||
}) if module == "pandas" => Resolution::PandasModule, | ||
_ => Resolution::IrrelevantBinding, | ||
} | ||
}) | ||
} | ||
_ => Resolution::RelevantLocal, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
use rustpython_parser::ast::{Expr, Ranged}; | ||
|
||
use ruff_diagnostics::Violation; | ||
use ruff_diagnostics::{Diagnostic, DiagnosticKind}; | ||
use ruff_macros::{derive_message_formats, violation}; | ||
|
||
use crate::checkers::ast::Checker; | ||
use crate::registry::Rule; | ||
use crate::rules::pandas_vet::helpers::{test_expression, Resolution}; | ||
|
||
#[violation] | ||
pub struct PandasUseOfDotValues; | ||
|
||
impl Violation for PandasUseOfDotValues { | ||
#[derive_message_formats] | ||
fn message(&self) -> String { | ||
format!("Use `.to_numpy()` instead of `.values`") | ||
} | ||
} | ||
|
||
pub(crate) fn attr(checker: &mut Checker, attr: &str, value: &Expr, attr_expr: &Expr) { | ||
let rules = &checker.settings.rules; | ||
let violation: DiagnosticKind = match attr { | ||
"values" if rules.enabled(Rule::PandasUseOfDotValues) => PandasUseOfDotValues.into(), | ||
_ => return, | ||
}; | ||
|
||
// Avoid flagging on function calls (e.g., `df.values()`). | ||
if let Some(parent) = checker.ctx.expr_parent() { | ||
if matches!(parent, Expr::Call(_)) { | ||
return; | ||
} | ||
} | ||
|
||
// Avoid flagging on non-DataFrames (e.g., `{"a": 1}.values`), and on irrelevant bindings | ||
// (like imports). | ||
if !matches!( | ||
test_expression(value, &checker.ctx), | ||
Resolution::RelevantLocal | ||
) { | ||
return; | ||
} | ||
|
||
checker | ||
.diagnostics | ||
.push(Diagnostic::new(violation, attr_expr.range())); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,16 @@ | ||
pub(crate) use assignment_to_df::{assignment_to_df, PandasDfVariableName}; | ||
pub(crate) use check_attr::{ | ||
check_attr, PandasUseOfDotAt, PandasUseOfDotIat, PandasUseOfDotIx, PandasUseOfDotValues, | ||
}; | ||
pub(crate) use check_call::{ | ||
check_call, PandasUseOfDotIsNull, PandasUseOfDotNotNull, PandasUseOfDotPivotOrUnstack, | ||
pub(crate) use attr::{attr, PandasUseOfDotValues}; | ||
pub(crate) use call::{ | ||
call, PandasUseOfDotIsNull, PandasUseOfDotNotNull, PandasUseOfDotPivotOrUnstack, | ||
PandasUseOfDotReadTable, PandasUseOfDotStack, | ||
}; | ||
pub(crate) use inplace_argument::{inplace_argument, PandasUseOfInplaceArgument}; | ||
pub(crate) use pd_merge::{use_of_pd_merge, PandasUseOfPdMerge}; | ||
pub(crate) use subscript::{subscript, PandasUseOfDotAt, PandasUseOfDotIat, PandasUseOfDotIx}; | ||
|
||
pub(crate) mod assignment_to_df; | ||
pub(crate) mod check_attr; | ||
pub(crate) mod check_call; | ||
pub(crate) mod attr; | ||
pub(crate) mod call; | ||
pub(crate) mod inplace_argument; | ||
pub(crate) mod pd_merge; | ||
pub(crate) mod subscript; |
Oops, something went wrong.