• Stars
    star
    309
  • Rank 135,306 (Top 3 %)
  • Language
    Python
  • License
    MIT License
  • Created almost 5 years ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Dark magics about variable names in python

varname

Pypi Github PythonVers Building Docs and API Codacy Codacy coverage Downloads

Dark magics about variable names in python

CHANGELOG | API | Playground | πŸ”₯ StackOverflow answer

Installation

pip install -U varname

Note if you use python < 3.8, install varname < 0.11

Features

  • Core features:

    • Retrieving names of variables a function/class call is assigned to from inside it, using varname.
    • Retrieving variable names directly, using nameof
    • Detecting next immediate attribute name, using will
    • Fetching argument names/sources passed to a function using argname
  • Other helper APIs (built based on core features):

    • A value wrapper to store the variable name that a value is assigned to, using Wrapper
    • 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

Credits

Thanks goes to these awesome people/projects:


executing

@alexmojaki

@breuleux

@ElCuboNegro

@thewchan

@LawsOfSympathy

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

Usage

Retrieving the variable names using varname(...)

  • From inside a function

    from varname import varname
    def function():
        return varname()
    
    func = function()  # func == 'func'

    When there are intermediate frames:

    def wrapped():
        return function()
    
    def function():
        # retrieve the variable name at the 2nd frame from this one
        return varname(frame=2)
    
    func = wrapped() # func == 'func'

    Or use ignore to ignore the wrapped frame:

    def wrapped():
        return function()
    
    def function():
        return varname(ignore=wrapped)
    
    func = wrapped() # func == 'func'

    Calls from standard libraries are ignored by default:

    import asyncio
    
    async def function():
        return varname()
    
    func = asyncio.run(function()) # func == 'func'

    Use strict to control whether the call should be assigned to the variable directly:

    def function(strict):
        return varname(strict=strict)
    
    func = function(True)     # OK, direct assignment, func == 'func'
    
    func = [function(True)]   # Not a direct assignment, raises ImproperUseError
    func = [function(False)]  # OK, func == ['func']
    
    func = function(False), function(False)   # OK, func = ('func', 'func')
  • Retrieving name of a class instance

    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
    
    foo = Foo()   # foo.id == 'foo'
    
    foo2 = foo.copy() # foo2.id == 'foo2'
  • Multiple variables on Left-hand side

    # since v0.5.4
    def func():
        return varname(multi_vars=True)
    
    a = func() # a == ('a',)
    a, b = func() # (a, b) == ('a', 'b')
    [a, b] = func() # (a, b) == ('a', 'b')
    
    # hierarchy is also possible
    a, (b, c) = func() # (a, b, c) == ('a', 'b', 'c')
  • Some unusual use

    def function(**kwargs):
        return varname(strict=False)
    
    func = func1 = function()  # func == func1 == 'func1'
    # if varname < 0.8: func == func1 == 'func'
    # a warning will be shown
    # since you may not want func to be 'func1'
    
    x = function(y = function())  # 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'

The decorator way to register __varname__ to functions/classes

  • Registering __varname__ to functions

    from varname.helpers import register
    
    @register
    def function():
        return __varname__
    
    func = function() # func == 'func'
    # arguments also allowed (frame, ignore and raise_exc)
    @register(frame=2)
    def function():
        return __varname__
    
    def wrapped():
        return function()
    
    func = wrapped() # func == 'func'
  • Registering __varname__ as a class property

    @register
    class Foo:
        ...
    
    foo = Foo()
    # foo.__varname__ == 'foo'

Getting variable names directly using nameof

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, vars_only=False) # 'func.a'

func.a.b = 1
nameof(func.a.b, vars_only=False) # '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!'

Fetching argument names/sources using argname

from varname import argname

def func(a, b=1):
    print(argname('a'))

x = y = z = 2
func(x) # prints: x


def func2(a, b=1):
    print(argname('a', 'b'))
func2(y, b=x) # prints: ('y', 'x')


# allow expressions
def func3(a, b=1):
    print(argname('a', 'b', vars_only=False))
func3(x+y, y+x) # prints: ('x+y', 'y+x')


# positional and keyword arguments
def func4(*args, **kwargs):
    print(argname('args[1]', 'kwargs[c]'))
func4(y, x, c=z) # prints: ('x', 'z')


# As of 0.9.0 (see: https://pwwang.github.io/python-varname/CHANGELOG/#v090)
# Can also fetch the source of the argument for
# __getattr__/__getitem__/__setattr/__setitem__/__add__/__lt__, etc.
class Foo:
    def __setattr__(self, name, value):
        print(argname("name", "value"))

Foo().a = 1 # prints: ("'a'", '1')

Value wrapper

from varname.helpers 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}

Creating dictionary using jsobj

from varname.helpers import jsobj

a = 1
b = 2
jsobj(a, b) # {'a': 1, 'b': 2}
jsobj(a, b, c=3) # {'a': 1, 'b': 2, 'c': 3}

Debugging with debug

from varname.helpers import debug

a = 'value'
b = ['val']
debug(a)
# "DEBUG: a='value'\n"
debug(b)
# "DEBUG: b=['val']\n"
debug(a, b)
# "DEBUG: a='value'\nDEBUG: b=['val']\n"
debug(a, b, merge=True)
# "DEBUG: a='value', b=['val']\n"
debug(a, repr=False, prefix='')
# 'a=value\n'
# also debug an expression
debug(a+a)
# "DEBUG: a+a='valuevalue'\n"
# If you want to disable it:
debug(a+a, vars_only=True) # ImproperUseError

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' # pytest manipulated the ast here
    
    # do this instead
    name_a = nameof(a)
    assert name_a == 'a'

More Repositories

1

datar

A Grammar of Data Manipulation in python
Python
271
star
2

vscode-gptcommit

Automated git commit messages using GPT models via gptcommit for VS Code.
TypeScript
192
star
3

cnn-convoluter

An interactive player for CNN convolution
JavaScript
112
star
4

pipen

A pipeline framework for python
Python
102
star
5

vcfstats

Powerful statistics for VCF files
Python
60
star
6

liquidpy

A port of liquid template engine for python
Python
56
star
7

toml-bench

Which toml package to use in python?
Python
50
star
8

pipda

A framework for data piping in python
Python
35
star
9

python-import-system

Python import system diagram
Jupyter Notebook
17
star
10

pymedoo

A lightweight database framework for python
Python
15
star
11

plotnine-prism

Prism themes for plotnine, inspired by ggprism.
Python
14
star
12

immunopipe

Integrative analysis for scTCR- and scRNA-seq data
Python
13
star
13

Bioinformatics-cheatsheet

A cheat sheet for Bioinformatians.
11
star
14

biopipen

A set of processes/pipelines for bioinformatics
Python
11
star
15

pyparam

Powerful parameter processing for python
Python
8
star
16

diot

Python dictionary with dot notation
Python
8
star
17

enrichr

A python wrapper for Enrichr APIs
Python
8
star
18

xqute

A job management system for python
Python
8
star
19

simplug

A simple plugin system for python with async hooks supported
Python
7
star
20

python-simpleconf

Simple configuration management with python
Python
6
star
21

attr_property

Property support for attrs
Python
5
star
22

cmdQueue

A job queue available on Windows and *nix
Python
5
star
23

pdtypes

Show data types for pandas data frames in terminal and notebooks
Python
5
star
24

dotdict-bench

Benchmarking for dot-accessible dict packages in python
Python
5
star
25

datar-pandas

The pandas backend for datar
Python
5
star
26

plkit

A wrapper of pytorch-lightning that makes you write even less code.
Python
5
star
27

datar-numpy

The numpy backend for datar.
Python
5
star
28

completions

Shell completions for your program made easy.
Python
5
star
29

argx

Supercharged argparse for Python
Python
4
star
30

jquery.msgbox

A jquery popup plugin
CSS
4
star
31

neutrapy

A CLI tool to build desktop applications with Neutralinojs and Python as backend
Python
4
star
32

pardoc

Yet another docstring parser for python
Python
4
star
33

cmdy

"Shell language" to run command in python
Python
4
star
34

pipen-args

Command line argument parser for pipen
Python
3
star
35

regexr

Regular expressions for humans
Python
3
star
36

pipen-diagram

Draw pipeline diagrams for pipen.
Python
3
star
37

datar-arrow

Python
3
star
38

pipen-report

Report generation system for pipen
HTML
3
star
39

pipen-verbose

Add verbosal information in logs for pipen.
Python
3
star
40

pipen-dry

Dry runner for pipen
Python
2
star
41

datar-polars

Python
2
star
42

pipen-filters

Add a set of useful filters for pipen templates.
Python
2
star
43

immunopipe-example

An example of immunopipe
CSS
2
star
44

pipen-cli-require

Checking the requirements for processes of a pipeline
Python
2
star
45

pipen-cli-init

A pipen CLI plugin to create a pipen project (pipeline)
Python
2
star
46

pipen-annotate

Use docstring to annotate pipen processes
Python
2
star
47

benchwork

A framework for benchmarking in python
Python
2
star
48

immunopipe-AdrienneML-2020

Reanalysis of the scRNA-seq and scTCR-seq data from Luoma, Adrienne M., et al. 2020 using immunopipe.
HTML
2
star
49

pyppl_echo

Echo script output to PyPPL logs
Python
1
star
50

pyppl_rich

Richer information in logs for PyPPL
Python
1
star
51

pyppl_export

Python
1
star
52

pwwang

1
star
53

wigtools

A set of tools for wiggle file
Python
1
star
54

datar-blog

A blog about datar
Python
1
star
55

pyppl_runcmd

Allowing to run local command before and after each process for PyPPL
Python
1
star
56

pipen-log2file

Save running logs to file for pipen
Python
1
star
57

pygff

A more general GFF/GTF parser.
Python
1
star
58

remotedata

Accessing and caching remote data.
Python
1
star
59

pyppl_strict

Python
1
star
60

pyppl_jobtime

Job running time statistics for PyPPL
Python
1
star
61

pyppl_lock

Preventing running processes from running again for PyPPL
Python
1
star
62

pyppl_runners

Some basic runners for PyPPL
Python
1
star
63

pipen-lock

Process lock for pipen to prevent multiple runs at the same time
Python
1
star
64

pyppl_flowchart

Generating flowchart for PyPPL
Python
1
star
65

pyppl_notify

Email notification for PyPPL
Python
1
star
66

mkdocs-material-2020

Try reproducing issue-2020 in mkdocs-material
1
star
67

pipen-runinfo

Generate running information for jobs in pipen pipelines.
Python
1
star
68

conda-jvarkit

Recipts for building jvarkit tools on conda
Python
1
star
69

pyppl_annotate

Adding long description/annotations for PyPPL processes.
Python
1
star
70

pipen-board

Visualizing configuration and running of pipen pipelines on the web
Svelte
1
star
71

pxGPT

pxGPT: Your personal, powerful and private GPT
Python
1
star
72

pipen-cli-run

A pipen cli plugin to run a process or a pipeline
Python
1
star
73

pyppl_context

Upstream and downstream process reference for PyPPL
Python
1
star
74

pyppl_require

Process requirement manager for PyPPL
Python
1
star
75

ceQTL

The co-expression Quantitative Trait Loci pipeline
Python
1
star
76

pyppl_report

A report generating system for PyPPL
JavaScript
1
star
77

Tech-Bytes

Daily digest with curated news, tech posts, articles, and edge technologies from a wide range of reputable sources.
1
star
78

pipen-gcs

Python
1
star
79

gglogger

gglogger is an R package that logs the calls used to create ggplot2 objects.
R
1
star
80

as_yt-dlp

A chrome extension that makes streams as yt-dlp commands for downloading
JavaScript
1
star
81

bioprocs-testdata

Test data for bioprocs
1
star