6 Commits

Author SHA1 Message Date
Stephen Finucane
fb9a3a9b2d Strip trailing periods when getting description
This yields slightly prettier output.

Change-Id: Ibec7cd861eacc3630182d6a782ffaf361f449aa6
Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
2022-12-12 18:14:52 +00:00
Stephen Finucane
7798cb2e37 Add '--sort-ascending', '--sort-descending' parameters
Allow users to reverse sorting direction.

Change-Id: Iecd539139c5a7ce4abaaee2ff5632a2459437d51
Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
2021-01-29 15:40:42 +00:00
Stephen Finucane
c1c991045c Make 'FormattableColumn' comparable
Implement the '__lt__' magic method, thus providing the minimal set of
rich comparison methods necessary to support sorting. This will allows
users using these formatters for the more basic types (i.e. not dicts)
to sort their output using the standard '--sort-column' option.

Change-Id: I08e1f1bc75fa6452f19dfb9d221c1daec194d58d
Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
2021-01-29 15:40:42 +00:00
Stephen Finucane
4f45f9a30e Handle null values when sorting
One unfortunate change (or fortunate, depending on how you look at
types) in Python 3 is the inability to sort iterables of different
types. For example:

  >>> x = ['foo', 'bar', None, 'qux']
  >>> sorted(x)
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  TypeError: '<' not supported between instances of 'NoneType' and 'str'

Fortunately, we can take advantage of the fact that by providing a
function for the 'key' that returns a tuple, we can sort on multiple
conditions. In this case, "when the first key returns that two elements
are equal, the second key is used to compare." [1] We can use this to
first separate the values by whether they are None or not, punting those
that are not to the end, before sorting the non-None values normally.
For example:

  >>> x = ['foo', 'bar', None, 'qux']
  >>> sorted(x, key=lambda k: (k is None, k))
  ['bar', 'foo', 'qux', None]

We were already using this feature implicitly through our use of
'operator.itemgetter(*indexes)', which will return a tuple if there is
more than one item in 'indexes', and now we simply make that explicit,
fixing the case where we're attempting to compare a comparable type
with None. For all other cases, such as comparing a value that isn't
comparable, we surround things with a try-catch and a debug logging
statement to allow things to continue.

Note that we could optimize what we're done further by building a key
value that covers all indexes, rather than using a for loop to do so.
For example:

  >>> x = [('baz', 2), (None, 0), ('bar', 3), ('baz', 4), ('qux', 0)]
  >>> sorted(x, key=lambda k: list(
  ...     itertools.chain((k[i] is None, k[i]) for i in (0, 1)))
  ... )
  [('bar', 3), ('baz', 2), ('baz', 4), ('qux', 0), (None, 0)]

However, this would be harder to grok and would also mean we're unable
to handle exceptions on a single column where e.g. there are mixed types
or types that are not comparable while still sorting on the other
columns. Perhaps this would be desirable for some users, but sorting on
a best-effort basis does seem wiser and generally more user friendly.
Anyone that wants to sort on such columns should ensure their types are
comparable or implement their own sorting implementation.

[1] https://www.kite.com/python/answers/how-to-sort-by-two-keys-in-python

Change-Id: I4803051a6dd05c143a15923254af97e32cd39693
Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
Story: 2008456
Task: 41466
2021-01-29 15:40:31 +00:00
Monty Taylor
8477c4dbd0 Import command group support from osc-lib
osc-lib adds support for named groups of commands. There's nothing
particularly openstackclient about this support, so add it here.
This way when we add defered plugin loading, it'll work.

Change-Id: Ia0260d2607f4a240b39e90da4b5b09e7cdfde04f
2020-06-07 11:08:29 -05:00
Hervé Beraud
1e04cb683e adding missing releasenote for the drop of py27 support
Change-Id: I6b35d628b4d5d1d06d0ffc5925ee5d54e0005923
2020-02-06 12:33:34 +01:00