python-varname
playground¶from contextlib import contextmanager
from varname import (
varname, nameof, will, argname, argname2,
config,
ImproperUseError, VarnameRetrievingError, QualnameNonUniqueError
)
from varname.helpers import Wrapper, register, debug
@contextmanager
def enable_debug():
try:
config.debug = True
yield
finally:
config.debug = False
@contextmanager
def expect_raising(error):
try:
yield
except error as exc:
print(f'{error.__name__}({exc}) raised!')
else:
raise Exception(f'{error.__name__!r} NOT raised!')
varname
¶def func():
return varname()
f = func()
f
'f'
class Foo:
def __init__(self):
self.id = varname()
foo = Foo()
foo.id
'foo'
def function():
return varname()
func = function()
print(1, func)
func = [function()]
print(2, func)
func = [function(), function()]
print(3, func)
func = (function(), )
print(4, func)
func = (function(), function())
print(5, func)
func_suffix = function()[:-7]
print(6, func_suffix)
alias = function
func = alias()
print(7, func)
1 func 2 ['func'] 3 ['func', 'func'] 4 ('func',) 5 ('func', 'func') 6 func 7 func
varname
not called directly¶def func():
return varname(frame=2)
def func2():
return func()
f = func2()
f
'f'
class Foo:
def __init__(self):
self.id = varname(frame=2)
def wrapper():
return Foo()
foo = wrapper()
foo.id
'foo'
ignore
argument to ignore intermediate calls¶def func():
return varname(ignore=func2)
def func2():
return func()
f = func2()
f
'f'
You can also use a tuple of module and the qualified name of the function
import __main__
def func():
return varname(ignore=(__main__, 'func2'))
def func2():
return func()
f = func2()
f
'f'
Pay attention to decorated functions:
def decor(f):
def wrapper():
return f()
return wrapper
def func():
return varname(ignore=func2)
@decor
def func2():
return func()
with expect_raising(VarnameRetrievingError):
f = func2()
VarnameRetrievingError(Failed to retrieve the variable name.) raised!
/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 frames with a tuple of the function and the number of its decorators. MaybeDecoratedFunctionWarning,
def decor(f):
def wrapper():
return f()
return wrapper
def func():
# tell varname that func2 has 1 decorator
return varname(ignore=(func2, 1))
@decor
def func2():
return func()
f = func2()
f
'f'
Ignore calls from an entire module and its submodules
import module_all_calls
def func():
return module_all_calls.func3()
with enable_debug():
f = func()
f
[varname] DEBUG: Ignored by IgnoreModule('varname') [In 'varname' at /home/pwwang/github/python-varname/varname/core.py:99] [varname] DEBUG: Ignored by IgnoreModule('module_all_calls') [In 'func' at /home/pwwang/github/python-varname/playground/module_all_calls.py:6] [varname] DEBUG: Ignored by IgnoreModule('module_all_calls') [In 'func2' at /home/pwwang/github/python-varname/playground/module_all_calls.py:9] [varname] DEBUG: Ignored by IgnoreModule('module_all_calls') [In 'func3' at /home/pwwang/github/python-varname/playground/module_all_calls.py:12] [varname] DEBUG: Skipping (0 more to skip) [In 'func' at <ipython-input-11-8d0a7ff1a188>:4] [varname] DEBUG: Gotcha! [In '<module>' at <ipython-input-11-8d0a7ff1a188>:7]
'f'
Ignore some calls using module and a glob-style qualname
import module_glob_qualname
with enable_debug():
f = module_glob_qualname.func3()
f
[varname] DEBUG: Ignored by IgnoreModule('varname') [In 'varname' at /home/pwwang/github/python-varname/varname/core.py:99] [varname] DEBUG: Ignored by IgnoreModuleQualname('module_glob_qualname', '_func*') [In '_func' at /home/pwwang/github/python-varname/playground/module_glob_qualname.py:6] [varname] DEBUG: Ignored by IgnoreModuleQualname('module_glob_qualname', '_func*') [In '_func2' at /home/pwwang/github/python-varname/playground/module_glob_qualname.py:9] [varname] DEBUG: Skipping (0 more to skip) [In 'func3' at /home/pwwang/github/python-varname/playground/module_glob_qualname.py:12] [varname] DEBUG: Gotcha! [In '<module>' at <ipython-input-12-07ea87f561cc>:4]
'f'
Note that when using an exact qualname, it has to be unique in the module
import module_dual_qualnames
with expect_raising(QualnameNonUniqueError):
f = module_dual_qualnames.func3()
QualnameNonUniqueError(Qualname 'func' in 'module_dual_qualnames' refers to multiple objects.) raised!
lambda
functions are automatically ignored
def func():
return varname()
func2 = lambda: func()
with enable_debug():
f = func2()
f
[varname] DEBUG: Ignored by IgnoreModule('varname') [In 'varname' at /home/pwwang/github/python-varname/varname/core.py:99] [varname] DEBUG: Skipping (0 more to skip) [In 'func' at <ipython-input-14-d27bbc23b8df>:2] [varname] DEBUG: Ignored by IgnoreOnlyQualname(None, '*<lambda>') [In '<lambda>' at <ipython-input-14-d27bbc23b8df>:4] [varname] DEBUG: Gotcha! [In '<module>' at <ipython-input-14-d27bbc23b8df>:7]
'f'
Calls from standard libraries are automatically ignored
import typing
from typing import Generic, TypeVar
T = TypeVar("T")
class Foo(Generic[T]):
def __init__(self):
self.id = varname(ignore=[typing])
with enable_debug():
foo:Foo = Foo[str]()
foo.id
[varname] DEBUG: Ignored by IgnoreModule('varname') [In 'varname' at /home/pwwang/github/python-varname/varname/core.py:99] [varname] DEBUG: Skipping (0 more to skip) [In '__init__' at <ipython-input-15-32fa2de0b542>:8] [varname] DEBUG: Ignored by IgnoreStdlib('/home/pwwang/miniconda3/lib/python3.7/') [In '__call__' at /home/pwwang/miniconda3/lib/python3.7/typing.py:678] [varname] DEBUG: Gotcha! [In '<module>' at <ipython-input-15-32fa2de0b542>:11]
'foo'
class Foo(Generic[T]):
def __init__(self):
self.id = varname()
foo: Foo = Foo[str]()
foo.id
'foo'
Filename can also be used instead of the module itself
source = '''
def foo():
return bar()
'''
code = compile(source, '<string>', 'exec')
def bar():
return varname(ignore='<string>')
# can also be used together with qualname
# return varname(ignore=('<string>', 'bar'))
globs = {'bar': bar}
exec(code, globs)
foo = globs['foo']
f = foo()
f
'f'
frame
and ignore
¶def func():
return varname(
frame=2, # skip func3
ignore=(func2, 2) # ignore func2 and its decorators
)
def decor(f):
def wrapper():
return f()
return wrapper
@decor
@decor
def func2():
return func()
def func3():
return func2()
with enable_debug():
f = func3()
f
[varname] DEBUG: Ignored by IgnoreModule('varname') [In 'varname' at /home/pwwang/github/python-varname/varname/core.py:99] [varname] DEBUG: Ignored by IgnoreDecorated('wrapper', 2) [In 'func' at <ipython-input-18-e7a4675a5e8e>:4] [varname] DEBUG: Skipping (1 more to skip) [In 'wrapper' at <ipython-input-18-e7a4675a5e8e>:9] [varname] DEBUG: Skipping (0 more to skip) [In 'func3' at <ipython-input-18-e7a4675a5e8e>:18] [varname] DEBUG: Gotcha! [In '<module>' at <ipython-input-18-e7a4675a5e8e>:21]
'f'
def function():
return varname(multi_vars=True)
a, b = function()
print(1, '(a, b) =', (a, b))
[a, b] = function()
print(2, '(a, b) =', (a, b))
a = function()
print(3, 'a =', a)
# hierarchy
a, (b, c) = function()
print(4, '(a, b, c) =', (a, b, c))
# with attributes
x = lambda: 1
a, (b, x.c) = function()
print(5, '(a, b, x.c) =', (a, b, x.c))
with expect_raising(ImproperUseError):
a, *b = function()
1 (a, b) = ('a', 'b') 2 (a, b) = ('a', 'b') 3 a = ('a',) 4 (a, b, c) = ('a', 'b', 'c') 5 (a, b, x.c) = ('a', 'b', 'c') ImproperUseError(Can only get name of a variable or attribute, not Starred(value=Name(id='b', ctx=Store()), ctx=Store())) raised!
raise_exc
¶def func_raise():
return varname(raise_exc=True) # default
def func_silent():
return varname(raise_exc=False) # will return None if failed
with expect_raising(VarnameRetrievingError):
func_raise() # no variable assigned
f = ''
f += str(func_silent())
f
VarnameRetrievingError(Failed to retrieve the variable name.) raised!
'None'
def func():
return varname()
f1 = f2 = func()
print(f'f1 = {f1!r}, f2 = {f2!r}')
f1 = 'f1', f2 = 'f1'
/home/pwwang/github/python-varname/varname/core.py:122: MultiTargetAssignmentWarning: Multiple targets in assignment, variable name on the very left will be used. MultiTargetAssignmentWarning,
nameof
¶x = lambda: None
print(nameof(x))
x
Get full name of a chain of attributes
x.a = x
x.a.b = x
print(nameof(x.a, vars_only=False))
print(nameof(x.a.b, vars_only=False))
print(nameof(x.a, x.a.b, vars_only=False))
print(nameof(x.a(), vars_only=False))
x.a x.a.b ('x.a', 'x.a.b') x.a()
If you want to wrap nameof
def nameof2(var, *more_vars):
return nameof(var, *more_vars, frame=2)
nameof2(x)
'x'
will
¶class Namespace:
public = 1
_private = 2
def func():
w = will()
if w.startswith('_'):
raise AttributeError('Unable to access private attributes.')
return Namespace
print(func().public)
with expect_raising(AttributeError):
func()._private
1 AttributeError(Unable to access private attributes.) raised!
argname
¶# argname is superseded by argname2
def func(a, b=1):
print(argname(a))
print(argname2('a'))
x = y = z = 2
func(x)
def func2(a, b=1):
print(argname(a, b))
print(argname2('a', 'b'))
func2(y, b=x)
# allow expressions
def func3(a, b=1):
print(argname(a, b, vars_only=False))
print(argname2('a', 'b', vars_only=False))
func3(x+y, y+x)
# positional and keyword arguments
def func4(*args, **kwargs):
print(argname(args[1], kwargs['c']))
print(argname2('args[1]', 'kwargs[c]'))
func4(y, x, c=z)
x x ('y', 'x') ('y', 'x') ('x+y', 'y+x') ('x+y', 'y+x') ('x', 'z') ('x', 'z')
# It is easier to wrap argname2
# You don't have to use the exact signature
def argname3(*args):
return argname2(*args, frame=2)
def func(a, b):
return argname3('a', 'b')
print(func(x, y))
('x', 'y')
Wrapper
¶value1 = True
value2 = {'a': 1}
wrapped1 = Wrapper(True)
wrapped2 = Wrapper(value2)
print(repr(wrapped1))
print(repr(wrapped2))
print(wrapped1.value is value1)
print(wrapped2.value is value2)
<Wrapper (name='wrapped1', value=True)> <Wrapper (name='wrapped2', value={'a': 1})> True True
You can wrap Wrapper
:
def wrap(value):
return Wrapper(value, frame=2)
wrapped3 = wrap(value1)
wrapped4 = wrap(value2)
print(repr(wrapped3))
print(repr(wrapped4))
print(wrapped3.value is value1)
print(wrapped4.value is value2)
<Wrapper (name='wrapped3', value=True)> <Wrapper (name='wrapped4', value={'a': 1})> True True
register
¶Register __varname__
to function
@register
def func():
return __varname__
f = func()
f
'f'
Register __varname__
to class
@register
class Foo:
pass
foo = Foo()
foo.__varname__
'foo'
debug
¶a = '1'
b = '2'
debug(a)
debug(b)
debug(a, b)
debug(a, b, merge=True)
debug(a, b, merge=True, repr=False)
debug(a, b, merge=True, prefix='DEBUG VARS: ')
debug(a+b, vars_only=False)
debug(a+b, sep=':', vars_only=False)
DEBUG: a='1' DEBUG: b='2' DEBUG: a='1' DEBUG: b='2' DEBUG: a='1', b='2' DEBUG: a=1, b=2 DEBUG VARS: a='1', b='2' DEBUG: a+b='12' DEBUG: a+b:'12'