@@ -71,7 +71,7 @@ def __init__(self, namespace: Mapping[str, Any] | None = None) -> None:
7171 self ._curr_sys_path : list [str ] = sys .path [:]
7272 self ._stdlib_path = os .path .dirname (importlib .__path__ [0 ])
7373
74- def get_completions (self , line : str ) -> tuple [list [str ], CompletionAction | None ] | None :
74+ def get_completions (self , line : str ) -> tuple [list [str ], list [ Any ], CompletionAction | None ] | None :
7575 """Return the next possible import completions for 'line'.
7676
7777 For attributes completion, if the module to complete from is not
@@ -86,26 +86,36 @@ def get_completions(self, line: str) -> tuple[list[str], CompletionAction | None
8686 except Exception :
8787 # Some unexpected error occurred, make it look like
8888 # no completions are available
89- return [], None
89+ return [], [], None
9090
91- def complete (self , from_name : str | None , name : str | None ) -> tuple [list [str ], CompletionAction | None ]:
91+ def complete (self , from_name : str | None , name : str | None ) -> tuple [list [str ], list [ Any ], CompletionAction | None ]:
9292 if from_name is None :
9393 # import x.y.z<tab>
9494 assert name is not None
9595 path , prefix = self .get_path_and_prefix (name )
9696 modules = self .find_modules (path , prefix )
97- return [self .format_completion (path , module ) for module in modules ], None
97+ names = [self .format_completion (path , module ) for module in modules ]
98+ return names , [sys ] * len (names ), None
9899
99100 if name is None :
100101 # from x.y.z<tab>
101102 path , prefix = self .get_path_and_prefix (from_name )
102103 modules = self .find_modules (path , prefix )
103- return [self .format_completion (path , module ) for module in modules ], None
104+ names = [self .format_completion (path , module ) for module in modules ]
105+ return names , [sys ] * len (names ), None
104106
105107 # from x.y import z<tab>
106108 submodules = self .find_modules (from_name , name )
107- attributes , action = self .find_attributes (from_name , name )
108- return sorted ({* submodules , * attributes }), action
109+ attr_names , attr_values , action = self .find_attributes (from_name , name )
110+ all_names = sorted ({* submodules , * attr_names })
111+ # Build values list matching the sorted order:
112+ # submodules use sys as a sentinel so they get the 'module' color,
113+ # attributes use their actual value from find_attributes.
114+ submodule_set = set (submodules )
115+ attr_map = dict (zip (attr_names , attr_values ))
116+ all_values = [attr_map .get (n ) if n not in submodule_set else sys
117+ for n in all_names ]
118+ return all_names , all_values , action
109119
110120 def find_modules (self , path : str , prefix : str ) -> list [str ]:
111121 """Find all modules under 'path' that start with 'prefix'."""
@@ -166,31 +176,43 @@ def _is_stdlib_module(self, module_info: pkgutil.ModuleInfo) -> bool:
166176 return (isinstance (module_info .module_finder , FileFinder )
167177 and module_info .module_finder .path == self ._stdlib_path )
168178
169- def find_attributes (self , path : str , prefix : str ) -> tuple [list [str ], CompletionAction | None ]:
179+ def find_attributes (self , path : str , prefix : str ) -> tuple [list [str ], list [ Any ], CompletionAction | None ]:
170180 """Find all attributes of module 'path' that start with 'prefix'."""
171- attributes , action = self ._find_attributes (path , prefix )
181+ attributes , values , action = self ._find_attributes (path , prefix )
172182 # Filter out invalid attribute names
173183 # (for example those containing dashes that cannot be imported with 'import')
174- return [attr for attr in attributes if attr .isidentifier ()], action
175-
176- def _find_attributes (self , path : str , prefix : str ) -> tuple [list [str ], CompletionAction | None ]:
184+ filtered = [(attr , val ) for attr , val in zip (attributes , values )
185+ if attr .isidentifier ()]
186+ if filtered :
187+ attrs , vals = zip (* filtered )
188+ return list (attrs ), list (vals ), action
189+ return [], [], action
190+
191+ def _find_attributes (self , path : str , prefix : str ) -> tuple [list [str ], list [Any ], CompletionAction | None ]:
177192 path = self ._resolve_relative_path (path ) # type: ignore[assignment]
178193 if path is None :
179- return [], None
194+ return [], [], None
180195
181196 imported_module = sys .modules .get (path )
182197 if not imported_module :
183198 if path in self ._failed_imports : # Do not propose to import again
184- return [], None
199+ return [], [], None
185200 imported_module = self ._maybe_import_module (path )
186201 if not imported_module :
187- return [], self ._get_import_completion_action (path )
202+ return [], [], self ._get_import_completion_action (path )
188203 try :
189204 module_attributes = dir (imported_module )
190205 except Exception :
191206 module_attributes = []
192- return [attr_name for attr_name in module_attributes
193- if self .is_suggestion_match (attr_name , prefix )], None
207+ from .fancycompleter import safe_getattr
208+ names = []
209+ values = []
210+ for attr_name in module_attributes :
211+ if not self .is_suggestion_match (attr_name , prefix ):
212+ continue
213+ names .append (attr_name )
214+ values .append (safe_getattr (imported_module , attr_name ))
215+ return names , values , None
194216
195217 def is_suggestion_match (self , module_name : str , prefix : str ) -> bool :
196218 if prefix :
0 commit comments