Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,6 @@ complete -o nospace -F _python_argcomplete \"az\"\n\
" > /etc/az.completion
RUN echo "\nsource '/etc/az.completion'\n" >> /etc/bash.bashrc

CMD az
WORKDIR /

CMD az; bash
59 changes: 46 additions & 13 deletions src/azure/cli/_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@
import sys
import json
import re
import traceback
from collections import OrderedDict
from six import StringIO, text_type, u

from azure.cli._util import CLIError
import azure.cli._logging as _logging

logger = _logging.get_az_logger(__name__)

def _decode_str(output):
if not isinstance(output, text_type):
output = u(str(output))
Expand All @@ -23,41 +29,62 @@ def default(self, obj): #pylint: disable=method-hidden
return json.JSONEncoder.default(self, obj)

def format_json(obj):
input_dict = obj.__dict__ if hasattr(obj, '__dict__') else obj
result = obj.result
input_dict = result.__dict__ if hasattr(result, '__dict__') else result
return json.dumps(input_dict, indent=2, sort_keys=True, cls=ComplexEncoder,
separators=(',', ': ')) + '\n'

def format_table(obj):
obj_list = obj if isinstance(obj, list) else [obj]
to = TableOutput()
result = obj.result
try:
if not obj.simple_output_query and not obj.is_query_active:
raise ValueError('No query specified and no built-in query available.')
if obj.simple_output_query and not obj.is_query_active:
from jmespath import compile as compile_jmespath, search, Options
result = compile_jmespath(obj.simple_output_query).search(result, Options(OrderedDict))
obj_list = result if isinstance(result, list) else [result]
to = TableOutput()
for item in obj_list:
for item_key in sorted(item):
for item_key in item:
to.cell(item_key, item[item_key])
to.end_row()
return to.dump()
except TypeError:
return ''
except (ValueError, KeyError, TypeError):
logger.debug(traceback.format_exc())
raise CLIError("Table output unavailable. "\
"Change output type with --output or use "\
"the --query option to specify an appropriate query. "\
"Use --debug for more info.")

def format_text(obj):
obj_list = obj if isinstance(obj, list) else [obj]
result = obj.result
result_list = result if isinstance(result, list) else [result]
to = TextOutput()
try:
for item in obj_list:
for item in result_list:
for item_key in sorted(item):
to.add(item_key, item[item_key])
return to.dump()
except TypeError:
return ''

def format_list(obj):
obj_list = obj if isinstance(obj, list) else [obj]
result = obj.result
result_list = result if isinstance(result, list) else [result]
lo = ListOutput()
return lo.dump(obj_list)
return lo.dump(result_list)

def format_tsv(obj):
obj_list = obj if isinstance(obj, list) else [obj]
return TsvOutput.dump(obj_list)
result = obj.result
result_list = result if isinstance(result, list) else [result]
return TsvOutput.dump(result_list)

class CommandResultItem(object): #pylint: disable=too-few-public-methods

def __init__(self, result, simple_output_query=None, is_query_active=False):
self.result = result
self.simple_output_query = simple_output_query
self.is_query_active = is_query_active

class OutputProducer(object): #pylint: disable=too-few-public-methods

Expand All @@ -81,7 +108,6 @@ def out(self, obj):
print(output.encode('ascii', 'ignore').decode('utf-8', 'ignore'),
file=self.file, end='')


@staticmethod
def get_formatter(format_type):
return OutputProducer.format_dict.get(format_type, format_list)
Expand Down Expand Up @@ -158,6 +184,9 @@ def dump(self, data):
return result

class TableOutput(object):

unsupported_types = (list, dict, set)

def __init__(self):
self._rows = [{}]
self._columns = {}
Expand Down Expand Up @@ -185,6 +214,10 @@ def any_rows(self):
return len(self._rows) > 1

def cell(self, name, value):
if isinstance(value, TableOutput.unsupported_types):
raise TypeError('Table output does not support objects of type {}.\n'\
'Offending object name={} value={}'.format(
[ut.__name__ for ut in TableOutput.unsupported_types], name, value))
n = str(name)
v = str(value)
max_width = self._columns.get(n)
Expand Down
11 changes: 8 additions & 3 deletions src/azure/cli/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import argparse
from enum import Enum
from .parser import AzCliCommandParser
from azure.cli._output import CommandResultItem
import azure.cli.extensions
import azure.cli._help as _help
import azure.cli._logging as _logging
Expand Down Expand Up @@ -56,7 +57,8 @@ def __init__(self, config=None):
'x-ms-client-request-id': str(uuid.uuid1())
},
'command': 'unknown',
'completer_active': ARGCOMPLETE_ENV_NAME in os.environ
'completer_active': ARGCOMPLETE_ENV_NAME in os.environ,
'query_active': False
}


Expand Down Expand Up @@ -124,7 +126,10 @@ def execute(self, argv):
event_data = {'result': results}
self.raise_event(self.TRANSFORM_RESULT, event_data=event_data)
self.raise_event(self.FILTER_RESULT, event_data=event_data)
return event_data['result']
return CommandResultItem(event_data['result'],
simple_output_query=
command_table[args.command].simple_output_query,
is_query_active=self.session['query_active'])

def raise_event(self, name, **kwargs):
'''Raise the event `name`.
Expand Down Expand Up @@ -188,7 +193,7 @@ def _register_builtin_arguments(**kwargs):
global_group = kwargs['global_group']
global_group.add_argument('--subscription', dest='_subscription_id', help=argparse.SUPPRESS)
global_group.add_argument('--output', '-o', dest='_output_format',
choices=['json', 'tsv', 'list'],
choices=['json', 'tsv', 'list', 'table'],
default='json',
help='Output format',
type=str.lower)
Expand Down
14 changes: 9 additions & 5 deletions src/azure/cli/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from msrestazure.azure_operation import AzureOperationPoller
from azure.cli._util import CLIError
import azure.cli._logging as _logging

from ._introspection import extract_args_from_signature

logger = _logging.get_az_logger(__name__)
Expand Down Expand Up @@ -134,12 +135,13 @@ def wrapped(func):

class CliCommand(object):

def __init__(self, name, handler, description=None):
def __init__(self, name, handler, description=None, simple_output_query=None):
self.name = name
self.handler = handler
self.description = description
self.help = None
self.arguments = {}
self.simple_output_query = simple_output_query

def add_argument(self, param_name, *option_strings, **kwargs):
argument = CliCommandArgument(
Expand Down Expand Up @@ -201,11 +203,13 @@ def register_extra_cli_argument(command, dest, **kwargs):
'''
_cli_extra_argument_registry[command][dest] = CliCommandArgument(dest, **kwargs)

def cli_command(name, operation, client_factory=None, transform=None):
def cli_command(name, operation, client_factory=None, transform=None, simple_output_query=None):
""" Registers a default Azure CLI command. These commands require no special parameters. """
command_table[name] = create_command(name, operation, transform, client_factory)
command_table[name] = create_command(name, operation, transform, simple_output_query,
client_factory)

def create_command(name, operation, transform_result, simple_output_query, client_factory):

def create_command(name, operation, transform_result, client_factory):
def _execute_command(kwargs):
client = client_factory(kwargs) if client_factory else None
try:
Expand All @@ -226,7 +230,7 @@ def _execute_command(kwargs):
raise CLIError(message)

name = ' '.join(name.split())
cmd = CliCommand(name, _execute_command)
cmd = CliCommand(name, _execute_command, simple_output_query=simple_output_query)
cmd.arguments.update(extract_args_from_signature(operation))
return cmd

Expand Down
1 change: 1 addition & 0 deletions src/azure/cli/extensions/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def filter_output(**kwargs):
kwargs['event_data']['result'], Options(collections.OrderedDict))
application.remove(application.FILTER_RESULT, filter_output)
application.register(application.FILTER_RESULT, filter_output)
application.session['query_active'] = True

application.register(application.GLOBAL_PARSER_CREATED, _register_global_parameter)
application.register(application.COMMAND_PARSER_PARSED, handle_query_parameter)
Expand Down
2 changes: 1 addition & 1 deletion src/azure/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def main(args, file=sys.stdout): #pylint: disable=redefined-builtin
cmd_result = APPLICATION.execute(args)
# Commands can return a dictionary/list of results
# If they do, we print the results.
if cmd_result:
if cmd_result and cmd_result.result:
formatter = OutputProducer.get_formatter(APPLICATION.configuration.output_format)
OutputProducer(formatter=formatter, file=file).out(cmd_result)
except Exception as ex: # pylint: disable=broad-except
Expand Down
Loading