Defer client imports

So we really weren't deferring the loading of client libs dadgummit,
do that for real where possible.  This shaves a couple of tenths off
the static import times.

Also defer as much import-time procesing as possible.  This is a little
ugly in api.auth but this also eliminates import of the auth plugins
until they are needed.

Change-Id: Ia11d4b9cf98231d37449103fc29101dc17afb009
This commit is contained in:
Dean Troyer 2015-04-13 16:47:49 -05:00
parent e60bf28ae3
commit f43c1f7655
5 changed files with 73 additions and 39 deletions

View File

@ -27,29 +27,49 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
# Initialize the list of Authentication plugins early in order
# to get the command-line options
PLUGIN_LIST = stevedore.ExtensionManager(
base.PLUGIN_NAMESPACE,
invoke_on_load=False,
propagate_map_exceptions=True,
)
PLUGIN_LIST = None
# Get the command line options so the help action has them available
# List of plugin command line options
OPTIONS_LIST = {}
for plugin in PLUGIN_LIST:
for o in plugin.plugin.get_options():
os_name = o.dest.lower().replace('_', '-')
os_env_name = 'OS_' + os_name.upper().replace('-', '_')
OPTIONS_LIST.setdefault(os_name, {'env': os_env_name, 'help': ''})
# TODO(mhu) simplistic approach, would be better to only add
# help texts if they vary from one auth plugin to another
# also the text rendering is ugly in the CLI ...
OPTIONS_LIST[os_name]['help'] += 'With %s: %s\n' % (
plugin.name,
o.help,
def get_plugin_list():
"""Gather plugin list and cache it"""
global PLUGIN_LIST
if PLUGIN_LIST is None:
PLUGIN_LIST = stevedore.ExtensionManager(
base.PLUGIN_NAMESPACE,
invoke_on_load=False,
propagate_map_exceptions=True,
)
return PLUGIN_LIST
def get_options_list():
"""Gather plugin options so the help action has them available"""
global OPTIONS_LIST
if not OPTIONS_LIST:
for plugin in get_plugin_list():
for o in plugin.plugin.get_options():
os_name = o.dest.lower().replace('_', '-')
os_env_name = 'OS_' + os_name.upper().replace('-', '_')
OPTIONS_LIST.setdefault(
os_name, {'env': os_env_name, 'help': ''},
)
# TODO(mhu) simplistic approach, would be better to only add
# help texts if they vary from one auth plugin to another
# also the text rendering is ugly in the CLI ...
OPTIONS_LIST[os_name]['help'] += 'With %s: %s\n' % (
plugin.name,
o.help,
)
return OPTIONS_LIST
def select_auth_plugin(options):
@ -57,7 +77,7 @@ def select_auth_plugin(options):
auth_plugin_name = None
if options.os_auth_type in [plugin.name for plugin in PLUGIN_LIST]:
if options.os_auth_type in [plugin.name for plugin in get_plugin_list()]:
# A direct plugin name was given, use it
return options.os_auth_type
@ -113,7 +133,7 @@ def build_auth_params(auth_plugin_name, cmd_options):
else:
LOG.debug('no auth_type')
# delay the plugin choice, grab every option
plugin_options = set([o.replace('-', '_') for o in OPTIONS_LIST])
plugin_options = set([o.replace('-', '_') for o in get_options_list()])
for option in plugin_options:
option_name = 'os_' + option
LOG.debug('fetching option %s' % option_name)
@ -147,7 +167,7 @@ def build_auth_plugins_option_parser(parser):
authentication plugin.
"""
available_plugins = [plugin.name for plugin in PLUGIN_LIST]
available_plugins = [plugin.name for plugin in get_plugin_list()]
parser.add_argument(
'--os-auth-type',
metavar='<auth-type>',
@ -169,7 +189,7 @@ def build_auth_plugins_option_parser(parser):
default=utils.env('OS_TENANT_ID')
),
}
for o in OPTIONS_LIST:
for o in get_options_list():
# remove allusion to tenants from v2.0 API
if 'tenant' not in o:
parser.add_argument(

View File

@ -15,14 +15,6 @@
import logging
from novaclient import client as nova_client
from novaclient import extension
try:
from novaclient.v2.contrib import list_extensions
except ImportError:
from novaclient.v1_1.contrib import list_extensions
from openstackclient.common import utils
LOG = logging.getLogger(__name__)
@ -34,6 +26,15 @@ API_NAME = 'compute'
def make_client(instance):
"""Returns a compute service client."""
# Defer client imports until we actually need them
from novaclient import client as nova_client
from novaclient import extension
try:
from novaclient.v2.contrib import list_extensions
except ImportError:
from novaclient.v1_1.contrib import list_extensions
compute_client = nova_client.get_client_class(
instance._api_version[API_NAME],
)

View File

@ -34,6 +34,10 @@ AUTH_REF.update(fakes.TEST_RESPONSE_DICT['access'])
SERVICE_CATALOG = service_catalog.ServiceCatalogV2(AUTH_REF)
# This is deferred in api.auth but we need it here...
auth.get_options_list()
class Container(object):
attr = clientmanager.ClientCache(lambda x: object())

View File

@ -24,6 +24,13 @@ from openstackclient.tests import utils as test_utils
from openstackclient.volume import client # noqa
# Monkey patch for v1 cinderclient
# NOTE(dtroyer): Do here because openstackclient.volume.client
# doesn't do it until the client object is created now.
volumes.Volume.NAME_ATTR = 'display_name'
volume_snapshots.Snapshot.NAME_ATTR = 'display_name'
ID = '1after909'
NAME = 'PhilSpector'

View File

@ -15,17 +15,8 @@
import logging
from cinderclient import extension
from cinderclient.v1.contrib import list_extensions
from cinderclient.v1 import volume_snapshots
from cinderclient.v1 import volumes
from openstackclient.common import utils
# Monkey patch for v1 cinderclient
volumes.Volume.NAME_ATTR = 'display_name'
volume_snapshots.Snapshot.NAME_ATTR = 'display_name'
LOG = logging.getLogger(__name__)
DEFAULT_VOLUME_API_VERSION = '1'
@ -38,6 +29,17 @@ API_VERSIONS = {
def make_client(instance):
"""Returns a volume service client."""
# Defer client imports until we actually need them
from cinderclient import extension
from cinderclient.v1.contrib import list_extensions
from cinderclient.v1 import volume_snapshots
from cinderclient.v1 import volumes
# Monkey patch for v1 cinderclient
volumes.Volume.NAME_ATTR = 'display_name'
volume_snapshots.Snapshot.NAME_ATTR = 'display_name'
volume_client = utils.get_client_class(
API_NAME,
instance._api_version[API_NAME],