@@ -808,45 +808,81 @@ def _inject_signature(
808808 lines : list [str ],
809809) -> None :
810810 for arg_name in signature .parameters :
811- annotation = type_hints .get (arg_name )
812-
813- default = signature .parameters [arg_name ].default
814-
815- if arg_name .endswith ("_" ):
816- arg_name = f"{ arg_name [:- 1 ]} \\ _" # noqa: PLW2901
817-
818- insert_index = None
819- for at , line in enumerate (lines ):
820- if _line_is_param_line_for_arg (line , arg_name ):
821- # Get the arg_name from the doc to match up for type in case it has a star prefix.
822- # Line is in the correct format so this is guaranteed to return tuple[str, str].
823- func = _get_sphinx_line_keyword_and_argument
824- _ , arg_name = func (line ) # type: ignore[assignment, misc] # noqa: PLW2901
825- insert_index = at
826- break
827-
828- if annotation is not None and insert_index is None and app .config .always_document_param_types :
829- lines .append (f":param { arg_name } :" )
830- insert_index = len (lines )
831-
832- if insert_index is not None :
833- if annotation is None :
834- type_annotation = f":type { arg_name } : "
835- else :
836- short_literals = app .config .python_display_short_literal_types
837- formatted_annotation = add_type_css_class (
838- format_annotation (annotation , app .config , short_literals = short_literals )
839- )
840- type_annotation = f":type { arg_name } : { formatted_annotation } "
811+ _inject_arg_signature (type_hints , signature , app , lines , arg_name )
812+
813+
814+ def _inject_arg_signature (
815+ type_hints : dict [str , Any ],
816+ signature : inspect .Signature ,
817+ app : Sphinx ,
818+ lines : list [str ],
819+ arg_name : str ,
820+ ) -> None :
821+ annotation = type_hints .get (arg_name )
822+
823+ default = signature .parameters [arg_name ].default
824+
825+ if arg_name .endswith ("_" ):
826+ arg_name = f"{ arg_name [:- 1 ]} \\ _"
827+
828+ insert_index = None
829+ for at , line in enumerate (lines ):
830+ if _line_is_param_line_for_arg (line , arg_name ):
831+ # Get the arg_name from the doc to match up for type in case it has a star prefix.
832+ # Line is in the correct format so this is guaranteed to return tuple[str, str].
833+ _ , arg_name = _get_sphinx_line_keyword_and_argument (line ) # type: ignore[assignment, misc]
834+ insert_index = at
835+ break
836+
837+ if annotation is not None and insert_index is None and app .config .always_document_param_types :
838+ lines .append (f":param { arg_name } :" )
839+ insert_index = len (lines )
841840
842- if app .config .typehints_defaults :
843- formatted_default = format_default (app , default , annotation is not None )
844- if formatted_default :
845- type_annotation = _append_default (app , lines , insert_index , type_annotation , formatted_default )
841+ if insert_index is not None :
842+ update_only = False
846843
844+ if annotation is None :
845+ type_annotation , insert_index , update_only = _find_preexisting_type_annotation (
846+ lines ,
847+ arg_name ,
848+ insert_index ,
849+ )
850+
851+ else :
852+ short_literals = app .config .python_display_short_literal_types
853+ formatted_annotation = add_type_css_class (
854+ format_annotation (annotation , app .config , short_literals = short_literals )
855+ )
856+ type_annotation = f":type { arg_name } : { formatted_annotation } "
857+
858+ if app .config .typehints_defaults :
859+ formatted_default = format_default (app , default , annotation is not None )
860+ if formatted_default :
861+ type_annotation = _append_default (app , lines , insert_index , type_annotation , formatted_default )
862+
863+ if update_only :
864+ lines [insert_index ] = type_annotation
865+ else :
847866 lines .insert (insert_index , type_annotation )
848867
849868
869+ def _find_preexisting_type_annotation (
870+ lines : list [str ],
871+ arg_name : str ,
872+ insert_index : int ,
873+ ) -> tuple [str , int , bool ]:
874+ """
875+ Find a type entry in the input docstring that matches the given arg name.
876+
877+ If not found, a placeholder type entry that can be inserted at the given index is returned.
878+ """
879+ type_annotation = f":type { arg_name } : "
880+ for at , line in enumerate (lines ):
881+ if line .startswith (type_annotation ):
882+ return line , at , True
883+ return type_annotation , insert_index , False
884+
885+
850886def _append_default (
851887 app : Sphinx , lines : list [str ], insert_index : int , type_annotation : str , formatted_default : str
852888) -> str :
@@ -864,7 +900,8 @@ def _append_default(
864900 lines [append_index ] += formatted_default
865901
866902 else : # add to last param doc line
867- type_annotation += formatted_default
903+ separator = "" if type_annotation .endswith (" " ) else ", "
904+ type_annotation = f"{ type_annotation } { separator } { formatted_default } "
868905
869906 return type_annotation
870907
0 commit comments