summaryrefslogtreecommitdiff
path: root/examples/async_printing.py
diff options
context:
space:
mode:
authorKevin Van Brunt <kmvanbrunt@gmail.com>2018-09-21 20:40:00 -0400
committerKevin Van Brunt <kmvanbrunt@gmail.com>2018-09-21 20:40:00 -0400
commitd10badce0e9c12aa341c2de247523b7a494761fc (patch)
tree9740f72bfaa70150a035516b1e1baee659560701 /examples/async_printing.py
parentc706f6a95c41392fcca0b3a93b689a19ba06a0f4 (diff)
downloadcmd2-git-d10badce0e9c12aa341c2de247523b7a494761fc.tar.gz
First version of async printing example
Diffstat (limited to 'examples/async_printing.py')
-rwxr-xr-xexamples/async_printing.py121
1 files changed, 121 insertions, 0 deletions
diff --git a/examples/async_printing.py b/examples/async_printing.py
new file mode 100755
index 00000000..65e83bbe
--- /dev/null
+++ b/examples/async_printing.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+# coding=utf-8
+"""A simple example demonstrating an application that asynchronously prints alerts and updates the prompt"""
+
+import random
+import threading
+import time
+from typing import List
+
+import cmd2
+
+ALERTS = ["Mail server is down",
+ "Blockage detected in sector 4",
+ "Ants have overrun the break room",
+ "Your next appointment has arrived",
+ "Christmas bonuses are cancelled",
+ "Mandatory overtime this weekend",
+ "Jimmy quit",
+ "Jody got married",
+ "Is there anyone on board who knows how to fly a plane?",
+ "Gentlemen, you can't fight in here. This is the War Room!",
+ "Your mom goes to college!"]
+
+
+def get_alerts() -> List[str]:
+ """ Randomly generates alerts for testing purposes """
+
+ rand_num = random.randint(1, 20)
+ if rand_num > 3:
+ return []
+
+ alerts = []
+ for i in range(0, rand_num):
+ cur_alert = random.choice(ALERTS)
+ if cur_alert in alerts:
+ i -= 1
+ else:
+ alerts.append(cur_alert)
+
+ return alerts
+
+
+def get_alert_str() -> str:
+ """
+ Combines alerts into one string that can be printed to the terminal
+ :return: the alert string
+ """
+ alert_str = ''
+ alerts = get_alerts()
+
+ longest_alert = max(ALERTS, key=len)
+ num_astericks = len(longest_alert) + 8
+
+ for i, cur_alert in enumerate(alerts):
+ # Use padding to center the alert
+ padding = ' ' * int((num_astericks - len(cur_alert)) / 2)
+
+ if i > 0:
+ alert_str += '\n'
+ alert_str += '*' * num_astericks + '\n'
+ alert_str += padding + cur_alert + padding + '\n'
+ alert_str += '*' * num_astericks + '\n'
+
+ return alert_str
+
+
+class AlerterApp(cmd2.Cmd):
+ """ An app that shows off async_alert() and async_update_prompt() """
+
+ def __init__(self, *args, **kwargs) -> None:
+ """ Initializer """
+
+ super().__init__(*args, **kwargs)
+
+ self.prompt = "(APR)> "
+
+ # The thread that will asynchronously alert the user of events
+ self._stop_thread = False
+ self._alerter_thread = threading.Thread(name='alerter', target=self._alerter_thread_func)
+
+ # Create some hooks to handle the starting and stopping of our thread
+ self.register_preloop_hook(self._preloop_hook)
+ self.register_postloop_hook(self._postloop_hook)
+
+ def _preloop_hook(self) -> None:
+ """ Start the alerter thread """
+
+ # This function runs after cmdloop() locks _terminal_lock, which will be locked until the prompt appears.
+ # Therefore it is safe to start our thread since there is no risk of it alerting before the prompt is displayed.
+ self._stop_thread = False
+ self._alerter_thread.start()
+
+ def _postloop_hook(self) -> None:
+ """ Stops the alerter thread """
+ self._stop_thread = True
+ if self._alerter_thread.is_alive():
+ self._alerter_thread.join()
+
+ def _alerter_thread_func(self) -> None:
+ """ Prints alerts and updates the prompt any time the prompt is showing """
+
+ while not self._stop_thread:
+ # Always acquire _terminal_lock before printing alerts or updating the prompt
+ # To keep the app responsive, do not block on this call
+ if self._terminal_lock.acquire(blocking=False):
+
+ # We have the terminal lock. See if any alerts need to be printed.
+ alert_str = get_alert_str()
+
+ if alert_str:
+ self.async_alert(alert_str)
+
+ # Don't forget to release the lock
+ self._terminal_lock.release()
+
+ time.sleep(0.5)
+
+
+if __name__ == '__main__':
+ app = AlerterApp()
+ app.cmdloop()