Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Bug]: font cache clearing should not need to be done for fresh install... #25306

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
jklymak opened this issue Feb 23, 2023 · 2 comments
Open

Comments

@jklymak
Copy link
Member

jklymak commented Feb 23, 2023

Bug summary

Somewhat a duplicate of #22281, but we should be doing something for users to rebuild the font cache if there is a failure in findfont.

Code for reproduction

New conda environment, with matplotlib 3.7.0


import matplotlib.pyplot as plt

plt.plot(range(10))
plt.xlabel('$\delta$')
plt.show()

Actual outcome

findfont: Font family ['cmsy10'] not found. Falling back to DejaVu Sans.
Traceback (most recent call last):
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/backend_bases.py", line 1222, in _on_timer
    ret = func(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/backends/backend_macosx.py", line 68, in callback_func
    callback()
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/backends/backend_macosx.py", line 88, in _draw_idle
    self.draw()
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/backends/backend_macosx.py", line 50, in draw
    super().draw()
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/backends/backend_agg.py", line 400, in draw
    self.figure.draw(self.renderer)
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/artist.py", line 95, in draw_wrapper
    result = draw(artist, renderer, *args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/artist.py", line 72, in draw_wrapper
    return draw(artist, renderer)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/figure.py", line 3125, in draw
    mimage._draw_list_compositing_images(
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/image.py", line 131, in _draw_list_compositing_images
    a.draw(renderer)
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/artist.py", line 72, in draw_wrapper
    return draw(artist, renderer)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/axes/_base.py", line 3066, in draw
    mimage._draw_list_compositing_images(
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/image.py", line 131, in _draw_list_compositing_images
    a.draw(renderer)
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/artist.py", line 72, in draw_wrapper
    return draw(artist, renderer)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/axis.py", line 1379, in draw
    self.label.draw(renderer)
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/artist.py", line 72, in draw_wrapper
    return draw(artist, renderer)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/text.py", line 752, in draw
    bbox, info, descent = self._get_layout(renderer)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/text.py", line 386, in _get_layout
    w, h, d = _get_text_metrics_with_cache(
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/text.py", line 97, in _get_text_metrics_with_cache
    return _get_text_metrics_with_cache_impl(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/text.py", line 105, in _get_text_metrics_with_cache_impl
    return renderer_ref().get_text_width_height_descent(text, fontprop, ismath)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/backends/backend_agg.py", line 230, in get_text_width_height_descent
    self.mathtext_parser.parse(s, self.dpi, prop)
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/mathtext.py", line 226, in parse
    return self._parse_cached(s, dpi, prop)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/mathtext.py", line 240, in _parse_cached
    fontset = fontset_class(prop, load_glyph_flags)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/_mathtext.py", line 603, in __init__
    self.bakoma = BakomaFonts(*args, **kwargs)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/_mathtext.py", line 385, in __init__
    fullpath = findfont(val)
               ^^^^^^^^^^^^^
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/font_manager.py", line 1259, in findfont
    ret = self._findfont_cached(
          ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/font_manager.py", line 1410, in _findfont_cached
    return self.findfont(default_prop, fontext, directory,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jklymak/miniconda3/envs/boo/lib/python3.11/site-packages/matplotlib/font_manager.py", line 1263, in findfont
    raise ret
ValueError: Failed to find font DejaVu Sans:style=normal:variant=normal:weight=normal:stretch=normal:size=10.0, and fallback to the default font was disabled

Expected outcome

A figure with a \delta symbol for an xlabel.

Additional information

Works fine if I clear the font list from ~/.matplotlib.

Operating system

MacOS

Matplotlib Version

3.7.0

Matplotlib Backend

No response

Python version

3.11

Jupyter version

No response

Installation

conda

@tacaswell
Copy link
Member

My theory as to the underlying issue here is that we have shared state between venvs / conda envs (there is only one cache file in the users home directory). Thus if you:

  • clear your cache
  • import in a new env (which will cache the absolute path to the font files in that env)
  • delete the env
  • make a new env

In the new env we will now try to find the removed files and hence fail like the above. This story gets a bit worse if you are using the "venv in your working directory" model. One major issue with this explanation is that at least on my machine the fonts we ship are listed as relative paths in the json file on my computer. Not clear if this is a "editable install" thing, a linux thing, or a general thing yet....

One thing we need is a one-button "nuke the cache, try again".

A second thing is that maybe we want to start scoping the cache files? I think that we need to use something like sys.executable rather than just the python version because if you are making / destroying envs odds are they are all with the same base python version.

  • use Path(sys.executable).parent in the name of the cache file (could make very nested direcotries like ~/.cache/matplotlib/usr/bin/fontlist-v330.json. The pro is that this would be unique, the con is that it makes really deep trees (and I have concerns about what it would do windows with envs on different drives)
  • hash sys.executable and add it to the name and/or path. The downside here is that it is really hard to reverse back which of the files is the one you care about
  • add another layer of structure to the cache file to include this path

All of the above can probably relpace sys.executable with Path(__file__).parent from the font manager.

@anntzer
Copy link
Contributor

anntzer commented Feb 23, 2023

One major issue with this explanation is that at least on my machine the fonts we ship are listed as relative paths in the json file on my computer. Not clear if this is a "editable install" thing, a linux thing, or a general thing yet....

See #10245.

Didn't look in detail, but the relative paths should always be good as we haven't changed the list of vendored fonts since a very long time ago.

Alternatively we could just special-case the vendored fonts and not include them into fontlist.json in any form but manually merge their entries into the FontManager object (in a sense that's probably the more robust solution wrt. the possibility that one day we may indeed change the vendored fonts).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants