def add_extension(source=None, extension_name=None, index_url=None, yes=None, # pylint: disable=unused-argument pip_extra_index_urls=None, pip_proxy=None): ext_sha256 = None if extension_name: ext = None try: ext = get_extension(extension_name) except ExtensionNotInstalledException: pass if ext: if isinstance(ext, WheelExtension): logger.warning("Extension '%s' is already installed.", extension_name) return else: logger.warning("Overriding development version of '%s' with production version.", extension_name) try: source, ext_sha256 = resolve_from_index(extension_name, index_url=index_url) except NoExtensionCandidatesError as err: logger.debug(err) raise CLIError("No matching extensions for '{}'. Use --debug for more information.".format(extension_name)) _add_whl_ext(source, ext_sha256=ext_sha256, pip_extra_index_urls=pip_extra_index_urls, pip_proxy=pip_proxy) _augment_telemetry_with_ext_info(extension_name) try: if extension_name and get_extension(extension_name).preview: logger.warning("The installed extension '%s' is in preview.", extension_name) except ExtensionNotInstalledException: pass
def start_shell(cmd, update=None, style=None): from importlib import import_module try: get_extension(INTERACTIVE_EXTENSION_NAME) if update: logger.warning( "Updating the Interactive extension to the latest available..." ) update_extension(cmd, INTERACTIVE_EXTENSION_NAME) reload_extension(INTERACTIVE_EXTENSION_NAME) except ExtensionNotInstalledException: logger.warning("Installing the Interactive extension...") add_extension(cmd, extension_name=INTERACTIVE_EXTENSION_NAME) ext = get_extension(INTERACTIVE_EXTENSION_NAME) try: check_version_compatibility(ext.get_metadata()) except CLIError: raise CLIError('Run `az interactive --update` and try again.') add_extension_to_path(INTERACTIVE_EXTENSION_NAME, ext_dir=ext.path) interactive_module = get_extension_modname( ext_name=INTERACTIVE_EXTENSION_NAME, ext_dir=ext.path) azext_interactive = import_module(interactive_module) azext_interactive.start_shell(cmd, style=style)
def add_extension(cmd, source=None, extension_name=None, index_url=None, yes=None, # pylint: disable=unused-argument pip_extra_index_urls=None, pip_proxy=None): ext_sha256 = None if extension_name: import colorama colorama.init() # Required for displaying the spinner correctly on windows issue #9140 cmd.cli_ctx.get_progress_controller().add(message='Searching') colorama.deinit() ext = None try: ext = get_extension(extension_name) except ExtensionNotInstalledException: pass if ext: if isinstance(ext, WheelExtension): logger.warning("Extension '%s' is already installed.", extension_name) return logger.warning("Overriding development version of '%s' with production version.", extension_name) try: source, ext_sha256 = resolve_from_index(extension_name, index_url=index_url) except NoExtensionCandidatesError as err: logger.debug(err) raise CLIError("No matching extensions for '{}'. Use --debug for more information.".format(extension_name)) _add_whl_ext(cmd=cmd, source=source, ext_sha256=ext_sha256, pip_extra_index_urls=pip_extra_index_urls, pip_proxy=pip_proxy) _augment_telemetry_with_ext_info(extension_name) try: if extension_name and get_extension(extension_name).preview: logger.warning("The installed extension '%s' is in preview.", extension_name) except ExtensionNotInstalledException: pass
def add_extension(cmd, source=None, extension_name=None, index_url=None, yes=None, # pylint: disable=unused-argument pip_extra_index_urls=None, pip_proxy=None, system=None): ext_sha256 = None if extension_name: cmd.cli_ctx.get_progress_controller().add(message='Searching') ext = None try: ext = get_extension(extension_name) except ExtensionNotInstalledException: pass if ext: if isinstance(ext, WheelExtension): logger.warning("Extension '%s' is already installed.", extension_name) return logger.warning("Overriding development version of '%s' with production version.", extension_name) try: source, ext_sha256 = resolve_from_index(extension_name, index_url=index_url) except NoExtensionCandidatesError as err: logger.debug(err) raise CLIError("No matching extensions for '{}'. Use --debug for more information.".format(extension_name)) extension_name = _add_whl_ext(cmd=cmd, source=source, ext_sha256=ext_sha256, pip_extra_index_urls=pip_extra_index_urls, pip_proxy=pip_proxy, system=system) try: ext = get_extension(extension_name) _augment_telemetry_with_ext_info(extension_name, ext) if extension_name and ext.experimental: logger.warning("The installed extension '%s' is experimental and not covered by customer support. " "Please use with discretion.", extension_name) elif extension_name and ext.preview: logger.warning("The installed extension '%s' is in preview.", extension_name) except ExtensionNotInstalledException: pass
def remove_extension(extension_name): try: # Get the extension and it will raise an error if it doesn't exist get_extension(extension_name) # We call this just before we remove the extension so we can get the metadata before it is gone _augment_telemetry_with_ext_info(extension_name) shutil.rmtree(get_extension_path(extension_name)) except ExtensionNotInstalledException as e: raise CLIError(e)
def test_wheel_user_system_same_extension(self): _install_test_extension1() _install_test_extension1(system=True) self.assertNotEqual(build_extension_path(EXT_NAME), build_extension_path(EXT_NAME, system=True)) actual = get_extension(EXT_NAME, ext_type=WheelExtension) self.assertEqual(actual.name, EXT_NAME) self.assertEqual(actual.path, build_extension_path(EXT_NAME)) shutil.rmtree(self.ext_dir) actual = get_extension(EXT_NAME, ext_type=WheelExtension) self.assertEqual(actual.name, EXT_NAME) self.assertEqual(actual.path, build_extension_path(EXT_NAME, system=True))
def remove_extension(extension_name): def log_err(func, path, exc_info): logger.debug("Error occurred attempting to delete item from the extension '%s'.", extension_name) logger.debug("%s: %s - %s", func, path, exc_info) try: # Get the extension and it will raise an error if it doesn't exist get_extension(extension_name) # We call this just before we remove the extension so we can get the metadata before it is gone _augment_telemetry_with_ext_info(extension_name) shutil.rmtree(get_extension_path(extension_name), onerror=log_err) except ExtensionNotInstalledException as e: raise CLIError(e)
def add_extension(cmd=None, source=None, extension_name=None, index_url=None, yes=None, # pylint: disable=unused-argument, too-many-statements pip_extra_index_urls=None, pip_proxy=None, system=None, version=None, cli_ctx=None, upgrade=None): ext_sha256 = None version = None if version == 'latest' else version cmd_cli_ctx = cli_ctx or cmd.cli_ctx if extension_name: cmd_cli_ctx.get_progress_controller().add(message='Searching') ext = None set_extension_management_detail(extension_name, version) try: ext = get_extension(extension_name) except ExtensionNotInstalledException: pass if ext: if isinstance(ext, WheelExtension): if not upgrade: logger.warning("Extension '%s' is already installed.", extension_name) return logger.warning("Extension '%s' %s is already installed.", extension_name, ext.get_version()) if version and version == ext.get_version(): return logger.warning("It will be overridden with version {}.".format(version) if version else "It will be updated if available.") update_extension(cmd=cmd, extension_name=extension_name, index_url=index_url, pip_extra_index_urls=pip_extra_index_urls, pip_proxy=pip_proxy, cli_ctx=cli_ctx, version=version) return logger.warning("Overriding development version of '%s' with production version.", extension_name) try: source, ext_sha256 = resolve_from_index(extension_name, index_url=index_url, target_version=version, cli_ctx=cmd_cli_ctx) except NoExtensionCandidatesError as err: logger.debug(err) if version: err = "No matching extensions for '{} ({})'. Use --debug for more information.".format(extension_name, version) else: err = "No matching extensions for '{}'. Use --debug for more information.".format(extension_name) raise CLIError(err) ext_name, ext_version = _get_extension_info_from_source(source) set_extension_management_detail(extension_name if extension_name else ext_name, ext_version) extension_name = _add_whl_ext(cli_ctx=cmd_cli_ctx, source=source, ext_sha256=ext_sha256, pip_extra_index_urls=pip_extra_index_urls, pip_proxy=pip_proxy, system=system) try: ext = get_extension(extension_name) if extension_name and ext.experimental: logger.warning("The installed extension '%s' is experimental and not covered by customer support. " "Please use with discretion.", extension_name) elif extension_name and ext.preview: logger.warning("The installed extension '%s' is in preview.", extension_name) CommandIndex().invalidate() except ExtensionNotInstalledException: pass
def update_extension(extension_name, index_url=None, pip_extra_index_urls=None, pip_proxy=None): try: ext = get_extension(extension_name) cur_version = ext.get_version() try: download_url, ext_sha256 = resolve_from_index(extension_name, cur_version=cur_version, index_url=index_url) except NoExtensionCandidatesError as err: logger.debug(err) raise CLIError("No updates available for '{}'. Use --debug for more information.".format(extension_name)) # Copy current version of extension to tmp directory in case we need to restore it after a failed install. backup_dir = os.path.join(tempfile.mkdtemp(), extension_name) extension_path = get_extension_path(extension_name) logger.debug('Backing up the current extension: %s to %s', extension_path, backup_dir) shutil.copytree(extension_path, backup_dir) # Remove current version of the extension shutil.rmtree(extension_path) # Install newer version try: _add_whl_ext(download_url, ext_sha256=ext_sha256, pip_extra_index_urls=pip_extra_index_urls, pip_proxy=pip_proxy) logger.debug('Deleting backup of old extension at %s', backup_dir) shutil.rmtree(backup_dir) # This gets the metadata for the extension *after* the update _augment_telemetry_with_ext_info(extension_name) except Exception as err: logger.error('An error occurred whilst updating.') logger.error(err) logger.debug('Copying %s to %s', backup_dir, extension_path) shutil.copytree(backup_dir, extension_path) raise CLIError('Failed to update. Rolled {} back to {}.'.format(extension_name, cur_version)) except ExtensionNotInstalledException as e: raise CLIError(e)
def start_shell(cmd, update=None, style=None): from importlib import import_module try: get_extension(INTERACTIVE_EXTENSION_NAME) if update: logger.warning("Updating the Interactive extension to the latest available..") update_extension(INTERACTIVE_EXTENSION_NAME) reload_extension(INTERACTIVE_EXTENSION_NAME) except ExtensionNotInstalledException: logger.warning("Installing the Interactive extension..") add_extension(extension_name=INTERACTIVE_EXTENSION_NAME) add_extension_to_path(INTERACTIVE_EXTENSION_NAME) interactive_module = get_extension_modname(ext_name=INTERACTIVE_EXTENSION_NAME) azext_interactive = import_module(interactive_module) azext_interactive.start_shell(cmd, style=style)
def add_extension( source=None, extension_name=None, index_url=None, yes=None, # pylint: disable=unused-argument pip_extra_index_urls=None, pip_proxy=None): ext_sha256 = None if extension_name: if extension_exists(extension_name): logger.warning("The extension '%s' already exists.", extension_name) return try: source, ext_sha256 = resolve_from_index(extension_name, index_url=index_url) except NoExtensionCandidatesError as err: logger.debug(err) raise CLIError( "No matching extensions for '{}'. Use --debug for more information." .format(extension_name)) _add_whl_ext(source, ext_sha256=ext_sha256, pip_extra_index_urls=pip_extra_index_urls, pip_proxy=pip_proxy) _augment_telemetry_with_ext_info(extension_name) try: if extension_name and get_extension(extension_name).preview: logger.warning("The installed extension '%s' is in preview.", extension_name) except ExtensionNotInstalledException: pass
def update_extension(extension_name, index_url=None): try: ext = get_extension(extension_name) cur_version = ext.get_version() try: download_url, ext_sha256 = resolve_from_index( extension_name, cur_version=cur_version, index_url=index_url) except NoExtensionCandidatesError as err: logger.debug(err) raise CLIError( "No updates available for '{}'. Use --debug for more information." .format(extension_name)) # Copy current version of extension to tmp directory in case we need to restore it after a failed install. backup_dir = os.path.join(tempfile.mkdtemp(), extension_name) extension_path = get_extension_path(extension_name) logger.debug('Backing up the current extension: %s to %s', extension_path, backup_dir) shutil.copytree(extension_path, backup_dir) # Remove current version of the extension shutil.rmtree(extension_path) # Install newer version try: _add_whl_ext(download_url, ext_sha256=ext_sha256) logger.debug('Deleting backup of old extension at %s', backup_dir) shutil.rmtree(backup_dir) except Exception as err: logger.error('An error occurred whilst updating.') logger.error(err) logger.debug('Copying %s to %s', backup_dir, extension_path) shutil.copytree(backup_dir, extension_path) raise CLIError('Failed to update. Rolled {} back to {}.'.format( extension_name, cur_version)) except ExtensionNotInstalledException as e: raise CLIError(e)
def list_available_extensions(index_url=None, show_details=False): index_data = get_index_extensions(index_url=index_url) if show_details: return index_data installed_extensions = get_extensions(ext_type=WheelExtension) installed_extension_names = [e.name for e in installed_extensions] results = [] for name, items in OrderedDict(sorted(index_data.items())).items(): # exclude extensions/versions incompatible with current CLI version items = [item for item in items if ext_compat_with_cli(item['metadata'])[0]] if not items: continue latest = max(items, key=lambda c: parse_version(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): installed = str(True) + ' (upgrade available)' results.append({ 'name': name, 'version': latest['metadata']['version'], 'summary': latest['metadata']['summary'], 'preview': latest['metadata'].get(EXT_METADATA_ISPREVIEW, False), 'experimental': latest['metadata'].get(EXT_METADATA_ISEXPERIMENTAL, False), 'installed': installed }) return results
def test_wheel_metadata2(self): _install_test_extension2() ext = get_extension(EXT_NAME) # There should be no exceptions and metadata should have some value self.assertTrue(ext.metadata) # We check that we can retrieve any one of the az extension metadata values self.assertTrue(ext.metadata.get(EXT_METADATA_MINCLICOREVERSION))
def list_available_extensions(index_url=None, show_details=False): index_data = get_index_extensions(index_url=index_url) if show_details: return index_data installed_extensions = get_extensions() installed_extension_names = [e.name for e in installed_extensions] results = [] for name, items in OrderedDict(sorted(index_data.items())).items(): latest = sorted(items, key=lambda c: parse_version(c['metadata']['version']), reverse=True)[0] 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): installed = str(True) + ' (upgrade available)' results.append({ 'name': name, 'version': latest['metadata']['version'], 'summary': latest['metadata']['summary'], 'preview': latest['metadata'].get(EXT_METADATA_ISPREVIEW, False), 'installed': installed }) return results
def list_available_extensions(index_url=None, show_details=False): index_data = get_index_extensions(index_url=index_url) if show_details: return index_data installed_extensions = get_extensions() installed_extension_names = [e.name for e in installed_extensions] results = [] for name, items in OrderedDict(sorted(index_data.items())).items(): # exclude extensions/versions incompatible with current CLI version items = [item for item in items if ext_compat_with_cli(item['metadata'])[0]] if not items: continue latest = max(items, key=lambda c: parse_version(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): installed = str(True) + ' (upgrade available)' results.append({ 'name': name, 'version': latest['metadata']['version'], 'summary': latest['metadata']['summary'], 'preview': latest['metadata'].get(EXT_METADATA_ISPREVIEW, False), 'installed': installed }) return results
def test_wheel_metadata2(self): _install_test_extension2(system=True) ext = get_extension(EXT_NAME) # There should be no exceptions and metadata should have some value self.assertTrue(ext.metadata) # We check that we can retrieve any one of the az extension metadata values self.assertTrue(ext.metadata.get(EXT_METADATA_MINCLICOREVERSION))
def show_extension(extension_name): try: extension = get_extension(extension_name) return {OUT_KEY_NAME: extension.name, OUT_KEY_VERSION: extension.version, OUT_KEY_TYPE: extension.ext_type, OUT_KEY_METADATA: extension.metadata} except ExtensionNotInstalledException as e: raise CLIError(e)
def start_shell(cmd, update=None, style=None): from importlib import import_module try: get_extension(INTERACTIVE_EXTENSION_NAME) if update: logger.warning( "Updating the Interactive extension to the latest available..") update_extension(INTERACTIVE_EXTENSION_NAME) reload_extension(INTERACTIVE_EXTENSION_NAME) except ExtensionNotInstalledException: logger.warning("Installing the Interactive extension..") add_extension(extension_name=INTERACTIVE_EXTENSION_NAME) add_extension_to_path(INTERACTIVE_EXTENSION_NAME) interactive_module = get_extension_modname( ext_name=INTERACTIVE_EXTENSION_NAME) azext_interactive = import_module(interactive_module) azext_interactive.start_shell(cmd, style=style)
def update_extension(cmd=None, extension_name=None, index_url=None, pip_extra_index_urls=None, pip_proxy=None, cli_ctx=None, version=None): try: cmd_cli_ctx = cli_ctx or cmd.cli_ctx ext = get_extension(extension_name, ext_type=WheelExtension) cur_version = ext.get_version() try: download_url, ext_sha256 = resolve_from_index( extension_name, cur_version=cur_version, index_url=index_url, target_version=version, cli_ctx=cmd_cli_ctx) _, ext_version = _get_extension_info_from_source(download_url) set_extension_management_detail(extension_name, ext_version) except NoExtensionCandidatesError as err: logger.debug(err) msg = "Extension {} with version {} not found.".format( extension_name, version ) if version else "No updates available for '{}'. Use --debug for more information.".format( extension_name) logger.warning(msg) return # Copy current version of extension to tmp directory in case we need to restore it after a failed install. backup_dir = os.path.join(tempfile.mkdtemp(), extension_name) extension_path = ext.path logger.debug('Backing up the current extension: %s to %s', extension_path, backup_dir) shutil.copytree(extension_path, backup_dir) # Remove current version of the extension rmtree_with_retry(extension_path) # Install newer version try: _add_whl_ext(cli_ctx=cmd_cli_ctx, source=download_url, ext_sha256=ext_sha256, pip_extra_index_urls=pip_extra_index_urls, pip_proxy=pip_proxy) logger.debug('Deleting backup of old extension at %s', backup_dir) rmtree_with_retry(backup_dir) except Exception as err: logger.error('An error occurred whilst updating.') logger.error(err) logger.debug('Copying %s to %s', backup_dir, extension_path) shutil.copytree(backup_dir, extension_path) raise CLIError('Failed to update. Rolled {} back to {}.'.format( extension_name, cur_version)) CommandIndex().invalidate() except ExtensionNotInstalledException as e: raise CLIError(e)
def _augment_telemetry_with_ext_info(extension_name, ext=None): # The extension must be available before calling this otherwise we can't get the version from metadata if not extension_name: return try: ext = ext or get_extension(extension_name) ext_version = ext.version set_extension_management_detail(extension_name, ext_version) except Exception: # nopa pylint: disable=broad-except # Don't error on telemetry pass
def _augment_telemetry_with_ext_info(extension_name): # The extension must be available before calling this otherwise we can't get the version from metadata if not extension_name: return try: ext = get_extension(extension_name) ext_version = ext.version set_extension_management_detail(extension_name, ext_version) except Exception: # nopa pylint: disable=broad-except # Don't error on telemetry pass
def test_add_extension_to_path(self): add_extension(cmd=self.cmd, source=MY_EXT_SOURCE) num_exts = len(list_extensions()) self.assertEqual(num_exts, 1) ext = get_extension('myfirstcliextension') old_path = sys.path[:] try: add_extension_to_path(ext.name) self.assertSequenceEqual(old_path, sys.path[:-1]) self.assertEqual(ext.path, sys.path[-1]) finally: sys.path[:] = old_path
def format_help(self): extension_version = None try: if self.command_source: extension_version = get_extension(self.command_source.extension_name).version except Exception: # pylint: disable=broad-except pass telemetry.set_command_details( command=self.prog[3:], extension_name=self.command_source.extension_name if self.command_source else None, extension_version=extension_version) telemetry.set_success(summary='show help') super(AzCliCommandParser, self).format_help()
def remove_extension(extension_name): try: # Get the extension and it will raise an error if it doesn't exist ext = get_extension(extension_name) if ext and isinstance(ext, DevExtension): raise CLIError( "Extension '{name}' was installed in development mode. Remove using " "`azdev extension remove {name}`".format(name=extension_name)) # We call this just before we remove the extension so we can get the metadata before it is gone _augment_telemetry_with_ext_info(extension_name, ext) rmtree_with_retry(ext.path) CommandIndex().invalidate() except ExtensionNotInstalledException as e: raise CLIError(e)
def teamcloud_update(cmd, version=None, prerelease=False): # pylint: disable=too-many-statements, too-many-locals import os import tempfile import shutil from azure.cli.core import CommandIndex from azure.cli.core.extension import get_extension from azure.cli.core.extension.operations import _add_whl_ext, _augment_telemetry_with_ext_info from ._deploy_utils import get_github_release release = get_github_release(cmd.cli_ctx, 'TeamCloud', version=version, prerelease=prerelease) asset = next((a for a in release['assets'] if 'py3-none-any.whl' in a['browser_download_url']), None) download_url = asset['browser_download_url'] if asset else None if not download_url: raise CLIError('Could not find extension .whl asset on release {}. ' 'Specify a specific prerelease version with --version ' 'or use latest prerelease with --pre'.format( release['tag_name'])) extension_name = 'tc' ext = get_extension(extension_name) cur_version = ext.get_version() # Copy current version of extension to tmp directory in case we need to restore it after a failed install. backup_dir = os.path.join(tempfile.mkdtemp(), extension_name) extension_path = ext.path logger.debug('Backing up the current extension: %s to %s', extension_path, backup_dir) shutil.copytree(extension_path, backup_dir) # Remove current version of the extension shutil.rmtree(extension_path) # Install newer version try: _add_whl_ext(cli_ctx=cmd.cli_ctx, source=download_url) logger.debug('Deleting backup of old extension at %s', backup_dir) shutil.rmtree(backup_dir) # This gets the metadata for the extension *after* the update _augment_telemetry_with_ext_info(extension_name) except Exception as err: logger.error('An error occurred whilst updating.') logger.error(err) logger.debug('Copying %s to %s', backup_dir, extension_path) shutil.copytree(backup_dir, extension_path) raise CLIError('Failed to update. Rolled {} back to {}.'.format( extension_name, cur_version)) CommandIndex().invalidate()
def list_versions(extension_name, index_url=None): index_data = get_index_extensions(index_url=index_url) try: exts = index_data[extension_name] except Exception: raise CLIError('Extension {} not found.'.format(extension_name)) try: installed_ext = get_extension(extension_name, ext_type=WheelExtension) except ExtensionNotInstalledException: installed_ext = None results = [] latest_compatible_version = None for ext in sorted(exts, key=lambda c: parse_version(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): installed = str(True) + ' (upgrade available)' version = ext['metadata']['version'] if latest_compatible_version == ext_version: version = version + ' (max compatible version)' results.append({ 'name': extension_name, 'version': version, 'preview': ext['metadata'].get(EXT_METADATA_ISPREVIEW, False), 'experimental': ext['metadata'].get(EXT_METADATA_ISEXPERIMENTAL, False), 'installed': installed, 'compatible': compatible }) results.reverse() return results
def remove_extension(extension_name): def log_err(func, path, exc_info): logger.warning("Error occurred attempting to delete item from the extension '%s'.", extension_name) logger.warning("%s: %s - %s", func, path, exc_info) try: # Get the extension and it will raise an error if it doesn't exist ext = get_extension(extension_name) if ext and isinstance(ext, DevExtension): raise CLIError( "Extension '{name}' was installed in development mode. Remove using " "`azdev extension remove {name}`".format(name=extension_name)) # We call this just before we remove the extension so we can get the metadata before it is gone _augment_telemetry_with_ext_info(extension_name, ext) shutil.rmtree(ext.path, onerror=log_err) except ExtensionNotInstalledException as e: raise CLIError(e)
def remove_extension(extension_name): def log_err(func, path, exc_info): logger.debug("Error occurred attempting to delete item from the extension '%s'.", extension_name) logger.debug("%s: %s - %s", func, path, exc_info) try: # Get the extension and it will raise an error if it doesn't exist ext = get_extension(extension_name) if ext and isinstance(ext, DevExtension): raise CLIError( "Extension '{name}' was installed in development mode. Remove using " "`azdev extension remove {name}`".format(name=extension_name)) # We call this just before we remove the extension so we can get the metadata before it is gone _augment_telemetry_with_ext_info(extension_name) shutil.rmtree(get_extension_path(extension_name), onerror=log_err) except ExtensionNotInstalledException as e: raise CLIError(e)
def update_extension(cmd, extension_name, index_url=None, pip_extra_index_urls=None, pip_proxy=None): try: ext = get_extension(extension_name, ext_type=WheelExtension) cur_version = ext.get_version() try: download_url, ext_sha256 = resolve_from_index( extension_name, cur_version=cur_version, index_url=index_url) except NoExtensionCandidatesError as err: logger.debug(err) raise CLIError( "No updates available for '{}'. Use --debug for more information." .format(extension_name)) # Copy current version of extension to tmp directory in case we need to restore it after a failed install. backup_dir = os.path.join(tempfile.mkdtemp(), extension_name) extension_path = ext.path logger.debug('Backing up the current extension: %s to %s', extension_path, backup_dir) shutil.copytree(extension_path, backup_dir) # Remove current version of the extension shutil.rmtree(extension_path) # Install newer version try: _add_whl_ext(cmd=cmd, source=download_url, ext_sha256=ext_sha256, pip_extra_index_urls=pip_extra_index_urls, pip_proxy=pip_proxy) logger.debug('Deleting backup of old extension at %s', backup_dir) shutil.rmtree(backup_dir) # This gets the metadata for the extension *after* the update _augment_telemetry_with_ext_info(extension_name) except Exception as err: logger.error('An error occurred whilst updating.') logger.error(err) logger.debug('Copying %s to %s', backup_dir, extension_path) shutil.copytree(backup_dir, extension_path) raise CLIError('Failed to update. Rolled {} back to {}.'.format( extension_name, cur_version)) CommandIndex().invalidate() except ExtensionNotInstalledException as e: raise CLIError(e)
def add_extension(source=None, extension_name=None, index_url=None, yes=None, # pylint: disable=unused-argument pip_extra_index_urls=None, pip_proxy=None): ext_sha256 = None if extension_name: if extension_exists(extension_name): raise CLIError('The extension {} already exists.'.format(extension_name)) try: source, ext_sha256 = resolve_from_index(extension_name, index_url=index_url) except NoExtensionCandidatesError as err: logger.debug(err) raise CLIError("No matching extensions for '{}'. Use --debug for more information.".format(extension_name)) _add_whl_ext(source, ext_sha256=ext_sha256, pip_extra_index_urls=pip_extra_index_urls, pip_proxy=pip_proxy) _augment_telemetry_with_ext_info(extension_name) try: if extension_name and get_extension(extension_name).preview: logger.warning("The installed extension '%s' is in preview.", extension_name) except ExtensionNotInstalledException: pass
def add_extension_to_path(extension_name, ext_dir=None): ext_dir = ext_dir or get_extension(extension_name).path sys.path.append(ext_dir) # If this path update should have made a new "azure" module available, # extend the existing module with its path. This allows extensions to # include (or depend on) Azure SDK modules that are not yet part of # the CLI. This applies to both the "azure" and "azure.mgmt" namespaces, # but ensures that modules installed by the CLI take priority. azure_dir = os.path.join(ext_dir, "azure") if os.path.isdir(azure_dir): import azure azure.__path__.append(azure_dir) azure_mgmt_dir = os.path.join(azure_dir, "mgmt") if os.path.isdir(azure_mgmt_dir): try: # Should have been imported already, so this will be quick import azure.mgmt except ImportError: pass else: azure.mgmt.__path__.append(azure_mgmt_dir)
def list_available_extensions(index_url=None, show_details=False): index_data = get_index_extensions(index_url=index_url) if show_details: return index_data installed_extensions = get_extensions() installed_extension_names = [e.name for e in installed_extensions] results = [] for name, items in OrderedDict(sorted(index_data.items())).items(): latest = sorted(items, key=lambda c: parse_version(c['metadata']['version']), reverse=True)[0] 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): installed = str(True) + ' (upgrade available)' results.append({ 'name': name, 'version': latest['metadata']['version'], 'summary': latest['metadata']['summary'], 'preview': latest['metadata'].get(EXT_METADATA_ISPREVIEW, False), 'installed': installed }) return results
def test_add_extension_azure_to_path(self): import azure import azure.mgmt old_path_0 = list(sys.path) old_path_1 = list(azure.__path__) old_path_2 = list(azure.mgmt.__path__) add_extension(cmd=self.cmd, source=MY_EXT_SOURCE) ext = get_extension('myfirstcliextension') azure_dir = os.path.join(ext.path, "azure") azure_mgmt_dir = os.path.join(azure_dir, "mgmt") os.mkdir(azure_dir) os.mkdir(azure_mgmt_dir) try: add_extension_to_path(ext.name) new_path_1 = list(azure.__path__) new_path_2 = list(azure.mgmt.__path__) finally: sys.path.remove(ext.path) remove_extension(ext.name) if isinstance(azure.__path__, list): azure.__path__[:] = old_path_1 else: list(azure.__path__) if isinstance(azure.mgmt.__path__, list): azure.mgmt.__path__[:] = old_path_2 else: list(azure.mgmt.__path__) self.assertSequenceEqual(old_path_1, new_path_1[:-1]) self.assertSequenceEqual(old_path_2, new_path_2[:-1]) self.assertEqual(azure_dir, new_path_1[-1]) self.assertEqual(azure_mgmt_dir, new_path_2[-1]) self.assertSequenceEqual(old_path_0, list(sys.path)) self.assertSequenceEqual(old_path_1, list(azure.__path__)) self.assertSequenceEqual(old_path_2, list(azure.mgmt.__path__))
def execute(self, args): from knack.events import (EVENT_INVOKER_PRE_CMD_TBL_CREATE, EVENT_INVOKER_POST_CMD_TBL_CREATE, EVENT_INVOKER_CMD_TBL_LOADED, EVENT_INVOKER_PRE_PARSE_ARGS, EVENT_INVOKER_POST_PARSE_ARGS, EVENT_INVOKER_FILTER_RESULT) from azure.cli.core.commands.events import EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE # TODO: Can't simply be invoked as an event because args are transformed args = _pre_command_table_create(self.cli_ctx, args) self.cli_ctx.raise_event(EVENT_INVOKER_PRE_CMD_TBL_CREATE, args=args) self.commands_loader.load_command_table(args) self.cli_ctx.raise_event( EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE, load_cmd_tbl_func=self.commands_loader.load_command_table, args=args) command = self._rudimentary_get_command(args) self.cli_ctx.invocation.data['command_string'] = command telemetry.set_raw_command_name(command) try: self.commands_loader.command_table = { command: self.commands_loader.command_table[command] } except KeyError: # Trim down the command table to reduce the number of subparsers required to optimize the performance. # # When given a command table like this: # # network application-gateway create # network application-gateway delete # network list-usages # storage account create # storage account list # # input: az # output: network application-gateway create # storage account create # # input: az network # output: network application-gateway create # network list-usages cmd_table = {} group_names = set() for cmd_name, cmd in self.commands_loader.command_table.items(): if command and not cmd_name.startswith(command): continue cmd_stub = cmd_name[len(command):].strip() group_name = cmd_stub.split(' ', 1)[0] if group_name not in group_names: cmd_table[cmd_name] = cmd group_names.add(group_name) self.commands_loader.command_table = cmd_table self.commands_loader.command_table = self.commands_loader.command_table # update with the truncated table self.commands_loader.command_name = command self.commands_loader.load_arguments(command) self.cli_ctx.raise_event(EVENT_INVOKER_POST_CMD_TBL_CREATE, commands_loader=self.commands_loader) self.parser.cli_ctx = self.cli_ctx self.parser.load_command_table(self.commands_loader) self.cli_ctx.raise_event(EVENT_INVOKER_CMD_TBL_LOADED, cmd_tbl=self.commands_loader.command_table, parser=self.parser) arg_check = [a for a in args if a not in ['--debug', '--verbose']] if not arg_check: self.parser.enable_autocomplete() subparser = self.parser.subparsers[tuple()] self.help.show_welcome(subparser) # TODO: No event in base with which to target telemetry.set_command_details('az') telemetry.set_success(summary='welcome') return CommandResultItem(None, exit_code=0) if args[0].lower() == 'help': args[0] = '--help' self.parser.enable_autocomplete() self.cli_ctx.raise_event(EVENT_INVOKER_PRE_PARSE_ARGS, args=args) parsed_args = self.parser.parse_args(args) self.cli_ctx.raise_event(EVENT_INVOKER_POST_PARSE_ARGS, command=parsed_args.command, args=parsed_args) # TODO: This fundamentally alters the way Knack.invocation works here. Cannot be customized # with an event. Would need to be customized via inheritance. cmd = parsed_args.func self.cli_ctx.data['command'] = parsed_args.command self.cli_ctx.data[ 'safe_params'] = AzCliCommandInvoker._extract_parameter_names(args) command_source = self.commands_loader.command_table[ command].command_source extension_version = None extension_name = None try: if isinstance(command_source, ExtensionCommandSource): extension_name = command_source.extension_name extension_version = get_extension( command_source.extension_name).version except Exception: # pylint: disable=broad-except pass telemetry.set_command_details(self.cli_ctx.data['command'], self.data['output'], self.cli_ctx.data['safe_params'], extension_name=extension_name, extension_version=extension_version) if extension_name: self.data['command_extension_name'] = extension_name self.resolve_warnings(cmd, parsed_args) self.resolve_confirmation(cmd, parsed_args) jobs = [] for expanded_arg in _explode_list_args(parsed_args): cmd_copy = copy.copy(cmd) cmd_copy.cli_ctx = copy.copy(cmd.cli_ctx) cmd_copy.cli_ctx.data = copy.deepcopy(cmd.cli_ctx.data) expanded_arg.cmd = cmd_copy if hasattr(expanded_arg, '_subscription'): cmd_copy.cli_ctx.data[ 'subscription_id'] = expanded_arg._subscription # pylint: disable=protected-access self._validation(expanded_arg) jobs.append((expanded_arg, cmd_copy)) ids = getattr(parsed_args, '_ids', None) or [None] * len(jobs) if self.cli_ctx.config.getboolean('core', 'disable_concurrent_ids', False) or len(ids) < 2: results, exceptions = self._run_jobs_serially(jobs, ids) else: results, exceptions = self._run_jobs_concurrently(jobs, ids) # handle exceptions if len(exceptions) == 1 and not results: ex, id_arg = exceptions[0] raise ex elif exceptions: for exception, id_arg in exceptions: logger.warning('%s: "%s"', id_arg, str(exception)) if not results: return CommandResultItem( None, exit_code=1, error=CLIError('Encountered more than one exception.')) else: logger.warning('Encountered more than one exception.') if results and len(results) == 1: results = results[0] event_data = {'result': results} self.cli_ctx.raise_event(EVENT_INVOKER_FILTER_RESULT, event_data=event_data) return CommandResultItem( event_data['result'], table_transformer=self.commands_loader.command_table[ parsed_args.command].table_transformer, is_query_active=self.data['query_active'])
def add_extension_to_path(extension_name, ext_dir=None): ext_dir = ext_dir or get_extension(extension_name).path sys.path.append(ext_dir)
def remove_extension(extension_name): try: get_extension(extension_name) shutil.rmtree(get_extension_path(extension_name)) except ExtensionNotInstalledException as e: raise CLIError(e)
def execute(self, args): from knack.events import ( EVENT_INVOKER_PRE_CMD_TBL_CREATE, EVENT_INVOKER_POST_CMD_TBL_CREATE, EVENT_INVOKER_CMD_TBL_LOADED, EVENT_INVOKER_PRE_PARSE_ARGS, EVENT_INVOKER_POST_PARSE_ARGS, EVENT_INVOKER_TRANSFORM_RESULT, EVENT_INVOKER_FILTER_RESULT) from knack.util import CommandResultItem, todict from azure.cli.core.commands.events import EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE # TODO: Can't simply be invoked as an event because args are transformed args = _pre_command_table_create(self.cli_ctx, args) self.cli_ctx.raise_event(EVENT_INVOKER_PRE_CMD_TBL_CREATE, args=args) self.commands_loader.load_command_table(args) self.cli_ctx.raise_event( EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE, load_cmd_tbl_func=self.commands_loader.load_command_table, args=args) command = self._rudimentary_get_command(args) telemetry.set_raw_command_name(command) try: self.commands_loader.command_table = { command: self.commands_loader.command_table[command] } except KeyError: # Trim down the command table to reduce the number of subparsers required to optimize the performance. # # When given a command table like this: # # network application-gateway create # network application-gateway delete # network list-usages # storage account create # storage account list # # input: az # output: network application-gateway create # storage account create # # input: az network # output: network application-gateway create # network list-usages cmd_table = {} group_names = set() for cmd_name, cmd in self.commands_loader.command_table.items(): if command and not cmd_name.startswith(command): continue cmd_stub = cmd_name[len(command):].strip() group_name = cmd_stub.split(' ', 1)[0] if group_name not in group_names: cmd_table[cmd_name] = cmd group_names.add(group_name) self.commands_loader.command_table = cmd_table self.commands_loader.command_table = self.commands_loader.command_table # update with the truncated table self.commands_loader.command_name = command self.commands_loader.load_arguments(command) self.cli_ctx.raise_event(EVENT_INVOKER_POST_CMD_TBL_CREATE, cmd_tbl=self.commands_loader.command_table) self.parser.cli_ctx = self.cli_ctx self.parser.load_command_table(self.commands_loader.command_table) self.cli_ctx.raise_event(EVENT_INVOKER_CMD_TBL_LOADED, cmd_tbl=self.commands_loader.command_table, parser=self.parser) if not args: self.cli_ctx.completion.enable_autocomplete(self.parser) subparser = self.parser.subparsers[tuple()] self.help.show_welcome(subparser) # TODO: No event in base with which to target telemetry.set_command_details('az') telemetry.set_success(summary='welcome') return None if args[0].lower() == 'help': args[0] = '--help' self.cli_ctx.completion.enable_autocomplete(self.parser) self.cli_ctx.raise_event(EVENT_INVOKER_PRE_PARSE_ARGS, args=args) parsed_args = self.parser.parse_args(args) self.cli_ctx.raise_event(EVENT_INVOKER_POST_PARSE_ARGS, command=parsed_args.command, args=parsed_args) # TODO: This fundamentally alters the way Knack.invocation works here. Cannot be customized # with an event. Would need to be customized via inheritance. results = [] for expanded_arg in _explode_list_args(parsed_args): cmd = expanded_arg.func if hasattr(expanded_arg, 'cmd'): expanded_arg.cmd = cmd self.cli_ctx.data['command'] = expanded_arg.command self._validation(expanded_arg) params = self._filter_params(expanded_arg) command_source = self.commands_loader.command_table[ command].command_source extension_version = None try: if command_source: extension_version = get_extension( command_source.extension_name).version except Exception: # pylint: disable=broad-except pass telemetry.set_command_details( self.cli_ctx.data['command'], self.data['output'], [(p.split('=', 1)[0] if p.startswith('--') else p[:2]) for p in args if (p.startswith('-') and len(p) > 1)], extension_name=command_source.extension_name if command_source else None, extension_version=extension_version) if command_source: self.data[ 'command_extension_name'] = command_source.extension_name try: result = cmd(params) if cmd.supports_no_wait and getattr(expanded_arg, 'no_wait', False): result = None elif cmd.no_wait_param and getattr(expanded_arg, cmd.no_wait_param, False): result = None # TODO: Not sure how to make this actually work with the TRANSFORM event... transform_op = cmd.command_kwargs.get('transform', None) if transform_op: result = transform_op(result) if _is_poller(result): result = LongRunningOperation( self.cli_ctx, 'Starting {}'.format(cmd.name))(result) elif _is_paged(result): result = list(result) result = todict(result) event_data = {'result': result} self.cli_ctx.raise_event(EVENT_INVOKER_TRANSFORM_RESULT, event_data=event_data) self.cli_ctx.raise_event(EVENT_INVOKER_FILTER_RESULT, event_data=event_data) result = event_data['result'] results.append(result) except Exception as ex: # pylint: disable=broad-except if cmd.exception_handler: cmd.exception_handler(ex) return None else: six.reraise(*sys.exc_info()) if results and len(results) == 1: results = results[0] return CommandResultItem( results, table_transformer=self.commands_loader.command_table[ parsed_args.command].table_transformer, is_query_active=self.data['query_active'])
def test_wheel_metadata1(self): _install_test_extension1(system=True) ext = get_extension(EXT_NAME) # There should be no exceptions and metadata should have some value self.assertTrue(ext.metadata)
def test_wheel_version(self): _install_test_extension1() ext = get_extension(EXT_NAME) self.assertEqual(ext.version, EXT_VERSION)
def test_wheel_metadata1(self): _install_test_extension1() ext = get_extension(EXT_NAME) # There should be no exceptions and metadata should have some value self.assertTrue(ext.metadata)
def test_wheel_type(self): _install_test_extension1() ext = get_extension(EXT_NAME) self.assertEqual(ext.ext_type, 'whl')
def test_get_extension(self): _install_test_extension1() actual = get_extension(EXT_NAME) self.assertEqual(actual.name, EXT_NAME)
def test_get_extension_not_installed(self): with self.assertRaises(ExtensionNotInstalledException): get_extension(EXT_NAME)
def execute(self, args): from knack.events import (EVENT_INVOKER_PRE_CMD_TBL_CREATE, EVENT_INVOKER_POST_CMD_TBL_CREATE, EVENT_INVOKER_CMD_TBL_LOADED, EVENT_INVOKER_PRE_PARSE_ARGS, EVENT_INVOKER_POST_PARSE_ARGS, EVENT_INVOKER_TRANSFORM_RESULT, EVENT_INVOKER_FILTER_RESULT) from knack.util import CommandResultItem, todict from azure.cli.core.commands.events import EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE # TODO: Can't simply be invoked as an event because args are transformed args = _pre_command_table_create(self.cli_ctx, args) self.cli_ctx.raise_event(EVENT_INVOKER_PRE_CMD_TBL_CREATE, args=args) self.commands_loader.load_command_table(args) self.cli_ctx.raise_event(EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE, load_cmd_tbl_func=self.commands_loader.load_command_table, args=args) command = self._rudimentary_get_command(args) telemetry.set_raw_command_name(command) try: self.commands_loader.command_table = {command: self.commands_loader.command_table[command]} except KeyError: # Trim down the command table to reduce the number of subparsers required to optimize the performance. # # When given a command table like this: # # network application-gateway create # network application-gateway delete # network list-usages # storage account create # storage account list # # input: az # output: network application-gateway create # storage account create # # input: az network # output: network application-gateway create # network list-usages cmd_table = {} group_names = set() for cmd_name, cmd in self.commands_loader.command_table.items(): if command and not cmd_name.startswith(command): continue cmd_stub = cmd_name[len(command):].strip() group_name = cmd_stub.split(' ', 1)[0] if group_name not in group_names: cmd_table[cmd_name] = cmd group_names.add(group_name) self.commands_loader.command_table = cmd_table self.commands_loader.command_table = self.commands_loader.command_table # update with the truncated table self.commands_loader.command_name = command self.commands_loader.load_arguments(command) self.cli_ctx.raise_event(EVENT_INVOKER_POST_CMD_TBL_CREATE, commands_loader=self.commands_loader) self.parser.cli_ctx = self.cli_ctx self.parser.load_command_table(self.commands_loader) self.cli_ctx.raise_event(EVENT_INVOKER_CMD_TBL_LOADED, cmd_tbl=self.commands_loader.command_table, parser=self.parser) if not args: self.parser.enable_autocomplete() subparser = self.parser.subparsers[tuple()] self.help.show_welcome(subparser) # TODO: No event in base with which to target telemetry.set_command_details('az') telemetry.set_success(summary='welcome') return None if args[0].lower() == 'help': args[0] = '--help' self.parser.enable_autocomplete() self.cli_ctx.raise_event(EVENT_INVOKER_PRE_PARSE_ARGS, args=args) parsed_args = self.parser.parse_args(args) self.cli_ctx.raise_event(EVENT_INVOKER_POST_PARSE_ARGS, command=parsed_args.command, args=parsed_args) # TODO: This fundamentally alters the way Knack.invocation works here. Cannot be customized # with an event. Would need to be customized via inheritance. results = [] for expanded_arg in _explode_list_args(parsed_args): cmd = expanded_arg.func if hasattr(expanded_arg, 'cmd'): expanded_arg.cmd = cmd self.cli_ctx.data['command'] = expanded_arg.command self._validation(expanded_arg) params = self._filter_params(expanded_arg) command_source = self.commands_loader.command_table[command].command_source extension_version = None extension_name = None try: if isinstance(command_source, ExtensionCommandSource): extension_name = command_source.extension_name extension_version = get_extension(command_source.extension_name).version except Exception: # pylint: disable=broad-except pass telemetry.set_command_details(self.cli_ctx.data['command'], self.data['output'], [(p.split('=', 1)[0] if p.startswith('--') else p[:2]) for p in args if (p.startswith('-') and len(p) > 1)], extension_name=extension_name, extension_version=extension_version) if extension_name: self.data['command_extension_name'] = extension_name deprecations = [] + getattr(expanded_arg, '_argument_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)) del path_comps[-1] if implicit_deprecate_info: deprecate_kwargs = implicit_deprecate_info.__dict__.copy() deprecate_kwargs['object_type'] = 'command' del deprecate_kwargs['_get_tag'] del deprecate_kwargs['_get_message'] deprecations.append(ImplicitDeprecated(**deprecate_kwargs)) for d in deprecations: logger.warning(d.message) try: result = cmd(params) if cmd.supports_no_wait and getattr(expanded_arg, 'no_wait', False): result = None elif cmd.no_wait_param and getattr(expanded_arg, cmd.no_wait_param, False): result = None transform_op = cmd.command_kwargs.get('transform', None) if transform_op: result = transform_op(result) if _is_poller(result): result = LongRunningOperation(self.cli_ctx, 'Starting {}'.format(cmd.name))(result) elif _is_paged(result): result = list(result) result = todict(result, AzCliCommandInvoker.remove_additional_prop_layer) event_data = {'result': result} self.cli_ctx.raise_event(EVENT_INVOKER_TRANSFORM_RESULT, event_data=event_data) result = event_data['result'] results.append(result) except Exception as ex: # pylint: disable=broad-except if cmd.exception_handler: cmd.exception_handler(ex) return None else: six.reraise(*sys.exc_info()) if results and len(results) == 1: results = results[0] event_data = {'result': results} self.cli_ctx.raise_event(EVENT_INVOKER_FILTER_RESULT, event_data=event_data) return CommandResultItem( event_data['result'], table_transformer=self.commands_loader.command_table[parsed_args.command].table_transformer, is_query_active=self.data['query_active'])
def execute(self, args): from knack.events import (EVENT_INVOKER_PRE_CMD_TBL_CREATE, EVENT_INVOKER_POST_CMD_TBL_CREATE, EVENT_INVOKER_CMD_TBL_LOADED, EVENT_INVOKER_PRE_PARSE_ARGS, EVENT_INVOKER_POST_PARSE_ARGS, EVENT_INVOKER_FILTER_RESULT) from azure.cli.core.commands.events import EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE # TODO: Can't simply be invoked as an event because args are transformed args = _pre_command_table_create(self.cli_ctx, args) self.cli_ctx.raise_event(EVENT_INVOKER_PRE_CMD_TBL_CREATE, args=args) self.commands_loader.load_command_table(args) self.cli_ctx.raise_event(EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE, load_cmd_tbl_func=self.commands_loader.load_command_table, args=args) command = self._rudimentary_get_command(args) self.cli_ctx.invocation.data['command_string'] = command telemetry.set_raw_command_name(command) try: self.commands_loader.command_table = {command: self.commands_loader.command_table[command]} except KeyError: # Trim down the command table to reduce the number of subparsers required to optimize the performance. # # When given a command table like this: # # network application-gateway create # network application-gateway delete # network list-usages # storage account create # storage account list # # input: az # output: network application-gateway create # storage account create # # input: az network # output: network application-gateway create # network list-usages cmd_table = {} group_names = set() for cmd_name, cmd in self.commands_loader.command_table.items(): if command and not cmd_name.startswith(command): continue cmd_stub = cmd_name[len(command):].strip() group_name = cmd_stub.split(' ', 1)[0] if group_name not in group_names: cmd_table[cmd_name] = cmd group_names.add(group_name) self.commands_loader.command_table = cmd_table self.commands_loader.command_table = self.commands_loader.command_table # update with the truncated table self.commands_loader.command_name = command self.commands_loader.load_arguments(command) self.cli_ctx.raise_event(EVENT_INVOKER_POST_CMD_TBL_CREATE, commands_loader=self.commands_loader) self.parser.cli_ctx = self.cli_ctx self.parser.load_command_table(self.commands_loader) self.cli_ctx.raise_event(EVENT_INVOKER_CMD_TBL_LOADED, cmd_tbl=self.commands_loader.command_table, parser=self.parser) arg_check = [a for a in args if a not in ['--debug', '--verbose']] if not arg_check: self.parser.enable_autocomplete() subparser = self.parser.subparsers[tuple()] self.help.show_welcome(subparser) # TODO: No event in base with which to target telemetry.set_command_details('az') telemetry.set_success(summary='welcome') return CommandResultItem(None, exit_code=0) if args[0].lower() == 'help': args[0] = '--help' self.parser.enable_autocomplete() self.cli_ctx.raise_event(EVENT_INVOKER_PRE_PARSE_ARGS, args=args) parsed_args = self.parser.parse_args(args) self.cli_ctx.raise_event(EVENT_INVOKER_POST_PARSE_ARGS, command=parsed_args.command, args=parsed_args) # TODO: This fundamentally alters the way Knack.invocation works here. Cannot be customized # with an event. Would need to be customized via inheritance. cmd = parsed_args.func self.cli_ctx.data['command'] = parsed_args.command self.cli_ctx.data['safe_params'] = AzCliCommandInvoker._extract_parameter_names(args) command_source = self.commands_loader.command_table[command].command_source extension_version = None extension_name = None try: if isinstance(command_source, ExtensionCommandSource): extension_name = command_source.extension_name extension_version = get_extension(command_source.extension_name).version except Exception: # pylint: disable=broad-except pass telemetry.set_command_details(self.cli_ctx.data['command'], self.data['output'], self.cli_ctx.data['safe_params'], extension_name=extension_name, extension_version=extension_version) if extension_name: self.data['command_extension_name'] = extension_name self.resolve_warnings(cmd, parsed_args) self.resolve_confirmation(cmd, parsed_args) jobs = [] for expanded_arg in _explode_list_args(parsed_args): cmd_copy = copy.copy(cmd) cmd_copy.cli_ctx = copy.copy(cmd.cli_ctx) cmd_copy.cli_ctx.data = copy.deepcopy(cmd.cli_ctx.data) expanded_arg.cmd = expanded_arg._cmd = cmd_copy if hasattr(expanded_arg, '_subscription'): cmd_copy.cli_ctx.data['subscription_id'] = expanded_arg._subscription # pylint: disable=protected-access self._validation(expanded_arg) jobs.append((expanded_arg, cmd_copy)) ids = getattr(parsed_args, '_ids', None) or [None] * len(jobs) if self.cli_ctx.config.getboolean('core', 'disable_concurrent_ids', False) or len(ids) < 2: results, exceptions = self._run_jobs_serially(jobs, ids) else: results, exceptions = self._run_jobs_concurrently(jobs, ids) # handle exceptions if len(exceptions) == 1 and not results: ex, id_arg = exceptions[0] raise ex elif exceptions: for exception, id_arg in exceptions: logger.warning('%s: "%s"', id_arg, str(exception)) if not results: return CommandResultItem(None, exit_code=1, error=CLIError('Encountered more than one exception.')) logger.warning('Encountered more than one exception.') if results and len(results) == 1: results = results[0] event_data = {'result': results} self.cli_ctx.raise_event(EVENT_INVOKER_FILTER_RESULT, event_data=event_data) return CommandResultItem( event_data['result'], table_transformer=self.commands_loader.command_table[parsed_args.command].table_transformer, is_query_active=self.data['query_active'])