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
 

Reply via email to