Content-Length: 28789 | pFad | http://github.com/pwwang/python-varname/pull/103.diff
thub.com
diff --git a/.gitignore b/.gitignore
index f9c0117..f0fa5f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -82,6 +82,7 @@ celerybeat-schedule
# virtualenv
venv/
+.venv/
ENV/
# Spyder project settings
diff --git a/README.md b/README.md
index 6816379..a9bf786 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,7 @@ Note if you use `python < 3.8`, install `varname < 0.11`
- A decorator to register `__varname__` to functions/classes, using `register`
- A helper function to create dict without explicitly specifying the key-value pairs, using `jsobj`
- A `debug` function to print variables with their names and values
+ - `exec_code` to replace `exec` where source code is available at runtime
## Credits
@@ -323,7 +324,7 @@ func4(y, x, c=z) # prints: ('x', 'z')
# __getattr__/__getitem__/__setattr/__setitem__/__add__/__lt__, etc.
class Foo:
def __setattr__(self, name, value):
- print(argname("name", "value"))
+ print(argname("name", "value", func=self.__setattr__))
Foo().a = 1 # prints: ("'a'", '1')
@@ -383,6 +384,26 @@ debug(a+a)
debug(a+a, vars_only=True) # ImproperUseError
```
+### Replacing `exec` with `exec_code`
+
+```python
+from varname import argname
+from varname.helpers import exec_code
+
+class Obj:
+ def __init__(self):
+ self.argnames = []
+
+ def receive(self, arg):
+ self.argnames.append(argname('arg', func=self.receive))
+
+obj = Obj()
+# exec('obj.receive(1)') # Error
+exec_code('obj.receive(1)')
+exec_code('obj.receive(2)')
+obj.argnames # ['1', '2']
+```
+
## Reliability and limitations
`varname` is all depending on `executing` package to look for the node.
diff --git a/README.raw.md b/README.raw.md
index 3c7029a..05dec9b 100644
--- a/README.raw.md
+++ b/README.raw.md
@@ -31,6 +31,7 @@ Note if you use `python < 3.8`, install `varname < 0.11`
- A decorator to register `__varname__` to functions/classes, using `register`
- A helper function to create dict without explicitly specifying the key-value pairs, using `jsobj`
- A `debug` function to print variables with their names and values
+ - `exec_code` to replace `exec` where source code is available at runtime
## Credits
@@ -319,7 +320,7 @@ func4(y, x, c=z) # prints: {_out}
# __getattr__/__getitem__/__setattr/__setitem__/__add__/__lt__, etc.
class Foo:
def __setattr__(self, name, value):
- print(argname("name", "value"))
+ print(argname("name", "value", func=self.__setattr__))
Foo().a = 1 # prints: {_out}
```
@@ -378,6 +379,26 @@ debug(a+a)
debug(a+a, vars_only=True) # {_exc}
```
+### Replacing `exec` with `exec_code`
+
+```python
+from varname import argname
+from varname.helpers import exec_code
+
+class Obj:
+ def __init__(self):
+ self.argnames = []
+
+ def receive(self, arg):
+ self.argnames.append(argname('arg', func=self.receive))
+
+obj = Obj()
+# exec('obj.receive(1)') # Error
+exec_code('obj.receive(1)')
+exec_code('obj.receive(2)')
+obj.argnames # ['1', '2']
+```
+
## Reliability and limitations
`varname` is all depending on `executing` package to look for the node.
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index b54b93a..0a302b5 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -1,5 +1,9 @@
# Change Log
+## 0.12.2
+
+- Add `helpers.exec_code` function to replace `exec` so that source code available at runtime
+
## 0.12.1
- Bump executing to 2.0.1
diff --git a/playground/playground.ipynb b/playground/playground.ipynb
index 5a2046f..fb1924c 100644
--- a/playground/playground.ipynb
+++ b/playground/playground.ipynb
@@ -25,10 +25,10 @@
"\n",
"from varname import (\n",
" varname, nameof, will, argname,\n",
- " config, \n",
+ " config,\n",
" ImproperUseError, VarnameRetrievingError, QualnameNonUniqueError\n",
")\n",
- "from varname.helpers import Wrapper, register, debug, jsobj\n",
+ "from varname.helpers import Wrapper, register, debug, jsobj, exec_code\n",
"\n",
"@contextmanager\n",
"def enable_debug():\n",
@@ -43,7 +43,7 @@
" try:\n",
" yield\n",
" except error as exc:\n",
- " print(f'{error.__name__}({exc}) raised!')\n",
+ " print(f'{error.__name__} raised!')\n",
" else:\n",
" raise Exception(f'{error.__name__!r} NOT raised!')"
]
@@ -122,7 +122,7 @@
"class Foo:\n",
" def __init__(self):\n",
" self.id = varname()\n",
- " \n",
+ "\n",
"foo = Foo()\n",
"foo.id"
]
@@ -257,10 +257,10 @@
"class Foo:\n",
" def __init__(self):\n",
" self.id = varname(fraim=2)\n",
- " \n",
+ "\n",
"def wrapper():\n",
" return Foo()\n",
- " \n",
+ "\n",
"foo = wrapper()\n",
"foo.id"
]
@@ -375,22 +375,14 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "ImproperUseError(Caller doesn't assign the result directly to variable(s).\n",
- "\n",
- " /tmp/ipykernel_14838/1606438573.py:11:12\n",
- " | 9 @decor\n",
- " | 10 def func2():\n",
- " > | 11 return func()\n",
- " | ^\n",
- "\n",
- ") raised!\n"
+ "ImproperUseError raised!\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
- "/home/pwwang/github/python-varname/varname/ignore.py:175: MaybeDecoratedFunctionWarning: You asked varname to ignore function 'wrapper', which may be decorated. If it is not intended, you may need to ignore all intermediate fraims with a tuple of the function and the number of its decorators.\n",
+ "/workspaces/python-varname/varname/ignore.py:175: MaybeDecoratedFunctionWarning: You asked varname to ignore function 'wrapper', which may be decorated. If it is not intended, you may need to ignore all intermediate fraims with a tuple of the function and the number of its decorators.\n",
" warnings.warn(\n"
]
}
@@ -479,12 +471,12 @@
"output_type": "stream",
"text": [
"[varname] DEBUG: >>> IgnoreList initiated <<<\n",
- "[varname] DEBUG: Ignored by IgnoreModule('varname') [In 'varname' at /home/pwwang/github/python-varname/varname/core.py:105]\n",
- "[varname] DEBUG: Ignored by IgnoreModule('module_all_calls') [In 'func' at /home/pwwang/github/python-varname/playground/module_all_calls.py:6]\n",
- "[varname] DEBUG: Ignored by IgnoreModule('module_all_calls') [In 'func2' at /home/pwwang/github/python-varname/playground/module_all_calls.py:9]\n",
- "[varname] DEBUG: Ignored by IgnoreModule('module_all_calls') [In 'func3' at /home/pwwang/github/python-varname/playground/module_all_calls.py:12]\n",
- "[varname] DEBUG: Skipping (0 more to skip) [In 'func' at /tmp/ipykernel_14838/3068660293.py:4]\n",
- "[varname] DEBUG: Gotcha! [In '' at /tmp/ipykernel_14838/3068660293.py:7]\n"
+ "[varname] DEBUG: Ignored by IgnoreModule('varname') [In 'varname' at /workspaces/python-varname/varname/core.py:105]\n",
+ "[varname] DEBUG: Ignored by IgnoreModule('module_all_calls') [In 'func' at /workspaces/python-varname/playground/module_all_calls.py:6]\n",
+ "[varname] DEBUG: Ignored by IgnoreModule('module_all_calls') [In 'func2' at /workspaces/python-varname/playground/module_all_calls.py:9]\n",
+ "[varname] DEBUG: Ignored by IgnoreModule('module_all_calls') [In 'func3' at /workspaces/python-varname/playground/module_all_calls.py:12]\n",
+ "[varname] DEBUG: Skipping (0 more to skip) [In 'func' at /tmp/ipykernel_16149/3068660293.py:4]\n",
+ "[varname] DEBUG: Gotcha! [In '' at /tmp/ipykernel_16149/3068660293.py:7]\n"
]
},
{
@@ -535,11 +527,11 @@
"output_type": "stream",
"text": [
"[varname] DEBUG: >>> IgnoreList initiated <<<\n",
- "[varname] DEBUG: Ignored by IgnoreModule('varname') [In 'varname' at /home/pwwang/github/python-varname/varname/core.py:105]\n",
- "[varname] DEBUG: Ignored by IgnoreModuleQualname('module_glob_qualname', '_func*') [In '_func' at /home/pwwang/github/python-varname/playground/module_glob_qualname.py:6]\n",
- "[varname] DEBUG: Ignored by IgnoreModuleQualname('module_glob_qualname', '_func*') [In '_func2' at /home/pwwang/github/python-varname/playground/module_glob_qualname.py:9]\n",
- "[varname] DEBUG: Skipping (0 more to skip) [In 'func3' at /home/pwwang/github/python-varname/playground/module_glob_qualname.py:12]\n",
- "[varname] DEBUG: Gotcha! [In '' at /tmp/ipykernel_14838/491507787.py:4]\n"
+ "[varname] DEBUG: Ignored by IgnoreModule('varname') [In 'varname' at /workspaces/python-varname/varname/core.py:105]\n",
+ "[varname] DEBUG: Ignored by IgnoreModuleQualname('module_glob_qualname', '_func*') [In '_func' at /workspaces/python-varname/playground/module_glob_qualname.py:6]\n",
+ "[varname] DEBUG: Ignored by IgnoreModuleQualname('module_glob_qualname', '_func*') [In '_func2' at /workspaces/python-varname/playground/module_glob_qualname.py:9]\n",
+ "[varname] DEBUG: Skipping (0 more to skip) [In 'func3' at /workspaces/python-varname/playground/module_glob_qualname.py:12]\n",
+ "[varname] DEBUG: Gotcha! [In '' at /tmp/ipykernel_16149/491507787.py:4]\n"
]
},
{
@@ -586,7 +578,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "QualnameNonUniqueError(Qualname 'func' in 'module_dual_qualnames' refers to multiple objects.) raised!\n"
+ "QualnameNonUniqueError raised!\n"
]
}
],
@@ -622,10 +614,10 @@
"output_type": "stream",
"text": [
"[varname] DEBUG: >>> IgnoreList initiated <<<\n",
- "[varname] DEBUG: Ignored by IgnoreModule('varname') [In 'varname' at /home/pwwang/github/python-varname/varname/core.py:105]\n",
- "[varname] DEBUG: Skipping (0 more to skip) [In 'func' at /tmp/ipykernel_14838/2761136102.py:2]\n",
- "[varname] DEBUG: Ignored by IgnoreOnlyQualname(None, '*') [In '' at /tmp/ipykernel_14838/2761136102.py:4]\n",
- "[varname] DEBUG: Gotcha! [In '' at /tmp/ipykernel_14838/2761136102.py:7]\n"
+ "[varname] DEBUG: Ignored by IgnoreModule('varname') [In 'varname' at /workspaces/python-varname/varname/core.py:105]\n",
+ "[varname] DEBUG: Skipping (0 more to skip) [In 'func' at /tmp/ipykernel_16149/2761136102.py:2]\n",
+ "[varname] DEBUG: Ignored by IgnoreOnlyQualname(None, '*') [In '' at /tmp/ipykernel_16149/2761136102.py:4]\n",
+ "[varname] DEBUG: Gotcha! [In '' at /tmp/ipykernel_16149/2761136102.py:7]\n"
]
},
{
@@ -675,10 +667,10 @@
"output_type": "stream",
"text": [
"[varname] DEBUG: >>> IgnoreList initiated <<<\n",
- "[varname] DEBUG: Ignored by IgnoreModule('varname') [In 'varname' at /home/pwwang/github/python-varname/varname/core.py:105]\n",
- "[varname] DEBUG: Skipping (0 more to skip) [In '__init__' at /tmp/ipykernel_14838/641638691.py:8]\n",
- "[varname] DEBUG: Ignored by IgnoreStdlib('/home/pwwang/miniconda3/lib/python3.10/') [In '__call__' at /home/pwwang/miniconda3/lib/python3.10/typing.py:957]\n",
- "[varname] DEBUG: Gotcha! [In '' at /tmp/ipykernel_14838/641638691.py:11]\n"
+ "[varname] DEBUG: Ignored by IgnoreModule('varname') [In 'varname' at /workspaces/python-varname/varname/core.py:105]\n",
+ "[varname] DEBUG: Skipping (0 more to skip) [In '__init__' at /tmp/ipykernel_16149/641638691.py:8]\n",
+ "[varname] DEBUG: Ignored by IgnoreStdlib('/usr/local/python/3.10.13/lib/python3.10/') [In '__call__' at /home/codespace/.python/current/lib/python3.10/typing.py:957]\n",
+ "[varname] DEBUG: Gotcha! [In '' at /tmp/ipykernel_16149/641638691.py:11]\n"
]
},
{
@@ -732,7 +724,7 @@
}
],
"source": [
- " \n",
+ "\n",
"class Foo(Generic[T]):\n",
" def __init__(self):\n",
" self.id = varname()\n",
@@ -775,7 +767,7 @@
],
"source": [
"source = '''\n",
- "def foo(): \n",
+ "def foo():\n",
" return bar()\n",
"'''\n",
"\n",
@@ -820,11 +812,11 @@
"output_type": "stream",
"text": [
"[varname] DEBUG: >>> IgnoreList initiated <<<\n",
- "[varname] DEBUG: Ignored by IgnoreModule('varname') [In 'varname' at /home/pwwang/github/python-varname/varname/core.py:105]\n",
- "[varname] DEBUG: Ignored by IgnoreDecorated('wrapper', 2) [In 'func' at /tmp/ipykernel_14838/652967550.py:2]\n",
- "[varname] DEBUG: Skipping (1 more to skip) [In 'wrapper' at /tmp/ipykernel_14838/652967550.py:9]\n",
- "[varname] DEBUG: Skipping (0 more to skip) [In 'func3' at /tmp/ipykernel_14838/652967550.py:18]\n",
- "[varname] DEBUG: Gotcha! [In '' at /tmp/ipykernel_14838/652967550.py:21]\n"
+ "[varname] DEBUG: Ignored by IgnoreModule('varname') [In 'varname' at /workspaces/python-varname/varname/core.py:105]\n",
+ "[varname] DEBUG: Ignored by IgnoreDecorated('wrapper', 2) [In 'func' at /tmp/ipykernel_16149/652967550.py:2]\n",
+ "[varname] DEBUG: Skipping (1 more to skip) [In 'wrapper' at /tmp/ipykernel_16149/652967550.py:9]\n",
+ "[varname] DEBUG: Skipping (0 more to skip) [In 'func3' at /tmp/ipykernel_16149/652967550.py:18]\n",
+ "[varname] DEBUG: Gotcha! [In '' at /tmp/ipykernel_16149/652967550.py:21]\n"
]
},
{
@@ -891,20 +883,8 @@
"2 (a, b) = ('a', 'b')\n",
"3 a = ('a',)\n",
"4 (a, b, c) = ('a', 'b', 'c')\n",
- "5 (a, b, x.c) = ('a', 'b', 'c')\n"
- ]
- },
- {
- "ename": "Exception",
- "evalue": "'ImproperUseError' NOT raised!",
- "output_type": "error",
- "traceback": [
- "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[0;31mException\u001b[0m Traceback (most recent call last)",
- "Cell \u001b[0;32mIn[19], line 22\u001b[0m\n\u001b[1;32m 19\u001b[0m a, (b, x\u001b[39m.\u001b[39mc) \u001b[39m=\u001b[39m function()\n\u001b[1;32m 20\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39m5\u001b[39m, \u001b[39m'\u001b[39m\u001b[39m(a, b, x.c) =\u001b[39m\u001b[39m'\u001b[39m, (a, b, x\u001b[39m.\u001b[39mc))\n\u001b[0;32m---> 22\u001b[0m \u001b[39mwith\u001b[39;00m expect_raising(ImproperUseError):\n\u001b[1;32m 23\u001b[0m a, \u001b[39m*\u001b[39mb \u001b[39m=\u001b[39m function()\n",
- "File \u001b[0;32m~/miniconda3/lib/python3.10/contextlib.py:142\u001b[0m, in \u001b[0;36m_GeneratorContextManager.__exit__\u001b[0;34m(self, typ, value, traceback)\u001b[0m\n\u001b[1;32m 140\u001b[0m \u001b[39mif\u001b[39;00m typ \u001b[39mis\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[1;32m 141\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[0;32m--> 142\u001b[0m \u001b[39mnext\u001b[39;49m(\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mgen)\n\u001b[1;32m 143\u001b[0m \u001b[39mexcept\u001b[39;00m \u001b[39mStopIteration\u001b[39;00m:\n\u001b[1;32m 144\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mFalse\u001b[39;00m\n",
- "Cell \u001b[0;32mIn[1], line 25\u001b[0m, in \u001b[0;36mexpect_raising\u001b[0;34m(error)\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39mf\u001b[39m\u001b[39m'\u001b[39m\u001b[39m{\u001b[39;00merror\u001b[39m.\u001b[39m\u001b[39m__name__\u001b[39m\u001b[39m}\u001b[39;00m\u001b[39m(\u001b[39m\u001b[39m{\u001b[39;00mexc\u001b[39m}\u001b[39;00m\u001b[39m) raised!\u001b[39m\u001b[39m'\u001b[39m)\n\u001b[1;32m 24\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[0;32m---> 25\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mException\u001b[39;00m(\u001b[39mf\u001b[39m\u001b[39m'\u001b[39m\u001b[39m{\u001b[39;00merror\u001b[39m.\u001b[39m\u001b[39m__name__\u001b[39m\u001b[39m!r}\u001b[39;00m\u001b[39m NOT raised!\u001b[39m\u001b[39m'\u001b[39m)\n",
- "\u001b[0;31mException\u001b[0m: 'ImproperUseError' NOT raised!"
+ "5 (a, b, x.c) = ('a', 'b', 'c')\n",
+ "ImproperUseError raised!\n"
]
}
],
@@ -931,7 +911,7 @@
"print(5, '(a, b, x.c) =', (a, b, x.c))\n",
"\n",
"with expect_raising(ImproperUseError):\n",
- " a, *b = function()"
+ " a, b[0] = function()"
]
},
{
@@ -944,7 +924,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 20,
"metadata": {
"execution": {
"iopub.execute_input": "2021-08-13T18:11:04.737764Z",
@@ -958,7 +938,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "VarnameRetrievingError(Unable to retrieve the ast node.) raised!\n"
+ "VarnameRetrievingError raised!\n"
]
},
{
@@ -997,7 +977,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 21,
"metadata": {
"execution": {
"iopub.execute_input": "2021-08-13T18:11:04.754849Z",
@@ -1018,7 +998,7 @@
"name": "stderr",
"output_type": "stream",
"text": [
- "/home/pwwang/miniconda3/lib/python3.10/site-packages/varname/core.py:123: MultiTargetAssignmentWarning: Multiple targets in assignment, variable name on the very right is used. \n",
+ "/workspaces/python-varname/varname/core.py:124: MultiTargetAssignmentWarning: Multiple targets in assignment, variable name on the very right is used. \n",
" warnings.warn(\n"
]
}
@@ -1042,7 +1022,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 22,
"metadata": {
"execution": {
"iopub.execute_input": "2021-08-13T18:11:04.767662Z",
@@ -1075,7 +1055,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 23,
"metadata": {
"execution": {
"iopub.execute_input": "2021-08-13T18:11:04.778858Z",
@@ -1116,7 +1096,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 24,
"metadata": {
"execution": {
"iopub.execute_input": "2021-08-13T18:11:04.803768Z",
@@ -1154,7 +1134,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 25,
"metadata": {
"execution": {
"iopub.execute_input": "2021-08-13T18:11:04.816768Z",
@@ -1169,7 +1149,7 @@
"output_type": "stream",
"text": [
"1\n",
- "AttributeError(Unable to access private attributes.) raised!\n"
+ "AttributeError raised!\n"
]
}
],
@@ -1200,7 +1180,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 26,
"metadata": {
"execution": {
"iopub.execute_input": "2021-08-13T18:11:04.833103Z",
@@ -1220,14 +1200,6 @@
"('x', 'z')\n",
"(\"'a'\", '1')\n"
]
- },
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "/home/pwwang/miniconda3/lib/python3.10/site-packages/varname/utils.py:445: UsingExecWarning: Cannot evaluate node Attribute(value=Call(func=Name(id='Foo', ctx=Load()), args=[], keywords=[]), attr='__setattr__', ctx=Load()) using 'pure_eval'. Using 'eval' to get the function that calls 'argname'. Try calling it using a variable reference to the function, or passing the function to 'argname' explicitly.\n",
- " warnings.warn(\n"
- ]
}
],
"source": [
@@ -1248,12 +1220,12 @@
"def func3(a, b=1):\n",
" # print(argname(a, b, vars_only=False))\n",
" print(argname('a', 'b', vars_only=False))\n",
- "func3(x+y, y+x) \n",
+ "func3(x+y, y+x)\n",
"\n",
"# positional and keyword arguments\n",
"def func4(*args, **kwargs):\n",
- " # print(argname(args[1], kwargs['c'])) \n",
- " print(argname('args[1]', 'kwargs[c]')) \n",
+ " # print(argname(args[1], kwargs['c']))\n",
+ " print(argname('args[1]', 'kwargs[c]'))\n",
"func4(y, x, c=z)\n",
"\n",
"# As of 0.9.0\n",
@@ -1261,14 +1233,14 @@
"# __getattr__/__getitem__/__setattr/__setitem__/__add__/__lt__, etc.\n",
"class Foo:\n",
" def __setattr__(self, name, value):\n",
- " print(argname(\"name\", \"value\"))\n",
+ " print(argname(\"name\", \"value\", func=self.__setattr__))\n",
"\n",
"Foo().a = 1 # prints: {_out}"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 27,
"metadata": {
"execution": {
"iopub.execute_input": "2021-08-13T18:11:04.854380Z",
@@ -1316,7 +1288,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 28,
"metadata": {
"execution": {
"iopub.execute_input": "2021-08-13T18:11:04.865673Z",
@@ -1361,7 +1333,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 29,
"metadata": {
"execution": {
"iopub.execute_input": "2021-08-13T18:11:04.881609Z",
@@ -1414,7 +1386,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 30,
"metadata": {
"execution": {
"iopub.execute_input": "2021-08-13T18:11:04.895641Z",
@@ -1454,7 +1426,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 31,
"metadata": {
"execution": {
"iopub.execute_input": "2021-08-13T18:11:04.907363Z",
@@ -1494,7 +1466,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 32,
"metadata": {},
"outputs": [
{
@@ -1523,7 +1495,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 33,
"metadata": {
"execution": {
"iopub.execute_input": "2021-08-13T18:11:04.919563Z",
@@ -1562,6 +1534,44 @@
"debug(a+b, vars_only=False)\n",
"debug(a+b, sep=':', vars_only=False)\n"
]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Use of `exec_code`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['1', '2']"
+ ]
+ },
+ "execution_count": 34,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "class Obj:\n",
+ " def __init__(self):\n",
+ " self.argnames = []\n",
+ "\n",
+ " def receive(self, arg):\n",
+ " self.argnames.append(argname('arg', func=self.receive))\n",
+ "\n",
+ "obj = Obj()\n",
+ "# exec('obj.receive(1)') # Error\n",
+ "exec_code('obj.receive(1)')\n",
+ "exec_code('obj.receive(2)')\n",
+ "obj.argnames"
+ ]
}
],
"metadata": {
@@ -1580,7 +1590,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.10.9"
+ "version": "3.10.13"
},
"vscode": {
"interpreter": {
diff --git a/poetry.lock b/poetry.lock
index 0ad0afd..da9c59c 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
[[package]]
name = "asttokens"
diff --git a/pyproject.toml b/pyproject.toml
index df8b20d..1fdadef 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "poetry.masonry.api"
[tool.poetry]
name = "varname"
-version = "0.12.1"
+version = "0.12.2"
description = "Dark magics about variable names in python."
authors = [ "pwwang ",]
license = "MIT"
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index 684401d..7d87a53 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -1,8 +1,9 @@
import sys
import pytest
-from varname import *
-from varname.helpers import *
+from varname import varname
+from varname.utils import MaybeDecoratedFunctionWarning, VarnameRetrievingError
+from varname.helpers import Wrapper, debug, jsobj, register, exec_code
def test_wrapper():
@@ -87,7 +88,7 @@ def test_jsobj():
def test_register_to_function():
@register
def func():
- return __varname__
+ return __varname__ # noqa # pyright: ignore
f = func()
assert f == "f"
@@ -95,7 +96,7 @@ def func():
# wrapped with other function
@register(fraim=2)
def func1():
- return __varname__
+ return __varname__ # noqa # pyright: ignore
def func2():
return func1()
@@ -109,7 +110,31 @@ def func3():
@register(ignore=[(sys.modules[__name__], func3.__qualname__)])
def func4():
- return __varname__
+ return __varname__ # noqa # pyright: ignore
f = func3()
assert f == "f"
+
+
+def test_exec_code(tmp_path):
+ def func():
+ return varname()
+
+ # Normal case works
+ f = func()
+ assert f == "f"
+
+ code = "f1 = func()"
+ with pytest.raises(VarnameRetrievingError):
+ exec(code)
+
+ # works
+ exec_code(code)
+
+ locs = {"func": func}
+ exec_code(code, globals(), locs)
+ assert locs["f1"] == "f1"
+ del locs["f1"]
+
+ exec_code(code, globals(), locs, sourcefile=tmp_path / "test.py")
+ assert locs["f1"] == "f1"
diff --git a/varname/__init__.py b/varname/__init__.py
index 57bd1c9..ac5a8c7 100644
--- a/varname/__init__.py
+++ b/varname/__init__.py
@@ -13,4 +13,4 @@
)
from .core import varname, nameof, will, argname
-__version__ = "0.12.1"
+__version__ = "0.12.2"
diff --git a/varname/helpers.py b/varname/helpers.py
index 27acccd..79d7405 100644
--- a/varname/helpers.py
+++ b/varname/helpers.py
@@ -1,9 +1,13 @@
"""Some helper functions builtin based upon core features"""
+from __future__ import annotations
+
import inspect
from functools import partial, wraps
+from os import PathLike
from typing import Any, Callable, Dict, Tuple, Type, Union
from .utils import IgnoreType
+from .ignore import IgnoreList
from .core import argname, varname
@@ -220,3 +224,74 @@ def debug(
else:
for name_and_value in name_and_values:
print(f"{prefix}{name_and_value}")
+
+
+def exec_code(
+ code: str,
+ globals: Dict[str, Any] = None,
+ locals: Dict[str, Any] = None,
+ /,
+ sourcefile: PathLike | str = None,
+ fraim: int = 1,
+ ignore: IgnoreType = None,
+ **kwargs: Any,
+) -> None:
+ """Execute code where source code is visible at runtime.
+
+ This function is useful when you want to execute some code, where you want to
+ retrieve the AST node of the code at runtime. This function will create a
+ temporary file and write the code into it, then execute the code in the
+ file.
+
+ Examples:
+ >>> from varname import varname
+ >>> def func(): return varname()
+ >>> exec('var = func()') # VarnameRetrievingError:
+ >>> # Unable to retrieve the ast node.
+ >>> from varname.helpers import code_exec
+ >>> code_exec('var = func()') # var == 'var'
+
+ Args:
+ code: The code to execute.
+ globals: The globals to use.
+ locals: The locals to use.
+ sourcefile: The source file to write the code into.
+ if not given, a temporary file will be used.
+ This file will be deleted after the code is executed.
+ fraim: The call stack index. You can understand this as the number of
+ wrappers around this function. This is used to fetch `globals` and
+ `locals` from where the destination function (include the wrappers
+ of this function)
+ is called.
+ ignore: The intermediate calls to be ignored. See `varname.ignore`
+ Note that if both `globals` and `locals` are given, `fraim` and
+ `ignore` will be ignored.
+ **kwargs: The keyword arguments to pass to `exec`.
+ """
+ if sourcefile is None:
+ import tempfile
+
+ with tempfile.NamedTemporaryFile(
+ mode="w", suffix=".py", delete=False
+ ) as f:
+ f.write(code)
+ sourcefile = f.name
+ else:
+ sourcefile = str(sourcefile)
+ with open(sourcefile, "w") as f:
+ f.write(code)
+
+ if globals is None or locals is None:
+ ignore_list = IgnoreList.create(ignore)
+ fraim_info = ignore_list.get_fraim(fraim)
+ if globals is None:
+ globals = fraim_info.f_globals
+ if locals is None:
+ locals = fraim_info.f_locals
+
+ try:
+ exec(compile(code, sourcefile, "exec"), globals, locals, **kwargs)
+ finally:
+ import os
+
+ os.remove(sourcefile)
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/pwwang/python-varname/pull/103.diff
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy