mirror of https://github.com/astral-sh/ruff
[ty] Make auto-import ignore symbols in modules starting with a `_`
This applies recursively. So if *any* component of a module name starts with a `_`, then symbols from that module are excluded from auto-import. The exception is when it's a module within first party code. Then we want to include it in auto-import.
This commit is contained in:
parent
2a38395bc8
commit
32f400a457
|
|
@ -36,6 +36,20 @@ pub fn all_symbols<'db>(
|
|||
let Some(file) = module.file(&*db) else {
|
||||
continue;
|
||||
};
|
||||
// By convention, modules starting with an underscore
|
||||
// are generally considered unexported. However, we
|
||||
// should consider first party modules fair game.
|
||||
//
|
||||
// Note that we apply this recursively. e.g.,
|
||||
// `numpy._core.multiarray` is considered private
|
||||
// because it's a child of `_core`.
|
||||
if module.name(&*db).components().any(|c| c.starts_with('_'))
|
||||
&& module
|
||||
.search_path(&*db)
|
||||
.is_none_or(|sp| !sp.is_first_party())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// TODO: also make it available in `TYPE_CHECKING` blocks
|
||||
// (we'd need https://github.com/astral-sh/ty/issues/1553 to do this well)
|
||||
if !is_typing_extensions_available && module.name(&*db) == &typing_extensions {
|
||||
|
|
|
|||
|
|
@ -5979,6 +5979,94 @@ ZQ<CURSOR>
|
|||
");
|
||||
}
|
||||
|
||||
// This test confirms current behavior (as of 2025-12-04), but
|
||||
// it's not consistent with auto-import. That is, it doesn't
|
||||
// strictly respect `__all__` on `bar`, but perhaps it should.
|
||||
//
|
||||
// See: https://github.com/astral-sh/ty/issues/1757
|
||||
#[test]
|
||||
fn object_attr_ignores_all() {
|
||||
let snapshot = CursorTest::builder()
|
||||
.source(
|
||||
"main.py",
|
||||
r#"
|
||||
import bar
|
||||
bar.ZQ<CURSOR>
|
||||
"#,
|
||||
)
|
||||
.source(
|
||||
"bar.py",
|
||||
r#"
|
||||
ZQZQ1 = 1
|
||||
ZQZQ2 = 1
|
||||
__all__ = ['ZQZQ1']
|
||||
"#,
|
||||
)
|
||||
.completion_test_builder()
|
||||
.auto_import()
|
||||
.module_names()
|
||||
.build()
|
||||
.snapshot();
|
||||
// We specifically do not want `ZQZQ2` here, since
|
||||
// it is not part of `__all__`.
|
||||
assert_snapshot!(snapshot, @r"
|
||||
ZQZQ1 :: <no import required>
|
||||
ZQZQ2 :: <no import required>
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_import_ignores_modules_with_leading_underscore() {
|
||||
let snapshot = CursorTest::builder()
|
||||
.source(
|
||||
"main.py",
|
||||
r#"
|
||||
Quitter<CURSOR>
|
||||
"#,
|
||||
)
|
||||
.completion_test_builder()
|
||||
.auto_import()
|
||||
.module_names()
|
||||
.build()
|
||||
.snapshot();
|
||||
// There is a `Quitter` in `_sitebuiltins` in the standard
|
||||
// library. But this is skipped by auto-import because it's
|
||||
// 1) not first party and 2) starts with an `_`.
|
||||
assert_snapshot!(snapshot, @"<No completions found>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_import_includes_modules_with_leading_underscore_in_first_party() {
|
||||
let snapshot = CursorTest::builder()
|
||||
.source(
|
||||
"main.py",
|
||||
r#"
|
||||
ZQ<CURSOR>
|
||||
"#,
|
||||
)
|
||||
.source(
|
||||
"bar.py",
|
||||
r#"
|
||||
ZQZQ1 = 1
|
||||
"#,
|
||||
)
|
||||
.source(
|
||||
"_foo.py",
|
||||
r#"
|
||||
ZQZQ1 = 1
|
||||
"#,
|
||||
)
|
||||
.completion_test_builder()
|
||||
.auto_import()
|
||||
.module_names()
|
||||
.build()
|
||||
.snapshot();
|
||||
assert_snapshot!(snapshot, @r"
|
||||
ZQZQ1 :: _foo
|
||||
ZQZQ1 :: bar
|
||||
");
|
||||
}
|
||||
|
||||
/// A way to create a simple single-file (named `main.py`) completion test
|
||||
/// builder.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -594,7 +594,7 @@ impl SearchPath {
|
|||
)
|
||||
}
|
||||
|
||||
pub(crate) fn is_first_party(&self) -> bool {
|
||||
pub fn is_first_party(&self) -> bool {
|
||||
matches!(&*self.0, SearchPathInner::FirstParty(_))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ expression: completions
|
|||
{
|
||||
"label": "Literal (import typing)",
|
||||
"kind": 6,
|
||||
"sortText": " 58",
|
||||
"sortText": " 35",
|
||||
"insertText": "Literal",
|
||||
"additionalTextEdits": [
|
||||
{
|
||||
|
|
@ -27,7 +27,7 @@ expression: completions
|
|||
{
|
||||
"label": "LiteralString (import typing)",
|
||||
"kind": 6,
|
||||
"sortText": " 59",
|
||||
"sortText": " 36",
|
||||
"insertText": "LiteralString",
|
||||
"additionalTextEdits": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ expression: completions
|
|||
{
|
||||
"label": "Literal (import typing)",
|
||||
"kind": 6,
|
||||
"sortText": " 58",
|
||||
"sortText": " 35",
|
||||
"insertText": "Literal",
|
||||
"additionalTextEdits": [
|
||||
{
|
||||
|
|
@ -27,7 +27,7 @@ expression: completions
|
|||
{
|
||||
"label": "LiteralString (import typing)",
|
||||
"kind": 6,
|
||||
"sortText": " 59",
|
||||
"sortText": " 36",
|
||||
"insertText": "LiteralString",
|
||||
"additionalTextEdits": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ expression: completions
|
|||
{
|
||||
"label": "Literal (import typing)",
|
||||
"kind": 6,
|
||||
"sortText": " 58",
|
||||
"sortText": " 35",
|
||||
"insertText": "Literal",
|
||||
"additionalTextEdits": [
|
||||
{
|
||||
|
|
@ -27,7 +27,7 @@ expression: completions
|
|||
{
|
||||
"label": "LiteralString (import typing)",
|
||||
"kind": 6,
|
||||
"sortText": " 59",
|
||||
"sortText": " 36",
|
||||
"insertText": "LiteralString",
|
||||
"additionalTextEdits": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ expression: completions
|
|||
{
|
||||
"label": "Literal (import typing)",
|
||||
"kind": 6,
|
||||
"sortText": " 58",
|
||||
"sortText": " 35",
|
||||
"insertText": "Literal",
|
||||
"additionalTextEdits": [
|
||||
{
|
||||
|
|
@ -27,7 +27,7 @@ expression: completions
|
|||
{
|
||||
"label": "LiteralString (import typing)",
|
||||
"kind": 6,
|
||||
"sortText": " 59",
|
||||
"sortText": " 36",
|
||||
"insertText": "LiteralString",
|
||||
"additionalTextEdits": [
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue