Fixed, I think.  We just need to patch a dlopen call.

Now TkAgg works out of the box, without requiring changes to user Python code.  I hope that also means we can change the default backend from Agg to TkAgg. 

Submitted this PR (and got lucky number 1000!)
https://codeberg.org/guix/guix/pulls/1000
with the fix and change of backend.

Cheers
Jake

On Thu, 3 Jul 2025 at 11:18 pm, Jake <jforst.mailman@gmail.com> wrote:
Continuing:
In `lib/matplotlib/cbook.py`, the function
`_get_running_interactive_framework` has:

```text
    if not _c_internal_utils.display_is_valid():
        return "headless"
```

In `src/_c_internal_utils.c` we have this docstring for `display_is_valid`:

```text
     "Check whether the current X11 or Wayland display is valid.\n\n"
     "On Linux, returns True if either $DISPLAY is set and XOpenDisplay(NULL)\n"
     "succeeds, or $WAYLAND_DISPLAY is set and wl_display_connect(NULL)\n"
     "succeeds.\n\n"
     "On other platforms, always returns True."},
```

$DISPLAY is set, so it must be XOpenDisplay(NULL) failing...

I found a post [1] from a Nix user:
> For anyone else facing similar issues - I resolved this by adding the X11 library to the LD_LIBRARY_PATH of my development shell.

But I haven't made any more progress.

Cheers
Jake

[1] https://discourse.nixos.org/t/python-matplotlib-tkinter-matplotlib-reverts-to-agg-backend-because-it-cant-find-a-valid-display/45064

On Thu, Jul 3, 2025 at 1:19 PM Jake <jforst.mailman@gmail.com> wrote:
>
> Hi Lily
>
> It looks like this bug isn't going away anytime soon.
>
> Here is the offending code in `lib/matplotlib/pyplot.py`, with an
> added print statement:
>
> ```text
> # If rcParams['backend_fallback'] is true, and an interactive backend is
> # requested, ignore rcParams['backend'] and force selection of a backend that
> # is compatible with the current running interactive framework.
> if (rcParams["backend_fallback"]
>         and rcParams._get_backend_or_none() in (  # type: ignore
>             set(rcsetup.interactive_bk) - {'WebAgg', 'nbAgg'})
>         and cbook._get_running_interactive_framework()):  # type: ignore
>     print(f"running interactive framework is:
> {cbook._get_running_interactive_framework()}")
>     rcParams._set("backend", rcsetup._auto_backend_sentinel)  # type: ignore
> ```
>
> Running this
>
> ```text
> MPLBACKEND=tkagg guix shell
> --with-source=python-matplotlib=$HOME/matplotlib
> --without-tests=python-matplotlib python python-matplotlib -- python3
> -c "import matplotlib; print(matplotlib.get_backend()); import
> matplotlib.pyplot as plt; print(matplotlib.get_backend())"
> ```
>
> gives
>
> ```text
> TkAgg
> running interactive framework is: headless
> agg
> ```
>
> So I guess the next question is: why does it think I'm headless?  To
> be continued...
>
> Thanks
> Jake
>
>
> On Sat, Apr 5, 2025 at 11:00 PM Liliana Marie Prikler
> <liliana.prikler@gmail.com> wrote:
> >
> > Hi Jake,
> >
> > Am Donnerstag, dem 03.04.2025 um 11:36 +0000 schrieb Jake:
> > > It appears that importing matplotlib.pyplot resets the Matplotlib
> > > backend to Agg.
> > >
> > >   $ guix shell python python-matplotlib -- bash -c 'MPLBACKEND=tkagg
> > > python3 -c "import matplotlib; print(matplotlib.get_backend());
> > > import matplotlib.pyplot; print(matplotlib.get_backend())"'
> > >
> > >   TkAgg
> > >   agg
> > I recently encountered the same issue.  The issue is that matplotlib
> > internally sources a configuration file that sets the backend *after*
> > reading the environment variable.  To circumvent this, you use
> > something along the lines of the following code until the issue is
> > fixed:
> >
> >   from matplotlib import set_backend
> >   from os import environ
> >
> >   […]
> >
> >   if __name__ == '__main__':
> >     if 'MPLBACKEND' in environ: set_backend(environ['MPLBACKEND'])
> >     […]
> >
> > Cheers