-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add b040: exception with note added not re-raised or used (#12097) #12764
Conversation
|
@@ -1474,6 +1474,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { | |||
checker, stmt, body, handlers, orelse, finalbody, | |||
); | |||
} | |||
if checker.enabled(Rule::SuppressedException) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rule name is very close to SIM105
suppressible-exception
.
Which makes me wonder if the rule should be integrated into PLW0133
? @AlexWaygood what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree the name is similar, but this check is about checking if there is only add_note
executed on the exception, so it's quite different from SIM105. On the other hand it is similar to PLW0133, but this rule exists in the flake8 bugbear, so it might be worth keeping the same set of rules with upstream..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Integrating it into PLW0133
is appealing because it's a more general rule, and I agree that this is just trying to tackle a variant of that problem.
But flake8-bugbear is more popular and widely used than pylint, I think, and I believe people much more often select our B
rules than our PLW
rules. So I agree that consistency with flake8-bugbear is useful here.
I think that when doing rule recategorisation (a medium-term goal that you don't have to worry about here @divident), we'll probably no longer have categories that refer to older linters, and at that point it would probably make sense to merge PLW0133
and this rule. But for now I guess I'd go for a separate check, despite how similar the two rules are.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels strange to me to introduce a new rule if we know we'll want to merge it eventually anyway. But I do see the point and redirects are relatively cheap for users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I think we have to come up a more specific name to also make it clear that this is a very specific rule and it doesn't aim to catch all suppressed exceptions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about the name "add_note_exception_suppression" ?
A few more suggestions:
- add_note_only_exception
- note_only_exception_suppression
- exclusive_add_note_exception
- exception_note_only
- suppressed_with_add_note
let raises = { | ||
let mut visitor = RaiseStatementVisitor::default(); | ||
visitor.visit_body(body); | ||
visitor.raises | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we can simplify this code by making more use of Binding
:
- Resolve the
bindings
ofname
- Iterate over all references of
binding
and- exit if any is statement (use
SemanticModel::statement(binding.expression_id())
) is a a raise statement and the target or cause is the binding node - count the
add_note
calls
- exit if any is statement (use
- Create a diagnostic if there's at least one
add_note
call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the upstream implementation, the following code is safe, which requires checking all raise
statements in scope, not just those with binding
try:
...
except Exception as e:
e.add_note("...")
raise # safe
On the last point, just checking for the add_note
call is not enough, because exceptions can be used in the print, function calls etc that are considered to be safe, so I'm counting all references to them. Not sure if there is a better way to find all usages of the exception that are referenced.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I follow. Doesn't
try: ...
except Exception as e:
e.add_note("...") raise # safe
introduce a binding for e
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does, but my point is that raise
is not included in the references list when I filter by exception name.
let bindings: Vec<&Binding> = semantic
.current_scope()
.get_all(exception_name.id.as_str())
.map(|binding_id| semantic.binding(binding_id))
.filter(|binding| matches!(binding.kind, BindingKind::UnboundException(_)))
.collect();
for binding in bindings.iter() {
for reference_id in binding.references() {
let reference = checker.semantic().reference(reference_id);
if let Some(node_id) = reference.expression_id() {
let stmt = checker.semantic().statement(node_id);
println!("stmt={:?}", stmt);
...
prints just a add_note
call
stmt=Expr(StmtExpr { range: 76..93, value: Call(ExprCall { range: 76..93, func: Attribute(ExprAttribute { range: 76..86, value: Name(ExprName { range: 76..77, id: Name("e"), ctx: Load }), attr: Identifier { id: Name("add_note"), range: 78..86 }, ctx: Load }), arguments: Arguments { range: 86..93, args: [StringLiteral(ExprStringLiteral { range: 87..92, value: StringLiteralValue { inner: Single
Summary
This PR ports the B040 implementation from PyCQA/flake8-bugbear#477.
It checks whenever the exception is used, except for the `add_note' method.