Skip to main content

Dark magics about variable names in python.

Project description

varname

Pypi Github PythonVers Building Docs and API Codacy Codacy coverage Chat on gitter

Dark magics about variable names in python

Change Log | API

Installation

pip install varname

Features

  • Fetching variable names from inside the function/class call using varname
  • Fetching variable names directly using nameof
  • A value wrapper to store the variable name that a value is assigned to using Wrapper
  • Detecting next immediate attribute name using will
  • Shortcut for collections.namedtuple
  • Injecting __varname__ to objects

Credits

Thanks goes to these awesome people/projects:


@alexmojaki

executing

Special thanks to @HanyuuLu to give up the name varname in pypi for this project.

Usage

Retrieving the variable names from inside a function call/class instantiation

  • From insdie a function call

    from varname import varname
    def function():
        return varname()
    
    func = function()  # func == 'func'
    
  • varname calls being buried deeply

    def function():
        # I know that at which stack this will be called
        return varname(caller=3)
    
    def function1():
        return function()
    
    def function2():
        return function1()
    
    func = function2()  # func == 'func'
    
  • Retrieving instance name of a class

    class Foo:
        def __init__(self):
            self.id = varname()
    
        def copy(self):
            # also able to fetch inside a method call
            copied = Foo() # copied.id == 'copied'
            copied.id = varname() # assign id to whatever variable name
            return copied
    
    k = Foo()   # k.id == 'k'
    
    k2 = k.copy() # k2.id == 'k2'
    
  • Some unusual use

    func = [function()]    # func == ['func']
    
    func = [function(), function()] # func == ['func', 'func']
    
    func = function(), function()   # func = ('func', 'func')
    
    func = func1 = function()  # func == func1 == 'func'
    # a warning will be printed
    # since you may not want func1 to be 'func'
    
    x = func(y = func())  # x == 'x'
    
    # get part of the name
    func_abc = function()[-3:]  # func_abc == 'abc'
    
    # function alias supported now
    function2 = function
    func = function2()  # func == 'func'
    
    a = lambda: 0
    a.b = function() # a.b == 'b'
    
    # Since v0.1.3
    # We can ask varname to raise exceptions
    # if it fails to detect the variable name
    def get_name(raise_exc):
        return varname(raise_exc=raise_exc)
    
    a = {}
    a['b'] = get_name(True) # VarnameRetrievingError
    a['b'] = get_name(False) # None
    

Value wrapper

from varname import Wrapper

foo = Wrapper(True)
# foo.name == 'foo'
# foo.value == True
bar = Wrapper(False)
# bar.name == 'bar'
# bar.value == False

def values_to_dict(*args):
    return {val.name: val.value for val in args}

mydict = values_to_dict(foo, bar)
# {'foo': True, 'bar': False}

Getting variable names directly

from varname import varname, nameof

a = 1
nameof(a) # 'a'

b = 2
nameof(a, b) # ('a', 'b')

def func():
    return varname() + '_suffix'

f = func() # f == 'f_suffix'
nameof(f)  # 'f'

# get full names of (chained) attribute calls
func.a = func
nameof(func.a, full=True) # 'func.a'

func.a.b = 1
nameof(func.a.b, full=True) # 'func.a.b'

Detecting next immediate attribute name

from varname import will
class AwesomeClass:
    def __init__(self):
        self.will = None

    def permit(self):
        self.will = will(raise_exc=False)
        if self.will == 'do':
            # let self handle do
            return self
        raise AttributeError('Should do something with AwesomeClass object')

    def do(self):
        if self.will != 'do':
            raise AttributeError("You don't have permission to do")
        return 'I am doing!'

awesome = AwesomeClass()
awesome.do() # AttributeError: You don't have permission to do
awesome.permit() # AttributeError: Should do something with AwesomeClass object
awesome.permit().do() == 'I am doing!'

Shortcut for collections.namedtuple

# instead of
from collections import namedtuple
Name = namedtuple('Name', ['first', 'last'])

# we can do:
from varname import namedtuple
Name = namedtuple(['first', 'last'])

Injecting __varname__

from varname import inject

class MyList(list):
    pass

a = inject(MyList())
b = inject(MyList())

a.__varname__ == 'a'
b.__varname__ == 'b'

a == b

# other methods not affected
a.append(1)
b.append(1)
a == b

Reliability and limitations

varname is all depending on executing package to look for the node. The node executing detects is ensured to be the correct one (see this).

It partially works with environments where other AST magics apply, including pytest, ipython, macropy, birdseye, reticulate with R, etc. Neither executing nor varname is 100% working with those environments. Use it at your own risk.

For example:

  • This will not work with pytest:

    a = 1
    assert nameof(a) == 'a'
    
    # do this instead
    name_a = nameof(a)
    assert name_a == 'a'
    
  • R with reticulate.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

varname-0.5.2.tar.gz (9.2 kB view hashes)

Uploaded Source

Built Distribution

varname-0.5.2-py3-none-any.whl (7.8 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page