Merge pull request #5801 from PySimpleGUI/Dev-latest

Fix for the stdout and stderr rerouting bug. Now correctly restores s…
This commit is contained in:
PySimpleGUI 2022-08-21 18:53:16 -04:00 committed by GitHub
commit 45e55b43f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 53 additions and 38 deletions

View File

@ -1,6 +1,6 @@
#!/usr/bin/python3
version = __version__ = "4.60.3.80 Unreleased"
version = __version__ = "4.60.3.81 Unreleased"
_change_log = """
Changelog since 4.60.0 released to PyPI on 8-May-2022
@ -211,6 +211,11 @@ _change_log = """
Quick and dirty addition of Alt-shortcuts for Buttons (like exists for Menus)
For backward compatablity, must be enabled using set_options with use_button_shortcuts=True
Fixed docstring errors in set_options docstring
4.60.3.81
Completed restoration of stdout & stderr
If an Output Element is used or a Multline element to reroute stdout and/or stderr, then this hasn't worked quite right in the past
Hopefuly now, it does. A LIFO list (stack) is used to keep track of the current output device and is scrubbed for closed windows and restored if one is closed
"""
__version__ = version.split()[0] # For PEP 396 and PEP 345
@ -3625,8 +3630,6 @@ class Multiline(Element):
self.WriteOnly = write_only
self.AutoRefresh = auto_refresh
key = key if key is not None else k
self.previous_stdout = None
self.previous_stderr = None
self.reroute_cprint = reroute_cprint
self.echo_stdout_stderr = echo_stdout_stderr
self.Justification = 'left' if justification is None else justification
@ -3638,10 +3641,6 @@ class Multiline(Element):
self.wrap_lines = wrap_lines
self.reroute_stdout = reroute_stdout
self.reroute_stderr = reroute_stderr
# if reroute_stdout:
# self.reroute_stdout_to_here()
# if reroute_stderr:
# self.reroute_stderr_to_here()
self.no_scrollbar = no_scrollbar
self.hscrollbar = None # The horizontal scrollbar
sz = size if size != (None, None) else s
@ -3856,33 +3855,32 @@ class Multiline(Element):
"""
Sends stdout (prints) to this element
"""
Window._rerouted_stdout_stack.insert(0, (self.ParentForm, self, sys.stdout))
self.previous_stdout = sys.stdout
# if nothing on the stack, then need to save the very first stdout
if len(Window._rerouted_stdout_stack) == 0:
Window._original_stdout = sys.stdout
Window._rerouted_stdout_stack.insert(0, (self.ParentForm, self))
sys.stdout = self
def reroute_stderr_to_here(self):
"""
Sends stderr to this element
"""
Window._rerouted_stderr_stack.insert(0, (self.ParentForm, self, sys.stderr))
self.previous_stderr = sys.stderr
if len(Window._rerouted_stderr_stack) == 0:
Window._original_stderr = sys.stderr
Window._rerouted_stderr_stack.insert(0, (self.ParentForm, self))
sys.stderr = self
def restore_stdout(self):
"""
Restore a previously re-reouted stdout back to the original destination
"""
if self.previous_stdout:
sys.stdout = self.previous_stdout
self.previous_stdout = None # indicate no longer routed here
Window._restore_stdout()
def restore_stderr(self):
"""
Restore a previously re-reouted stderr back to the original destination
"""
if self.previous_stderr:
sys.stderr = self.previous_stderr
self.previous_stderr = None # indicate no longer routed here
Window._restore_stderr()
def write(self, txt):
"""
@ -3891,8 +3889,6 @@ class Multiline(Element):
:param txt: text of output
:type txt: (str)
"""
if self._this_elements_window_closed():
Window._restore_stdout()
try:
self.update(txt, append=True)
# if need to echo, then send the same text to the destinatoin that isn't thesame as this one
@ -3901,7 +3897,6 @@ class Multiline(Element):
sys.stdout.write(txt)
elif sys.stderr != self:
sys.stderr.write(txt)
# self.previous_stdout.write(txt)
except:
pass
@ -3918,19 +3913,14 @@ class Multiline(Element):
def __del__(self):
"""
If this Widget is deleted, be sure and restore the old stdout, stderr
"""
# These trys are here because found that if the init fails, then
# the variables holding the old stdout won't exist and will get an error
AT ONE TIME --- If this Widget is deleted, be sure and restore the old stdout, stderr
Now the restore is done differently. Do not want to RELY on Python to call this method
in order for stdout and stderr to be restored. Instead explicit restores are called.
"""
return
try:
self.restore_stdout()
except Exception as e:
pass
try:
self.restore_stderr()
except:
pass
Get = get
Update = update
@ -9750,8 +9740,10 @@ class Window:
_floating_debug_window_build_needed = False
_main_debug_window_build_needed = False
# rereouted stdout info. List of tuples (window, element, previous destination)
_rerouted_stdout_stack = [] # type: List[Tuple[Window, Element, Any]]
_rerouted_stderr_stack = [] # reroutred sterr info. List of tuples (window, element, previous destination)
_rerouted_stdout_stack = [] # type: List[Tuple[Window, Element]]
_rerouted_stderr_stack = [] # type: List[Tuple[Window, Element]]
_original_stdout = None
_original_stderr = None
def __init__(self, title, layout=None, default_element_size=None,
default_button_element_size=(None, None),
@ -11365,7 +11357,6 @@ class Window:
Closes window. Users can safely call even if window has been destroyed. Should always call when done with
a window so that resources are properly freed up within your thread.
"""
self._restore_stdout()
try:
del Window._active_windows[self] # will only be in the list if window was explicitly finalized
@ -11397,6 +11388,11 @@ class Window:
self.Rows = None
self.TKroot = None
self._restore_stdout()
self._restore_stderr()
def is_closed(self, quick_check=None):
"""
Returns True is the window is maybe closed. Can be difficult to tell sometimes
@ -11449,6 +11445,8 @@ class Window:
self.LastButtonClicked = WINDOW_CLOSE_ATTEMPTED_EVENT
if self.close_destroys_window:
self.RootNeedsDestroying = True
self._restore_stdout()
self._restore_stderr()
def disable(self):
"""
@ -12300,12 +12298,29 @@ class Window:
@classmethod
def _restore_stdout(cls):
for item in cls._rerouted_stdout_stack:
(window, element, previous_stdout) = item # type: (Window, Element, Any)
(window, element) = item # type: (Window, Element)
if not window.is_closed():
sys.stdout = previous_stdout
sys.stdout = element
break
cls._rerouted_stdout_stack = [item for item in cls._rerouted_stdout_stack if not item[0].is_closed()]
if len(cls._rerouted_stdout_stack) == 0 and cls._original_stdout is not None:
sys.stdout = cls._original_stdout
# print('Restored stdout... new stack:', [item[0].Title for item in cls._rerouted_stdout_stack ])
@classmethod
def _restore_stderr(cls):
for item in cls._rerouted_stderr_stack:
(window, element) = item # type: (Window, Element)
if not window.is_closed():
sys.stderr = element
break
cls._rerouted_stderr_stack = [item for item in cls._rerouted_stderr_stack if not item[0].is_closed()]
if len(cls._rerouted_stderr_stack) == 0 and cls._original_stderr is not None:
sys.stderr = cls._original_stderr
# print('Restored stderr... new stack:', [item[0].Title for item in cls._rerouted_stderr_stack ])