2121from mypy .stubdoc import ArgSig , FunctionSig
2222from mypy .types import (
2323 AnyType ,
24+ CallableType ,
25+ DeletedType ,
26+ ErasedType ,
27+ Instance ,
2428 NoneType ,
2529 Type ,
2630 TypeList ,
2731 TypeStrVisitor ,
32+ TypeVarType ,
2833 UnboundType ,
34+ UninhabitedType ,
2935 UnionType ,
3036 UnpackType ,
3137)
@@ -251,6 +257,23 @@ def __init__(
251257 self .known_modules = known_modules
252258 self .local_modules = local_modules or ["builtins" ]
253259
260+ def track_imports (self , s : str ) -> str | None :
261+ if self .known_modules is not None and "." in s :
262+ # see if this object is from any of the modules that we're currently processing.
263+ # reverse sort so that subpackages come before parents: e.g. "foo.bar" before "foo".
264+ for module_name in self .local_modules + sorted (self .known_modules , reverse = True ):
265+ if s .startswith (module_name + "." ):
266+ if module_name in self .local_modules :
267+ s = s [len (module_name ) + 1 :]
268+ arg_module = module_name
269+ break
270+ else :
271+ arg_module = s [: s .rindex ("." )]
272+ if arg_module not in self .local_modules :
273+ self .stubgen .import_tracker .add_import (arg_module , require = True )
274+ return s
275+ return None
276+
254277 def visit_any (self , t : AnyType ) -> str :
255278 s = super ().visit_any (t )
256279 self .stubgen .import_tracker .require_name (s )
@@ -267,19 +290,9 @@ def visit_unbound_type(self, t: UnboundType) -> str:
267290 return self .stubgen .add_name ("_typeshed.Incomplete" )
268291 if fullname in TYPING_BUILTIN_REPLACEMENTS :
269292 s = self .stubgen .add_name (TYPING_BUILTIN_REPLACEMENTS [fullname ], require = True )
270- if self .known_modules is not None and "." in s :
271- # see if this object is from any of the modules that we're currently processing.
272- # reverse sort so that subpackages come before parents: e.g. "foo.bar" before "foo".
273- for module_name in self .local_modules + sorted (self .known_modules , reverse = True ):
274- if s .startswith (module_name + "." ):
275- if module_name in self .local_modules :
276- s = s [len (module_name ) + 1 :]
277- arg_module = module_name
278- break
279- else :
280- arg_module = s [: s .rindex ("." )]
281- if arg_module not in self .local_modules :
282- self .stubgen .import_tracker .add_import (arg_module , require = True )
293+
294+ if new_s := self .track_imports (s ):
295+ s = new_s
283296 elif s == "NoneType" :
284297 # when called without analysis all types are unbound, so this won't hit
285298 # visit_none_type().
@@ -322,6 +335,55 @@ def args_str(self, args: Iterable[Type]) -> str:
322335 res .append (arg_str )
323336 return ", " .join (res )
324337
338+ def visit_type_var (self , t : TypeVarType ) -> str :
339+ return t .name
340+
341+ def visit_uninhabited_type (self , t : UninhabitedType ) -> str :
342+ return self .stubgen .add_name ("typing.Any" )
343+
344+ def visit_erased_type (self , t : ErasedType ) -> str :
345+ return self .stubgen .add_name ("typing.Any" )
346+
347+ def visit_deleted_type (self , t : DeletedType ) -> str :
348+ return self .stubgen .add_name ("typing.Any" )
349+
350+ def visit_instance (self , t : Instance ) -> str :
351+ if t .last_known_value and not t .args :
352+ # Instances with a literal fallback should never be generic. If they are,
353+ # something went wrong so we fall back to showing the full Instance repr.
354+ s = f"{ t .last_known_value .accept (self )} "
355+ else :
356+ s = t .type .fullname or t .type .name or self .stubgen .add_name ("_typeshed.Incomplete" )
357+
358+ s = self .track_imports (s ) or s
359+
360+ if t .args :
361+ if t .type .fullname == "builtins.tuple" :
362+ assert len (t .args ) == 1
363+ s += f"[{ self .list_str (t .args )} , ...]"
364+ else :
365+ s += f"[{ self .list_str (t .args )} ]"
366+ elif t .type .has_type_var_tuple_type and len (t .type .type_vars ) == 1 :
367+ s += "[()]"
368+
369+ return s
370+
371+ def visit_callable_type (self , t : CallableType ) -> str :
372+ from mypy .suggestions import is_tricky_callable
373+
374+ if is_tricky_callable (t ):
375+ arg_str = "..."
376+ else :
377+ # Note: for default arguments, we just assume that they
378+ # are required. This isn't right, but neither is the
379+ # other thing, and I suspect this will produce more better
380+ # results than falling back to `...`
381+ args = [typ .accept (self ) for typ in t .arg_types ]
382+ arg_str = f"[{ ', ' .join (args )} ]"
383+
384+ callable = self .stubgen .add_name ("typing.Callable" )
385+ return f"{ callable } [{ arg_str } , { t .ret_type .accept (self )} ]"
386+
325387
326388class ClassInfo :
327389 def __init__ (
@@ -454,11 +516,11 @@ class ImportTracker:
454516
455517 def __init__ (self ) -> None :
456518 # module_for['foo'] has the module name where 'foo' was imported from, or None if
457- # 'foo' is a module imported directly;
519+ # 'foo' is a module imported directly;
458520 # direct_imports['foo'] is the module path used when the name 'foo' was added to the
459- # namespace.
521+ # namespace.
460522 # reverse_alias['foo'] is the name that 'foo' had originally when imported with an
461- # alias; examples
523+ # alias; examples
462524 # 'from pkg import mod' ==> module_for['mod'] == 'pkg'
463525 # 'from pkg import mod as m' ==> module_for['m'] == 'pkg'
464526 # ==> reverse_alias['m'] == 'mod'
@@ -618,7 +680,9 @@ def __init__(
618680 include_private : bool = False ,
619681 export_less : bool = False ,
620682 include_docstrings : bool = False ,
683+ known_modules : list [str ] | None = None ,
621684 ) -> None :
685+ self .known_modules = known_modules or []
622686 # Best known value of __all__.
623687 self ._all_ = _all_
624688 self ._include_private = include_private
@@ -839,7 +903,9 @@ def print_annotation(
839903 known_modules : list [str ] | None = None ,
840904 local_modules : list [str ] | None = None ,
841905 ) -> str :
842- printer = AnnotationPrinter (self , known_modules , local_modules )
906+ printer = AnnotationPrinter (
907+ self , known_modules , local_modules or ["builtins" , self .module_name ]
908+ )
843909 return t .accept (printer )
844910
845911 def is_not_in_all (self , name : str ) -> bool :
0 commit comments