def test_command_index(self): from azure.cli.core._session import INDEX from azure.cli.core import CommandIndex, __version__ cli = DummyCli() loader = cli.commands_loader command_index = CommandIndex(cli) expected_command_index = { 'hello': [ 'azure.cli.command_modules.hello', 'azext_hello2', 'azext_hello1' ], 'extra': ['azure.cli.command_modules.extra'] } expected_command_table = [ 'hello mod-only', 'hello overridden', 'extra unused', 'hello ext-only' ] def _set_index(dict_): INDEX[CommandIndex._COMMAND_INDEX] = dict_ def _check_index(): self.assertEqual(INDEX[CommandIndex._COMMAND_INDEX_VERSION], __version__) self.assertEqual(INDEX[CommandIndex._COMMAND_INDEX_CLOUD_PROFILE], cli.cloud.profile) self.assertDictEqual(INDEX[CommandIndex._COMMAND_INDEX], expected_command_index) # Clear the command index _set_index({}) self.assertFalse(INDEX[CommandIndex._COMMAND_INDEX]) loader.load_command_table(None) # Test command index is built for None args _check_index() # Test command index is built when `args` is provided _set_index({}) loader.load_command_table(["hello", "mod-only"]) _check_index() # Test rebuild command index if no module found _set_index({"network": ["azure.cli.command_modules.network"]}) loader.load_command_table(["hello", "mod-only"]) _check_index() with mock.patch("azure.cli.core.__version__", "2.7.0"), mock.patch.object(cli.cloud, "profile", "2019-03-01-hybrid"): def update_and_check_index(): loader.load_command_table(["hello", "mod-only"]) self.assertEqual(INDEX[CommandIndex._COMMAND_INDEX_VERSION], "2.7.0") self.assertEqual( INDEX[CommandIndex._COMMAND_INDEX_CLOUD_PROFILE], "2019-03-01-hybrid") self.assertDictEqual(INDEX[CommandIndex._COMMAND_INDEX], expected_command_index) # Test rebuild command index if version is not present del INDEX[CommandIndex._COMMAND_INDEX_VERSION] del INDEX[CommandIndex._COMMAND_INDEX] update_and_check_index() # Test rebuild command index if version is not valid INDEX[CommandIndex._COMMAND_INDEX_VERSION] = "" _set_index({}) update_and_check_index() # Test rebuild command index if version is outdated INDEX[CommandIndex._COMMAND_INDEX_VERSION] = "2.6.0" _set_index({}) update_and_check_index() # Test rebuild command index if profile is outdated INDEX[CommandIndex. _COMMAND_INDEX_CLOUD_PROFILE] = "2017-03-09-profile" _set_index({}) update_and_check_index() # Test rebuild command index if modules are found but outdated # This only happens in dev environment. For users, the version check logic prevents it _set_index({"hello": ["azure.cli.command_modules.extra"]}) loader.load_command_table(["hello", "mod-only"]) _check_index() # Test irrelevant commands are not loaded _set_index(expected_command_index) cmd_tbl = loader.load_command_table(["hello", "mod-only"]) self.assertEqual( ['hello mod-only', 'hello overridden', 'hello ext-only'], list(cmd_tbl.keys())) # Full scenario test 1: Installing an extension 'azext_hello1' that extends 'hello' group outdated_command_index = { 'hello': ['azure.cli.command_modules.hello'], 'extra': ['azure.cli.command_modules.extra'] } _set_index(outdated_command_index) # Command for an outdated group cmd_tbl = loader.load_command_table(["hello", "-h"]) # Index is not updated, and only built-in commands are loaded _set_index(outdated_command_index) self.assertListEqual(list(cmd_tbl), ['hello mod-only', 'hello overridden']) # Command index is explicitly invalidated by azure.cli.core.extension.operations.add_extension command_index.invalidate() cmd_tbl = loader.load_command_table(["hello", "-h"]) # Index is updated, and new commands are loaded _check_index() self.assertListEqual(list(cmd_tbl), expected_command_table) # Full scenario test 2: Installing extension 'azext_hello2' that overrides existing command 'hello overridden' outdated_command_index = { 'hello': ['azure.cli.command_modules.hello', 'azext_hello1'], 'extra': ['azure.cli.command_modules.extra'] } _set_index(outdated_command_index) # Command for an overridden command cmd_tbl = loader.load_command_table(["hello", "overridden"]) # Index is not updated self.assertEqual(INDEX[CommandIndex._COMMAND_INDEX], outdated_command_index) # With the old command index, 'hello overridden' is loaded from the build-in module hello_overridden_cmd = cmd_tbl['hello overridden'] self.assertEqual(hello_overridden_cmd.command_source, 'hello') self.assertListEqual( list(cmd_tbl), ['hello mod-only', 'hello overridden', 'hello ext-only']) # Command index is explicitly invalidated by azure.cli.core.extension.operations.add_extension command_index.invalidate() # Command index is updated, and 'hello overridden' is loaded from the new extension cmd_tbl = loader.load_command_table(["hello", "overridden"]) hello_overridden_cmd = cmd_tbl['hello overridden'] self.assertTrue( isinstance(hello_overridden_cmd.command_source, ExtensionCommandSource)) _check_index() self.assertListEqual(list(cmd_tbl), expected_command_table) # Call again with the new command index. Irrelevant commands are not loaded cmd_tbl = loader.load_command_table(["hello", "overridden"]) hello_overridden_cmd = cmd_tbl['hello overridden'] self.assertTrue( isinstance(hello_overridden_cmd.command_source, ExtensionCommandSource)) _check_index() self.assertListEqual( list(cmd_tbl), ['hello mod-only', 'hello overridden', 'hello ext-only']) del INDEX[CommandIndex._COMMAND_INDEX_VERSION] del INDEX[CommandIndex._COMMAND_INDEX_CLOUD_PROFILE] del INDEX[CommandIndex._COMMAND_INDEX]
def add_extension( cmd=None, source=None, extension_name=None, index_url=None, yes=None, # pylint: disable=unused-argument pip_extra_index_urls=None, pip_proxy=None, system=None, version=None, cli_ctx=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 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, target_version=version) 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) 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) _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) CommandIndex().invalidate() except ExtensionNotInstalledException: pass
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 overriden 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