@@ -548,8 +548,8 @@ def __init__(self, completekey: str='tab', stdin=None, stdout=None, persistent_h
548
548
self .exit_code = None
549
549
550
550
# This lock should be acquired before doing any asynchronous changes to the terminal to
551
- # ensure the updates to the terminal don't interfere with the input being typed. It can be
552
- # acquired any time there is a readline prompt on screen .
551
+ # ensure the updates to the terminal don't interfere with the input being typed or output
552
+ # being printed by a command .
553
553
self .terminal_lock = threading .RLock ()
554
554
555
555
# ----- Methods related to presenting output to the user -----
@@ -3481,12 +3481,13 @@ def _clear_input_lines_str(self) -> str: # pragma: no cover
3481
3481
3482
3482
def async_alert (self , alert_msg : str , new_prompt : Optional [str ] = None ) -> None : # pragma: no cover
3483
3483
"""
3484
- Used to display an important message to the user while they are at the prompt in between commands.
3484
+ Display an important message to the user while they are at the prompt in between commands.
3485
3485
To the user it appears as if an alert message is printed above the prompt and their current input
3486
3486
text and cursor location is left alone.
3487
3487
3488
- IMPORTANT: Do not call this unless you have acquired self.terminal_lock
3489
- first, which ensures a prompt is onscreen
3488
+ IMPORTANT: This function will not print an alert unless it can acquire self.terminal_lock to ensure
3489
+ a prompt is onscreen. Therefore it is best to acquire the lock before calling this function
3490
+ to guarantee the alert prints.
3490
3491
3491
3492
:param alert_msg: the message to display to the user
3492
3493
:param new_prompt: if you also want to change the prompt that is displayed, then include it here
@@ -3525,34 +3526,48 @@ def async_alert(self, alert_msg: str, new_prompt: Optional[str] = None) -> None:
3525
3526
3526
3527
def async_update_prompt (self , new_prompt : str ) -> None : # pragma: no cover
3527
3528
"""
3528
- Updates the prompt while the user is still typing at it. This is good for alerting the user to system
3529
+ Update the prompt while the user is still typing at it. This is good for alerting the user to system
3529
3530
changes dynamically in between commands. For instance you could alter the color of the prompt to indicate
3530
3531
a system status or increase a counter to report an event. If you do alter the actual text of the prompt,
3531
3532
it is best to keep the prompt the same width as what's on screen. Otherwise the user's input text will
3532
3533
be shifted and the update will not be seamless.
3533
3534
3534
- IMPORTANT: Do not call this unless you have acquired self.terminal_lock
3535
- first, which ensures a prompt is onscreen
3535
+ IMPORTANT: This function will not update the prompt unless it can acquire self.terminal_lock to ensure
3536
+ a prompt is onscreen. Therefore it is best to acquire the lock before calling this function
3537
+ to guarantee the prompt changes.
3536
3538
3537
3539
:param new_prompt: what to change the prompt to
3540
+ :raises RuntimeError if called while another thread holds terminal_lock
3538
3541
"""
3539
3542
self .async_alert ('' , new_prompt )
3540
3543
3541
- @staticmethod
3542
- def set_window_title (title : str ) -> None : # pragma: no cover
3544
+ def set_window_title (self , title : str ) -> None : # pragma: no cover
3543
3545
"""
3544
- Sets the terminal window title
3546
+ Set the terminal window title
3547
+
3548
+ IMPORTANT: This function will not set the title unless it can acquire self.terminal_lock to avoid
3549
+ writing to stderr while a command is running. Therefore it is best to acquire the lock
3550
+ before calling this function to guarantee the title changes.
3551
+
3545
3552
:param title: the new window title
3553
+ :raises RuntimeError if called while another thread holds terminal_lock
3546
3554
"""
3547
3555
if not vt100_support :
3548
3556
return
3549
3557
3550
- import colorama .ansi as ansi
3551
- try :
3552
- sys .stderr .write (ansi .set_title (title ))
3553
- except AttributeError :
3554
- # Debugging in Pycharm has issues with setting terminal title
3555
- pass
3558
+ # Sanity check that can't fail if self.terminal_lock was acquired before calling this function
3559
+ if self .terminal_lock .acquire (blocking = False ):
3560
+ try :
3561
+ import colorama .ansi as ansi
3562
+ sys .stderr .write (ansi .set_title (title ))
3563
+ except AttributeError :
3564
+ # Debugging in Pycharm has issues with setting terminal title
3565
+ pass
3566
+
3567
+ self .terminal_lock .release ()
3568
+
3569
+ else :
3570
+ raise RuntimeError ("another thread holds terminal_lock" )
3556
3571
3557
3572
def cmdloop (self , intro : Optional [str ]= None ) -> None :
3558
3573
"""This is an outer wrapper around _cmdloop() which deals with extra features provided by cmd2.
0 commit comments