varname 0.12.1__tar.gz → 0.13.0__tar.gz
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-0.12.1 → varname-0.13.0}/PKG-INFO +24 -3
- {varname-0.12.1 → varname-0.13.0}/README.md +23 -2
- {varname-0.12.1 → varname-0.13.0}/pyproject.toml +2 -2
- varname-0.13.0/setup.py +34 -0
- {varname-0.12.1 → varname-0.13.0}/varname/__init__.py +1 -1
- {varname-0.12.1 → varname-0.13.0}/varname/helpers.py +75 -0
- {varname-0.12.1 → varname-0.13.0}/varname/utils.py +43 -5
- varname-0.12.1/setup.py +0 -34
- {varname-0.12.1 → varname-0.13.0}/LICENSE +0 -0
- {varname-0.12.1 → varname-0.13.0}/varname/core.py +0 -0
- {varname-0.12.1 → varname-0.13.0}/varname/ignore.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: varname
|
3
|
-
Version: 0.
|
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.
|
@@ -31,6 +31,7 @@ Note if you use `python < 3.8`, install `varname < 0.11`
|
|
31
31
|
- A decorator to register `__varname__` to functions/classes, using `register`
|
32
32
|
- A helper function to create dict without explicitly specifying the key-value pairs, using `jsobj`
|
33
33
|
- A `debug` function to print variables with their names and values
|
34
|
+
- `exec_code` to replace `exec` where source code is available at runtime
|
34
35
|
|
35
36
|
## Credits
|
36
37
|
|
@@ -198,7 +199,7 @@ Special thanks to [@HanyuuLu][2] to give up the name `varname` in pypi for this
|
|
198
199
|
func = function2() # func == 'func'
|
199
200
|
|
200
201
|
a = lambda: 0
|
201
|
-
a.b = function() # a.b == 'b'
|
202
|
+
a.b = function() # a.b == 'a.b'
|
202
203
|
```
|
203
204
|
|
204
205
|
### The decorator way to register `__varname__` to functions/classes
|
@@ -323,7 +324,7 @@ func4(y, x, c=z) # prints: ('x', 'z')
|
|
323
324
|
# __getattr__/__getitem__/__setattr/__setitem__/__add__/__lt__, etc.
|
324
325
|
class Foo:
|
325
326
|
def __setattr__(self, name, value):
|
326
|
-
print(argname("name", "value"))
|
327
|
+
print(argname("name", "value", func=self.__setattr__))
|
327
328
|
|
328
329
|
Foo().a = 1 # prints: ("'a'", '1')
|
329
330
|
|
@@ -383,6 +384,26 @@ debug(a+a)
|
|
383
384
|
debug(a+a, vars_only=True) # ImproperUseError
|
384
385
|
```
|
385
386
|
|
387
|
+
### Replacing `exec` with `exec_code`
|
388
|
+
|
389
|
+
```python
|
390
|
+
from varname import argname
|
391
|
+
from varname.helpers import exec_code
|
392
|
+
|
393
|
+
class Obj:
|
394
|
+
def __init__(self):
|
395
|
+
self.argnames = []
|
396
|
+
|
397
|
+
def receive(self, arg):
|
398
|
+
self.argnames.append(argname('arg', func=self.receive))
|
399
|
+
|
400
|
+
obj = Obj()
|
401
|
+
# exec('obj.receive(1)') # Error
|
402
|
+
exec_code('obj.receive(1)')
|
403
|
+
exec_code('obj.receive(2)')
|
404
|
+
obj.argnames # ['1', '2']
|
405
|
+
```
|
406
|
+
|
386
407
|
## Reliability and limitations
|
387
408
|
|
388
409
|
`varname` is all depending on `executing` package to look for the node.
|
@@ -4,7 +4,7 @@ build-backend = "poetry.masonry.api"
|
|
4
4
|
|
5
5
|
[tool.poetry]
|
6
6
|
name = "varname"
|
7
|
-
version = "0.
|
7
|
+
version = "0.13.0"
|
8
8
|
description = "Dark magics about variable names in python."
|
9
9
|
authors = [ "pwwang <pwwang@pwwang.com>",]
|
10
10
|
license = "MIT"
|
@@ -42,6 +42,6 @@ show_error_codes = true
|
|
42
42
|
strict_optional = false
|
43
43
|
|
44
44
|
[tool.black]
|
45
|
-
line-length =
|
45
|
+
line-length = 88
|
46
46
|
target-version = ['py37', 'py38', 'py39', 'py310']
|
47
47
|
include = '\.pyi?$'
|
varname-0.13.0/setup.py
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
from setuptools import setup
|
3
|
+
|
4
|
+
packages = \
|
5
|
+
['varname']
|
6
|
+
|
7
|
+
package_data = \
|
8
|
+
{'': ['*']}
|
9
|
+
|
10
|
+
install_requires = \
|
11
|
+
['executing>=2.0,<3.0']
|
12
|
+
|
13
|
+
extras_require = \
|
14
|
+
{'all': ['asttokens==2.*', 'pure_eval==0.*']}
|
15
|
+
|
16
|
+
setup_kwargs = {
|
17
|
+
'name': 'varname',
|
18
|
+
'version': '0.13.0',
|
19
|
+
'description': 'Dark magics about variable names in python.',
|
20
|
+
'long_description': '![varname][7]\n\n[![Pypi][3]][4] [![Github][5]][6] [![PythonVers][8]][4] ![Building][10]\n[![Docs and API][9]][15] [![Codacy][12]][13] [![Codacy coverage][14]][13]\n![Downloads][17]\n\nDark magics about variable names in python\n\n[CHANGELOG][16] | [API][15] | [Playground][11] | :fire: [StackOverflow answer][20]\n\n## Installation\n\n```shell\npip install -U varname\n```\n\nNote if you use `python < 3.8`, install `varname < 0.11`\n\n## Features\n\n- Core features:\n\n - Retrieving names of variables a function/class call is assigned to from inside it, using `varname`.\n - Retrieving variable names directly, using `nameof`\n - Detecting next immediate attribute name, using `will`\n - Fetching argument names/sources passed to a function using `argname`\n\n- Other helper APIs (built based on core features):\n\n - A value wrapper to store the variable name that a value is assigned to, using `Wrapper`\n - A decorator to register `__varname__` to functions/classes, using `register`\n - A helper function to create dict without explicitly specifying the key-value pairs, using `jsobj`\n - A `debug` function to print variables with their names and values\n - `exec_code` to replace `exec` where source code is available at runtime\n\n## Credits\n\nThanks goes to these awesome people/projects:\n\n<table>\n <tr>\n <td align="center" style="min-width: 75px">\n <a href="https://github.com/alexmojaki/executing">\n <img src="https://ui-avatars.com/api/?color=3333ff&background=ffffff&bold=true&name=e&size=400" width="50px;" alt=""/>\n <br /><sub><b>executing</b></sub>\n </a>\n </td>\n <td align="center" style="min-width: 75px">\n <a href="https://github.com/alexmojaki">\n <img src="https://avatars0.githubusercontent.com/u/3627481?s=400&v=4" width="50px;" alt=""/>\n <br /><sub><b>@alexmojaki</b></sub>\n </a>\n </td>\n <td align="center" style="min-width: 75px">\n <a href="https://github.com/breuleux">\n <img src="https://avatars.githubusercontent.com/u/599820?s=400&v=4" width="50px;" alt=""/>\n <br /><sub><b>@breuleux</b></sub>\n </a>\n </td>\n <td align="center" style="min-width: 75px">\n <a href="https://github.com/ElCuboNegro">\n <img src="https://avatars.githubusercontent.com/u/5524219?s=400&v=4" width="50px;" alt=""/>\n <br /><sub><b>@ElCuboNegro</b></sub>\n </a>\n </td>\n <td align="center" style="min-width: 75px">\n <a href="https://github.com/thewchan">\n <img src="https://avatars.githubusercontent.com/u/49702524?s=400&v=4" width="50px;" alt=""/>\n <br /><sub><b>@thewchan</b></sub>\n </a>\n </td>\n <td align="center" style="min-width: 75px">\n <a href="https://github.com/LawsOfSympathy">\n <img src="https://avatars.githubusercontent.com/u/96355982?s=400&v=4" width="50px;" alt=""/>\n <br /><sub><b>@LawsOfSympathy</b></sub>\n </a>\n </td>\n </tr>\n</table>\n\nSpecial thanks to [@HanyuuLu][2] to give up the name `varname` in pypi for this project.\n\n## Usage\n\n### Retrieving the variable names using `varname(...)`\n\n- From inside a function\n\n ```python\n from varname import varname\n def function():\n return varname()\n\n func = function() # func == \'func\'\n ```\n\n When there are intermediate frames:\n\n ```python\n def wrapped():\n return function()\n\n def function():\n # retrieve the variable name at the 2nd frame from this one\n return varname(frame=2)\n\n func = wrapped() # func == \'func\'\n ```\n\n Or use `ignore` to ignore the wrapped frame:\n\n ```python\n def wrapped():\n return function()\n\n def function():\n return varname(ignore=wrapped)\n\n func = wrapped() # func == \'func\'\n ```\n\n Calls from standard libraries are ignored by default:\n\n ```python\n import asyncio\n\n async def function():\n return varname()\n\n func = asyncio.run(function()) # func == \'func\'\n ```\n\n Use `strict` to control whether the call should be assigned to\n the variable directly:\n\n ```python\n def function(strict):\n return varname(strict=strict)\n\n func = function(True) # OK, direct assignment, func == \'func\'\n\n func = [function(True)] # Not a direct assignment, raises ImproperUseError\n func = [function(False)] # OK, func == [\'func\']\n\n func = function(False), function(False) # OK, func = (\'func\', \'func\')\n ```\n\n- Retrieving name of a class instance\n\n ```python\n class Foo:\n def __init__(self):\n self.id = varname()\n\n def copy(self):\n # also able to fetch inside a method call\n copied = Foo() # copied.id == \'copied\'\n copied.id = varname() # assign id to whatever variable name\n return copied\n\n foo = Foo() # foo.id == \'foo\'\n\n foo2 = foo.copy() # foo2.id == \'foo2\'\n ```\n\n- Multiple variables on Left-hand side\n\n ```python\n # since v0.5.4\n def func():\n return varname(multi_vars=True)\n\n a = func() # a == (\'a\',)\n a, b = func() # (a, b) == (\'a\', \'b\')\n [a, b] = func() # (a, b) == (\'a\', \'b\')\n\n # hierarchy is also possible\n a, (b, c) = func() # (a, b, c) == (\'a\', \'b\', \'c\')\n ```\n\n- Some unusual use\n\n ```python\n def function(**kwargs):\n return varname(strict=False)\n\n func = func1 = function() # func == func1 == \'func1\'\n # if varname < 0.8: func == func1 == \'func\'\n # a warning will be shown\n # since you may not want func to be \'func1\'\n\n x = function(y = function()) # x == \'x\'\n\n # get part of the name\n func_abc = function()[-3:] # func_abc == \'abc\'\n\n # function alias supported now\n function2 = function\n func = function2() # func == \'func\'\n\n a = lambda: 0\n a.b = function() # a.b == \'a.b\'\n ```\n\n### The decorator way to register `__varname__` to functions/classes\n\n- Registering `__varname__` to functions\n\n ```python\n from varname.helpers import register\n\n @register\n def function():\n return __varname__\n\n func = function() # func == \'func\'\n ```\n\n ```python\n # arguments also allowed (frame, ignore and raise_exc)\n @register(frame=2)\n def function():\n return __varname__\n\n def wrapped():\n return function()\n\n func = wrapped() # func == \'func\'\n ```\n\n- Registering `__varname__` as a class property\n\n ```python\n @register\n class Foo:\n ...\n\n foo = Foo()\n # foo.__varname__ == \'foo\'\n ```\n\n### Getting variable names directly using `nameof`\n\n```python\nfrom varname import varname, nameof\n\na = 1\nnameof(a) # \'a\'\n\nb = 2\nnameof(a, b) # (\'a\', \'b\')\n\ndef func():\n return varname() + \'_suffix\'\n\nf = func() # f == \'f_suffix\'\nnameof(f) # \'f\'\n\n# get full names of (chained) attribute calls\nfunc.a = func\nnameof(func.a, vars_only=False) # \'func.a\'\n\nfunc.a.b = 1\nnameof(func.a.b, vars_only=False) # \'func.a.b\'\n```\n\n### Detecting next immediate attribute name\n\n```python\nfrom varname import will\nclass AwesomeClass:\n def __init__(self):\n self.will = None\n\n def permit(self):\n self.will = will(raise_exc=False)\n if self.will == \'do\':\n # let self handle do\n return self\n raise AttributeError(\'Should do something with AwesomeClass object\')\n\n def do(self):\n if self.will != \'do\':\n raise AttributeError("You don\'t have permission to do")\n return \'I am doing!\'\n\nawesome = AwesomeClass()\nawesome.do() # AttributeError: You don\'t have permission to do\nawesome.permit() # AttributeError: Should do something with AwesomeClass object\nawesome.permit().do() == \'I am doing!\'\n```\n\n### Fetching argument names/sources using `argname`\n\n```python\nfrom varname import argname\n\ndef func(a, b=1):\n print(argname(\'a\'))\n\nx = y = z = 2\nfunc(x) # prints: x\n\n\ndef func2(a, b=1):\n print(argname(\'a\', \'b\'))\nfunc2(y, b=x) # prints: (\'y\', \'x\')\n\n\n# allow expressions\ndef func3(a, b=1):\n print(argname(\'a\', \'b\', vars_only=False))\nfunc3(x+y, y+x) # prints: (\'x+y\', \'y+x\')\n\n\n# positional and keyword arguments\ndef func4(*args, **kwargs):\n print(argname(\'args[1]\', \'kwargs[c]\'))\nfunc4(y, x, c=z) # prints: (\'x\', \'z\')\n\n\n# As of 0.9.0 (see: https://pwwang.github.io/python-varname/CHANGELOG/#v090)\n# Can also fetch the source of the argument for\n# __getattr__/__getitem__/__setattr/__setitem__/__add__/__lt__, etc.\nclass Foo:\n def __setattr__(self, name, value):\n print(argname("name", "value", func=self.__setattr__))\n\nFoo().a = 1 # prints: ("\'a\'", \'1\')\n\n```\n\n### Value wrapper\n\n```python\nfrom varname.helpers import Wrapper\n\nfoo = Wrapper(True)\n# foo.name == \'foo\'\n# foo.value == True\nbar = Wrapper(False)\n# bar.name == \'bar\'\n# bar.value == False\n\ndef values_to_dict(*args):\n return {val.name: val.value for val in args}\n\nmydict = values_to_dict(foo, bar)\n# {\'foo\': True, \'bar\': False}\n```\n\n### Creating dictionary using `jsobj`\n\n```python\nfrom varname.helpers import jsobj\n\na = 1\nb = 2\njsobj(a, b) # {\'a\': 1, \'b\': 2}\njsobj(a, b, c=3) # {\'a\': 1, \'b\': 2, \'c\': 3}\n```\n\n### Debugging with `debug`\n\n```python\nfrom varname.helpers import debug\n\na = \'value\'\nb = [\'val\']\ndebug(a)\n# "DEBUG: a=\'value\'\\n"\ndebug(b)\n# "DEBUG: b=[\'val\']\\n"\ndebug(a, b)\n# "DEBUG: a=\'value\'\\nDEBUG: b=[\'val\']\\n"\ndebug(a, b, merge=True)\n# "DEBUG: a=\'value\', b=[\'val\']\\n"\ndebug(a, repr=False, prefix=\'\')\n# \'a=value\\n\'\n# also debug an expression\ndebug(a+a)\n# "DEBUG: a+a=\'valuevalue\'\\n"\n# If you want to disable it:\ndebug(a+a, vars_only=True) # ImproperUseError\n```\n\n### Replacing `exec` with `exec_code`\n\n```python\nfrom varname import argname\nfrom varname.helpers import exec_code\n\nclass Obj:\n def __init__(self):\n self.argnames = []\n\n def receive(self, arg):\n self.argnames.append(argname(\'arg\', func=self.receive))\n\nobj = Obj()\n# exec(\'obj.receive(1)\') # Error\nexec_code(\'obj.receive(1)\')\nexec_code(\'obj.receive(2)\')\nobj.argnames # [\'1\', \'2\']\n```\n\n## Reliability and limitations\n\n`varname` is all depending on `executing` package to look for the node.\nThe node `executing` detects is ensured to be the correct one (see [this][19]).\n\nIt partially works with environments where other AST magics apply, including\n`pytest`, `ipython`, `macropy`, `birdseye`, `reticulate` with `R`, etc. Neither\n`executing` nor `varname` is 100% working with those environments. Use\nit at your own risk.\n\nFor example:\n\n- This will not work with `pytest`:\n\n ```python\n a = 1\n assert nameof(a) == \'a\' # pytest manipulated the ast here\n\n # do this instead\n name_a = nameof(a)\n assert name_a == \'a\'\n ```\n\n[1]: https://github.com/pwwang/python-varname\n[2]: https://github.com/HanyuuLu\n[3]: https://img.shields.io/pypi/v/varname?style=flat-square\n[4]: https://pypi.org/project/varname/\n[5]: https://img.shields.io/github/tag/pwwang/python-varname?style=flat-square\n[6]: https://github.com/pwwang/python-varname\n[7]: logo.png\n[8]: https://img.shields.io/pypi/pyversions/varname?style=flat-square\n[9]: https://img.shields.io/github/actions/workflow/status/pwwang/python-varname/docs.yml?branch=master\n[10]: https://img.shields.io/github/actions/workflow/status/pwwang/python-varname/build.yml?branch=master\n[11]: https://mybinder.org/v2/gh/pwwang/python-varname/dev?filepath=playground%2Fplayground.ipynb\n[12]: https://img.shields.io/codacy/grade/6fdb19c845f74c5c92056e88d44154f7?style=flat-square\n[13]: https://app.codacy.com/gh/pwwang/python-varname/dashboard\n[14]: https://img.shields.io/codacy/coverage/6fdb19c845f74c5c92056e88d44154f7?style=flat-square\n[15]: https://pwwang.github.io/python-varname/api/varname\n[16]: https://pwwang.github.io/python-varname/CHANGELOG/\n[17]: https://img.shields.io/pypi/dm/varname?style=flat-square\n[19]: https://github.com/alexmojaki/executing#is-it-reliable\n[20]: https://stackoverflow.com/a/59364138/5088165\n',
|
21
|
+
'author': 'pwwang',
|
22
|
+
'author_email': 'pwwang@pwwang.com',
|
23
|
+
'maintainer': 'None',
|
24
|
+
'maintainer_email': 'None',
|
25
|
+
'url': 'https://github.com/pwwang/python-varname',
|
26
|
+
'packages': packages,
|
27
|
+
'package_data': package_data,
|
28
|
+
'install_requires': install_requires,
|
29
|
+
'extras_require': extras_require,
|
30
|
+
'python_requires': '>=3.8,<4.0',
|
31
|
+
}
|
32
|
+
|
33
|
+
|
34
|
+
setup(**setup_kwargs)
|
@@ -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)
|
@@ -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(
|
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,
|
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"
|
204
|
-
|
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
|
|
varname-0.12.1/setup.py
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
from setuptools import setup
|
3
|
-
|
4
|
-
packages = \
|
5
|
-
['varname']
|
6
|
-
|
7
|
-
package_data = \
|
8
|
-
{'': ['*']}
|
9
|
-
|
10
|
-
install_requires = \
|
11
|
-
['executing>=2.0,<3.0']
|
12
|
-
|
13
|
-
extras_require = \
|
14
|
-
{'all': ['asttokens==2.*', 'pure_eval==0.*']}
|
15
|
-
|
16
|
-
setup_kwargs = {
|
17
|
-
'name': 'varname',
|
18
|
-
'version': '0.12.1',
|
19
|
-
'description': 'Dark magics about variable names in python.',
|
20
|
-
'long_description': '![varname][7]\n\n[![Pypi][3]][4] [![Github][5]][6] [![PythonVers][8]][4] ![Building][10]\n[![Docs and API][9]][15] [![Codacy][12]][13] [![Codacy coverage][14]][13]\n![Downloads][17]\n\nDark magics about variable names in python\n\n[CHANGELOG][16] | [API][15] | [Playground][11] | :fire: [StackOverflow answer][20]\n\n## Installation\n\n```shell\npip install -U varname\n```\n\nNote if you use `python < 3.8`, install `varname < 0.11`\n\n## Features\n\n- Core features:\n\n - Retrieving names of variables a function/class call is assigned to from inside it, using `varname`.\n - Retrieving variable names directly, using `nameof`\n - Detecting next immediate attribute name, using `will`\n - Fetching argument names/sources passed to a function using `argname`\n\n- Other helper APIs (built based on core features):\n\n - A value wrapper to store the variable name that a value is assigned to, using `Wrapper`\n - A decorator to register `__varname__` to functions/classes, using `register`\n - A helper function to create dict without explicitly specifying the key-value pairs, using `jsobj`\n - A `debug` function to print variables with their names and values\n\n## Credits\n\nThanks goes to these awesome people/projects:\n\n<table>\n <tr>\n <td align="center" style="min-width: 75px">\n <a href="https://github.com/alexmojaki/executing">\n <img src="https://ui-avatars.com/api/?color=3333ff&background=ffffff&bold=true&name=e&size=400" width="50px;" alt=""/>\n <br /><sub><b>executing</b></sub>\n </a>\n </td>\n <td align="center" style="min-width: 75px">\n <a href="https://github.com/alexmojaki">\n <img src="https://avatars0.githubusercontent.com/u/3627481?s=400&v=4" width="50px;" alt=""/>\n <br /><sub><b>@alexmojaki</b></sub>\n </a>\n </td>\n <td align="center" style="min-width: 75px">\n <a href="https://github.com/breuleux">\n <img src="https://avatars.githubusercontent.com/u/599820?s=400&v=4" width="50px;" alt=""/>\n <br /><sub><b>@breuleux</b></sub>\n </a>\n </td>\n <td align="center" style="min-width: 75px">\n <a href="https://github.com/ElCuboNegro">\n <img src="https://avatars.githubusercontent.com/u/5524219?s=400&v=4" width="50px;" alt=""/>\n <br /><sub><b>@ElCuboNegro</b></sub>\n </a>\n </td>\n <td align="center" style="min-width: 75px">\n <a href="https://github.com/thewchan">\n <img src="https://avatars.githubusercontent.com/u/49702524?s=400&v=4" width="50px;" alt=""/>\n <br /><sub><b>@thewchan</b></sub>\n </a>\n </td>\n <td align="center" style="min-width: 75px">\n <a href="https://github.com/LawsOfSympathy">\n <img src="https://avatars.githubusercontent.com/u/96355982?s=400&v=4" width="50px;" alt=""/>\n <br /><sub><b>@LawsOfSympathy</b></sub>\n </a>\n </td>\n </tr>\n</table>\n\nSpecial thanks to [@HanyuuLu][2] to give up the name `varname` in pypi for this project.\n\n## Usage\n\n### Retrieving the variable names using `varname(...)`\n\n- From inside a function\n\n ```python\n from varname import varname\n def function():\n return varname()\n\n func = function() # func == \'func\'\n ```\n\n When there are intermediate frames:\n\n ```python\n def wrapped():\n return function()\n\n def function():\n # retrieve the variable name at the 2nd frame from this one\n return varname(frame=2)\n\n func = wrapped() # func == \'func\'\n ```\n\n Or use `ignore` to ignore the wrapped frame:\n\n ```python\n def wrapped():\n return function()\n\n def function():\n return varname(ignore=wrapped)\n\n func = wrapped() # func == \'func\'\n ```\n\n Calls from standard libraries are ignored by default:\n\n ```python\n import asyncio\n\n async def function():\n return varname()\n\n func = asyncio.run(function()) # func == \'func\'\n ```\n\n Use `strict` to control whether the call should be assigned to\n the variable directly:\n\n ```python\n def function(strict):\n return varname(strict=strict)\n\n func = function(True) # OK, direct assignment, func == \'func\'\n\n func = [function(True)] # Not a direct assignment, raises ImproperUseError\n func = [function(False)] # OK, func == [\'func\']\n\n func = function(False), function(False) # OK, func = (\'func\', \'func\')\n ```\n\n- Retrieving name of a class instance\n\n ```python\n class Foo:\n def __init__(self):\n self.id = varname()\n\n def copy(self):\n # also able to fetch inside a method call\n copied = Foo() # copied.id == \'copied\'\n copied.id = varname() # assign id to whatever variable name\n return copied\n\n foo = Foo() # foo.id == \'foo\'\n\n foo2 = foo.copy() # foo2.id == \'foo2\'\n ```\n\n- Multiple variables on Left-hand side\n\n ```python\n # since v0.5.4\n def func():\n return varname(multi_vars=True)\n\n a = func() # a == (\'a\',)\n a, b = func() # (a, b) == (\'a\', \'b\')\n [a, b] = func() # (a, b) == (\'a\', \'b\')\n\n # hierarchy is also possible\n a, (b, c) = func() # (a, b, c) == (\'a\', \'b\', \'c\')\n ```\n\n- Some unusual use\n\n ```python\n def function(**kwargs):\n return varname(strict=False)\n\n func = func1 = function() # func == func1 == \'func1\'\n # if varname < 0.8: func == func1 == \'func\'\n # a warning will be shown\n # since you may not want func to be \'func1\'\n\n x = function(y = function()) # x == \'x\'\n\n # get part of the name\n func_abc = function()[-3:] # func_abc == \'abc\'\n\n # function alias supported now\n function2 = function\n func = function2() # func == \'func\'\n\n a = lambda: 0\n a.b = function() # a.b == \'b\'\n ```\n\n### The decorator way to register `__varname__` to functions/classes\n\n- Registering `__varname__` to functions\n\n ```python\n from varname.helpers import register\n\n @register\n def function():\n return __varname__\n\n func = function() # func == \'func\'\n ```\n\n ```python\n # arguments also allowed (frame, ignore and raise_exc)\n @register(frame=2)\n def function():\n return __varname__\n\n def wrapped():\n return function()\n\n func = wrapped() # func == \'func\'\n ```\n\n- Registering `__varname__` as a class property\n\n ```python\n @register\n class Foo:\n ...\n\n foo = Foo()\n # foo.__varname__ == \'foo\'\n ```\n\n### Getting variable names directly using `nameof`\n\n```python\nfrom varname import varname, nameof\n\na = 1\nnameof(a) # \'a\'\n\nb = 2\nnameof(a, b) # (\'a\', \'b\')\n\ndef func():\n return varname() + \'_suffix\'\n\nf = func() # f == \'f_suffix\'\nnameof(f) # \'f\'\n\n# get full names of (chained) attribute calls\nfunc.a = func\nnameof(func.a, vars_only=False) # \'func.a\'\n\nfunc.a.b = 1\nnameof(func.a.b, vars_only=False) # \'func.a.b\'\n```\n\n### Detecting next immediate attribute name\n\n```python\nfrom varname import will\nclass AwesomeClass:\n def __init__(self):\n self.will = None\n\n def permit(self):\n self.will = will(raise_exc=False)\n if self.will == \'do\':\n # let self handle do\n return self\n raise AttributeError(\'Should do something with AwesomeClass object\')\n\n def do(self):\n if self.will != \'do\':\n raise AttributeError("You don\'t have permission to do")\n return \'I am doing!\'\n\nawesome = AwesomeClass()\nawesome.do() # AttributeError: You don\'t have permission to do\nawesome.permit() # AttributeError: Should do something with AwesomeClass object\nawesome.permit().do() == \'I am doing!\'\n```\n\n### Fetching argument names/sources using `argname`\n\n```python\nfrom varname import argname\n\ndef func(a, b=1):\n print(argname(\'a\'))\n\nx = y = z = 2\nfunc(x) # prints: x\n\n\ndef func2(a, b=1):\n print(argname(\'a\', \'b\'))\nfunc2(y, b=x) # prints: (\'y\', \'x\')\n\n\n# allow expressions\ndef func3(a, b=1):\n print(argname(\'a\', \'b\', vars_only=False))\nfunc3(x+y, y+x) # prints: (\'x+y\', \'y+x\')\n\n\n# positional and keyword arguments\ndef func4(*args, **kwargs):\n print(argname(\'args[1]\', \'kwargs[c]\'))\nfunc4(y, x, c=z) # prints: (\'x\', \'z\')\n\n\n# As of 0.9.0 (see: https://pwwang.github.io/python-varname/CHANGELOG/#v090)\n# Can also fetch the source of the argument for\n# __getattr__/__getitem__/__setattr/__setitem__/__add__/__lt__, etc.\nclass Foo:\n def __setattr__(self, name, value):\n print(argname("name", "value"))\n\nFoo().a = 1 # prints: ("\'a\'", \'1\')\n\n```\n\n### Value wrapper\n\n```python\nfrom varname.helpers import Wrapper\n\nfoo = Wrapper(True)\n# foo.name == \'foo\'\n# foo.value == True\nbar = Wrapper(False)\n# bar.name == \'bar\'\n# bar.value == False\n\ndef values_to_dict(*args):\n return {val.name: val.value for val in args}\n\nmydict = values_to_dict(foo, bar)\n# {\'foo\': True, \'bar\': False}\n```\n\n### Creating dictionary using `jsobj`\n\n```python\nfrom varname.helpers import jsobj\n\na = 1\nb = 2\njsobj(a, b) # {\'a\': 1, \'b\': 2}\njsobj(a, b, c=3) # {\'a\': 1, \'b\': 2, \'c\': 3}\n```\n\n### Debugging with `debug`\n\n```python\nfrom varname.helpers import debug\n\na = \'value\'\nb = [\'val\']\ndebug(a)\n# "DEBUG: a=\'value\'\\n"\ndebug(b)\n# "DEBUG: b=[\'val\']\\n"\ndebug(a, b)\n# "DEBUG: a=\'value\'\\nDEBUG: b=[\'val\']\\n"\ndebug(a, b, merge=True)\n# "DEBUG: a=\'value\', b=[\'val\']\\n"\ndebug(a, repr=False, prefix=\'\')\n# \'a=value\\n\'\n# also debug an expression\ndebug(a+a)\n# "DEBUG: a+a=\'valuevalue\'\\n"\n# If you want to disable it:\ndebug(a+a, vars_only=True) # ImproperUseError\n```\n\n## Reliability and limitations\n\n`varname` is all depending on `executing` package to look for the node.\nThe node `executing` detects is ensured to be the correct one (see [this][19]).\n\nIt partially works with environments where other AST magics apply, including\n`pytest`, `ipython`, `macropy`, `birdseye`, `reticulate` with `R`, etc. Neither\n`executing` nor `varname` is 100% working with those environments. Use\nit at your own risk.\n\nFor example:\n\n- This will not work with `pytest`:\n\n ```python\n a = 1\n assert nameof(a) == \'a\' # pytest manipulated the ast here\n\n # do this instead\n name_a = nameof(a)\n assert name_a == \'a\'\n ```\n\n[1]: https://github.com/pwwang/python-varname\n[2]: https://github.com/HanyuuLu\n[3]: https://img.shields.io/pypi/v/varname?style=flat-square\n[4]: https://pypi.org/project/varname/\n[5]: https://img.shields.io/github/tag/pwwang/python-varname?style=flat-square\n[6]: https://github.com/pwwang/python-varname\n[7]: logo.png\n[8]: https://img.shields.io/pypi/pyversions/varname?style=flat-square\n[9]: https://img.shields.io/github/actions/workflow/status/pwwang/python-varname/docs.yml?branch=master\n[10]: https://img.shields.io/github/actions/workflow/status/pwwang/python-varname/build.yml?branch=master\n[11]: https://mybinder.org/v2/gh/pwwang/python-varname/dev?filepath=playground%2Fplayground.ipynb\n[12]: https://img.shields.io/codacy/grade/6fdb19c845f74c5c92056e88d44154f7?style=flat-square\n[13]: https://app.codacy.com/gh/pwwang/python-varname/dashboard\n[14]: https://img.shields.io/codacy/coverage/6fdb19c845f74c5c92056e88d44154f7?style=flat-square\n[15]: https://pwwang.github.io/python-varname/api/varname\n[16]: https://pwwang.github.io/python-varname/CHANGELOG/\n[17]: https://img.shields.io/pypi/dm/varname?style=flat-square\n[19]: https://github.com/alexmojaki/executing#is-it-reliable\n[20]: https://stackoverflow.com/a/59364138/5088165\n',
|
21
|
-
'author': 'pwwang',
|
22
|
-
'author_email': 'pwwang@pwwang.com',
|
23
|
-
'maintainer': 'None',
|
24
|
-
'maintainer_email': 'None',
|
25
|
-
'url': 'https://github.com/pwwang/python-varname',
|
26
|
-
'packages': packages,
|
27
|
-
'package_data': package_data,
|
28
|
-
'install_requires': install_requires,
|
29
|
-
'extras_require': extras_require,
|
30
|
-
'python_requires': '>=3.8,<4.0',
|
31
|
-
}
|
32
|
-
|
33
|
-
|
34
|
-
setup(**setup_kwargs)
|
File without changes
|
File without changes
|
File without changes
|