From e35deee583c1baa17856d3e0936e988bf597df2d Mon Sep 17 00:00:00 2001 From: Christoph Hasse Date: Wed, 29 May 2024 23:15:05 -0400 Subject: [PATCH] fix(F822): add option to enable F822 in __init__.py files (#11370) ## Summary This PR aims to close #10095 by adding an option `init-allow-undef-export` to the `pyflakes` settings. This option is currently set to `true` such that behavior is kept identical. But setting this option to `false` will lead to `F822` warnings to be shown in all files, **including** `__init__.py` files. As I've mentioned on #10095, I think `init-allow-undef-export=false` would be the more user-friendly default option, as it creates fewer surprises. @charliermarsh what do you think about making that the default? With this option in place, it's a single line fix for people that rely on the old behavior. And thinking longer term, for future major releases, one could probably consider deprecating the option and eventually having people just `noqa` these warnings if they are not wanted. ## Test Plan I've added a `test_init_f822_enabled` test which repeats the test that is done in the `init` test but this time with `init-allow-undef-export=false` and the snap file correctly shows that ruff will then trigger the otherwise suppressed F822 warning. closes #10095 --- crates/ruff_linter/src/checkers/ast/mod.rs | 4 ++- crates/ruff_linter/src/rules/pyflakes/mod.rs | 1 + .../rules/pyflakes/rules/undefined_export.rs | 7 +++++ ...kes__tests__preview__F822___init__.py.snap | 26 +++++++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F822___init__.py.snap diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index d91dcc25f9..faaf41595a 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -2301,7 +2301,9 @@ impl<'a> Checker<'a> { } } else { if self.enabled(Rule::UndefinedExport) { - if !self.path.ends_with("__init__.py") { + if self.settings.preview.is_enabled() + || !self.path.ends_with("__init__.py") + { self.diagnostics.push( Diagnostic::new( pyflakes::rules::UndefinedExport { diff --git a/crates/ruff_linter/src/rules/pyflakes/mod.rs b/crates/ruff_linter/src/rules/pyflakes/mod.rs index 107cb96fb3..81bc61c1f1 100644 --- a/crates/ruff_linter/src/rules/pyflakes/mod.rs +++ b/crates/ruff_linter/src/rules/pyflakes/mod.rs @@ -212,6 +212,7 @@ mod tests { #[test_case(Rule::UnusedImport, Path::new("F401_27__all_mistyped/__init__.py"))] #[test_case(Rule::UnusedImport, Path::new("F401_28__all_multiple/__init__.py"))] #[test_case(Rule::UnusedImport, Path::new("F401_29__all_conditional/__init__.py"))] + #[test_case(Rule::UndefinedExport, Path::new("__init__.py"))] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!( "preview__{}_{}", diff --git a/crates/ruff_linter/src/rules/pyflakes/rules/undefined_export.rs b/crates/ruff_linter/src/rules/pyflakes/rules/undefined_export.rs index 6858f1124c..a922c43b3e 100644 --- a/crates/ruff_linter/src/rules/pyflakes/rules/undefined_export.rs +++ b/crates/ruff_linter/src/rules/pyflakes/rules/undefined_export.rs @@ -13,6 +13,11 @@ use ruff_macros::{derive_message_formats, violation}; /// Including an undefined name in `__all__` is likely to raise `NameError` at /// runtime, when the module is imported. /// +/// In [preview], this rule will flag undefined names in `__init__.py` file, +/// even if those names implicitly refer to other modules in the package. Users +/// that rely on implicit exports should disable this rule in `__init__.py` +/// files via [`lint.per-file-ignores`]. +/// /// ## Example /// ```python /// from foo import bar @@ -31,6 +36,8 @@ use ruff_macros::{derive_message_formats, violation}; /// /// ## References /// - [Python documentation: `__all__`](https://docs.python.org/3/tutorial/modules.html#importing-from-a-package) +/// +/// [preview]: https://docs.astral.sh/ruff/preview/ #[violation] pub struct UndefinedExport { pub name: String, diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F822___init__.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F822___init__.py.snap new file mode 100644 index 0000000000..d9761fcd78 --- /dev/null +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F822___init__.py.snap @@ -0,0 +1,26 @@ +--- +source: crates/ruff_linter/src/rules/pyflakes/mod.rs +--- +__init__.py:5:12: F822 Undefined name `a` in `__all__` + | +3 | print(__path__) +4 | +5 | __all__ = ["a", "b", "c"] + | ^^^ F822 + | + +__init__.py:5:17: F822 Undefined name `b` in `__all__` + | +3 | print(__path__) +4 | +5 | __all__ = ["a", "b", "c"] + | ^^^ F822 + | + +__init__.py:5:22: F822 Undefined name `c` in `__all__` + | +3 | print(__path__) +4 | +5 | __all__ = ["a", "b", "c"] + | ^^^ F822 + |