Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-knack for openSUSE:Factory checked in at 2026-05-18 17:47:03 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-knack (Old) and /work/SRC/openSUSE:Factory/.python-knack.new.1966 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-knack" Mon May 18 17:47:03 2026 rev:25 rq:1353630 version:0.14.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-knack/python-knack.changes 2025-11-04 18:42:12.753062081 +0100 +++ /work/SRC/openSUSE:Factory/.python-knack.new.1966/python-knack.changes 2026-05-18 17:47:20.002608301 +0200 @@ -1,0 +2,9 @@ +Sun May 17 21:33:42 UTC 2026 - Dirk Müller <[email protected]> + +- update to 0.14.0: + * Declare support for Python 3.14 and drop support for Python + 3.9 + * Fix help text rendering for Python 3.14 argparse strict + validation + +------------------------------------------------------------------- Old: ---- knack-0.13.0.tar.gz New: ---- knack-0.14.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-knack.spec ++++++ --- /var/tmp/diff_new_pack.oaOE8B/_old 2026-05-18 17:47:20.682636401 +0200 +++ /var/tmp/diff_new_pack.oaOE8B/_new 2026-05-18 17:47:20.682636401 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-knack # -# Copyright (c) 2024 SUSE LLC +# Copyright (c) 2026 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-knack -Version: 0.13.0 +Version: 0.14.0 Release: 0 Summary: A Command-Line Interface framework License: MIT ++++++ knack-0.13.0.tar.gz -> knack-0.14.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/knack-0.13.0/HISTORY.rst new/knack-0.14.0/HISTORY.rst --- old/knack-0.13.0/HISTORY.rst 2025-10-22 09:49:15.000000000 +0200 +++ new/knack-0.14.0/HISTORY.rst 2026-05-13 10:14:45.000000000 +0200 @@ -3,6 +3,12 @@ Release History =============== +0.14.0 +++++++ + +* Declare support for Python 3.14 and drop support for Python 3.9 (#296) +* Fix help text rendering for Python 3.14 argparse strict validation (#300) + 0.13.0 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/knack-0.13.0/PKG-INFO new/knack-0.14.0/PKG-INFO --- old/knack-0.13.0/PKG-INFO 2025-10-22 09:50:40.741148500 +0200 +++ new/knack-0.14.0/PKG-INFO 2026-05-13 10:16:00.004198000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: knack -Version: 0.13.0 +Version: 0.14.0 Summary: A Command-Line Interface framework Home-page: https://github.com/microsoft/knack Author: Microsoft Corporation @@ -11,11 +11,11 @@ Classifier: Intended Audience :: System Administrators Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 Classifier: License :: OSI Approved :: MIT License License-File: LICENSE Requires-Dist: argcomplete diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/knack-0.13.0/knack/help.py new/knack-0.14.0/knack/help.py --- old/knack-0.13.0/knack/help.py 2025-10-22 09:49:15.000000000 +0200 +++ new/knack-0.14.0/knack/help.py 2026-05-13 10:14:45.000000000 +0200 @@ -280,7 +280,7 @@ 'deprecate_info': getattr(action, 'deprecate_info', None), 'preview_info': getattr(action, 'preview_info', None), 'experimental_info': getattr(action, 'experimental_info', None), - 'description': action.help, + 'description': self._expand_action_help(action), 'choices': action.choices, 'required': False, 'default': None, @@ -291,9 +291,28 @@ help_param = next(p for p in self.parameters if p.name == '--help -h') help_param.group_name = 'Global Arguments' + @staticmethod + def _expand_action_help(action): + """Expand argparse-style help placeholders for Knack-rendered help.""" + if not isinstance(action.help, str) or '%' not in action.help: + return action.help + + parser = getattr(action.container, '_parser', None) + prog = getattr(parser, 'prog', '') + params = dict(vars(action), prog=prog) + for key in list(params): + if params[key] is argparse.SUPPRESS: + del params[key] + + try: + return action.help % params + except (KeyError, TypeError, ValueError): + # Keep help resilient even when token expansion cannot be resolved. + return action.help.replace('%%', '%') + def _add_parameter_help(self, param): param_kwargs = { - 'description': param.help, + 'description': self._expand_action_help(param), 'choices': param.choices, 'required': param.required, 'default': param.default, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/knack-0.13.0/knack/parser.py new/knack-0.14.0/knack/parser.py --- old/knack-0.13.0/knack/parser.py 2025-10-22 09:49:15.000000000 +0200 +++ new/knack-0.14.0/knack/parser.py 2026-05-13 10:14:45.000000000 +0200 @@ -33,6 +33,21 @@ class CLICommandParser(argparse.ArgumentParser): @staticmethod + def _sanitize_help_for_argparse(help_text): + """Escape literal '%' for argparse unless the author already escaped. + + If a help string already contains ``%(...)`` placeholders or ``%%``, + keep it as-is and rely on argparse's native formatting behavior. + Otherwise, escape ``%`` so literal percent tokens don't break help + processing in newer Python versions. + """ + if not isinstance(help_text, str) or '%' not in help_text: + return help_text + if '%(' in help_text or '%%' in help_text: + return help_text + return help_text.replace('%', '%%') + + @staticmethod def create_global_parser(cli_ctx=None): global_parser = argparse.ArgumentParser(prog=cli_ctx.name, add_help=False) arg_group = global_parser.add_argument_group('global', 'Global Arguments') @@ -43,6 +58,8 @@ def _add_argument(obj, arg): """ Only pass valid argparse kwargs to argparse.ArgumentParser.add_argument """ argparse_options = {name: value for name, value in arg.options.items() if name in ARGPARSE_SUPPORTED_KWARGS} + if 'help' in argparse_options: + argparse_options['help'] = CLICommandParser._sanitize_help_for_argparse(argparse_options['help']) if arg.options_list: scrubbed_options_list = [] for item in arg.options_list: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/knack-0.13.0/knack.egg-info/PKG-INFO new/knack-0.14.0/knack.egg-info/PKG-INFO --- old/knack-0.13.0/knack.egg-info/PKG-INFO 2025-10-22 09:50:40.000000000 +0200 +++ new/knack-0.14.0/knack.egg-info/PKG-INFO 2026-05-13 10:15:59.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: knack -Version: 0.13.0 +Version: 0.14.0 Summary: A Command-Line Interface framework Home-page: https://github.com/microsoft/knack Author: Microsoft Corporation @@ -11,11 +11,11 @@ Classifier: Intended Audience :: System Administrators Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 Classifier: License :: OSI Approved :: MIT License License-File: LICENSE Requires-Dist: argcomplete diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/knack-0.13.0/setup.py new/knack-0.14.0/setup.py --- old/knack-0.13.0/setup.py 2025-10-22 09:49:15.000000000 +0200 +++ new/knack-0.14.0/setup.py 2026-05-13 10:14:45.000000000 +0200 @@ -8,7 +8,7 @@ import sys from setuptools import setup -VERSION = '0.13.0' +VERSION = '0.14.0' DEPENDENCIES = [ 'argcomplete', @@ -38,11 +38,11 @@ 'Intended Audience :: System Administrators', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.13', + 'Programming Language :: Python :: 3.14', 'License :: OSI Approved :: MIT License', ], packages=['knack', 'knack.testsdk'], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/knack-0.13.0/tests/test_help.py new/knack-0.14.0/tests/test_help.py --- old/knack-0.13.0/tests/test_help.py 2025-10-22 09:49:15.000000000 +0200 +++ new/knack-0.14.0/tests/test_help.py 2026-05-13 10:14:45.000000000 +0200 @@ -69,6 +69,9 @@ g.command('n3', 'example_handler') g.command('n4', 'example_handler') g.command('n5', 'example_handler') + g.command('n6', 'example_handler') + g.command('n7', 'example_handler') + g.command('n8', 'example_handler') with CommandGroup(self, 'group alpha', '{}#{{}}'.format(__name__)) as g: g.command('n1', 'example_handler') @@ -90,6 +93,16 @@ c.argument('arg2', options_list=['--foobar2'], required=True) c.argument('arg3', options_list=['--foobar3'], help='the foobar3') + with ArgumentsContext(self, 'n6') as c: + c.argument('arg1', options_list=['--fmt'], default='my-default', + help='default=%(default)s prog=%(prog)s') + + with ArgumentsContext(self, 'n7') as c: + c.argument('arg1', options_list=['--pct'], help='ratio 100%%') + + with ArgumentsContext(self, 'n8') as c: + c.argument('arg1', options_list=['--bad'], help='bad % token') + super().load_arguments(command) helps['n2'] = """ @@ -401,6 +414,39 @@ self.assertEqual(actual, expected) @redirect_io + def test_help_argparse_default_and_prog_placeholders(self): + """Ensure argparse placeholders are expanded in help text.""" + + with self.assertRaises(SystemExit): + self.cli_ctx.invoke('n6 -h'.split()) + + actual = self.io.getvalue() + self.assertIn('Default=my-default prog=', actual) + self.assertNotIn('%(default)s', actual) + self.assertNotIn('%(prog)s', actual) + + @redirect_io + def test_help_argparse_escaped_percent(self): + """Ensure escaped percent signs render as a single literal percent.""" + + with self.assertRaises(SystemExit): + self.cli_ctx.invoke('n7 -h'.split()) + + actual = self.io.getvalue() + self.assertIn('Ratio 100%.', actual) + self.assertNotIn('Ratio 100%%.', actual) + + @redirect_io + def test_help_argparse_bad_percent_falls_back(self): + """Ensure malformed formatting falls back to the original help text.""" + + with self.assertRaises(SystemExit): + self.cli_ctx.invoke('n8 -h'.split()) + + actual = self.io.getvalue() + self.assertIn('Bad % token.', actual) + + @redirect_io @mock.patch('knack.cli.CLI.register_event') def test_help_global_params(self, _): """ Ensure global parameters can be added and display correctly. """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/knack-0.13.0/tests/test_parser.py new/knack-0.14.0/tests/test_parser.py --- old/knack-0.13.0/tests/test_parser.py 2025-10-22 09:49:15.000000000 +0200 +++ new/knack-0.14.0/tests/test_parser.py 2026-05-13 10:14:45.000000000 +0200 @@ -180,6 +180,22 @@ remove_test_file('test.json') + def test_help_string_with_literal_percent_does_not_crash(self): + def test_handler(): + pass + + command = CLICommand(self.mock_ctx, 'test command', test_handler) + command.add_argument('date_fmt', '--date-fmt', help='Expected format: %Y-%m-%d') + cmd_table = {'test command': command} + self.mock_ctx.commands_loader.command_table = cmd_table + + parser = CLICommandParser() + parser.load_command_table(self.mock_ctx.commands_loader) + + def test_help_string_preserves_argparse_placeholders(self): + sanitized = CLICommandParser._sanitize_help_for_argparse('default is %(default)s (100%% expected)') + self.assertEqual(sanitized, 'default is %(default)s (100%% expected)') + class VerifyError(object): # pylint: disable=too-few-public-methods
