11use itertools:: join;
22use ruff_diagnostics:: { Diagnostic , Violation } ;
33use ruff_macros:: { derive_message_formats, violation} ;
4+ use std:: borrow:: Cow ;
45
5- use ruff_python_semantic:: { Imported , ResolvedReference , Scope } ;
6+ use ruff_python_semantic:: { FromImport , Import , Imported , ResolvedReference , Scope } ;
67use ruff_text_size:: Ranged ;
78
89use crate :: checkers:: ast:: Checker ;
@@ -76,25 +77,27 @@ pub(crate) fn import_private_name(
7677 let Some ( import) = binding. as_any_import ( ) else {
7778 continue ;
7879 } ;
79- let Some ( import) = import. from_import ( ) else {
80- continue ;
80+
81+ let import_info = match import {
82+ import if import. is_import ( ) => ImportInfo :: from ( import. import ( ) . unwrap ( ) ) ,
83+ import if import. is_from_import ( ) => ImportInfo :: from ( import. from_import ( ) . unwrap ( ) ) ,
84+ _ => return ,
8185 } ;
8286
83- let module = import. module_name ( ) ;
84- let Some ( root_module) = module. first ( ) else {
87+ let Some ( root_module) = import_info. module_name . first ( ) else {
8588 continue ;
8689 } ;
8790
8891 // Relative imports are not a public API.
8992 // Ex) `from . import foo`
90- if module . starts_with ( & [ "." ] ) {
93+ if import_info . module_name . starts_with ( & [ "." ] ) {
9194 continue ;
9295 }
9396
9497 // We can also ignore dunder names.
9598 // Ex) `from __future__ import annotations`
9699 // Ex) `from foo import __version__`
97- if root_module. starts_with ( "__" ) || import . member_name ( ) . starts_with ( "__" ) {
100+ if root_module. starts_with ( "__" ) || import_info . member_name . starts_with ( "__" ) {
98101 continue ;
99102 }
100103
@@ -107,8 +110,11 @@ pub(crate) fn import_private_name(
107110 continue ;
108111 }
109112
110- let call_path = import. call_path ( ) ;
111- if call_path. iter ( ) . any ( |name| name. starts_with ( '_' ) ) {
113+ if import_info
114+ . call_path
115+ . iter ( )
116+ . any ( |name| name. starts_with ( '_' ) )
117+ {
112118 // Ignore private imports used for typing.
113119 if binding. context . is_runtime ( )
114120 && binding
@@ -119,9 +125,16 @@ pub(crate) fn import_private_name(
119125 continue ;
120126 }
121127
122- let private_name = call_path. iter ( ) . find ( |name| name. starts_with ( '_' ) ) . unwrap ( ) ;
128+ let private_name = import_info
129+ . call_path
130+ . iter ( )
131+ . find ( |name| name. starts_with ( '_' ) )
132+ . unwrap ( ) ;
123133 let external_module = Some ( join (
124- call_path. iter ( ) . take_while ( |name| name != & private_name) ,
134+ import_info
135+ . call_path
136+ . iter ( )
137+ . take_while ( |name| name != & private_name) ,
125138 "." ,
126139 ) )
127140 . filter ( |module| !module. is_empty ( ) ) ;
@@ -144,3 +157,35 @@ fn is_typing(reference: &ResolvedReference) -> bool {
144157 || reference. in_simple_string_type_definition ( )
145158 || reference. in_runtime_evaluated_annotation ( )
146159}
160+
161+ struct ImportInfo < ' a > {
162+ module_name : & ' a [ & ' a str ] ,
163+ member_name : Cow < ' a , str > ,
164+ call_path : & ' a [ & ' a str ] ,
165+ }
166+
167+ impl < ' a > From < & ' a FromImport < ' _ > > for ImportInfo < ' a > {
168+ fn from ( import : & ' a FromImport ) -> Self {
169+ let module_name = import. module_name ( ) ;
170+ let member_name = import. member_name ( ) ;
171+ let call_path = import. call_path ( ) ;
172+ Self {
173+ module_name,
174+ member_name,
175+ call_path,
176+ }
177+ }
178+ }
179+
180+ impl < ' a > From < & ' a Import < ' _ > > for ImportInfo < ' a > {
181+ fn from ( import : & ' a Import ) -> Self {
182+ let module_name = import. module_name ( ) ;
183+ let member_name = import. member_name ( ) ;
184+ let call_path = import. call_path ( ) ;
185+ Self {
186+ module_name,
187+ member_name,
188+ call_path,
189+ }
190+ }
191+ }
0 commit comments