summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Leonhardt <todd.leonhardt@gmail.com>2018-05-09 23:29:33 -0400
committerGitHub <noreply@github.com>2018-05-09 23:29:33 -0400
commit9d4d929709ffbcfcbd0974d8193c44d514f5a9b4 (patch)
treed8043347df39aa5511543fa09d030b6d67116880
parent6a3dbec1111ef1131781a6af441b89f68801c82a (diff)
parentb88b13ea9a157196bef4269564b0583adc531053 (diff)
downloadcmd2-git-9d4d929709ffbcfcbd0974d8193c44d514f5a9b4.tar.gz
Merge pull request #397 from python-cmd2/extract_submenu
Extract submenu code to a new project
-rw-r--r--CHANGELOG.md1
-rwxr-xr-xcmd2/cmd2.py264
-rwxr-xr-xexamples/submenus.py109
-rw-r--r--tests/test_completion.py121
-rw-r--r--tests/test_submenu.py181
5 files changed, 1 insertions, 675 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dac0756f..503f15e0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@
* Changes
* ``strip_ansi()`` and ``strip_quotes()`` functions have moved to new utils module
* Several constants moved to new constants module
+ * Submenu support has been moved to a new [cmd2-submenu](https://github.com/python-cmd2/cmd2-submenu) plugin. If you use submenus, you will need to update your dependencies and modify your imports.
* Deletions (potentially breaking changes)
* Deleted all ``optparse`` code which had previously been deprecated in release 0.8.0
* The ``options`` decorator no longer exists
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index 725b6497..02ae96fe 100755
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -327,270 +327,6 @@ class EmptyStatement(Exception):
pass
-def _pop_readline_history(clear_history: bool=True) -> List[str]:
- """Returns a copy of readline's history and optionally clears it (default)"""
- # noinspection PyArgumentList
- if rl_type == RlType.NONE:
- return []
-
- history = [
- readline.get_history_item(i)
- for i in range(1, 1 + readline.get_current_history_length())
- ]
- if clear_history:
- readline.clear_history()
- return history
-
-
-def _push_readline_history(history, clear_history=True):
- """Restores readline's history and optionally clears it first (default)"""
- if rl_type != RlType.NONE:
- if clear_history:
- readline.clear_history()
- for line in history:
- readline.add_history(line)
-
-
-def _complete_from_cmd(cmd_obj, text, line, begidx, endidx):
- """Complete as though the user was typing inside cmd's cmdloop()"""
- from itertools import takewhile
- command_subcommand_params = line.split(None, 3)
-
- if len(command_subcommand_params) < (3 if text else 2):
- n = len(command_subcommand_params[0])
- n += sum(1 for _ in takewhile(str.isspace, line[n:]))
- return cmd_obj.completenames(text, line[n:], begidx - n, endidx - n)
-
- command, subcommand = command_subcommand_params[:2]
- n = len(command) + sum(1 for _ in takewhile(str.isspace, line))
- cfun = getattr(cmd_obj, 'complete_' + subcommand, cmd_obj.complete)
- return cfun(text, line[n:], begidx - n, endidx - n)
-
-
-class AddSubmenu(object):
- """Conveniently add a submenu (Cmd-like class) to a Cmd
-
- e.g. given "class SubMenu(Cmd): ..." then
-
- @AddSubmenu(SubMenu(), 'sub')
- class MyCmd(cmd.Cmd):
- ....
-
- will have the following effects:
- 1. 'sub' will interactively enter the cmdloop of a SubMenu instance
- 2. 'sub cmd args' will call do_cmd(args) in a SubMenu instance
- 3. 'sub ... [TAB]' will have the same behavior as [TAB] in a SubMenu cmdloop
- i.e., autocompletion works the way you think it should
- 4. 'help sub [cmd]' will print SubMenu's help (calls its do_help())
- """
-
- class _Nonexistent(object):
- """
- Used to mark missing attributes.
- Disable __dict__ creation since this class does nothing
- """
- __slots__ = () #
-
- def __init__(self,
- submenu,
- command,
- aliases=(),
- reformat_prompt="{super_prompt}>> {sub_prompt}",
- shared_attributes=None,
- require_predefined_shares=True,
- create_subclass=False,
- preserve_shares=False,
- persistent_history_file=None
- ):
- """Set up the class decorator
-
- submenu (Cmd): Instance of something cmd.Cmd-like
-
- command (str): The command the user types to access the SubMenu instance
-
- aliases (iterable): More commands that will behave like "command"
-
- reformat_prompt (str): Format str or None to disable
- if it's a string, it should contain one or more of:
- {super_prompt}: The current cmd's prompt
- {command}: The command in the current cmd with which it was called
- {sub_prompt}: The subordinate cmd's original prompt
- the default is "{super_prompt}{command} {sub_prompt}"
-
- shared_attributes (dict): dict of the form {'subordinate_attr': 'parent_attr'}
- the attributes are copied to the submenu at the last moment; the submenu's
- attributes are backed up before this and restored afterward
-
- require_predefined_shares: The shared attributes above must be independently
- defined in the subordinate Cmd (default: True)
-
- create_subclass: put the modifications in a subclass rather than modifying
- the existing class (default: False)
- """
- self.submenu = submenu
- self.command = command
- self.aliases = aliases
- if persistent_history_file:
- self.persistent_history_file = os.path.expanduser(persistent_history_file)
- else:
- self.persistent_history_file = None
-
- if reformat_prompt is not None and not isinstance(reformat_prompt, str):
- 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 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.create_subclass = create_subclass
- self.preserve_shares = preserve_shares
-
- def _get_original_attributes(self):
- return {
- attr: getattr(self.submenu, attr, AddSubmenu._Nonexistent)
- for attr in self.shared_attributes.keys()
- }
-
- def _copy_in_shared_attrs(self, parent_cmd):
- for sub_attr, par_attr in self.shared_attributes.items():
- setattr(self.submenu, sub_attr, getattr(parent_cmd, par_attr))
-
- def _copy_out_shared_attrs(self, parent_cmd, original_attributes):
- if self.preserve_shares:
- for sub_attr, par_attr in self.shared_attributes.items():
- setattr(parent_cmd, par_attr, getattr(self.submenu, sub_attr))
- else:
- for attr, value in original_attributes.items():
- if attr is not AddSubmenu._Nonexistent:
- setattr(self.submenu, attr, value)
- else:
- delattr(self.submenu, attr)
-
- def __call__(self, cmd_obj):
- """Creates a subclass of Cmd wherein the given submenu can be accessed via the given command"""
- def enter_submenu(parent_cmd, statement):
- """
- This function will be bound to do_<submenu> and will change the scope of the CLI to that of the
- submenu.
- """
- submenu = self.submenu
- original_attributes = self._get_original_attributes()
- history = _pop_readline_history()
-
- if self.persistent_history_file and rl_type != RlType.NONE:
- try:
- readline.read_history_file(self.persistent_history_file)
- except FileNotFoundError:
- pass
-
- try:
- # copy over any shared attributes
- self._copy_in_shared_attrs(parent_cmd)
-
- if statement.args:
- # Remove the menu argument and execute the command in the submenu
- submenu.onecmd_plus_hooks(statement.args)
- else:
- if self.reformat_prompt is not None:
- prompt = submenu.prompt
- submenu.prompt = self.reformat_prompt.format(
- super_prompt=parent_cmd.prompt,
- command=self.command,
- sub_prompt=prompt,
- )
- submenu.cmdloop()
- if self.reformat_prompt is not None:
- # noinspection PyUnboundLocalVariable
- self.submenu.prompt = prompt
- finally:
- # copy back original attributes
- self._copy_out_shared_attrs(parent_cmd, original_attributes)
-
- # write submenu history
- if self.persistent_history_file and rl_type != RlType.NONE:
- readline.write_history_file(self.persistent_history_file)
- # reset main app history before exit
- _push_readline_history(history)
-
- def complete_submenu(_self, text, line, begidx, endidx):
- """
- This function will be bound to complete_<submenu> and will perform the complete commands of the submenu.
- """
- submenu = self.submenu
- original_attributes = self._get_original_attributes()
- try:
- # copy over any shared attributes
- self._copy_in_shared_attrs(_self)
-
- # Reset the submenu's tab completion parameters
- submenu.allow_appended_space = True
- submenu.allow_closing_quote = True
- submenu.display_matches = []
-
- return _complete_from_cmd(submenu, text, line, begidx, endidx)
- finally:
- # copy back original attributes
- self._copy_out_shared_attrs(_self, original_attributes)
-
- # Pass the submenu's tab completion parameters back up to the menu that called complete()
- _self.allow_appended_space = submenu.allow_appended_space
- _self.allow_closing_quote = submenu.allow_closing_quote
- _self.display_matches = copy.copy(submenu.display_matches)
-
- original_do_help = cmd_obj.do_help
- original_complete_help = cmd_obj.complete_help
-
- def help_submenu(_self, line):
- """
- This function will be bound to help_<submenu> and will call the help commands of the submenu.
- """
- tokens = line.split(None, 1)
- if tokens and (tokens[0] == self.command or tokens[0] in self.aliases):
- self.submenu.do_help(tokens[1] if len(tokens) == 2 else '')
- else:
- original_do_help(_self, line)
-
- def _complete_submenu_help(_self, text, line, begidx, endidx):
- """autocomplete to match help_submenu()'s behavior"""
- tokens = line.split(None, 1)
- if len(tokens) == 2 and (
- not (not tokens[1].startswith(self.command) and not any(
- tokens[1].startswith(alias) for alias in self.aliases))
- ):
- return self.submenu.complete_help(
- text,
- tokens[1],
- begidx - line.index(tokens[1]),
- endidx - line.index(tokens[1]),
- )
- else:
- return original_complete_help(_self, text, line, begidx, endidx)
-
- if self.create_subclass:
- class _Cmd(cmd_obj):
- do_help = help_submenu
- complete_help = _complete_submenu_help
- else:
- _Cmd = cmd_obj
- _Cmd.do_help = help_submenu
- _Cmd.complete_help = _complete_submenu_help
-
- # Create bindings in the parent command to the submenus commands.
- setattr(_Cmd, 'do_' + self.command, enter_submenu)
- setattr(_Cmd, 'complete_' + self.command, complete_submenu)
-
- # Create additional bindings for aliases
- for _alias in self.aliases:
- setattr(_Cmd, 'do_' + _alias, enter_submenu)
- setattr(_Cmd, 'complete_' + _alias, complete_submenu)
- return _Cmd
-
-
class Cmd(cmd.Cmd):
"""An easy but powerful framework for writing line-oriented command interpreters.
diff --git a/examples/submenus.py b/examples/submenus.py
deleted file mode 100755
index 27c8cb10..00000000
--- a/examples/submenus.py
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/usr/bin/env python
-# coding=utf-8
-"""
-Create a CLI with a nested command structure as follows. The commands 'second' and 'third' navigate the CLI to the scope
-of the submenu. Nesting of the submenus is done with the cmd2.AddSubmenu() decorator.
-
- (Top Level)----second----->(2nd Level)----third----->(3rd Level)
- | | |
- ---> say ---> say ---> say
-"""
-from __future__ import print_function
-import sys
-
-from cmd2 import cmd2
-from IPython import embed
-
-
-class ThirdLevel(cmd2.Cmd):
- """To be used as a third level command class. """
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.prompt = '3rdLevel '
- self.top_level_attr = None
- self.second_level_attr = None
-
- def do_say(self, line):
- print("You called a command in ThirdLevel with '%s'. "
- "It has access to top_level_attr: %s "
- "and second_level_attr: %s" % (line, self.top_level_attr, self.second_level_attr))
-
- def help_say(self):
- print("This is a third level submenu (submenu_ab). Options are qwe, asd, zxc")
-
- def complete_say(self, text, line, begidx, endidx):
- return [s for s in ['qwe', 'asd', 'zxc'] if s.startswith(text)]
-
-
-@cmd2.AddSubmenu(ThirdLevel(),
- command='third',
- aliases=('third_alias',),
- shared_attributes=dict(second_level_attr='second_level_attr', top_level_attr='top_level_attr'))
-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 '
- self.top_level_attr = None
- self.second_level_attr = 987654321
-
- def do_ipy(self, arg):
- """Enters an interactive IPython shell.
-
- Run python code from external files with ``run filename.py``
- End with ``Ctrl-D`` (Unix) / ``Ctrl-Z`` (Windows), ``quit()``, '`exit()``.
- """
- banner = 'Entering an embedded IPython shell type quit() or <Ctrl>-d to exit ...'
- exit_msg = 'Leaving IPython, back to {}'.format(sys.argv[0])
- embed(banner1=banner, exit_msg=exit_msg)
-
- def do_say(self, line):
- print("You called a command in SecondLevel with '%s'. "
- "It has access to top_level_attr: %s" % (line, self.top_level_attr))
-
- def help_say(self):
- print("This is a SecondLevel menu. Options are qwe, asd, zxc")
-
- def complete_say(self, text, line, begidx, endidx):
- return [s for s in ['qwe', 'asd', 'zxc'] if s.startswith(text)]
-
-
-@cmd2.AddSubmenu(SecondLevel(),
- command='second',
- aliases=('second_alias',),
- shared_attributes=dict(top_level_attr='top_level_attr'))
-class TopLevel(cmd2.Cmd):
- """To be used as the main / top level command class that will contain other submenus."""
-
- def __init__(self, *args, **kwargs):
- cmd2.Cmd.__init__(self, *args, **kwargs)
- self.prompt = 'TopLevel '
- self.top_level_attr = 123456789
-
- def do_ipy(self, arg):
- """Enters an interactive IPython shell.
-
- Run python code from external files with ``run filename.py``
- End with ``Ctrl-D`` (Unix) / ``Ctrl-Z`` (Windows), ``quit()``, '`exit()``.
- """
- banner = 'Entering an embedded IPython shell type quit() or <Ctrl>-d to exit ...'
- exit_msg = 'Leaving IPython, back to {}'.format(sys.argv[0])
- embed(banner1=banner, exit_msg=exit_msg)
-
- def do_say(self, line):
- print("You called a command in TopLevel with '%s'. "
- "TopLevel has attribute top_level_attr=%s" % (line, self.top_level_attr))
-
- def help_say(self):
- print("This is a top level submenu. Options are qwe, asd, zxc")
-
- def complete_say(self, text, line, begidx, endidx):
- return [s for s in ['qwe', 'asd', 'zxc'] if s.startswith(text)]
-
-
-if __name__ == '__main__':
-
- root = TopLevel()
- root.cmdloop()
-
diff --git a/tests/test_completion.py b/tests/test_completion.py
index bda5bb8a..c7650dbb 100644
--- a/tests/test_completion.py
+++ b/tests/test_completion.py
@@ -949,124 +949,3 @@ def test_subcommand_tab_completion_space_in_text_scu(scu_app):
assert first_match is not None and \
scu_app.completion_matches == ['Ball" '] and \
scu_app.display_matches == ['Space Ball']
-
-####################################################
-
-
-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 '
-
- def do_foo(self, line):
- self.poutput("You called a command in SecondLevel with '%s'. " % line)
-
- def help_foo(self):
- self.poutput("This is a second level menu. Options are qwe, asd, zxc")
-
- def complete_foo(self, text, line, begidx, endidx):
- return [s for s in ['qwe', 'asd', 'zxc'] if s.startswith(text)]
-
-
-second_level_cmd = SecondLevel()
-
-
-@cmd2.AddSubmenu(second_level_cmd,
- command='second',
- require_predefined_shares=False)
-class SubmenuApp(cmd2.Cmd):
- """To be used as the main / top level command class that will contain other submenus."""
-
- def __init__(self, *args, **kwargs):
- cmd2.Cmd.__init__(self, *args, **kwargs)
- self.prompt = 'TopLevel '
-
-
-@pytest.fixture
-def sb_app():
- app = SubmenuApp()
- return app
-
-
-def test_cmd2_submenu_completion_single_end(sb_app):
- text = 'f'
- line = 'second {}'.format(text)
- endidx = len(line)
- begidx = endidx - len(text)
-
- first_match = complete_tester(text, line, begidx, endidx, sb_app)
-
- # It is at end of line, so extra space is present
- assert first_match is not None and sb_app.completion_matches == ['foo ']
-
-
-def test_cmd2_submenu_completion_multiple(sb_app):
- text = 'e'
- line = 'second {}'.format(text)
- endidx = len(line)
- begidx = endidx - len(text)
-
- expected = ['edit', 'eof', 'eos']
- first_match = complete_tester(text, line, begidx, endidx, sb_app)
-
- assert first_match is not None and sb_app.completion_matches == expected
-
-
-def test_cmd2_submenu_completion_nomatch(sb_app):
- text = 'z'
- line = 'second {}'.format(text)
- endidx = len(line)
- begidx = endidx - len(text)
-
- first_match = complete_tester(text, line, begidx, endidx, sb_app)
- assert first_match is None
-
-
-def test_cmd2_submenu_completion_after_submenu_match(sb_app):
- text = 'a'
- line = 'second foo {}'.format(text)
- endidx = len(line)
- begidx = endidx - len(text)
-
- first_match = complete_tester(text, line, begidx, endidx, sb_app)
- assert first_match is not None and sb_app.completion_matches == ['asd ']
-
-
-def test_cmd2_submenu_completion_after_submenu_nomatch(sb_app):
- text = 'b'
- line = 'second foo {}'.format(text)
- endidx = len(line)
- begidx = endidx - len(text)
-
- first_match = complete_tester(text, line, begidx, endidx, sb_app)
- assert first_match is None
-
-
-def test_cmd2_help_submenu_completion_multiple(sb_app):
- text = 'p'
- line = 'help second {}'.format(text)
- endidx = len(line)
- begidx = endidx - len(text)
-
- matches = sorted(sb_app.complete_help(text, line, begidx, endidx))
- assert matches == ['py', 'pyscript']
-
-
-def test_cmd2_help_submenu_completion_nomatch(sb_app):
- text = 'fake'
- line = 'help second {}'.format(text)
- endidx = len(line)
- begidx = endidx - len(text)
- assert sb_app.complete_help(text, line, begidx, endidx) == []
-
-
-def test_cmd2_help_submenu_completion_subcommands(sb_app):
- text = 'p'
- line = 'help second {}'.format(text)
- endidx = len(line)
- begidx = endidx - len(text)
-
- matches = sorted(sb_app.complete_help(text, line, begidx, endidx))
- assert matches == ['py', 'pyscript']
diff --git a/tests/test_submenu.py b/tests/test_submenu.py
deleted file mode 100644
index db334daa..00000000
--- a/tests/test_submenu.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# coding=utf-8
-"""
-Cmd2 testing for argument parsing
-"""
-import pytest
-
-from cmd2 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 '
-
- def do_get_top_level_attr(self, line):
- self.poutput(str(self.top_level_attr))
-
- def do_set_top_level_attr(self, line):
- self.top_level_attr = 987654321
-
-
-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 '
- self.top_level_attr = None
-
- def do_say(self, line):
- self.poutput("You called a command in SecondLevel with '%s'. " % line)
-
- def help_say(self):
- self.poutput("This is a second level menu. Options are qwe, asd, zxc")
-
- def complete_say(self, text, line, begidx, endidx):
- return [s for s in ['qwe', 'asd', 'zxc'] if s.startswith(text)]
-
- def do_get_top_level_attr(self, line):
- self.poutput(str(self.top_level_attr))
-
- def do_get_prompt(self, line):
- self.poutput(self.prompt)
-
-
-second_level_cmd = SecondLevel()
-second_level_b_cmd = SecondLevelB()
-
-
-@cmd2.AddSubmenu(SecondLevelB(),
- command='should_work_with_default_kwargs')
-@cmd2.AddSubmenu(second_level_b_cmd,
- command='secondb',
- shared_attributes=dict(top_level_attr='top_level_attr'),
- require_predefined_shares=False,
- preserve_shares=True
- )
-@cmd2.AddSubmenu(second_level_cmd,
- command='second',
- aliases=('second_alias',),
- shared_attributes=dict(top_level_attr='top_level_attr'))
-class SubmenuApp(cmd2.Cmd):
- """To be used as the main / top level command class that will contain other submenus."""
-
- def __init__(self, *args, **kwargs):
- cmd2.Cmd.__init__(self, *args, **kwargs)
- self.prompt = 'TopLevel '
- self.top_level_attr = 123456789
-
- def do_say(self, line):
- self.poutput("You called a command in TopLevel with '%s'. " % line)
-
- def help_say(self):
- self.poutput("This is a top level submenu. Options are qwe, asd, zxc")
-
- def complete_say(self, text, line, begidx, endidx):
- return [s for s in ['qwe', 'asd', 'zxc'] if s.startswith(text)]
-
-
-@pytest.fixture
-def submenu_app():
- app = SubmenuApp()
- app.stdout = StdOut()
- second_level_cmd.stdout = StdOut()
- second_level_b_cmd.stdout = StdOut()
- return app
-
-
-@pytest.fixture
-def secondlevel_app():
- app = SecondLevel()
- app.stdout = StdOut()
- return app
-
-
-@pytest.fixture
-def secondlevel_app_b():
- app = SecondLevelB()
- app.stdout = StdOut()
- return app
-
-
-def run_submenu_cmd(app, second_level_app, cmd):
- """ Clear StdOut buffers, run the command, extract the buffer contents."""
- app.stdout.clear()
- second_level_app.stdout.clear()
- app.onecmd_plus_hooks(cmd)
- out1 = app.stdout.buffer
- out2 = second_level_app.stdout.buffer
- app.stdout.clear()
- second_level_app.stdout.clear()
- return normalize(out1), normalize(out2)
-
-
-def test_submenu_say_from_top_level(submenu_app):
- line = 'testing'
- out1, out2 = run_submenu_cmd(submenu_app, second_level_cmd, 'say ' + line)
- assert len(out1) == 1
- 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_level_cmd, 'second say ' + line)
-
- # No output expected from the top level
- assert out1 == []
-
- # Output expected from the second level
- 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)
- assert out == ["You called a command in SecondLevel with '%s'." % line]
-
-
-def test_submenu_help_second_say_from_top_level(submenu_app):
- out1, out2 = run_submenu_cmd(submenu_app, second_level_cmd, 'help second say')
- # No output expected from the top level
- assert out1 == []
-
- # Output expected from the second level
- assert out2 == ["This is a second level menu. Options are qwe, asd, zxc"]
-
-
-def test_submenu_help_say_from_second_level(secondlevel_app):
- out = run_cmd(secondlevel_app, 'help say')
- assert out == ["This is a second level menu. Options are qwe, asd, zxc"]
-
-
-def test_submenu_help_second(submenu_app):
- out1, out2 = run_submenu_cmd(submenu_app, second_level_cmd, 'help second')
- out3 = run_cmd(second_level_cmd, 'help')
- assert out2 == out3
-
-
-def test_submenu_from_top_help_second_say(submenu_app):
- out1, out2 = run_submenu_cmd(submenu_app, second_level_cmd, 'help second say')
- out3 = run_cmd(second_level_cmd, 'help say')
- assert out2 == out3
-
-
-def test_submenu_shared_attribute(submenu_app):
- out1, out2 = run_submenu_cmd(submenu_app, second_level_cmd, 'second get_top_level_attr')
- assert out2 == [str(submenu_app.top_level_attr)]
-
-
-def test_submenu_shared_attribute_preserve(submenu_app):
- out1, out2 = run_submenu_cmd(submenu_app, second_level_b_cmd, 'secondb get_top_level_attr')
- assert out2 == [str(submenu_app.top_level_attr)]
- out1, out2 = run_submenu_cmd(submenu_app, second_level_b_cmd, 'secondb set_top_level_attr')
- assert submenu_app.top_level_attr == 987654321
- out1, out2 = run_submenu_cmd(submenu_app, second_level_b_cmd, 'secondb get_top_level_attr')
- assert out2 == [str(987654321)]