Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
@@ -0,0 +1,60 @@
# `lazy-import-immediately-resolved` (`TID255`)

```toml
target-version = "py315"

[lint]
preview = true
select = ["TID254", "TID255"]
flake8-tidy-imports.require-lazy = "all"
```

## Required lazy imports

`TID255` ignores imports that are required to be lazy to avoid a conflict with `TID254`, even if the
import is resolved immediately.

```py
import foo # snapshot: lazy-import-mismatch

class Bar(foo.Base): ...
```

```snapshot
error[TID254]: Use a `lazy` import instead of an eager import
--> src/mdtest_snippet.py:1:8
|
1 | import foo # snapshot: lazy-import-mismatch
| ^^^
help: Convert to a lazy import
|
- import foo # snapshot: lazy-import-mismatch
1 + lazy import foo # snapshot: lazy-import-mismatch
2 |
|
note: This is an unsafe fix and may change runtime behavior
```

## Partially required lazy imports

`TID255` still reports immediately resolved names outside `require-lazy`, even when another name in
the same import is required to be lazy.

```toml
target-version = "py315"

[lint]
preview = true
select = ["TID254", "TID255"]
flake8-tidy-imports.require-lazy = ["foo", "pkg.Base"]
```

```py
lazy import foo as required, bar
lazy from pkg import Base as RequiredBase, OtherBase

required.value
RequiredBase()
bar.value # error: [lazy-import-immediately-resolved]
OtherBase() # error: [lazy-import-immediately-resolved]
```
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use ruff_text_size::{Ranged, TextRange};

use crate::checkers::ast::Checker;
use crate::codes::Category;
use crate::rules::flake8_tidy_imports::rules::BannedModuleImportPolicies;
use crate::{Edit, Fix, FixAvailability, Violation};

/// ## What it does
Expand Down Expand Up @@ -46,6 +47,12 @@ use crate::{Edit, Fix, FixAvailability, Violation};
/// The fix is only available when the lazy import statement imports a single
/// member, since removing `lazy` from a multi-member import would make every
/// imported member eager, including names that may not be resolved immediately.
///
/// ## Options
///
/// The rule ignores imports required to be lazy by the following setting:
///
/// - [`lint.flake8-tidy-imports.require-lazy`]
#[derive(ViolationMetadata)]
#[violation_metadata(preview_since = "0.15.13", category = Category::Correctness)]
pub(crate) struct LazyImportImmediatelyResolved {
Expand Down Expand Up @@ -90,6 +97,19 @@ pub(crate) fn lazy_import_immediately_resolved(checker: &Checker, name: &ExprNam
return;
};

// Ignore imports that are required to be lazy.
let require_lazy = &checker.settings().flake8_tidy_imports.require_lazy;
for (policy, node) in &BannedModuleImportPolicies::new(import, checker) {
// An `all` selector matches a `from` import's module, not its individual members.
if require_lazy.includes_all() && import.is_import_from_stmt() && node.is_alias() {
break;
}

if node.range().contains_range(binding.range()) && require_lazy.find(&policy).is_some() {
return;
}
}

let fix_range = if is_single_member_import(import) {
lazy_import_prefix_range(import, checker.source_tokens())
} else {
Expand Down
Loading