diff --git a/requirements/posix.txt b/requirements/posix.txt index 0c22fa57..e27732a1 100644 --- a/requirements/posix.txt +++ b/requirements/posix.txt @@ -5,3 +5,4 @@ jupyter_client>=7.4.9,<9 pyzmq>=24.0.0 wurlitzer>=1.0.3 pyxdg>=0.26 +setuptools<71.0 diff --git a/requirements/windows.txt b/requirements/windows.txt index 9d6370b6..9b8ed909 100644 --- a/requirements/windows.txt +++ b/requirements/windows.txt @@ -3,3 +3,4 @@ ipykernel>=6.29.3,<7 ipython>=8.12.2,<9 jupyter_client>=7.4.9,<9 pyzmq>=24.0.0 +setuptools<71.0 diff --git a/spyder_kernels/console/outstream.py b/spyder_kernels/console/outstream.py index 0477ab29..78ce38b4 100644 --- a/spyder_kernels/console/outstream.py +++ b/spyder_kernels/console/outstream.py @@ -9,6 +9,8 @@ """ Custom Spyder Outstream class. """ +import os +import sys from ipykernel.iostream import OutStream @@ -20,3 +22,51 @@ def __init__(self, session, pub_thread, name, pipe=None, echo=None, *, watchfd=True): super().__init__(session, pub_thread, name, pipe, echo=echo, watchfd=watchfd, isatty=True) + + def _flush(self): + """This is where the actual send happens. + + _flush should generally be called in the IO thread, + unless the thread has been destroyed (e.g. forked subprocess). + + NOTE: Overrided method to be able to filter messages. + See spyder-ide/spyder#22181 + """ + self._flush_pending = False + self._subprocess_flush_pending = False + + if self.echo is not None: + try: + self.echo.flush() + except OSError as e: + if self.echo is not sys.__stderr__: + print(f"Flush failed: {e}", file=sys.__stderr__) + + for parent, data in self._flush_buffers(): + # Messages that will not be printed to the console. This allows us + # to deal with issues such as spyder-ide/spyder#22181 + filter_messages = ["Parent poll failed."] + + if data and not any( + [message in data for message in filter_messages] + ): + # FIXME: this disables Session's fork-safe check, + # since pub_thread is itself fork-safe. + # There should be a better way to do this. + self.session.pid = os.getpid() + content = {"name": self.name, "text": data} + msg = self.session.msg("stream", content, parent=parent) + + # Each transform either returns a new + # message or None. If None is returned, + # the message has been 'used' and we return. + for hook in self._hooks: + msg = hook(msg) + if msg is None: + return + + self.session.send( + self.pub_thread, + msg, + ident=self.topic, + )