From 521057dd97d1656838e861d8c189f9611b7d6c6c Mon Sep 17 00:00:00 2001 From: Doug Hellmann <doug@doughellmann.com> Date: Sat, 25 Jul 2020 13:23:23 -0400 Subject: [PATCH] drop use of pkg_resources Importing pkg_resources scans all of the installed modules for data that won't be used. Switch to using importlib.metdata, which more efficiently loads the metadata for a package. Since the name of the module where importlib.metadata is found depends on the version of python, mocking a function in the library is more complicated. Provide a wrapper in the module that uses importlib.metadata.version() so its tests can examine behavior using different versions via mocks. The distutils package in the standard library is deprecated. Use the packaging library for parsing version strings into something that can be compared. Change-Id: I45d0851cdb5f241ff8dc774dc22123b410502cd9 Signed-off-by: Doug Hellmann <doug@doughellmann.com> --- cinder/backup/drivers/gcs.py | 25 ++++++++++++++++--- .../unit/backup/drivers/test_backup_google.py | 24 +++++++++--------- .../volume/drivers/dell_emc/powermax/utils.py | 6 +++-- lower-constraints.txt | 2 ++ requirements.txt | 2 ++ 5 files changed, 41 insertions(+), 18 deletions(-) diff --git a/cinder/backup/drivers/gcs.py b/cinder/backup/drivers/gcs.py index e372131c755..8a85b37a0d5 100644 --- a/cinder/backup/drivers/gcs.py +++ b/cinder/backup/drivers/gcs.py @@ -27,10 +27,16 @@ Server-centric flow is used for authentication. """ import base64 -from distutils import version import hashlib import os +try: + # For python 3.8 and later + import importlib.metadata as importlib_metadata +except ImportError: + # For everyone else + import importlib_metadata + try: from google.auth import exceptions as gexceptions from google.oauth2 import service_account @@ -49,7 +55,7 @@ from googleapiclient import http from oslo_config import cfg from oslo_log import log as logging from oslo_utils import timeutils -import pkg_resources +from packaging import version import six from cinder.backup import chunkeddriver @@ -142,6 +148,17 @@ def gcs_logger(func): return func_wrapper +def _get_dist_version(name): + """Mock-able wrapper for importlib_metadata.version() + + The module name where version() is found varies by python + version. This function makes it easier for tests to mock the + function and change the return value. + + """ + return importlib_metadata.version(name) + + @interface.backupdriver class GoogleBackupDriver(chunkeddriver.ChunkedBackupDriver): """Provides backup, restore and delete of backup objects within GCS.""" @@ -174,8 +191,8 @@ class GoogleBackupDriver(chunkeddriver.ChunkedBackupDriver): # If we have google client that support google-auth library # (v1.6.0 or higher) and all required libraries are installed use # google-auth for the credentials - dist = pkg_resources.get_distribution('google-api-python-client') - if (version.LooseVersion(dist.version) >= version.LooseVersion('1.6.0') + dist_version = _get_dist_version('google-api-python-client') + if (version.parse(dist_version) >= version.parse('1.6.0') and service_account): creds = service_account.Credentials.from_service_account_file( backup_credential) diff --git a/cinder/tests/unit/backup/drivers/test_backup_google.py b/cinder/tests/unit/backup/drivers/test_backup_google.py index f0491206080..10b4aec26ef 100644 --- a/cinder/tests/unit/backup/drivers/test_backup_google.py +++ b/cinder/tests/unit/backup/drivers/test_backup_google.py @@ -623,59 +623,59 @@ class GoogleBackupDriverTestCase(test.TestCase): self.assertEqual('none', result[0]) self.assertEqual(already_compressed_data, result[1]) - @mock.patch('pkg_resources.get_distribution') + @mock.patch.object(google_dr, '_get_dist_version') @mock.patch.object(google_dr.client.GoogleCredentials, 'from_stream') @mock.patch.object(google_dr.discovery, 'build') @mock.patch.object(google_dr, 'service_account') def test_non_google_auth_version(self, account, build, from_stream, - get_dist_mock): + get_dist_version): # Prior to v1.6.0 Google api client doesn't support google-auth library - get_dist_mock.return_value.version = '1.5.5' + get_dist_version.return_value = '1.5.5' google_dr.CONF.set_override('backup_gcs_credential_file', 'credentials_file') google_dr.GoogleBackupDriver(self.ctxt) - get_dist_mock.assert_called_once_with('google-api-python-client') + get_dist_version.assert_called_once_with('google-api-python-client') from_stream.assert_called_once_with('credentials_file') account.Credentials.from_service_account_file.assert_not_called() build.assert_called_once_with('storage', 'v1', cache_discovery=False, credentials=from_stream.return_value) - @mock.patch('pkg_resources.get_distribution') + @mock.patch.object(google_dr, '_get_dist_version') @mock.patch.object(google_dr.client.GoogleCredentials, 'from_stream') @mock.patch.object(google_dr.discovery, 'build') @mock.patch.object(google_dr, 'service_account', None) - def test_no_httplib2_auth(self, build, from_stream, get_dist_mock): + def test_no_httplib2_auth(self, build, from_stream, get_dist_version): # Google api client requires google-auth-httplib2 if not present we # use legacy credentials - get_dist_mock.return_value.version = '1.6.6' + get_dist_version.return_value = '1.6.6' google_dr.CONF.set_override('backup_gcs_credential_file', 'credentials_file') google_dr.GoogleBackupDriver(self.ctxt) - get_dist_mock.assert_called_once_with('google-api-python-client') + get_dist_version.assert_called_once_with('google-api-python-client') from_stream.assert_called_once_with('credentials_file') build.assert_called_once_with('storage', 'v1', cache_discovery=False, credentials=from_stream.return_value) - @mock.patch('pkg_resources.get_distribution') + @mock.patch.object(google_dr, '_get_dist_version') @mock.patch.object(google_dr, 'gexceptions', mock.Mock()) @mock.patch.object(google_dr.client.GoogleCredentials, 'from_stream') @mock.patch.object(google_dr.discovery, 'build') @mock.patch.object(google_dr, 'service_account') def test_google_auth_used(self, account, build, from_stream, - get_dist_mock): + get_dist_version): # Google api client requires google-auth-httplib2 if not present we # use legacy credentials - get_dist_mock.return_value.version = '1.6.6' + get_dist_version.return_value = '1.6.6' google_dr.CONF.set_override('backup_gcs_credential_file', 'credentials_file') google_dr.GoogleBackupDriver(self.ctxt) - get_dist_mock.assert_called_once_with('google-api-python-client') + get_dist_version.assert_called_once_with('google-api-python-client') from_stream.assert_not_called() create_creds = account.Credentials.from_service_account_file create_creds.assert_called_once_with('credentials_file') diff --git a/cinder/volume/drivers/dell_emc/powermax/utils.py b/cinder/volume/drivers/dell_emc/powermax/utils.py index 618edf75a88..0660cd20139 100644 --- a/cinder/volume/drivers/dell_emc/powermax/utils.py +++ b/cinder/volume/drivers/dell_emc/powermax/utils.py @@ -21,6 +21,7 @@ import re from oslo_log import log as logging from oslo_utils import strutils from oslo_utils import units +import packaging.version import six from cinder import exception @@ -1933,8 +1934,9 @@ class PowerMaxUtils(object): :param minimum_version: minimum version allowed :returns: boolean """ - from pkg_resources import parse_version - return parse_version(version) >= parse_version(minimum_version) + checking = packaging.version.parse(version) + minimum = packaging.version.parse(minimum_version) + return checking >= minimum @staticmethod def parse_specs_from_pool_name(pool_name): diff --git a/lower-constraints.txt b/lower-constraints.txt index 0771620a4e5..bf0842402ad 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -42,6 +42,7 @@ hacking==3.0.1 httplib2==0.9.1 idna==2.6 imagesize==1.0.0 +importlib-metadata==1.7.0 iso8601==0.1.12 Jinja2==2.10 jsonpatch==1.21 @@ -85,6 +86,7 @@ oslo.versionedobjects==1.31.2 oslo.vmware==2.35.0 oslotest==3.2.0 osprofiler==1.4.0 +packaging==20.4 paramiko==2.4.0 Paste==2.0.2 PasteDeploy==1.5.0 diff --git a/requirements.txt b/requirements.txt index c0c4599f40d..bfcd9b8174a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ decorator>=3.4.0 # BSD eventlet!=0.23.0,!=0.25.0,>=0.22.0 # MIT greenlet>=0.4.13 # MIT httplib2>=0.9.1 # MIT +importlib_metadata>=1.7.0;python_version<'3.8' # Apache-2.0 iso8601>=0.1.12 # MIT jsonschema>=3.2.0 # MIT keystoneauth1>=3.14.0 # Apache-2.0 @@ -30,6 +31,7 @@ oslo.upgradecheck>=0.1.0 # Apache-2.0 oslo.utils>=3.34.0 # Apache-2.0 oslo.versionedobjects>=1.31.2 # Apache-2.0 osprofiler>=1.4.0 # Apache-2.0 +packaging>=20.4 paramiko>=2.4.0 # LGPLv2.1+ Paste>=2.0.2 # MIT PasteDeploy>=1.5.0 # MIT