Skip to content

Commit de05e95

Browse files
pwwangalexmojaki
andauthored
0.6.0 (#39)
* Init 0.6.0 in changelog * Close #36 * Fix #35 * Close #34 * Fix linting * Open ignore to Wrapper and register. * Update README and Changelog * Fix typo in README * Allow ignore to work with modules when source code is not retrievable. * Allow functions in ignore * Refactor to separate ignore functionality. * Rename varname/varname.py to varname/core.py * Update config files * Allow regex for qualname matching in ignore * Ignore standard libraries by default. * Warn when a potential decorated function used directly as an ignore element * Warn when a suspicious decorated function used as ignore element directly * Use fnmatch instead of re for qualname matching for ignore; Check realpath instead of raw for module filename check for ignore. * Split IgnoreModule * Update docs. * Split IgnoreQualname * Update docs. * Try adding python3.9 in CI * Update pylintrc * Apply suggestions from code review Co-authored-by: Alex Hall <[email protected]> * Take suggestions by Alex in the PR. * Take suggestions by Alex in the PR. * Add __varname_ignore_id__ to the module of module-qname ignore pair. * Take suggestions by Alex in the PR. * Submit README.rst, converted by dephell * Fix a typo in test * Change argument module to modname for utils.check_qualname_by_source * Add lru_cache to check_qualname_by_source * Initiate the playground * Finish playground * Add qualname uniqueness check for IgnoreFilenameQualname * Uniform __repr__ and simplify subclass_init for IgnoreElem subclasses * Update varname/ignore.py Co-authored-by: Alex Hall <[email protected]> * Update varname/ignore.py Co-authored-by: Alex Hall <[email protected]> Co-authored-by: Alex Hall <[email protected]>
1 parent a9c86fc commit de05e95

25 files changed

+3089
-1316
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
runs-on: ubuntu-latest
99
strategy:
1010
matrix:
11-
python-version: [3.6, 3.7, 3.8]
11+
python-version: [3.6, 3.7, 3.8, 3.9]
1212

1313
steps:
1414
- uses: actions/checkout@v2
@@ -23,7 +23,7 @@ jobs:
2323
python -m pip install poetry
2424
poetry install -v
2525
- name: Run pylint
26-
run: pylint varname.py
26+
run: pylint varname
2727
- name: Test with pytest
2828
run: poetry run pytest tests/ --junitxml=junit/test-results-${{ matrix.python-version }}.xml
2929
- name: Upload pytest test results

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ repos:
2222
rev: v2.4.4
2323
hooks:
2424
- id: pylint
25-
files: ^varname.py$
25+
files: ^varname/.+$
2626
pass_filenames: false
2727
types: [python]
2828
args: [varname]
@@ -46,4 +46,4 @@ repos:
4646
language: system
4747
args: [tests/]
4848
pass_filenames: false
49-
files: ^tests/.+$|^varname.py$
49+
files: ^tests/.+$|^varname/.+$

.pylintrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@ disable=print-statement,
155155
too-many-arguments,
156156
too-many-return-statements,
157157
too-many-locals,
158-
not-callable
158+
not-callable,
159+
unsubscriptable-object
159160

160161
# Enable the message, report, category or checker with the given id(s). You can
161162
# either give multiple identifier separated by comma (,) or put this option

.tagitrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ checksource = py:False
1212
checktoml = py:False
1313

1414
; The source file or the package name.
15-
versource = varname.py
15+
versource = varname
1616

1717
; The toml file, typically pyproject.toml.
1818
vertoml = ./pyproject.toml

README.md

Lines changed: 92 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,26 @@
66

77
Dark magics about variable names in python
88

9-
[Change Log][16] | [API][15]
9+
[Change Log][16] | [API][15] | [Playground][11]
1010

1111
## Installation
1212
```shell
13-
pip install varname
13+
pip install -U varname
1414
```
1515

1616
## Features
1717

18-
- Fetching variable names from inside the function/class call using `varname`
19-
- Fetching variable names directly using `nameof`
20-
- A value wrapper to store the variable name that a value is assigned to using `Wrapper`
21-
- Detecting next immediate attribute name using `will`
22-
- Injecting `__varname__` to classes
23-
- A `debug` function to print variables with their names and values.
18+
- Core features:
19+
20+
- Retrieving names of variables a function/class call is assigned to from inside it, using `varname`.
21+
- Retrieving variable names directly, using `nameof`
22+
- Detecting next immediate attribute name, using `will`
23+
24+
- Other helper APIs (built based on core features):
25+
26+
- A value wrapper to store the variable name that a value is assigned to, using `Wrapper`
27+
- A decorator to register `__varname__` to functions/classes, using `register`
28+
- A `debug` function to print variables with their names and values
2429

2530
## Credits
2631

@@ -47,9 +52,10 @@ Special thanks to [@HanyuuLu][2] to give up the name `varname` in pypi for this
4752

4853
## Usage
4954

50-
### Retrieving the variable names from inside a function call/class instantiation
55+
### Retrieving the variable names using `varname(...)`
56+
57+
- From inside a function
5158

52-
- From inside a function call
5359
```python
5460
from varname import varname
5561
def function():
@@ -58,45 +64,40 @@ Special thanks to [@HanyuuLu][2] to give up the name `varname` in pypi for this
5864
func = function() # func == 'func'
5965
```
6066

61-
- `varname` calls being buried deeply
62-
67+
When there are intermediate frames:
6368
```python
64-
def function():
65-
# I know that at which frame this will be called
66-
return varname(3)
67-
# with v0.5.6+ now you can also specify a list of intermediate
68-
# calls to be ignored in counting:
69-
# module = sys.modules[__name__]
70-
# return varname(ignore=[(module, 'function1'), (module, 'function2)])
71-
72-
def function1():
69+
def wrapped():
7370
return function()
7471

75-
def function2():
76-
return function1()
72+
def function():
73+
# retrieve the variable name at the 2nd frame from this one
74+
return varname(frame=2)
7775

78-
func = function2() # func == 'func'
76+
func = wrapped() # func == 'func'
7977
```
8078

81-
- `varname` in type annotation or async context
79+
Or use `ignore` to ignore the wrapped frame:
8280
```python
83-
import typing
84-
class Foo:
85-
def __init__(self):
86-
self.id = varname(ignore=[typing])
81+
def wrapped():
82+
return function()
83+
84+
def function():
85+
return varname(ignore=wrapped)
8786

88-
foo: Foo = Foo() # foo.id == 'foo'
87+
func = wrapped() # func == 'func'
8988
```
9089

90+
Calls from standard libraries are ignored by default:
9191
```python
9292
import asyncio
93-
async def func():
94-
return varname(ignore=[asyncio])
9593

96-
x = asyncio.run(func()) # x == 'x'
94+
async def function():
95+
return varname()
96+
97+
func = asyncio.run(function()) # func == 'func'
9798
```
9899

99-
- Retrieving instance name of a class
100+
- Retrieving name of a class instance
100101

101102
```python
102103
class Foo:
@@ -109,17 +110,15 @@ Special thanks to [@HanyuuLu][2] to give up the name `varname` in pypi for this
109110
copied.id = varname() # assign id to whatever variable name
110111
return copied
111112

112-
k = Foo() # k.id == 'k'
113-
# see also register __varname__ to classes
113+
foo = Foo() # foo.id == 'foo'
114114

115-
k2 = k.copy() # k2.id == 'k2'
115+
foo2 = foo.copy() # foo2.id == 'foo2'
116116
```
117117

118118
- Multiple variables on Left-hand side
119119

120120
```python
121121
# since v0.5.4
122-
123122
def func():
124123
return varname(multi_vars=True)
125124

@@ -134,14 +133,17 @@ Special thanks to [@HanyuuLu][2] to give up the name `varname` in pypi for this
134133
- Some unusual use
135134

136135
```python
136+
def function():
137+
return varname()
138+
137139
func = [function()] # func == ['func']
138140

139141
func = [function(), function()] # func == ['func', 'func']
140142

141143
func = function(), function() # func = ('func', 'func')
142144

143145
func = func1 = function() # func == func1 == 'func'
144-
# a warning will be printed
146+
# a warning will be shown
145147
# since you may not want func1 to be 'func'
146148

147149
x = func(y = func()) # x == 'x'
@@ -167,26 +169,44 @@ Special thanks to [@HanyuuLu][2] to give up the name `varname` in pypi for this
167169
a['b'] = get_name(False) # None
168170
```
169171

170-
### Value wrapper
172+
### The decorator way to register `__varname__` to functions/classes
171173

172-
```python
173-
from varname import Wrapper
174+
- Registering `__varname__` to functions
174175

175-
foo = Wrapper(True)
176-
# foo.name == 'foo'
177-
# foo.value == True
178-
bar = Wrapper(False)
179-
# bar.name == 'bar'
180-
# bar.value == False
176+
```python
177+
from varname.helpers import register
181178

182-
def values_to_dict(*args):
183-
return {val.name: val.value for val in args}
179+
@register
180+
def function():
181+
return __varname__
184182

185-
mydict = values_to_dict(foo, bar)
186-
# {'foo': True, 'bar': False}
187-
```
183+
func = function() # func == 'func'
184+
```
185+
186+
```python
187+
# arguments also allowed (frame, ignore and raise_exc)
188+
@register(frame=2)
189+
def function():
190+
return __varname__
191+
192+
def wrapped():
193+
return function()
194+
195+
func = wrapped() # func == 'func'
196+
```
197+
198+
- Registering `__varname__` as a class property
199+
200+
```python
201+
@register
202+
class Foo:
203+
...
204+
205+
foo = Foo()
206+
# foo.__varname__ == 'foo'
207+
```
188208

189-
### Getting variable names directly
209+
### Getting variable names directly using `nameof`
190210

191211
```python
192212
from varname import varname, nameof
@@ -236,25 +256,29 @@ awesome.permit() # AttributeError: Should do something with AwesomeClass object
236256
awesome.permit().do() == 'I am doing!'
237257
```
238258

239-
### Register `__varname__` to classes
259+
### Value wrapper
240260

241261
```python
242-
from varname import register
243-
244-
@register
245-
class Dict(dict):
246-
pass
247-
248-
a = Dict(a=1)
249-
b = Dict(b=2)
250-
a.__varname__ == 'a'
251-
b.__varname__ == 'b'
252-
a.update(b)
253-
a == {'a':1, 'b':2}
262+
from varname.helpers import Wrapper
263+
264+
foo = Wrapper(True)
265+
# foo.name == 'foo'
266+
# foo.value == True
267+
bar = Wrapper(False)
268+
# bar.name == 'bar'
269+
# bar.value == False
270+
271+
def values_to_dict(*args):
272+
return {val.name: val.value for val in args}
273+
274+
mydict = values_to_dict(foo, bar)
275+
# {'foo': True, 'bar': False}
254276
```
255277

256278
### Debugging with `debug`
257279
```python
280+
from varname.helpers import debug
281+
258282
a = 'value'
259283
b = object()
260284
debug(a) # DEBUG: a='value'
@@ -300,6 +324,7 @@ For example:
300324
[8]: https://img.shields.io/pypi/pyversions/varname?style=flat-square
301325
[9]: https://img.shields.io/github/workflow/status/pwwang/python-varname/Build%20Docs?label=docs&style=flat-square
302326
[10]: https://img.shields.io/github/workflow/status/pwwang/python-varname/Build%20and%20Deploy?style=flat-square
327+
[11]: https://mybinder.org/v2/gh/pwwang/python-varname/dev?filepath=playground%2Fplayground.ipynb
303328
[12]: https://img.shields.io/codacy/grade/ed851ff47b194e3e9389b2a44d6f21da?style=flat-square
304329
[13]: https://app.codacy.com/manual/pwwang/python-varname/dashboard
305330
[14]: https://img.shields.io/codacy/coverage/ed851ff47b194e3e9389b2a44d6f21da?style=flat-square

0 commit comments

Comments
 (0)