Skip to content

Commit f1c48fd

Browse files
committed
Add better messaging for nameof wierd calls.
1 parent 0341029 commit f1c48fd

File tree

3 files changed

+46
-17
lines changed

3 files changed

+46
-17
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ Dark magics about variable names in python
1010

1111
## Installation
1212
```shell
13-
pip install python-varname # will be deprecated after 0.4.0
14-
pip install varname # use this instead
13+
pip install varname
1514
```
1615

1716
## Features
@@ -44,7 +43,7 @@ Thanks goes to these awesome people/projects:
4443
</tr>
4544
</table>
4645

47-
Special thanks to @HanyuuLu to give up the name `varname` in pypi for this project.
46+
Special thanks to [@HanyuuLu][2] to give up the name `varname` in pypi for this project.
4847

4948
## Usage
5049

@@ -255,6 +254,7 @@ For example:
255254
- `R` with `reticulate`.
256255

257256
[1]: https://github.com/pwwang/python-varname
257+
[2]: https://github.com/HanyuuLu
258258
[3]: https://img.shields.io/pypi/v/varname?style=flat-square
259259
[4]: https://pypi.org/project/varname/
260260
[5]: https://img.shields.io/github/tag/pwwang/python-varname?style=flat-square

tests/test_varname.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import unittest
33

44
import pytest
5+
import subprocess
56
from varname import (varname,
67
VarnameRetrievingError,
78
Wrapper,
@@ -646,6 +647,19 @@ def test_nameof_full():
646647
# we are not able to retreive full names without source code available
647648
with pytest.raises(
648649
VarnameRetrievingError,
649-
match='Did you call nameof in a weird way'
650+
match='Are you trying to call nameof from evaluation'
650651
):
651652
eval('nameof(a.b.c, full=True)')
653+
654+
def test_nameof_from_stdin():
655+
code = ('from varname import nameof; '
656+
'x = lambda: 0; '
657+
'x.y = x; '
658+
'print(nameof(x.y, full=True))')
659+
p = subprocess.Popen([sys.executable],
660+
stdin=subprocess.PIPE,
661+
stdout=subprocess.PIPE,
662+
stderr=subprocess.STDOUT,
663+
encoding='utf8')
664+
out, _ = p.communicate(input=code)
665+
assert 'Are you trying to call nameof in REPL/python shell' in out

varname.py

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,8 @@ class Wrapper:
283283
"""
284284

285285
def __init__(self, value: Any, raise_exc: bool = True):
286-
self.name: str = varname(raise_exc=raise_exc)
287-
self.value: Any = value
286+
self.name = varname(raise_exc=raise_exc)
287+
self.value = value
288288

289289
def __str__(self) -> str:
290290
return repr(self.value)
@@ -310,11 +310,11 @@ def _get_node(caller: int, raise_exc: bool = True) -> Optional[NodeType]:
310310
When the node can not be retrieved, try to return the first statement.
311311
"""
312312
try:
313-
frame: FrameType = _get_frame(caller + 2)
313+
frame = _get_frame(caller + 2)
314314
except VarnameRetrievingError:
315315
return None
316316

317-
exet: executing.Executing = executing.Source.executing(frame)
317+
exet = executing.Source.executing(frame)
318318

319319
if exet.node:
320320
return exet.node
@@ -354,12 +354,19 @@ def _node_name(node: NodeType) -> str:
354354

355355
def _bytecode_nameof(caller: int = 1) -> str:
356356
"""Bytecode version of nameof as a fallback"""
357-
frame: FrameType = _get_frame(caller)
358-
return _bytecode_nameof_cached(frame.f_code, frame.f_lasti)
357+
frame = _get_frame(caller)
358+
source = frame.f_code.co_filename
359+
return _bytecode_nameof_cached(frame.f_code, frame.f_lasti, source)
359360

360361
@lru_cache()
361-
def _bytecode_nameof_cached(code: CodeType, offset: int) -> str:
362-
"""Cached Bytecode version of nameof"""
362+
def _bytecode_nameof_cached(code: CodeType, offset: int, source: str) -> str:
363+
"""Cached Bytecode version of nameof
364+
365+
We are trying this version only when the soucecode is unavisible. In most
366+
cases, this will happen when user is trying to run a script in REPL/
367+
python shell, with `eval`, or other circumstances where the code is
368+
manipulated to run but sourcecode is not available.
369+
"""
363370
instructions = list(dis.get_instructions(code))
364371
(current_instruction_index, current_instruction), = (
365372
(index, instruction)
@@ -368,11 +375,19 @@ def _bytecode_nameof_cached(code: CodeType, offset: int) -> str:
368375
)
369376

370377
if current_instruction.opname not in ("CALL_FUNCTION", "CALL_METHOD"):
371-
raise VarnameRetrievingError(
372-
"Did you call nameof in a weird way? "
373-
"Without soucecode available, nameof can only retrieve the name of "
374-
"a single variable without any keyword arguments being passed in."
375-
)
378+
if source == '<stdin>':
379+
raise VarnameRetrievingError(
380+
"Are you trying to call nameof in REPL/python shell? "
381+
"In such a case, nameof can only be called with single "
382+
"argument and no keyword arguments."
383+
)
384+
if source == '<string>':
385+
raise VarnameRetrievingError(
386+
"Are you trying to call nameof from evaluation? "
387+
"In such a case, nameof can only be called with single "
388+
"argument and no keyword arguments."
389+
)
390+
raise VarnameRetrievingError("Did you call nameof in a weird way? ")
376391

377392
name_instruction = instructions[
378393
current_instruction_index - 1

0 commit comments

Comments
 (0)