diff options
author | Stephen Finucane <sfinucan@redhat.com> | 2020-12-10 10:56:16 +0000 |
---|---|---|
committer | Stephen Finucane <sfinucan@redhat.com> | 2021-01-29 15:40:42 +0000 |
commit | 7798cb2e3786f803518afc9334e99b6de0a3cb15 (patch) | |
tree | bec4a550670deefef4ada94fccd70c7181de3e37 | |
parent | c1c991045cece85dd55494f9d4670429e370e131 (diff) | |
download | cliff-7798cb2e3786f803518afc9334e99b6de0a3cb15.tar.gz |
Add '--sort-ascending', '--sort-descending' parameters
Allow users to reverse sorting direction.
Change-Id: Iecd539139c5a7ce4abaaee2ff5632a2459437d51
Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
-rw-r--r-- | cliff/lister.py | 67 | ||||
-rw-r--r-- | cliff/tests/test_lister.py | 15 | ||||
-rw-r--r-- | releasenotes/notes/add-Lister-sort-direction-5f34dba3c9743572.yaml | 21 |
3 files changed, 82 insertions, 21 deletions
diff --git a/cliff/lister.py b/cliff/lister.py index dde0f2d..eed4875 100644 --- a/cliff/lister.py +++ b/cliff/lister.py @@ -10,8 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. -"""Application base class for providing a list of data as output. -""" +"""Application base class for providing a list of data as output.""" + import abc import logging @@ -19,8 +19,7 @@ from . import display class Lister(display.DisplayCommandBase, metaclass=abc.ABCMeta): - """Command base class for providing a list of data as output. - """ + """Command base class for providing a list of data as output.""" log = logging.getLogger(__name__) @@ -37,13 +36,16 @@ class Lister(display.DisplayCommandBase, metaclass=abc.ABCMeta): """Whether sort procedure is performed by cliff itself. Should be overridden (return False) when there is a need to implement - custom sorting procedure or data is already sorted.""" + custom sorting procedure or data is already sorted. + """ return True @abc.abstractmethod def take_action(self, parsed_args): - """Return a tuple containing the column names and an iterable - containing the data to be listed. + """Run command. + + Return a tuple containing the column names and an iterable containing + the data to be listed. """ def get_parser(self, prog_name): @@ -55,16 +57,36 @@ class Lister(display.DisplayCommandBase, metaclass=abc.ABCMeta): default=[], dest='sort_columns', metavar='SORT_COLUMN', - help=("specify the column(s) to sort the data (columns specified " - "first have a priority, non-existing columns are ignored), " - "can be repeated") + help=( + 'specify the column(s) to sort the data (columns specified ' + 'first have a priority, non-existing columns are ignored), ' + 'can be repeated' + ), + ) + sort_dir_group = group.add_mutually_exclusive_group() + sort_dir_group.add_argument( + '--sort-ascending', + action='store_const', + dest='sort_direction', + const='asc', + help=('sort the column(s) in ascending order'), + ) + sort_dir_group.add_argument( + '--sort-descending', + action='store_const', + dest='sort_direction', + const='desc', + help=('sort the column(s) in descending order'), ) return parser def produce_output(self, parsed_args, column_names, data): if parsed_args.sort_columns and self.need_sort_by_cliff: - indexes = [column_names.index(c) for c in parsed_args.sort_columns - if c in column_names] + indexes = [ + column_names.index(c) for c in parsed_args.sort_columns + if c in column_names + ] + reverse = parsed_args.sort_direction == 'desc' for index in indexes[::-1]: try: # We need to handle unset values (i.e. None) so we sort on @@ -76,6 +98,7 @@ class Lister(display.DisplayCommandBase, metaclass=abc.ABCMeta): # the same, i.e. both None or both not-None data = sorted( data, key=lambda k: (k[index] is None, k[index]), + reverse=reverse, ) except TypeError: # Simply log and then ignore this; sorting is best effort @@ -84,18 +107,20 @@ class Lister(display.DisplayCommandBase, metaclass=abc.ABCMeta): parsed_args.sort_columns[index], ) - (columns_to_include, selector) = self._generate_columns_and_selector( - parsed_args, column_names) + columns_to_include, selector = self._generate_columns_and_selector( + parsed_args, column_names, + ) if selector: # Generator expression to only return the parts of a row # of data that the user has expressed interest in # seeing. We have to convert the compress() output to a # list so the table formatter can ask for its length. - data = (list(self._compress_iterable(row, selector)) - for row in data) - self.formatter.emit_list(columns_to_include, - data, - self.app.stdout, - parsed_args, - ) + data = ( + list(self._compress_iterable(row, selector)) for row in data + ) + + self.formatter.emit_list( + columns_to_include, data, self.app.stdout, parsed_args, + ) + return 0 diff --git a/cliff/tests/test_lister.py b/cliff/tests/test_lister.py index 7fc7222..5dfa69a 100644 --- a/cliff/tests/test_lister.py +++ b/cliff/tests/test_lister.py @@ -106,6 +106,21 @@ class TestLister(base.TestBase): data = list(args[1]) self.assertEqual([['a', 'A'], ['c', 'A'], ['b', 'B']], data) + def test_sort_by_column_reverse_order(self): + test_lister = ExerciseLister(mock.Mock(), []) + parsed_args = mock.Mock() + parsed_args.columns = ('Col1', 'Col2') + parsed_args.formatter = 'test' + parsed_args.sort_columns = ['Col2', 'Col1'] + parsed_args.sort_direction = 'desc' + + test_lister.run(parsed_args) + + f = test_lister._formatter_plugins['test'] + args = f.args[0] + data = list(args[1]) + self.assertEqual([['b', 'B'], ['c', 'A'], ['a', 'A']], data) + def test_sort_by_column_data_already_sorted(self): test_lister = ExerciseListerCustomSort(mock.Mock(), []) parsed_args = mock.Mock() diff --git a/releasenotes/notes/add-Lister-sort-direction-5f34dba3c9743572.yaml b/releasenotes/notes/add-Lister-sort-direction-5f34dba3c9743572.yaml new file mode 100644 index 0000000..219e20c --- /dev/null +++ b/releasenotes/notes/add-Lister-sort-direction-5f34dba3c9743572.yaml @@ -0,0 +1,21 @@ +--- +features: + - | + The ``cliff.lister.Lister`` base class now implements ``--sort-ascending`` + and ``--sort-descending`` options, which can be used to configure the sort + direction. For example:: + + $ hello-world list-users --sort-column email --sort-descending + +----------------+-----------------------------+ + | Name | Email | + +----------------+-----------------------------+ + | Charles Xavier | therealcharliex@example.com | + | Jim Hendrix | jim@example.com | + | John Doe | doe.john@example.com | + | Alice Baker | abaker@example.com | + +----------------+-----------------------------+ +upgrade: + - | + ``cliff.lister.Lister`` implementations that override the + ``need_sort_by_cliff`` property should now consider the + ``--sort-ascending`` and ``--sort-descending`` options. |