diff options
-rw-r--r-- | cmd2/cmd2.py | 14 | ||||
-rw-r--r-- | docs/hooks.rst | 42 | ||||
-rw-r--r-- | tests/test_plugin.py | 48 |
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') |