From 635d94a952c636c7d125cac5a7caf2c9e7db87f3 Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 3 Mar 2025 08:38:12 +0100 Subject: [PATCH 1/6] restore output coloring without IPython just needed a couple ANSI constants --- ipyparallel/client/client.py | 22 +++++++++++++++++++++- ipyparallel/util.py | 8 ++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/ipyparallel/client/client.py b/ipyparallel/client/client.py index b47e60c0..f3754e73 100644 --- a/ipyparallel/client/client.py +++ b/ipyparallel/client/client.py @@ -49,6 +49,7 @@ from ipyparallel import error, serialize, util from ipyparallel.serialize import PrePickled, Reference from ipyparallel.util import _OutputProducingThread as Thread +from ipyparallel.util import _TermColors from .asyncresult import AsyncHubResult, AsyncResult from .futures import MessageFuture, multi_future @@ -161,11 +162,30 @@ def _plaintext(self) -> str: if not text_out: return '' + ip = get_ipython() + if ip is None: + colors = "NoColor" + else: + colors = ip.colors + + if colors == "NoColor": + out = normal = "" + else: + out = _TermColors.Red + normal = _TermColors.Normal + if '\n' in text_out and not text_out.startswith('\n'): # add newline for multiline reprs text_out = '\n' + text_out - return f"Out[{self.metadata['engine_id']}:{self.execution_count}]: {text_out}" + return ''.join( + [ + out, + f"Out[{self.metadata['engine_id']}:{self.execution_count}]: ", + normal, + text_out, + ] + ) def _repr_pretty_(self, p, cycle): p.text(self._plaintext()) diff --git a/ipyparallel/util.py b/ipyparallel/util.py index d4a953f4..e87fc152 100644 --- a/ipyparallel/util.py +++ b/ipyparallel/util.py @@ -13,6 +13,7 @@ import sys import warnings from datetime import datetime, timezone +from enum import Enum from functools import lru_cache, partial from signal import SIGABRT, SIGINT, SIGTERM, signal from threading import Thread, current_thread @@ -836,3 +837,10 @@ def __init__(self, target, **kwargs): def _wrapped_target(self, target, *args, **kwargs): _detach_thread_output(self.ident) return target(*args, **kwargs) + + +# minimal subset of TermColors, removed from IPython +# not for public consumption +class _TermColors(Enum): + Normal = '\033[0m' + Red = '\033[0;31m' From e3530b9ff945879a1598172a74b060a69330e939 Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 3 Mar 2025 08:42:01 +0100 Subject: [PATCH 2/6] don't need enum --- ipyparallel/util.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ipyparallel/util.py b/ipyparallel/util.py index e87fc152..7a675980 100644 --- a/ipyparallel/util.py +++ b/ipyparallel/util.py @@ -13,7 +13,6 @@ import sys import warnings from datetime import datetime, timezone -from enum import Enum from functools import lru_cache, partial from signal import SIGABRT, SIGINT, SIGTERM, signal from threading import Thread, current_thread @@ -841,6 +840,6 @@ def _wrapped_target(self, target, *args, **kwargs): # minimal subset of TermColors, removed from IPython # not for public consumption -class _TermColors(Enum): +class _TermColors: Normal = '\033[0m' Red = '\033[0;31m' From 40841dcc819cb2ece8dd8072fc80a2162fae5e58 Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 3 Mar 2025 08:49:28 +0100 Subject: [PATCH 3/6] avoid imports of internal core.display less sensitive to implementation details, use public imports --- ipyparallel/tests/clienttest.py | 2 +- ipyparallel/tests/test_magics.py | 6 +++--- ipyparallel/tests/test_view.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ipyparallel/tests/clienttest.py b/ipyparallel/tests/clienttest.py index 9f5fb96e..9ac2272d 100644 --- a/ipyparallel/tests/clienttest.py +++ b/ipyparallel/tests/clienttest.py @@ -55,7 +55,7 @@ def generate_output(): a rich displayable object. """ - from IPython.core.display import HTML, Math, display + from IPython.display import HTML, Math, display print("stdout") print("stderr", file=sys.stderr) diff --git a/ipyparallel/tests/test_magics.py b/ipyparallel/tests/test_magics.py index ca3588b7..52f3a52f 100644 --- a/ipyparallel/tests/test_magics.py +++ b/ipyparallel/tests/test_magics.py @@ -173,18 +173,18 @@ def test_cellpx_groupby_order(self): expected.extend( [ r'\[output:\d+\]', - 'IPython.core.display.HTML', + 'IPython..+.HTML', ] * len(v) ) expected.extend( [ r'\[output:\d+\]', - 'IPython.core.display.Math', + 'IPython..+.Math', ] * len(v) ) - expected.extend([r'Out\[\d+:\d+\]:.*IPython\.core\.display\.Math'] * len(v)) + expected.extend([r'Out\[\d+:\d+\]:.*IPython\..+\.Math'] * len(v)) assert len(lines), len(expected) == io.stdout for line, expect in zip(lines, expected): diff --git a/ipyparallel/tests/test_view.py b/ipyparallel/tests/test_view.py index 87439015..e073ec44 100644 --- a/ipyparallel/tests/test_view.py +++ b/ipyparallel/tests/test_view.py @@ -614,7 +614,7 @@ def test_execute_magic(self): def test_execute_displaypub(self): """execute tracks display_pub output""" view = self.client[:] - view.execute("from IPython.core.display import *") + view.execute("from IPython.display import *") ar = view.execute("[ display(i) for i in range(5) ]", block=True) expected = [{'text/plain': str(j)} for j in range(5)] @@ -625,7 +625,7 @@ def test_execute_displaypub(self): def test_apply_displaypub(self): """apply tracks display_pub output""" view = self.client[:] - view.execute("from IPython.core.display import *") + view.execute("from IPython.display import *") @interactive def publish(): From 421fd3224c6552227874a523eaa3de99d26300e8 Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 3 Mar 2025 09:00:06 +0100 Subject: [PATCH 4/6] case-insensitive nocolor check --- ipyparallel/client/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipyparallel/client/client.py b/ipyparallel/client/client.py index f3754e73..8fec6f11 100644 --- a/ipyparallel/client/client.py +++ b/ipyparallel/client/client.py @@ -168,7 +168,7 @@ def _plaintext(self) -> str: else: colors = ip.colors - if colors == "NoColor": + if colors.lower() == "nocolor": out = normal = "" else: out = _TermColors.Red From 772ac64f02c887037dec4704bf39d08b5200de16 Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 3 Mar 2025 09:05:40 +0100 Subject: [PATCH 5/6] don't test old-timey pylab test matplotlib directly --- ipyparallel/tests/test_magics.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ipyparallel/tests/test_magics.py b/ipyparallel/tests/test_magics.py index 52f3a52f..7602250c 100644 --- a/ipyparallel/tests/test_magics.py +++ b/ipyparallel/tests/test_magics.py @@ -440,8 +440,8 @@ def test_result(self): ip.run_line_magic('pxresult', '') assert str(data[name]) in io.stdout - def test_px_pylab(self): - """%pylab works on engines""" + def test_px_matplotlib(self): + """%matplotlib inline works on engines""" pytest.importorskip('matplotlib') ip = get_ipython() v = self.client[-1] @@ -449,15 +449,19 @@ def test_px_pylab(self): v.activate() with capture_output() as io: - ip.run_line_magic("px", "%pylab inline") - - assert ( - "Populating the interactive namespace from numpy and matplotlib" - in io.stdout - ) + ip.run_line_magic( + "px", + "\n".join( + [ + "%matplotlib inline", + "import numpy as np", + "import matplotlib.pyplot as plt", + ] + ), + ) with capture_output(display=False) as io: - ip.run_line_magic("px", "plot(rand(100))") + ip.run_line_magic("px", "plt.plot(np.random.rand(100))") assert 'Out[' in io.stdout assert 'matplotlib.lines' in io.stdout From 943fa507cfed2c1f2b5ac706d2af7e78a8713f5e Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 3 Mar 2025 09:52:53 +0100 Subject: [PATCH 6/6] add some missing stages to shell init make sure to pre-init gui integration and load extensions --- ipyparallel/engine/app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ipyparallel/engine/app.py b/ipyparallel/engine/app.py index 7f27dcaa..9028f400 100755 --- a/ipyparallel/engine/app.py +++ b/ipyparallel/engine/app.py @@ -758,6 +758,8 @@ def send_with_metadata( if self.use_mpi and self.init_mpi: app.exec_lines.insert(0, self.init_mpi) app.init_profile_dir() + app.init_gui_pylab() + app.init_extensions() app.init_code() # redirect output at the end, only after start is called