Repository: matplotlib/matplotlib
File: lib/matplotlib/backends/_backend_tk.py (Python)

Code before:

try:
    from . import _tkagg
    from ._tkagg import TK_PHOTO_COMPOSITE_OVERLAY, TK_PHOTO_COMPOSITE_SET
except ImportError as e:
    # catch incompatibility of python-build-standalone with Tk
    cause1 = getattr(e, '__cause__', None)
    cause2 = getattr(cause1, '__cause__', None)
    if (isinstance(cause1, ImportError) and
        isinstance(cause2, AttributeError) and
        "'_tkinter' has no attribute '__file__'" in str(cause2)):

        is_uv_python = "/uv/python" in (os.path.realpath(sys.executable))
        if is_uv_python:
            raise ImportError(
                "Failed to import tkagg backend. You are using a uv-installed python "
                "executable, which is not compatible with Tk. "
                "Please update your python via: "
                "`uv self update && uv python upgrade --reinstall`"
                ) from e
        else:
            raise ImportError(
                "Failed to import tkagg backend. This is likely caused by using a "
                "Python executable based on python-build-standalone, which is not "
                "compatible with Tk. "
                "Please use another Python interpreter or select another backend."
                ) from e
    else:
        raise


_log = logging.getLogger(__name__)
cursord = {
    cursors.MOVE: "fleur",
    cursors.HAND: "hand2",
    cursors.POINTER: "arrow",
    cursors.SELECT_REGION: "crosshair",
    cursors.WAIT: "watch",
    cursors.RESIZE_HORIZONTAL: "sb_h_double_arrow",
    cursors.RESIZE_VERTICAL: "sb_v_double_arrow",
}


@contextmanager
def _restore_foreground_window_at_end():
    foreground = _c_internal_utils.Win32_GetForegroundWindow()
    try:
        yield
    finally:
        if foreground and mpl.rcParams['tk.window_focus']:
            _c_internal_utils.Win32_SetForegroundWindow(foreground)

Review comment (suggestion):
This should be updated too to mention that a newer python-build-standalone version will fix this - for users of other tools (Poetry, mise, Bazel rules_python, etc.) there should be a path forward without changing the tools and workflow they're using, though it's not practical or necessary to specifically document what exactly they should do to upgrade.

(Also, if you're editing this, small nit above - I would specifically say that you're using an _outdated_ Python version, now that current uv-installed Python versions are compatible with Tk.)

Code after:

try:
    from . import _tkagg
    from ._tkagg import TK_PHOTO_COMPOSITE_OVERLAY, TK_PHOTO_COMPOSITE_SET
except ImportError as e:
    # catch incompatibility of python-build-standalone with Tk
    cause1 = getattr(e, '__cause__', None)
    cause2 = getattr(cause1, '__cause__', None)
    if (isinstance(cause1, ImportError) and
        isinstance(cause2, AttributeError) and
        "'_tkinter' has no attribute '__file__'" in str(cause2)):

        is_uv_python = "/uv/python" in (os.path.realpath(sys.executable))
        if is_uv_python:
            raise ImportError(
                "Failed to import tkagg backend. You appear to be using an outdated "
                "version of uv's managed Python distribution which is not compatible "
                "with Tk. Please upgrade to the latest uv version, then update "
                "Python with: `uv python upgrade --reinstall`"
                ) from e
        else:
            raise ImportError(
                "Failed to import tkagg backend. This is likely caused by using a "
                "Python executable based on python-build-standalone, which is not "
                "compatible with Tk. Recent versions of python-build-standalone "
                "should be compatible with Tk. Please update your python version "
                "or select another backend."
                ) from e
    else:
        raise


_log = logging.getLogger(__name__)
cursord = {
    cursors.MOVE: "fleur",
    cursors.HAND: "hand2",
    cursors.POINTER: "arrow",
    cursors.SELECT_REGION: "crosshair",
    cursors.WAIT: "watch",
    cursors.RESIZE_HORIZONTAL: "sb_h_double_arrow",
    cursors.RESIZE_VERTICAL: "sb_v_double_arrow",
}


@contextmanager
def _restore_foreground_window_at_end():
    foreground = _c_internal_utils.Win32_GetForegroundWindow()
    try:
        yield
    finally:
        if foreground and mpl.rcParams['tk.window_focus']: