- Bump leapp-framework to 6.5 - Change how commands are converted for text based report from the list representation - Introduce the format_list function for unified presentation of lists in reports and logs - Resolves: RHEL-169278
195 lines
7.6 KiB
Diff
195 lines
7.6 KiB
Diff
From d67332c2234e437113493bfdce37594c1e2ae417 Mon Sep 17 00:00:00 2001
|
|
From: Petr Stodulka <pstodulk@redhat.com>
|
|
Date: Wed, 3 Dec 2025 08:48:08 +0100
|
|
Subject: [PATCH 01/10] introduce the format_list function
|
|
|
|
The construction of report messages involving lists (e.g., package
|
|
names, file paths) lacked a consistent formatting which led to
|
|
inconsistencies and redefinitions when creating reports.
|
|
|
|
This patch introduces the format_list function to provide a consistent
|
|
formatting option across all reports. The function is available from both
|
|
leapp.libraries.stdlib and leapp.reporting. It supports custom sorting,
|
|
item limits, and configurable separator.
|
|
|
|
Jira-ref: RHEL-126447
|
|
---
|
|
docs/source/best-practices.md | 17 +++++++++++
|
|
leapp/libraries/stdlib/__init__.py | 36 +++++++++++++++++++++--
|
|
leapp/reporting/__init__.py | 5 +++-
|
|
packaging/leapp.spec | 2 +-
|
|
tests/scripts/test_format_list.py | 47 ++++++++++++++++++++++++++++++
|
|
5 files changed, 103 insertions(+), 4 deletions(-)
|
|
create mode 100644 tests/scripts/test_format_list.py
|
|
|
|
diff --git a/docs/source/best-practices.md b/docs/source/best-practices.md
|
|
index 11f861a..913dcbd 100644
|
|
--- a/docs/source/best-practices.md
|
|
+++ b/docs/source/best-practices.md
|
|
@@ -155,6 +155,23 @@ In case of [StopActorExecutionError](leapp.exceptions.StopActorExecutionError) t
|
|
|
|
You can also use the [StopActorExecution](leapp.exceptions.StopActorExecution) and [StopActorExecutionError](leapp.exceptions.StopActorExecutionError) exceptions inside a private or shared library.
|
|
|
|
+## Consistent list formatting in reports
|
|
+
|
|
+When constructing report messages that include lists of items (e.g. package names, file paths), use the `format_list` function to ensure consistent formatting across all reports. The function is available from both `leapp.libraries.stdlib` and `leapp.reporting`. It supports custom sorting, item limits, and configurable separators.
|
|
+
|
|
+```python
|
|
+from leapp.reporting import format_list
|
|
+
|
|
+pkgs = ['kernel', 'bash', 'glibc']
|
|
+msg = 'The following packages will be removed:{}'.format(format_list(pkgs))
|
|
+
|
|
+# Output:
|
|
+#The following packages will be removed:
|
|
+# - bash
|
|
+# - glibc
|
|
+# - kernel
|
|
+```
|
|
+
|
|
## Use the LEAPP and LEAPP\_DEVEL prefixes for new envars
|
|
|
|
In case you need to change a behaviour of actor(s) for testing or development purposes - e.g. be able to skip a functionality in your actor - use environment variables. Such environment variables should start with prefix *LEAPP\_DEVEL*. Such variables are not possible to use on production systems without special *LEAPP\_UNSUPPORTED* variable. This prevents users to break their systems by a mistake.
|
|
diff --git a/leapp/libraries/stdlib/__init__.py b/leapp/libraries/stdlib/__init__.py
|
|
index 89e59b2..efe2029 100644
|
|
--- a/leapp/libraries/stdlib/__init__.py
|
|
+++ b/leapp/libraries/stdlib/__init__.py
|
|
@@ -8,12 +8,15 @@ import logging
|
|
import os
|
|
import sys
|
|
import uuid
|
|
+from itertools import islice
|
|
|
|
from leapp.exceptions import LeappError
|
|
-from leapp.utils.audit import create_audit_entry
|
|
from leapp.libraries.stdlib import api
|
|
-from leapp.libraries.stdlib.call import _call, STDOUT
|
|
+from leapp.libraries.stdlib.call import STDOUT, _call
|
|
from leapp.libraries.stdlib.config import is_debug
|
|
+from leapp.utils.audit import create_audit_entry
|
|
+
|
|
+FMT_LIST_SEPARATOR = '\n - '
|
|
|
|
|
|
class CalledProcessError(LeappError):
|
|
@@ -214,3 +217,32 @@ def run(args, split=False, callback_raw=_console_logging_handler, callback_lineb
|
|
)
|
|
api.current_logger().debug('External command has finished: {0}'.format(str(args)))
|
|
return result
|
|
+
|
|
+
|
|
+def format_list(data, sep=FMT_LIST_SEPARATOR, callback_sort=sorted, limit=0):
|
|
+ """
|
|
+ Format an iterable into a string using a specified separator that is prepended to every item.
|
|
+
|
|
+ This function can be used to consistently format lists in reports, logs, and error messages.
|
|
+
|
|
+ :param data: Iterable of items to format.
|
|
+ :type data: Iterable
|
|
+ :param sep: Separator prepended to each item. Defaults to FMT_LIST_SEPARATOR.
|
|
+ :type sep: str
|
|
+ :param callback_sort: Callable returning a new list, called before the limit is applied.
|
|
+ Set to None to preserve original order. Defaults to sorted.
|
|
+ :type callback_sort: Callable or None
|
|
+ :param limit: Maximum number of items to include. Defaults to 0 (no limit).
|
|
+ :type limit: int
|
|
+ :returns: A string with each item prefixed by the specified separator.
|
|
+ :rtype: str
|
|
+ """
|
|
+ items = data
|
|
+ if callback_sort is not None:
|
|
+ items = callback_sort(data)
|
|
+
|
|
+ if limit > 0:
|
|
+ items = islice(items, limit)
|
|
+
|
|
+ res = ['{}{}'.format(sep, item) for item in items]
|
|
+ return ''.join(res)
|
|
diff --git a/leapp/reporting/__init__.py b/leapp/reporting/__init__.py
|
|
index 7a0e223..34af17a 100644
|
|
--- a/leapp/reporting/__init__.py
|
|
+++ b/leapp/reporting/__init__.py
|
|
@@ -6,9 +6,12 @@ import os
|
|
import six
|
|
|
|
from leapp.compat import string_types
|
|
+# NOTE(pstodulk): the format_list is imported to provide the function
|
|
+# also in this library. Its use is not planned here however.
|
|
+from leapp.libraries.stdlib import format_list
|
|
+from leapp.libraries.stdlib.api import produce
|
|
from leapp.models import fields, Model, ErrorModel
|
|
from leapp.topics import ReportTopic
|
|
-from leapp.libraries.stdlib.api import produce
|
|
from leapp.utils.deprecation import deprecated
|
|
|
|
|
|
diff --git a/packaging/leapp.spec b/packaging/leapp.spec
|
|
index 1e32cf6..a06b141 100644
|
|
--- a/packaging/leapp.spec
|
|
+++ b/packaging/leapp.spec
|
|
@@ -13,7 +13,7 @@
|
|
# This is kind of help for more flexible development of leapp repository,
|
|
# so people do not have to wait for new official release of leapp to ensure
|
|
# it is installed/used the compatible one.
|
|
-%global framework_version 6.2
|
|
+%global framework_version 6.3
|
|
|
|
# IMPORTANT: everytime the requirements are changed, increment number by one
|
|
# - same for Provides in deps subpackage
|
|
diff --git a/tests/scripts/test_format_list.py b/tests/scripts/test_format_list.py
|
|
new file mode 100644
|
|
index 0000000..1b0ca24
|
|
--- /dev/null
|
|
+++ b/tests/scripts/test_format_list.py
|
|
@@ -0,0 +1,47 @@
|
|
+import pytest
|
|
+
|
|
+from leapp.libraries.stdlib import FMT_LIST_SEPARATOR, format_list
|
|
+
|
|
+SEP = ', '
|
|
+
|
|
+
|
|
+@pytest.mark.parametrize('data, kwargs, expected', [
|
|
+ # Basic usage
|
|
+ ([], {}, ''),
|
|
+ (['c', 'a', 'b'], {}, '{0}a{0}b{0}c'.format(FMT_LIST_SEPARATOR)),
|
|
+ (['c', 'a', 'b'], {'sep': SEP}, ', a, b, c'),
|
|
+ (['a'], {'sep': SEP}, ', a'),
|
|
+ # Sorting
|
|
+ (['c', 'a', 'b'], {'sep': SEP, 'callback_sort': None}, ', c, a, b'),
|
|
+ (['c', 'a', 'b'], {'sep': SEP, 'callback_sort': lambda d: sorted(d, reverse=True)}, ', c, b, a'),
|
|
+ # Limit
|
|
+ (['c', 'a', 'b'], {'sep': SEP, 'limit': 2}, ', a, b'),
|
|
+ (['c', 'a', 'b'], {'sep': SEP, 'limit': 0}, ', a, b, c'),
|
|
+ (['b', 'a'], {'sep': SEP, 'limit': 10}, ', a, b'),
|
|
+ (['c', 'a', 'b'], {'sep': SEP, 'limit': -1}, ', a, b, c'),
|
|
+ # Non-list iterables
|
|
+ ({'a', 'b', 'c'}, {'sep': SEP, 'limit': 2}, ', a, b'),
|
|
+ (('a', 'b'), {'sep': SEP}, ', a, b'),
|
|
+ ({'b': 1, 'a': 2}, {'sep': SEP}, ', a, b'),
|
|
+ # Generators
|
|
+ ((x for x in ['c', 'a', 'b']), {'sep': SEP}, ', a, b, c'),
|
|
+ ((x for x in ['c', 'a', 'b']), {'sep': SEP, 'callback_sort': None, 'limit': 2}, ', c, a'),
|
|
+], ids=[
|
|
+ 'empty_data',
|
|
+ 'single_item',
|
|
+ 'default_separator',
|
|
+ 'custom_separator',
|
|
+ 'no_sort',
|
|
+ 'reverse_sort',
|
|
+ 'limit',
|
|
+ 'limit_zero',
|
|
+ 'limit_larger_than_data',
|
|
+ 'negative_limit_ignored',
|
|
+ 'set_input',
|
|
+ 'tuple_input',
|
|
+ 'dict_keys_input',
|
|
+ 'generator_sorted',
|
|
+ 'generator_unsorted_with_limit',
|
|
+])
|
|
+def test_format_list(data, kwargs, expected):
|
|
+ assert format_list(data, **kwargs) == expected
|
|
--
|
|
2.53.0
|
|
|