1818from mypy .moduleinspect import is_c_module
1919from mypy .stubdoc import (
2020 ArgSig ,
21+ ClassSig ,
2122 FunctionSig ,
23+ PropertySig ,
2224 Sig ,
2325 find_unique_signatures ,
2426 infer_arg_sig_from_anon_docstring ,
@@ -649,16 +651,23 @@ def generate_function_stub(
649651 output .extend (self .format_func_def (inferred , decorators = decorators , docstring = docstring ))
650652 self ._fix_iter (ctx , inferred , output )
651653
652- def _indent_docstring (self , docstring : str ) -> str :
654+ def _indent_docstring (
655+ self , docstring : str , extra_indent : bool = True , trailing_newline : bool = False
656+ ) -> str :
653657 """Fix indentation of docstring extracted from pybind11 or other binding generators."""
654658 lines = docstring .splitlines (keepends = True )
655- indent = self ._indent + " "
659+ indent = self ._indent + ( " " if extra_indent else "" )
656660 if len (lines ) > 1 :
657661 if not all (line .startswith (indent ) or not line .strip () for line in lines ):
658662 # if the docstring is not indented, then indent all but the first line
659663 for i , line in enumerate (lines [1 :]):
660664 if line .strip ():
661- lines [i + 1 ] = indent + line
665+ # ignore any left space to keep the standard ident
666+ lines [i + 1 ] = indent + line .lstrip ()
667+
668+ if trailing_newline and not lines [- 1 ].endswith ("\n " ):
669+ lines [- 1 ] += "\n "
670+
662671 # if there's a trailing newline, add a final line to visually indent the quoted docstring
663672 if lines [- 1 ].endswith ("\n " ):
664673 if len (lines ) > 1 :
@@ -728,6 +737,13 @@ def generate_property_stub(
728737 self .record_name (ctx .name )
729738 static = self .is_static_property (raw_obj )
730739 readonly = self .is_property_readonly (raw_obj )
740+
741+ if docstring :
742+ # fields must define its docstring using the same ident
743+ # readonly properties generates a function,
744+ # which requires an extra ident in the first line
745+ docstring = self ._indent_docstring (docstring , extra_indent = readonly )
746+
731747 if static :
732748 ret_type : str | None = self .strip_or_import (self .get_type_annotation (obj ))
733749 else :
@@ -738,25 +754,35 @@ def generate_property_stub(
738754 if inferred_type is not None :
739755 inferred_type = self .strip_or_import (inferred_type )
740756
757+ if not self ._include_docstrings :
758+ docstring = None
759+
741760 if static :
742761 classvar = self .add_name ("typing.ClassVar" )
743- trailing_comment = " # read-only" if readonly else ""
744762 if inferred_type is None :
745763 inferred_type = self .add_name ("_typeshed.Incomplete" )
746764
765+ prop_sig = PropertySig (name , inferred_type )
747766 static_properties .append (
748- f"{ self ._indent } { name } : { classvar } [{ inferred_type } ] = ...{ trailing_comment } "
767+ prop_sig .format_sig (
768+ indent = self ._indent ,
769+ is_readonly = readonly ,
770+ is_static = True ,
771+ name_ref = classvar ,
772+ docstring = docstring ,
773+ )
749774 )
750775 else : # regular property
751776 if readonly :
752777 ro_properties .append (f"{ self ._indent } @property" )
753- sig = FunctionSig (name , [ArgSig ("self" )], inferred_type )
754- ro_properties .append (sig .format_sig (indent = self ._indent ))
778+ func_sig = FunctionSig (name , [ArgSig ("self" )], inferred_type )
779+ ro_properties .append (func_sig .format_sig (indent = self ._indent , docstring = docstring ))
755780 else :
756781 if inferred_type is None :
757782 inferred_type = self .add_name ("_typeshed.Incomplete" )
758783
759- rw_properties .append (f"{ self ._indent } { name } : { inferred_type } " )
784+ prop_sig = PropertySig (name , inferred_type )
785+ rw_properties .append (prop_sig .format_sig (indent = self ._indent , docstring = docstring ))
760786
761787 def get_type_fullname (self , typ : type ) -> str :
762788 """Given a type, return a string representation"""
@@ -859,34 +885,27 @@ def generate_class_stub(
859885 classvar = self .add_name ("typing.ClassVar" )
860886 static_properties .append (f"{ self ._indent } { attr } : { classvar } [{ prop_type_name } ] = ..." )
861887
888+ docstring = class_info .docstring if self ._include_docstrings else None
889+ if docstring :
890+ docstring = self ._indent_docstring (
891+ docstring , extra_indent = False , trailing_newline = True
892+ )
893+
862894 self .dedent ()
863895
864896 bases = self .get_base_types (cls )
865- if bases :
866- bases_str = "(%s)" % ", " .join (bases )
867- else :
868- bases_str = ""
869- if types or static_properties or rw_properties or methods or ro_properties :
870- output .append (f"{ self ._indent } class { class_name } { bases_str } :" )
871- for line in types :
872- if (
873- output
874- and output [- 1 ]
875- and not output [- 1 ].strip ().startswith ("class" )
876- and line .strip ().startswith ("class" )
877- ):
878- output .append ("" )
879- output .append (line )
880- for line in static_properties :
881- output .append (line )
882- for line in rw_properties :
883- output .append (line )
884- for line in methods :
885- output .append (line )
886- for line in ro_properties :
887- output .append (line )
888- else :
889- output .append (f"{ self ._indent } class { class_name } { bases_str } : ..." )
897+ sig = ClassSig (class_name , bases )
898+ output .extend (
899+ sig .format_sig (
900+ indent = self ._indent ,
901+ types = types ,
902+ methods = methods ,
903+ static_properties = static_properties ,
904+ rw_properties = rw_properties ,
905+ ro_properties = ro_properties ,
906+ docstring = docstring ,
907+ )
908+ )
890909
891910 def generate_variable_stub (self , name : str , obj : object , output : list [str ]) -> None :
892911 """Generate stub for a single variable using runtime introspection.
0 commit comments