Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
linter.ruff.strictly_empty_init_modules = false

# Formatter Settings
formatter.exclude = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
linter.ruff.strictly_empty_init_modules = false

# Formatter Settings
formatter.exclude = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
linter.ruff.strictly_empty_init_modules = false

# Formatter Settings
formatter.exclude = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
linter.ruff.strictly_empty_init_modules = false

# Formatter Settings
formatter.exclude = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
linter.ruff.strictly_empty_init_modules = false

# Formatter Settings
formatter.exclude = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
linter.ruff.strictly_empty_init_modules = false

# Formatter Settings
formatter.exclude = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
linter.ruff.strictly_empty_init_modules = false

# Formatter Settings
formatter.exclude = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
linter.ruff.strictly_empty_init_modules = false

# Formatter Settings
formatter.exclude = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
linter.ruff.strictly_empty_init_modules = false

# Formatter Settings
formatter.exclude = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
linter.ruff.strictly_empty_init_modules = false

# Formatter Settings
formatter.exclude = []
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""This is the module docstring."""

# convenience imports:
import os
from pathlib import Path

__all__ = ["MY_CONSTANT"]
__all__ += ["foo"]
__all__: list[str] = __all__
__all__ = __all__ = __all__

MY_CONSTANT = 5
"""This is an important constant."""

os.environ["FOO"] = 1


def foo():
return Path("foo.py")

def __getattr__(name): # ok
return name

__path__ = __import__('pkgutil').extend_path(__path__, __name__) # ok

if os.environ["FOO"] != "1": # RUF067
MY_CONSTANT = 4 # ok, don't flag nested statements

if TYPE_CHECKING: # ok
MY_CONSTANT = 3

import typing

if typing.TYPE_CHECKING: # ok
MY_CONSTANT = 2

__version__ = "1.2.3" # ok

def __dir__(): # ok
return ["foo"]

import pkgutil

__path__ = pkgutil.extend_path(__path__, __name__) # ok
__path__ = unknown.extend_path(__path__, __name__) # also ok

# non-`extend_path` assignments are not allowed
__path__ = 5 # RUF067

# also allow `__author__`
__author__ = "The Author" # ok
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""
The code here is not in an `__init__.py` file and should not trigger the
lint.
"""

# convenience imports:
import os
from pathlib import Path

__all__ = ["MY_CONSTANT"]
__all__ += ["foo"]
__all__: list[str] = __all__
__all__ = __all__ = __all__

MY_CONSTANT = 5
"""This is an important constant."""

os.environ["FOO"] = 1


def foo():
return Path("foo.py")

def __getattr__(name): # ok
return name

__path__ = __import__('pkgutil').extend_path(__path__, __name__) # ok

if os.environ["FOO"] != "1": # RUF067
MY_CONSTANT = 4 # ok, don't flag nested statements

if TYPE_CHECKING: # ok
MY_CONSTANT = 3

import typing

if typing.TYPE_CHECKING: # ok
MY_CONSTANT = 2

__version__ = "1.2.3" # ok

def __dir__(): # ok
return ["foo"]

import pkgutil

__path__ = pkgutil.extend_path(__path__, __name__) # ok
__path__ = unknown.extend_path(__path__, __name__) # also ok

# non-`extend_path` assignments are not allowed
__path__ = 5 # RUF067

# also allow `__author__`
__author__ = "The Author" # ok
3 changes: 3 additions & 0 deletions crates/ruff_linter/src/checkers/ast/analyze/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1630,4 +1630,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
}
_ => {}
}
if checker.is_rule_enabled(Rule::NonEmptyInitModule) {
ruff::rules::non_empty_init_module(checker, stmt);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub(crate) fn unresolved_references(checker: &Checker) {
}

// Allow __path__.
if checker.path.ends_with("__init__.py") {
if checker.in_init_module() {
if reference.name(checker.source()) == "__path__" {
continue;
}
Expand Down
15 changes: 10 additions & 5 deletions crates/ruff_linter/src/checkers/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
//! represents the lint-rule analysis phase. In the future, these steps may be separated into
//! distinct passes over the AST.

use std::cell::RefCell;
use std::cell::{OnceCell, RefCell};
use std::path::Path;

use itertools::Itertools;
Expand Down Expand Up @@ -198,6 +198,8 @@ pub(crate) struct Checker<'a> {
parsed_type_annotation: Option<&'a ParsedAnnotation>,
/// The [`Path`] to the file under analysis.
path: &'a Path,
/// Whether `path` points to an `__init__.py` file.
in_init_module: OnceCell<bool>,
/// The [`Path`] to the package containing the current file.
package: Option<PackageRoot<'a>>,
/// The module representation of the current file (e.g., `foo.bar`).
Expand Down Expand Up @@ -274,6 +276,7 @@ impl<'a> Checker<'a> {
noqa_line_for,
noqa,
path,
in_init_module: OnceCell::new(),
package,
module,
source_type,
Expand Down Expand Up @@ -482,9 +485,11 @@ impl<'a> Checker<'a> {
self.context.settings
}

/// The [`Path`] to the file under analysis.
pub(crate) const fn path(&self) -> &'a Path {
self.path
/// Returns whether the file under analysis is an `__init__.py` file.
pub(crate) fn in_init_module(&self) -> bool {
*self
.in_init_module
.get_or_init(|| self.path.ends_with("__init__.py"))
}

/// The [`Path`] to the package containing the current file.
Expand Down Expand Up @@ -3171,7 +3176,7 @@ impl<'a> Checker<'a> {
// F822
if self.is_rule_enabled(Rule::UndefinedExport) {
if is_undefined_export_in_dunder_init_enabled(self.settings())
|| !self.path.ends_with("__init__.py")
|| !self.in_init_module()
{
self.report_diagnostic(
pyflakes::rules::UndefinedExport {
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_linter/src/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Ruff, "064") => rules::ruff::rules::NonOctalPermissions,
(Ruff, "065") => rules::ruff::rules::LoggingEagerConversion,
(Ruff, "066") => rules::ruff::rules::PropertyWithoutReturn,
(Ruff, "067") => rules::ruff::rules::NonEmptyInitModule,

(Ruff, "100") => rules::ruff::rules::UnusedNOQA,
(Ruff, "101") => rules::ruff::rules::RedirectedNOQA,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ pub(crate) fn unused_import(checker: &Checker, scope: &Scope) {
}
}

let in_init = checker.path().ends_with("__init__.py");
let in_init = checker.in_init_module();
let fix_init = !checker.settings().ignore_init_module_imports;
let preview_mode = is_dunder_init_fix_unused_import_enabled(checker.settings());
let dunder_all_exprs = find_dunder_all_exprs(checker.semantic());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pub(crate) fn useless_import_alias(checker: &Checker, alias: &Alias) {
}

// A re-export in __init__.py is probably intentional.
if checker.path().ends_with("__init__.py") {
if checker.in_init_module() {
return;
}

Expand Down Expand Up @@ -116,7 +116,7 @@ pub(crate) fn useless_import_from_alias(
}

// A re-export in __init__.py is probably intentional.
if checker.path().ends_with("__init__.py") {
if checker.in_init_module() {
return;
}

Expand Down
26 changes: 26 additions & 0 deletions crates/ruff_linter/src/rules/ruff/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ mod tests {
#[test_case(Rule::RedirectedNOQA, Path::new("RUF101_0.py"))]
#[test_case(Rule::RedirectedNOQA, Path::new("RUF101_1.py"))]
#[test_case(Rule::InvalidRuleCode, Path::new("RUF102.py"))]
#[test_case(Rule::NonEmptyInitModule, Path::new("RUF067/modules/__init__.py"))]
#[test_case(Rule::NonEmptyInitModule, Path::new("RUF067/modules/okay.py"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
let diagnostics = test_path(
Expand All @@ -136,6 +138,7 @@ mod tests {
&LinterSettings {
ruff: super::settings::Settings {
parenthesize_tuple_in_subscript: true,
..super::settings::Settings::default()
},
..LinterSettings::for_rule(Rule::IncorrectlyParenthesizedTupleInSubscript)
},
Expand All @@ -151,6 +154,7 @@ mod tests {
&LinterSettings {
ruff: super::settings::Settings {
parenthesize_tuple_in_subscript: false,
..super::settings::Settings::default()
},
unresolved_target_version: PythonVersion::PY310.into(),
..LinterSettings::for_rule(Rule::IncorrectlyParenthesizedTupleInSubscript)
Expand Down Expand Up @@ -714,4 +718,26 @@ mod tests {
assert_diagnostics!(snapshot, diagnostics);
Ok(())
}

#[test]
fn strictly_empty_init_modules_ruf067() -> Result<()> {
assert_diagnostics_diff!(
Path::new("ruff/RUF067/modules/__init__.py"),
&LinterSettings {
ruff: super::settings::Settings {
strictly_empty_init_modules: false,
..super::settings::Settings::default()
},
..LinterSettings::for_rule(Rule::NonEmptyInitModule)
},
&LinterSettings {
ruff: super::settings::Settings {
strictly_empty_init_modules: true,
..super::settings::Settings::default()
},
..LinterSettings::for_rule(Rule::NonEmptyInitModule)
},
);
Ok(())
}
}
2 changes: 2 additions & 0 deletions crates/ruff_linter/src/rules/ruff/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub(crate) use mutable_dataclass_default::*;
pub(crate) use mutable_fromkeys_value::*;
pub(crate) use needless_else::*;
pub(crate) use never_union::*;
pub(crate) use non_empty_init_module::*;
pub(crate) use non_octal_permissions::*;
pub(crate) use none_not_at_end_of_union::*;
pub(crate) use parenthesize_chained_operators::*;
Expand Down Expand Up @@ -99,6 +100,7 @@ mod mutable_dataclass_default;
mod mutable_fromkeys_value;
mod needless_else;
mod never_union;
mod non_empty_init_module;
mod non_octal_permissions;
mod none_not_at_end_of_union;
mod parenthesize_chained_operators;
Expand Down
Loading