Repository: napari/napari

Issue:
magic_name should work for all layer types
## 🐛 Bug

<!-- A clear and concise description of what the bug is. -->

The `magic_name` function should work for all layer types, imo.

https://github.com/napari/napari/blob/5e8be0987b3ab691da5755380ce1dd798978b1c6/napari/utils/naming.py#L49

## To Reproduce

Steps to reproduce the behavior:

```python
viewer = napari.Viewer()

my_points = np.random.random((2, 10))
points_layer = viewer.add_points(my_points)

print(points_layer.name)
# expected output "my_points", got "data" instead
```
![napari-magicname-screenshot1](https://user-images.githubusercontent.com/30920819/95419212-dd6a5b00-0984-11eb-8dde-a804670b8e82.png)

```python
my_image = np.random.random((100, 100))
image_layer = viewer.add_image(my_image)

print(image_layer.name)
# expected output "my_image", got expected result
```
![napari-magicname-screenshot2](https://user-images.githubusercontent.com/30920819/95419234-ed823a80-0984-11eb-90fb-997a60b6b360.png)



<!-- If you have a code sample, error messages, stack traces, please provide it here as well -->

## Expected behavior

<!-- A clear and concise description of what you expected to happen. -->
I expect if I add a points layer where the name of the point data variable is "my_points", then the name of the napari layer should also be "my_points".

## Environment

 - Please copy and paste the information at napari info option in help menubar here:

```
# Name                    Version                   Build  Channel
alabaster                 0.7.12                   pypi_0    pypi
appdirs                   1.4.4                    pypi_0    pypi
argon2-cffi               20.1.0                   pypi_0    pypi
asciitree                 0.3.3                    pypi_0    pypi
async-generator           1.10                     pypi_0    pypi
atomicwrites              1.4.0                    pypi_0    pypi
attrs                     20.2.0                   pypi_0    pypi
babel                     2.8.0                    pypi_0    pypi
backcall                  0.2.0                    pypi_0    pypi
black                     19.10b0                  pypi_0    pypi
bleach                    3.2.1                    pypi_0    pypi
ca-certificates           2020.7.22                     0  
cachey                    0.2.1                    pypi_0    pypi
certifi                   2020.6.20                py37_0  
cffi                      1.14.3                   pypi_0    pypi
cfgv                      3.2.0                    pypi_0    pypi
chardet                   3.0.4                    pypi_0    pypi
check-manifest            0.44                     pypi_0    pypi
click                     7.1.2                    pypi_0    pypi
colorama                  0.4.3                    pypi_0    pypi
commonmark                0.9.1                    pypi_0    pypi
cycler                    0.10.0                   pypi_0    pypi
dask                      2.30.0                   pypi_0    pypi
decorator                 4.4.2                    pypi_0    pypi
defusedxml                0.6.0                    pypi_0    pypi
distlib                   0.3.1                    pypi_0    pypi
docutils                  0.16                     pypi_0    pypi
entrypoints               0.3                      pypi_0    pypi
fasteners                 0.15                     pypi_0    pypi
filelock                  3.0.12                   pypi_0    pypi
flake8                    3.8.3                    pypi_0    pypi
freetype-py               2.2.0                    pypi_0    pypi
fsspec                    0.8.3                    pypi_0    pypi
heapdict                  1.0.1                    pypi_0    pypi
identify                  1.5.5                    pypi_0    pypi
idna                      2.10                     pypi_0    pypi
imageio                   2.9.0                    pypi_0    pypi
imagesize                 1.2.0                    pypi_0    pypi
importlib-metadata        2.0.0                    pypi_0    pypi
iniconfig                 1.0.1                    pypi_0    pypi
ipykernel                 5.3.4                    pypi_0    pypi
ipython                   7.18.1                   pypi_0    pypi
ipython-genutils          0.2.0                    pypi_0    pypi
ipywidgets                7.5.1                    pypi_0    pypi
jedi                      0.17.2                   pypi_0    pypi
jinja2                    2.11.2                   pypi_0    pypi
jsonschema                3.2.0                    pypi_0    pypi
jupyter                   1.0.0                    pypi_0    pypi
jupyter-client            6.1.7                    pypi_0    pypi
jupyter-console           6.2.0                    pypi_0    pypi
jupyter-core              4.6.3                    pypi_0    pypi
jupyterlab-pygments       0.1.2                    pypi_0    pypi
kiwisolver                1.2.0                    pypi_0    pypi
markupsafe                1.1.1                    pypi_0    pypi
matplotlib                3.3.2                    pypi_0    pypi
mccabe                    0.6.1                    pypi_0    pypi
mistune                   0.8.4                    pypi_0    pypi
monotonic                 1.5                      pypi_0    pypi
napari                    0.3.9.dev53+gb230dc22           dev_0    <develop>
napari-plugin-engine      0.1.8                    pypi_0    pypi
napari-svg                0.1.4                    pypi_0    pypi
nbclient                  0.5.0                    pypi_0    pypi
nbconvert                 6.0.7                    pypi_0    pypi
nbformat                  5.0.7                    pypi_0    pypi
nest-asyncio              1.4.1                    pypi_0    pypi
networkx                  2.5                      pypi_0    pypi
nodeenv                   1.5.0                    pypi_0    pypi
notebook                  6.1.4                    pypi_0    pypi
numcodecs                 0.7.2                    pypi_0    pypi
numpy                     1.19.2                   pypi_0    pypi
numpydoc                  1.1.0                    pypi_0    pypi
openssl                   1.1.1h               he774522_0  
packaging                 20.4                     pypi_0    pypi
pandas                    1.1.3                    pypi_0    pypi
pandocfilters             1.4.2                    pypi_0    pypi
parso                     0.7.1                    pypi_0    pypi
pathspec                  0.8.0                    pypi_0    pypi
pep517                    0.8.2                    pypi_0    pypi
pickleshare               0.7.5                    pypi_0    pypi
pillow                    7.2.0                    pypi_0    pypi
pip                       20.2.3                   py37_0  
pluggy                    0.13.1                   pypi_0    pypi
pooch                     1.2.0                    pypi_0    pypi
pre-commit                2.7.1                    pypi_0    pypi
prometheus-client         0.8.0                    pypi_0    pypi
prompt-toolkit            3.0.7                    pypi_0    pypi
psutil                    5.7.2                    pypi_0    pypi
py                        1.9.0                    pypi_0    pypi
pycodestyle               2.6.0                    pypi_0    pypi
pycparser                 2.20                     pypi_0    pypi
pyflakes                  2.2.0                    pypi_0    pypi
pygments                  2.7.1                    pypi_0    pypi
pyopengl                  3.1.5                    pypi_0    pypi
pyparsing                 2.4.7                    pypi_0    pypi
pyqt5                     5.14.2                   pypi_0    pypi
pyqt5-sip                 12.8.1                   pypi_0    pypi
pyrsistent                0.17.3                   pypi_0    pypi
pytest                    6.1.1                    pypi_0    pypi
pytest-faulthandler       2.0.1                    pypi_0    pypi
pytest-ordering           0.6                      pypi_0    pypi
pytest-qt                 3.3.0                    pypi_0    pypi
pytest-timeout            1.4.2                    pypi_0    pypi
python                    3.7.9                h60c2a47_0  
python-dateutil           2.8.1                    pypi_0    pypi
pytz                      2020.1                   pypi_0    pypi
pywavelets                1.1.1                    pypi_0    pypi
pywin32                   228                      pypi_0    pypi
pywinpty                  0.5.7                    pypi_0    pypi
pyyaml                    5.3.1                    pypi_0    pypi
pyzmq                     19.0.2                   pypi_0    pypi
qtconsole                 4.7.7                    pypi_0    pypi
qtpy                      1.9.0                    pypi_0    pypi
recommonmark              0.6.0                    pypi_0    pypi
regex                     2020.9.27                pypi_0    pypi
requests                  2.24.0                   pypi_0    pypi
scikit-image              0.17.2                   pypi_0    pypi
scipy                     1.5.2                    pypi_0    pypi
send2trash                1.5.0                    pypi_0    pypi
setuptools                50.3.0           py37h9490d1a_1  
six                       1.15.0                   pypi_0    pypi
snowballstemmer           2.0.0                    pypi_0    pypi
sphinx                    3.0.1                    pypi_0    pypi
sphinx-rtd-theme          0.4.3                    pypi_0    pypi
sphinxcontrib-applehelp   1.0.2                    pypi_0    pypi
sphinxcontrib-devhelp     1.0.2                    pypi_0    pypi
sphinxcontrib-htmlhelp    1.0.3                    pypi_0    pypi
sphinxcontrib-jsmath      1.0.1                    pypi_0    pypi
sphinxcontrib-qthelp      1.0.3                    pypi_0    pypi
sphinxcontrib-serializinghtml 1.1.4                    pypi_0    pypi
sqlite                    3.33.0               h2a8f88b_0  
terminado                 0.9.1                    pypi_0    pypi
testpath                  0.4.4                    pypi_0    pypi
tifffile                  2020.10.1                pypi_0    pypi
toml                      0.10.1                   pypi_0    pypi
toolz                     0.11.1                   pypi_0    pypi
tornado                   6.0.4                    pypi_0    pypi
traitlets                 5.0.4                    pypi_0    pypi
typed-ast                 1.4.1                    pypi_0    pypi
typing-extensions         3.7.4.3                  pypi_0    pypi
urllib3                   1.25.10                  pypi_0    pypi
vc                        14.1                 h0510ff6_4  
virtualenv                20.0.33                  pypi_0    pypi
vispy                     0.6.5                    pypi_0    pypi
vs2015_runtime            14.16.27012          hf0eaf9b_3  
wcwidth                   0.2.5                    pypi_0    pypi
webencodings              0.5.1                    pypi_0    pypi
wheel                     0.35.1                     py_0  
widgetsnbextension        3.5.1                    pypi_0    pypi
wincertstore              0.2                      py37_0  
wrapt                     1.12.1                   pypi_0    pypi
xarray                    0.16.1                   pypi_0    pypi
zarr                      2.5.0                    pypi_0    pypi
zipp                      3.3.0                    pypi_0    pypi
zlib                      1.2.11               h62dcd97_4  
```

 - Any other relevant information:

## Additional context

<!-- Add any other context about the problem here. -->

magic_name should work for all layer types
## 🐛 Bug

<!-- A clear and concise description of what the bug is. -->

The `magic_name` function should work for all layer types, imo.

https://github.com/napari/napari/blob/5e8be0987b3ab691da5755380ce1dd798978b1c6/napari/utils/naming.py#L49

## To Reproduce

Steps to reproduce the behavior:

```python
viewer = napari.Viewer()

my_points = np.random.random((2, 10))
points_layer = viewer.add_points(my_points)

print(points_layer.name)
# expected output "my_points", got "data" instead
```
![napari-magicname-screenshot1](https://user-images.githubusercontent.com/30920819/95419212-dd6a5b00-0984-11eb-8dde-a804670b8e82.png)

```python
my_image = np.random.random((100, 100))
image_layer = viewer.add_image(my_image)

print(image_layer.name)
# expected output "my_image", got expected result
```
![napari-magicname-screenshot2](https://user-images.githubusercontent.com/30920819/95419234-ed823a80-0984-11eb-90fb-997a60b6b360.png)



<!-- If you have a code sample, error messages, stack traces, please provide it here as well -->

## Expected behavior

<!-- A clear and concise description of what you expected to happen. -->
I expect if I add a points layer where the name of the point data variable is "my_points", then the name of the napari layer should also be "my_points".

## Environment

 - Please copy and paste the information at napari info option in help menubar here:

```
# Name                    Version                   Build  Channel
alabaster                 0.7.12                   pypi_0    pypi
appdirs                   1.4.4                    pypi_0    pypi
argon2-cffi               20.1.0                   pypi_0    pypi
asciitree                 0.3.3                    pypi_0    pypi
async-generator           1.10                     pypi_0    pypi
atomicwrites              1.4.0                    pypi_0    pypi
attrs                     20.2.0                   pypi_0    pypi
babel                     2.8.0                    pypi_0    pypi
backcall                  0.2.0                    pypi_0    pypi
black                     19.10b0                  pypi_0    pypi
bleach                    3.2.1                    pypi_0    pypi
ca-certificates           2020.7.22                     0  
cachey                    0.2.1                    pypi_0    pypi
certifi                   2020.6.20                py37_0  
cffi                      1.14.3                   pypi_0    pypi
cfgv                      3.2.0                    pypi_0    pypi
chardet                   3.0.4                    pypi_0    pypi
check-manifest            0.44                     pypi_0    pypi
click                     7.1.2                    pypi_0    pypi
colorama                  0.4.3                    pypi_0    pypi
commonmark                0.9.1                    pypi_0    pypi
cycler                    0.10.0                   pypi_0    pypi
dask                      2.30.0                   pypi_0    pypi
decorator                 4.4.2                    pypi_0    pypi
defusedxml                0.6.0                    pypi_0    pypi
distlib                   0.3.1                    pypi_0    pypi
docutils                  0.16                     pypi_0    pypi
entrypoints               0.3                      pypi_0    pypi
fasteners                 0.15                     pypi_0    pypi
filelock                  3.0.12                   pypi_0    pypi
flake8                    3.8.3                    pypi_0    pypi
freetype-py               2.2.0                    pypi_0    pypi
fsspec                    0.8.3                    pypi_0    pypi
heapdict                  1.0.1                    pypi_0    pypi
identify                  1.5.5                    pypi_0    pypi
idna                      2.10                     pypi_0    pypi
imageio                   2.9.0                    pypi_0    pypi
imagesize                 1.2.0                    pypi_0    pypi
importlib-metadata        2.0.0                    pypi_0    pypi
iniconfig                 1.0.1                    pypi_0    pypi
ipykernel                 5.3.4                    pypi_0    pypi
ipython                   7.18.1                   pypi_0    pypi
ipython-genutils          0.2.0                    pypi_0    pypi
ipywidgets                7.5.1                    pypi_0    pypi
jedi                      0.17.2                   pypi_0    pypi
jinja2                    2.11.2                   pypi_0    pypi
jsonschema                3.2.0                    pypi_0    pypi
jupyter                   1.0.0                    pypi_0    pypi
jupyter-client            6.1.7                    pypi_0    pypi
jupyter-console           6.2.0                    pypi_0    pypi
jupyter-core              4.6.3                    pypi_0    pypi
jupyterlab-pygments       0.1.2                    pypi_0    pypi
kiwisolver                1.2.0                    pypi_0    pypi
markupsafe                1.1.1                    pypi_0    pypi
matplotlib                3.3.2                    pypi_0    pypi
mccabe                    0.6.1                    pypi_0    pypi
mistune                   0.8.4                    pypi_0    pypi
monotonic                 1.5                      pypi_0    pypi
napari                    0.3.9.dev53+gb230dc22           dev_0    <develop>
napari-plugin-engine      0.1.8                    pypi_0    pypi
napari-svg                0.1.4                    pypi_0    pypi
nbclient                  0.5.0                    pypi_0    pypi
nbconvert                 6.0.7                    pypi_0    pypi
nbformat                  5.0.7                    pypi_0    pypi
nest-asyncio              1.4.1                    pypi_0    pypi
networkx                  2.5                      pypi_0    pypi
nodeenv                   1.5.0                    pypi_0    pypi
notebook                  6.1.4                    pypi_0    pypi
numcodecs                 0.7.2                    pypi_0    pypi
numpy                     1.19.2                   pypi_0    pypi
numpydoc                  1.1.0                    pypi_0    pypi
openssl                   1.1.1h               he774522_0  
packaging                 20.4                     pypi_0    pypi
pandas                    1.1.3                    pypi_0    pypi
pandocfilters             1.4.2                    pypi_0    pypi
parso                     0.7.1                    pypi_0    pypi
pathspec                  0.8.0                    pypi_0    pypi
pep517                    0.8.2                    pypi_0    pypi
pickleshare               0.7.5                    pypi_0    pypi
pillow                    7.2.0                    pypi_0    pypi
pip                       20.2.3                   py37_0  
pluggy                    0.13.1                   pypi_0    pypi
pooch                     1.2.0                    pypi_0    pypi
pre-commit                2.7.1                    pypi_0    pypi
prometheus-client         0.8.0                    pypi_0    pypi
prompt-toolkit            3.0.7                    pypi_0    pypi
psutil                    5.7.2                    pypi_0    pypi
py                        1.9.0                    pypi_0    pypi
pycodestyle               2.6.0                    pypi_0    pypi
pycparser                 2.20                     pypi_0    pypi
pyflakes                  2.2.0                    pypi_0    pypi
pygments                  2.7.1                    pypi_0    pypi
pyopengl                  3.1.5                    pypi_0    pypi
pyparsing                 2.4.7                    pypi_0    pypi
pyqt5                     5.14.2                   pypi_0    pypi
pyqt5-sip                 12.8.1                   pypi_0    pypi
pyrsistent                0.17.3                   pypi_0    pypi
pytest                    6.1.1                    pypi_0    pypi
pytest-faulthandler       2.0.1                    pypi_0    pypi
pytest-ordering           0.6                      pypi_0    pypi
pytest-qt                 3.3.0                    pypi_0    pypi
pytest-timeout            1.4.2                    pypi_0    pypi
python                    3.7.9                h60c2a47_0  
python-dateutil           2.8.1                    pypi_0    pypi
pytz                      2020.1                   pypi_0    pypi
pywavelets                1.1.1                    pypi_0    pypi
pywin32                   228                      pypi_0    pypi
pywinpty                  0.5.7                    pypi_0    pypi
pyyaml                    5.3.1                    pypi_0    pypi
pyzmq                     19.0.2                   pypi_0    pypi
qtconsole                 4.7.7                    pypi_0    pypi
qtpy                      1.9.0                    pypi_0    pypi
recommonmark              0.6.0                    pypi_0    pypi
regex                     2020.9.27                pypi_0    pypi
requests                  2.24.0                   pypi_0    pypi
scikit-image              0.17.2                   pypi_0    pypi
scipy                     1.5.2                    pypi_0    pypi
send2trash                1.5.0                    pypi_0    pypi
setuptools                50.3.0           py37h9490d1a_1  
six                       1.15.0                   pypi_0    pypi
snowballstemmer           2.0.0                    pypi_0    pypi
sphinx                    3.0.1                    pypi_0    pypi
sphinx-rtd-theme          0.4.3                    pypi_0    pypi
sphinxcontrib-applehelp   1.0.2                    pypi_0    pypi
sphinxcontrib-devhelp     1.0.2                    pypi_0    pypi
sphinxcontrib-htmlhelp    1.0.3                    pypi_0    pypi
sphinxcontrib-jsmath      1.0.1                    pypi_0    pypi
sphinxcontrib-qthelp      1.0.3                    pypi_0    pypi
sphinxcontrib-serializinghtml 1.1.4                    pypi_0    pypi
sqlite                    3.33.0               h2a8f88b_0  
terminado                 0.9.1                    pypi_0    pypi
testpath                  0.4.4                    pypi_0    pypi
tifffile                  2020.10.1                pypi_0    pypi
toml                      0.10.1                   pypi_0    pypi
toolz                     0.11.1                   pypi_0    pypi
tornado                   6.0.4                    pypi_0    pypi
traitlets                 5.0.4                    pypi_0    pypi
typed-ast                 1.4.1                    pypi_0    pypi
typing-extensions         3.7.4.3                  pypi_0    pypi
urllib3                   1.25.10                  pypi_0    pypi
vc                        14.1                 h0510ff6_4  
virtualenv                20.0.33                  pypi_0    pypi
vispy                     0.6.5                    pypi_0    pypi
vs2015_runtime            14.16.27012          hf0eaf9b_3  
wcwidth                   0.2.5                    pypi_0    pypi
webencodings              0.5.1                    pypi_0    pypi
wheel                     0.35.1                     py_0  
widgetsnbextension        3.5.1                    pypi_0    pypi
wincertstore              0.2                      py37_0  
wrapt                     1.12.1                   pypi_0    pypi
xarray                    0.16.1                   pypi_0    pypi
zarr                      2.5.0                    pypi_0    pypi
zipp                      3.3.0                    pypi_0    pypi
zlib                      1.2.11               h62dcd97_4  
```

 - Any other relevant information:

## Additional context

<!-- Add any other context about the problem here. -->



Discussion:

Resolving patch:
diff --git a/napari/utils/naming.py b/napari/utils/naming.py
--- a/napari/utils/naming.py
+++ b/napari/utils/naming.py
@@ -61,20 +61,44 @@ def magic_name(value, *, path_prefix):
     name : str or None
         Name of the variable, if found.
     """
-    frame = inspect.currentframe().f_back
-    code = frame.f_code
-
-    # Iterate frames while filename starts with path_prefix or autogenerated
-    while (
-        code.co_filename.startswith(path_prefix)
-        or code.co_filename == "<string>"
-    ):
-        frame = frame.f_back
-        code = frame.f_code
-
-    varmap = ChainMap(frame.f_locals, frame.f_globals)
-    names = *code.co_varnames, *code.co_names
-
-    for name in names:
-        if name.isidentifier() and name in varmap and varmap[name] is value:
-            return name
+    frame = inspect.currentframe()
+    try:
+        # See issue #1635 regarding potential AttributeError
+        # since frame could be None.
+        # https://github.com/napari/napari/pull/1635
+        if inspect.isframe(frame):
+            frame = frame.f_back
+
+        # Iterate frames while filename starts with path_prefix (part of Napari)
+        # or is autogenerated such as for the add_* for layers (#1694 / #1709)
+        while (
+            inspect.isframe(frame)
+            and inspect.iscode(frame.f_code)
+            and (
+                frame.f_code.co_filename.startswith(path_prefix)
+                or frame.f_code.co_filename == "<string>"
+            )
+        ):
+            frame = frame.f_back
+
+        if inspect.isframe(frame) and inspect.iscode(frame.f_code):
+            varmap = ChainMap(frame.f_locals, frame.f_globals)
+            names = *frame.f_code.co_varnames, *frame.f_code.co_names
+
+            for name in names:
+                if (
+                    name.isidentifier()
+                    and name in varmap
+                    and varmap[name] is value
+                ):
+                    return name
+        return None
+    finally:
+        # We need to delete the frame explicitly according to the inspect
+        # documentation for deterministic removal of the frame.
+        # Otherwise, proper deletion is dependent on a cycle detector and
+        # automatic garbage collection.
+        # See handle_stackframe_without_leak example at the following URLs:
+        # https://docs.python.org/3/library/inspect.html#the-interpreter-stack
+        # https://bugs.python.org/issue543148
+        del frame


Test patch:
diff --git a/napari/utils/_tests/test_naming.py b/napari/utils/_tests/test_naming.py
--- a/napari/utils/_tests/test_naming.py
+++ b/napari/utils/_tests/test_naming.py
@@ -116,3 +116,25 @@ def bar(y):
 
     if walrus:
         assert eval_with_filename('foo(i:=33)', 'rye.py') == 'i'
+
+
+def test_empty_path_prefix():
+    """Test an empty path prefix that matches the entire stack"""
+    # Repeat tests with an empty path_prefix
+    mname = functools.partial(magic_name, path_prefix="")
+
+    def foo(x):
+        def bar(y):
+            return mname(y)
+
+        return bar(x)
+
+    # Test are all None because the path_prefix matches everything
+    # magic_name reads through until the end of the stack
+    assert eval_with_filename('foo(42)', 'hi.py') is None
+
+    r = 8  # noqa
+    assert eval_with_filename('foo(r)', 'bye.py') is None
+
+    if walrus:
+        assert eval_with_filename('foo(i:=33)', 'rye.py') is None