varname 0.12.1__py3-none-any.whl → 0.13.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
varname/__init__.py CHANGED
@@ -13,4 +13,4 @@ from .utils import (
13
13
  )
14
14
  from .core import varname, nameof, will, argname
15
15
 
16
- __version__ = "0.12.1"
16
+ __version__ = "0.13.0"
varname/helpers.py CHANGED
@@ -1,9 +1,13 @@
1
1
  """Some helper functions builtin based upon core features"""
2
+ from __future__ import annotations
3
+
2
4
  import inspect
3
5
  from functools import partial, wraps
6
+ from os import PathLike
4
7
  from typing import Any, Callable, Dict, Tuple, Type, Union
5
8
 
6
9
  from .utils import IgnoreType
10
+ from .ignore import IgnoreList
7
11
  from .core import argname, varname
8
12
 
9
13
 
@@ -220,3 +224,74 @@ def debug(
220
224
  else:
221
225
  for name_and_value in name_and_values:
222
226
  print(f"{prefix}{name_and_value}")
227
+
228
+
229
+ def exec_code(
230
+ code: str,
231
+ globals: Dict[str, Any] = None,
232
+ locals: Dict[str, Any] = None,
233
+ /,
234
+ sourcefile: PathLike | str = None,
235
+ frame: int = 1,
236
+ ignore: IgnoreType = None,
237
+ **kwargs: Any,
238
+ ) -> None:
239
+ """Execute code where source code is visible at runtime.
240
+
241
+ This function is useful when you want to execute some code, where you want to
242
+ retrieve the AST node of the code at runtime. This function will create a
243
+ temporary file and write the code into it, then execute the code in the
244
+ file.
245
+
246
+ Examples:
247
+ >>> from varname import varname
248
+ >>> def func(): return varname()
249
+ >>> exec('var = func()') # VarnameRetrievingError:
250
+ >>> # Unable to retrieve the ast node.
251
+ >>> from varname.helpers import code_exec
252
+ >>> code_exec('var = func()') # var == 'var'
253
+
254
+ Args:
255
+ code: The code to execute.
256
+ globals: The globals to use.
257
+ locals: The locals to use.
258
+ sourcefile: The source file to write the code into.
259
+ if not given, a temporary file will be used.
260
+ This file will be deleted after the code is executed.
261
+ frame: The call stack index. You can understand this as the number of
262
+ wrappers around this function. This is used to fetch `globals` and
263
+ `locals` from where the destination function (include the wrappers
264
+ of this function)
265
+ is called.
266
+ ignore: The intermediate calls to be ignored. See `varname.ignore`
267
+ Note that if both `globals` and `locals` are given, `frame` and
268
+ `ignore` will be ignored.
269
+ **kwargs: The keyword arguments to pass to `exec`.
270
+ """
271
+ if sourcefile is None:
272
+ import tempfile
273
+
274
+ with tempfile.NamedTemporaryFile(
275
+ mode="w", suffix=".py", delete=False
276
+ ) as f:
277
+ f.write(code)
278
+ sourcefile = f.name
279
+ else:
280
+ sourcefile = str(sourcefile)
281
+ with open(sourcefile, "w") as f:
282
+ f.write(code)
283
+
284
+ if globals is None or locals is None:
285
+ ignore_list = IgnoreList.create(ignore)
286
+ frame_info = ignore_list.get_frame(frame)
287
+ if globals is None:
288
+ globals = frame_info.f_globals
289
+ if locals is None:
290
+ locals = frame_info.f_locals
291
+
292
+ try:
293
+ exec(compile(code, sourcefile, "exec"), globals, locals, **kwargs)
294
+ finally:
295
+ import os
296
+
297
+ os.remove(sourcefile)
varname/utils.py CHANGED
@@ -185,7 +185,10 @@ def lookfor_parent_assign(node: ast.AST, strict: bool = True) -> AssignType:
185
185
  return None
186
186
 
187
187
 
188
- def node_name(node: ast.AST) -> Union[str, Tuple[Union[str, Tuple], ...]]:
188
+ def node_name(
189
+ node: ast.AST,
190
+ subscript_slice: bool = False,
191
+ ) -> Union[str, Tuple[Union[str, Tuple], ...]]:
189
192
  """Get the node node name.
190
193
 
191
194
  Raises ImproperUseError when failed
@@ -193,15 +196,50 @@ def node_name(node: ast.AST) -> Union[str, Tuple[Union[str, Tuple], ...]]:
193
196
  if isinstance(node, ast.Name):
194
197
  return node.id
195
198
  if isinstance(node, ast.Attribute):
196
- return node.attr
197
- if isinstance(node, (ast.List, ast.Tuple)):
199
+ return f"{node_name(node.value)}.{node.attr}"
200
+ if isinstance(node, ast.Constant):
201
+ return repr(node.value)
202
+ if isinstance(node, (ast.List, ast.Tuple)) and not subscript_slice:
198
203
  return tuple(node_name(elem) for elem in node.elts)
204
+ if isinstance(node, ast.List):
205
+ return f"[{', '.join(node_name(elem) for elem in node.elts)}]" # type: ignore
206
+ if isinstance(node, ast.Tuple):
207
+ if len(node.elts) == 1:
208
+ return f"({node_name(node.elts[0])},)"
209
+ return f"({', '.join(node_name(elem) for elem in node.elts)})" # type: ignore
199
210
  if isinstance(node, ast.Starred):
200
211
  return f"*{node_name(node.value)}"
212
+ if isinstance(node, ast.Slice):
213
+ return (
214
+ f"{node_name(node.lower)}:{node_name(node.upper)}:{node_name(node.step)}"
215
+ if node.lower is not None
216
+ and node.upper is not None
217
+ and node.step is not None
218
+ else f"{node_name(node.lower)}:{node_name(node.upper)}"
219
+ if node.lower is not None and node.upper is not None
220
+ else f"{node_name(node.lower)}:"
221
+ if node.lower is not None
222
+ else f":{node_name(node.upper)}"
223
+ if node.upper is not None
224
+ else ":"
225
+ )
226
+
227
+ name = type(node).__name__
228
+ if isinstance(node, ast.Subscript):
229
+ try:
230
+ return f"{node_name(node.value)}[{node_name(node.slice, True)}]"
231
+ except ImproperUseError:
232
+ name = f"{node_name(node.value)}[{type(node.slice).__name__}]"
201
233
 
202
234
  raise ImproperUseError(
203
- f"Can only get name of a variable or attribute, "
204
- f"not {ast.dump(node)}"
235
+ f"Node {name!r} detected, but only following nodes are supported: \n"
236
+ " - ast.Name (e.g. x)\n"
237
+ " - ast.Attribute (e.g. x.y, x be other supported nodes)\n"
238
+ " - ast.Constant (e.g. 1, 'a')\n"
239
+ " - ast.List (e.g. [x, y, z])\n"
240
+ " - ast.Tuple (e.g. (x, y, z))\n"
241
+ " - ast.Starred (e.g. *x)\n"
242
+ " - ast.Subscript with slice of the above nodes (e.g. x[y])"
205
243
  )
206
244
 
207
245
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: varname
3
- Version: 0.12.1
3
+ Version: 0.13.0
4
4
  Summary: Dark magics about variable names in python.
5
5
  Home-page: https://github.com/pwwang/python-varname
6
6
  License: MIT
@@ -54,6 +54,7 @@ Note if you use `python < 3.8`, install `varname < 0.11`
54
54
  - A decorator to register `__varname__` to functions/classes, using `register`
55
55
  - A helper function to create dict without explicitly specifying the key-value pairs, using `jsobj`
56
56
  - A `debug` function to print variables with their names and values
57
+ - `exec_code` to replace `exec` where source code is available at runtime
57
58
 
58
59
  ## Credits
59
60
 
@@ -221,7 +222,7 @@ Special thanks to [@HanyuuLu][2] to give up the name `varname` in pypi for this
221
222
  func = function2() # func == 'func'
222
223
 
223
224
  a = lambda: 0
224
- a.b = function() # a.b == 'b'
225
+ a.b = function() # a.b == 'a.b'
225
226
  ```
226
227
 
227
228
  ### The decorator way to register `__varname__` to functions/classes
@@ -346,7 +347,7 @@ func4(y, x, c=z) # prints: ('x', 'z')
346
347
  # __getattr__/__getitem__/__setattr/__setitem__/__add__/__lt__, etc.
347
348
  class Foo:
348
349
  def __setattr__(self, name, value):
349
- print(argname("name", "value"))
350
+ print(argname("name", "value", func=self.__setattr__))
350
351
 
351
352
  Foo().a = 1 # prints: ("'a'", '1')
352
353
 
@@ -406,6 +407,26 @@ debug(a+a)
406
407
  debug(a+a, vars_only=True) # ImproperUseError
407
408
  ```
408
409
 
410
+ ### Replacing `exec` with `exec_code`
411
+
412
+ ```python
413
+ from varname import argname
414
+ from varname.helpers import exec_code
415
+
416
+ class Obj:
417
+ def __init__(self):
418
+ self.argnames = []
419
+
420
+ def receive(self, arg):
421
+ self.argnames.append(argname('arg', func=self.receive))
422
+
423
+ obj = Obj()
424
+ # exec('obj.receive(1)') # Error
425
+ exec_code('obj.receive(1)')
426
+ exec_code('obj.receive(2)')
427
+ obj.argnames # ['1', '2']
428
+ ```
429
+
409
430
  ## Reliability and limitations
410
431
 
411
432
  `varname` is all depending on `executing` package to look for the node.
@@ -0,0 +1,9 @@
1
+ varname/__init__.py,sha256=UTuY7fG_AbLwpbhBnup66zRlEvl0WnG6exf5AvOyyp0,369
2
+ varname/core.py,sha256=nntOVpiavXP0EXijTNF6xIe4mxvz_FRpBjrL9z_JHJ0,19311
3
+ varname/helpers.py,sha256=ho5X2t3PiSYDexwpA4TO_l68VWh3HER7bsUKzcbiCNM,9242
4
+ varname/ignore.py,sha256=-VC3oqag44y2UlAw0ErYKHotx616qJL3Sjb_5y7Tw1c,14400
5
+ varname/utils.py,sha256=ByHbUjbdMlHHckW4y77Jr_DHhkSmk8TabTy3INDJWsY,20971
6
+ varname-0.13.0.dist-info/LICENSE,sha256=3bS8O2tMbBPz8rWmZBAOzkHQjcK-b7KwFHyyghEZ-Ak,1063
7
+ varname-0.13.0.dist-info/METADATA,sha256=6YjfqWLcmjJ9TXHN5yLlAn0DHrBg3escnFyzqLgKdWM,12580
8
+ varname-0.13.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
9
+ varname-0.13.0.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- varname/__init__.py,sha256=wCvOdwu0J6X2OQr3j_LgE5rTFYtVK7gPqDD382qOkVs,369
2
- varname/core.py,sha256=nntOVpiavXP0EXijTNF6xIe4mxvz_FRpBjrL9z_JHJ0,19311
3
- varname/helpers.py,sha256=TUeohi5yCSleXA2betkulAG2_23jsoM_vohHuCEVPQI,6679
4
- varname/ignore.py,sha256=-VC3oqag44y2UlAw0ErYKHotx616qJL3Sjb_5y7Tw1c,14400
5
- varname/utils.py,sha256=f0dDRYfOeebdDvEwVSQVXNpHC1sB3-1qNE8mICg1IcE,19296
6
- varname-0.12.1.dist-info/LICENSE,sha256=3bS8O2tMbBPz8rWmZBAOzkHQjcK-b7KwFHyyghEZ-Ak,1063
7
- varname-0.12.1.dist-info/METADATA,sha256=IrxRHVvT9g4Xtq0w_1tEV9o3_rCeeW1KqkU0dAsg3s4,12074
8
- varname-0.12.1.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
9
- varname-0.12.1.dist-info/RECORD,,