diff options
author | Todd Leonhardt <todd.leonhardt@gmail.com> | 2018-03-13 17:53:49 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-13 17:53:49 -0400 |
commit | 2ba567b9883aa5b4cedb58791a3cf27b41f6a484 (patch) | |
tree | 5deafdd151a8bd2e0673a17af09bf42d966349ed | |
parent | 2aa397cf872f713843945038e1bcdd282b20ebc2 (diff) | |
parent | 40d3e5bfeb286eaa507363b78637d6198372737b (diff) | |
download | cmd2-git-2ba567b9883aa5b4cedb58791a3cf27b41f6a484.tar.gz |
Merge branch 'master' into feature/copy-back-shared-attrs
-rwxr-xr-x | cmd2.py | 14 | ||||
-rw-r--r-- | tests/test_cmd2.py | 41 | ||||
-rw-r--r-- | tests/test_submenu.py | 15 |
3 files changed, 66 insertions, 4 deletions
@@ -868,14 +868,14 @@ class AddSubmenu(object): raise ValueError("reformat_prompt should be either a format string or None") self.reformat_prompt = reformat_prompt + self.shared_attributes = {} if shared_attributes is None else shared_attributes if require_predefined_shares: - for attr in shared_attributes.keys(): + for attr in self.shared_attributes.keys(): if not hasattr(submenu, attr): raise AttributeError("The shared attribute '{attr}' is not defined in {cmd}. Either define {attr} " "in {cmd} or set require_predefined_shares=False." .format(cmd=submenu.__class__.__name__, attr=attr)) - self.shared_attributes = {} if shared_attributes is None else shared_attributes self.create_subclass = create_subclass self.preserve_shares = preserve_shares @@ -1024,6 +1024,7 @@ class Cmd(cmd.Cmd): allow_cli_args = True # Should arguments passed on the command-line be processed as commands? allow_redirection = True # Should output redirection and pipes be allowed default_to_shell = False # Attempt to run unrecognized commands as shell commands + quit_on_sigint = True # Quit the loop on interrupt instead of just resetting prompt reserved_words = [] # Attributes which ARE dynamically settable at runtime @@ -1897,7 +1898,14 @@ class Cmd(cmd.Cmd): self.poutput('{}{}'.format(self.prompt, line)) else: # Otherwise, read a command from stdin - line = self.pseudo_raw_input(self.prompt) + if not self.quit_on_sigint: + try: + line = self.pseudo_raw_input(self.prompt) + except KeyboardInterrupt: + self.poutput('^C') + line = '' + else: + line = self.pseudo_raw_input(self.prompt) # Run the command along with all associated pre and post hooks stop = self.onecmd_plus_hooks(line) diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index 92f21757..e22212d4 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -900,6 +900,47 @@ def test_precmd_hook_failure(hook_failure): assert out == True +class SayApp(cmd2.Cmd): + def __init__(self, *args, **kwargs): + # Need to use this older form of invoking super class constructor to support Python 2.x and Python 3.x + cmd2.Cmd.__init__(self, *args, **kwargs) + + def do_say(self, arg): + self.poutput(arg) + +@pytest.fixture +def say_app(): + app = SayApp() + app.stdout = StdOut() + return app + +def test_interrupt_quit(say_app): + # Mock out the input call so we don't actually wait for a user's response on stdin + m = mock.MagicMock(name='input') + m.side_effect = ['say hello', KeyboardInterrupt(), 'say goodbye', 'eof'] + sm.input = m + + say_app.cmdloop() + + # And verify the expected output to stdout + out = say_app.stdout.buffer + assert out == 'hello\n' + +def test_interrupt_noquit(say_app): + say_app.quit_on_sigint = False + + # Mock out the input call so we don't actually wait for a user's response on stdin + m = mock.MagicMock(name='input') + m.side_effect = ['say hello', KeyboardInterrupt(), 'say goodbye', 'eof'] + sm.input = m + + say_app.cmdloop() + + # And verify the expected output to stdout + out = say_app.stdout.buffer + assert out == 'hello\n^C\ngoodbye\n' + + class ShellApp(cmd2.Cmd): def __init__(self, *args, **kwargs): # Need to use this older form of invoking super class constructor to support Python 2.x and Python 3.x diff --git a/tests/test_submenu.py b/tests/test_submenu.py index 9b42f38f..0b5330a8 100644 --- a/tests/test_submenu.py +++ b/tests/test_submenu.py @@ -8,8 +8,17 @@ import cmd2 from conftest import run_cmd, StdOut, normalize +class SecondLevelB(cmd2.Cmd): + """To be used as a second level command class. """ + + def __init__(self, *args, **kwargs): + cmd2.Cmd.__init__(self, *args, **kwargs) + self.prompt = '2ndLevel B ' + + class SecondLevel(cmd2.Cmd): """To be used as a second level command class. """ + def __init__(self, *args, **kwargs): cmd2.Cmd.__init__(self, *args, **kwargs) self.prompt = '2ndLevel ' @@ -32,8 +41,10 @@ class SecondLevel(cmd2.Cmd): second_level_cmd = SecondLevel() +second_level_b_cmd = SecondLevelB() +@cmd2.AddSubmenu(second_level_b_cmd, command='secondb') @cmd2.AddSubmenu(second_level_cmd, command='second', aliases=('second_alias',), @@ -63,6 +74,7 @@ def submenu_app(): second_level_cmd.stdout = StdOut() return app + @pytest.fixture def secondlevel_app(): app = SecondLevel() @@ -89,6 +101,7 @@ def test_submenu_say_from_top_level(submenu_app): assert len(out2) == 0 assert out1[0] == "You called a command in TopLevel with {!r}.".format(line) + def test_submenu_second_say_from_top_level(submenu_app): line = 'testing' out1, out2 = run_submenu_cmd(submenu_app, 'second say ' + line) @@ -100,6 +113,7 @@ def test_submenu_second_say_from_top_level(submenu_app): assert len(out2) == 1 assert out2[0] == "You called a command in SecondLevel with {!r}.".format(line) + def test_submenu_say_from_second_level(secondlevel_app): line = 'testing' out = run_cmd(secondlevel_app, 'say ' + line) @@ -135,4 +149,3 @@ def test_submenu_from_top_help_second_say(submenu_app): def test_submenu_shared_attribute(submenu_app): out1, out2 = run_submenu_cmd(submenu_app, 'second get_top_level_attr') assert out2 == [str(submenu_app.top_level_attr)] - |