8000 [Bug]: ImportError when using Matplotlib v3.8.0 in Python package tests · Issue #26827 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

[Bug]: ImportError when using Matplotlib v3.8.0 in Python package tests #26827

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

Closed
jvsguerra opened this issue Sep 19, 2023 · 49 comments · Fixed by #29673
Closed

[Bug]: ImportError when using Matplotlib v3.8.0 in Python package tests #26827

jvsguerra opened this issue Sep 19, 2023 · 49 comments · Fixed by #29673
Milestone

Comments

@jvsguerra
Copy link

Bug summary

I am encountering an ImportError when attempting to use Matplotlib v3.8.0 in my Python package's tests, specifically when running them with pytest. This issue does not occur with older versions of Matplotlib, such as v3.7.3.

Code for reproduction

>>> import matplotlib.pyplot as plt
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/ABTLUS/joao.guerra/.local/lib/python3.10/site-packages/matplotlib/pyplot.py", line 66, in <module>
    from matplotlib.figure import Figure, FigureBase, figaspect
  File "/home/ABTLUS/joao.guerra/.local/lib/python3.10/site-packages/matplotlib/figure.py", line 43, in <module>
    from matplotlib import _blocking_input, backend_bases, _docstring, projections
  File "/home/ABTLUS/joao.guerra/.local/lib/python3.10/site-packages/matplotlib/projections/__init__.py", line 58, in <module>
    from mpl_toolkits.mplot3d import Axes3D
  File "/usr/lib/python3/dist-packages/mpl_toolkits/mplot3d/__init__.py", line 1, in <module>
    from .axes3d import Axes3D
  File "/usr/lib/python3/dist-packages/mpl_toolkits/mplot3d/axes3d.py", line 23, in <module>
    from matplotlib import _api, cbook, docstring, _preprocess_data
ImportError: cannot import name 'docstring' from 'matplotlib' (/home/ABTLUS/joao.guerra/.local/lib/python3.10/site-packages/matplotlib/__init__.py)

Actual outcome

When running the tests, I receive the following ImportError:

tests/integration/test_cli.py:263: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../.local/lib/python3.10/site-packages/pyKVFinder/main.py:214: in cli
    plot_frequencies(frequencies, output_plot)
../../.local/lib/python3.10/site-packages/pyKVFinder/utils.py:952: in plot_frequencies
    import matplotlib.pyplot as plt
../../.local/lib/python3.10/site-packages/matplotlib/pyplot.py:66: in <module>
    from matplotlib.figure import Figure, FigureBase, figaspect
../../.local/lib/python3.10/site-packages/matplotlib/figure.py:43: in <module>
    from matplotlib import _blocking_input, backend_bases, _docstring, projections
../../.local/lib/python3.10/site-packages/matplotlib/projections/__init__.py:58: in <module>
    from mpl_toolkits.mplot3d import Axes3D
/usr/lib/python3/dist-packages/mpl_toolkits/mplot3d/__init__.py:1: in <module>
    from .axes3d import Axes3D
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    """
    axes3d.py, original mplot3d version by John Porter
    Created: 23 Sep 2005
    
    Parts fixed by Reinier Heeres <reinier@heeres.eu>
    Minor additions by Ben Axelrod <baxelrod@coroware.com>
    Significant updates and revisions by Ben Root <ben.v.root@gmail.com>
    
    Module containing Axes3D, an object which can plot 3D objects on a
    2D matplotlib figure.
    """
    
    from collections import defaultdict
    import functools
    import inspect
    import itertools
    import math
    from numbers import Integral
    import textwrap
    
    import numpy as np
    
>   from matplotlib import _api, cbook, docstring, _preprocess_data
E   ImportError: cannot import name 'docstring' from 'matplotlib' (/home/ABTLUS/joao.guerra/.local/lib/python3.10/site-packages/matplotlib/__init__.py)

/usr/lib/python3/dist-packages/mpl_toolkits/mplot3d/axes3d.py:23: ImportError

Expected outcome

I expected my tests to run successfully with Matplotlib v3.8.0, just as they did with v3.7.3.

Additional information

  • Has this worked in earlier versions?
    It is working in previous versions (eg, v3.7.3).

  • Do you know why this bug is happening?
    When running the following command to get the Matplotlib Backend:

import matplotlib; print(matplotlib.get_backend())

I got the following error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/ABTLUS/joao.guerra/.local/lib/python3.10/site-packages/matplotlib/__init__.py", line 1275, in get_backend
    return rcParams['backend']
  File "/home/ABTLUS/joao.guerra/.local/lib/python3.10/site-packages/matplotlib/__init__.py", line 759, in __getitem__
    from matplotlib import pyplot as plt
  File "/home/ABTLUS/joao.guerra/.local/lib/python3.10/site-packages/matplotlib/pyplot.py", line 66, in <module>
    from matplotlib.figure import Figure, FigureBase, figaspect
  File "/home/ABTLUS/joao.guerra/.local/lib/python3.10/site-packages/matplotlib/figure.py", line 43, in <module>
    from matplotlib import _blocking_input, backend_bases, _docstring, projections
  File "/home/ABTLUS/joao.guerra/.local/lib/python3.10/site-packages/matplotlib/projections/__init__.py", line 58, in <module>
    from mpl_toolkits.mplot3d import Axes3D
  File "/usr/lib/python3/dist-packages/mpl_toolkits/mplot3d/__init__.py", line 1, in <module>
    from .axes3d import Axes3D
  File "/usr/lib/python3/dist-packages/mpl_toolkits/mplot3d/axes3d.py", line 23, in <module>
    from matplotlib import _api, cbook, docstring, _preprocess_data
ImportError: cannot import name 'docstring' from 'matplotlib' (/home/ABTLUS/joao.guerra/.local/lib/python3.10/site-packages/matplotlib/__init__.py)

Operating system

Pop!_OS 22.04 LTS

Matplotlib Version

3.8.0

Matplotlib Backend

ImportError: cannot import name 'docstring' from 'matplotlib' (/home/ABTLUS/joao.guerra/.local/lib/python3.10/site-packages/matplotlib/init.py)

Python version

3.10.12

Jupyter version

6.5.1 (Not using Jupyter notebook)

Installation

pip

@WolfgangFahl
Copy link

breaks my CI too

WolfgangFahl added a commit to WolfgangFahl/pyLoDStorage that referenced this issue Sep 19, 2023
@jklymak
Copy link
Member
jklymak commented Sep 19, 2023

Your installs are crossed. You are picking up mpl_toolkits from a different distribution.

On the other hand this happens pretty commonly - I forget the explanation and maybe indicates an issue with how we install the wheels?

@ksunden
Copy link
Member
ksunden commented Sep 19, 2023

This looks like an installation issue?

It is certainly true that docstring was removed after a deprecation cycle of 2 releases and changed to be private (_docstring)

However, the line that is erroring in mplot3d/axes3d.py was updated #22148, indicating that you have a version of mpl that it is picking up that is <3.6

52df591#diff-a67656224bfb26dc6de0a0f89e398cd6999377d1482aca3e867ee550ff05be57

@ksunden
Copy link
Member
ksunden commented Sep 19, 2023

I will also point out that matplotlib is coming from site-packages, but mpl_toolkits is coming from dist-packages

@WolfgangFahl
Copy link

@jklymak - "You are ..." sound like we did something actively. I assume we did not. The nightly CI build in jenkins fail. This seems to be just dependency hell as I have seen quite a few times in the last few months.

@WolfgangFahl
Copy link

Avoiding 3.8.0 fixes the jenkins problem and does not harm the github CI. The github CI also worked with the 3.8.0 version so there might be indeed be a sandboxing problem and some crossover between packages. I donot even know what mpl_toolkits is and why it would have anything to do with the way I am using matplotlib. My code is quite short and it surprises me that it should have such hidden dependencies

'''
Created on 2020-07-05

@author: wf
'''
import matplotlib.pyplot as pl
8000
t
from collections import Counter
import numpy as np
import os

class Plot(object):
    '''
    create Plot based on counters
    see https://stackoverflow.com/questions/19198920/using-counter-in-python-to-build-histogram
    '''
    def __init__(self, valueList,title,xlabel=None,ylabel=None,gformat='.png',fontsize=12,plotdir=None,debug=False):
        '''
        Constructor
        '''
        self.counter=Counter(valueList)
        self.valueList=valueList
        self.title=title
        self.xlabel=xlabel
        self.ylabel=ylabel
        self.fontsize=fontsize
        self.gformat=gformat
        self.debug=debug
        path=os.path.dirname(__file__)
        if plotdir is not None:
            self.plotdir=plotdir
        else:
            self.plotdir=path+"/../plots/"
            os.makedirs(self.plotdir,exist_ok=True)
            
    def titleMe(self):   
        ''' set my title and labels '''     
        plt.title(self.title, fontsize=self.fontsize)
        if self.xlabel is not None:
            plt.xlabel(self.xlabel)
        if self.ylabel is not None:    
            plt.ylabel(self.ylabel)
            
    def showMe(self,mode='show',close=True):
        ''' show me in the given mode '''
        if mode=="show":
            plt.show() 
        else:
            plt.savefig(self.plotdir+self.title+self.gformat)
        if close:    
            plt.close()    
            
    def barchart(self,mode='show'):
        ''' barchart based histogram for the given counter '''
        labels, values = zip(*self.counter.items())
        indexes = np.arange(len(labels))
        width = 1
        self.titleMe()
        plt.bar(indexes, values, width)
        plt.xticks(indexes + width * 0.5, labels)
        plt.yticks(np.arange(1,max(values)+1,step=1))
        self.showMe(mode)
        
    def showDebug(self):    
        print("   value  list: ",self.valueList)
        print("counter  items: ",self.counter.items())
        print("counter values: ",self.counter.values())
        print("counter   keys: ",self.counter.keys())
        
    def hist(self,mode="show"):
        ''' create histogram for the given counter '''
        if self.debug:
            self.showDebug()
        self.titleMe()
        # see https://stackoverflow.com/a/2162045/1497139
        plt.hist(self.valueList,bins=len(self.counter.keys()))
        self.showMe(mode)
        pass
            
        

@ghost
Copy link
ghost commented Sep 19, 2023

I am getting the same problem on Linux Mint 21.2 after I upgraded to 3.8.0.

The import sequence jump from
~/.local/lib/python3.10/site-packages/matplotlib
to
/usr/lib/python3/dist-packages/mpl_toolkits

The latter directory is part of the matplotlib 3.5.1 that is bundled with Mint. I uninstalled the Mint package and the import then started working correctly.

~/.local/lib/python3.10/site-packages comes before /usr/lib/python3/dist-packages in my sys.path, so is something in 3.8.0 changing it at run time?

I have another PC with Mint 21.2 which is still on matplotlib 3.7.2 and it doe not have this problem.

@ksunden
Copy link
Member
ksunden commented Sep 19, 2023

mpl_toolkits is distributed with matplotlib, in the matplotlib pypi (and conda, etc) package. It is a separate python package, where some more experimental features (most notably 3D plotting support), though it is imported (for 3D) by default.

As to why you have multiple and why python is choosing different locations for matplotlib and mpl_toolkits, I don't honestly know. Is it possible you edit sys.path in process (perhaps in test setup)?

Even with Matplotlib 3.7, I'd have expected this to at least be warning, as that was deprecated in 3.6.

@tacaswell
Copy link
Member

mpl_toolkits is a namespace package that we ship with Matplotlib (that is where the 3d and axis artist code live).

For 3.8 we did change to using the "new-style" version of doing namespace packages (see #25244 #25381) because upstream started to actively warn about using the old way.

It looks like all of these issues are in cases where pip install --local xxx is being used to shadow an existing Matplotlib installation from the system package manager with a version from pip. Although this works most of the time, I think that this is one of the corner cases where it does not.

What is the version of setuptools on the cases where it is broken? I strongly suspect that is going to be the determining factor of when this works or not.

@ghost
Copy link
ghost commented Sep 19, 2023

I'm not changing sys.path in my own code. Just executing the following line in the interpreter will trigger it:

from mpl_toolkits import mplot3d
Python 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from mpl_toolkits import mplot3d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3/dist-packages/mpl_toolkits/mplot3d/__init__.py", line 1, in <module>
    from .axes3d import Axes3D
  File "/usr/lib/python3/dist-packages/mpl_toolkits/mplot3d/axes3d.py", line 23, in <module>
    from matplotlib import _api, cbook, docstring, _preprocess_data
ImportError: cannot import name 'docstring' from 'matplotlib' (/home/richardt/.local/lib/python3.10/site-packages/matplotlib/__init__.py)

@ksunden
Copy link
Member
ksunden commented Sep 19, 2023

Ah, yeah, the namespace package definition did also change, though I think that the changes to packaging are likely orthogonal here... setuptools has been stripping out the changes silently for quite a while, the fact that it is a namespace package is probably important though, since namespace packages can have multiple locations on a filesystem. I'd have thought they would resolve in sys.path order, though, and it seems like it may not be?

@ksunden
Copy link
8000 Member
ksunden commented Sep 19, 2023

When I made the change, I found that setuptools had already been stripping out __init__.py from mpl_toolkits for wheels already, so I was pretty sure there would be no change.

I think that much is true, and that even from wheels/recent setuptools installs from previous versions, the OS version would still be selected for mpl_toolkits.

It is extremely important that every distribution that uses the namespace package omits the init.py or uses a pkgutil-style init.py. If any distribution does not, it will cause the namespace logic to fail and the other sub-packages will not be importable.

I had been under the impression that it was okay to have a mix of "present" and "absent" __init__.py, but that it was very much a problem to have non-matching __init__.py. But rereading that sentence from the Namespace packaging docs does make me question it, though it is slightly ambiguous usage of the word "or". I did do a variety of tests installing basemap and matplotlib together to make sure that things did not clash, though I don't think I did much with --local (I was using separate virtualenvs)

That said, setuptools is kind of forcing our hand here. Even if we did not make the change to be explicit about how we package it, setuptools was stripping it from the wheels, and they are deprecating the old style entirely. And the upgrade path is just not good... I suspect that people who install via --local have been getting old 3D code for years at this point...

@jvsguerra
Copy link
Author

I resolved the ImportError issue by following these steps:

  1. Uninstalled the system-installed python3-matplotlib package:
sudo apt remove python3-matplotlib
  1. Uninstalled the previously installed Matplotlib using pip:
pip uninstall matplotlib
  1. Reinstalled Matplotlib via pip:
pip install matplotlib

After completing these steps, I can now import Matplotlib in Python without encountering any errors:

>>> import matplotlib.pyplot as plt

It's worth noting that while I resolved the issue, I don't remember the initial installation method, but I have been updating Matplotlib via pip since then.

@jklymak
Copy link
Member
jklymak commented Sep 19, 2023

I suspect that people who install via --local have been getting old 3D code for years at this point...

That would be my guess as well, and just that making doscstring private is now making old 3D incompatible with new matplotlib.

@ksunden
Copy link
Member
ksunden commented Sep 19, 2023

I will also point out the frustrating state of being with this sentence in the pkg_resources style namespace packages (which we used), when combined with the fact that pkg_resources is deprecated and going away:

While this approach is no longer recommended, it is widely present in most existing namespace packages. If you are creating a new distribution within an existing namespace package that uses this method then 8000 it’s recommended to continue using this as the different methods are not cross-compatible and it’s not advisable to try to migrate an existing package.

(emphasis mine)

But we have to migrate the existing package, because setuptools is taking it away (and silently changing it so that our distribution has already been using new style namespace packages, so we are kind of stuck.

@jklymak
Copy link
Member
jklymak commented Sep 19, 2023

I know this has been discussed before, but I forget the answer: is there some compelling reason we have mpl_toolkits install in a separate namespace from the main lib? They are released together, so I don't see the advantage. Conversely, if we are going to do this, it seems we need to be able to install mpl_toolkits as a versioned dependency of matplotlib if we are going to import it.

@ksunden
Copy link
Member
ksunden commented Sep 19, 2023

mpl_toolkits is a namespace package, meaning that other things can install into the same namespace. In practice, I think this is done almost exclusively by basemap.

I think I found like two other things, but they weren't maintained:

Is the only one I actually remember finding (most searches only showed copies of the mpl source vendored in various locations.)

@jklymak
Copy link
Member
jklymak commented Sep 21, 2023

Discussed briefly on the call: this import should at least be a conditional import and Warning if the import fails.

@tacaswell
Copy link
Member

@jaraco we do seem to be having some regression that may or may not be related to Matplotlib moving to new-style namespace packages. Is there a better fix for this?

@jklymak
Copy link
Member
jklymak commented Sep 22, 2023

@Momen-Mawad
Copy link

I resolved the ImportError issue by following these steps:

  1. Uninstalled the system-installed python3-matplotlib package:
sudo apt remove python3-matplotlib
  1. Uninstalled the previously installed Matplotlib using pip:
pip uninstall matplotlib
  1. Reinstalled Matplotlib via pip:
pip install matplotlib

After completing these steps, I can now import Matplotlib in Python without encountering any errors:

>>> import matplotlib.pyplot as plt

It's worth noting that while I resolved the issue, I don't remember the initial installation method, but I have been updating Matplotlib via pip since then.

This solved my problem, thank you.

@jaraco
Copy link
jaraco commented Sep 22, 2023

@jaraco we do seem to be having some regression that may or may not be related to Matplotlib moving to new-style namespace packages. Is there a better fix for this?

I'd be happy to investigate. Are there steps I can take to repro?

@jklymak
Copy link
Member
jklymak commented Sep 22, 2023

@jaraco I think it would be good to have step-by-step instructions for the setup that could ideally be run in a container or something akin to your CI

@QuLogic
Copy link
Member
QuLogic commented Sep 23, 2023

I can reproduce this in an Ubuntu container using:

$ podman run --rm -it ubuntu:22.04
# apt update -yy
# apt install python3-matplotlib python3-pip
# pip install --user -U matplotlib
# python3
>>> import matplotlib
>>> import mpl_toolkits.mplot3d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3/dist-packages/mpl_toolkits/mplot3d/__init__.py", line 1, in <module>
    from .axes3d import Axes3D
  File "/usr/lib/python3/dist-packages/mpl_toolkits/mplot3d/axes3d.py", line 23, in <module>
    from matplotlib import _api, cbook, docstring, _preprocess_data
ImportError: cannot import name 'docstring' from 'matplotlib' (/root/.local/lib/python3.10/site-packages/matplotlib/__init__.py)

The regular matplotlib is found in the user directory, but mplot3d (part of the mpl_toolkits namespace package) is in the distro location.

If I downgrade to 3.7.3:

# pip install --user matplotlib==3.7.3
# python3
>>> import mpl_toolkits.mplot3d
>>> mpl_toolkits.mplot3d.__file__
'/root/.local/lib/python3.10/site-packages/mpl_toolkits/mplot3d/__init__.py'

and the toolkit is in the user directory as expected.

@ghost
Copy link
ghost commented Sep 23, 2023

I reinstalled the matplotlib package that comes with Linux Mint 21.2 and I tried stepping through the import mpl_toolkits.mplot3d statement in the PyCharm debugger, but it wouldn't show me the frames.

So I tried using pdb:

import pdb; pdb.set_trace()
import mpl_toolkits.mplot3d

I got to the following point:

(Pdb) s
> <frozen importlib._bootstrap>(994)_find_and_load_unlocked()
(Pdb) l
989  	    parent = name.rpartition('.')[0]
990  	    if parent:
991  	        if parent not in sys.modules:
992  	            _call_with_frames_removed(import_, parent)
993  	        # Crazy side-effects!
994  ->	        if name in sys.modules:
995  	            return sys.modules[name]
996  	        parent_module = sys.modules[parent]
997  	        try:
998  	            path = parent_module.__path__
999  	        except AttributeError:
(Pdb) p sys.modules['mpl_toolkits']
<module 'mpl_toolkits' from '/usr/lib/python3/dist-packages/mpl_toolkits/__init__.py'>

I then ran python3 in a new terminal window and and confirmed that the mpl_toolkits module in sys.modules references
/usr/lib/python3/dist-packages and not ~/.local/lib/python3.10/site-packages.

>>> import sys
>>> sys.modules['mpl_toolkits']
<module 'mpl_toolkits' from '/usr/lib/python3/dist-packages/mpl_toolkits/__init__.py'>

How does mpl_toolkits get added to sys.modules?

Could this be the cause of the problem?

@ghost
Copy link
ghost commented Sep 23, 2023

I found the .deb file for the matplotlib-3.5.1 package that comes with Linux Mint 21.2.

I unpacked it and found it includes a file: matplotlib-3.5.1-nspkg.pth and noticed that file gets installed in /usr/lib/python3/dist-packages/

That file contains the following code:

import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('mpl_toolkits',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('mpl_toolkits', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('mpl_toolkits', [os.path.dirname(p)])));m = m or sys.modules.setdefault('mpl_toolkits', types.ModuleType('mpl_toolkits'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p)

Presumably the python interpreter always runs this code at startup and adds the distribution's mpl_toolkits module to sys.modules?

EDIT:
I renamed the matplotlib-3.5.1-nspkg.pth file and restarted the python interpreter. The mpl_toolkits no longer appeared in sys.modules, but the import still failed as before.

I then ran a new pdb session and noticed that it skipped over ~/.local/lib/python3.10/site-packages/mpl_toolkits because it didn't have a __init__.py file. However, the interpreter did find /usr/lib/python3/dist-packages/mpl_toolkits/__init__.py and thus selected the wrong mpl_toolkits module again.

> <frozen importlib._bootstrap>(1007)_find_and_load_unlocked()
(Pdb) l
1002 	    spec = _find_spec(name, path)
1003 	    if spec is None:
1004 	        raise ModuleNotFoundError(_ERR_MSG.format(name), name=name)
1005 	    else:
1006 	        module = _load_unlocked(spec)
1007 ->	    if parent:
1008 	        # Set the module as an attribute on its parent.
1009 	        parent_module = sys.modules[parent]
1010 	        child = name.rpartition('.')[2]
1011 	        try:
1012 	            setattr(parent_module, child, module)
(Pdb) p module
<module 'mpl_toolkits' from '/usr/lib/python3/dist-packages/mpl_toolkits/__init__.py'>
(Pdb) 

@QuLogic
Copy link
Member
QuLogic commented Sep 27, 2023

If I replicate my reproducer in Fedora (36 if you want to have 3.5.* installed in the system location), then I cannot reproduce:

$ podman run --rm -it fedora:36
# dnf install python3-matplotlib python3-pip
# pip install --user -U matplotlib
# python3
>>> import matplotlib
>>> matplotlib.__file__
'/root/.local/lib/python3.10/site-packages/matplotlib/__init__.py'
>>> import mpl_toolkits.mplot3d
>>> mpl_toolkits.mplot3d.__file__
'/root/.local/lib/python3.10/site-packages/mpl_toolkits/mplot3d/__init__.py'

This may have something to do with Debian's patching to use dist-packages instead of site-packages.

@QuLogic
Copy link
Member
QuLogic commented Sep 27, 2023

I unpacked it and found it includes a file: matplotlib-3.5.1-nspkg.pth and noticed that file gets installed in /usr/lib/python3/dist-package F438 s/

This file doesn't exist in 3.8.0 installed on Ubuntu or Fedora. However, it's not particularly version-specific, so if you copy it from /usr/lib/python3/dist-packages/ to ~/.local/lib/python3/site-packages/, then the user path is consulted first again. This is not needed on Fedora though. I think it's still something to do with Debian patches.

@barronh
Copy link
barronh commented Nov 9, 2023

I had a similar issue to many people whose mpl_toolkits was importing from an unexpected location. I found the issue could be solved by adding a __init__.py file in the mpl_toolkits directory. Basically, the system install had a __init__.py and was preempting the user installed version.

@scottshambaugh
Copy link
Contributor
scottshambaugh commented Dec 20, 2023

I'm running into this in my dev environment, will update with whatever fix I find out.

Show Traceback
(mpl-dev) scott@DESKTOP-M5S4AGS:/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib$  cd /mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib ; /usr/bin/env /home/scott/anaconda3/envs/mpl-dev/bin/python /home/scott/.vscode-server/extensions/ms-python.python-2023.23.13541005/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher 47143 -- /mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/_test_gh27543_2.py 
Traceback (most recent call last):
  File "/home/scott/.vscode-server/extensions/ms-python.python-2023.23.13541005/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_frame.py", line 988, in trace_dispatch
    self.do_wait_suspend(thread, frame, event, arg)
  File "/home/scott/.vscode-server/extensions/ms-python.python-2023.23.13541005/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_frame.py", line 165, in do_wait_suspend
    self._args[0].do_wait_suspend(*args, **kwargs)
  File "/home/scott/.vscode-server/extensions/ms-python.python-2023.23.13541005/pythonFiles/lib/python/debugpy/_vendored/pydevd/pydevd.py", line 2070, in do_wait_suspend
    keep_suspended = self._do_wait_suspend(thread, frame, event, arg, suspend_type, from_this_thread, frames_tracker)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/scott/.vscode-server/extensions/ms-python.python-2023.23.13541005/pythonFiles/lib/python/debugpy/_vendored/pydevd/pydevd.py", line 2093, in _do_wait_suspend
    self._activate_gui_if_needed()
  File "/home/scott/.vscode-server/extensions/ms-python.python-2023.23.13541005/pythonFiles/lib/python/debugpy/_vendored/pydevd/pydevd.py", line 1592, in _activate_gui_if_needed
    activate_function()
  File "/home/scott/.vscode-server/extensions/ms-python.python-2023.23.13541005/pythonFiles/lib/python/debugpy/_vendored/pydevd/pydevd.py", line 1576, in <lambda>
    self.mpl_modules_for_patching = {"matplotlib": lambda: activate_matplotlib(do_enable_gui),
                                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/scott/.vscode-server/extensions/ms-python.python-2023.23.13541005/pythonFiles/lib/python/debugpy/_vendored/pydevd/pydev_ipython/matplotlibtools.py", line 93, in activate_matplotlib
    gui, backend = find_gui_and_backend()
                   ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/scott/.vscode-server/extensions/ms-python.python-2023.23.13541005/pythonFiles/lib/python/debugpy/_vendored/pydevd/pydev_ipython/matplotlibtools.py", line 45, in find_gui_and_backend
    backend = matplotlib.rcParams['backend']
              ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
  File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/__init__.py", line 765, in __getitem__
    plt.switch_backend(rcsetup._auto_backend_sentinel)
    ^^^^^^^^^^^^^^^^^^
AttributeError: partially initialized module 'matplotlib.pyplot' has no attribute 'switch_backend' (most likely due to a circular import)
/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/projections/__init__.py:63: UserWarning: Unable to import Axes3D. This may be due to multiple versions of Matplotlib being installed (e.g. as a system package and as a pip package). As a result, the 3D projection is not available.
  warnings.warn("Unable to import Axes3D. This may be due to multiple versions of "
Traceback (most recent call last):
  File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/projections/__init__.py", line 120, in get_projection_class
    return projection_registry.get_projection_class(projection)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/projections/__init__.py", line 83, in get_projection_class
    return self._all_projection_types[name]
           ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
KeyError: '3d'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/scott/anaconda3/envs/mpl-dev/lib/python3.11/runpy.py", line 198, in _run_module_as_main
    return _run_code(code, main_globals, None,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/scott/anaconda3/envs/mpl-dev/lib/python3.11/runpy.py", line 88, in _run_code
    exec(code, run_globals)
  File "/home/scott/.vscode-server/extensions/ms-python.python-2023.23.13541005/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/__main__.py", line 39, in <module>
    cli.main()
  File "/home/scott/.vscode-server/extensions/ms-python.python-2023.23.13541005/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 430, in main
    run()
  File "/home/scott/.vscode-server/extensions/ms-python.python-2023.23.13541005/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 284, in run_file
    runpy.run_path(target, run_name="__main__")
  File "/home/scott/.vscode-server/extensions/ms-python.python-2023.23.13541005/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 321, in run_path
    return _run_module_code(code, init_globals, run_name,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/scott/.vscode-server/extensions/ms-python.python-2023.23.13541005/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 135, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "/home/scott/.vscode-server/extensions/ms-python.python-2023.23.13541005/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 124, in _run_code
    exec(code, run_globals)
  File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/_test_gh27543_2.py", line 19, in <module>
    fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/pyplot.py", line 1613, in subplots
    axs = fig.subplots(nrows=nrows, ncols=ncols, sharex=sharex, sharey=sharey,
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/figure.py", line 868, in subplots
    axs = gs.subplots(sharex=sharex, sharey=sharey, squeeze=squeeze,
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/gridspec.py", line 283, in subplots
    axarr[row, col] = figure.add_subplot(
                      ^^^^^^^^^^^^^^^^^^^
  File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/figure.py", line 718, in add_subplot
    projection_class, pkw = self._process_projection_requirements(**kwargs)
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/figure.py", line 1660, in _process_projection_requirements
    projection_class = projections.get_projection_class(projection)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/projections/__init__.py", line 122, in get_projection_class
    raise ValueError("Unknown projection %r" % projection) from err
ValueError: Unknown projection '3d'

Uninstalling and reinstalling matplotlib with pip from source did not work, and I did not have a system matplotlib package installed. My imports of matplotlib and mpl_toolkits.mplot3d are pointing towards the same source folder.

Frustratingly, this only happens when debugging the file with VSCode and not setting a breakpoint. If I run the script not in debug mode, it works fine. If I set a breakpoint on the first line (import matplotlib), run the debug mode, step once and allow the import to finish, and continue on, it works fine. If I set a breakpoint on the first line, run the debug mode, and immediately continue on, it breaks. If I don't set a breakpoint and run debug mode, it breaks. I really have no idea what the difference is.

@nhf216
Copy link
nhf216 commented Feb 6, 2024

I just upgraded to 3.8.2 and I'm experiencing the related #27342 issue (Ubuntu 22.04, Python 3.10). When I went to run sudo apt remove python3-matplotlib, I realized that it's pulling mpl_toolkits from my SageMath installation, which I'd rather not purge to fix this warning.

@tacaswell
Copy link
Member

@nhf216 Are you getting errors or warnings? Can you update matplotlib via the sagemath?

@nhf216
Copy link
nhf216 commented Feb 7, 2024

@tacaswell I'm just getting warnings. I updated the version of matplotlib with sagemath up to 3.8.2 also, and now I'm getting this warning there too. It turns out I have an Anaconda installation from years ago with some old versions of matplotlib hanging around in there. I'm assuming that's the source of the warnings. Dealing with that is painful.

@barronh
Copy link
barronh commented Feb 7, 2024

Did you try the __init__.py suggestion? Just add an empty __init__.py file in the ‘mpl_toolkits’ folder that should have preference based on the sys.path order.

@nhf216
Copy link
nhf216 commented Feb 13, 2024

@barronh I tried that (after you commented), and it did not fix anything. But, I did confirm that it's a system package (Version 3.5.1) causing the issue, not any of the Anaconda packages. I also confirmed that the sys.path order itself is not the issue. So, it's the same issue others are having, but the same solutions (short of downgrading) aren't working for me.

@cielavenir
Copy link
cielavenir commented Feb 29, 2024

@reticulatus #26827 (comment)

How does mpl_toolkits get added to sys.modules

by /usr/lib/python3/dist-packages/matplotlib*nspkg.pth invoked from site.py.

To remove nspkg.pth, we need to install python3-matplotlib 3.8 debian package, but it is not available yet, so I created one.

https://github.com/cielavenir/salsa-matplotlib/releases/tag/ciel%2F3.8.3_1 (edit 20240312: simplified build process a bit)


@QuLogic #26827 (comment)

I can reproduce this in an Ubuntu container using:

$ podman run --rm -it ubuntu:22.04
# apt update -yy
# apt install python3-matplotlib python3-pip
# pip install --user -U matplotlib
# python3
>>> import matplotlib
>>> import mpl_toolkits.mplot3d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3/dist-packages/mpl_toolkits/mplot3d/__init__.py", line 1, in <module>
    from .axes3d import Axes3D
  File "/usr/lib/python3/dist-packages/mpl_toolkits/mplot3d/axes3d.py", line 23, in <module>
    from matplotlib import _api, cbook, docstring, _preprocess_data
ImportError: cannot import name 'docstring' from 'matplotlib' (/root/.local/lib/python3.10/site-packages/matplotlib/__init__.py)
$ docker run --rm -it ubuntu:22.04
# apt update -yy
# # (copy) jammy directory
# cd jammy
# apt install ./python3-matplotlib_3.8.3-1_amd64.deb ./python-matplotlib-data_3.8.3-1_all.deb ./python3-numpy_1.26.3-1_amd64.deb ./python3-contourpy_1.0.7-1*_amd64.deb python3-pip
# pip install --user -U matplotlib
# python3
>>> import matplotlib
>>> mpl_toolkits.mplot3d

It does import.

@drew-parsons
Copy link
drew-parsons commented Jun 23, 2024

This issue is blocking building docs for the debian packaging of matplotlib (3.8.0). The problem is as described by #26827 (comment) or #26827 (comment).

The issue is that mpl_toolkits from matplotlib 3.6.3 is installed as a system package (python3-matplotlib) in /usr/lib/python3/dist-packages/mpl_toolkits. The old version gets installed while building the new version because of a circular dependency via python3-sphinx-gallery

I've tried many variations to solve it: adding an empty mpl_toolkits/__init__.py; creating a local sitecustomize.py, or mpl_toolkits.pth to ensure the local path is set in sys.path before /usr/lib/python3/dist-packages/; or using virtualenv (with --system-site-packages). With sitecustomize.py, for instance, I can see that sys.path got modified as intended. The local path for the new build is placed before the system path. Indeed I can successfully import matplotlib 3.8.0 itself from the local path.

But every attempt to import the new mpl_toolkits (or mpl_toolkits.mplot3d) from the local path fails.
In every case it imports from /usr/lib/python3/dist-packages/mpl_toolkits instead of the local path.

@cielavenir
Copy link

Do you need python3-sphinx-gallery to build matplotlib? I dont think so

@drew-parsons
Copy link

The docs, man, the docs.

@jaraco
Copy link
jaraco commented Jun 24, 2024

@jaraco we do seem to be having some regression that may or may not be related to Matplotlib moving to new-style namespace packages. Is there a better fix for this?

Sorry for the long delay in responding. I'd fully intended to follow up. I don't remember why I did not. The recent activity revived my awareness of the issue.

Following the repro instructions, I'm able to replicate the failure with this Dockerfile:

from ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
run apt update -yy
run apt install -y python3-matplotlib python3-pip
run pip install --user -U matplotlib
cmd python3 -i -c 'import matplotlib; import mpl_toolkits.mplot3d'

I can see now that the debian-installed matplotlib is using the pkg_resources-style namespace package for mpl_toolkits but the 3.8 pip-installed version is using the native namespace packages. As you've discovered, these two forms of namespace packages are incompatible within the same environment. It doesn't matter whether mpl_toolkits is installed by an older install in dist-packages or created by some other installer elsewhere on PYTHONPATH/sys.path, if one of the installs is using pkg_resources-style namespace packages, it will take precedence over other installs, even if they appear earlier on the path.

pkg_resources-style packages have two ways of declaring themselves, once early with the -nspkg.pth file and again with the declaration in the __init__.py of the namespace.

The reason adding the __init__.py to mpl_toolkits works around the issue is because it's turning that namespace package into a normal package and causing it to take precedence over any mpl_toolkits in dist-packages (later in the path).

Since matplotlib is migrating, it means that dual-installations will be incompatible across the 3.7/3.8 boundary, so users will need to somehow keep these versions isolated. Probably the two easiest ways to achieve this isolation is to use a virtualenv for the newer installation or to uninstall the older version from dist-packages. Upgrading the package-installed version also looks to be a viable alternative. Deleting the -nspth.pkg and mpl_toolkits/init.py from dist-packages should also work, though I'd feel uneasy about that, given that those files are managed by the package manager.

I wish I had a better suggestion. If we could go back in time, one way to ease the transition is to use pkgutil-style namespace packages. If mpl_toolkits could have transitioned to that in the late 2010s, it has better compatibility with both pkg_resources-style and native namespace packages. Now that mpl_toolkits is using native packages, it's probably too late to consider a transitional period.

What is the version of setuptools on the cases where it is broken? I strongly suspect that is going to be the determining factor of when this works or not.

There's no version of setuptools that fixes this issue. It's an inherent limitation in the design.

@drew-parsons
Copy link

As far as the doc build goes, the blocking problem is specifically with the autosummary generated for doc/api/toolkits/mplot3d/axes3d.rst. That is, the warning about the version mismatch with mpl_toolkits.mplot3d is emitted for other pages, but sphinx can ignore the warning and carry on. With axes3d.rst however, sphinx gets an unrecoverable exception and halts.

I can get a "successful" doc built by removing the autoclass:: Axes3D and autosummary:: sections from axes3d.rst. Loses linking for axes3d.Axes3D, but other pages are fine. Also needed to skip 3d, axes, random_walk examples from the gallery, replacing l.235 in doc/conf.py with

    'filename_pattern': '^((?!sgskip|3d|3D|axes|random_walk).)*$',

I'll apply that workaround to get the docs building for Debian's python-matplotlib-doc.

@MRiabov
Copy link
MRiabov commented Jul 13, 2024

For me the solution was to sudo apt remove python3-matplotlib. You can indicate that matplotlib, and python3-matplotlib different packages in the error stack, because I did not know it, and many others probably won't too.

@cielavenir
Copy link

yeah, removing python3-matplotlib works, but in my case freecad had dependency, so installing python3-matplotlib 3.8 was the only solution for me.

@DimitriBolt
Copy link

I resolved the ImportError issue by following these steps:

  1. Uninstalled the system-installed python3-matplotlib package:
sudo apt remove python3-matplotlib
  1. Uninstalled the previously installed Matplotlib using pip:
pip uninstall matplotlib
  1. Reinstalled Matplotlib via pip:
pip install matplotlib

After completing these steps, I can now import Matplotlib in Python without encountering any errors:

>>> import matplotlib.pyplot as plt

It's worth noting that while I resolved the issue, I don't remember the initial installation method, but I have been updating Matplotlib via pip since then.

Works!!! Thanks a lot!!!

@scottshambaugh
Copy link
Contributor
scottshambaugh F168 commented Jan 2, 2025

I think I tracked down the cause of this on my local machine.

When debugging in VSCode on WSL, there are multiple paths that point towards the same file. One is the local linux filepath (./matplotlib), and one is the file on the windows filesystem as mounted on the linux subsystem (/mnt/c/.../matplotlib). If you set a breakpoint in the former, then the debugger will sometimes open the latter to step through the code. But if you then set a breakpoint in the latter, then something gets confused and thinks there are multiple installations, leading to the error here.

The solution (if I'm right about this) was to go through my breakpoints and make sure you aren't splitting them across files on the separate paths.

@tacaswell
Copy link
Member

Per #26827 (comment) we went across a hard compatibly edge with mpl 3.7 -> 3.8 and having overlapping installs of anything < 3.8 and anything >= 3.8 is going to have this issue.

I've opened a PR ( #29673 ) add this to the API changes for 3.8 to close this issue. I don't think there is anything we can do at this point that would not be as or more disruptive than that status quo.

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

Successfully merging a pull request may close this issue.

0