Skip to content

Conversation

@DanielMicrosoft
Copy link
Contributor

@DanielMicrosoft DanielMicrosoft commented Feb 2, 2026

Related command

Description

Version 2 of the previously reverted attempt to thread module loading, when building the command index:
#32518

More info on situations that trigger a build here.

A look at the speed improvements from the changes in this PR here.

Testing Guide

History Notes

[Component Name 1] BREAKING CHANGE: az command a: Make some customer-facing breaking change
[Component Name 2] az command b: Add some customer-facing feature


This checklist is used to make sure that common guidelines for a pull request are followed.

@azure-client-tools-bot-prd
Copy link

azure-client-tools-bot-prd bot commented Feb 2, 2026

️✔️AzureCLI-FullTest
️✔️acr
️✔️latest
️✔️3.12
️✔️3.13
️✔️acs
️✔️latest
️✔️3.12
️✔️3.13
️✔️advisor
️✔️latest
️✔️3.12
️✔️3.13
️✔️ams
️✔️latest
️✔️3.12
️✔️3.13
️✔️apim
️✔️latest
️✔️3.12
️✔️3.13
️✔️appconfig
️✔️latest
️✔️3.12
️✔️3.13
️✔️appservice
️✔️latest
️✔️3.12
️✔️3.13
️✔️aro
️✔️latest
️✔️3.12
️✔️3.13
️✔️backup
️✔️latest
️✔️3.12
️✔️3.13
️✔️batch
️✔️latest
️✔️3.12
️✔️3.13
️✔️batchai
️✔️latest
️✔️3.12
️✔️3.13
️✔️billing
️✔️latest
️✔️3.12
️✔️3.13
️✔️botservice
️✔️latest
️✔️3.12
️✔️3.13
️✔️cdn
️✔️latest
️✔️3.12
️✔️3.13
️✔️cloud
️✔️latest
️✔️3.12
️✔️3.13
️✔️cognitiveservices
️✔️latest
️✔️3.12
️✔️3.13
️✔️compute_recommender
️✔️latest
️✔️3.12
️✔️3.13
️✔️computefleet
️✔️latest
️✔️3.12
️✔️3.13
️✔️config
️✔️latest
️✔️3.12
️✔️3.13
️✔️configure
️✔️latest
️✔️3.12
️✔️3.13
️✔️consumption
️✔️latest
️✔️3.12
️✔️3.13
️✔️container
️✔️latest
️✔️3.12
️✔️3.13
️✔️containerapp
️✔️latest
️✔️3.12
️✔️3.13
️✔️core
️✔️latest
️✔️3.12
️✔️3.13
️✔️cosmosdb
️✔️latest
️✔️3.12
️✔️3.13
️✔️databoxedge
️✔️latest
️✔️3.12
️✔️3.13
️✔️dls
️✔️latest
️✔️3.12
️✔️3.13
️✔️dms
️✔️latest
️✔️3.12
️✔️3.13
️✔️eventgrid
️✔️latest
️✔️3.12
️✔️3.13
️✔️eventhubs
️✔️latest
️✔️3.12
️✔️3.13
️✔️feedback
️✔️latest
️✔️3.12
️✔️3.13
️✔️find
️✔️latest
️✔️3.12
️✔️3.13
️✔️hdinsight
️✔️latest
️✔️3.12
️✔️3.13
️✔️identity
️✔️latest
️✔️3.12
️✔️3.13
️✔️iot
️✔️latest
️✔️3.12
️✔️3.13
️✔️keyvault
️✔️latest
️✔️3.12
️✔️3.13
️✔️lab
️✔️latest
️✔️3.12
️✔️3.13
️✔️managedservices
️✔️latest
️✔️3.12
️✔️3.13
️✔️maps
️✔️latest
️✔️3.12
️✔️3.13
️✔️marketplaceordering
️✔️latest
️✔️3.12
️✔️3.13
️✔️monitor
️✔️latest
️✔️3.12
️✔️3.13
️✔️mysql
️✔️latest
️✔️3.12
️✔️3.13
️✔️netappfiles
️✔️latest
️✔️3.12
️✔️3.13
️✔️network
️✔️latest
️✔️3.12
️✔️3.13
️✔️policyinsights
️✔️latest
️✔️3.12
️✔️3.13
️✔️postgresql
️✔️latest
️✔️3.12
️✔️3.13
️✔️privatedns
️✔️latest
️✔️3.12
️✔️3.13
️✔️profile
️✔️latest
️✔️3.12
️✔️3.13
️✔️rdbms
️✔️latest
️✔️3.12
️✔️3.13
️✔️redis
️✔️latest
️✔️3.12
️✔️3.13
️✔️relay
️✔️latest
️✔️3.12
️✔️3.13
️✔️resource
️✔️latest
️✔️3.12
️✔️3.13
️✔️role
️✔️latest
️✔️3.12
️✔️3.13
️✔️search
️✔️latest
️✔️3.12
️✔️3.13
️✔️security
️✔️latest
️✔️3.12
️✔️3.13
️✔️servicebus
️✔️latest
️✔️3.12
️✔️3.13
️✔️serviceconnector
️✔️latest
️✔️3.12
️✔️3.13
️✔️servicefabric
️✔️latest
️✔️3.12
️✔️3.13
️✔️signalr
️✔️latest
️✔️3.12
️✔️3.13
️✔️sql
️✔️latest
️✔️3.12
️✔️3.13
️✔️sqlvm
️✔️latest
️✔️3.12
️✔️3.13
️✔️storage
️✔️latest
️✔️3.12
️✔️3.13
️✔️synapse
️✔️latest
️✔️3.12
️✔️3.13
️✔️telemetry
️✔️latest
️✔️3.12
️✔️3.13
️✔️util
️✔️latest
️✔️3.12
️✔️3.13
️✔️vm
️✔️latest
️✔️3.12
️✔️3.13

@yonzhan
Copy link
Collaborator

yonzhan commented Feb 2, 2026

Thank you for your contribution! We will review the pull request and get back to you soon.

@azure-client-tools-bot-prd
Copy link

azure-client-tools-bot-prd bot commented Feb 2, 2026

️✔️AzureCLI-BreakingChangeTest
️✔️Non Breaking Changes

@github-actions
Copy link

github-actions bot commented Feb 2, 2026

The git hooks are available for azure-cli and azure-cli-extensions repos. They could help you run required checks before creating the PR.

Please sync the latest code with latest dev branch (for azure-cli) or main branch (for azure-cli-extensions).
After that please run the following commands to enable git hooks:

pip install azdev --upgrade
azdev setup -c <your azure-cli repo path> -r <your azure-cli-extensions repo path>

@DanielMicrosoft DanielMicrosoft marked this pull request as ready for review February 4, 2026 05:51
Copilot AI review requested due to automatic review settings February 4, 2026 05:51
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the core command-loading pipeline to load command modules in parallel, aiming to reduce command index rebuild time while preserving command table semantics.

Changes:

  • Introduces a threaded module-loading mechanism in MainCommandsLoader (azure.cli.core.__init__), along with ModuleLoadResult, timeout/worker-count configuration, and refactored error-handling and telemetry for module load failures.
  • Refactors _load_command_loader / _load_module_command_loader / _load_extension_command_loader in azure.cli.core.commands.__init__ to return the command_loader object in addition to command and group tables, moving population of loaders and cmd_to_loader_map into MainCommandsLoader.
  • Adds and updates tests to validate command table integrity, command-to-loader mapping after parallel loading, and to make existing expectations order-insensitive.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/azure-cli-core/azure/cli/core/__init__.py Adds threaded module loading (_load_modules, _load_single_module) and ModuleLoadResult, refactors load_command_table to use pre-loaded results, maintains cmd_to_loader_map and command_source, and introduces timeout/worker-count constants and top-level completion helpers.
src/azure-cli-core/azure/cli/core/commands/__init__.py Changes _load_command_loader and wrappers to return (command_table, group_table, command_loader), deferring loaders/cmd_to_loader_map updates to MainCommandsLoader.
src/azure-cli-core/azure/cli/core/tests/test_command_registration.py Updates the mocked _load_command_loader to the new 3-tuple signature, adds test_cmd_to_loader_map_populated_after_parallel_loading, and relaxes some assertions to be order-insensitive to accommodate parallel loading.
src/azure-cli-core/azure/cli/core/tests/test_parser.py Updates the local _mock_load_command_loader used for parser tests to return the new 3-tuple, keeping it consistent with the refactored core loader API.
src/azure-cli-core/azure/cli/core/tests/test_command_table_integrity.py Adds a new end-to-end test verifying there are no duplicate commands, core command groups exist, every command has a command_source, and that command and group tables are non-empty after loading via the new pipeline.

In addition to the inline comment already recorded, note that changing _load_command_loader to return a 3-tuple will break any remaining callers that still unpack only two values (for example, test_help.mock_load_command_loader currently does module_command_table, module_group_table = mock_load_command_loader(...)), so those sites need to be updated to handle the new (command_table, group_table, command_loader) contract.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +598 to +615
for future in concurrent.futures.as_completed(future_to_module):
try:
result = future.result(timeout=MODULE_LOAD_TIMEOUT_SECONDS)
results.append(result)
except concurrent.futures.TimeoutError:
mod = future_to_module[future]
logger.warning("Module '%s' load timeout after %s seconds", mod, MODULE_LOAD_TIMEOUT_SECONDS)
results.append(ModuleLoadResult(mod, {}, {}, 0,
Exception(f"Module '{mod}' load timeout")))
except (ImportError, AttributeError, TypeError, ValueError) as ex:
mod = future_to_module[future]
logger.warning("Module '%s' load failed: %s", mod, ex)
results.append(ModuleLoadResult(mod, {}, {}, 0, ex))
except Exception as ex: # pylint: disable=broad-exception-caught
mod = future_to_module[future]
logger.warning("Module '%s' load failed with unexpected exception: %s", mod, ex)
results.append(ModuleLoadResult(mod, {}, {}, 0, ex))

Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MODULE_LOAD_TIMEOUT_SECONDS is currently ineffective because future.result(timeout=MODULE_LOAD_TIMEOUT_SECONDS) is only called on futures yielded by concurrent.futures.as_completed, which guarantees the future is already complete. That means a hung module load will block inside as_completed indefinitely and never raise TimeoutError, contrary to the timeout comment and constant. To actually enforce a per-module timeout, the timeout needs to be applied at the as_completed/wait level (or by starting a timed wait and then cancelling unfinished futures), rather than on future.result() for already-completed futures.

Suggested change
for future in concurrent.futures.as_completed(future_to_module):
try:
result = future.result(timeout=MODULE_LOAD_TIMEOUT_SECONDS)
results.append(result)
except concurrent.futures.TimeoutError:
mod = future_to_module[future]
logger.warning("Module '%s' load timeout after %s seconds", mod, MODULE_LOAD_TIMEOUT_SECONDS)
results.append(ModuleLoadResult(mod, {}, {}, 0,
Exception(f"Module '{mod}' load timeout")))
except (ImportError, AttributeError, TypeError, ValueError) as ex:
mod = future_to_module[future]
logger.warning("Module '%s' load failed: %s", mod, ex)
results.append(ModuleLoadResult(mod, {}, {}, 0, ex))
except Exception as ex: # pylint: disable=broad-exception-caught
mod = future_to_module[future]
logger.warning("Module '%s' load failed with unexpected exception: %s", mod, ex)
results.append(ModuleLoadResult(mod, {}, {}, 0, ex))
# Wait for all module loads up to the configured timeout.
done, not_done = concurrent.futures.wait(
future_to_module.keys(),
timeout=MODULE_LOAD_TIMEOUT_SECONDS
)
# Process all futures that completed within the timeout.
for future in done:
mod = future_to_module[future]
try:
result = future.result()
results.append(result)
except (ImportError, AttributeError, TypeError, ValueError) as ex:
logger.warning("Module '%s' load failed: %s", mod, ex)
results.append(ModuleLoadResult(mod, {}, {}, 0, ex))
except Exception as ex: # pylint: disable=broad-exception-caught
logger.warning("Module '%s' load failed with unexpected exception: %s", mod, ex)
results.append(ModuleLoadResult(mod, {}, {}, 0, ex))
# Any futures still not done after the timeout are treated as timed out.
for future in not_done:
mod = future_to_module[future]
# Best effort to stop further work on this module load.
future.cancel()
logger.warning("Module '%s' load timeout after %s seconds", mod, MODULE_LOAD_TIMEOUT_SECONDS)
results.append(
ModuleLoadResult(
mod,
{},
{},
0,
Exception(f"Module '{mod}' load timeout")
)
)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Auto-Assign Auto assign by bot Core CLI core infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants