Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package azure-cli-core for openSUSE:Factory 
checked in at 2024-09-06 17:18:55
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/azure-cli-core (Old)
 and      /work/SRC/openSUSE:Factory/.azure-cli-core.new.10096 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "azure-cli-core"

Fri Sep  6 17:18:55 2024 rev:72 rq:1199120 version:2.64.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/azure-cli-core/azure-cli-core.changes    
2024-08-08 10:59:12.121602364 +0200
+++ /work/SRC/openSUSE:Factory/.azure-cli-core.new.10096/azure-cli-core.changes 
2024-09-06 17:19:19.905852801 +0200
@@ -1,0 +2,8 @@
+Wed Sep  4 08:01:18 UTC 2024 - John Paul Adrian Glaubitz 
<adrian.glaub...@suse.com>
+
+- New upstream release
+  + Version 2.64.0
+  + For detailed information about changes see the
+    HISTORY.rst file provided with this package
+
+-------------------------------------------------------------------

Old:
----
  azure_cli_core-2.63.0.tar.gz

New:
----
  azure_cli_core-2.64.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ azure-cli-core.spec ++++++
--- /var/tmp/diff_new_pack.EHh0BB/_old  2024-09-06 17:19:20.365871917 +0200
+++ /var/tmp/diff_new_pack.EHh0BB/_new  2024-09-06 17:19:20.365871917 +0200
@@ -24,7 +24,7 @@
 %global _sitelibdir %{%{pythons}_sitelib}
 
 Name:           azure-cli-core
-Version:        2.63.0
+Version:        2.64.0
 Release:        0
 Summary:        Microsoft Azure CLI Core Module
 License:        MIT

++++++ azure_cli_core-2.63.0.tar.gz -> azure_cli_core-2.64.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.63.0/HISTORY.rst 
new/azure_cli_core-2.64.0/HISTORY.rst
--- old/azure_cli_core-2.63.0/HISTORY.rst       2024-07-31 05:39:17.000000000 
+0200
+++ new/azure_cli_core-2.64.0/HISTORY.rst       2024-08-28 07:44:58.000000000 
+0200
@@ -3,6 +3,10 @@
 Release History
 ===============
 
+2.64.0
+++++++
+* Minor fixes
+
 2.63.0
 ++++++
 * Resolve CVE-2024-39689 (#29320)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.63.0/PKG-INFO 
new/azure_cli_core-2.64.0/PKG-INFO
--- old/azure_cli_core-2.63.0/PKG-INFO  2024-07-31 05:39:33.541276200 +0200
+++ new/azure_cli_core-2.64.0/PKG-INFO  2024-08-28 07:45:13.234815400 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: azure-cli-core
-Version: 2.63.0
+Version: 2.64.0
 Summary: Microsoft Azure Command-Line Tools Core Module
 Home-page: https://github.com/Azure/azure-cli
 Author: Microsoft Corporation
@@ -15,6 +15,7 @@
 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: License :: OSI Approved :: MIT License
 Requires-Python: >=3.8.0
 License-File: LICENSE.txt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.63.0/azure/cli/core/__init__.py 
new/azure_cli_core-2.64.0/azure/cli/core/__init__.py
--- old/azure_cli_core-2.63.0/azure/cli/core/__init__.py        2024-07-31 
05:39:17.000000000 +0200
+++ new/azure_cli_core-2.64.0/azure/cli/core/__init__.py        2024-08-28 
07:44:58.000000000 +0200
@@ -4,7 +4,7 @@
 # 
--------------------------------------------------------------------------------------------
 # pylint: disable=line-too-long
 
-__version__ = "2.63.0"
+__version__ = "2.64.0"
 
 import os
 import sys
@@ -58,6 +58,7 @@
     def __init__(self, **kwargs):
         super(AzCli, self).__init__(**kwargs)
 
+        from azure.cli.core.breaking_change import 
register_upcoming_breaking_change_info
         from azure.cli.core.commands import register_cache_arguments
         from azure.cli.core.commands.arm import (
             register_ids_argument, register_global_subscription_argument)
@@ -90,6 +91,7 @@
         register_global_subscription_argument(self)
         register_ids_argument(self)  # global subscription must be registered 
first!
         register_cache_arguments(self)
+        register_upcoming_breaking_change_info(self)
 
         self.progress_controller = None
 
@@ -218,6 +220,7 @@
             _load_module_command_loader, _load_extension_command_loader, 
BLOCKED_MODS, ExtensionCommandSource)
         from azure.cli.core.extension import (
             get_extensions, get_extension_path, get_extension_modname)
+        from azure.cli.core.breaking_change import 
(import_module_breaking_changes, import_extension_breaking_changes)
 
         def _update_command_table_from_modules(args, command_modules=None):
             """Loads command tables from modules and merge into the main 
command table.
@@ -254,6 +257,7 @@
                 try:
                     start_time = timeit.default_timer()
                     module_command_table, module_group_table = 
_load_module_command_loader(self, args, mod)
+                    import_module_breaking_changes(mod)
                     for cmd in module_command_table.values():
                         cmd.command_source = mod
                     self.command_table.update(module_command_table)
@@ -349,6 +353,7 @@
                         start_time = timeit.default_timer()
                         extension_command_table, extension_group_table = \
                             _load_extension_command_loader(self, args, ext_mod)
+                        import_extension_breaking_changes(ext_mod)
 
                         for cmd_name, cmd in extension_command_table.items():
                             cmd.command_source = ExtensionCommandSource(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.63.0/azure/cli/core/_help.py 
new/azure_cli_core-2.64.0/azure/cli/core/_help.py
--- old/azure_cli_core-2.63.0/azure/cli/core/_help.py   2024-07-31 
05:39:17.000000000 +0200
+++ new/azure_cli_core-2.64.0/azure/cli/core/_help.py   2024-08-28 
07:44:58.000000000 +0200
@@ -250,6 +250,76 @@
         super(CliHelpFile, self).__init__(help_ctx, delimiters)
         self.links = []
 
+        from knack.deprecation import resolve_deprecate_info, 
ImplicitDeprecated, Deprecated
+        from azure.cli.core.breaking_change import UpcomingBreakingChangeTag, 
MergedStatusTag
+        direct_deprecate_info = None
+        breaking_changes = []
+        deprecate_info = resolve_deprecate_info(help_ctx.cli_ctx, delimiters)
+        if isinstance(deprecate_info, Deprecated):
+            direct_deprecate_info = deprecate_info
+        elif isinstance(deprecate_info, UpcomingBreakingChangeTag):
+            breaking_changes.append(deprecate_info)
+        # If there are more than two `deprecate_info` and/or upcoming breaking 
changes,
+        # extract them and store separately from the merged status tag.
+        elif isinstance(deprecate_info, MergedStatusTag):
+            depr, bcs = CliHelpFile.classify_merged_status_tag(deprecate_info)
+            direct_deprecate_info = depr[0] if depr else None
+            breaking_changes.extend(bcs)
+
+        # search for implicit deprecation
+        path_comps = delimiters.split()[:-1]
+        implicit_deprecate_info = None
+        while path_comps:
+            deprecate_info = resolve_deprecate_info(help_ctx.cli_ctx, ' 
'.join(path_comps))
+            if isinstance(deprecate_info, Deprecated) and 
implicit_deprecate_info is None:
+                implicit_deprecate_info = deprecate_info
+            elif isinstance(deprecate_info, UpcomingBreakingChangeTag):
+                breaking_changes.append(deprecate_info)
+            # If there are more than two `deprecate_info` and/or upcoming 
breaking changes,
+            # extract them and store separately from the merged status tag.
+            elif isinstance(deprecate_info, MergedStatusTag):
+                depr, bcs = 
CliHelpFile.classify_merged_status_tag(deprecate_info)
+                if depr and implicit_deprecate_info is None:
+                    implicit_deprecate_info = depr[0]
+                breaking_changes.extend(bcs)
+            del path_comps[-1]
+
+        if implicit_deprecate_info:
+            deprecate_kwargs = implicit_deprecate_info.__dict__.copy()
+            deprecate_kwargs['object_type'] = 'command' if delimiters in \
+                help_ctx.cli_ctx.invocation.commands_loader.command_table else 
'command group'
+            del deprecate_kwargs['_get_tag']
+            del deprecate_kwargs['_get_message']
+            self.deprecate_info = ImplicitDeprecated(cli_ctx=help_ctx.cli_ctx, 
**deprecate_kwargs)
+        else:
+            self.deprecate_info = direct_deprecate_info
+
+        all_deprecate_info = [self.deprecate_info] if self.deprecate_info else 
[]
+        all_deprecate_info.extend(breaking_changes)
+        if len(all_deprecate_info) > 1:
+            # Merge multiple `deprecate_info` and/or breaking changes so their 
messages can be displayed together.
+            self.deprecate_info = MergedStatusTag(help_ctx.cli_ctx, 
*all_deprecate_info)
+        elif all_deprecate_info:
+            self.deprecate_info = all_deprecate_info[0]
+
+    @staticmethod
+    def classify_merged_status_tag(merged_status_tag):
+        from knack.deprecation import Deprecated
+        from azure.cli.core.breaking_change import UpcomingBreakingChangeTag, 
MergedStatusTag
+
+        deprecate_info = []
+        breaking_changes = []
+        for tag in merged_status_tag.tags:
+            if isinstance(tag, Deprecated):
+                deprecate_info.append(tag)
+            elif isinstance(tag, UpcomingBreakingChangeTag):
+                breaking_changes.append(tag)
+            elif isinstance(tag, MergedStatusTag):
+                depr, bcs = CliHelpFile.classify_merged_status_tag(tag)
+                deprecate_info.extend(depr)
+                breaking_changes.extend(bcs)
+        return deprecate_info, breaking_changes
+
     def _should_include_example(self, ex):
         supported_profiles = ex.get('supported-profiles')
         unsupported_profiles = ex.get('unsupported-profiles')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_cli_core-2.63.0/azure/cli/core/breaking_change.py 
new/azure_cli_core-2.64.0/azure/cli/core/breaking_change.py
--- old/azure_cli_core-2.63.0/azure/cli/core/breaking_change.py 1970-01-01 
01:00:00.000000000 +0100
+++ new/azure_cli_core-2.64.0/azure/cli/core/breaking_change.py 2024-08-28 
07:44:58.000000000 +0200
@@ -0,0 +1,611 @@
+# 
--------------------------------------------------------------------------------------------
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License. See License.txt in the project root for 
license information.
+# 
--------------------------------------------------------------------------------------------
+import abc
+import argparse
+from collections import defaultdict
+
+from knack.log import get_logger
+from knack.deprecation import Deprecated
+from knack.util import StatusTag, color_map
+
+
+logger = get_logger()
+
+NEXT_BREAKING_CHANGE_RELEASE = '2.67.0'
+DEFAULT_BREAKING_CHANGE_TAG = '[Breaking Change]'
+
+
+def _get_action_class(cli_ctx, action):
+    action_class = argparse.Action
+
+    # action is either a user-defined Action class or a string referring a 
library-defined Action
+    if isinstance(action, type) and issubclass(action, argparse.Action):
+        action_class = action
+    elif isinstance(action, str):
+        action_class = 
cli_ctx.invocation.command_loader.cli_ctx.invocation.parser._registries['action'][action]
  # pylint: disable=protected-access
+    return action_class
+
+
+def _argument_breaking_change_action(cli_ctx, status_tag, action):
+
+    action_class = _get_action_class(cli_ctx, action)
+
+    class ArgumentBreakingChangeAction(action_class):
+
+        def __call__(self, parser, namespace, values, option_string=None):
+            if not hasattr(namespace, '_argument_deprecations'):
+                setattr(namespace, '_argument_deprecations', [])
+            if isinstance(status_tag, MergedStatusTag):
+                for tag in status_tag.tags:
+                    namespace._argument_deprecations.append(tag)
+            else:
+                if status_tag not in namespace._argument_deprecations:
+                    namespace._argument_deprecations.append(status_tag)
+            try:
+                super().__call__(parser, namespace, values, option_string)
+            except NotImplementedError:
+                setattr(namespace, self.dest, values)
+
+    return ArgumentBreakingChangeAction
+
+
+def _find_arg(arg_name, arguments):
+    if arg_name in arguments:
+        return arg_name, arguments[arg_name]
+    for key, argument in arguments.items():
+        for option in argument.options_list or []:
+            if arg_name == option or (isinstance(option, Deprecated) and 
arg_name == option.target):
+                return key, argument
+    trimmed_arg_name = arg_name.strip('-').replace('-', '_')
+    if trimmed_arg_name in arguments:
+        return trimmed_arg_name, arguments[trimmed_arg_name]
+    return None, None
+
+
+class UpcomingBreakingChangeTag(StatusTag):
+    def __init__(self, cli_ctx, object_type='', target=None, 
target_version=None, tag_func=None, message_func=None,
+                 always_display=False):
+        def _default_get_message(bc):
+            msg = f"A breaking change may occur to this {bc.object_type} "
+            if isinstance(target_version, TargetVersion):
+                msg += str(target_version) + '.'
+            elif isinstance(target_version, str):
+                msg += 'in ' + target_version + '.'
+            else:
+                msg += 'in future release.'
+                return msg
+
+        self.always_display = always_display
+        self.target_version = target_version
+        super().__init__(
+            cli_ctx=cli_ctx,
+            object_type=object_type,
+            target=target,
+            color=color_map['deprecation'],
+            tag_func=tag_func or (lambda _: DEFAULT_BREAKING_CHANGE_TAG),
+            message_func=message_func or _default_get_message
+        )
+
+    def expired(self):
+        return False
+
+
+class MergedStatusTag(StatusTag):
+    # This class is used to merge multiple status tags into one.
+    # It is particularly useful when multiple breaking changes and deprecation 
information need to be recorded
+    # in a single deprecate_info field.
+
+    def __init__(self, cli_ctx, *tags):
+        assert len(tags) > 0
+        tag = tags[0]
+        self.tags = list(tags)
+
+        def _get_merged_tag(self):
+            return ''.join({tag._get_tag(self) for tag in self.tags})  # 
pylint: disable=protected-access
+
+        def _get_merged_msg(self):
+            return '\n'.join({tag._get_message(self) for tag in self.tags})  # 
pylint: disable=protected-access
+
+        super().__init__(cli_ctx, tag.object_type, tag.target, 
tag_func=_get_merged_tag,
+                         message_func=_get_merged_msg, color=tag._color)
+
+    def merge(self, other):
+        self.tags.append(other)
+
+    def hidden(self):
+        return any(tag.hidden() for tag in self.tags)
+
+    def show_in_help(self):
+        return any(tag.show_in_help() for tag in self.tags)
+
+    def expired(self):
+        return any(tag.expired() for tag in self.tags)
+
+    @property
+    def tag(self):
+        return ''.join({str(tag.tag) for tag in self.tags})
+
+    @property
+    def message(self):
+        return '\n'.join({str(tag.message) for tag in self.tags})
+
+
+def _next_breaking_change_version():
+    return NEXT_BREAKING_CHANGE_RELEASE
+
+
+# pylint: disable=too-few-public-methods
+class TargetVersion(abc.ABC):
+    @abc.abstractmethod
+    def __str__(self):
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def version(self):
+        raise NotImplementedError()
+
+
+# pylint: disable=too-few-public-methods
+class NextBreakingChangeWindow(TargetVersion):
+    def __str__(self):
+        next_breaking_change_version = _next_breaking_change_version()
+        if next_breaking_change_version:
+            return f'in next breaking change 
release({next_breaking_change_version})'
+        return 'in next breaking change release'
+
+    def version(self):
+        return _next_breaking_change_version()
+
+
+# pylint: disable=too-few-public-methods
+class ExactVersion(TargetVersion):
+    def __init__(self, version):
+        self._version = version
+
+    def __str__(self):
+        return f'in {self._version}'
+
+    def version(self):
+        return self._version
+
+
+# pylint: disable=too-few-public-methods
+class UnspecificVersion(TargetVersion):
+    def __str__(self):
+        return 'in a future release'
+
+    def version(self):
+        return None
+
+
+class BreakingChange(abc.ABC):
+    def __init__(self, cmd, arg=None, target=None, target_version=None):
+        self.cmd = cmd
+        if isinstance(arg, str):
+            self.args = [arg]
+        elif isinstance(arg, list):
+            self.args = arg
+        else:
+            self.args = []
+        self.target = target if target else '/'.join(self.args) if self.args 
else self.cmd
+        if isinstance(target_version, TargetVersion):
+            self._target_version = target_version
+        elif isinstance(target_version, str):
+            self._target_version = ExactVersion(target_version)
+        else:
+            self._target_version = UnspecificVersion()
+
+    @property
+    def message(self):
+        return ''
+
+    @property
+    def target_version(self):
+        return self._target_version
+
+    @staticmethod
+    def format_doc_link(doc_link):
+        return f' To know more about the Breaking Change, please visit 
{doc_link}.' if doc_link else ''
+
+    @property
+    def command_name(self):
+        if self.cmd.startswith('az '):
+            return self.cmd[3:].strip()
+        return self.cmd
+
+    def is_command_group(self, cli_ctx):
+        return self.command_name in 
cli_ctx.invocation.commands_loader.command_group_table
+
+    def to_tag(self, cli_ctx, **kwargs):
+        if self.args:
+            object_type = 'argument'
+        elif self.is_command_group(cli_ctx):
+            object_type = 'command group'
+        else:
+            object_type = 'command'
+        tag_kwargs = {
+            'object_type': object_type,
+            'target': self.target,
+            'target_version': self.target_version,
+            'message_func': lambda _: self.message,
+        }
+        tag_kwargs.update(kwargs)
+        return UpcomingBreakingChangeTag(cli_ctx, **tag_kwargs)
+
+    def register(self, cli_ctx):
+        if self.args:
+            command = 
cli_ctx.invocation.commands_loader.command_table.get(self.command_name)
+            if not command:
+                return
+            for arg_name in self.args:
+                if self._register_option_deprecate(cli_ctx, command.arguments, 
arg_name):
+                    continue
+                arg_name, arg = _find_arg(arg_name, command.arguments)
+                if not arg:
+                    continue
+                arg.deprecate_info = self.appended_status_tag(cli_ctx, 
arg.deprecate_info, self.to_tag(cli_ctx))
+                arg.action = _argument_breaking_change_action(cli_ctx, 
arg.deprecate_info, arg.options['action'])
+        elif self.is_command_group(cli_ctx):
+            command_group = 
cli_ctx.invocation.commands_loader.command_group_table[self.command_name]
+            if not command_group:
+                self._register_to_direct_sub_cg_or_command(cli_ctx, 
self.command_name, self.to_tag(cli_ctx))
+            else:
+                command_group.group_kwargs['deprecate_info'] = \
+                    self.appended_status_tag(cli_ctx, 
command_group.group_kwargs.get('deprecate_info'),
+                                             self.to_tag(cli_ctx))
+        else:
+            command = 
cli_ctx.invocation.commands_loader.command_table.get(self.cmd)
+            if not command:
+                return
+            command.deprecate_info = self.appended_status_tag(cli_ctx, 
command.deprecate_info, self.to_tag(cli_ctx))
+
+    @staticmethod
+    def appended_status_tag(cli_ctx, old_status_tag, new_status_tag):
+        if isinstance(old_status_tag, (Deprecated, UpcomingBreakingChangeTag)):
+            return MergedStatusTag(cli_ctx, old_status_tag, new_status_tag)
+        if isinstance(old_status_tag, MergedStatusTag):
+            old_status_tag.merge(new_status_tag)
+            return old_status_tag
+        return new_status_tag
+
+    def _register_to_direct_sub_cg_or_command(self, cli_ctx, cg_name, 
status_tag):
+        for key, command_group in 
cli_ctx.invocation.commands_loader.command_group_table.items():
+            split_key = key.rsplit(maxsplit=1)
+            # If inpass command group name is empty, all first level command 
group should be registered to.
+            # Otherwise, we need to find all direct sub command groups.
+            if (not cg_name and len(split_key) == 1) or (len(split_key) == 2 
and split_key[0] == cg_name):
+                from azure.cli.core.commands import AzCommandGroup
+                if isinstance(command_group, AzCommandGroup):
+                    command_group.group_kwargs['deprecate_info'] = \
+                        self.appended_status_tag(cli_ctx, 
command_group.group_kwargs.get('deprecate_info'), status_tag)
+                else:
+                    self._register_to_direct_sub_cg_or_command(cli_ctx, key, 
status_tag)
+        for key, command in 
cli_ctx.invocation.commands_loader.command_table.items():
+            # If inpass command group name is empty, all first level command 
should be registered to.
+            if (not cg_name and ' ' not in key) or key.rsplit(maxsplit=1)[0] 
== cg_name:
+                command.deprecate_info = self.appended_status_tag(cli_ctx, 
command.deprecate_info, self.to_tag(cli_ctx))
+
+    def _register_option_deprecate(self, cli_ctx, arguments, option_name):
+        for _, argument in arguments.items():
+            if argument.options_list and len(argument.options_list) > 1:
+                for idx, option in enumerate(argument.options_list):
+                    if isinstance(option, str) and option_name == option and 
isinstance(self, AzCLIDeprecate):
+                        if isinstance(argument.options_list, tuple):
+                            # Some of the command would declare options_list 
as tuple
+                            argument.options_list = list(argument.options_list)
+                        argument.options_list[idx] = self.to_tag(cli_ctx, 
object_type='option')
+                        argument.options_list[idx].target = option
+                        argument.action = 
_argument_breaking_change_action(cli_ctx, argument.options_list[idx],
+                                                                           
argument.options['action'])
+                        return True
+        return False
+
+
+class AzCLIDeprecate(BreakingChange):
+    def __init__(self, cmd, arg=None, 
target_version=NextBreakingChangeWindow(), **kwargs):
+        super().__init__(cmd, arg, None, target_version)
+        self.kwargs = kwargs
+
+    @staticmethod
+    def _build_message(object_type, target, target_version, redirect):
+        if object_type in ['argument', 'option']:
+            msg = "{} '{}' has been deprecated and will be removed 
".format(object_type, target).capitalize()
+        elif object_type:
+            msg = "This {} has been deprecated and will be removed 
".format(object_type)
+        else:
+            msg = "'{}' has been deprecated and will be removed 
".format(target)
+        msg += str(target_version) + '.'
+        if redirect:
+            msg += " Use '{}' instead.".format(redirect)
+        return msg
+
+    @property
+    def message(self):
+        return self._build_message(self.kwargs.get('object_type'), 
self.target, self.target_version,
+                                   self.kwargs.get('redirect'))
+
+    def to_tag(self, cli_ctx, **kwargs):
+        if self.args:
+            object_type = 'argument'
+        elif self.is_command_group(cli_ctx):
+            object_type = 'command group'
+        else:
+            object_type = 'command'
+        tag_kwargs = {
+            'object_type': object_type,
+            'message_func': lambda depr: self._build_message(
+                depr.object_type, depr.target, self.target_version, 
depr.redirect),
+            'target': self.target
+        }
+        tag_kwargs.update(self.kwargs)
+        tag_kwargs.update(kwargs)
+        return Deprecated(cli_ctx, **tag_kwargs)
+
+
+class AzCLIRemoveChange(BreakingChange):
+    """
+    Remove the command groups, commands or arguments in a future release.
+
+    **It is recommended to utilize `deprecate_info` instead of this class to 
pre-announce Breaking Change of Removal.**
+    :param target: name of the removed command group, command or argument
+    :param target_version: version where the breaking change is expected to 
happen.
+    :type target_version: TargetVersion
+    :param redirect: alternative way to replace the old behavior
+    :param doc_link: link of the related document
+    """
+
+    def __init__(self, cmd, arg=None, 
target_version=NextBreakingChangeWindow(), target=None, redirect=None,
+                 doc_link=None):
+        super().__init__(cmd, arg, target, target_version)
+        self.alter = redirect
+        self.doc_link = doc_link
+
+    @property
+    def message(self):
+        alter = f" Please use '{self.alter}' instead." if self.alter else ''
+        doc = self.format_doc_link(self.doc_link)
+        return f"'{self.target}' will be removed 
{str(self._target_version)}.{alter}{doc}"
+
+
+class AzCLIRenameChange(BreakingChange):
+    """
+    Rename the command groups, commands or arguments to a new name in a future 
release.
+
+    **It is recommended to utilize `deprecate_info` instead of this class to 
pre-announce Breaking Change of Renaming.**
+    It is recommended that the old name and the new name should be reserved in 
few releases.
+    :param target: name of the renamed command group, command or argument
+    :param new_name: new name
+    :param target_version: version where the breaking change is expected to 
happen.
+    :type target_version: TargetVersion
+    :param doc_link: link of the related document
+    """
+
+    def __init__(self, cmd, new_name, arg=None, target=None, 
target_version=NextBreakingChangeWindow(), doc_link=None):
+        super().__init__(cmd, arg, target, target_version)
+        self.new_name = new_name
+        self.doc_link = doc_link
+
+    @property
+    def message(self):
+        doc = self.format_doc_link(self.doc_link)
+        return f"'{self.target}' will be renamed to '{self.new_name}' 
{str(self._target_version)}.{doc}"
+
+
+class AzCLIOutputChange(BreakingChange):
+    """
+    The output of the command will be changed in a future release.
+    :param description: describe the changes in output
+    :param target_version: version where the breaking change is expected to 
happen.
+    :type target_version: TargetVersion
+    :param guide: how to adapt to the change
+    :param doc_link: link of the related document
+    """
+
+    def __init__(self, cmd, description: str, 
target_version=NextBreakingChangeWindow(), guide=None, doc_link=None):
+        super().__init__(cmd, None, None, target_version)
+        self.desc = description
+        self.guide = guide
+        self.doc_link = doc_link
+
+    @property
+    def message(self):
+        desc = self.desc.rstrip()
+        if desc and desc[-1] not in ',.;?!':
+            desc = desc + '.'
+        if self.guide:
+            guide = self.guide.rstrip()
+            if guide and guide[-1] not in ',.;?!':
+                guide = ' ' + guide + '.'
+        else:
+            guide = ''
+        doc = self.format_doc_link(self.doc_link)
+        return f'The output will be changed {str(self.target_version)}. 
{desc}{guide}{doc}'
+
+
+class AzCLILogicChange(BreakingChange):
+    """
+    There would be a breaking change in the logic of the command in future 
release.
+    :param summary: a short summary about the breaking change
+    :param target_version: version where the breaking change is expected to 
happen.
+    :type target_version: TargetVersion
+    :param detail: detailed information
+    :param doc_link: link of the related document
+    """
+
+    def __init__(self, cmd, summary, 
target_version=NextBreakingChangeWindow(), detail=None, doc_link=None):
+        super().__init__(cmd, None, None, target_version)
+        self.summary = summary
+        self.detail = detail
+        self.doc_link = doc_link
+
+    @property
+    def message(self):
+        detail = f' {self.detail}' if self.detail else ''
+        return f'{self.summary} 
{str(self.target_version)}.{detail}{self.format_doc_link(self.doc_link)}'
+
+
+class AzCLIDefaultChange(BreakingChange):
+    """
+    The default value of an argument would be changed in a future release.
+    :param target: name of the related argument
+    :param current_default: current default value of the argument
+    :param new_default: new default value of the argument
+    :param target_version: version where the breaking change is expected to 
happen.
+    :type target_version: TargetVersion
+    :param doc_link: link of the related document
+    """
+
+    def __init__(self, cmd, arg, current_default, new_default, 
target_version=NextBreakingChangeWindow(),
+                 target=None, doc_link=None):
+        super().__init__(cmd, arg, target, target_version)
+        self.current_default = current_default
+        self.new_default = new_default
+        self.doc_link = doc_link
+
+    @property
+    def message(self):
+        doc = self.format_doc_link(self.doc_link)
+        return (f"The default value of '{self.target}' will be changed to 
'{self.new_default}' from "
+                f"'{self.current_default}' {str(self._target_version)}.{doc}")
+
+    def to_tag(self, cli_ctx, **kwargs):
+        if 'always_display' not in kwargs:
+            kwargs['always_display'] = True
+        return super().to_tag(cli_ctx, **kwargs)
+
+
+class AzCLIBeRequired(BreakingChange):
+    """
+    The argument would become required in a future release.
+    :param target: name of the related argument
+    :param target_version: version where the breaking change is expected to 
happen.
+    :type target_version: TargetVersion
+    :param doc_link: link of the related document
+    """
+
+    def __init__(self, cmd, arg, target_version=NextBreakingChangeWindow(), 
target=None, doc_link=None):
+        super().__init__(cmd, arg, target, target_version)
+        self.doc_link = doc_link
+
+    @property
+    def message(self):
+        doc = self.format_doc_link(self.doc_link)
+        return f"The argument '{self.target}' will become required 
{str(self._target_version)}.{doc}"
+
+    def to_tag(self, cli_ctx, **kwargs):
+        if 'always_display' not in kwargs:
+            kwargs['always_display'] = True
+        return super().to_tag(cli_ctx, **kwargs)
+
+
+class AzCLIOtherChange(BreakingChange):
+    """
+    Other custom breaking changes.
+    :param message: A description of the breaking change, including the 
version number where it is expected to occur.
+    :param target_version: version where the breaking change is expected to 
happen.
+    :type target_version: TargetVersion
+    """
+
+    def __init__(self, cmd, message, arg=None, 
target_version=NextBreakingChangeWindow()):
+        super().__init__(cmd, arg, None, target_version)
+        self._message = message
+
+    @property
+    def message(self):
+        return self._message
+
+
+upcoming_breaking_changes = defaultdict(lambda: [])
+
+
+def import_module_breaking_changes(mod):
+    try:
+        from importlib import import_module
+        import_module('azure.cli.command_modules.' + mod + '._breaking_change')
+    except ImportError:
+        pass
+
+
+def import_extension_breaking_changes(ext_mod):
+    try:
+        from importlib import import_module
+        import_module(ext_mod + '._breaking_change')
+    except ImportError:
+        pass
+
+
+def register_upcoming_breaking_change_info(cli_ctx):
+    from knack import events
+
+    def update_breaking_change_info(cli_ctx, **kwargs):  # pylint: 
disable=unused-argument
+        for key, breaking_changes in upcoming_breaking_changes.items():
+            # Conditional Breaking Changes are announced with key 
`CommandName.Tag`. They should not be registered.
+            if '.' in key:
+                continue
+            for breaking_change in breaking_changes:
+                breaking_change.register(cli_ctx)
+
+    cli_ctx.register_event(events.EVENT_INVOKER_POST_CMD_TBL_CREATE, 
update_breaking_change_info)
+
+
+def register_deprecate_info(command_name, arg=None, 
target_version=NextBreakingChangeWindow(), **kwargs):
+    
upcoming_breaking_changes[command_name].append(AzCLIDeprecate(command_name, 
arg, target_version, **kwargs))
+
+
+def register_output_breaking_change(command_name, description, 
target_version=NextBreakingChangeWindow(), guide=None,
+                                    doc_link=None):
+    upcoming_breaking_changes[command_name].append(
+        AzCLIOutputChange(command_name, description, target_version, guide, 
doc_link))
+
+
+def register_logic_breaking_change(command_name, summary, 
target_version=NextBreakingChangeWindow(), detail=None,
+                                   doc_link=None):
+    upcoming_breaking_changes[command_name].append(
+        AzCLILogicChange(command_name, summary, target_version, detail, 
doc_link))
+
+
+def register_default_value_breaking_change(command_name, arg, current_default, 
new_default,
+                                           
target_version=NextBreakingChangeWindow(), target=None, doc_link=None):
+    upcoming_breaking_changes[command_name].append(
+        AzCLIDefaultChange(command_name, arg, current_default, new_default, 
target_version, target, doc_link))
+
+
+def register_required_flag_breaking_change(command_name, arg, 
target_version=NextBreakingChangeWindow(), target=None,
+                                           doc_link=None):
+    
upcoming_breaking_changes[command_name].append(AzCLIBeRequired(command_name, 
arg, target_version, target, doc_link))
+
+
+def register_other_breaking_change(command_name, message, arg=None, 
target_version=NextBreakingChangeWindow()):
+    
upcoming_breaking_changes[command_name].append(AzCLIOtherChange(command_name, 
message, arg, target_version))
+
+
+def register_command_group_deprecate(command_group, redirect=None, hide=None,
+                                     
target_version=NextBreakingChangeWindow(), **kwargs):
+    register_deprecate_info(command_group, redirect=redirect, hide=hide, 
target_version=target_version, **kwargs)
+
+
+def register_command_deprecate(command, redirect=None, hide=None,
+                               target_version=NextBreakingChangeWindow(), 
**kwargs):
+    register_deprecate_info(command, redirect=redirect, hide=hide, 
target_version=target_version, **kwargs)
+
+
+def register_argument_deprecate(command, argument, redirect=None, hide=None,
+                                target_version=NextBreakingChangeWindow(), 
**kwargs):
+    register_deprecate_info(command, argument, redirect=redirect, hide=hide, 
target_version=target_version, **kwargs)
+
+
+def register_conditional_breaking_change(tag, breaking_change):
+    upcoming_breaking_changes[breaking_change.command_name + '.' + 
tag].append(breaking_change)
+
+
+def print_conditional_breaking_change(cli_ctx, tag, custom_logger=None):
+    command = cli_ctx.invocation.commands_loader.command_name
+    custom_logger = custom_logger or logger
+
+    command_comps = command.split()
+    while command_comps:
+        for breaking_change in upcoming_breaking_changes.get(' 
'.join(command_comps) + '.' + tag, []):
+            custom_logger.warning(breaking_change.message)
+        del command_comps[-1]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_cli_core-2.63.0/azure/cli/core/commands/__init__.py 
new/azure_cli_core-2.64.0/azure/cli/core/commands/__init__.py
--- old/azure_cli_core-2.63.0/azure/cli/core/commands/__init__.py       
2024-07-31 05:39:17.000000000 +0200
+++ new/azure_cli_core-2.64.0/azure/cli/core/commands/__init__.py       
2024-08-28 07:44:58.000000000 +0200
@@ -14,8 +14,10 @@
 import sys
 import time
 import copy
+from collections import OrderedDict
 from importlib import import_module
 
+from azure.cli.core.breaking_change import UpcomingBreakingChangeTag, 
MergedStatusTag
 # pylint: disable=unused-import
 from azure.cli.core.commands.constants import (
     BLOCKED_MODS, DEFAULT_QUERY_TIME_RANGE, CLI_COMMON_KWARGS, 
CLI_COMMAND_KWARGS, CLI_PARAM_KWARGS,
@@ -31,7 +33,7 @@
 
 from knack.arguments import CLICommandArgument
 from knack.commands import CLICommand, CommandGroup, 
PREVIEW_EXPERIMENTAL_CONFLICT_ERROR
-from knack.deprecation import ImplicitDeprecated, resolve_deprecate_info
+from knack.deprecation import ImplicitDeprecated, resolve_deprecate_info, 
Deprecated
 from knack.invocation import CommandInvoker
 from knack.preview import ImplicitPreviewItem, PreviewItem, 
resolve_preview_info
 from knack.experimental import ImplicitExperimentalItem, ExperimentalItem, 
resolve_experimental_info
@@ -751,15 +753,36 @@
         self._resolve_extension_override_warning(cmd)
 
     def _resolve_preview_and_deprecation_warnings(self, cmd, parsed_args):
-        deprecations = [] + getattr(parsed_args, '_argument_deprecations', [])
+        deprecations = getattr(parsed_args, '_argument_deprecations', [])
+        # Handle `always_display` argument breaking changes
+        for _, argument in parsed_args.func.arguments.items():
+            # Some arguments have breaking changes that must always be 
displayed.
+            # Iterate through them and show the warnings.
+            if isinstance(argument.deprecate_info, UpcomingBreakingChangeTag):
+                if argument.deprecate_info.always_display:
+                    deprecations.append(argument.deprecate_info)
+            elif isinstance(argument.deprecate_info, MergedStatusTag):
+                for deprecation in argument.deprecate_info.tags:
+                    if isinstance(deprecation, UpcomingBreakingChangeTag) and 
deprecation.always_display:
+                        deprecations.append(deprecation)
+        # Dedup the deprecations
+        # If an argument has multiple breaking changes or deprecations,
+        # duplicated deprecations would be produced due to the inherent logic 
of action
+        deprecations = list(OrderedDict.fromkeys(deprecations))
         if cmd.deprecate_info:
             deprecations.append(cmd.deprecate_info)
 
         # search for implicit deprecation
         path_comps = cmd.name.split()[:-1]
         implicit_deprecate_info = None
-        while path_comps and not implicit_deprecate_info:
-            implicit_deprecate_info = resolve_deprecate_info(self.cli_ctx, ' 
'.join(path_comps))
+        while path_comps:
+            deprecate_info = resolve_deprecate_info(self.cli_ctx, ' 
'.join(path_comps))
+            if isinstance(deprecate_info, Deprecated) and 
implicit_deprecate_info is None:
+                implicit_deprecate_info = deprecate_info
+            elif isinstance(deprecate_info, UpcomingBreakingChangeTag):
+                deprecations.append(deprecate_info)
+            elif isinstance(deprecate_info, MergedStatusTag):
+                deprecations.extend(deprecate_info.tags)
             del path_comps[-1]
 
         if implicit_deprecate_info:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_cli_core-2.63.0/azure/cli/core/profiles/_shared.py 
new/azure_cli_core-2.64.0/azure/cli/core/profiles/_shared.py
--- old/azure_cli_core-2.63.0/azure/cli/core/profiles/_shared.py        
2024-07-31 05:39:17.000000000 +0200
+++ new/azure_cli_core-2.64.0/azure/cli/core/profiles/_shared.py        
2024-08-28 07:44:58.000000000 +0200
@@ -156,7 +156,7 @@
     'latest': {
         ResourceType.MGMT_STORAGE: '2023-05-01',
         ResourceType.MGMT_NETWORK: '2022-01-01',
-        ResourceType.MGMT_COMPUTE: SDKProfile('2024-03-01', {
+        ResourceType.MGMT_COMPUTE: SDKProfile('2024-07-01', {
             'resource_skus': '2019-04-01',
             'disks': '2023-04-02',
             'disk_encryption_sets': '2022-03-02',
@@ -168,7 +168,7 @@
             'gallery_applications': '2021-07-01',
             'gallery_application_versions': '2022-01-03',
             'shared_galleries': '2022-01-03',
-            'virtual_machine_scale_sets': '2024-03-01',
+            'virtual_machine_scale_sets': '2024-07-01',
         }),
         ResourceType.MGMT_RESOURCE_FEATURES: '2021-07-01',
         ResourceType.MGMT_RESOURCE_LINKS: '2016-09-01',
@@ -215,7 +215,7 @@
         ResourceType.DATA_STORAGE: '2018-11-09',
         ResourceType.DATA_STORAGE_BLOB: '2022-11-02',
         ResourceType.DATA_STORAGE_FILEDATALAKE: '2021-08-06',
-        ResourceType.DATA_STORAGE_FILESHARE: '2022-11-02',
+        ResourceType.DATA_STORAGE_FILESHARE: '2024-08-04',
         ResourceType.DATA_STORAGE_QUEUE: '2018-03-28',
         ResourceType.DATA_COSMOS_TABLE: '2017-04-17',
         ResourceType.MGMT_SERVICEBUS: '2022-10-01-preview',
@@ -258,7 +258,7 @@
         ResourceType.MGMT_IOTHUB: '2023-06-30-preview',
         ResourceType.MGMT_IOTDPS: '2021-10-15',
         ResourceType.MGMT_IOTCENTRAL: '2021-11-01-preview',
-        ResourceType.MGMT_ARO: '2023-09-04',
+        ResourceType.MGMT_ARO: '2023-11-22',
         ResourceType.MGMT_DATABOXEDGE: '2021-02-01-preview',
         ResourceType.MGMT_CUSTOMLOCATION: '2021-03-15-preview',
         ResourceType.MGMT_CONTAINERSERVICE: SDKProfile('2024-05-01'),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.63.0/azure/cli/core/util.py 
new/azure_cli_core-2.64.0/azure/cli/core/util.py
--- old/azure_cli_core-2.63.0/azure/cli/core/util.py    2024-07-31 
05:39:17.000000000 +0200
+++ new/azure_cli_core-2.64.0/azure/cli/core/util.py    2024-08-28 
07:44:58.000000000 +0200
@@ -1356,3 +1356,17 @@
     encrypt = cli_ctx.config.getboolean('core', 'encrypt_token_cache', 
fallback=fallback)
 
     return encrypt
+
+
+def run_cmd(args, *, capture_output=False, timeout=None, check=False, 
encoding=None, env=None):
+    """Run command in a subprocess. It reduces (not eliminates) shell 
injection by forcing args to be a list
+    and shell=False. Other arguments are keyword-only. For their 
documentation, see
+    https://docs.python.org/3/library/subprocess.html#subprocess.run
+    """
+    if not isinstance(args, list):
+        from azure.cli.core.azclierror import ArgumentUsageError
+        raise ArgumentUsageError("Invalid args. run_cmd args must be a list")
+
+    import subprocess
+    return subprocess.run(args, capture_output=capture_output, 
timeout=timeout, check=check,
+                          encoding=encoding, env=env)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_cli_core-2.63.0/azure_cli_core.egg-info/PKG-INFO 
new/azure_cli_core-2.64.0/azure_cli_core.egg-info/PKG-INFO
--- old/azure_cli_core-2.63.0/azure_cli_core.egg-info/PKG-INFO  2024-07-31 
05:39:33.000000000 +0200
+++ new/azure_cli_core-2.64.0/azure_cli_core.egg-info/PKG-INFO  2024-08-28 
07:45:13.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: azure-cli-core
-Version: 2.63.0
+Version: 2.64.0
 Summary: Microsoft Azure Command-Line Tools Core Module
 Home-page: https://github.com/Azure/azure-cli
 Author: Microsoft Corporation
@@ -15,6 +15,7 @@
 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: License :: OSI Approved :: MIT License
 Requires-Python: >=3.8.0
 License-File: LICENSE.txt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_cli_core-2.63.0/azure_cli_core.egg-info/SOURCES.txt 
new/azure_cli_core-2.64.0/azure_cli_core.egg-info/SOURCES.txt
--- old/azure_cli_core-2.63.0/azure_cli_core.egg-info/SOURCES.txt       
2024-07-31 05:39:33.000000000 +0200
+++ new/azure_cli_core-2.64.0/azure_cli_core.egg-info/SOURCES.txt       
2024-08-28 07:45:13.000000000 +0200
@@ -19,6 +19,7 @@
 azure/cli/core/api.py
 azure/cli/core/azclierror.py
 azure/cli/core/azlogging.py
+azure/cli/core/breaking_change.py
 azure/cli/core/cloud.py
 azure/cli/core/command_recommender.py
 azure/cli/core/credential_helper.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.63.0/setup.py 
new/azure_cli_core-2.64.0/setup.py
--- old/azure_cli_core-2.63.0/setup.py  2024-07-31 05:39:17.000000000 +0200
+++ new/azure_cli_core-2.64.0/setup.py  2024-08-28 07:44:58.000000000 +0200
@@ -8,7 +8,7 @@
 from codecs import open
 from setuptools import setup, find_packages
 
-VERSION = "2.63.0"
+VERSION = "2.64.0"
 
 # If we have source, validate that our version numbers match
 # This should prevent uploading releases with mismatched versions.
@@ -39,6 +39,7 @@
     'Programming Language :: Python :: 3.9',
     'Programming Language :: Python :: 3.10',
     'Programming Language :: Python :: 3.11',
+    'Programming Language :: Python :: 3.12',
     'License :: OSI Approved :: MIT License',
 ]
 

Reply via email to