Skip to content

Commit e00c071

Browse files
authored
Deprecate inject and introduce register to register classes (#30)
* Deprecate inject * Implement #30 * 0.5.5
1 parent 09498d8 commit e00c071

File tree

5 files changed

+91
-18
lines changed

5 files changed

+91
-18
lines changed

README.md

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pip install varname
1919
- Fetching variable names directly using `nameof`
2020
- A value wrapper to store the variable name that a value is assigned to using `Wrapper`
2121
- Detecting next immediate attribute name using `will`
22-
- Injecting `__varname__` to objects
22+
- Injecting `__varname__` to classes
2323
- A `debug` function to print variables with their names and values.
2424

2525
## Credits
@@ -213,26 +213,21 @@ awesome.permit() # AttributeError: Should do something with AwesomeClass object
213213
awesome.permit().do() == 'I am doing!'
214214
```
215215

216-
### Injecting `__varname__`
216+
### Injecting `__varname__` to classes
217217

218218
```python
219-
from varname import inject
219+
from varname import inject_varname
220220

221-
class MyList(list):
221+
@inject_varname
222+
class Dict(dict):
222223
pass
223224

224-
a = inject(MyList())
225-
b = inject(MyList())
226-
225+
a = Dict(a=1)
226+
b = Dict(b=2)
227227
a.__varname__ == 'a'
228228
b.__varname__ == 'b'
229-
230-
a == b
231-
232-
# other methods not affected
233-
a.append(1)
234-
b.append(1)
235-
a == b
229+
a.update(b)
230+
a == {'a':1, 'b':2}
236231
```
237232

238233
### Debugging with `debug`

docs/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## v0.5.5
2+
- Deprecate inject and use inject_varname decorator instead
3+
14
## v0.5.4
25
- Allow `varname.varname` to receive multiple variables on the left-hand side
36

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "poetry.masonry.api"
44

55
[tool.poetry]
66
name = "varname"
7-
version = "0.5.4"
7+
version = "0.5.5"
88
description = "Dark magics about variable names in python."
99
authors = [ "pwwang <[email protected]>",]
1010
license = "MIT"

tests/test_varname.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,3 +572,18 @@ def test_debug(capsys):
572572
assert 'DEBUG: a=1\n' == capsys.readouterr().out
573573
debug(a, b, merge=True)
574574
assert 'DEBUG: a=1, b=<object' in capsys.readouterr().out
575+
576+
def test_inject_varname():
577+
578+
@inject_varname
579+
class Foo:
580+
def __init__(self, a=1):
581+
self.a = a
582+
583+
f = Foo()
584+
assert f.__varname__ == 'f'
585+
assert f.a == 1
586+
587+
f2 = Foo(2)
588+
assert f2.__varname__ == 'f2'
589+
assert f2.a == 2

varname.py

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@
33
import dis
44
import sys
55
import warnings
6-
from typing import Union, Tuple, Any, Optional
6+
from typing import Callable, Union, Tuple, Any, Optional
77
from types import FrameType, CodeType
88
from collections import namedtuple as standard_namedtuple
9+
from functools import wraps
910
from functools import lru_cache
1011

1112
import executing
1213

13-
__version__ = "0.5.4"
14+
__version__ = "0.5.5"
1415
__all__ = [
15-
"VarnameRetrievingError", "varname", "will",
16+
"VarnameRetrievingError", "varname", "will", "inject_varname",
1617
"inject", "nameof", "namedtuple", "Wrapper", "debug"
1718
]
1819

@@ -151,6 +152,62 @@ def will(caller: int = 1, raise_exc: bool = True) -> Optional[str]:
151152
# ast.Attribute
152153
return node.attr
153154

155+
def inject_varname(
156+
cls: type = None, *,
157+
caller: int = 1,
158+
multi_vars: bool = False,
159+
raise_exc: bool = True
160+
) -> Union[type, Callable[[type], type]]:
161+
"""A decorator to inject __varname__ attribute to a class
162+
163+
Args:
164+
caller: The call stack index, indicating where this class
165+
is instantiated relative to where the variable is finally retrieved
166+
multi_vars: Whether allow multiple variables on left-hand side (LHS).
167+
If `True`, this function returns a tuple of the variable names,
168+
even there is only one variable on LHS.
169+
If `False`, and multiple variables on LHS, a
170+
`VarnameRetrievingError` will be raised.
171+
raise_exc: Whether we should raise an exception if failed
172+
to retrieve the name.
173+
174+
Examples:
175+
>>> @inject_varname
176+
>>> class Foo: pass
177+
>>> foo = Foo()
178+
>>> # foo.__varname__ == 'foo'
179+
180+
Returns:
181+
The wrapper function or the class itself if it is specified explictly.
182+
"""
183+
if cls is not None:
184+
# Used as @inject_varname directly
185+
return inject_varname(
186+
caller=caller,
187+
multi_vars=multi_vars,
188+
raise_exc=raise_exc
189+
)(cls)
190+
191+
# Used as @inject_varname(multi_vars=..., raise_exc=...)
192+
def wrapper(cls):
193+
"""The wrapper function to wrap a class and inject `__varname__`"""
194+
orig_init = cls.__init__
195+
196+
@wraps(cls.__init__)
197+
def wrapped_init(self, *args, **kwargs):
198+
"""Wrapped init function to replace the original one"""
199+
self.__varname__ = varname(
200+
caller=caller,
201+
multi_vars=multi_vars,
202+
raise_exc=raise_exc
203+
)
204+
orig_init(self, *args, **kwargs)
205+
206+
cls.__init__ = wrapped_init
207+
return cls
208+
209+
return wrapper
210+
154211
def inject(obj: object) -> object:
155212
"""Inject attribute `__varname__` to an object
156213
@@ -181,6 +238,9 @@ def inject(obj: object) -> object:
181238
Returns:
182239
The object with __varname__ injected
183240
"""
241+
warnings.warn("Function inject will be removed in 0.6.0. Use "
242+
"varname.inject_varname to decorate your class.",
243+
DeprecationWarning)
184244
vname = varname()
185245
try:
186246
setattr(obj, '__varname__', vname)

0 commit comments

Comments
 (0)