summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd2/cmd2.py14
-rw-r--r--docs/hooks.rst42
-rw-r--r--tests/test_plugin.py48
3 files changed, 97 insertions, 7 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index b7be8cb2..a694b3ae 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -3080,6 +3080,8 @@ Script should contain one command per line, just like command would be typed in
self.cmdqueue.extend(callargs)
# Always run the preloop first
+ for func in self._preloop_hooks:
+ func()
self.preloop()
# If transcript-based regression testing was requested, then do that instead of the main loop
@@ -3098,6 +3100,8 @@ Script should contain one command per line, just like command would be typed in
self._cmdloop()
# Run the postloop() no matter what
+ for func in self._postloop_hooks:
+ func()
self.postloop()
###
@@ -3107,8 +3111,18 @@ Script should contain one command per line, just like command would be typed in
###
def _initialize_plugin_system(self):
"""Initialize the plugin system"""
+ self._preloop_hooks = []
+ self._postloop_hooks = []
self._postparsing_hooks = []
+ def register_preloop_hook(self, func):
+ """Register a function to be called at the beginning of the command loop."""
+ self._preloop_hooks.append(func)
+
+ def register_postloop_hook(self, func):
+ """Register a function to be called at the end of the command loop."""
+ self._postloop_hooks.append(func)
+
def register_postparsing_hook(self, func):
"""Register a function to be called after parsing user input but before running the command"""
self._postparsing_hooks.append(func)
diff --git a/docs/hooks.rst b/docs/hooks.rst
index 6a5975b8..b2553744 100644
--- a/docs/hooks.rst
+++ b/docs/hooks.rst
@@ -13,16 +13,36 @@ The typical way of starting a cmd2 application is as follows::
app = App()
app.cmdloop()
-There are several pre-existing methods and attributes which you can tweak to control the overall behavior of your
-application before, during, and after the command processing loop.
+There are several pre-existing methods and attributes which you can tweak to
+control the overall behavior of your application before, during,
+and after the command processing loop.
-Application Lifecycle Hook Methods
-----------------------------------
-The ``preloop`` and ``postloop`` methods run before and after the main loop, respectively.
+Application Lifecycle Hooks
+---------------------------
-.. automethod:: cmd2.cmd2.Cmd.preloop
+You can register methods to be called at the beginning of the command loop::
+
+ class App(cmd2.Cmd):
+ def __init__(self, *args, *kwargs):
+ super().__init__(*args, **kwargs)
+ self.register_preloop_hook(self.myhookmethod)
+
+ def myhookmethod(self):
+ self.poutput("before the loop begins")
+
+And also after the command loop has finished::
+
+ class App(cmd2.Cmd):
+ def __init__(self, *args, *kwargs):
+ super().__init__(*args, **kwargs)
+ self.register_postloop_hook(self.myhookmethod)
+
+ def myhookmethod(self):
+ self.poutput("before the loop begins")
+
+As you can see the preloop and postloop hook methods are not passed any
+parameters and any return value is ignored.
-.. automethod:: cmd2.cmd2.Cmd.postloop
Application Lifecycle Attributes
--------------------------------
@@ -119,6 +139,14 @@ Postcommand Hooks
Command Completed Hooks
^^^^^^^^^^^^^^^^^^^^^^^
+Deprecated Application Lifecycle Hook Methods
+---------------------------------------------
+
+The ``preloop`` and ``postloop`` methods run before and after the main loop, respectively.
+
+.. automethod:: cmd2.cmd2.Cmd.preloop
+
+.. automethod:: cmd2.cmd2.Cmd.postloop
Deprecated Command Processing Hooks
-----------------------------------
diff --git a/tests/test_plugin.py b/tests/test_plugin.py
index c82e6bcd..a6fc8fc1 100644
--- a/tests/test_plugin.py
+++ b/tests/test_plugin.py
@@ -23,6 +23,12 @@ class Plugin:
def reset_counters(self):
self.called_pph = 0
+ def prepost_hook_one(self):
+ self.poutput("one")
+
+ def prepost_hook_two(self):
+ self.poutput("two")
+
def pph(self, statement: cmd2.Statement) -> Tuple[bool, cmd2.Statement]:
self.called_pph += 1
return False, statement
@@ -53,6 +59,48 @@ class PluggedApp(Plugin, cmd2.Cmd):
# test hooks
#
###
+def test_preloop_hook(capsys):
+ app = PluggedApp()
+ app.register_preloop_hook(app.prepost_hook_one)
+ app.cmdqueue.append('say hello')
+ app.cmdqueue.append('quit')
+ app.cmdloop()
+ out, err = capsys.readouterr()
+ assert out == 'one\nhello\n'
+ assert not err
+
+def test_preloop_hooks(capsys):
+ app = PluggedApp()
+ app.register_preloop_hook(app.prepost_hook_one)
+ app.register_preloop_hook(app.prepost_hook_two)
+ app.cmdqueue.append('say hello')
+ app.cmdqueue.append('quit')
+ app.cmdloop()
+ out, err = capsys.readouterr()
+ assert out == 'one\ntwo\nhello\n'
+ assert not err
+
+def test_postloop_hook(capsys):
+ app = PluggedApp()
+ app.register_postloop_hook(app.prepost_hook_one)
+ app.cmdqueue.append('say hello')
+ app.cmdqueue.append('quit')
+ app.cmdloop()
+ out, err = capsys.readouterr()
+ assert out == 'hello\none\n'
+ assert not err
+
+def test_postloop_hooks(capsys):
+ app = PluggedApp()
+ app.register_postloop_hook(app.prepost_hook_one)
+ app.register_postloop_hook(app.prepost_hook_two)
+ app.cmdqueue.append('say hello')
+ app.cmdqueue.append('quit')
+ app.cmdloop()
+ out, err = capsys.readouterr()
+ assert out == 'hello\none\ntwo\n'
+ assert not err
+
def test_postparsing_hook(capsys):
app = PluggedApp()
app.onecmd_plus_hooks('say hello')