From a5d3f7959c252ee23cf6360b81292d376b8c6fcc Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Tue, 4 Feb 2020 17:44:35 -0500 Subject: Updated set command to support tab completion of values --- cmd2/utils.py | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 3 deletions(-) (limited to 'cmd2/utils.py') diff --git a/cmd2/utils.py b/cmd2/utils.py index 42248884..a23826c3 100644 --- a/cmd2/utils.py +++ b/cmd2/utils.py @@ -2,6 +2,7 @@ """Shared utility functions""" import collections +import collections.abc as collections_abc import glob import os import re @@ -9,9 +10,8 @@ import subprocess import sys import threading import unicodedata -import collections.abc as collections_abc from enum import Enum -from typing import Any, Iterable, List, Optional, TextIO, Union +from typing import Any, Callable, Iterable, List, Optional, TextIO, Union from . import constants @@ -57,6 +57,61 @@ def strip_quotes(arg: str) -> str: return arg +def str_to_bool(val: str) -> bool: + """ + Converts a string to a boolean based on its value + :param val: string being converted + :return: boolean value expressed in the string + :raises: ValueError if the string does not contain a value corresponding to a boolean value + """ + if val.lower() == "true": + return True + elif val.lower() == "false": + return False + raise ValueError("must be true or false") + + +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, *, + 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): + """ + Settable Initializer + + :param name: name of the instance attribute being made settable + :param val_type: type or callable used to cast the string value from the command line + setting this to bool provides tab completion for true/false and validation using str_to_bool + :param description: string describing this setting + + 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. + + :param choices: iterable of accepted values + :param choices_function: function that provides choices for this argument + :param choices_method: cmd2-app method that provides choices for this argument + :param completer_function: tab-completion function that provides choices for this argument + :param completer_method: cmd2-app tab-completion method that provides choices for this argument + """ + if val_type == bool: + val_type = str_to_bool + choices = ['true', 'false'] + + self.name = name + self.val_type = val_type + self.description = description + 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]], default_values: collections_abc.Iterable = ()): """ @@ -372,7 +427,7 @@ class StdSim(object): def __init__(self, inner_stream, echo: bool = False, encoding: str = 'utf-8', errors: str = 'replace') -> None: """ - Initializer + StdSim Initializer :param inner_stream: the wrapped stream. Should be a TextIO or StdSim instance. :param echo: if True, then all input will be echoed to inner_stream :param encoding: codec for encoding/decoding strings (defaults to utf-8) -- cgit v1.2.1 From 457123d3a1376a2ab713f0ff638313b0eacfcf3e Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Tue, 4 Feb 2020 21:57:05 -0500 Subject: Updated CHANGELOG and made a few minor tweaks --- cmd2/utils.py | 42 +++++------------------------------------- 1 file changed, 5 insertions(+), 37 deletions(-) (limited to 'cmd2/utils.py') diff --git a/cmd2/utils.py b/cmd2/utils.py index a23826c3..2f2efefa 100644 --- a/cmd2/utils.py +++ b/cmd2/utils.py @@ -58,17 +58,17 @@ def strip_quotes(arg: str) -> str: def str_to_bool(val: str) -> bool: - """ - Converts a string to a boolean based on its value + """Converts a string to a boolean based on its value. + :param val: string being converted :return: boolean value expressed in the string :raises: ValueError if the string does not contain a value corresponding to a boolean value """ - if val.lower() == "true": + if val.capitalize() == str(True): return True - elif val.lower() == "false": + elif val.capitalize() == str(False): return False - raise ValueError("must be true or false") + raise ValueError("must be True or False") class Settable: @@ -143,38 +143,6 @@ def namedtuple_with_defaults(typename: str, field_names: Union[str, List[str]], return T -def cast(current: Any, new: str) -> Any: - """Tries to force a new value into the same type as the current when trying to set the value for a parameter. - - :param current: current value for the parameter, type varies - :param new: new value - :return: new value with same type as current, or the current value if there was an error casting - """ - typ = type(current) - orig_new = new - - if typ == bool: - try: - return bool(int(new)) - except (ValueError, TypeError): - pass - try: - new = new.lower() - if (new == 'on') or (new[0] in ('y', 't')): - return True - if (new == 'off') or (new[0] in ('n', 'f')): - return False - except AttributeError: - pass - else: - try: - return typ(new) - except (ValueError, TypeError): - pass - print("Problem setting parameter (now {}) to {}; incorrect type?".format(current, orig_new)) - return current - - def which(exe_name: str) -> Optional[str]: """Find the full path of a given executable on a Linux or Mac machine -- cgit v1.2.1 From 14f456b0c3b70021fa650a596f81e0cfa7bd8949 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Tue, 4 Feb 2020 22:18:56 -0500 Subject: Fixed a bug in a very unusual case and added some unit tests --- cmd2/utils.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'cmd2/utils.py') diff --git a/cmd2/utils.py b/cmd2/utils.py index 2f2efefa..c200085a 100644 --- a/cmd2/utils.py +++ b/cmd2/utils.py @@ -64,10 +64,11 @@ def str_to_bool(val: str) -> bool: :return: boolean value expressed in the string :raises: ValueError if the string does not contain a value corresponding to a boolean value """ - if val.capitalize() == str(True): - return True - elif val.capitalize() == str(False): - return False + if isinstance(val, str): + if val.capitalize() == str(True): + return True + elif val.capitalize() == str(False): + return False raise ValueError("must be True or False") -- cgit v1.2.1 From d812fbc4d6dbd31c643caf4f8561f47ee8cb58aa Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Tue, 4 Feb 2020 23:36:49 -0500 Subject: Removed unnecessary inheritance from object --- cmd2/utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'cmd2/utils.py') diff --git a/cmd2/utils.py b/cmd2/utils.py index c200085a..ee53e924 100644 --- a/cmd2/utils.py +++ b/cmd2/utils.py @@ -388,7 +388,7 @@ def get_exes_in_path(starts_with: str) -> List[str]: return list(exes_set) -class StdSim(object): +class StdSim: """ Class to simulate behavior of sys.stdout or sys.stderr. Stores contents in internal buffer and optionally echos to the inner stream it is simulating. @@ -468,7 +468,7 @@ class StdSim(object): return getattr(self.inner_stream, item) -class ByteBuf(object): +class ByteBuf: """ Used by StdSim to write binary data and stores the actual bytes written """ @@ -497,7 +497,7 @@ class ByteBuf(object): self.std_sim_instance.flush() -class ProcReader(object): +class ProcReader: """ Used to capture stdout and stderr from a Popen process if any of those were set to subprocess.PIPE. If neither are pipes, then the process will run normally and no output will be captured. @@ -599,7 +599,7 @@ class ProcReader(object): pass -class ContextFlag(object): +class ContextFlag: """A context manager which is also used as a boolean flag value within the default sigint handler. Its main use is as a flag to prevent the SIGINT handler in cmd2 from raising a KeyboardInterrupt @@ -623,7 +623,7 @@ class ContextFlag(object): raise ValueError("count has gone below 0") -class RedirectionSavedState(object): +class RedirectionSavedState: """Created by each command to store information about their redirection.""" def __init__(self, self_stdout: Union[StdSim, TextIO], sys_stdout: Union[StdSim, TextIO], -- cgit v1.2.1 From 34f00eda97d44922896beac608a9d4a085ae6e0d Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Wed, 5 Feb 2020 09:41:47 -0500 Subject: Updated documentation --- cmd2/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'cmd2/utils.py') diff --git a/cmd2/utils.py b/cmd2/utils.py index ee53e924..728c1ac3 100644 --- a/cmd2/utils.py +++ b/cmd2/utils.py @@ -85,8 +85,10 @@ class Settable: Settable Initializer :param name: name of the instance attribute being made settable - :param val_type: type or callable used to cast the string value from the command line - setting this to bool provides tab completion for true/false and validation using str_to_bool + :param val_type: callable used to cast the string value from the command line into its proper type and + even validate its value. Setting this to bool provides tab completion for true/false and + 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 The following optional settings provide tab completion for a parameter's values. They correspond to the -- cgit v1.2.1 From 7519f742923a31599c56dfc5db9aad6901bfce73 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Wed, 5 Feb 2020 11:38:06 -0500 Subject: Added remove_settable() since cmd2 has add_settable() Documented Settable.onchange_cb --- cmd2/utils.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'cmd2/utils.py') 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]], -- cgit v1.2.1 From 9b95f50975ecaa8dbfd37c375ebf38e1971fc960 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Wed, 5 Feb 2020 21:00:51 -0500 Subject: Added more to onchange_cb documentation --- cmd2/utils.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'cmd2/utils.py') diff --git a/cmd2/utils.py b/cmd2/utils.py index 9635be42..2bb91ccd 100644 --- a/cmd2/utils.py +++ b/cmd2/utils.py @@ -90,9 +90,13 @@ 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 + :param onchange_cb: optional function or method to call when the value of this settable is altered + by the set command. (e.g. onchange_cb=self.debug_changed) + + Cmd.do_set() passes the following 3 arguments to onchange_cb: + param_name: str - name of the changed parameter + old_value: Any - the value before being changed + new_value: Any - the value after being changed 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. -- cgit v1.2.1 From de12ee4782799350233be7adadeac08907c13e84 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Wed, 5 Feb 2020 21:18:47 -0500 Subject: Added more to Settable docstring --- cmd2/utils.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'cmd2/utils.py') diff --git a/cmd2/utils.py b/cmd2/utils.py index 2bb91ccd..cfe75f53 100644 --- a/cmd2/utils.py +++ b/cmd2/utils.py @@ -103,9 +103,16 @@ class Settable: :param choices: iterable of accepted values :param choices_function: function that provides choices for this argument - :param choices_method: cmd2-app method that provides choices for this argument + :param choices_method: cmd2-app method that provides choices for this argument (See note below) :param completer_function: tab-completion function that provides choices for this argument - :param completer_method: cmd2-app tab-completion method that provides choices for this argument + :param completer_method: cmd2-app tab-completion method that provides choices + for this argument (See note below) + + Note: + For choices_method and completer_method, do not set them to a bound method. This is because AutoCompleter + passes the self argument explicitly to these functions. + + Therefore instead of passing something like self.path_complete, pass cmd2.Cmd.path_complete. """ if val_type == bool: val_type = str_to_bool -- cgit v1.2.1