4
4
"""
5
5
6
6
import importlib
7
+ import inspect
7
8
import os .path
8
9
import re
9
- from typing import List , Dict , Tuple , Optional , Mapping , Any
10
+ from typing import List , Dict , Tuple , Optional , Mapping , Any , Set
10
11
from types import ModuleType
11
12
12
- from mypy .stubutil import is_c_module , write_header , infer_sig_from_docstring
13
+ from mypy .stubutil import (
14
+ is_c_module , write_header , infer_sig_from_docstring ,
15
+ infer_prop_type_from_docstring
16
+ )
13
17
14
18
15
19
def generate_stub_for_c_module (module_name : str ,
@@ -41,7 +45,7 @@ def generate_stub_for_c_module(module_name: str,
41
45
for name , obj in items :
42
46
if name .startswith ('__' ) and name .endswith ('__' ):
43
47
continue
44
- if name not in done :
48
+ if name not in done and not inspect . ismodule ( obj ) :
45
49
type_str = type (obj ).__name__
46
50
if type_str not in ('int' , 'str' , 'bytes' , 'float' , 'bool' ):
47
51
type_str = 'Any'
@@ -67,7 +71,7 @@ def generate_stub_for_c_module(module_name: str,
67
71
68
72
def add_typing_import (output : List [str ]) -> List [str ]:
69
73
names = []
70
- for name in ['Any' ]:
74
+ for name in ['Any' , 'Union' , 'Tuple' , 'Optional' , 'List' , 'Dict' ]:
71
75
if any (re .search (r'\b%s\b' % name , line ) for line in output ):
72
76
names .append (name )
73
77
if names :
@@ -77,22 +81,30 @@ def add_typing_import(output: List[str]) -> List[str]:
77
81
78
82
79
83
def is_c_function (obj : object ) -> bool :
80
- return type (obj ) is type (ord )
84
+ return inspect . isbuiltin ( obj ) or type (obj ) is type (ord )
81
85
82
86
83
87
def is_c_method (obj : object ) -> bool :
84
- return type (obj ) in (type (str .index ),
85
- type (str .__add__ ),
86
- type (str .__new__ ))
88
+ return inspect . ismethoddescriptor ( obj ) or type (obj ) in (type (str .index ),
89
+ type (str .__add__ ),
90
+ type (str .__new__ ))
87
91
88
92
89
93
def is_c_classmethod (obj : object ) -> bool :
90
- type_str = type (obj ).__name__
91
- return type_str == 'classmethod_descriptor'
94
+ return inspect .isbuiltin (obj ) or type (obj ).__name__ in ('classmethod' ,
95
+ 'classmethod_descriptor' )
96
+
97
+
98
+ def is_c_property (obj : object ) -> bool :
99
+ return inspect .isdatadescriptor (obj ) and hasattr (obj , 'fget' )
100
+
101
+
102
+ def is_c_property_readonly (prop : object ) -> bool :
103
+ return getattr (prop , 'fset' ) is None
92
104
93
105
94
106
def is_c_type (obj : object ) -> bool :
95
- return type (obj ) is type (int )
107
+ return inspect . isclass ( obj ) or type (obj ) is type (int )
96
108
97
109
98
110
def generate_c_function_stub (module : ModuleType ,
@@ -104,6 +116,8 @@ def generate_c_function_stub(module: ModuleType,
104
116
class_name : Optional [str ] = None ,
105
117
class_sigs : Dict [str , str ] = {},
106
118
) -> None :
119
+ ret_type = 'Any'
120
+
107
121
if self_var :
108
122
self_arg = '%s, ' % self_var
109
123
else :
@@ -115,19 +129,37 @@ def generate_c_function_stub(module: ModuleType,
115
129
docstr = getattr (obj , '__doc__' , None )
116
130
inferred = infer_sig_from_docstring (docstr , name )
117
131
if inferred :
118
- sig = inferred
132
+ sig , ret_type = inferred
119
133
else :
120
134
if class_name and name not in sigs :
121
135
sig = infer_method_sig (name )
122
136
else :
123
137
sig = sigs .get (name , '(*args, **kwargs)' )
138
+ # strip away parenthesis
124
139
sig = sig [1 :- 1 ]
125
140
if sig :
126
- if sig .split (',' , 1 )[0 ] == self_var :
127
- self_arg = ''
141
+ if self_var :
142
+ # remove annotation on self from signature if present
143
+ groups = sig .split (',' , 1 )
144
+ if groups [0 ] == self_var or groups [0 ].startswith (self_var + ':' ):
145
+ self_arg = ''
146
+ sig = '{},{}' .format (self_var , groups [1 ]) if len (groups ) > 1 else self_var
128
147
else :
129
148
self_arg = self_arg .replace (', ' , '' )
130
- output .append ('def %s(%s%s): ...' % (name , self_arg , sig ))
149
+ output .append ('def %s(%s%s) -> %s: ...' % (name , self_arg , sig , ret_type ))
150
+
151
+
152
+ def generate_c_property_stub (name : str , obj : object , output : List [str ], readonly : bool ) -> None :
153
+ docstr = getattr (obj , '__doc__' , None )
154
+ inferred = infer_prop_type_from_docstring (docstr )
155
+ if not inferred :
156
+ inferred = 'Any'
157
+
158
+ output .append ('@property' )
159
+ output .append ('def {}(self) -> {}: ...' .format (name , inferred ))
160
+ if not readonly :
161
+ output .append ('@{}.setter' .format (name ))
162
+ output .append ('def {}(self, val: {}) -> None: ...' .format (name , inferred ))
131
163
132
164
133
165
def generate_c_type_stub (module : ModuleType ,
@@ -141,8 +173,9 @@ def generate_c_type_stub(module: ModuleType,
141
173
# (it could be a mappingproxy!), which makes mypyc mad, so obfuscate it.
142
174
obj_dict = getattr (obj , '__dict__' ) # type: Mapping[str, Any]
143
175
items = sorted (obj_dict .items (), key = lambda x : method_name_sort_key (x [0 ]))
144
- methods = []
145
- done = set ()
176
+ methods = [] # type: List[str]
177
+ properties = [] # type: List[str]
178
+ done = set () # type: Set[str]
146
179
for attr , value in items :
147
180
if is_c_method (value ) or is_c_classmethod (value ):
148
181
done .add (attr )
@@ -162,6 +195,10 @@ def generate_c_type_stub(module: ModuleType,
162
195
attr = '__init__'
163
196
generate_c_function_stub (module , attr , value , methods , self_var , sigs = sigs ,
164
197
class_name = class_name , class_sigs = class_sigs )
198
+ elif is_c_property (value ):
199
+ done .add (attr )
200
+ generate_c_property_stub (attr , value , properties , is_c_property_readonly (value ))
201
+
165
202
variables = []
166
203
for attr , value in items :
167
204
if is_skipped_attribute (attr ):
@@ -183,14 +220,16 @@ def generate_c_type_stub(module: ModuleType,
183
220
bases_str = '(%s)' % ', ' .join (base .__name__ for base in bases )
184
221
else :
185
222
bases_str = ''
186
- if not methods and not variables :
223
+ if not methods and not variables and not properties :
187
224
output .append ('class %s%s: ...' % (class_name , bases_str ))
188
225
else :
189
226
output .append ('class %s%s:' % (class_name , bases_str ))
190
227
for variable in variables :
191
228
output .append (' %s' % variable )
192
229
for method in methods :
193
230
output .append (' %s' % method )
231
+ for prop in properties :
232
+ output .append (' %s' % prop )
194
233
195
234
196
235
def method_name_sort_key (name : str ) -> Tuple [int , str ]:
0 commit comments