|
8 | 8 | import inspect
|
9 | 9 | import os.path
|
10 | 10 | import re
|
11 |
| -from typing import List, Dict, Tuple, Optional, Mapping, Any, Set |
12 | 11 | from types import ModuleType
|
| 12 | +from typing import List, Dict, Tuple, Optional, Mapping, Any, Set, ClassVar, Union |
13 | 13 |
|
14 | 14 | from mypy.moduleinspect import is_c_module
|
15 | 15 | from mypy.stubdoc import (
|
@@ -92,7 +92,7 @@ def add_typing_import(output: List[str]) -> List[str]:
|
92 | 92 |
|
93 | 93 |
|
94 | 94 | def is_c_function(obj: object) -> bool:
|
95 |
| - return inspect.isbuiltin(obj) or type(obj) is type(ord) |
| 95 | + return inspect.isbuiltin(obj) or type(obj) is type(ord) or type(obj).__name__ == 'cython_function_or_method' |
96 | 96 |
|
97 | 97 |
|
98 | 98 | def is_c_method(obj: object) -> bool:
|
@@ -139,24 +139,12 @@ def generate_c_function_stub(module: ModuleType,
|
139 | 139 | if class_sigs is None:
|
140 | 140 | class_sigs = {}
|
141 | 141 |
|
142 |
| - ret_type = 'None' if name == '__init__' and class_name else 'Any' |
143 |
| - |
144 |
| - if (name in ('__new__', '__init__') and name not in sigs and class_name and |
145 |
| - class_name in class_sigs): |
146 |
| - inferred = [FunctionSig(name=name, |
147 |
| - args=infer_arg_sig_from_docstring(class_sigs[class_name]), |
148 |
| - ret_type=ret_type)] # type: Optional[List[FunctionSig]] |
149 |
| - else: |
150 |
| - docstr = getattr(obj, '__doc__', None) |
151 |
| - inferred = infer_sig_from_docstring(docstr, name) |
152 |
| - if not inferred: |
153 |
| - if class_name and name not in sigs: |
154 |
| - inferred = [FunctionSig(name, args=infer_method_sig(name), ret_type=ret_type)] |
155 |
| - else: |
156 |
| - inferred = [FunctionSig(name=name, |
157 |
| - args=infer_arg_sig_from_docstring( |
158 |
| - sigs.get(name, '(*args, **kwargs)')), |
159 |
| - ret_type=ret_type)] |
| 142 | + inferred = _infer_signature_for_c_function_stub( |
| 143 | + class_name=class_name, |
| 144 | + class_sigs=class_sigs, |
| 145 | + name=name, |
| 146 | + obj=obj, |
| 147 | + sigs=sigs) |
160 | 148 |
|
161 | 149 | is_overloaded = len(inferred) > 1 if inferred else False
|
162 | 150 | if is_overloaded:
|
@@ -189,6 +177,82 @@ def generate_c_function_stub(module: ModuleType,
|
189 | 177 | ))
|
190 | 178 |
|
191 | 179 |
|
| 180 | +def _infer_signature_for_c_function_stub( |
| 181 | + class_name: Optional[str], |
| 182 | + class_sigs: Optional[Dict[str, str]], |
| 183 | + name: str, |
| 184 | + obj: object, |
| 185 | + sigs: Optional[Dict[str, str]]) -> List[FunctionSig]: |
| 186 | + default_ret_type = 'None' if name == '__init__' and class_name else 'Any' |
| 187 | + |
| 188 | + if type(obj).__name__ == 'cython_function_or_method': |
| 189 | + # Special-case Cython functions: if binding=True when compiling a Cython binary, it generates Python annotations |
| 190 | + # sufficient to use inspect#signature. |
| 191 | + sig = _infer_signature_via_inspect(obj=obj, default_ret_type=default_ret_type) |
| 192 | + if sig is not None: |
| 193 | + return [sig] |
| 194 | + # Fall through to parse via doc if inspect.signature() didn't work |
| 195 | + |
| 196 | + if (name in ('__new__', '__init__') and name not in sigs and class_name and |
| 197 | + class_name in class_sigs): |
| 198 | + return [FunctionSig(name=name, |
| 199 | + args=infer_arg_sig_from_docstring(class_sigs[class_name]), |
| 200 | + ret_type=default_ret_type)] # type: Optional[List[FunctionSig]] |
| 201 | + |
| 202 | + docstr = getattr(obj, '__doc__', None) |
| 203 | + inferred = infer_sig_from_docstring(docstr, name) |
| 204 | + if inferred: |
| 205 | + return inferred |
| 206 | + |
| 207 | + if class_name and name not in sigs: |
| 208 | + return [FunctionSig(name, args=infer_method_sig(name), ret_type=default_ret_type)] |
| 209 | + else: |
| 210 | + return [FunctionSig(name=name, |
| 211 | + args=infer_arg_sig_from_docstring( |
| 212 | + sigs.get(name, '(*args, **kwargs)')), |
| 213 | + ret_type=default_ret_type)] |
| 214 | + |
| 215 | + |
| 216 | +def _infer_signature_via_inspect(obj: object, default_ret_type: str) -> Optional[FunctionSig]: |
| 217 | + """ |
| 218 | + Parses a FunctionSig via annotations found in inspect#signature(). Returns None if inspect.signature() failed to |
| 219 | + generate a signature. |
| 220 | + """ |
| 221 | + |
| 222 | + try: |
| 223 | +<
9E88
/span> signature = inspect.signature(obj) |
| 224 | + except (ValueError, TypeError): |
| 225 | + # inspect.signature() failed to generate a signature; this can happen for some methods depending on the |
| 226 | + # implementation of Python, or if a cython function was not compiled with binding=True. |
| 227 | + return None |
| 228 | + args = [] |
| 229 | + |
| 230 | + def annotation_to_name(annotation) -> Optional[str]: |
| 231 | + if annotation == inspect.Signature.empty: |
| 232 | + return None |
| 233 | + if isinstance(annotation, str): |
| 234 | + return annotation |
| 235 | + if inspect.isclass(annotation): |
| 236 | + return annotation.__name__ |
| 237 | + if hasattr(annotation, '__str__'): |
| 238 | + return annotation.__str__() |
| 239 | + # Can't do anything here, so ignore |
| 240 | + return None |
| 241 | + |
| 242 | + for arg_param in signature.parameters.values(): |
| 243 | + args.append(ArgSig( |
| 244 | + name=arg_param.name, |
| 245 | + type=annotation_to_name(arg_param.annotation), |
| 246 | + default=arg_param.default != inspect.Parameter.empty, |
| 247 | + )) |
| 248 | + ret_type = annotation_to_name(signature.return_annotation) or default_ret_type |
| 249 | + return FunctionSig( |
| 250 | + name=obj.__name__, |
| 251 | + args=args, |
| 252 | + ret_type=ret_type, |
| 253 | + ) |
| 254 | + |
| 255 | + |
192 | 256 | def strip_or_import(typ: str, module: ModuleType, imports: List[str]) -> str:
|
193 | 257 | """Strips unnecessary module names from typ.
|
194 | 258 |
|
|
0 commit comments