3
3
import dis
4
4
import sys
5
5
import warnings
6
- from typing import Union , Tuple
6
+ from typing import Union , Tuple , Any , Optional , Type , List , Iterator
7
+ from types import FrameType , CodeType
7
8
from collections import namedtuple as standard_namedtuple
8
9
from functools import lru_cache
9
10
10
11
import executing
11
12
12
13
__version__ = "0.4.0"
13
14
15
+ NodeType = Type [ast .AST ]
16
+
14
17
class VarnameRetrievingError (Exception ):
15
18
"""When failed to retrieve the varname"""
16
19
17
- def varname (caller : int = 1 , raise_exc : bool = True ) -> str :
20
+ def varname (caller : int = 1 , raise_exc : bool = True ) -> Optional [ str ] :
18
21
"""Get the variable name that assigned by function/class calls
19
22
20
23
Args:
21
- caller (int) : The call stack index, indicating where this function
24
+ caller: The call stack index, indicating where this function
22
25
is called relative to where the variable is finally retrieved
23
- raise_exc (bool) : Whether we should raise an exception if failed
26
+ raise_exc: Whether we should raise an exception if failed
24
27
to retrieve the name.
25
28
26
29
Returns:
27
- str|None: The variable name, or `None` when `raise_exc` is `False` and
30
+ The variable name, or `None` when `raise_exc` is `False` and
28
31
we failed to retrieve the variable name.
29
32
30
33
Raises:
@@ -33,12 +36,11 @@ def varname(caller: int = 1, raise_exc: bool = True) -> str:
33
36
when we are unable to retrieve the variable name and `raise_exc`
34
37
is set to `True`.
35
38
36
- Warns:
37
39
UserWarning: When there are multiple target
38
40
in the assign node. (e.g: `a = b = func()`, in such a case,
39
41
`b == 'a'`, may not be the case you want)
40
42
"""
41
- node = _get_node (caller , raise_exc = raise_exc )
43
+ node : Optional [ NodeType ] = _get_node (caller , raise_exc = raise_exc )
42
44
if not node :
43
45
if raise_exc :
44
46
raise VarnameRetrievingError ("Unable to retrieve the ast node." )
@@ -58,10 +60,10 @@ def varname(caller: int = 1, raise_exc: bool = True) -> str:
58
60
warnings .warn ("Multiple targets in assignment, variable name "
59
61
"on the very left will be used." ,
60
62
UserWarning )
61
- target = node .targets [0 ]
63
+ target : str = node .targets [0 ]
62
64
return _node_name (target )
63
65
64
- def will (caller : int = 1 , raise_exc : bool = True ) -> str :
66
+ def will (caller : int = 1 , raise_exc : bool = True ) -> Optional [ str ] :
65
67
"""Detect the attribute name right immediately after a function call.
66
68
67
69
Examples:
@@ -91,25 +93,25 @@ def will(caller: int = 1, raise_exc: bool = True) -> str:
91
93
>>> awesome.permit().do() == 'I am doing!'
92
94
93
95
Args:
94
- caller (int) : At which stack this function is called.
95
- raise_exc (bool) : Raise exception we failed to detect
96
+ caller: At which stack this function is called.
97
+ raise_exc: Raise exception we failed to detect
96
98
97
99
Returns:
98
- str: The attribute name right after the function call
99
- None: If there is no attribute attached and `raise_exc` is `False`
100
+ The attribute name right after the function call
101
+ If there is no attribute attached and `raise_exc` is `False`
100
102
101
103
Raises:
102
104
VarnameRetrievingError: When `raise_exc` is `True` and we failed to
103
105
detect the attribute name (including not having one)
104
106
"""
105
- node = _get_node (caller , raise_exc = raise_exc )
107
+ node : Optional [ NodeType ] = _get_node (caller , raise_exc = raise_exc )
106
108
if not node :
107
109
if raise_exc :
108
110
raise VarnameRetrievingError ("Unable to retrieve the frame." )
109
111
return None
110
112
111
113
# try to get not inst.attr from inst.attr()
112
- node = node .parent
114
+ node : NodeType = node .parent
113
115
114
116
# see test_will_fail
115
117
if not isinstance (node , ast .Attribute ):
@@ -122,7 +124,7 @@ def will(caller: int = 1, raise_exc: bool = True) -> str:
122
124
# ast.Attribute
123
125
return node .attr
124
126
125
- def inject (obj : any ) -> any :
127
+ def inject (obj : object ) -> object :
126
128
"""Inject attribute `__varname__` to an object
127
129
128
130
Examples:
@@ -150,9 +152,9 @@ def inject(obj: any) -> any:
150
152
be set as an attribute
151
153
152
154
Returns:
153
- obj: The object with __varname__ injected
155
+ The object with __varname__ injected
154
156
"""
155
- vname = varname ()
157
+ vname : Optional [ str ] = varname ()
156
158
try :
157
159
setattr (obj , '__varname__' , vname )
158
160
except AttributeError :
@@ -178,15 +180,15 @@ def nameof(*args, caller: int = 1) -> Union[str, Tuple[str]]:
178
180
*args: A couple of variables passed in
179
181
180
182
Returns:
181
- tuple|str: The names of variables passed in
183
+ The names of variables passed in
182
184
"""
183
- node = _get_node (caller - 1 , raise_exc = True )
185
+ node : Optional [ NodeType ] = _get_node (caller - 1 , raise_exc = True )
184
186
if not node :
185
187
if len (args ) == 1 :
186
188
return _bytecode_nameof (caller + 1 )
187
189
raise VarnameRetrievingError ("Unable to retrieve callee's node." )
188
190
189
- ret = []
191
+ ret : List [ str ] = []
190
192
for arg in node .args :
191
193
ret .append (_node_name (arg ))
192
194
@@ -196,7 +198,7 @@ def nameof(*args, caller: int = 1) -> Union[str, Tuple[str]]:
196
198
197
199
return ret [0 ] if len (args ) == 1 else tuple (ret )
198
200
199
- def namedtuple (* args , ** kwargs ):
201
+ def namedtuple (* args , ** kwargs ) -> type :
200
202
"""A shortcut for namedtuple
201
203
202
204
You don't need to specify the typename, which will be fetched from
@@ -214,6 +216,9 @@ def namedtuple(*args, **kwargs):
214
216
*args: arguments for `collections.namedtuple` except `typename`
215
217
**kwargs: keyword arguments for `collections.namedtuple`
216
218
except `typename`
219
+
220
+ Returns:
221
+ The namedtuple you desired.
217
222
"""
218
223
typename : str = varname (raise_exc = True )
219
224
return standard_namedtuple (typename , * args , ** kwargs )
@@ -232,16 +237,17 @@ class Wrapper:
232
237
>>> # bar.value is val
233
238
234
239
Args:
235
-
240
+ value: The value to be wrapped
241
+ raise_exc: Whether to raise exception when varname is failed to retrieve
236
242
237
243
Attributes:
238
- name (str) : The variable name to which the instance is assigned
239
- value (any) : The value this wrapper wraps
244
+ name: The variable name to which the instance is assigned
245
+ value: The value this wrapper wraps
240
246
"""
241
247
242
- def __init__ (self , value : any , raise_exc : bool = True ):
248
+ def __init__ (self , value : Any , raise_exc : bool = True ):
243
249
self .name : str = varname (raise_exc = raise_exc )
244
- self .value : any = value
250
+ self .value : Any = value
245
251
246
252
def __str__ (self ) -> str :
247
253
return repr (self .value )
@@ -250,14 +256,14 @@ def __repr__(self) -> str:
250
256
return (f"<{ self .__class__ .__name__ } "
251
257
f"(name={ self .name !r} , value={ self .value !r} )>" )
252
258
253
- def _get_frame (caller ) :
259
+ def _get_frame (caller : int ) -> FrameType :
254
260
"""Get the frame at `caller` depth"""
255
261
try :
256
262
return sys ._getframe (caller + 1 )
257
263
except Exception as exc :
258
264
raise VarnameRetrievingError from exc
259
265
260
- def _get_node (caller : int , raise_exc : bool = True ):
266
+ def _get_node (caller : int , raise_exc : bool = True ) -> Optional [ NodeType ] :
261
267
"""Try to get node from the executing object.
262
268
263
269
This can fail when a frame is failed to retrieve.
@@ -267,11 +273,11 @@ def _get_node(caller: int, raise_exc: bool = True):
267
273
When the node can not be retrieved, try to return the first statement.
268
274
"""
269
275
try :
270
- frame = _get_frame (caller + 2 )
276
+ frame : FrameType = _get_frame (caller + 2 )
271
277
except VarnameRetrievingError :
272
278
return None
273
279
274
- exet = executing .Source .executing (frame )
280
+ exet : executing . Executing = executing .Source .executing (frame )
275
281
276
282
if exet .node :
277
283
return exet .node
@@ -285,7 +291,7 @@ def _get_node(caller: int, raise_exc: bool = True):
285
291
286
292
return None
287
293
288
- def _lookfor_parent_assign (node ) :
294
+ def _lookfor_parent_assign (node : NodeType ) -> Optional [ ast . Assign ] :
289
295
"""Look for an ast.Assign node in the parents"""
290
296
while hasattr (node , 'parent' ):
291
297
node = node .parent
@@ -294,8 +300,11 @@ def _lookfor_parent_assign(node):
294
300
return node
295
301
return None
296
302
297
- def _node_name (node ):
298
- """Get the node node name"""
303
+ def _node_name (node : NodeType ) -> str :
304
+ """Get the node node name.
305
+
306
+ Raises VarnameRetrievingError when failed
307
+ """
299
308
if isinstance (node , ast .Name ):
300
309
return node .id
301
310
if isinstance (node , ast .Attribute ):
@@ -306,15 +315,15 @@ def _node_name(node):
306
315
f"not { ast .dump (node )} "
307
316
)
308
317
309
- def _bytecode_nameof (caller = 1 ) :
318
+ def _bytecode_nameof (caller : int = 1 ) -> str :
310
319
"""Bytecode version of nameof as a fallback"""
311
- frame = _get_frame (caller )
320
+ frame : FrameType = _get_frame (caller )
312
321
return _bytecode_nameof_cached (frame .f_code , frame .f_lasti )
313
322
314
323
@lru_cache ()
315
- def _bytecode_nameof_cached (code , offset ) :
324
+ def _bytecode_nameof_cached (code : CodeType , offset : int ) -> str :
316
325
"""Cached Bytecode version of nameof"""
317
- instructions = list (dis .get_instructions (code ))
326
+ instructions : Iterator [ dis . Instruction ] = list (dis .get_instructions (code ))
318
327
(current_instruction_index , current_instruction ), = (
319
328
(index , instruction )
320
329
for index , instruction in enumerate (instructions )
@@ -324,7 +333,9 @@ def _bytecode_nameof_cached(code, offset):
324
333
if current_instruction .opname not in ("CALL_FUNCTION" , "CALL_METHOD" ):
325
334
raise VarnameRetrievingError ("Did you call nameof in a weird way?" )
326
335
327
- name_instruction = instructions [current_instruction_index - 1 ]
336
+ name_instruction : dis .Instruction = instructions [
337
+ current_instruction_index - 1
338
+ ]
328
339
if not name_instruction .opname .startswith ("LOAD_" ):
329
340
raise VarnameRetrievingError ("Argument must be a variable or attribute" )
330
341
0 commit comments