From 6cb3105805089db1d6c25f49c832d9708516fa0c Mon Sep 17 00:00:00 2001 From: Neil Vaytet Date: Fri, 31 Jul 2026 12:13:26 +0200 Subject: [PATCH 1/3] monkeypatch _mathtext.Parser.parse() with a threading.Lock() --- src/plopp/__init__.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/plopp/__init__.py b/src/plopp/__init__.py index 6e84745c..c385659b 100644 --- a/src/plopp/__init__.py +++ b/src/plopp/__init__.py @@ -44,4 +44,29 @@ backends = BackendManager() +# Workaround for thread-safety issues in matplotlib's mathtext parser. +# The _mathtext.Parser singleton has mutable state (_state_stack, etc.) that is +# not protected against concurrent access from multiple threads. When ipympl is +# used with ipykernel >= 7 (which introduces additional threads for message +# routing), concurrent calls to the parser can corrupt this state, leading to +# ParseException errors like 'Expected end of text, found "$"'. +# See https://github.com/matplotlib/ipympl/issues/610 +try: + import functools + from threading import Lock + + from matplotlib._mathtext import Parser as _MathTextInternalParser + + _mathtext_parse_lock = Lock() + _original_mathtext_parse = _MathTextInternalParser.parse + + @functools.wraps(_original_mathtext_parse) + def _thread_safe_mathtext_parse(self, *args, **kwargs): + with _mathtext_parse_lock: + return _original_mathtext_parse(self, *args, **kwargs) + + _MathTextInternalParser.parse = _thread_safe_mathtext_parse +except Exception: + pass + del importlib From d65f3c5c193cfdac8ad8b9523f4ab59f7894bca4 Mon Sep 17 00:00:00 2001 From: Neil Vaytet Date: Fri, 31 Jul 2026 12:25:36 +0200 Subject: [PATCH 2/3] cleanup after monkeypatch --- src/plopp/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plopp/__init__.py b/src/plopp/__init__.py index c385659b..50747d6e 100644 --- a/src/plopp/__init__.py +++ b/src/plopp/__init__.py @@ -66,6 +66,8 @@ def _thread_safe_mathtext_parse(self, *args, **kwargs): return _original_mathtext_parse(self, *args, **kwargs) _MathTextInternalParser.parse = _thread_safe_mathtext_parse + + del functools, Lock except Exception: pass From c9066b84ecf2885cc1de90707c6055172a1b06c5 Mon Sep 17 00:00:00 2001 From: Neil Vaytet Date: Fri, 31 Jul 2026 12:27:26 +0200 Subject: [PATCH 3/3] ruff happy --- src/plopp/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plopp/__init__.py b/src/plopp/__init__.py index 50747d6e..57a53f09 100644 --- a/src/plopp/__init__.py +++ b/src/plopp/__init__.py @@ -68,7 +68,7 @@ def _thread_safe_mathtext_parse(self, *args, **kwargs): _MathTextInternalParser.parse = _thread_safe_mathtext_parse del functools, Lock -except Exception: +except Exception: # noqa: S110 pass del importlib