33import dis
44import sys
55import warnings
6- from typing import Union , Tuple
6+ from typing import Union , Tuple , Any , Optional , Type , List , Iterator
7+ from types import FrameType , CodeType
78from collections import namedtuple as standard_namedtuple
89from functools import lru_cache
910
1011import executing
1112
1213__version__ = "0.4.0"
1314
15+ NodeType = Type [ast .AST ]
16+
1417class VarnameRetrievingError (Exception ):
1518 """When failed to retrieve the varname"""
1619
17- def varname (caller : int = 1 , raise_exc : bool = True ) -> str :
20+ def varname (caller : int = 1 , raise_exc : bool = True ) -> Optional [ str ] :
1821 """Get the variable name that assigned by function/class calls
1922
2023 Args:
21- caller (int) : The call stack index, indicating where this function
24+ caller: The call stack index, indicating where this function
2225 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
2427 to retrieve the name.
2528
2629 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
2831 we failed to retrieve the variable name.
2932
3033 Raises:
@@ -33,12 +36,11 @@ def varname(caller: int = 1, raise_exc: bool = True) -> str:
3336 when we are unable to retrieve the variable name and `raise_exc`
3437 is set to `True`.
3538
36- Warns:
3739 UserWarning: When there are multiple target
3840 in the assign node. (e.g: `a = b = func()`, in such a case,
3941 `b == 'a'`, may not be the case you want)
4042 """
41- node = _get_node (caller , raise_exc = raise_exc )
43+ node : Optional [ NodeType ] = _get_node (caller , raise_exc = raise_exc )
4244 if not node :
4345 if raise_exc :
4446 raise VarnameRetrievingError ("Unable to retrieve the ast node." )
@@ -58,10 +60,10 @@ def varname(caller: int = 1, raise_exc: bool = True) -> str:
5860 warnings .warn ("Multiple targets in assignment, variable name "
5961 "on the very left will be used." ,
6062 UserWarning )
61- target = node .targets [0 ]
63+ target : str = node .targets [0 ]
6264 return _node_name (target )
6365
64- def will (caller : int = 1 , raise_exc : bool = True ) -> str :
66+ def will (caller : int = 1 , raise_exc : bool = True ) -> Optional [ str ] :
6567 """Detect the attribute name right immediately after a function call.
6668
6769 Examples:
@@ -91,25 +93,25 @@ def will(caller: int = 1, raise_exc: bool = True) -> str:
9193 >>> awesome.permit().do() == 'I am doing!'
9294
9395 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
9698
9799 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`
100102
101103 Raises:
102104 VarnameRetrievingError: When `raise_exc` is `True` and we failed to
103105 detect the attribute name (including not having one)
104106 """
105- node = _get_node (caller , raise_exc = raise_exc )
107+ node : Optional [ NodeType ] = _get_node (caller , raise_exc = raise_exc )
106108 if not node :
107109 if raise_exc :
108110 raise VarnameRetrievingError ("Unable to retrieve the frame." )
109111 return None
110112
111113 # try to get not inst.attr from inst.attr()
112- node = node .parent
114+ node : NodeType = node .parent
113115
114116 # see test_will_fail
115117 if not isinstance (node , ast .Attribute ):
@@ -122,7 +124,7 @@ def will(caller: int = 1, raise_exc: bool = True) -> str:
122124 # ast.Attribute
123125 return node .attr
124126
125- def inject (obj : any ) -> any :
127+ def inject (obj : object ) -> object :
126128 """Inject attribute `__varname__` to an object
127129
128130 Examples:
@@ -150,9 +152,9 @@ def inject(obj: any) -> any:
150152 be set as an attribute
151153
152154 Returns:
153- obj: The object with __varname__ injected
155+ The object with __varname__ injected
154156 """
155- vname = varname ()
157+ vname : Optional [ str ] = varname ()
156158 try :
157159 setattr (obj , '__varname__' , vname )
158160 except AttributeError :
@@ -178,15 +180,15 @@ def nameof(*args, caller: int = 1) -> Union[str, Tuple[str]]:
178180 *args: A couple of variables passed in
179181
180182 Returns:
181- tuple|str: The names of variables passed in
183+ The names of variables passed in
182184 """
183- node = _get_node (caller - 1 , raise_exc = True )
185+ node : Optional [ NodeType ] = _get_node (caller - 1 , raise_exc = True )
184186 if not node :
185187 if len (args ) == 1 :
186188 return _bytecode_nameof (caller + 1 )
187189 raise VarnameRetrievingError ("Unable to retrieve callee's node." )
188190
189- ret = []
191+ ret : List [ str ] = []
190192 for arg in node .args :
191193 ret .append (_node_name (arg ))
192194
@@ -196,7 +198,7 @@ def nameof(*args, caller: int = 1) -> Union[str, Tuple[str]]:
196198
197199 return ret [0 ] if len (args ) == 1 else tuple (ret )
198200
199- def namedtuple (* args , ** kwargs ):
201+ def namedtuple (* args , ** kwargs ) -> type :
200202 """A shortcut for namedtuple
201203
202204 You don't need to specify the typename, which will be fetched from
@@ -214,6 +216,9 @@ def namedtuple(*args, **kwargs):
214216 *args: arguments for `collections.namedtuple` except `typename`
215217 **kwargs: keyword arguments for `collections.namedtuple`
216218 except `typename`
219+
220+ Returns:
221+ The namedtuple you desired.
217222 """
218223 typename : str = varname (raise_exc = True )
219224 return standard_namedtuple (typename , * args , ** kwargs )
@@ -232,16 +237,17 @@ class Wrapper:
232237 >>> # bar.value is val
233238
234239 Args:
235-
240+ value: The value to be wrapped
241+ raise_exc: Whether to raise exception when varname is failed to retrieve
236242
237243 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
240246 """
241247
242- def __init__ (self , value : any , raise_exc : bool = True ):
248+ def __init__ (self , value : Any , raise_exc : bool = True ):
243249 self .name : str = varname (raise_exc = raise_exc )
244- self .value : any = value
250+ self .value : Any = value
245251
246252 def __str__ (self ) -> str :
247253 return repr (self .value )
@@ -250,14 +256,14 @@ def __repr__(self) -> str:
250256 return (f"<{ self .__class__ .__name__ } "
251257 f"(name={ self .name !r} , value={ self .value !r} )>" )
252258
253- def _get_frame (caller ) :
259+ def _get_frame (caller : int ) -> FrameType :
254260 """Get the frame at `caller` depth"""
255261 try :
256262 return sys ._getframe (caller + 1 )
257263 except Exception as exc :
258264 raise VarnameRetrievingError from exc
259265
260- def _get_node (caller : int , raise_exc : bool = True ):
266+ def _get_node (caller : int , raise_exc : bool = True ) -> Optional [ NodeType ] :
261267 """Try to get node from the executing object.
262268
263269 This can fail when a frame is failed to retrieve.
@@ -267,11 +273,11 @@ def _get_node(caller: int, raise_exc: bool = True):
267273 When the node can not be retrieved, try to return the first statement.
268274 """
269275 try :
270- frame = _get_frame (caller + 2 )
276+ frame : FrameType = _get_frame (caller + 2 )
271277 except VarnameRetrievingError :
272278 return None
273279
274- exet = executing .Source .executing (frame )
280+ exet : executing . Executing = executing .Source .executing (frame )
275281
276282 if exet .node :
277283 return exet .node
@@ -285,7 +291,7 @@ def _get_node(caller: int, raise_exc: bool = True):
285291
286292 return None
287293
288- def _lookfor_parent_assign (node ) :
294+ def _lookfor_parent_assign (node : NodeType ) -> Optional [ ast . Assign ] :
289295 """Look for an ast.Assign node in the parents"""
290296 while hasattr (node , 'parent' ):
291297 node = node .parent
@@ -294,8 +300,11 @@ def _lookfor_parent_assign(node):
294300 return node
295301 return None
296302
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+ """
299308 if isinstance (node , ast .Name ):
300309 return node .id
301310 if isinstance (node , ast .Attribute ):
@@ -306,15 +315,15 @@ def _node_name(node):
306315 f"not { ast .dump (node )} "
307316 )
308317
309- def _bytecode_nameof (caller = 1 ) :
318+ def _bytecode_nameof (caller : int = 1 ) -> str :
310319 """Bytecode version of nameof as a fallback"""
311- frame = _get_frame (caller )
320+ frame : FrameType = _get_frame (caller )
312321 return _bytecode_nameof_cached (frame .f_code , frame .f_lasti )
313322
314323@lru_cache ()
315- def _bytecode_nameof_cached (code , offset ) :
324+ def _bytecode_nameof_cached (code : CodeType , offset : int ) -> str :
316325 """Cached Bytecode version of nameof"""
317- instructions = list (dis .get_instructions (code ))
326+ instructions : Iterator [ dis . Instruction ] = list (dis .get_instructions (code ))
318327 (current_instruction_index , current_instruction ), = (
319328 (index , instruction )
320329 for index , instruction in enumerate (instructions )
@@ -324,7 +333,9 @@ def _bytecode_nameof_cached(code, offset):
324333 if current_instruction .opname not in ("CALL_FUNCTION" , "CALL_METHOD" ):
325334 raise VarnameRetrievingError ("Did you call nameof in a weird way?" )
326335
327- name_instruction = instructions [current_instruction_index - 1 ]
336+ name_instruction : dis .Instruction = instructions [
337+ current_instruction_index - 1
338+ ]
328339 if not name_instruction .opname .startswith ("LOAD_" ):
329340 raise VarnameRetrievingError ("Argument must be a variable or attribute" )
330341
0 commit comments