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 #!/usr/bin/python3
version = __version__ = "4.60.3.80 Unreleased" version = __version__ = "4.60.3.81 Unreleased"
_change_log = """ _change_log = """
Changelog since 4.60.0 released to PyPI on 8-May-2022 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) 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 For backward compatablity, must be enabled using set_options with use_button_shortcuts=True
Fixed docstring errors in set_options docstring 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 __version__ = version.split()[0] # For PEP 396 and PEP 345
@ -3625,8 +3630,6 @@ class Multiline(Element):
self.WriteOnly = write_only self.WriteOnly = write_only
self.AutoRefresh = auto_refresh self.AutoRefresh = auto_refresh
key = key if key is not None else k key = key if key is not None else k
self.previous_stdout = None
self.previous_stderr = None
self.reroute_cprint = reroute_cprint self.reroute_cprint = reroute_cprint
self.echo_stdout_stderr = echo_stdout_stderr self.echo_stdout_stderr = echo_stdout_stderr
self.Justification = 'left' if justification is None else justification self.Justification = 'left' if justification is None else justification
@ -3638,10 +3641,6 @@ class Multiline(Element):
self.wrap_lines = wrap_lines self.wrap_lines = wrap_lines
self.reroute_stdout = reroute_stdout self.reroute_stdout = reroute_stdout
self.reroute_stderr = reroute_stderr 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.no_scrollbar = no_scrollbar
self.hscrollbar = None # The horizontal scrollbar self.hscrollbar = None # The horizontal scrollbar
sz = size if size != (None, None) else s sz = size if size != (None, None) else s
@ -3856,33 +3855,32 @@ class Multiline(Element):
""" """
Sends stdout (prints) to this element Sends stdout (prints) to this element
""" """
Window._rerouted_stdout_stack.insert(0, (self.ParentForm, self, sys.stdout)) # if nothing on the stack, then need to save the very first stdout
self.previous_stdout = sys.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 sys.stdout = self
def reroute_stderr_to_here(self): def reroute_stderr_to_here(self):
""" """
Sends stderr to this element Sends stderr to this element
""" """
Window._rerouted_stderr_stack.insert(0, (self.ParentForm, self, sys.stderr)) if len(Window._rerouted_stderr_stack) == 0:
self.previous_stderr = sys.stderr Window._original_stderr = sys.stderr
Window._rerouted_stderr_stack.insert(0, (self.ParentForm, self))
sys.stderr = self sys.stderr = self
def restore_stdout(self): def restore_stdout(self):
""" """
Restore a previously re-reouted stdout back to the original destination Restore a previously re-reouted stdout back to the original destination
""" """
if self.previous_stdout: Window._restore_stdout()
sys.stdout = self.previous_stdout
self.previous_stdout = None # indicate no longer routed here
def restore_stderr(self): def restore_stderr(self):
""" """
Restore a previously re-reouted stderr back to the original destination Restore a previously re-reouted stderr back to the original destination
""" """
if self.previous_stderr: Window._restore_stderr()
sys.stderr = self.previous_stderr
self.previous_stderr = None # indicate no longer routed here
def write(self, txt): def write(self, txt):
""" """
@ -3891,8 +3889,6 @@ class Multiline(Element):
:param txt: text of output :param txt: text of output
:type txt: (str) :type txt: (str)
""" """
if self._this_elements_window_closed():
Window._restore_stdout()
try: try:
self.update(txt, append=True) self.update(txt, append=True)
# if need to echo, then send the same text to the destinatoin that isn't thesame as this one # 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) sys.stdout.write(txt)
elif sys.stderr != self: elif sys.stderr != self:
sys.stderr.write(txt) sys.stderr.write(txt)
# self.previous_stdout.write(txt)
except: except:
pass pass
@ -3918,19 +3913,14 @@ class Multiline(Element):
def __del__(self): def __del__(self):
""" """
If this Widget is deleted, be sure and restore the old stdout, stderr 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
# These trys are here because found that if the init fails, then in order for stdout and stderr to be restored. Instead explicit restores are called.
# the variables holding the old stdout won't exist and will get an error
"""
return
try:
self.restore_stdout()
except Exception as e:
pass
try:
self.restore_stderr()
except:
pass
Get = get Get = get
Update = update Update = update
@ -9750,8 +9740,10 @@ class Window:
_floating_debug_window_build_needed = False _floating_debug_window_build_needed = False
_main_debug_window_build_needed = False _main_debug_window_build_needed = False
# rereouted stdout info. List of tuples (window, element, previous destination) # rereouted stdout info. List of tuples (window, element, previous destination)
_rerouted_stdout_stack = [] # type: List[Tuple[Window, Element, Any]] _rerouted_stdout_stack = [] # type: List[Tuple[Window, Element]]
_rerouted_stderr_stack = [] # reroutred sterr info. List of tuples (window, element, previous destination) _rerouted_stderr_stack = [] # type: List[Tuple[Window, Element]]
_original_stdout = None
_original_stderr = None
def __init__(self, title, layout=None, default_element_size=None, def __init__(self, title, layout=None, default_element_size=None,
default_button_element_size=(None, 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 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. a window so that resources are properly freed up within your thread.
""" """
self._restore_stdout()
try: try:
del Window._active_windows[self] # will only be in the list if window was explicitly finalized 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.Rows = None
self.TKroot = None self.TKroot = None
self._restore_stdout()
self._restore_stderr()
def is_closed(self, quick_check=None): def is_closed(self, quick_check=None):
""" """
Returns True is the window is maybe closed. Can be difficult to tell sometimes 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 self.LastButtonClicked = WINDOW_CLOSE_ATTEMPTED_EVENT
if self.close_destroys_window: if self.close_destroys_window:
self.RootNeedsDestroying = True self.RootNeedsDestroying = True
self._restore_stdout()
self._restore_stderr()
def disable(self): def disable(self):
""" """
@ -12300,12 +12298,29 @@ class Window:
@classmethod @classmethod
def _restore_stdout(cls): def _restore_stdout(cls):
for item in cls._rerouted_stdout_stack: 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(): if not window.is_closed():
sys.stdout = previous_stdout sys.stdout = element
break break
cls._rerouted_stdout_stack = [item for item in cls._rerouted_stdout_stack if not item[0].is_closed()] 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 ])