Skip to content

Commit c58d3d8

Browse files
authored
Drop support for pytest, don't try to find node when executing fails (#15)
* Drop support for pytest, don't try to find node when executing fails * Check that bytecode_nameof returns a valid identifier * Test nameof failing under pytest * pylint
1 parent 0a7370e commit c58d3d8

File tree

2 files changed

+100
-108
lines changed

2 files changed

+100
-108
lines changed

tests/test_varname.py

Lines changed: 84 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import sys
2+
import unittest
3+
24
import pytest
35
from varname import (varname,
46
VarnameRetrievingWarning,
@@ -26,12 +28,6 @@ def nameof(*args):
2628
varname_module.nameof = nameof
2729

2830

29-
def test_original_nameof():
30-
x = 1
31-
assert original_nameof(x) == nameof(x) == _bytecode_nameof(x) == 'x'
32-
33-
34-
3531
@pytest.fixture
3632
def no_getframe():
3733
"""
@@ -258,74 +254,103 @@ def test_wrapper():
258254
assert str(val1) == 'True'
259255
assert repr(val1) == "<Wrapper (name='val1', value=True)>"
260256

261-
def test_nameof():
262-
a = 1
263-
b = nameof(a)
264-
assert b == 'a'
265-
nameof2 = nameof
266-
c = nameof2(a, b)
267-
assert b == 'a'
268-
assert c == ('a', 'b')
269257

270-
def func():
271-
return varname() + 'abc'
258+
class TestNameof(unittest.TestCase):
259+
def test_original_nameof(self):
260+
x = 1
261+
self.assertEqual(original_nameof(x), 'x')
262+
self.assertEqual(nameof(x), 'x')
263+
self.assertEqual(_bytecode_nameof(x), 'x')
272264

273-
f = func()
274-
assert f == 'fabc'
265+
def test_nameof(self):
266+
a = 1
267+
b = nameof(a)
268+
assert b == 'a'
269+
nameof2 = nameof
270+
c = nameof2(a, b)
271+
assert b == 'a'
272+
assert c == ('a', 'b')
275273

276-
assert nameof(f) == 'f'
277-
assert 'f' == nameof(f)
278-
assert len(nameof(f)) == 1
274+
def func():
275+
return varname() + 'abc'
279276

280-
fname1 = fname = nameof(f)
281-
assert fname1 == fname == 'f'
277+
f = func()
278+
assert f == 'fabc'
282279

283-
with pytest.raises(VarnameRetrievingError):
284-
nameof(a==1)
280+
self.assertEqual(nameof(f), 'f')
281+
self.assertEqual('f', nameof(f))
282+
self.assertEqual(len(nameof(f)), 1)
285283

286-
with pytest.raises(VarnameRetrievingError):
287-
_bytecode_nameof(a == 1)
284+
fname1 = fname = nameof(f)
285+
self.assertEqual(fname, 'f')
286+
self.assertEqual(fname1, 'f')
288287

289-
with pytest.raises(VarnameRetrievingError):
290-
nameof()
288+
with pytest.raises(VarnameRetrievingError):
289+
nameof(a==1)
291290

292-
def test_nameof_statements():
293-
a = {'test': 1}
294-
test = {}
295-
del a[nameof(test)]
296-
assert a == {}
291+
with pytest.raises(VarnameRetrievingError):
292+
_bytecode_nameof(a == 1)
297293

298-
def func():
299-
return nameof(test)
294+
with pytest.raises(VarnameRetrievingError):
295+
nameof()
300296

301-
assert func() == 'test'
297+
def test_nameof_statements(self):
298+
a = {'test': 1}
299+
test = {}
300+
del a[nameof(test)]
301+
assert a == {}
302302

303-
def func2():
304-
yield nameof(test)
303+
def func():
304+
return nameof(test)
305+
306+
assert func() == 'test'
307+
308+
def func2():
309+
yield nameof(test)
305310

306-
assert list(func2()) == ['test']
311+
assert list(func2()) == ['test']
307312

308-
def func3():
309-
raise ValueError(nameof(test))
313+
def func3():
314+
raise ValueError(nameof(test))
310315

311-
with pytest.raises(ValueError) as verr:
312-
func3()
313-
assert str(verr.value) == 'test'
316+
with pytest.raises(ValueError) as verr:
317+
func3()
318+
assert str(verr.value) == 'test'
314319

315-
for i in [0]:
316-
assert nameof(test) == 'test'
317-
assert len(nameof(test)) == 4
320+
for i in [0]:
321+
self.assertEqual(nameof(test), 'test')
322+
self.assertEqual(len(nameof(test)), 4)
318323

319-
def test_nameof_expr():
320-
test = {}
321-
assert len(varname_module.nameof(test)) == 4
324+
def test_nameof_expr(self):
325+
test = {}
326+
self.assertEqual(len(varname_module.nameof(test)), 4)
327+
328+
lam = lambda: 0
329+
lam.a = 1
330+
with pytest.raises(VarnameRetrievingError) as vrerr:
331+
varname_module.nameof(test, lam.a)
332+
assert str(vrerr.value) == ("Only variables should "
333+
"be passed to nameof.")
334+
335+
336+
def test_nameof_pytest_fail():
337+
with pytest.raises(
338+
VarnameRetrievingError,
339+
match="Couldn't retrieve the call node. "
340+
"This may happen if you're using some other AST magic"
341+
):
342+
assert nameof(nameof) == 'nameof'
343+
344+
345+
def test_bytecode_pytest_nameof_fail():
346+
with pytest.raises(
347+
VarnameRetrievingError,
348+
match="Found the variable name '@py_assert2' which is obviously wrong.",
349+
):
350+
lam = lambda: 0
351+
lam.a = 1
352+
assert _bytecode_nameof(lam.a) == 'a'
322353

323-
lam = lambda: 0
324-
lam.a = 1
325-
with pytest.raises(VarnameRetrievingError) as vrerr:
326-
varname_module.nameof(test, lam.a)
327-
assert str(vrerr.value) == ("Only variables should "
328-
"be passed to nameof.")
329354

330355
def test_class_property():
331356
class C:
@@ -382,7 +407,8 @@ def do(self):
382407
return 'I will do something'
383408

384409
c = C()
385-
assert c.iwill.do() == 'I will do something'
410+
result = c.iwill.do()
411+
assert result == 'I will do something'
386412

387413
def test_will_fail():
388414

varname.py

Lines changed: 16 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ def _get_node(caller):
5050
return exet.node
5151

5252
if exet.source.text and exet.source.tree:
53-
return list(exet.statements)[0]
53+
raise VarnameRetrievingError(
54+
"Couldn't retrieve the call node. "
55+
"This may happen if you're using some other AST magic at the "
56+
"same time, such as pytest, ipython, macropy, or birdseye."
57+
)
5458

5559
return None
5660

@@ -64,53 +68,6 @@ def _lookfor_parent_assign(node):
6468
return node
6569
return None
6670

67-
def _lookfor_child_nameof(node):
68-
"""Look for ast.Call with func=Name(id='nameof',...)"""
69-
# pylint: disable=too-many-return-statements
70-
if isinstance(node, ast.Call):
71-
# if node.func.id == 'nameof':
72-
# return node
73-
74-
# We want to support alias for nameof, i.e. nameof2
75-
# Or called like: varname.nameof(test)
76-
# If all args are ast.Name, then if must be alias of nameof
77-
# Since this is originated from it, and there is no other
78-
# ast.Call node in args
79-
if not any(isinstance(arg, ast.Call) for arg in node.args):
80-
return node
81-
82-
# print(nameof(test))
83-
for arg in node.args:
84-
found = _lookfor_child_nameof(arg)
85-
if found:
86-
return found
87-
88-
elif isinstance(node, ast.Compare):
89-
# nameof(test) == 'test'
90-
found = _lookfor_child_nameof(node.left)
91-
if found:
92-
return found
93-
# 'test' == nameof(test)
94-
for comp in node.comparators:
95-
found = _lookfor_child_nameof(comp)
96-
if found:
97-
return found
98-
99-
elif isinstance(node, ast.Assert):
100-
# assert nameof(test) == 'test'
101-
found = _lookfor_child_nameof(node.test)
102-
if found:
103-
return found
104-
105-
elif isinstance(node, ast.Expr): # pragma: no cover
106-
# print(nameof(test)) in ipython's forloop
107-
# issue #5
108-
found = _lookfor_child_nameof(node.value)
109-
if found:
110-
return found
111-
112-
return None
113-
11471

11572
def varname(caller=1, raise_exc=False):
11673
"""Get the variable name that assigned by function/class calls
@@ -245,6 +202,7 @@ def inject(obj):
245202
raise VarnameRetrievingError('Unable to inject __varname__.')
246203
return obj
247204

205+
248206
def nameof(*args, caller=1):
249207
"""Get the names of the variables passed in
250208
@@ -255,7 +213,6 @@ def nameof(*args, caller=1):
255213
tuple|str: The names of variables passed in
256214
"""
257215
node = _get_node(caller - 1)
258-
node = _lookfor_child_nameof(node)
259216
if not node:
260217
if len(args) == 1:
261218
return _bytecode_nameof(caller + 1)
@@ -293,7 +250,16 @@ def _bytecode_nameof_cached(code, offset):
293250
name_instruction = instructions[current_instruction_index - 1]
294251
if not name_instruction.opname.startswith("LOAD_"):
295252
raise VarnameRetrievingError("Argument must be a variable or attribute")
296-
return name_instruction.argrepr
253+
254+
name: str = name_instruction.argrepr
255+
if not name.isidentifier():
256+
raise VarnameRetrievingError(
257+
f"Found the variable name {name!r} which is obviously wrong. "
258+
"This may happen if you're using some other AST magic at the "
259+
"same time, such as pytest, ipython, macropy, or birdseye."
260+
)
261+
262+
return name
297263

298264

299265
def namedtuple(*args, **kwargs):

0 commit comments

Comments
 (0)