Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-cliff for openSUSE:Factory checked in at 2026-05-18 17:46:53 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-cliff (Old) and /work/SRC/openSUSE:Factory/.python-cliff.new.1966 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-cliff" Mon May 18 17:46:53 2026 rev:52 rq:1353615 version:4.14.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-cliff/python-cliff.changes 2026-04-11 22:29:39.308441966 +0200 +++ /work/SRC/openSUSE:Factory/.python-cliff.new.1966/python-cliff.changes 2026-05-18 17:47:09.110158202 +0200 @@ -1,0 +2,16 @@ +Sun May 17 19:05:11 UTC 2026 - Dirk Müller <[email protected]> + +- update to 4.14.0: + * Fix command name alignment broken by ANSI color codes in help + output + * typing: Remove additional use of private _argparse module + * typing: Use objects from typing + * trivial: Use objects from collections.abc + * typing: Broaden return type of Command.get_parser + * ruff: Configure hacking as external linter + * tests: Drop use of testscenarios + * Relax help usage assertion for module-based runners + * Fix compatibility with cmd2 3.1.0+ + * Fix cliff sphinxextention for Python 3.14 + +------------------------------------------------------------------- Old: ---- cliff-4.13.3.tar.gz New: ---- cliff-4.14.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-cliff.spec ++++++ --- /var/tmp/diff_new_pack.O245Vg/_old 2026-05-18 17:47:09.754184814 +0200 +++ /var/tmp/diff_new_pack.O245Vg/_new 2026-05-18 17:47:09.754184814 +0200 @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-cliff -Version: 4.13.3 +Version: 4.14.0 Release: 0 Summary: Command Line Interface Formulation Framework License: Apache-2.0 @@ -33,7 +33,7 @@ BuildRequires: %{python_module PrettyTable >= 0.7.2} BuildRequires: %{python_module PyYAML >= 3.12} BuildRequires: %{python_module Sphinx >= 5.0.0} -BuildRequires: %{python_module cmd2 >= 1.0.0} +BuildRequires: %{python_module cmd2 >= 3.0.0} BuildRequires: %{python_module coverage >= 5.0} BuildRequires: %{python_module fixtures >= 3.0.0} BuildRequires: %{python_module pytest} @@ -45,7 +45,7 @@ Requires: python-PrettyTable >= 0.7.2 Requires: python-PyYAML >= 3.12 Requires: python-autopage >= 0.4.0 -Requires: python-cmd2 >= 1.0.0 +Requires: python-cmd2 >= 3.0.0 Requires: python-stevedore >= 5.6.0 BuildArch: noarch %python_subpackages @@ -69,7 +69,7 @@ %pytest cliff/tests %files %{python_files} -%doc AUTHORS ChangeLog README.rst +%doc ChangeLog README.rst %license LICENSE %{python_sitelib}/cliff %{python_sitelib}/cliff-%{version}.dist-info ++++++ cliff-4.13.3.tar.gz -> cliff-4.14.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/AUTHORS new/cliff-4.14.0/AUTHORS --- old/cliff-4.13.3/AUTHORS 2026-04-08 11:09:59.000000000 +0200 +++ new/cliff-4.14.0/AUTHORS 2026-05-13 11:00:23.000000000 +0200 @@ -87,6 +87,7 @@ Thomas Bechtold <[email protected]> Thomas Goirand <[email protected]> Thomas Herve <[email protected]> +Todd Stansell <[email protected]> Tomaz Muraus <[email protected]> Tony Breeds <[email protected]> Tony Xu <[email protected]> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/ChangeLog new/cliff-4.14.0/ChangeLog --- old/cliff-4.13.3/ChangeLog 2026-04-08 11:09:59.000000000 +0200 +++ new/cliff-4.14.0/ChangeLog 2026-05-13 11:00:23.000000000 +0200 @@ -1,17 +1,19 @@ CHANGES ======= -4.13.3 +4.14.0 ------ -* STABLE-ONLY: Revert bump of cmd2 +* Fix command name alignment broken by ANSI color codes in help output * typing: Remove additional use of private \_argparse module +* typing: Use objects from typing +* trivial: Use objects from collections.abc * typing: Broaden return type of Command.get\_parser +* ruff: Configure hacking as external linter +* tests: Drop use of testscenarios * Relax help usage assertion for module-based runners -* Fix cliff sphinxextention for Python 3.14 -* Update TOX\_CONSTRAINTS\_FILE for stable/2026.1 -* Update .gitreview for stable/2026.1 * Fix compatibility with cmd2 3.1.0+ +* Fix cliff sphinxextention for Python 3.14 4.13.2 ------ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/PKG-INFO new/cliff-4.14.0/PKG-INFO --- old/cliff-4.13.3/PKG-INFO 2026-04-08 11:09:59.334140000 +0200 +++ new/cliff-4.14.0/PKG-INFO 2026-05-13 11:00:23.282622600 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: cliff -Version: 4.13.3 +Version: 4.14.0 Summary: Command Line Interface Formulation Framework Author-email: OpenStack <[email protected]> License: Apache-2.0 @@ -22,7 +22,7 @@ Description-Content-Type: text/x-rst License-File: LICENSE Requires-Dist: autopage>=0.4.0 -Requires-Dist: cmd2>=1.0.0 +Requires-Dist: cmd2>=3.0.0 Requires-Dist: PrettyTable>=0.7.2 Requires-Dist: stevedore>=5.6.0 Requires-Dist: PyYAML>=3.12 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/_argparse.py new/cliff-4.14.0/cliff/_argparse.py --- old/cliff-4.13.3/cliff/_argparse.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/_argparse.py 2026-05-13 10:59:55.000000000 +0200 @@ -13,8 +13,8 @@ """Overrides of standard argparse behavior.""" import argparse -import collections.abc -import typing as ty +from collections.abc import Iterable +from typing import Any import warnings import autopage.argparse @@ -26,14 +26,14 @@ # special conflict handler. def add_argument_group( - self, *args: ty.Any, **kwargs: ty.Any + self, *args: Any, **kwargs: Any ) -> '_ArgumentGroup': group = _ArgumentGroup(self, *args, **kwargs) self._action_groups.append(group) return group def add_mutually_exclusive_group( - self, **kwargs: ty.Any + self, **kwargs: Any ) -> '_MutuallyExclusiveGroup': group = _MutuallyExclusiveGroup(self, **kwargs) self._mutually_exclusive_groups.append(group) @@ -42,9 +42,7 @@ def _handle_conflict_ignore( self, action: argparse.Action, - conflicting_actions: collections.abc.Iterable[ - tuple[str, argparse.Action] - ], + conflicting_actions: Iterable[tuple[str, argparse.Action]], ) -> None: _handle_conflict_ignore( self, @@ -58,7 +56,7 @@ container: argparse._ActionsContainer, option_string_actions: dict[str, argparse.Action], new_action: argparse.Action, - conflicting_actions: collections.abc.Iterable[tuple[str, argparse.Action]], + conflicting_actions: Iterable[tuple[str, argparse.Action]], ) -> None: # Remember the option strings the new action starts with so we can # restore them as part of error reporting if we need to. @@ -91,14 +89,14 @@ # special conflict handler. def add_argument_group( - self, *args: ty.Any, **kwargs: ty.Any + self, *args: Any, **kwargs: Any ) -> '_ArgumentGroup': group = _ArgumentGroup(self, *args, **kwargs) self._action_groups.append(group) return group def add_mutually_exclusive_group( - self, **kwargs: ty.Any + self, **kwargs: Any ) -> '_MutuallyExclusiveGroup': group = _MutuallyExclusiveGroup(self, **kwargs) self._mutually_exclusive_groups.append(group) @@ -107,9 +105,7 @@ def _handle_conflict_ignore( self, action: argparse.Action, - conflicting_actions: collections.abc.Iterable[ - tuple[str, argparse.Action] - ], + conflicting_actions: Iterable[tuple[str, argparse.Action]], ) -> None: _handle_conflict_ignore( self, @@ -125,14 +121,14 @@ # special conflict handler. def add_argument_group( - self, *args: ty.Any, **kwargs: ty.Any + self, *args: Any, **kwargs: Any ) -> '_ArgumentGroup': group = _ArgumentGroup(self, *args, **kwargs) self._action_groups.append(group) return group def add_mutually_exclusive_group( - self, **kwargs: ty.Any + self, **kwargs: Any ) -> '_MutuallyExclusiveGroup': group = _MutuallyExclusiveGroup(self, **kwargs) self._mutually_exclusive_groups.append(group) @@ -141,9 +137,7 @@ def _handle_conflict_ignore( self, action: argparse.Action, - conflicting_actions: collections.abc.Iterable[ - tuple[str, argparse.Action] - ], + conflicting_actions: Iterable[tuple[str, argparse.Action]], ) -> None: _handle_conflict_ignore( self, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/app.py new/cliff-4.14.0/cliff/app.py --- old/cliff-4.13.3/cliff/app.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/app.py 2026-05-13 10:59:55.000000000 +0200 @@ -19,14 +19,14 @@ import logging.handlers import os import sys -import typing as ty +from typing import TYPE_CHECKING, Any, TextIO from cliff import _argparse from . import complete from . import help from . import utils -if ty.TYPE_CHECKING: +if TYPE_CHECKING: from . import command as _command from . import commandmanager as _commandmanager from . import interactive as _interactive @@ -79,9 +79,9 @@ description: str | None, version: str | None, command_manager: '_commandmanager.CommandManager', - stdin: ty.TextIO | None = None, - stdout: ty.TextIO | None = None, - stderr: ty.TextIO | None = None, + stdin: TextIO | None = None, + stdout: TextIO | None = None, + stderr: TextIO | None = None, interactive_app_factory: type['_interactive.InteractiveApp'] | None = None, deferred_help: bool = False, @@ -99,9 +99,9 @@ def _set_streams( self, - stdin: ty.TextIO | None, - stdout: ty.TextIO | None, - stderr: ty.TextIO | None, + stdin: TextIO | None, + stdout: TextIO | None, + stderr: TextIO | None, ) -> None: try: locale.setlocale(locale.LC_ALL, '') @@ -142,7 +142,7 @@ self, description: str | None, version: str | None, - argparse_kwargs: dict[str, ty.Any] | None = None, + argparse_kwargs: dict[str, Any] | None = None, ) -> argparse.ArgumentParser: """Return an argparse option parser for this application. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/columns.py new/cliff-4.14.0/cliff/columns.py --- old/cliff-4.13.3/cliff/columns.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/columns.py 2026-05-13 10:59:55.000000000 +0200 @@ -12,12 +12,12 @@ """Formattable column tools.""" -import typing as ty +from typing import Generic, TypeVar -_T = ty.TypeVar('_T') +_T = TypeVar('_T') -class FormattableColumn(ty.Generic[_T]): +class FormattableColumn(Generic[_T]): def __init__(self, value: _T) -> None: self._value = value diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/command.py new/cliff-4.14.0/cliff/command.py --- old/cliff-4.13.3/cliff/command.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/command.py 2026-05-13 10:59:55.000000000 +0200 @@ -15,18 +15,18 @@ from importlib.metadata import packages_distributions import inspect import types -import typing as ty +from typing import TYPE_CHECKING, Any, TypeVar from stevedore import extension from cliff import _argparse -if ty.TYPE_CHECKING: +if TYPE_CHECKING: from . import app as _app from . import hooks from typing_extensions import Never -_T = ty.TypeVar('_T') +_T = TypeVar('_T') _dists_by_mods = None @@ -174,7 +174,7 @@ return parser @abc.abstractmethod - def take_action(self, parsed_args: argparse.Namespace) -> ty.Any: + def take_action(self, parsed_args: argparse.Namespace) -> Any: """Override to do something useful. The returned value will be returned by the program. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/commandmanager.py new/cliff-4.14.0/cliff/commandmanager.py --- old/cliff-4.13.3/cliff/commandmanager.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/commandmanager.py 2026-05-13 10:59:55.000000000 +0200 @@ -12,7 +12,7 @@ """Discover and lookup command plugins.""" -import collections.abc +from collections.abc import Iterable, Iterator import importlib.metadata import logging from typing import TypeAlias @@ -83,7 +83,7 @@ namespace: str | None = None, convert_underscores: bool = True, *, - ignored_modules: collections.abc.Iterable[str] | None = None, + ignored_modules: Iterable[str] | None = None, ) -> None: self.namespace = namespace self.convert_underscores = convert_underscores @@ -101,7 +101,7 @@ @staticmethod def _is_module_ignored( - module_name: str, ignored_modules: collections.abc.Iterable[str] + module_name: str, ignored_modules: Iterable[str] ) -> bool: # given module_name = 'foo.bar.baz:wow', we expect to match any of # the following ignores: foo.bar.baz:wow, foo.bar.baz, foo.bar, foo @@ -175,7 +175,7 @@ def __iter__( self, - ) -> collections.abc.Iterator[tuple[str, EntryPointT]]: + ) -> Iterator[tuple[str, EntryPointT]]: return iter(self.commands.items()) def add_command( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/complete.py new/cliff-4.14.0/cliff/complete.py --- old/cliff-4.13.3/cliff/complete.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/complete.py 2026-05-13 10:59:55.000000000 +0200 @@ -15,13 +15,13 @@ import abc import argparse import logging -import typing as ty +from typing import TYPE_CHECKING, Any, TextIO import stevedore from cliff import command as _command -if ty.TYPE_CHECKING: +if TYPE_CHECKING: from cliff import app @@ -59,7 +59,7 @@ return ' '.join(k for k in sorted(self._dictionary.keys())) def _get_data_recurse( - self, dictionary: dict[str, ty.Any], path: str + self, dictionary: dict[str, Any], path: str ) -> list[tuple[str, str]]: ray = [] keys = sorted(dictionary.keys()) @@ -81,7 +81,7 @@ class CompleteShellBase(metaclass=abc.ABCMeta): """base class for bash completion generation""" - def __init__(self, name: str, output: ty.TextIO) -> None: + def __init__(self, name: str, output: TextIO) -> None: self.name = str(name) self.output = output @@ -107,7 +107,7 @@ class CompleteNoCode(CompleteShellBase): """completion with no code""" - def __init__(self, name: str, output: ty.TextIO) -> None: + def __init__(self, name: str, output: TextIO) -> None: super().__init__(name, output) def get_header(self) -> str: @@ -120,7 +120,7 @@ class CompleteBash(CompleteShellBase): """completion for bash""" - def __init__(self, name: str, output: ty.TextIO) -> None: + def __init__(self, name: str, output: TextIO) -> None: super().__init__(name, output) def get_header(self) -> str: @@ -232,7 +232,7 @@ return cmd_parser._get_optional_actions() def take_action(self, parsed_args: argparse.Namespace) -> int: - self.log.debug(f'take_action({parsed_args})') + self.log.debug('take_action(%s)', parsed_args) name = parsed_args.name or self.app.NAME try: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/display.py new/cliff-4.14.0/cliff/display.py --- old/cliff-4.13.3/cliff/display.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/display.py 2026-05-13 10:59:55.000000000 +0200 @@ -14,9 +14,9 @@ import abc import argparse -import collections.abc +from collections.abc import Iterable, Iterator, Sequence from itertools import compress -import typing as ty +from typing import Any, Generic, TypeVar import stevedore @@ -24,12 +24,12 @@ from cliff import command from cliff.formatters import base as base_formatters -_T = ty.TypeVar("_T") +_T = TypeVar("_T") class DisplayCommandBase( command.Command, - ty.Generic[base_formatters.FormatterT], + Generic[base_formatters.FormatterT], metaclass=abc.ABCMeta, ): """Command base class for displaying data about a single object.""" @@ -106,8 +106,8 @@ def produce_output( self, parsed_args: argparse.Namespace, - column_names: collections.abc.Sequence[str], - data: ty.Any, + column_names: Sequence[str], + data: Any, ) -> int: """Use the formatter to generate the output. @@ -121,7 +121,7 @@ def _generate_columns_and_selector( self, parsed_args: argparse.Namespace, - column_names: collections.abc.Sequence[str], + column_names: Sequence[str], ) -> tuple[list[str], list[bool] | None]: """Generate included columns and selector according to parsed args. @@ -167,12 +167,8 @@ def _run_after_hooks( # type: ignore[override] self, parsed_args: argparse.Namespace, - data: tuple[ - collections.abc.Sequence[str], collections.abc.Iterable[ty.Any] - ], - ) -> tuple[ - collections.abc.Sequence[str], collections.abc.Iterable[ty.Any] - ]: + data: tuple[Sequence[str], Iterable[Any]], + ) -> tuple[Sequence[str], Iterable[Any]]: """Calls after() method of the hooks. This method is intended to be called from the run() method after @@ -196,7 +192,7 @@ @staticmethod def _compress_iterable( - iterable: collections.abc.Iterable[_T], - selectors: collections.abc.Iterable[ty.Any], - ) -> collections.abc.Iterator[_T]: + iterable: Iterable[_T], + selectors: Iterable[Any], + ) -> Iterator[_T]: return compress(iterable, selectors) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/formatters/base.py new/cliff-4.14.0/cliff/formatters/base.py --- old/cliff-4.13.3/cliff/formatters/base.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/formatters/base.py 2026-05-13 10:59:55.000000000 +0200 @@ -14,11 +14,11 @@ import abc import argparse -import collections.abc -import typing as ty +from collections.abc import Iterable, Sequence +from typing import Any, TextIO, TypeVar -FormatterT = ty.TypeVar('FormatterT', bound='Formatter') +FormatterT = TypeVar('FormatterT', bound='Formatter') class Formatter(metaclass=abc.ABCMeta): @@ -36,9 +36,9 @@ @abc.abstractmethod def emit_list( self, - column_names: collections.abc.Sequence[str], - data: collections.abc.Iterable[collections.abc.Sequence[ty.Any]], - stdout: ty.TextIO, + column_names: Sequence[str], + data: Iterable[Sequence[Any]], + stdout: TextIO, parsed_args: argparse.Namespace, ) -> None: """Format and print the list from the iterable data source. @@ -63,9 +63,9 @@ @abc.abstractmethod def emit_one( self, - column_names: collections.abc.Sequence[str], - data: collections.abc.Sequence[ty.Any], - stdout: ty.TextIO, + column_names: Sequence[str], + data: Sequence[Any], + stdout: TextIO, parsed_args: argparse.Namespace, ) -> None: """Format and print the values associated with the single object. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/formatters/commaseparated.py new/cliff-4.14.0/cliff/formatters/commaseparated.py --- old/cliff-4.13.3/cliff/formatters/commaseparated.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/formatters/commaseparated.py 2026-05-13 10:59:55.000000000 +0200 @@ -13,17 +13,17 @@ """Output formatters using csv format.""" import argparse -import collections.abc +from collections.abc import Iterable, Sequence import csv import os -import typing as ty +from typing import Any, Literal, TextIO from cliff import columns from cliff.formatters import base class CSVLister(base.ListFormatter): - QUOTE_MODES: dict[str, ty.Literal[0, 1, 2, 3]] = { + QUOTE_MODES: dict[str, Literal[0, 1, 2, 3]] = { 'all': csv.QUOTE_ALL, 'minimal': csv.QUOTE_MINIMAL, 'nonnumeric': csv.QUOTE_NONNUMERIC, @@ -42,9 +42,9 @@ def emit_list( self, - column_names: collections.abc.Sequence[str], - data: collections.abc.Iterable[collections.abc.Sequence[ty.Any]], - stdout: ty.TextIO, + column_names: Sequence[str], + data: Iterable[Sequence[Any]], + stdout: TextIO, parsed_args: argparse.Namespace, ) -> None: writer = csv.writer( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/formatters/json_format.py new/cliff-4.14.0/cliff/formatters/json_format.py --- old/cliff-4.13.3/cliff/formatters/json_format.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/formatters/json_format.py 2026-05-13 10:59:55.000000000 +0200 @@ -13,9 +13,9 @@ """Output formatters for JSON.""" import argparse -import collections.abc +from collections.abc import Iterable, Sequence import json -import typing as ty +from typing import Any, TextIO from cliff import columns from cliff.formatters import base @@ -33,9 +33,9 @@ def emit_list( self, - column_names: collections.abc.Sequence[str], - data: collections.abc.Iterable[collections.abc.Sequence[ty.Any]], - stdout: ty.TextIO, + column_names: Sequence[str], + data: Iterable[Sequence[Any]], + stdout: TextIO, parsed_args: argparse.Namespace, ) -> None: items = [] @@ -56,9 +56,9 @@ def emit_one( self, - column_names: collections.abc.Sequence[str], - data: collections.abc.Sequence[ty.Any], - stdout: ty.TextIO, + column_names: Sequence[str], + data: Sequence[Any], + stdout: TextIO, parsed_args: argparse.Namespace, ) -> None: one = { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/formatters/shell.py new/cliff-4.14.0/cliff/formatters/shell.py --- old/cliff-4.13.3/cliff/formatters/shell.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/formatters/shell.py 2026-05-13 10:59:55.000000000 +0200 @@ -13,8 +13,8 @@ """Output formatters using shell syntax.""" import argparse -import collections.abc -import typing as ty +from collections.abc import Sequence +from typing import Any, TextIO from cliff import columns from cliff.formatters import base @@ -44,9 +44,9 @@ def emit_one( self, - column_names: collections.abc.Sequence[str], - data: collections.abc.Sequence[ty.Any], - stdout: ty.TextIO, + column_names: Sequence[str], + data: Sequence[Any], + stdout: TextIO, parsed_args: argparse.Namespace, ) -> None: variable_names = [c.lower().replace(' ', '_') for c in column_names] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/formatters/table.py new/cliff-4.14.0/cliff/formatters/table.py --- old/cliff-4.13.3/cliff/formatters/table.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/formatters/table.py 2026-05-13 10:59:55.000000000 +0200 @@ -13,10 +13,10 @@ """Output formatters using prettytable.""" import argparse -import collections.abc +from collections.abc import Iterable, Sequence import os import sys -import typing as ty +from typing import Any, Literal, TextIO, TypeVar import prettytable @@ -24,13 +24,11 @@ from cliff.formatters import base from cliff import utils -_T = ty.TypeVar('_T') +_T = TypeVar('_T') def _format_row( - row: collections.abc.Iterable[ - columns.FormattableColumn[ty.Any] | str | _T - ], + row: Iterable[columns.FormattableColumn[Any] | str | _T], ) -> list[_T | str]: new_row = [] for r in row: @@ -51,7 +49,7 @@ class TableFormatter(base.ListFormatter, base.SingleFormatter): - ALIGNMENTS: dict[type[int | str | float], ty.Literal['l', 'c', 'r']] = { + ALIGNMENTS: dict[type[int | str | float], Literal['l', 'c', 'r']] = { int: 'r', str: 'l', float: 'r', @@ -90,8 +88,8 @@ def add_rows( self, table: prettytable.PrettyTable, - column_names: collections.abc.Sequence[str], - data: collections.abc.Iterable[collections.abc.Sequence[ty.Any]], + column_names: Sequence[str], + data: Iterable[Sequence[Any]], ) -> None: # Figure out the types of the columns in the # first row and set the alignment of the @@ -112,9 +110,9 @@ def emit_list( self, - column_names: collections.abc.Sequence[str], - data: collections.abc.Iterable[collections.abc.Sequence[ty.Any]], - stdout: ty.TextIO, + column_names: Sequence[str], + data: Iterable[Sequence[Any]], + stdout: TextIO, parsed_args: argparse.Namespace, ) -> None: x = prettytable.PrettyTable( @@ -142,9 +140,9 @@ def emit_one( self, - column_names: collections.abc.Sequence[str], - data: collections.abc.Sequence[ty.Any], - stdout: ty.TextIO, + column_names: Sequence[str], + data: Sequence[Any], + stdout: TextIO, parsed_args: argparse.Namespace, ) -> None: x = prettytable.PrettyTable( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/formatters/value.py new/cliff-4.14.0/cliff/formatters/value.py --- old/cliff-4.13.3/cliff/formatters/value.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/formatters/value.py 2026-05-13 10:59:55.000000000 +0200 @@ -13,8 +13,8 @@ """Output formatters values only""" import argparse -import collections.abc -import typing as ty +from collections.abc import Iterable, Sequence +from typing import Any, TextIO from cliff import columns from cliff.formatters import base @@ -26,9 +26,9 @@ def emit_list( self, - column_names: collections.abc.Sequence[str], - data: collections.abc.Iterable[collections.abc.Sequence[ty.Any]], - stdout: ty.TextIO, + column_names: Sequence[str], + data: Iterable[Sequence[Any]], + stdout: TextIO, parsed_args: argparse.Namespace, ) -> None: for row in data: @@ -47,9 +47,9 @@ def emit_one( self, - column_names: collections.abc.Sequence[str], - data: collections.abc.Sequence[ty.Any], - stdout: ty.TextIO, + column_names: Sequence[str], + data: Sequence[Any], + stdout: TextIO, parsed_args: argparse.Namespace, ) -> None: for value in data: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/formatters/yaml_format.py new/cliff-4.14.0/cliff/formatters/yaml_format.py --- old/cliff-4.13.3/cliff/formatters/yaml_format.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/formatters/yaml_format.py 2026-05-13 10:59:55.000000000 +0200 @@ -13,14 +13,14 @@ """Output formatters using PyYAML.""" import argparse -import collections.abc -import typing as ty +from collections.abc import Iterable, Sequence +from typing import Any, TextIO from cliff import columns from cliff.formatters import base -def _yaml_friendly(value: ty.Any) -> ty.Any: +def _yaml_friendly(value: Any) -> Any: if isinstance(value, columns.FormattableColumn): return value.machine_readable() elif hasattr(value, "toDict"): @@ -37,9 +37,9 @@ def emit_list( self, - column_names: collections.abc.Sequence[str], - data: collections.abc.Iterable[collections.abc.Sequence[ty.Any]], - stdout: ty.TextIO, + column_names: Sequence[str], + data: Iterable[Sequence[Any]], + stdout: TextIO, parsed_args: argparse.Namespace, ) -> None: # the yaml import is slow, so defer loading until we know we want it @@ -54,9 +54,9 @@ def emit_one( self, - column_names: collections.abc.Sequence[str], - data: collections.abc.Sequence[ty.Any], - stdout: ty.TextIO, + column_names: Sequence[str], + data: Sequence[Any], + stdout: TextIO, parsed_args: argparse.Namespace, ) -> None: # the yaml import is slow, so defer loading until we know we want it diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/help.py new/cliff-4.14.0/cliff/help.py --- old/cliff-4.13.3/cliff/help.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/help.py 2026-05-13 10:59:55.000000000 +0200 @@ -11,10 +11,10 @@ # under the License. import argparse -import collections.abc +from collections.abc import Sequence import inspect import traceback -import typing as ty +from typing import Any import autopage.argparse @@ -43,7 +43,7 @@ self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, - values: str | collections.abc.Sequence[ty.Any] | None, + values: str | Sequence[Any] | None, option_string: str | None = None, ) -> None: app = self.default @@ -94,9 +94,10 @@ dist_info = f'\033[90m{dist_info}\033[39m' else: dist_info = '' + padded_name = f'{name:<13}' if color: - name = f'\033[36m{name}\033[39m' - out.write(f' {name:<13} {one_liner}{dist_info}\n') + padded_name = f'\033[36m{padded_name}\033[39m' + out.write(f' {padded_name} {one_liner}{dist_info}\n') raise HelpExit() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/interactive.py new/cliff-4.14.0/cliff/interactive.py --- old/cliff-4.13.3/cliff/interactive.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/interactive.py 2026-05-13 10:59:55.000000000 +0200 @@ -15,12 +15,12 @@ import itertools import shlex import sys -import typing as ty +from typing import TYPE_CHECKING, TextIO import autopage.argparse import cmd2 -if ty.TYPE_CHECKING: +if TYPE_CHECKING: from . import app as _app from . import commandmanager as _commandmanager @@ -50,8 +50,8 @@ self, parent_app: '_app.App', command_manager: '_commandmanager.CommandManager', - stdin: ty.TextIO | None, - stdout: ty.TextIO | None, + stdin: TextIO | None, + stdout: TextIO | None, errexit: bool = False, ): self.parent_app = parent_app diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/lister.py new/cliff-4.14.0/cliff/lister.py --- old/cliff-4.13.3/cliff/lister.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/lister.py 2026-05-13 10:59:55.000000000 +0200 @@ -14,9 +14,9 @@ import abc import argparse -import collections.abc +from collections.abc import Iterable, Sequence import logging -import typing as ty +from typing import Any from cliff import display from cliff.formatters import base as base_formatters @@ -50,9 +50,7 @@ @abc.abstractmethod def take_action( self, parsed_args: argparse.Namespace - ) -> tuple[ - collections.abc.Sequence[str], collections.abc.Iterable[ty.Any] - ]: + ) -> tuple[Sequence[str], Iterable[Any]]: """Run command. Return a tuple containing the column names and an iterable containing @@ -94,8 +92,8 @@ def produce_output( self, parsed_args: argparse.Namespace, - column_names: collections.abc.Sequence[str], - data: collections.abc.Iterable[collections.abc.Sequence[ty.Any]], + column_names: Sequence[str], + data: Iterable[Sequence[Any]], ) -> int: if parsed_args.sort_columns and self.need_sort_by_cliff: indexes = [ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/show.py new/cliff-4.14.0/cliff/show.py --- old/cliff-4.13.3/cliff/show.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/show.py 2026-05-13 10:59:55.000000000 +0200 @@ -14,8 +14,8 @@ import abc import argparse -import collections.abc -import typing as ty +from collections.abc import Iterable, Sequence +from typing import Any from cliff import display from cliff.formatters import base as base_formatters @@ -38,9 +38,7 @@ @abc.abstractmethod def take_action( self, parsed_args: argparse.Namespace - ) -> tuple[ - collections.abc.Sequence[str], collections.abc.Iterable[ty.Any] - ]: + ) -> tuple[Sequence[str], Iterable[Any]]: """Run command. Return a tuple containing the column names and an iterable containing @@ -50,8 +48,8 @@ def produce_output( self, parsed_args: argparse.Namespace, - column_names: collections.abc.Sequence[str], - data: collections.abc.Sequence[ty.Any], + column_names: Sequence[str], + data: Sequence[Any], ) -> int: columns_to_include, selector = self._generate_columns_and_selector( parsed_args, column_names @@ -64,8 +62,8 @@ return 0 def dict2columns( - self, data: dict[str, ty.Any] - ) -> tuple[tuple[str, ...], tuple[ty.Any, ...]]: + self, data: dict[str, Any] + ) -> tuple[tuple[str, ...], tuple[Any, ...]]: """Implement the common task of converting a dict-based object to the two-column output that ShowOne expects. """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/sphinxext.py new/cliff-4.14.0/cliff/sphinxext.py --- old/cliff-4.13.3/cliff/sphinxext.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/sphinxext.py 2026-05-13 10:59:55.000000000 +0200 @@ -13,13 +13,13 @@ # under the License. import argparse -import collections.abc +from collections.abc import Iterable import fnmatch import importlib import inspect import re import sys -import typing as ty +from typing import Any from docutils import nodes from docutils.parsers import rst @@ -36,7 +36,7 @@ """Indent by four spaces.""" prefix = ' ' * 4 - def prefixed_lines() -> collections.abc.Iterable[str]: + def prefixed_lines() -> Iterable[str]: for line in text.splitlines(True): yield (prefix + line if line.strip() else line) @@ -45,7 +45,7 @@ def _format_description( parser: argparse.ArgumentParser, -) -> collections.abc.Iterable[str]: +) -> Iterable[str]: """Get parser description. We parse this as reStructuredText, allowing users to embed rich @@ -110,7 +110,7 @@ def _format_epilog( parser: argparse.ArgumentParser, -) -> collections.abc.Iterable[str]: +) -> Iterable[str]: """Get parser epilog. We parse this as reStructuredText, allowing users to embed rich @@ -127,7 +127,7 @@ def _format_positional_action( action: argparse.Action, -) -> collections.abc.Iterable[str]: +) -> Iterable[str]: """Format a positional action.""" if action.help == argparse.SUPPRESS: return @@ -156,7 +156,7 @@ def _format_optional_action( action: argparse.Action, -) -> collections.abc.Iterable[str]: +) -> Iterable[str]: """Format an optional action.""" if action.help == argparse.SUPPRESS or action.option_strings is None: return @@ -191,7 +191,7 @@ def _format_parser( parser: argparse.ArgumentParser, -) -> collections.abc.Iterable[str]: +) -> Iterable[str]: """Format the output of an argparse 'ArgumentParser' object. Given the following parser:: @@ -465,7 +465,7 @@ return self._generate_command_nodes(commands, application_name) -def setup(app: sphinx.application.Sphinx) -> dict[str, ty.Any]: +def setup(app: sphinx.application.Sphinx) -> dict[str, Any]: app.add_directive('autoprogram-cliff', AutoprogramCliffDirective) app.add_config_value('autoprogram_cliff_application', '', 'env') app.add_config_value('autoprogram_cliff_ignored', ['--help'], 'env') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/tests/test_commandmanager.py new/cliff-4.14.0/cliff/tests/test_commandmanager.py --- old/cliff-4.13.3/cliff/tests/test_commandmanager.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/tests/test_commandmanager.py 2026-05-13 10:59:55.000000000 +0200 @@ -12,68 +12,63 @@ from unittest import mock -import testscenarios # type: ignore - from cliff import command from cliff import commandmanager from cliff.tests import base from cliff.tests import utils -load_tests = testscenarios.load_tests_apply_scenarios - - class TestLookupAndFind(base.TestBase): scenarios = [ - ('one-word', {'argv': ['one']}), - ('two-words', {'argv': ['two', 'words']}), - ('three-words', {'argv': ['three', 'word', 'command']}), + ('one-word', ['one']), + ('two-words', ['two', 'words']), + ('three-words', ['three', 'word', 'command']), ] - argv: list[str] - def test(self): - mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) - cmd, name, remaining = mgr.find_command(self.argv) - self.assertTrue(cmd) - self.assertEqual(' '.join(self.argv), name) - self.assertFalse(remaining) + for scenario, argv in self.scenarios: + with self.subTest(scenario, argv=argv): + mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) + cmd, name, remaining = mgr.find_command(argv) + self.assertTrue(cmd) + self.assertEqual(' '.join(argv), name) + self.assertFalse(remaining) class TestLookupWithRemainder(base.TestBase): scenarios = [ - ('one', {'argv': ['one', '--opt']}), - ('two', {'argv': ['two', 'words', '--opt']}), - ('three', {'argv': ['three', 'word', 'command', '--opt']}), + ('one', ['one', '--opt']), + ('two', ['two', 'words', '--opt']), + ('three', ['three', 'word', 'command', '--opt']), ] - argv: list[str] - def test(self): - mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) - cmd, name, remaining = mgr.find_command(self.argv) - self.assertTrue(cmd) - self.assertEqual(['--opt'], remaining) + for scenario, argv in self.scenarios: + with self.subTest(scenario, argv=argv): + mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) + cmd, name, remaining = mgr.find_command(argv) + self.assertTrue(cmd) + self.assertEqual(['--opt'], remaining) class TestFindInvalidCommand(base.TestBase): scenarios = [ - ('no-such-command', {'argv': ['a', '-b']}), - ('no-command-given', {'argv': ['-b']}), + ('no-such-command', ['a', '-b']), + ('no-command-given', ['-b']), ] - argv: list[str] - def test(self): - mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) - try: - mgr.find_command(self.argv) - except ValueError as err: - # make sure err include 'a' when ['a', '-b'] - self.assertIn(self.argv[0], str(err)) - self.assertIn('-b', str(err)) - else: - self.fail('expected a failure') + for scenario, argv in self.scenarios: + with self.subTest(scenario, argv=argv): + mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) + try: + mgr.find_command(argv) + except ValueError as err: + # make sure err include 'a' when ['a', '-b'] + self.assertIn(argv[0], str(err)) + self.assertIn('-b', str(err)) + else: + self.fail('expected a failure') class TestFindUnknownCommand(base.TestBase): @@ -247,19 +242,19 @@ class TestLookupAndFindPartialName(base.TestBase): scenarios = [ - ('one-word', {'argv': ['o']}), - ('two-words', {'argv': ['t', 'w']}), - ('three-words', {'argv': ['t', 'w', 'c']}), + ('one-word', ['o']), + ('two-words', ['t', 'w']), + ('three-words', ['t', 'w', 'c']), ] - argv: list[str] - def test(self): - mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) - cmd, name, remaining = mgr.find_command(self.argv) - self.assertTrue(cmd) - self.assertEqual(' '.join(self.argv), name) - self.assertFalse(remaining) + for scenario, argv in self.scenarios: + with self.subTest(scenario, argv=argv): + mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) + cmd, name, remaining = mgr.find_command(argv) + self.assertTrue(cmd) + self.assertEqual(' '.join(argv), name) + self.assertFalse(remaining) class TestIsModuleIgnored(base.TestBase): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/tests/test_help.py new/cliff-4.14.0/cliff/tests/test_help.py --- old/cliff-4.13.3/cliff/tests/test_help.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/tests/test_help.py 2026-05-13 10:59:55.000000000 +0200 @@ -11,6 +11,7 @@ # under the License. import io +import re from unittest import mock @@ -194,3 +195,36 @@ self.assertIn('Commands:', help_output) self.assertIn('Could not load', help_output) self.assertIn('Exception: Could not load EntryPoint', help_output) + + def test_show_help_color_output_equivalent_to_plain(self): + """Colored help should be identical save for ANSI codes.""" + + def run_help(color): + output = io.StringIO() + pager = mock.MagicMock() + pager.to_terminal.return_value = color + pager.__enter__ = mock.Mock(return_value=output) + pager.__exit__ = mock.Mock(return_value=False) + app = application.App( + 'testing', + '1', + utils.TestCommandManager(utils.TEST_NAMESPACE), + stdout=io.StringIO(), + ) + app.NAME = 'test' + with mock.patch( + 'cliff.help.autopage.argparse.help_pager', return_value=pager + ): + with mock.patch( + 'cliff.help.autopage.argparse.use_color_for_parser' + ): + try: + app.run(['--help']) + except help.HelpExit: + pass + return output.getvalue() + + plain = run_help(color=False) + colored = run_help(color=True) + self.assertNotEqual(plain, colored) + self.assertEqual(plain, re.sub(r'\x1b\[[0-9;]*m', '', colored)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff/tests/test_lister.py new/cliff-4.14.0/cliff/tests/test_lister.py --- old/cliff-4.13.3/cliff/tests/test_lister.py 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/cliff/tests/test_lister.py 2026-05-13 10:59:55.000000000 +0200 @@ -11,7 +11,7 @@ # under the License. import argparse -import typing as ty +from typing import Any import weakref from unittest import mock @@ -34,7 +34,7 @@ class ExerciseLister(lister.Lister): - data: list[tuple[ty.Any, ty.Any]] = [('a', 'A'), ('b', 'B'), ('c', 'A')] + data: list[tuple[Any, Any]] = [('a', 'A'), ('b', 'B'), ('c', 'A')] def _load_formatter_plugins(self): return { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff.egg-info/PKG-INFO new/cliff-4.14.0/cliff.egg-info/PKG-INFO --- old/cliff-4.13.3/cliff.egg-info/PKG-INFO 2026-04-08 11:09:59.000000000 +0200 +++ new/cliff-4.14.0/cliff.egg-info/PKG-INFO 2026-05-13 11:00:23.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: cliff -Version: 4.13.3 +Version: 4.14.0 Summary: Command Line Interface Formulation Framework Author-email: OpenStack <[email protected]> License: Apache-2.0 @@ -22,7 +22,7 @@ Description-Content-Type: text/x-rst License-File: LICENSE Requires-Dist: autopage>=0.4.0 -Requires-Dist: cmd2>=1.0.0 +Requires-Dist: cmd2>=3.0.0 Requires-Dist: PrettyTable>=0.7.2 Requires-Dist: stevedore>=5.6.0 Requires-Dist: PyYAML>=3.12 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff.egg-info/pbr.json new/cliff-4.14.0/cliff.egg-info/pbr.json --- old/cliff-4.13.3/cliff.egg-info/pbr.json 2026-04-08 11:09:59.000000000 +0200 +++ new/cliff-4.14.0/cliff.egg-info/pbr.json 2026-05-13 11:00:23.000000000 +0200 @@ -1 +1 @@ -{"git_version": "9f0d93b", "is_release": true} \ No newline at end of file +{"git_version": "9e12d09", "is_release": true} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/cliff.egg-info/requires.txt new/cliff-4.14.0/cliff.egg-info/requires.txt --- old/cliff-4.13.3/cliff.egg-info/requires.txt 2026-04-08 11:09:59.000000000 +0200 +++ new/cliff-4.14.0/cliff.egg-info/requires.txt 2026-05-13 11:00:23.000000000 +0200 @@ -1,5 +1,5 @@ autopage>=0.4.0 -cmd2>=1.0.0 +cmd2>=3.0.0 PrettyTable>=0.7.2 stevedore>=5.6.0 PyYAML>=3.12 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/pyproject.toml new/cliff-4.14.0/pyproject.toml --- old/cliff-4.13.3/pyproject.toml 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/pyproject.toml 2026-05-13 10:59:55.000000000 +0200 @@ -75,6 +75,7 @@ show_column_numbers = true show_error_context = true strict = true +disable_error_code = ["import-untyped"] exclude = "(?x)(doc | demoapp | releasenotes)" [[tool.mypy.overrides]] @@ -91,7 +92,8 @@ docstring-code-format = true [tool.ruff.lint] -select = ["E4", "E5", "E7", "E9", "F", "S", "UP"] +select = ["E4", "E5", "E7", "E9", "F", "G", "LOG", "S", "UP"] +external = ["H"] [tool.ruff.lint.per-file-ignores] "cliff/tests/*" = ["S"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/requirements.txt new/cliff-4.14.0/requirements.txt --- old/cliff-4.13.3/requirements.txt 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/requirements.txt 2026-05-13 10:59:55.000000000 +0200 @@ -1,5 +1,5 @@ autopage>=0.4.0 # Apache 2.0 -cmd2>=1.0.0 # MIT +cmd2>=3.0.0 # MIT PrettyTable>=0.7.2 # BSD stevedore>=5.6.0 # Apache-2.0 PyYAML>=3.12 # MIT diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/test-requirements.txt new/cliff-4.14.0/test-requirements.txt --- old/cliff-4.13.3/test-requirements.txt 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/test-requirements.txt 2026-05-13 10:59:55.000000000 +0200 @@ -1,10 +1,6 @@ -stestr>=1.0.0 # Apache-2.0 -testtools>=2.2.0 # MIT -testscenarios>=0.4 # Apache-2.0/BSD fixtures>=3.0.0 # Apache-2.0/BSD - -coverage>=5.0 # Apache-2.0 - # sphinx is required in test-requirements in addition to doc/requirements # because there is a sphinx extension that has tests sphinx>=5.0.0 # BSD +stestr>=1.0.0 # Apache-2.0 +testtools>=2.2.0 # MIT diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cliff-4.13.3/tox.ini new/cliff-4.14.0/tox.ini --- old/cliff-4.13.3/tox.ini 2026-04-08 11:09:14.000000000 +0200 +++ new/cliff-4.14.0/tox.ini 2026-05-13 10:59:55.000000000 +0200 @@ -12,7 +12,7 @@ stestr run {posargs} stestr slowest deps = - -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2026.1} + -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt @@ -55,11 +55,14 @@ [testenv:docs] deps = - -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2026.1} + -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/doc/requirements.txt commands = sphinx-build -W -b html doc/source doc/build/html [testenv:cover] +deps = + {[testenv]deps} + coverage>=5.0 setenv = {[testenv]setenv} PYTHON=coverage run --source cliff --parallel-mode @@ -80,3 +83,8 @@ import-order-style = pep8 show-source = true exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build + +[hacking] +import_exceptions = + collections.abc + typing
