diff options
-rw-r--r-- | cmd2/cmd2.py | 11 | ||||
-rw-r--r-- | cmd2/utils.py | 11 | ||||
-rwxr-xr-x | tests/test_cmd2.py | 6 | ||||
-rw-r--r-- | tests/test_utils.py | 2 |
4 files changed, 25 insertions, 5 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 37779f56..7373188b 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -388,6 +388,17 @@ class Cmd(cmd.Cmd): """ self.settables[settable.name] = settable + def remove_settable(self, name: str) -> None: + """ + Convenience method for removing a settable parameter from self.settables + :param name: name of the settable being removed + :raises: KeyError if the no Settable matches this name + """ + try: + del self.settables[name] + except KeyError: + raise KeyError(name + " is not a settable parameter") + def build_settables(self): """Populates self.add_settable with parameters that can be edited via the set command""" self.add_settable(Settable('allow_style', str, diff --git a/cmd2/utils.py b/cmd2/utils.py index 728c1ac3..9635be42 100644 --- a/cmd2/utils.py +++ b/cmd2/utils.py @@ -69,18 +69,18 @@ def str_to_bool(val: str) -> bool: return True elif val.capitalize() == str(False): return False - raise ValueError("must be True or False") + raise ValueError("must be True or False (case-insensitive)") class Settable: """Used to configure a cmd2 instance member to be settable via the set command in the CLI""" def __init__(self, name: str, val_type: Callable, description: str, *, + onchange_cb: Callable[[str, Any, Any], Any] = None, choices: Iterable = None, choices_function: Optional[Callable] = None, choices_method: Optional[Callable] = None, completer_function: Optional[Callable] = None, - completer_method: Optional[Callable] = None, - onchange_cb: Callable[[str, Any, Any], Any] = None): + completer_method: Optional[Callable] = None): """ Settable Initializer @@ -90,6 +90,9 @@ class Settable: validation using str_to_bool(). The val_type function should raise an exception if it fails. This exception will be caught and printed by Cmd.do_set(). :param description: string describing this setting + :param onchange_cb: optional function to call when the value of this settable is altered by the set command + Callback signature: + val_changed_cb(param_name: str, old_value: Any, new_value: Any) -> Any The following optional settings provide tab completion for a parameter's values. They correspond to the same settings in argparse-based tab completion. A maximum of one of these should be provided. @@ -107,12 +110,12 @@ class Settable: self.name = name self.val_type = val_type self.description = description + self.onchange_cb = onchange_cb self.choices = choices self.choices_function = choices_function self.choices_method = choices_method self.completer_function = completer_function self.completer_method = completer_method - self.onchange_cb = onchange_cb def namedtuple_with_defaults(typename: str, field_names: Union[str, List[str]], diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index 0615ed46..fecab628 100755 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -630,7 +630,7 @@ now: True def test_debug_not_settable(base_app): # Set debug to False and make it unsettable base_app.debug = False - del base_app.settables['debug'] + base_app.remove_settable('debug') # Cause an exception out, err = run_cmd(base_app, 'bad "quote') @@ -638,6 +638,10 @@ def test_debug_not_settable(base_app): # Since debug is unsettable, the user will not be given the option to enable a full traceback assert err == ['Invalid syntax: No closing quotation'] +def test_remove_settable_keyerror(base_app): + with pytest.raises(KeyError): + base_app.remove_settable('fake') + def test_edit_file(base_app, request, monkeypatch): # Set a fake editor just to make sure we have one. We aren't really going to call it due to the mock base_app.editor = 'fooedit' diff --git a/tests/test_utils.py b/tests/test_utils.py index 804e58be..5030ce0e 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -537,11 +537,13 @@ def test_str_to_bool_true(): assert cu.str_to_bool('true') assert cu.str_to_bool('True') assert cu.str_to_bool('TRUE') + assert cu.str_to_bool('tRuE') def test_str_to_bool_false(): assert not cu.str_to_bool('false') assert not cu.str_to_bool('False') assert not cu.str_to_bool('FALSE') + assert not cu.str_to_bool('fAlSe') def test_str_to_bool_invalid(): with pytest.raises(ValueError): |