@@ -118,3 +118,59 @@ def test_signature_without_docstring(
118118 sigs = sig_info ["signatures" ]
119119 assert len (sigs ) == 1
120120 assert sigs [0 ]["documentation" ] == {"kind" : "markdown" , "value" : "" }
121+
122+
123+ CALLABLE_CLASS_DOC = """class MyCallable:
124+ \" \" \" Docstring for MyCallable\" \" \"
125+ def __call__(self, a, b=1):
126+ \" \" \" Call docstring\" \" \"
127+ pass
128+
129+ c = MyCallable()
130+ c(
131+ """
132+
133+
134+ def test_callable_instance_signature (workspace ) -> None :
135+ # Over '( ' in c(
136+ sig_position = {"line" : 8 , "character" : 2 }
137+ doc = Document (DOC_URI , workspace , CALLABLE_CLASS_DOC )
138+
139+ sig_info = signature .pylsp_signature_help (doc ._config , doc , sig_position )
140+
141+ sigs = sig_info ["signatures" ]
142+ assert len (sigs ) == 1
143+ # Parameter labels must be substrings of the signature label per LSP 3.17
144+ assert sigs [0 ]["label" ] == "MyCallable(a, b=1)"
145+ param_labels = [p ["label" ] for p in sigs [0 ]["parameters" ]]
146+ assert param_labels == ["a" , "b" ]
147+ assert all (label in sigs [0 ]["label" ] for label in param_labels )
148+
149+
150+ def test_signature_fallback_when_label_missing_params (workspace , monkeypatch ) -> None :
151+ # Test fallback when s.to_string() does not contain all parameter names
152+ sig_position = {"line" : 10 , "character" : 5 }
153+ doc = Document (DOC_URI , workspace , DOC )
154+
155+ orig_jedi_script = doc .jedi_script
156+
157+ class MockScript :
158+ def get_signatures (self , ** kwargs ):
159+ signatures = orig_jedi_script ().get_signatures (** kwargs )
160+ if not signatures :
161+ return signatures
162+ s = signatures [0 ]
163+ # Mock to_string to return a label missing the parameters (like ufunc())
164+ monkeypatch .setattr (s , "to_string" , lambda : "mock_func()" )
165+ return [s ]
166+
167+ monkeypatch .setattr (doc , "jedi_script" , lambda : MockScript ())
168+
169+ sig_info = signature .pylsp_signature_help (doc ._config , doc , sig_position )
170+ sigs = sig_info ["signatures" ]
171+ assert len (sigs ) == 1
172+ # Fallback should ensure parameter labels are substrings of signature label
173+ assert sigs [0 ]["label" ] == "main(param1, param2)"
174+ param_labels = [p ["label" ] for p in sigs [0 ]["parameters" ]]
175+ assert param_labels == ["param1" , "param2" ]
176+ assert all (label in sigs [0 ]["label" ] for label in param_labels )
0 commit comments