From 2f37b1d54fcda2b8f3fb42b27515004a00b24a72 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Fri, 23 Feb 2018 23:34:38 -0500 Subject: [PATCH 1/2] Converted a few class variables into instance variables Now that users can nest instances of cmd.Cmd2 to support creating sub-menus, we should need to be more careful about class vs instance variables to prevent potential problems. This converts the following former class variables into instance variables: - multiline_commands - shortcuts - exclude_from_help - exclude_from_history In the process, a couple camelCase variable names got converted to pep8_compliant names. There may be a few other class variables which should be converted to instance variables. But at the very least, this is a good start. This closes #273. --- cmd2.py | 18 +++++++++++------- tests/test_parsing.py | 36 ++++++++++++++++++------------------ 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/cmd2.py b/cmd2.py index 1c2daf7b5..4fa2a0df9 100755 --- a/cmd2.py +++ b/cmd2.py @@ -788,18 +788,14 @@ class Cmd(cmd.Cmd): commentGrammars = pyparsing.Or([pyparsing.pythonStyleComment, pyparsing.cStyleComment]) commentInProgress = pyparsing.Literal('/*') + pyparsing.SkipTo(pyparsing.stringEnd ^ '*/') legalChars = u'!#$%.:?@_-' + pyparsing.alphanums + pyparsing.alphas8bit - multilineCommands = [] # NOTE: Multiline commands can never be abbreviated, even if abbrev is True prefixParser = pyparsing.Empty() redirector = '>' # for sending output to file - shortcuts = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'} terminators = [';'] # make sure your terminators are not in legalChars! # Attributes which are NOT dynamically settable at runtime 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 - excludeFromHistory = '''run ru r history histor histo hist his hi h edit edi ed e eof eo eos'''.split() - exclude_from_help = ['do_eof', 'do_eos', 'do__relative_load'] # Commands to exclude from the help menu reserved_words = [] # Attributes which ARE dynamically settable at runtime @@ -869,13 +865,21 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, persistent_histor # Call super class constructor. Need to do it in this way for Python 2 and 3 compatibility cmd.Cmd.__init__(self, completekey=completekey, stdin=stdin, stdout=stdout) + self.multiline_commands = [] # NOTE: Multiline commands can never be abbreviated, even if abbrev is True + self.shortcuts = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'} + + # Commands to exclude from the help menu or history command + self.exclude_from_help = ['do_eof', 'do_eos', 'do__relative_load'] + self.exclude_from_history = '''history histor histo hist his hi h edit edi ed e eof eo eos'''.split() + self._finalize_app_parameters() + self.initial_stdout = sys.stdout self.history = History() self.pystate = {} self.keywords = self.reserved_words + [fname[3:] for fname in dir(self) if fname.startswith('do_')] self.parser_manager = ParserManager(redirector=self.redirector, terminators=self.terminators, - multilineCommands=self.multilineCommands, + multilineCommands=self.multiline_commands, legalChars=self.legalChars, commentGrammars=self.commentGrammars, commentInProgress=self.commentInProgress, case_insensitive=self.case_insensitive, @@ -1390,7 +1394,7 @@ def _func_named(self, arg): result = target else: if self.abbrev: # accept shortened versions of commands - funcs = [func for func in self.keywords if func.startswith(arg) and func not in self.multilineCommands] + funcs = [func for func in self.keywords if func.startswith(arg) and func not in self.multiline_commands] if len(funcs) == 1: result = 'do_' + funcs[0] return result @@ -1409,7 +1413,7 @@ def onecmd(self, line): return self.default(statement) # Since we have a valid command store it in the history - if statement.parsed.command not in self.excludeFromHistory: + if statement.parsed.command not in self.exclude_from_history: self.history.append(statement.parsed.raw) try: diff --git a/tests/test_parsing.py b/tests/test_parsing.py index da8f66925..561b48ec6 100644 --- a/tests/test_parsing.py +++ b/tests/test_parsing.py @@ -24,39 +24,39 @@ def hist(): @pytest.fixture def parser(): c = cmd2.Cmd() - c.multilineCommands = ['multiline'] + c.multiline_commands = ['multiline'] c.case_insensitive = True - c.parser_manager = cmd2.ParserManager(redirector=c.redirector, terminators=c.terminators, multilineCommands=c.multilineCommands, - legalChars=c.legalChars, commentGrammars=c.commentGrammars, - commentInProgress=c.commentInProgress, case_insensitive=c.case_insensitive, - blankLinesAllowed=c.blankLinesAllowed, prefixParser=c.prefixParser, - preparse=c.preparse, postparse=c.postparse, shortcuts=c.shortcuts) + c.parser_manager = cmd2.ParserManager(redirector=c.redirector, terminators=c.terminators, multilineCommands=c.multiline_commands, + legalChars=c.legalChars, commentGrammars=c.commentGrammars, + commentInProgress=c.commentInProgress, case_insensitive=c.case_insensitive, + blankLinesAllowed=c.blankLinesAllowed, prefixParser=c.prefixParser, + preparse=c.preparse, postparse=c.postparse, shortcuts=c.shortcuts) return c.parser_manager.main_parser # Case-insensitive ParserManager @pytest.fixture def ci_pm(): c = cmd2.Cmd() - c.multilineCommands = ['multiline'] + c.multiline_commands = ['multiline'] c.case_insensitive = True - c.parser_manager = cmd2.ParserManager(redirector=c.redirector, terminators=c.terminators, multilineCommands=c.multilineCommands, - legalChars=c.legalChars, commentGrammars=c.commentGrammars, - commentInProgress=c.commentInProgress, case_insensitive=c.case_insensitive, - blankLinesAllowed=c.blankLinesAllowed, prefixParser=c.prefixParser, - preparse=c.preparse, postparse=c.postparse, shortcuts=c.shortcuts) + c.parser_manager = cmd2.ParserManager(redirector=c.redirector, terminators=c.terminators, multilineCommands=c.multiline_commands, + legalChars=c.legalChars, commentGrammars=c.commentGrammars, + commentInProgress=c.commentInProgress, case_insensitive=c.case_insensitive, + blankLinesAllowed=c.blankLinesAllowed, prefixParser=c.prefixParser, + preparse=c.preparse, postparse=c.postparse, shortcuts=c.shortcuts) return c.parser_manager # Case-sensitive ParserManager @pytest.fixture def cs_pm(): c = cmd2.Cmd() - c.multilineCommands = ['multiline'] + c.multiline_commands = ['multiline'] c.case_insensitive = False - c.parser_manager = cmd2.ParserManager(redirector=c.redirector, terminators=c.terminators, multilineCommands=c.multilineCommands, - legalChars=c.legalChars, commentGrammars=c.commentGrammars, - commentInProgress=c.commentInProgress, case_insensitive=c.case_insensitive, - blankLinesAllowed=c.blankLinesAllowed, prefixParser=c.prefixParser, - preparse=c.preparse, postparse=c.postparse, shortcuts=c.shortcuts) + c.parser_manager = cmd2.ParserManager(redirector=c.redirector, terminators=c.terminators, multilineCommands=c.multiline_commands, + legalChars=c.legalChars, commentGrammars=c.commentGrammars, + commentInProgress=c.commentInProgress, case_insensitive=c.case_insensitive, + blankLinesAllowed=c.blankLinesAllowed, prefixParser=c.prefixParser, + preparse=c.preparse, postparse=c.postparse, shortcuts=c.shortcuts) return c.parser_manager From 91255cd002bf65c99ea7931c3f4cda3e775b09e6 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Tue, 27 Feb 2018 09:26:38 -0500 Subject: [PATCH 2/2] Revert to old attribute names for multilineCommands and excludeFromHistory to prevent breaking change Also: - Reverted multilineCommands and shortcuts to class variables to prevent other breaking changes --- cmd2.py | 13 ++++++------- tests/test_parsing.py | 12 ++++++------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/cmd2.py b/cmd2.py index 920ca21ef..776c85d88 100755 --- a/cmd2.py +++ b/cmd2.py @@ -793,8 +793,10 @@ class Cmd(cmd.Cmd): commentGrammars = pyparsing.Or([pyparsing.pythonStyleComment, pyparsing.cStyleComment]) commentInProgress = pyparsing.Literal('/*') + pyparsing.SkipTo(pyparsing.stringEnd ^ '*/') legalChars = u'!#$%.:?@_-' + pyparsing.alphanums + pyparsing.alphas8bit + multilineCommands = [] # NOTE: Multiline commands can never be abbreviated, even if abbrev is True prefixParser = pyparsing.Empty() redirector = '>' # for sending output to file + shortcuts = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'} terminators = [';'] # make sure your terminators are not in legalChars! # Attributes which are NOT dynamically settable at runtime @@ -870,12 +872,9 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, persistent_histor # Call super class constructor. Need to do it in this way for Python 2 and 3 compatibility cmd.Cmd.__init__(self, completekey=completekey, stdin=stdin, stdout=stdout) - self.multiline_commands = [] # NOTE: Multiline commands can never be abbreviated, even if abbrev is True - self.shortcuts = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'} - # Commands to exclude from the help menu or history command self.exclude_from_help = ['do_eof', 'do_eos', 'do__relative_load'] - self.exclude_from_history = '''history histor histo hist his hi h edit edi ed e eof eo eos'''.split() + self.excludeFromHistory = '''history histor histo hist his hi h edit edi ed e eof eo eos'''.split() self._finalize_app_parameters() @@ -884,7 +883,7 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, persistent_histor self.pystate = {} self.keywords = self.reserved_words + [fname[3:] for fname in dir(self) if fname.startswith('do_')] self.parser_manager = ParserManager(redirector=self.redirector, terminators=self.terminators, - multilineCommands=self.multiline_commands, + multilineCommands=self.multilineCommands, legalChars=self.legalChars, commentGrammars=self.commentGrammars, commentInProgress=self.commentInProgress, case_insensitive=self.case_insensitive, @@ -1399,7 +1398,7 @@ def _func_named(self, arg): result = target else: if self.abbrev: # accept shortened versions of commands - funcs = [func for func in self.keywords if func.startswith(arg) and func not in self.multiline_commands] + funcs = [func for func in self.keywords if func.startswith(arg) and func not in self.multilineCommands] if len(funcs) == 1: result = 'do_' + funcs[0] return result @@ -1418,7 +1417,7 @@ def onecmd(self, line): return self.default(statement) # Since we have a valid command store it in the history - if statement.parsed.command not in self.exclude_from_history: + if statement.parsed.command not in self.excludeFromHistory: self.history.append(statement.parsed.raw) try: diff --git a/tests/test_parsing.py b/tests/test_parsing.py index 561b48ec6..5a741b578 100644 --- a/tests/test_parsing.py +++ b/tests/test_parsing.py @@ -24,9 +24,9 @@ def hist(): @pytest.fixture def parser(): c = cmd2.Cmd() - c.multiline_commands = ['multiline'] + c.multilineCommands = ['multiline'] c.case_insensitive = True - c.parser_manager = cmd2.ParserManager(redirector=c.redirector, terminators=c.terminators, multilineCommands=c.multiline_commands, + c.parser_manager = cmd2.ParserManager(redirector=c.redirector, terminators=c.terminators, multilineCommands=c.multilineCommands, legalChars=c.legalChars, commentGrammars=c.commentGrammars, commentInProgress=c.commentInProgress, case_insensitive=c.case_insensitive, blankLinesAllowed=c.blankLinesAllowed, prefixParser=c.prefixParser, @@ -37,9 +37,9 @@ def parser(): @pytest.fixture def ci_pm(): c = cmd2.Cmd() - c.multiline_commands = ['multiline'] + c.multilineCommands = ['multiline'] c.case_insensitive = True - c.parser_manager = cmd2.ParserManager(redirector=c.redirector, terminators=c.terminators, multilineCommands=c.multiline_commands, + c.parser_manager = cmd2.ParserManager(redirector=c.redirector, terminators=c.terminators, multilineCommands=c.multilineCommands, legalChars=c.legalChars, commentGrammars=c.commentGrammars, commentInProgress=c.commentInProgress, case_insensitive=c.case_insensitive, blankLinesAllowed=c.blankLinesAllowed, prefixParser=c.prefixParser, @@ -50,9 +50,9 @@ def ci_pm(): @pytest.fixture def cs_pm(): c = cmd2.Cmd() - c.multiline_commands = ['multiline'] + c.multilineCommands = ['multiline'] c.case_insensitive = False - c.parser_manager = cmd2.ParserManager(redirector=c.redirector, terminators=c.terminators, multilineCommands=c.multiline_commands, + c.parser_manager = cmd2.ParserManager(redirector=c.redirector, terminators=c.terminators, multilineCommands=c.multilineCommands, legalChars=c.legalChars, commentGrammars=c.commentGrammars, commentInProgress=c.commentInProgress, case_insensitive=c.case_insensitive, blankLinesAllowed=c.blankLinesAllowed, prefixParser=c.prefixParser,