diff --git a/src/azure-cli-core/azure/cli/core/__init__.py b/src/azure-cli-core/azure/cli/core/__init__.py index c4dfc7f8b22..fb2f2ad3af3 100644 --- a/src/azure-cli-core/azure/cli/core/__init__.py +++ b/src/azure-cli-core/azure/cli/core/__init__.py @@ -622,9 +622,9 @@ def __init__(self, mod_name, suppress_extension_name, suppress_up_to_version, re self.recommend_update = recommend_update def handle_suppress(self, ext): - from pkg_resources import parse_version + from packaging.version import parse should_suppress = ext.name == self.suppress_extension_name and ext.version and \ - parse_version(ext.version) <= parse_version(self.suppress_up_to_version) + parse(ext.version) <= parse(self.suppress_up_to_version) if should_suppress: reason = self.reason or "Use --debug for more information." logger.warning("Extension %s (%s) has been suppressed. %s", diff --git a/src/azure-cli-core/azure/cli/core/extension/__init__.py b/src/azure-cli-core/azure/cli/core/extension/__init__.py index 89a75d83108..50bdea534f7 100644 --- a/src/azure-cli-core/azure/cli/core/extension/__init__.py +++ b/src/azure-cli-core/azure/cli/core/extension/__init__.py @@ -280,20 +280,20 @@ def _collect(path, depth=0, max_depth=3): def ext_compat_with_cli(azext_metadata): from azure.cli.core import __version__ as core_version - from pkg_resources import parse_version + from packaging.version import parse is_compatible, min_required, max_required = (True, None, None) if azext_metadata: min_required = azext_metadata.get(EXT_METADATA_MINCLICOREVERSION) max_required = azext_metadata.get(EXT_METADATA_MAXCLICOREVERSION) - parsed_cli_version = parse_version(core_version) - if min_required and parsed_cli_version < parse_version(min_required): + parsed_cli_version = parse(core_version) + if min_required and parsed_cli_version < parse(min_required): is_compatible = False - elif max_required and parsed_cli_version > parse_version(max_required): + elif max_required and parsed_cli_version > parse(max_required): is_compatible = False try: min_ext_required = EXTENSION_VERSION_REQUIREMENTS.get(azext_metadata.get('name')).get(MIN_EXT_VERSION) - if parse_version(azext_metadata.get('version')) < parse_version(min_ext_required): + if parse(azext_metadata.get('version')) < parse(min_ext_required): is_compatible = False except AttributeError: min_ext_required = None diff --git a/src/azure-cli-core/azure/cli/core/extension/_resolve.py b/src/azure-cli-core/azure/cli/core/extension/_resolve.py index 40841be48fc..f29d338c967 100644 --- a/src/azure-cli-core/azure/cli/core/extension/_resolve.py +++ b/src/azure-cli-core/azure/cli/core/extension/_resolve.py @@ -2,7 +2,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -from pkg_resources import parse_version +from packaging.version import parse from azure.cli.core.extension import ext_compat_with_cli, WHEEL_INFO_RE from azure.cli.core.extension._index import get_index_extensions @@ -43,10 +43,10 @@ def _is_compatible_with_cli_version(item): def _is_greater_than_cur_version(cur_version): if not cur_version: return None - cur_version_parsed = parse_version(cur_version) + cur_version_parsed = parse(cur_version) def filter_func(item): - item_version = parse_version(item['metadata']['version']) + item_version = parse(item['metadata']['version']) if item_version > cur_version_parsed: return True logger.debug("Skipping '%s' as %s not greater than current version %s", item['filename'], @@ -76,7 +76,7 @@ def resolve_from_index(extension_name, cur_version=None, index_url=None, target_ if not candidates: raise NoExtensionCandidatesError("No suitable extensions found.") - candidates_sorted = sorted(candidates, key=lambda c: parse_version(c['metadata']['version']), reverse=True) + candidates_sorted = sorted(candidates, key=lambda c: parse(c['metadata']['version']), reverse=True) logger.debug("Candidates %s", [c['filename'] for c in candidates_sorted]) if target_version: diff --git a/src/azure-cli-core/azure/cli/core/extension/operations.py b/src/azure-cli-core/azure/cli/core/extension/operations.py index fff8b7357d0..b43aabe23d0 100644 --- a/src/azure-cli-core/azure/cli/core/extension/operations.py +++ b/src/azure-cli-core/azure/cli/core/extension/operations.py @@ -15,7 +15,7 @@ from subprocess import check_output, STDOUT, CalledProcessError from urllib.parse import urlparse -from pkg_resources import parse_version +from packaging.version import parse from azure.cli.core import CommandIndex from azure.cli.core.util import CLIError, reload_module @@ -439,12 +439,12 @@ def list_available_extensions(index_url=None, show_details=False, cli_ctx=None): if not items: continue - latest = max(items, key=lambda c: parse_version(c['metadata']['version'])) + latest = max(items, key=lambda c: parse(c['metadata']['version'])) installed = False if name in installed_extension_names: installed = True ext_version = get_extension(name).version - if ext_version and parse_version(latest['metadata']['version']) > parse_version(ext_version): + if ext_version and parse(latest['metadata']['version']) > parse(ext_version): installed = str(True) + ' (upgrade available)' results.append({ 'name': name, @@ -473,13 +473,13 @@ def list_versions(extension_name, index_url=None, cli_ctx=None): results = [] latest_compatible_version = None - for ext in sorted(exts, key=lambda c: parse_version(c['metadata']['version']), reverse=True): + for ext in sorted(exts, key=lambda c: parse(c['metadata']['version']), reverse=True): compatible = ext_compat_with_cli(ext['metadata'])[0] ext_version = ext['metadata']['version'] if latest_compatible_version is None and compatible: latest_compatible_version = ext_version installed = ext_version == installed_ext.version if installed_ext else False - if installed and parse_version(latest_compatible_version) > parse_version(installed_ext.version): + if installed and parse(latest_compatible_version) > parse(installed_ext.version): installed = str(True) + ' (upgrade available)' version = ext['metadata']['version'] if latest_compatible_version == ext_version: diff --git a/src/azure-cli-core/azure/cli/core/util.py b/src/azure-cli-core/azure/cli/core/util.py index 424574ee83a..187b3176ab7 100644 --- a/src/azure-cli-core/azure/cli/core/util.py +++ b/src/azure-cli-core/azure/cli/core/util.py @@ -352,10 +352,10 @@ def _print(val=''): print(val, file=output) def _get_version_string(name, version_dict): - from distutils.version import LooseVersion # pylint: disable=import-error,no-name-in-module + from packaging.version import parse # pylint: disable=import-error,no-name-in-module local = version_dict['local'] pypi = version_dict.get('pypi', None) - if pypi and LooseVersion(pypi) > LooseVersion(local): + if pypi and parse(pypi) > parse(local): return name.ljust(25) + local.rjust(15) + ' *' return name.ljust(25) + local.rjust(15) @@ -1180,11 +1180,11 @@ def handle_version_update(): """ try: from azure.cli.core._session import VERSIONS - from distutils.version import LooseVersion # pylint: disable=import-error,no-name-in-module + from packaging.version import parse # pylint: disable=import-error,no-name-in-module from azure.cli.core import __version__ if not VERSIONS['versions']: get_cached_latest_versions() - elif LooseVersion(VERSIONS['versions']['core']['local']) != LooseVersion(__version__): + elif parse(VERSIONS['versions']['core']['local']) != parse(__version__): logger.debug("Azure CLI has been updated.") logger.debug("Clean up versions and refresh cloud endpoints information in local files.") VERSIONS['versions'] = {} diff --git a/src/azure-cli/azure/cli/__main__.py b/src/azure-cli/azure/cli/__main__.py index 7e315c4456e..ebe4773c1ca 100644 --- a/src/azure-cli/azure/cli/__main__.py +++ b/src/azure-cli/azure/cli/__main__.py @@ -81,8 +81,8 @@ def cli_main(cli, args): version_update_time = datetime.datetime.strptime(VERSIONS[_VERSION_UPDATE_TIME], '%Y-%m-%d %H:%M:%S.%f') if datetime.datetime.now() > version_update_time + datetime.timedelta(days=11): get_cached_latest_versions() - from distutils.version import LooseVersion - if LooseVersion(VERSIONS['versions']['core']['local']) < LooseVersion(VERSIONS['versions']['core']['pypi']): # pylint: disable=line-too-long + from packaging.version import parse + if parse(VERSIONS['versions']['core']['local']) < parse(VERSIONS['versions']['core']['pypi']): # pylint: disable=line-too-long import subprocess import platform from azure.cli.core.azclierror import UnclassifiedUserFault # pylint: disable=ungrouped-imports diff --git a/src/azure-cli/azure/cli/command_modules/acr/check_health.py b/src/azure-cli/azure/cli/command_modules/acr/check_health.py index dea06a33da9..d9737bff87b 100644 --- a/src/azure-cli/azure/cli/command_modules/acr/check_health.py +++ b/src/azure-cli/azure/cli/command_modules/acr/check_health.py @@ -138,7 +138,7 @@ def _get_cli_version(): # Get helm versions def _get_helm_version(ignore_errors): from ._errors import HELM_VERSION_ERROR - from distutils.version import LooseVersion # pylint: disable=import-error,no-name-in-module + from packaging.version import parse # pylint: disable=import-error,no-name-in-module # Helm command check helm_command, error = get_helm_command(is_diagnostics_context=True) @@ -165,7 +165,7 @@ def _get_helm_version(ignore_errors): logger.warning("Helm version: %s", output) # Display an error message if the current helm version < min required version - if match_obj and LooseVersion(output) < LooseVersion(MIN_HELM_VERSION): + if match_obj and parse(output) < parse(MIN_HELM_VERSION): obsolete_ver_error = HELM_VERSION_ERROR.set_error_message( "Current Helm client version is not recommended. Please upgrade your Helm client to at least version {}." .format(MIN_HELM_VERSION)) @@ -175,7 +175,7 @@ def _get_helm_version(ignore_errors): def _get_notary_version(ignore_errors): from ._errors import NOTARY_VERSION_ERROR from .notary import get_notary_command - from distutils.version import LooseVersion # pylint: disable=import-error,no-name-in-module + from packaging.version import parse # pylint: disable=import-error,no-name-in-module # Notary command check notary_command, error = get_notary_command(is_diagnostics_context=True) @@ -202,9 +202,9 @@ def _get_notary_version(ignore_errors): logger.warning("Notary version: %s", output) # Display error if the current version does not match the recommended version - if match_obj and LooseVersion(output) != LooseVersion(RECOMMENDED_NOTARY_VERSION): + if match_obj and parse(output) != parse(RECOMMENDED_NOTARY_VERSION): version_msg = "upgrade" - if LooseVersion(output) > LooseVersion(RECOMMENDED_NOTARY_VERSION): + if parse(output) > parse(RECOMMENDED_NOTARY_VERSION): version_msg = "downgrade" obsolete_ver_error = NOTARY_VERSION_ERROR.set_error_message( "Current notary version is not recommended. Please {} your notary client to version {}." diff --git a/src/azure-cli/azure/cli/command_modules/find/custom.py b/src/azure-cli/azure/cli/command_modules/find/custom.py index f59a9202ca4..714fc25e7ce 100644 --- a/src/azure-cli/azure/cli/command_modules/find/custom.py +++ b/src/azure-cli/azure/cli/command_modules/find/custom.py @@ -16,7 +16,7 @@ from azure.cli.core import telemetry as telemetry_core from azure.cli.core import __version__ as core_version from azure.cli.core.commands.constants import SURVEY_PROMPT -from pkg_resources import parse_version +from packaging.version import parse from knack.log import get_logger logger = get_logger(__name__) @@ -94,7 +94,7 @@ def should_enable_styling(): def call_aladdin_service(query): - version = str(parse_version(core_version)) + version = str(parse(core_version)) correlation_id = telemetry_core._session.correlation_id # pylint: disable=protected-access subscription_id = telemetry_core._get_azure_subscription_id() # pylint: disable=protected-access diff --git a/src/azure-cli/azure/cli/command_modules/util/custom.py b/src/azure-cli/azure/cli/command_modules/util/custom.py index eb7025a2f6c..8672c7caf57 100644 --- a/src/azure-cli/azure/cli/command_modules/util/custom.py +++ b/src/azure-cli/azure/cli/command_modules/util/custom.py @@ -40,14 +40,14 @@ def upgrade_version(cmd, update_all=None, yes=None): # pylint: disable=too-many from azure.cli.core import __version__ as local_version from azure.cli.core._environment import _ENV_AZ_INSTALLER from azure.cli.core.extension import get_extensions, WheelExtension - from distutils.version import LooseVersion + from packaging.version import parse from knack.util import CLIError update_cli = True from azure.cli.core.util import get_latest_from_github try: latest_version = get_latest_from_github() - if latest_version and LooseVersion(latest_version) <= LooseVersion(local_version): + if latest_version and parse(latest_version) <= parse(local_version): logger.warning("You already have the latest azure-cli version: %s", local_version) update_cli = False if not update_all: diff --git a/src/azure-cli/azure/cli/command_modules/vm/_actions.py b/src/azure-cli/azure/cli/command_modules/vm/_actions.py index 558e96ca9b3..8eaadadfb2a 100644 --- a/src/azure-cli/azure/cli/command_modules/vm/_actions.py +++ b/src/azure-cli/azure/cli/command_modules/vm/_actions.py @@ -147,7 +147,7 @@ def load_images_from_aliases_doc(cli_ctx, publisher=None, offer=None, sku=None): def load_extension_images_thru_services(cli_ctx, publisher, name, version, location, show_latest=False, partial_match=True): from concurrent.futures import ThreadPoolExecutor, as_completed - from distutils.version import LooseVersion # pylint: disable=no-name-in-module,import-error + from packaging.version import parse # pylint: disable=no-name-in-module,import-error all_images = [] client = _compute_client_factory(cli_ctx) if location is None: @@ -175,7 +175,7 @@ def _load_extension_images_from_publisher(publisher): if show_latest: # pylint: disable=no-member - versions.sort(key=lambda v: LooseVersion(v.name), reverse=True) + versions.sort(key=lambda v: parse(v.name), reverse=True) try: all_images.append({ 'publisher': publisher, diff --git a/src/azure-cli/azure/cli/command_modules/vm/custom.py b/src/azure-cli/azure/cli/command_modules/vm/custom.py index abd2a5ee5e4..cd00f67ad43 100644 --- a/src/azure-cli/azure/cli/command_modules/vm/custom.py +++ b/src/azure-cli/azure/cli/command_modules/vm/custom.py @@ -114,10 +114,10 @@ def _get_access_extension_upgrade_info(extensions, name): if extensions: extension = next((e for e in extensions if e.name == name), None) - from distutils.version import LooseVersion # pylint: disable=no-name-in-module,import-error - if extension and LooseVersion(extension.type_handler_version) < LooseVersion(version): + from packaging.version import parse # pylint: disable=no-name-in-module,import-error + if extension and parse(extension.type_handler_version) < parse(version): auto_upgrade = True - elif extension and LooseVersion(extension.type_handler_version) > LooseVersion(version): + elif extension and parse(extension.type_handler_version) > parse(version): version = extension.type_handler_version return publisher, version, auto_upgrade diff --git a/src/azure-cli/requirements.py3.Darwin.txt b/src/azure-cli/requirements.py3.Darwin.txt index 6e2143f1246..1b6b0d9f106 100644 --- a/src/azure-cli/requirements.py3.Darwin.txt +++ b/src/azure-cli/requirements.py3.Darwin.txt @@ -112,6 +112,7 @@ msal==1.10.0 msrest==0.6.21 msrestazure==0.6.3 oauthlib==3.0.1 +packaging==20.9 paramiko==2.6.0 pbr==5.3.1 portalocker==1.7.1 diff --git a/src/azure-cli/requirements.py3.Linux.txt b/src/azure-cli/requirements.py3.Linux.txt index 4d4bfcb83e7..c03134381cf 100644 --- a/src/azure-cli/requirements.py3.Linux.txt +++ b/src/azure-cli/requirements.py3.Linux.txt @@ -112,6 +112,7 @@ msal==1.10.0 msrest==0.6.21 msrestazure==0.6.3 oauthlib==3.0.1 +packaging==20.9 paramiko==2.6.0 pbr==5.3.1 portalocker==1.7.1 diff --git a/src/azure-cli/requirements.py3.windows.txt b/src/azure-cli/requirements.py3.windows.txt index 68d8bd8aceb..ec33d3fb5b3 100644 --- a/src/azure-cli/requirements.py3.windows.txt +++ b/src/azure-cli/requirements.py3.windows.txt @@ -111,6 +111,7 @@ msal==1.10.0 msrest==0.6.21 msrestazure==0.6.3 oauthlib==3.0.1 +packaging==20.9 paramiko==2.6.0 pbr==5.3.1 portalocker==1.7.1 diff --git a/src/azure-cli/setup.py b/src/azure-cli/setup.py index 6e2c466da1e..32c8b7bbb64 100644 --- a/src/azure-cli/setup.py +++ b/src/azure-cli/setup.py @@ -136,6 +136,7 @@ 'javaproperties==0.5.1', 'jsmin~=2.2.2', 'jsondiff==1.2.0', + 'packaging~=20.9', 'pytz==2019.1', 'scp~=0.13.2', 'semver==2.13.0',