Skip to content

Commit

Permalink
Use function_type::classify for yield-in-init (#2742)
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Feb 10, 2023
1 parent a5e42d2 commit 3f20f73
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 18 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1360,7 +1360,7 @@ For more, see [Pylint](https://pypi.org/project/pylint/) on PyPI.

| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| PLE0100 | yield-in-init | `__init__` method is a generator | |
| PLE0100 | [yield-in-init](https://github.com/charliermarsh/ruff/blob/main/docs/rules/yield-in-init.md) | `__init__` method is a generator | |
| PLE0117 | nonlocal-without-binding | Nonlocal name `{name}` found without binding | |
| PLE0118 | used-prior-global-declaration | Name `{name}` is used prior to global declaration on line {line} | |
| PLE0604 | invalid-all-object | Invalid object in `__all__`, must contain only strings | |
Expand Down
8 changes: 8 additions & 0 deletions crates/ruff/src/checkers/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3884,6 +3884,14 @@ impl<'a> Checker<'a> {
&self.scopes[*(self.scope_stack.last().expect("No current scope found"))]
}

pub fn current_scope_parent(&self) -> Option<&Scope> {
self.scope_stack
.iter()
.rev()
.nth(1)
.map(|index| &self.scopes[*index])
}

pub fn current_scopes(&self) -> impl Iterator<Item = &Scope> {
self.scope_stack
.iter()
Expand Down
65 changes: 48 additions & 17 deletions crates/ruff/src/rules/pylint/rules/yield_in_init.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use ruff_macros::{define_violation, derive_message_formats};
use rustpython_parser::ast::Expr;

use ruff_macros::{define_violation, derive_message_formats};

use crate::ast::function_type;
use crate::ast::function_type::FunctionType;
use crate::{
ast::types::{FunctionDef, Range, ScopeKind},
checkers::ast::Checker,
Expand All @@ -9,6 +12,19 @@ use crate::{
};

define_violation!(
/// ### What it does
/// Checks for `__init__.py` methods that turned into generators
/// via the presence of `yield` or `yield from` statements.
///
/// ### Why is this bad?
/// Generators are not allowed in `__init__.py` methods.
///
/// ### Example
/// ```python
/// class Foo:
/// def __init__(self):
/// yield 1
/// ```
pub struct YieldInInit;
);

Expand All @@ -21,23 +37,38 @@ impl Violation for YieldInInit {

/// PLE0100
pub fn yield_in_init(checker: &mut Checker, expr: &Expr) {
let parent_scope_is_class: Option<bool> =
checker
.current_scopes()
.nth(1)
.and_then(|scope| match scope.kind {
ScopeKind::Class(..) => Some(true),
_ => None,
});

let current_scope_is_init = match checker.current_scope().kind {
ScopeKind::Function(FunctionDef { name, .. }) => Some(name == "__init__"),
_ => None,
let scope = checker.current_scope();
let ScopeKind::Function(FunctionDef {
name,
decorator_list,
..
}) = &scope.kind else {
return;
};

if *name != "__init__" {
return;
}

let Some(parent) = checker.current_scope_parent() else {
return;
};

if parent_scope_is_class == Some(true) && current_scope_is_init == Some(true) {
checker
.diagnostics
.push(Diagnostic::new(YieldInInit, Range::from_located(expr)));
if !matches!(
function_type::classify(
checker,
parent,
name,
decorator_list,
&checker.settings.pep8_naming.classmethod_decorators,
&checker.settings.pep8_naming.staticmethod_decorators,
),
FunctionType::Method
) {
return;
}

checker
.diagnostics
.push(Diagnostic::new(YieldInInit, Range::from_located(expr)));
}
17 changes: 17 additions & 0 deletions docs/rules/yield-in-init.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# yield-in-init (PLE0100)

Derived from the **Pylint** linter.

### What it does
Checks for `__init__.py` methods that turned into generators
via the presence of `yield` or `yield from` statements.

### Why is this bad?
Generators are not allowed in `__init__.py` methods.

### Example
```python
class Foo:
def __init__(self):
yield 1
```

0 comments on commit 3f20f73

Please sign in to comment.