Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions doc/authoring_command_modules/authoring_commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,9 @@ with self.command_group('mymod', mymod_sdk) as g:
# (3) Registering different types of commands
g.command('command1', 'do_something_1')
g.custom_command('command2', 'do_something_2')
g.generic_update('update', custom_function_name='my_custom_update')
g.generic_wait('wait')
g.generic_update_command('update', custom_function_name='my_custom_update')
g.wait_command('wait')
g.show_command('show')
```

At this point, you should be able to access your command using `az [name]` and access the built-in help with `az [name] -h/--help`. Your command will automatically be 'wired up' with the global parameters. See below for amplifying information.
Expand Down Expand Up @@ -141,24 +142,44 @@ The signature for `custom_command` is exactly the same as `command`. The only di

See the section on "Suppporting Generic Update"

***generic_wait_command***
***wait_command***

The generic wait command provides a templated solution for polling Azure resources until specific conditions are met.

```Python
generic_wait_command(self, name, getter_name='get', getter_type=None, **kwargs)
wait_command(self, name, getter_name='get', **kwargs)
```

- `name`: The name of the command within the command group. Commonly called 'wait'.
- `getter_name`: The name of the method for the object getter, relative to the path specified in `operations_tmpl`.
- `getter_type`: A `CliCommandType` object to apply to this command (optional).
- `kwargs`: any supported kwarg.

Since most wait commands rely on a simple GET call from the SDK, most of these entries simply look like:
```Python
g.generic_wait_command('wait')
g.wait_command('wait')
```

***custom_wait_command***

Similar to `custom_command` and `command`, the signature for `custom_wait_command` is exactly the same as `wait_command` but uses `custom_command_type` as the fallback for missings kwargs.

***show_command***

The generic show command ensures a consistent behavior when encountering a missing Azure resource.
With little exception, all `show` commands should be registered using this method or `custom_show_command` to ensure consistency.

```Python
show_command(self, name, getter_name='get', **kwargs)
```

- `name`: The name of the command within the command group. Commonly called 'show'.
- `getter_name`: The name of the method for the object getter, relative to the path specified in `operations_tmpl`.
- `kwargs`: any supported kwarg.

***custom_show_command***

Similar to `custom_command` and `command`, the signature for `custom_show_command` is exactly the same as `show_command` but uses `custom_command_type` as the fallback for missings kwargs.

**(4) Supporting --no-wait**

When registering a command, the boolean `supports_no_wait` property can be used to specify that the command supports `--no-wait`.
Expand Down
81 changes: 40 additions & 41 deletions src/azure-cli-core/azure/cli/core/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from azure.cli.core.commands.parameters import (
AzArgumentContext, patch_arg_make_required, patch_arg_make_optional)
from azure.cli.core.extension import get_extension
from azure.cli.core.util import get_command_type_kwarg, read_file_content, get_arg_list, poller_classes
import azure.cli.core.telemetry as telemetry

logger = get_logger(__name__)
Expand Down Expand Up @@ -60,7 +61,6 @@ def _explode_list_args(args):

def _expand_file_prefixed_files(args):
def _load_file(path):
from azure.cli.core.util import read_file_content
if path == '-':
content = sys.stdin.read()
else:
Expand Down Expand Up @@ -380,7 +380,6 @@ def execute(self, args):
is_query_active=self.data['query_active'])

def _build_kwargs(self, func, ns): # pylint: disable=no-self-use
from azure.cli.core.util import get_arg_list
arg_list = get_arg_list(func)
kwargs = {}
if 'cmd' in arg_list:
Expand Down Expand Up @@ -573,7 +572,6 @@ def __call__(self, poller):
class DeploymentOutputLongRunningOperation(LongRunningOperation):
def __call__(self, result):
from msrest.pipeline import ClientRawResponse
from azure.cli.core.util import poller_classes

if isinstance(result, poller_classes()):
# most deployment operations return a poller
Expand Down Expand Up @@ -677,7 +675,6 @@ def _is_paged(obj):
def _is_poller(obj):
# Since loading msrest is expensive, we avoid it until we have to
if obj.__class__.__name__ in ['AzureOperationPoller', 'LROPoller']:
from azure.cli.core.util import poller_classes
return isinstance(obj, poller_classes())
return False

Expand Down Expand Up @@ -750,7 +747,7 @@ def _flatten_kwargs(self, kwargs, default_source_name):
# pylint: disable=arguments-differ
def command(self, name, method_name=None, **kwargs):
"""
Register a CLI command
Register a CLI command.
:param name: Name of the command as it will be called on the command line
:type name: str
:param method_name: Name of the method the command maps to
Expand All @@ -762,7 +759,7 @@ def command(self, name, method_name=None, **kwargs):
- exception_handler: Exception handler for handling non-standard exceptions (function)
- supports_no_wait: The command supports no wait. (bool)
- no_wait_param: [deprecated] The name of a boolean parameter that will be exposed as `--no-wait`
to skip long-running operation polling. (string)
to skip long running operation polling. (string)
- transform: Transform function for transforming the output of the command (function)
- table_transformer: Transform function or JMESPath query to be applied to table output to create a
better output format for tables. (function or string)
Expand All @@ -771,20 +768,11 @@ def command(self, name, method_name=None, **kwargs):
- max_api: Maximum API version required for commands within the group (string)
:rtype: None
"""
self._check_stale()
merged_kwargs = self._flatten_kwargs(kwargs, 'command_type')
# don't inherit deprecation info from command group
merged_kwargs['deprecate_info'] = kwargs.get('deprecate_info', None)
operations_tmpl = merged_kwargs['operations_tmpl']
command_name = '{} {}'.format(self.group_name, name) if self.group_name else name
operation = operations_tmpl.format(method_name) if operations_tmpl else None
self.command_loader._cli_command(command_name, operation, **merged_kwargs) # pylint: disable=protected-access

return command_name
return self._command(name, method_name=method_name, **kwargs)

def custom_command(self, name, method_name, **kwargs):
def custom_command(self, name, method_name=None, **kwargs):
"""
Register a custom CLI command.
Register a CLI command.
:param name: Name of the command as it will be called on the command line
:type name: str
:param method_name: Name of the method the command maps to
Expand All @@ -805,8 +793,11 @@ def custom_command(self, name, method_name, **kwargs):
- max_api: Maximum API version required for commands within the group (string)
:rtype: None
"""
return self._command(name, method_name=method_name, custom_command=True, **kwargs)

def _command(self, name, method_name, custom_command=False, **kwargs):
self._check_stale()
merged_kwargs = self._flatten_kwargs(kwargs, 'custom_command_type')
merged_kwargs = self._flatten_kwargs(kwargs, get_command_type_kwarg(custom_command))
# don't inherit deprecation info from command group
merged_kwargs['deprecate_info'] = kwargs.get('deprecate_info', None)

Expand All @@ -819,12 +810,8 @@ def custom_command(self, name, method_name, **kwargs):
return command_name

# pylint: disable=no-self-use
def _resolve_operation(self, kwargs, name, command_type=None, source_kwarg='command_type'):

allowed_source_kwargs = ['command_type', 'custom_command_type']
if source_kwarg not in allowed_source_kwargs:
raise ValueError("command authoring error: 'source_kwarg' value '{}'. Allowed values: {}".format(
source_kwarg, ' '.join(allowed_source_kwargs)))
def _resolve_operation(self, kwargs, name, command_type=None, custom_command=False):
source_kwarg = get_command_type_kwarg(custom_command)

operations_tmpl = None
if command_type:
Expand Down Expand Up @@ -860,7 +847,7 @@ def generic_update_command(self, name,
getter_op = self._resolve_operation(merged_kwargs, getter_name, getter_type)
setter_op = self._resolve_operation(merged_kwargs, setter_name, setter_type)
custom_func_op = self._resolve_operation(merged_kwargs, custom_func_name, custom_func_type,
source_kwarg='custom_command_type') if custom_func_name else None
custom_command=True) if custom_func_name else None
_cli_generic_update_command(
self.command_loader,
'{} {}'.format(self.group_name, name),
Expand All @@ -873,31 +860,43 @@ def generic_update_command(self, name,
child_arg_name=child_arg_name,
**merged_kwargs)

def wait_command(self, name, getter_name='get', **kwargs):
self._wait_command(name, getter_name=getter_name, custom_command=False, **kwargs)

def custom_wait_command(self, name, getter_name='get', **kwargs):
self._wait_command(name, getter_name=getter_name, custom_command=True, **kwargs)

def generic_wait_command(self, name, getter_name='get', getter_type=None, **kwargs):
from azure.cli.core.commands.arm import _cli_generic_wait_command
self._wait_command(name, getter_name=getter_name, getter_type=getter_type, **kwargs)

def _wait_command(self, name, getter_name='get', getter_type=None, custom_command=False, **kwargs):
from azure.cli.core.commands.arm import _cli_wait_command
self._check_stale()
merged_kwargs = _merge_kwargs(kwargs, self.group_kwargs, CLI_COMMAND_KWARGS)
# don't inherit deprecation info from command group
merged_kwargs['deprecate_info'] = kwargs.get('deprecate_info', None)

if getter_type:
merged_kwargs = _merge_kwargs(getter_type.settings, merged_kwargs, CLI_COMMAND_KWARGS)
getter_op = self._resolve_operation(merged_kwargs, getter_name, getter_type)
_cli_generic_wait_command(
self.command_loader,
'{} {}'.format(self.group_name, name),
getter_op=getter_op,
**merged_kwargs)
getter_op = self._resolve_operation(merged_kwargs, getter_name, getter_type, custom_command=custom_command)
_cli_wait_command(self.command_loader, '{} {}'.format(self.group_name, name), getter_op=getter_op,
custom_command=custom_command, **merged_kwargs)

def generic_show_command(self, name, getter_name='get', getter_type=None, **kwargs):
from azure.cli.core.commands.arm import _cli_generic_show_command
def show_command(self, name, getter_name='get', **kwargs):
self._show_command(name, getter_name=getter_name, custom_command=False, **kwargs)

def custom_show_command(self, name, getter_name='get', **kwargs):
self._show_command(name, getter_name=getter_name, custom_command=True, **kwargs)

def _show_command(self, name, getter_name='get', getter_type=None, custom_command=False, **kwargs):
from azure.cli.core.commands.arm import _cli_show_command
self._check_stale()
merged_kwargs = _merge_kwargs(kwargs, self.group_kwargs, CLI_COMMAND_KWARGS)
# don't inherit deprecation info from command group
merged_kwargs['deprecate_info'] = kwargs.get('deprecate_info', None)

if getter_type:
merged_kwargs = _merge_kwargs(getter_type.settings, merged_kwargs, CLI_COMMAND_KWARGS)
getter_op = self._resolve_operation(merged_kwargs, getter_name, getter_type)
_cli_generic_show_command(
self.command_loader,
'{} {}'.format(self.group_name, name),
getter_op=getter_op,
**merged_kwargs)
getter_op = self._resolve_operation(merged_kwargs, getter_name, getter_type, custom_command=custom_command)
_cli_show_command(self.command_loader, '{} {}'.format(self.group_name, name), getter_op=getter_op,
custom_command=custom_command, **merged_kwargs)
Loading