Skip to content

Commit e654572

Browse files
authored
[stubgen] Fix a few bugs with stubgen of c modules (#11096)
* [stubgenc] Fix bug when inferring signatures with no args * [stubgenc] Add self arg when it is omitted from docstring signature * [stubgenc] Use 'cls' as self arg for classmethods without signature info
1 parent 3ef8040 commit e654572

File tree

3 files changed

+55
-10
lines changed

3 files changed

+55
-10
lines changed

mypy/stubdoc.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,16 @@ def add_token(self, token: tokenize.TokenInfo) -> None:
148148

149149
if token.string == ')':
150150
self.state.pop()
151-
try:
152-
self.args.append(ArgSig(name=self.arg_name, type=self.arg_type,
153-
default=bool(self.arg_default)))
154-
except ValueError:
155-
# wrong type, use Any
156-
self.args.append(ArgSig(name=self.arg_name, type=None,
157-
default=bool(self.arg_default)))
151+
152+
# arg_name is empty when there are no args. e.g. func()
153+
if self.arg_name:
154+
try:
155+
self.args.append(ArgSig(name=self.arg_name, type=self.arg_type,
156+
default=bool(self.arg_default)))
157+
except ValueError:
158+
# wrong type, use Any
159+
self.args.append(ArgSig(name=self.arg_name, type=None,
160+
default=bool(self.arg_default)))
158161
self.arg_name = ""
159162
self.arg_type = None
160163
self.arg_default = None

mypy/stubgenc.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,17 @@ def generate_c_function_stub(module: ModuleType,
182182
del inferred[-1]
183183
if not inferred:
184184
if class_name and name not in sigs:
185-
inferred = [FunctionSig(name, args=infer_method_sig(name), ret_type=ret_type)]
185+
inferred = [FunctionSig(name, args=infer_method_sig(name, self_var),
186+
ret_type=ret_type)]
186187
else:
187188
inferred = [FunctionSig(name=name,
188189
args=infer_arg_sig_from_anon_docstring(
189190
sigs.get(name, '(*args, **kwargs)')),
190191
ret_type=ret_type)]
192+
elif class_name and self_var:
193+
args = inferred[0].args
194+
if not args or args[0].name != self_var:
195+
args.insert(0, ArgSig(name=self_var))
191196

192197
is_overloaded = len(inferred) > 1 if inferred else False
193198
if is_overloaded:
@@ -438,7 +443,7 @@ def is_skipped_attribute(attr: str) -> bool:
438443
)
439444

440445

441-
def infer_method_sig(name: str) -> List[ArgSig]:
446+
def infer_method_sig(name: str, self_var: Optional[str] = None) -> List[ArgSig]:
442447
args: Optional[List[ArgSig]] = None
443448
if name.startswith('__') and name.endswith('__'):
444449
name = name[2:-2]
@@ -487,4 +492,4 @@ def infer_method_sig(name: str) -> List[ArgSig]:
487492
if args is None:
488493
args = [ArgSig(name='*args'),
489494
ArgSig(name='**kwargs')]
490-
return [ArgSig(name='self')] + args
495+
return [ArgSig(name=self_var or 'self')] + args

mypy/test/teststubgen.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ def test_find_unique_signatures(self) -> None:
187187
def test_infer_sig_from_docstring(self) -> None:
188188
assert_equal(infer_sig_from_docstring('\nfunc(x) - y', 'func'),
189189
[FunctionSig(name='func', args=[ArgSig(name='x')], ret_type='Any')])
190+
assert_equal(infer_sig_from_docstring('\nfunc(x)', 'func'),
191+
[FunctionSig(name='func', args=[ArgSig(name='x')], ret_type='Any')])
190192

191193
assert_equal(infer_sig_from_docstring('\nfunc(x, Y_a=None)', 'func'),
192194
[FunctionSig(name='func',
@@ -218,6 +220,13 @@ def test_infer_sig_from_docstring(self) -> None:
218220
[FunctionSig(name='func', args=[ArgSig(name='x', type='int', default=True)],
219221
ret_type='Any')])
220222

223+
assert_equal(infer_sig_from_docstring('\nfunc(x=3)', 'func'),
224+
[FunctionSig(name='func', args=[ArgSig(name='x', type=None, default=True)],
225+
ret_type='Any')])
226+
227+
assert_equal(infer_sig_from_docstring('\nfunc() -> int', 'func'),
228+
[FunctionSig(name='func', args=[], ret_type='int')])
229+
221230
assert_equal(infer_sig_from_docstring('\nfunc(x: int=3) -> int', 'func'),
222231
[FunctionSig(name='func', args=[ArgSig(name='x', type='int', default=True)],
223232
ret_type='int')])
@@ -737,6 +746,34 @@ def test(self, arg0: str) -> None:
737746
assert_equal(output, ['def test(self, arg0: int) -> Any: ...'])
738747
assert_equal(imports, [])
739748

749+
def test_generate_c_type_with_docstring_no_self_arg(self) -> None:
750+
class TestClass:
751+
def test(self, arg0: str) -> None:
752+
"""
753+
test(arg0: int)
754+
"""
755+
pass
756+
output = [] # type: List[str]
757+
imports = [] # type: List[str]
758+
mod = ModuleType(TestClass.__module__, '')
759+
generate_c_function_stub(mod, 'test', TestClass.test, output, imports,
760+
self_var='self', class_name='TestClass')
761+
assert_equal(output, ['def test(self, arg0: int) -> Any: ...'])
762+
assert_equal(imports, [])
763+
764+
def test_generate_c_type_classmethod(self) -> None:
765+
class TestClass:
766+
@classmethod
767+
def test(cls, arg0: str) -> None:
768+
pass
769+
output = [] # type: List[str]
770+
imports = [] # type: List[str]
771+
mod = ModuleType(TestClass.__module__, '')
772+
generate_c_function_stub(mod, 'test', TestClass.test, output, imports,
773+
self_var='cls', class_name='TestClass')
774+
assert_equal(output, ['def test(cls, *args, **kwargs) -> Any: ...'])
775+
assert_equal(imports, [])
776+
740777
def test_generate_c_type_with_docstring_empty_default(self) -> None:
741778
class TestClass:
742779
def test(self, arg0: str = "") -> None:

0 commit comments

Comments
 (0)