uv icon indicating copy to clipboard operation
uv copied to clipboard

tkagg is not compatible with uv-managed Python installs

Open bluthej opened this issue 1 year ago • 24 comments

From the matplot lib issue tracker (comment), apparently:

python-build-standalone is not compatible with tkagg

I am on Linux and it seems like tkagg is the default interactive backend (not sure about other platforms):

❯ python -c "import matplotlib.pyplot as plt; print(plt.get_backend())"
tkagg

Since uv relies on python-build-standalone for managed installs, this has the unfortunate consequence that I have to use my system Python whenever I need to plot something in interactive mode.

This is related to this matplotlib issue and this python-build-standalone issue, at which point things were even worse because people couldn't even import matplotlib successfully!

It sounds like python-build-standalone and tkagg will not be compatible anytime soon. I don't necessarily mind, but in any case I think it would be nice if we could either:

  • document this issue and recommend a different interactive backend that works with python-build-standalone, along with the steps to take to make that work
  • take some steps on the python-build-standalone side so that there is some functioning interactive backend by default

Hardly a day goes by that I don't need to plot something with matplotlib and I assume I am not alone, so this should benefit a lot of people!

bluthej avatar Aug 31 '24 16:08 bluthej

This is a big issue for me as well. Any help would be appreciated.

spaceman-cb avatar Sep 01 '24 07:09 spaceman-cb

For people coming here and needing immediate help, setting your backend as described here may solve your problem: https://matplotlib.org/stable/users/explain/figure/backends.html

spaceman-cb avatar Sep 01 '24 19:09 spaceman-cb

What operating system and Python version do you use, and do you set and custom matplotlib settings? For me, with Ubuntu 24.04 and cpython-3.12.1-linux-x86_64-gnu:

pyproject.toml

[project]
name = "mpl-basic"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
    "matplotlib>=3.9.2",
]

main.py

import matplotlib.pyplot as plt

if __name__ == '__main__':
    print(plt.get_backend())

    plt.plot([0, 1], [0, 1])
    plt.savefig("hello.png")

With uv run main.py, I get agg as backend and a hello.png.

konstin avatar Sep 02 '24 11:09 konstin

Running into this too. savefig works fine with the standalone builds; the problem is when trying to bring up an interactive window.

For example, when running:

from matplotlib import pyplot

pyplot.plot([0, 1], [0, 1])
pyplot.show()

gives the warning on the call to pyplot.show() (and nothing else of interest happens besides the program terminating)

UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown

I'm running with cpython-3.11.9-linux-x86_64-gnu on WSL (ubuntu 22.04) right now.

edit: my current workaround is to have PyQt6 installed in whichever environment I'm working, as matplotlib will then pick that up as the default interactive backend without any further intervention (I haven't set additional environment variables or call matplotlib.use explicitly).

tpgillam avatar Sep 02 '24 15:09 tpgillam

I am on a mac and was running matplotlib 3.9.1 and 3.9.0. savefig('foo.pdf') was causing issues. Not sure why it was trying to use tk when nothing about it was interactive.

spaceman-cb avatar Sep 02 '24 16:09 spaceman-cb

@konstin as I said in the issue, the problems I'm having are with the interactive backend, the static backend indeed works just fine out of the box (just like what @tpgillam said).

bluthej avatar Sep 02 '24 16:09 bluthej

Running into this too. savefig works fine with the standalone builds; the problem is when trying to bring up an interactive window.

For example, when running:

from matplotlib import pyplot

pyplot.plot([0, 1], [0, 1])
pyplot.show()

gives the warning on the call to pyplot.show() (and nothing else of interest happens besides the program terminating)

UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown

I'm running with cpython-3.11.9-linux-x86_64-gnu on WSL (ubuntu 22.04) right now.

edit: my current workaround is to have PyQt6 installed in whichever environment I'm working, as matplotlib will then pick that up as the default interactive backend without any further intervention (I haven't set additional environment variables or call matplotlib.use explicitly).

After lots of trouble, uv pip install PyQt6 is the only thing that got matplotlib interactive backend to work inside my venv!

MalekWahidi avatar Oct 25 '24 11:10 MalekWahidi

After lots of trouble, uv pip install PyQt6 is the only thing that got matplotlib interactive backend to work inside my venv!

I'm afraid this does not solve it for me. I've gone down the rabbit hole trying to fix this. It should not be this complicated.

Failed to create wl_display (No such file or directory)
qt.qpa.plugin: Could not load the Qt platform plugin "wayland" in "" even though it was found.
qt.qpa.plugin: From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin.
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

Available platform plugins are: minimalegl, eglfs, offscreen, wayland, linuxfb, vkkhrdisplay, wayland-egl, vnc, minimal, xcb.

fjsuarez avatar Nov 13 '24 21:11 fjsuarez

After lots of trouble, uv pip install PyQt6 is the only thing that got matplotlib interactive backend to work inside my venv!

I'm afraid this does not solve it for me. I've gone down the rabbit hole trying to fix this. It should not be this complicated.

Failed to create wl_display (No such file or directory)
qt.qpa.plugin: Could not load the Qt platform plugin "wayland" in "" even though it was found.
qt.qpa.plugin: From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin.
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

Available platform plugins are: minimalegl, eglfs, offscreen, wayland, linuxfb, vkkhrdisplay, wayland-egl, vnc, minimal, xcb.

I have a vague recollection that installing libxcb-cursor0 from the operating system's package manager helped me with a similar issue once (but it was a while ago so can't remember the details I'm afraid!)

tpgillam avatar Nov 13 '24 21:11 tpgillam

I have a vague recollection that installing libxcb-cursor0 from the operating system's package manager helped me with a similar issue once (but it was a while ago so can't remember the details I'm afraid!)

Thanks for pointing it out. I already tried it out to no avail. Also tried doing export QT_QPA_PLATFORM = offscreen and I get something along the lines of raise() not supported by plugin. Tried with PyQt5, PyQt6, the whole nine yards. Just can't get it to work.

fjsuarez avatar Nov 13 '24 22:11 fjsuarez

I stand by my comment in https://github.com/matplotlib/matplotlib/issues/23074#issuecomment-1134052145 that this is an issue that needs to be handled, if at all, on the python-build-standalone/astral side.

Matplotlib has been using this method of loading the tk binding from 2016 (via https://github.com/matplotlib/matplotlib/pull/6442) in mpl 1.5. The motivation for doing this and the dlopen was to make the built wheels independent of the version of tk/x11 that was in the multibuild (predecessor to cibuildwheel) and now cibuildwheel container where we build the wheels. Without doing this, if that version did not exactly match the version that the users' Python was built with we would expect crashes. A lot of this pain is inherent to wheels (see https://pypackaging-native.github.io for more details) and we don't, to my knowledge, have these issues with any of the other packaging ecosystems (linux packages, brew, conda, ...).


With I the matrix of compatibility for python-build-stand-alone after the symbols got hidden:

  • Matplotlib <3.6 will fail hard when doing backend discovery with python-build-standalone if no other GUI toolkits are installed for Matplotlib >3. tk is at the end, so if the user has a qt, wx, or gtk binding installed or is on OSX we will never look at tk.
  • Matplotlib >3.6 will gracefully detects that we can not get the tk bindings and falls back to Agg.

For version of python-build-standalone from before the symbols were hidden for Matplotlib >3.4 I am seeing segfaults locally (I only tested back to 3.5, but what I suspect is the source of the problem is in mpl3.4). I suspect the issue is that we use dlopen("libX11.so.6", RTLD_LAZY) to see if libX11 is available. We use libX11 as part of detecting if we are running in a graphical environment as part of detecting if the user has already imported an GUI framework or even have an available display. Setting $DISPLAY to an empty string (which skips the check) avoids the segfault but of course will force mpl back to only non-interactive backends. This seems consistent with the quirk note about pyqt + x11. It may be the case that my local X11 too new and/or just built differently and other systems may not see the segfault. I'm also not sure if on pure wayland systems there is an libX11.so to find.

This is what I have been using for testing:

```toml [project] name = "scratch" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.5, ```shell uv run --python 3.10.3 python -X faulthandler -c 'import sys;import matplotlib; print(sys.executable); print(matplotlib.__version__); print(matplotlib.get_backend()); import matplotlib.pyplot as plt; plt.plot(range(5)); plt.show()'

setting whatever Python version.

I don't have a Windows system setup to test at the moment, but we use a different method of finding the symbols (by inspecting dlls loaded to the current process) so I suspect we side-step this problem, but I'm not sure.

With mpl >3.6 the auto-backend selection no longer raises, but if the user explicitly asks for tkagg it will fail but I think this is OK as there are not any usable tk libraries available. By analogy, if the user asks for tk with a Python build that does not include tk at all, failing is the correct behavior.


I think the long term paths out of this are:

  • Just document the issue. I see the case to hide extra symbols in python-build-standalone. On the Matplotlib side we can change the note in https://matplotlib.org/stable/install/index.html that says "Python is typically shipped with tk bindings which are used by TkAgg.", we could change that add "Notably, python-build-standalone -- used by uv -- does not include tk bindings that are usable by Matplotlib.". A similar section could be added to the "quirks" page of python-build-standalone explaining under what conditions Matplotlib will/won't work with uv managed environments.
  • special case to expose the symbols Matplotlib needs in python-build-standalone (or all of the tk ones). GUIs are always a bit special (due to being more-or-less obligitory process singletons) so if there were any other c-extensions that are exporting conflicting symbols I strongly suspect they would be broken in other ways with python-build-standalone. However, I'm not sure what else would have to be done to fix the segfault issue.
  • adapt how Matplotlib finds these symbols. I do not think it is technically possible to do what we do on windows on unix-like platform, but if it is that might be a solution. I would be happy to review any PRs to improve this, but this is not a priority for me on Matplotlib as it works with every other packaging ecosystem.
  • astral publishes a tk wheel to pypi that Matplotlib can depend on to source the needed symbols + PPs the updates to mpl to do so. However I suspect that would be super brittle as we would be back to the initial problem (different versions of tk floating around in a process) that drove Matplotlib to this dynamic loading and to my understanding there is not enough metadata available to make sure that uv/pip pull a version of tk compatible with what ever Python is (or is not) built with. Tk is an outlier in terms of the GUI toolkits because it is frequently shipped as part of Python, where as the bindings to all of the other toolkits are free to bring their own underlying libraries. A much (much) bigger lift would be to get the tkinter module pulled out of the standard library all together and only live on pypi. I think this is technically possible as debian has already done this split but I have no idea how politically possible it is. This is again assuming the segfault can be fixed.

An (I assume off the table) option is for astral to build custom Matplotlib wheels to work with python-build-standalone (you could revert our runtime logic to instead statically link to the correct tk and x11 libraries at your build time, that code is pretty static so keeping a local patch up-to-date should not be onerous) and then swap them in for users instead of the ones from pypi when using uv. Doing this would mean the Matplotlib wheels could be kept in exact sync with the build details of python-build-standalone (you could also do PyQt, PySides, and the rest of the GUI toolkits for the same reason), but would open up a whole bunch of other cans of worms 🦋 .


For users that need a fix now rather than when we have solved Python packaging I suggest:

  • if you are committed to working in the wheel packaging ecosystem and python-build-standalone and can use a different gui toolkit: install the dependencies for a different GUI toolkit of your choice. I think these invocations will work, but
    • for Qt6 (pyqt) uv add pyqt6 for pyqt (although, per https://github.com/astral-sh/python-build-standalone/blob/main/docs/quirks.rst#static-linking-of-libx11--incompatibility-with-pyqt-on-linux there is a chance this won't actually work),
    • wx requires adding an extra index from https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ to match your platform but it looks like you can thread that through uv.
    • I'm not sure if you can get gtk buildings without falling through to system packages and I could not get uv to install pyside6 (I assume user error on my part).
  • if you not need userspace Python but still need to use wheels for userspace package management, make sure you fall through to your system Python (which is likely dynamically linked to tk and x11 so things will work), see https://docs.astral.sh/uv/guides/install-python/#using-an-existing-python-installation
  • if you need userspace packaging and Python, but do not need to use wheels (at the risk committing a faux pas in uv's issues tracker) use pixi (https://prefix.dev) which primarily pulls its built artifacts from conda-forge (so they are all built consistently which avoids all of theses problems) and for anything that is not packaged on conda-forge will also add pypi / local dependencies. pixi is built around a similar project-based-venv workflow as uv.
  • if you don't need userspace package management, (again risking a faux pas) consider using your system package manager (yum, apt, brew, pacman, ...) as it will also be built in a consistent way and things will work.

tacaswell avatar Dec 30 '24 22:12 tacaswell

I just encountered this issue too, and I find this a deal breaker for me. I use matplotlib all the time and I don't want to switch to another backend. Until this is fixed, I'll configure uv to only use system Python installations and use pyenv to manage my Python installations instead of uv.

I read the docs about why uv doesn't build Python from source, and honestly I am not convinced. Most people have the required dependencies to build Python (Especially people who want to move from pyenv to uv like me) and those dependencies can easily be installed on most systems. About the fact that it could be slow to create an optimized build of Python: most people don't install tens or hundreds of different versions of Python and they don't install Python every day. Usually, only a few versions are installed and then virtual environments are created based on them. For example, I only really need Python 3.12 and maybe 3.13, so I don't install Python a lot. That being said, since Python installation doesn't happen very frequently anyway, I think that it's fine that building Python from source will be slower than using python-build-standalone. The only inconvenience it creates is when uv has to download Python dynamically (for example, if I request to run a command with a Python version that's not on my system), and this only happens once, since subsequential calls will use the previously installed Python download.

I personally disable automatic Python downloads, so uv downloads Python only when I tell it to, so I wouldn't care if downloading Python and building it from source will be slightly slower. As mentioned, I don't download new versions of Python frequently and I think most people don't either.

I would prefer if uv acted like pyenv and built Python from source since it would solve this issue and all the other quirks of python-build-standalone. At the very least, I think that we should be able to configure uv to build Python from source instead of using python-build-standalone.

ShaiAvr avatar Feb 18 '25 18:02 ShaiAvr

I appreciate your perspective but I think we're extremely unlikely to move towards requiring users to build Python from source. It does a huge disservice to users by pushing the complexity onto them. The solution here is to fix the issue in the matplotlib integration :)

charliermarsh avatar Feb 18 '25 18:02 charliermarsh

@charliermarsh I agree the matplotlib integration issue should be fixed, but I still believe it would be better to build Python from the source. As I said, if we're not ready to completely move to this option, we should at least allow the user to configure whether he wants that or not. We could slowly transition to building from source as the default option and recommend it over python-build-standalone.

Despite that, I still find uv an extremely useful and efficient tool, and I am certainly going to use it for projects, tools, and scripts management. I am still not ready to use it for Python management at this state :)

ShaiAvr avatar Feb 18 '25 18:02 ShaiAvr

I still believe it would be better to build Python from the source. As I said, if we're not ready to completely move to this option, we should at least allow the user to configure whether he wants that or not.

I'm sorry, but as Charlie said, this is extremely unlikely for us. We make it easy to disable our Python versions and use system interpreters for this reason. There are other tools that focus on building Python from source.

Please let's keep discussion here focused on the interaction between tkagg, matplotlib, and python-build-standalone.

zanieb avatar Feb 18 '25 19:02 zanieb

I have this code and this issue (it seems that there is a work around but not able to put it place)

from os import environ
from pathlib import Path
from sys import base_prefix

environ["TCL_LIBRARY"] = str(Path(base_prefix) / "lib" / "tcl8.6")
environ["TK_LIBRARY"] = str(Path(base_prefix) / "lib" / "tk8.6")

print(environ["TCL_LIBRARY"])
print(environ["TK_LIBRARY"])

from tkinter import *
from tkinter import ttk
root = Tk()
frm = ttk.Frame(root, padding=10)
frm.grid()
ttk.Label(frm, text="Hello World!").grid(column=0, row=0)
ttk.Button(frm, text="Quit", command=root.destroy).grid(column=1, row=0)
root.mainloop()

here is the issue .

UX_tkinter_opalka % /Users/user/Documents/UX_tkinter_opalka/.venv/bin/python "/Users/user/Documen
ts/UX_tkinter_opalka/test Tkinter.py"
/Users/user/.pyenv/versions/3.11.9/lib/tcl8.6
/Users/user/.pyenv/versions/3.11.9/lib/tk8.6
Traceback (most recent call last):
  File "/Users/user/Documents/UX_tkinter_opalka/test Tkinter.py", line 11, in <module>
    from tkinter import *
  File "/Users/user/.pyenv/versions/3.11.9/lib/python3.11/tkinter/__init__.py", line 38, in <module>
    import _tkinter # If this fails your Python may not be configured for Tk
    ^^^^^^^^^^^^^^^
ModuleNotFoundError: No module named '_tkinter'

pautric avatar Feb 20 '25 09:02 pautric

@pautric looks like you're using pyenv? That's a separate problem from this issue — and unrelated to uv as far as I can tell. pyenv builds Python on your machine, perhaps it did so without Tk available.

zanieb avatar Feb 20 '25 14:02 zanieb

I should this https://dev.to/xshapira/using-tkinter-with-pyenv-a-simple-two-step-guide-hh5

nippotam avatar Feb 20 '25 15:02 nippotam

I should this https://dev.to/xshapira/using-tkinter-with-pyenv-a-simple-two-step-guide-hh5

But it does not work 🤨

nippotam avatar Feb 20 '25 17:02 nippotam

I should this https://dev.to/xshapira/using-tkinter-with-pyenv-a-simple-two-step-guide-hh5

But it does not work 🤨

At the end I use qt instead and it works perfectly well with pyenv and uv.

nippotam avatar Feb 20 '25 21:02 nippotam

Please let's keep discussion here focused on the interaction between tkagg, matplotlib, and python-build-standalone.

Makes sense. I would just like to add a link to https://github.com/astral-sh/uv/issues/11942 & https://github.com/astral-sh/python-build-standalone/issues/146 , to make it clear to other thread readers, that there are a lot of other projects that depend on tkinter that are potentially breaking, and not only tkagg/matplotlib, such as FreeSimpleGUI etc.

villares avatar Mar 16 '25 15:03 villares

I changed for qt which is modern and works with uv. Then for me question solved. Le 16 mars 2025 à 16:46, Alexandre B A Villares @.***> a écrit :

Please let's keep discussion here focused on the interaction between tkagg, matplotlib, and python-build-standalone.

Makes sense. I would just like to add a link to #11942 , to make it clear to other thread readers, that there are a lot of other projects that depend on tkinter that are potentially breaking, and not only tkagg/matplotlib, such as FreeSimpleGUI etc.—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>

villares left a comment (astral-sh/uv#6893)

Please let's keep discussion here focused on the interaction between tkagg, matplotlib, and python-build-standalone.

Makes sense. I would just like to add a link to #11942 , to make it clear to other thread readers, that there are a lot of other projects that depend on tkinter that are potentially breaking, and not only tkagg/matplotlib, such as FreeSimpleGUI etc.

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>

pautric avatar Mar 16 '25 16:03 pautric

This is also an issue for me, I just tried to run

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

and I'm getting the error:

AttributeError: module '_tkinter' has no attribute '__file__'. Did you mean: '__name__'?

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

ImportError: failed to load tkinter functions

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

Traceback (most recent call last):
  File "/Users/richard/Documents/SwarmUROP/crazyflie-firmware/examples/swarmalator_swarm/GUI/towergui.py", line 9, in <module>
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
  File "/Users/richard/Documents/SwarmUROP/crazyflie-firmware/.venv/lib/python3.10/site-packages/matplotlib/backends/backend_tkagg.py", line 1, in <module>
    from . import _backend_tk
  File "/Users/richard/Documents/SwarmUROP/crazyflie-firmware/.venv/lib/python3.10/site-packages/matplotlib/backends/_backend_tk.py", line 25, in <module>
    from . import _tkagg
ImportError: initialization failed

r-bt avatar Mar 18 '25 02:03 r-bt

Update to python version 3.13.3 solved the issue for me

This is also an issue for me, I just tried to run

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

and I'm getting the error:

AttributeError: module '_tkinter' has no attribute '__file__'. Did you mean: '__name__'?

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

ImportError: failed to load tkinter functions

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

Traceback (most recent call last):
  File "/Users/richard/Documents/SwarmUROP/crazyflie-firmware/examples/swarmalator_swarm/GUI/towergui.py", line 9, in <module>
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
  File "/Users/richard/Documents/SwarmUROP/crazyflie-firmware/.venv/lib/python3.10/site-packages/matplotlib/backends/backend_tkagg.py", line 1, in <module>
    from . import _backend_tk
  File "/Users/richard/Documents/SwarmUROP/crazyflie-firmware/.venv/lib/python3.10/site-packages/matplotlib/backends/_backend_tk.py", line 25, in <module>
    from . import _tkagg
ImportError: initialization failed

Razlaw avatar May 16 '25 10:05 Razlaw

I just tried it again with a managed Python 3.13.3 install and nothing has changed on my side

bluthej avatar May 20 '25 20:05 bluthej

Use qt instead 🤓

nippotam avatar May 20 '25 20:05 nippotam

On osx 14.7.2 Was failing with python 3;10 still failing with python 3.13

python -c "from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg"

AttributeError: module '_tkinter' has no attribute '__file__'. Did you mean: '__name__'?

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

ImportError: failed to load tkinter functions

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

Traceback (most recent call last):
  File "<string>", line 1, in <module>
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
  File "/Users/alexandre/Code/python/scoring-mobile-v2/.venv/lib/python3.13/site-packages/matplotlib/backends/backend_tkagg.py", line 1, in <module>
    from . import _backend_tk
  File "/Users/alexandre/Code/python/scoring-mobile-v2/.venv/lib/python3.13/site-packages/matplotlib/backends/_backend_tk.py", line 25, in <module>
    from . import _tkagg
ImportError: initialization failed

t0k4rt avatar May 21 '25 09:05 t0k4rt

Just to state something that may be obvious to advanced users, but not to everyone:

It may work if you force uv to use non-managed, system python.

uv run --python /usr/bin/python3.10 ipython (or wherever your system python is installed)

(You may need to use uv pip install or something to reinstall matplotlib, ipython, etc.)

Then %matplotlib automatically and successfully uses the tkagg package, and plt.plot will display plots in interactive mode.

Unfortunately after you do that the specified python interpreter will be used by default (which is weird). If you want to switch back to previous/managed python you should run the same thing with the path of whichever version you want to use. Type uv python list to see all available python interpreters.

ThomasMiconi avatar May 22 '25 20:05 ThomasMiconi

I tried to investigate a possible workaround on the matplotlib side, but I think I'm running into astral-sh/python-build-standalone#533. More at

https://github.com/astral-sh/python-build-standalone/issues/129#issuecomment-3014950387

jkseppan avatar Jun 28 '25 04:06 jkseppan

Thanks! Replied to your comment over there but tl;dr I think we're going to have to move to building libtcl, libtk, and _tkinter as dynamic libraries so that uv's managed Python build acts enough like every other Python build for things to work as expected.

geofft avatar Jun 29 '25 03:06 geofft