varname 0.13.4__tar.gz → 0.14.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,7 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: varname
3
- Version: 0.13.4
3
+ Version: 0.14.0
4
4
  Summary: Dark magics about variable names in python.
5
- Home-page: https://github.com/pwwang/python-varname
6
5
  License: MIT
7
6
  Author: pwwang
8
7
  Author-email: pwwang@pwwang.com
@@ -14,10 +13,12 @@ Classifier: Programming Language :: Python :: 3.9
14
13
  Classifier: Programming Language :: Python :: 3.10
15
14
  Classifier: Programming Language :: Python :: 3.11
16
15
  Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
17
  Provides-Extra: all
18
- Requires-Dist: asttokens (==2.*) ; extra == "all"
19
- Requires-Dist: executing (>=2.0,<3.0)
18
+ Requires-Dist: asttokens (==3.*) ; extra == "all"
19
+ Requires-Dist: executing (>=2.1,<3.0)
20
20
  Requires-Dist: pure_eval (==0.*) ; extra == "all"
21
+ Project-URL: Homepage, https://github.com/pwwang/python-varname
21
22
  Project-URL: Repository, https://github.com/pwwang/python-varname
22
23
  Description-Content-Type: text/markdown
23
24
 
@@ -44,7 +45,6 @@ Note if you use `python < 3.8`, install `varname < 0.11`
44
45
  - Core features:
45
46
 
46
47
  - Retrieving names of variables a function/class call is assigned to from inside it, using `varname`.
47
- - Retrieving variable names directly, using `nameof`
48
48
  - Detecting next immediate attribute name, using `will`
49
49
  - Fetching argument names/sources passed to a function using `argname`
50
50
 
@@ -268,31 +268,6 @@ Special thanks to [@HanyuuLu][2] to give up the name `varname` in pypi for this
268
268
  # foo.__varname__ == 'foo'
269
269
  ```
270
270
 
271
- ### Getting variable names directly using `nameof`
272
-
273
- ```python
274
- from varname import varname, nameof
275
-
276
- a = 1
277
- nameof(a) # 'a'
278
-
279
- b = 2
280
- nameof(a, b) # ('a', 'b')
281
-
282
- def func():
283
- return varname() + '_suffix'
284
-
285
- f = func() # f == 'f_suffix'
286
- nameof(f) # 'f'
287
-
288
- # get full names of (chained) attribute calls
289
- func.a = func
290
- nameof(func.a, vars_only=False) # 'func.a'
291
-
292
- func.a.b = 1
293
- nameof(func.a.b, vars_only=False) # 'func.a.b'
294
- ```
295
-
296
271
  ### Detecting next immediate attribute name
297
272
 
298
273
  ```python
@@ -438,22 +413,27 @@ obj.argnames # ['1', '2']
438
413
  `varname` is all depending on `executing` package to look for the node.
439
414
  The node `executing` detects is ensured to be the correct one (see [this][19]).
440
415
 
441
- It partially works with environments where other AST magics apply, including
442
- `pytest`, `ipython`, `macropy`, `birdseye`, `reticulate` with `R`, etc. Neither
416
+ It partially works with environments where other AST magics apply, including [`exec`][24] function,
417
+ [`macropy`][21], [`birdseye`][22], [`reticulate`][23] with `R`, etc. Neither
443
418
  `executing` nor `varname` is 100% working with those environments. Use
444
419
  it at your own risk.
445
420
 
446
421
  For example:
447
422
 
448
- - This will not work with `pytest`:
423
+ - This will not work:
449
424
 
450
425
  ```python
426
+ from varname import argname
427
+
428
+ def getname(x):
429
+ print(argname("x"))
430
+
451
431
  a = 1
452
- assert nameof(a) == 'a' # pytest manipulated the ast here
432
+ exec("getname(a)") # Cannot retrieve the node where the function is called.
453
433
 
454
- # do this instead
455
- name_a = nameof(a)
456
- assert name_a == 'a'
434
+ ## instead
435
+ # from varname.helpers import exec_code
436
+ # exec_code("getname(a)")
457
437
  ```
458
438
 
459
439
  [1]: https://github.com/pwwang/python-varname
@@ -475,4 +455,8 @@ For example:
475
455
  [17]: https://img.shields.io/pypi/dm/varname?style=flat-square
476
456
  [19]: https://github.com/alexmojaki/executing#is-it-reliable
477
457
  [20]: https://stackoverflow.com/a/59364138/5088165
458
+ [21]: https://github.com/lihaoyi/macropy
459
+ [22]: https://github.com/alexmojaki/birdseye
460
+ [23]: https://rstudio.github.io/reticulate/
461
+ [24]: https://docs.python.org/3/library/functions.html#exec
478
462
 
@@ -21,7 +21,6 @@ Note if you use `python < 3.8`, install `varname < 0.11`
21
21
  - Core features:
22
22
 
23
23
  - Retrieving names of variables a function/class call is assigned to from inside it, using `varname`.
24
- - Retrieving variable names directly, using `nameof`
25
24
  - Detecting next immediate attribute name, using `will`
26
25
  - Fetching argument names/sources passed to a function using `argname`
27
26
 
@@ -245,31 +244,6 @@ Special thanks to [@HanyuuLu][2] to give up the name `varname` in pypi for this
245
244
  # foo.__varname__ == 'foo'
246
245
  ```
247
246
 
248
- ### Getting variable names directly using `nameof`
249
-
250
- ```python
251
- from varname import varname, nameof
252
-
253
- a = 1
254
- nameof(a) # 'a'
255
-
256
- b = 2
257
- nameof(a, b) # ('a', 'b')
258
-
259
- def func():
260
- return varname() + '_suffix'
261
-
262
- f = func() # f == 'f_suffix'
263
- nameof(f) # 'f'
264
-
265
- # get full names of (chained) attribute calls
266
- func.a = func
267
- nameof(func.a, vars_only=False) # 'func.a'
268
-
269
- func.a.b = 1
270
- nameof(func.a.b, vars_only=False) # 'func.a.b'
271
- ```
272
-
273
247
  ### Detecting next immediate attribute name
274
248
 
275
249
  ```python
@@ -415,22 +389,27 @@ obj.argnames # ['1', '2']
415
389
  `varname` is all depending on `executing` package to look for the node.
416
390
  The node `executing` detects is ensured to be the correct one (see [this][19]).
417
391
 
418
- It partially works with environments where other AST magics apply, including
419
- `pytest`, `ipython`, `macropy`, `birdseye`, `reticulate` with `R`, etc. Neither
392
+ It partially works with environments where other AST magics apply, including [`exec`][24] function,
393
+ [`macropy`][21], [`birdseye`][22], [`reticulate`][23] with `R`, etc. Neither
420
394
  `executing` nor `varname` is 100% working with those environments. Use
421
395
  it at your own risk.
422
396
 
423
397
  For example:
424
398
 
425
- - This will not work with `pytest`:
399
+ - This will not work:
426
400
 
427
401
  ```python
402
+ from varname import argname
403
+
404
+ def getname(x):
405
+ print(argname("x"))
406
+
428
407
  a = 1
429
- assert nameof(a) == 'a' # pytest manipulated the ast here
408
+ exec("getname(a)") # Cannot retrieve the node where the function is called.
430
409
 
431
- # do this instead
432
- name_a = nameof(a)
433
- assert name_a == 'a'
410
+ ## instead
411
+ # from varname.helpers import exec_code
412
+ # exec_code("getname(a)")
434
413
  ```
435
414
 
436
415
  [1]: https://github.com/pwwang/python-varname
@@ -452,3 +431,7 @@ For example:
452
431
  [17]: https://img.shields.io/pypi/dm/varname?style=flat-square
453
432
  [19]: https://github.com/alexmojaki/executing#is-it-reliable
454
433
  [20]: https://stackoverflow.com/a/59364138/5088165
434
+ [21]: https://github.com/lihaoyi/macropy
435
+ [22]: https://github.com/alexmojaki/birdseye
436
+ [23]: https://rstudio.github.io/reticulate/
437
+ [24]: https://docs.python.org/3/library/functions.html#exec
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "varname"
7
- version = "0.13.4"
7
+ version = "0.14.0"
8
8
  description = "Dark magics about variable names in python."
9
9
  authors = [ "pwwang <pwwang@pwwang.com>",]
10
10
  license = "MIT"
@@ -17,14 +17,15 @@ generate-setup-file = true
17
17
 
18
18
  [tool.poetry.dependencies]
19
19
  python = "^3.8"
20
- executing = "^2.0"
21
- asttokens = { version = "2.*", optional = true }
20
+ executing = "^2.1"
21
+ asttokens = { version = "3.*", optional = true }
22
22
  pure_eval = { version = "0.*", optional = true }
23
23
 
24
24
  [tool.poetry.dev-dependencies]
25
25
  pytest = "^8"
26
26
  pytest-cov = "^5"
27
27
  coverage = { version = "^7", extras = ["toml"] }
28
+ ipykernel = "^6.29.5"
28
29
 
29
30
  [tool.poetry.extras]
30
31
  all = ["asttokens", "pure_eval"]
@@ -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.1,<3.0']
12
+
13
+ extras_require = \
14
+ {'all': ['asttokens==3.*', 'pure_eval==0.*']}
15
+
16
+ setup_kwargs = {
17
+ 'name': 'varname',
18
+ 'version': '0.14.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 - 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 <td align="center" style="min-width: 75px">\n <a href="https://github.com/elliotgunton">\n <img src="https://avatars.githubusercontent.com/u/17798778?s=400&v=4" width="50px;" alt=""/>\n <br /><sub><b>@elliotgunton</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### 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 [`exec`][24] function,\n[`macropy`][21], [`birdseye`][22], [`reticulate`][23] 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:\n\n ```python\n from varname import argname\n\n def getname(x):\n print(argname("x"))\n\n a = 1\n exec("getname(a)") # Cannot retrieve the node where the function is called.\n\n ## instead\n # from varname.helpers import exec_code\n # exec_code("getname(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]: https://github.com/lihaoyi/macropy\n[22]: https://github.com/alexmojaki/birdseye\n[23]: https://rstudio.github.io/reticulate/\n[24]: https://docs.python.org/3/library/functions.html#exec\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)
@@ -11,6 +11,6 @@ from .utils import (
11
11
  MaybeDecoratedFunctionWarning,
12
12
  UsingExecWarning,
13
13
  )
14
- from .core import varname, nameof, will, argname
14
+ from .core import varname, will, argname
15
15
 
16
- __version__ = "0.13.4"
16
+ __version__ = "0.14.0"
@@ -1,13 +1,13 @@
1
1
  """Provide core features for varname"""
2
+ from __future__ import annotations
2
3
  import ast
3
4
  import re
4
5
  import warnings
5
- from typing import Any, List, Union, Tuple, Type, Callable, overload
6
+ from typing import List, Union, Tuple, Type, Callable, overload
6
7
 
7
8
  from executing import Source
8
9
 
9
10
  from .utils import (
10
- bytecode_nameof,
11
11
  get_node,
12
12
  get_node_by_frame,
13
13
  lookfor_parent_assign,
@@ -217,134 +217,6 @@ def will(frame: int = 1, raise_exc: bool = True) -> str:
217
217
  return node.attr
218
218
 
219
219
 
220
- @overload
221
- def nameof(
222
- var: Any,
223
- *,
224
- frame: int = 1,
225
- vars_only: bool = True,
226
- ) -> str: # pragma: no cover
227
- ...
228
-
229
-
230
- @overload
231
- def nameof(
232
- var: Any,
233
- more_var: Any,
234
- /, # introduced in python 3.8
235
- *more_vars: Any,
236
- frame: int = 1,
237
- vars_only: bool = True,
238
- ) -> Tuple[str, ...]: # pragma: no cover
239
- ...
240
-
241
-
242
- def nameof(
243
- var: Any,
244
- *more_vars: Any,
245
- frame: int = 1,
246
- vars_only: bool = True,
247
- ) -> Union[str, Tuple[str, ...]]:
248
- """Get the names of the variables passed in
249
-
250
- Examples:
251
- >>> a = 1
252
- >>> nameof(a) # 'a'
253
-
254
- >>> b = 2
255
- >>> nameof(a, b) # ('a', 'b')
256
-
257
- >>> x = lambda: None
258
- >>> x.y = 1
259
- >>> nameof(x.y, vars_only=False) # 'x.y'
260
-
261
- Note:
262
- This function works with the environments where source code is
263
- available, in other words, the callee's node can be retrieved by
264
- `executing`. In some cases, for example, running code from python
265
- shell/REPL or from `exec`/`eval`, we try to fetch the variable name
266
- from the bytecode. This requires only a single variable name is passed
267
- to this function and no keyword arguments, meaning that getting full
268
- names of attribute calls are not supported in such cases.
269
-
270
- Args:
271
- var: The variable to retrieve the name of
272
- *more_vars: Other variables to retrieve the names of
273
- frame: The this function is called from the wrapper of it. `frame=1`
274
- means no wrappers.
275
- Note that the calls from standard libraries are ignored.
276
- Also note that the wrapper has to have signature as this one.
277
- vars_only: Whether only allow variables/attributes as arguments or
278
- any expressions. If `False`, then the sources of the arguments
279
- will be returned.
280
-
281
- Returns:
282
- The names/sources of variables/expressions passed in.
283
- If a single argument is passed, return the name/source of it.
284
- If multiple variables are passed, return a tuple of their
285
- names/sources.
286
- If the argument is an attribute (e.g. `a.b`) and `vars_only` is
287
- `True`, only `"b"` will returned. Set `vars_only` to `False` to
288
- get `"a.b"`.
289
-
290
- Raises:
291
- VarnameRetrievingError: When the callee's node cannot be retrieved or
292
- trying to retrieve the full name of non attribute series calls.
293
- """
294
- warnings.warn(
295
- "`nameof` is deprecated and will be removed in the future. "
296
- "Please use `argname` instead.",
297
- DeprecationWarning,
298
- )
299
- # Frame is anyway used in get_node
300
- frameobj = IgnoreList.create(
301
- ignore_lambda=False,
302
- ignore_varname=False,
303
- ).get_frame(frame)
304
-
305
- node = get_node_by_frame(frameobj, raise_exc=True)
306
- if not node:
307
- # We can't retrieve the node by executing.
308
- # It can be due to running code from python/shell, exec/eval or
309
- # other environments where sourcecode cannot be reached
310
- # make sure we keep it simple (only single variable passed and no
311
- # full passed) to use bytecode_nameof
312
- #
313
- # We don't have to check keyword arguments here, as the instruction
314
- # will then be CALL_FUNCTION_KW.
315
- if not more_vars:
316
- return bytecode_nameof(frameobj.f_code, frameobj.f_lasti)
317
-
318
- # We are anyway raising exceptions, no worries about additional burden
319
- # of frame retrieval again
320
- source = frameobj.f_code.co_filename
321
- if source == "<stdin>":
322
- raise VarnameRetrievingError(
323
- "Are you trying to call nameof in REPL/python shell? "
324
- "In such a case, nameof can only be called with single "
325
- "argument and no keyword arguments."
326
- )
327
- if source == "<string>":
328
- raise VarnameRetrievingError(
329
- "Are you trying to call nameof from exec/eval? "
330
- "In such a case, nameof can only be called with single "
331
- "argument and no keyword arguments."
332
- )
333
- raise VarnameRetrievingError(
334
- "Source code unavailable, nameof can only retrieve the name of "
335
- "a single variable, and argument `full` should not be specified."
336
- )
337
-
338
- out = argname(
339
- "var",
340
- "*more_vars",
341
- func=nameof,
342
- frame=frame,
343
- vars_only=vars_only,
344
- )
345
- return out if more_vars else out[0] # type: ignore
346
-
347
-
348
220
  @overload
349
221
  def argname(
350
222
  arg: str,
@@ -10,7 +10,6 @@ Attributes:
10
10
  `inspect.getmodule(frame)`
11
11
  """
12
12
  import sys
13
- import dis
14
13
  import ast
15
14
  import warnings
16
15
  import inspect
@@ -18,7 +17,7 @@ from os import path
18
17
  from pathlib import Path
19
18
  from functools import lru_cache, singledispatch
20
19
  from types import ModuleType, FunctionType, CodeType, FrameType
21
- from typing import Tuple, Union, List, Mapping, Callable, Dict
20
+ from typing import Tuple, Union, List, Mapping, Callable
22
21
 
23
22
  from executing import Source
24
23
 
@@ -163,11 +162,11 @@ def get_node_by_frame(frame: FrameType, raise_exc: bool = True) -> ast.AST:
163
162
  exect.node.__frame__ = frame
164
163
  return exect.node
165
164
 
166
- if exect.source.text and exect.source.tree and raise_exc:
165
+ if exect.source.text and exect.source.tree and raise_exc: # pragma: no cover
167
166
  raise VarnameRetrievingError(
168
167
  "Couldn't retrieve the call node. "
169
168
  "This may happen if you're using some other AST magic at the "
170
- "same time, such as pytest, ipython, macropy, or birdseye."
169
+ "same time, such as pytest, macropy, or birdseye."
171
170
  )
172
171
 
173
172
  return None
@@ -244,71 +243,6 @@ def node_name(
244
243
  )
245
244
 
246
245
 
247
- @lru_cache()
248
- def bytecode_nameof(code: CodeType, offset: int) -> str:
249
- """Cached Bytecode version of nameof
250
-
251
- We are trying this version only when the sourcecode is unavisible. In most
252
- cases, this will happen when user is trying to run a script in REPL/
253
- python shell, with `eval`, or other circumstances where the code is
254
- manipulated to run but sourcecode is not available.
255
- """
256
- kwargs: Dict[str, bool] = (
257
- {"show_caches": True} if sys.version_info[:2] >= (3, 11) else {}
258
- )
259
-
260
- instructions = list(dis.get_instructions(code, **kwargs))
261
- ((current_instruction_index, current_instruction),) = (
262
- (index, instruction)
263
- for index, instruction in enumerate(instructions)
264
- if instruction.offset == offset
265
- )
266
-
267
- while current_instruction.opname == "CACHE": # pragma: no cover
268
- current_instruction_index -= 1
269
- current_instruction = instructions[current_instruction_index]
270
-
271
- pos_only_error = VarnameRetrievingError(
272
- "'nameof' can only be called with a single positional argument "
273
- "when source code is not avaiable."
274
- )
275
- if current_instruction.opname in ( # pragma: no cover
276
- "CALL_FUNCTION_EX",
277
- "CALL_FUNCTION_KW",
278
- ):
279
- raise pos_only_error
280
-
281
- if current_instruction.opname not in (
282
- "CALL_FUNCTION",
283
- "CALL_METHOD",
284
- "CALL",
285
- "CALL_KW",
286
- ):
287
- raise VarnameRetrievingError("Did you call 'nameof' in a weird way?")
288
-
289
- current_instruction_index -= 1
290
- name_instruction = instructions[current_instruction_index]
291
- while name_instruction.opname in ("CACHE", "PRECALL"): # pragma: no cover
292
- current_instruction_index -= 1
293
- name_instruction = instructions[current_instruction_index]
294
-
295
- if name_instruction.opname in ("KW_NAMES", "LOAD_CONST"): # LOAD_CONST python 3.13
296
- raise pos_only_error
297
-
298
- if not name_instruction.opname.startswith("LOAD_"):
299
- raise VarnameRetrievingError("Argument must be a variable or attribute")
300
-
301
- name = name_instruction.argrepr
302
- if not name.isidentifier():
303
- raise VarnameRetrievingError(
304
- f"Found the variable name {name!r} which is obviously wrong. "
305
- "This may happen if you're using some other AST magic at the "
306
- "same time, such as pytest, ipython, macropy, or birdseye."
307
- )
308
-
309
- return name
310
-
311
-
312
246
  def attach_ignore_id_to_module(module: ModuleType) -> None:
313
247
  """Attach the ignore id to module
314
248
 
@@ -550,7 +484,7 @@ def _(node: Union[ast.Attribute, ast.Subscript]) -> ast.Call:
550
484
  args=[keynode],
551
485
  keywords=[],
552
486
  )
553
- else:
487
+ else: # pragma: no cover
554
488
  return ast.Call( # type: ignore
555
489
  func=ast.Attribute(
556
490
  value=node.value,
@@ -597,7 +531,7 @@ def _(node: Union[ast.Attribute, ast.Subscript]) -> ast.Call:
597
531
  args=[keynode, node.parent.value], # type: ignore
598
532
  keywords=[],
599
533
  )
600
- else:
534
+ else: # pragma: no cover
601
535
  return ast.Call(
602
536
  func=ast.Attribute(
603
537
  value=node.value,
@@ -638,7 +572,7 @@ def _(node: ast.Compare) -> ast.Call:
638
572
  args=[node.comparators[0]],
639
573
  keywords=[],
640
574
  )
641
- else:
575
+ else: # pragma: no cover
642
576
  return ast.Call( # type: ignore
643
577
  func=ast.Attribute(
644
578
  value=node.left,
@@ -672,7 +606,7 @@ def _(node: ast.BinOp) -> ast.Call:
672
606
  args=[node.right],
673
607
  keywords=[],
674
608
  )
675
- else:
609
+ else: # pragma: no cover
676
610
  return ast.Call( # type: ignore
677
611
  func=ast.Attribute(
678
612
  value=node.left,
varname-0.13.4/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.13.4',
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 <td align="center" style="min-width: 75px">\n <a href="https://github.com/elliotgunton">\n <img src="https://avatars.githubusercontent.com/u/17798778?s=400&v=4" width="50px;" alt=""/>\n <br /><sub><b>@elliotgunton</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)
File without changes
File without changes
File without changes
File without changes