-
Notifications
You must be signed in to change notification settings - Fork 3.3k
{Core} Fix #14175: Load ALWAYS_LOADED_EXTENSIONS correctly #14180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
21a9bf4
Load ALWAYS_LOADED_EXTENSIONS correctly
jiasli 65c5a31
Fix debug log header
jiasli 501e86c
Add tests
jiasli 2ec582b
Update
jiasli cd45d52
linter
jiasli 564a8a2
Add comments
jiasli 28a5a6e
Show debug log for not-installed extensions
jiasli File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,8 +30,12 @@ | |
| EXCLUDED_PARAMS = ['self', 'raw', 'polling', 'custom_headers', 'operation_config', | ||
| 'content_version', 'kwargs', 'client', 'no_wait'] | ||
| EVENT_FAILED_EXTENSION_LOAD = 'MainLoader.OnFailedExtensionLoad' | ||
| # Extensions that will always be loaded if installed. These extensions don't expose commands but hook into CLI core. | ||
| ALWAYS_LOADED_EXTENSION_MODNAMES = ['azext_ai_examples', 'azext_ai_did_you_mean_this'] | ||
|
|
||
| # [Reserved, in case of future usage] | ||
| # Modules that will always be loaded. They don't expose commands but hook into CLI core. | ||
| ALWAYS_LOADED_MODULES = [] | ||
| # Extensions that will always be loaded if installed. They don't expose commands but hook into CLI core. | ||
| ALWAYS_LOADED_EXTENSIONS = ['azext_ai_examples', 'azext_ai_did_you_mean_this'] | ||
|
|
||
|
|
||
| class AzCli(CLI): | ||
|
|
@@ -153,7 +157,7 @@ def save_local_context(self, parsed_args, argument_definitions, specified_argume | |
| class MainCommandsLoader(CLICommandsLoader): | ||
|
|
||
| # Format string for pretty-print the command module table | ||
| header_mod = "%-20s %10s %9s %9s" % ("Extension", "Load Time", "Groups", "Commands") | ||
| header_mod = "%-20s %10s %9s %9s" % ("Name", "Load Time", "Groups", "Commands") | ||
| item_format_string = "%-20s %10.3f %9d %9d" | ||
| header_ext = header_mod + " Directory" | ||
| item_ext_format_string = item_format_string + " %s" | ||
|
|
@@ -181,12 +185,19 @@ def load_command_table(self, args): | |
| get_extensions, get_extension_path, get_extension_modname) | ||
|
|
||
| def _update_command_table_from_modules(args, command_modules=None): | ||
| '''Loads command table(s) | ||
| When `module_name` is specified, only commands from that module will be loaded. | ||
| If the module is not found, all commands are loaded. | ||
| ''' | ||
|
|
||
| if not command_modules: | ||
| """Loads command tables from modules and merge into the main command table. | ||
|
|
||
| :param args: Arguments of the command. | ||
| :param list command_modules: Command modules to load, in the format like ['resource', 'profile']. | ||
| If None, will do module discovery and load all modules. | ||
| If [], only ALWAYS_LOADED_MODULES will be loaded. | ||
| Otherwise, the list will be extended using ALWAYS_LOADED_MODULES. | ||
| """ | ||
|
|
||
| # As command modules are built-in, the existence of modules in ALWAYS_LOADED_MODULES is NOT checked | ||
| if command_modules is not None: | ||
| command_modules.extend(ALWAYS_LOADED_MODULES) | ||
| else: | ||
| # Perform module discovery | ||
| command_modules = [] | ||
| try: | ||
|
|
@@ -234,6 +245,15 @@ def _update_command_table_from_modules(args, command_modules=None): | |
| cumulative_group_count, cumulative_command_count) | ||
|
|
||
| def _update_command_table_from_extensions(ext_suppressions, extension_modname=None): | ||
| """Loads command tables from extensions and merge into the main command table. | ||
|
|
||
| :param ext_suppressions: Extension suppression information. | ||
| :param extension_modname: Command modules to load, in the format like ['azext_timeseriesinsights']. | ||
| If None, will do extension discovery and load all extensions. | ||
| If [], only ALWAYS_LOADED_EXTENSIONS will be loaded. | ||
| Otherwise, the list will be extended using ALWAYS_LOADED_EXTENSIONS. | ||
| If the extensions in the list are not installed, it will be skipped. | ||
| """ | ||
|
|
||
| from azure.cli.core.extension.operations import check_version_compatibility | ||
|
|
||
|
|
@@ -251,19 +271,20 @@ def _handle_extension_suppressions(extensions): | |
| def _filter_modname(extensions): | ||
| # Extension's name may not be the same as its modname. eg. name: virtual-wan, modname: azext_vwan | ||
| filtered_extensions = [] | ||
| extension_modname.extend(ALWAYS_LOADED_EXTENSION_MODNAMES) | ||
| for ext in extensions: | ||
| ext_name = ext.name | ||
| ext_dir = ext.path or get_extension_path(ext.name) | ||
| ext_mod = get_extension_modname(ext_name, ext_dir=ext_dir) | ||
| ext_mod = get_extension_modname(ext.name, ext.path) | ||
| # Filter the extensions according to the index | ||
| if ext_mod in extension_modname: | ||
| filtered_extensions.append(ext) | ||
| extension_modname.remove(ext_mod) | ||
| if extension_modname: | ||
| logger.debug("These extensions are not installed and will be skipped: %s", extension_modname) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will show something like |
||
| return filtered_extensions | ||
|
|
||
| extensions = get_extensions() | ||
| if extensions: | ||
| if extension_modname: | ||
| if extension_modname is not None: | ||
| extension_modname.extend(ALWAYS_LOADED_EXTENSIONS) | ||
| extensions = _filter_modname(extensions) | ||
| allowed_extensions = _handle_extension_suppressions(extensions) | ||
| module_commands = set(self.command_table.keys()) | ||
|
|
@@ -375,18 +396,21 @@ def _roughly_parse_command(args): | |
| index_result = command_index.get(args) | ||
| if index_result: | ||
| index_modules, index_extensions = index_result | ||
| if index_modules: | ||
| _update_command_table_from_modules(args, index_modules) | ||
| if index_extensions: | ||
| # The index won't contain suppressed extensions | ||
| _update_command_table_from_extensions([], index_extensions) | ||
| # Always load modules and extensions, because some of them (like those in | ||
| # ALWAYS_LOADED_EXTENSIONS) don't expose a command, but hooks into handlers in CLI core | ||
| _update_command_table_from_modules(args, index_modules) | ||
| # The index won't contain suppressed extensions | ||
| _update_command_table_from_extensions([], index_extensions) | ||
|
|
||
| logger.debug("Loaded %d groups, %d commands.", len(self.command_group_table), len(self.command_table)) | ||
| # The index may be outdated. Make sure the command appears in the loaded command table | ||
| command_str = _roughly_parse_command(args) | ||
| if command_str in self.command_table or command_str in self.command_group_table: | ||
| if command_str in self.command_table: | ||
| logger.debug("Found a match in the command table for '%s'", command_str) | ||
| return self.command_table | ||
| if command_str in self.command_group_table: | ||
| logger.debug("Found a match in the command group table for '%s'", command_str) | ||
| return self.command_table | ||
|
Comment on lines
+411
to
+413
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Separate the debug log for found in command table and found in the command group table. |
||
|
|
||
| logger.debug("Could not find a match in the command table for '%s'. The index may be outdated", | ||
| command_str) | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix an incorrect naming.