Skip to content

Examples gallery fails on case insensitive/case preserving systems when napari installed as editable #214

@lucyleeow

Description

@lucyleeow

🐛 Bug

This problem has been discussed in #209 (comment) and #207 (comment) (and following comments).

@aganders3 has been doing all the investigation, just combining information so it's all in one place and so there is a place to discuss this issue.

On case-preserving but case-insensitive filesytem (e.g., macOS) when napari is installed in editable mode, examples gallery will fail.

With scraper added in #207, following error is seen:

Traceback (most recent call last):
  File "/Users/melissa/mambaforge/envs/napari-dev/lib/python3.11/site-packages/sphinx_gallery/scrapers.py", line 340, in save_figures
    rst = scraper(block, block_vars, gallery_conf)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/melissa/projects/napari-docs/docs/conf.py", line 236, in napari_scraper
    napari.Viewer.close_all()
    ^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: module 'napari.Viewer' has no attribute 'close_all'

Similar napari.viewer/napari.Viewer (module/class) confusion has been noted before, notably when executing example custom_key_bindings.py which uses the fixture: @napari.Viewer.bind_key('w') (more details here).

This is due to combination of:

Together these create our problem in the docs build. Here are the steps to failure:

  1. custom_key_bindings.py (maybe others) access napari.Viewer.bind_key()
  2. sphinx-gallery searches for backreferences and in the process tries from napari.Viewer import bind_key
  3. The above import fails, but the damage is done - napari.Viewer is now in sys.modules and points to napari/viewer.py, this part of the import succeeds due to the setuptools issue with case-sensitivity
  4. The next time napari.Viewer is accessed, it bypasses the getattr lazy-loading in napari/__init__.py and instead gives back the module napari.viewer (with the wrong capitalized name). This can happen in the scraper where it accesses napari.Viewer.close_all(), or in the next example that uses napari.Viewer().

We could fix this in Sphinx-Gallery by avoiding importing - @aganders3 suggested to use importlib.util's find_spec/module_from_spec/exec_module, but this only avoids one layer of side-effects as exec_module will mean anything imported inside the module is still imported. I think this fix will at least our error though. Importing in a subprocess will be safest but is slow see: #209 (comment)
Cleaning up sys.modules was also explored but there are potential problems if all references are not cleaned up and we have different module objects existing at the same time (details here).

We could also fix this if setuptools avoids case-insensitive imports.

Thanks to @aganders3 for all the work on this issue 🙏 If there are any inaccuracies, please let me know and I can amend it (as I don't think you can)!

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions