diff --git a/azure-cli.pyproj b/azure-cli.pyproj
index f39c9761fea..f34f17e456e 100644
--- a/azure-cli.pyproj
+++ b/azure-cli.pyproj
@@ -15,8 +15,7 @@
{1dd9c42b-5980-42ce-a2c3-46d3bf0eede4}
3.5
False
-
-
+ vm list-all --query "[].name"
@@ -36,7 +35,6 @@
-
Code
@@ -48,15 +46,11 @@
-
- Code
-
-
diff --git a/pylintrc b/pylintrc
index f304190876a..e964c356744 100644
--- a/pylintrc
+++ b/pylintrc
@@ -6,6 +6,8 @@
# W0511 fixme
disable=C0111,C0103,I0011,W0511
[DESIGN]
+# Minimum lines number of a similarity.
+min-similarity-lines=6
# Maximum number of locals for function / method body
max-locals=25
# Maximum number of branch for function / method body
diff --git a/requirements.txt b/requirements.txt
index 45159a2c1ac..28463f097cd 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,5 @@
applicationinsights==0.10.0
+argcomplete==1.1.0
azure==2.0.0rc1
jmespath
mock==1.3.0
diff --git a/src/azure/cli/_event_dispatcher.py b/src/azure/cli/_event_dispatcher.py
deleted file mode 100644
index 869ea9f0f7a..00000000000
--- a/src/azure/cli/_event_dispatcher.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from collections import defaultdict
-
-class EventDispatcher(object):
- """Register for and raise events.
-
- During the execution of a command, a set of events are raised
- that allow extensions to change the flow of actions.
-
- Clients can register handlers by calling the `EventDispatcher.register`
- method passing in the event handler function.
- """
-
- REGISTER_GLOBAL_PARAMETERS = 'RegisterGlobalParameters'
- PARSING_PARAMETERS = 'ParsingParameters'
- VALIDATING_PARAMETERS = 'ValidatingParameters'
- EXECUTING_COMMAND = 'ExecutingCommand'
- TRANSFORM_RESULT = 'TransformResult'
- FILTER_RESULT = 'FilterResult'
-
- def __init__(self):
- self._handlers = defaultdict(lambda: [])
-
- def raise_event(self, name, event_data):
- for func in self._handlers[name]:
- func(name, event_data)
-
- def register(self, name, handler):
- '''Register a callable that will be called when the
- event `name` is raised.
-
- param: name: The name of the event
- param: handler: Function that takes two parameters;
- name: name of the event raised
- event_data: `dict` with event specific data.
- '''
- self._handlers[name].append(handler)
-
- def event_handler(self, name):
- '''Any function decorated by @event_handler will
- be registered as a handler for the given event name
- '''
- def wrapper(func):
- self.register(name, func)
- return func
- return wrapper
-
diff --git a/src/azure/cli/_output.py b/src/azure/cli/_output.py
index c74943191bf..2c19ca09eeb 100644
--- a/src/azure/cli/_output.py
+++ b/src/azure/cli/_output.py
@@ -3,8 +3,6 @@
import sys
import json
import re
-from datetime import datetime
-from enum import Enum
from six import StringIO
class OutputFormatException(Exception):
@@ -51,40 +49,13 @@ class OutputProducer(object): #pylint: disable=too-few-public-methods
'list': format_list
}
- KEYS_CAMELCASE_PATTERN = re.compile('(?!^)_([a-zA-Z])')
-
def __init__(self, formatter=format_list, file=sys.stdout): #pylint: disable=redefined-builtin
self.formatter = formatter
self.file = file
- def out(self, session, obj):
- obj = OutputProducer.todict(obj)
- event_data = {'result': obj}
- session.raise_event(session.TRANSFORM_RESULT, event_data)
- session.raise_event(session.FILTER_RESULT, event_data)
- print(self.formatter(event_data['result']), file=self.file)
+ def out(self, obj):
+ print(self.formatter(obj), file=self.file)
- @staticmethod
- def todict(obj): #pylint: disable=too-many-return-statements
- def to_camelcase(s):
- return re.sub(OutputProducer.KEYS_CAMELCASE_PATTERN, lambda x: x.group(1).upper(), s)
-
- if isinstance(obj, dict):
- return {k: OutputProducer.todict(v) for (k, v) in obj.items()}
- elif isinstance(obj, list):
- return [OutputProducer.todict(a) for a in obj]
- elif isinstance(obj, Enum):
- return obj.value
- elif isinstance(obj, datetime):
- return obj.isoformat()
- elif hasattr(obj, '_asdict'):
- return OutputProducer.todict(obj._asdict())
- elif hasattr(obj, '__dict__'):
- return dict([(to_camelcase(k), OutputProducer.todict(v))
- for k, v in obj.__dict__.items()
- if not callable(v) and not k.startswith('_')])
- else:
- return obj
@staticmethod
def get_formatter(format_type):
diff --git a/src/azure/cli/application.py b/src/azure/cli/application.py
index d96c0da560d..411781d393e 100644
--- a/src/azure/cli/application.py
+++ b/src/azure/cli/application.py
@@ -1,75 +1,133 @@
+from collections import defaultdict
+from datetime import datetime
+import sys
+import re
import argparse
import logging
+from enum import Enum
from .parser import AzCliCommandParser
-import argcomplete
-import logging
-from ._event_dispatcher import EventDispatcher
import azure.cli.extensions
-class Session(EventDispatcher):
- """The session object tracks session specific data such
- as output formats, log settings as well as providing an
- event dispatch mechanism that allows us to make modifications
- during the execution of a command
+class Configuration(object): # pylint: disable=too-few-public-methods
+ """The configuration object tracks session specific data such
+ as output formats, available commands etc.
"""
- def __init__(self):
- super(Session, self).__init__()
+ def __init__(self, argv=None):
+ self.argv = argv or sys.argv
self.log = logging.getLogger('az')
self.output_format = 'list'
+ def get_command_table(self):
+ import azure.cli.commands as commands
+
+ # Find the first noun on the command line and only load commands from that
+ # module to improve startup time.
+ for a in self.argv:
+ if not a.startswith('-'):
+ return commands.get_command_table(a)
+
+ # No noun found, so load all commands.
+ return commands.get_command_table()
+
class Application(object):
- def __init__(self, session=None):
- self.session = session or Session()
+ TRANSFORM_RESULT = 'Application.TransformResults'
+ FILTER_RESULT = 'Application.FilterResults'
+ GLOBAL_PARSER_CREATED = 'GlobalParser.Created'
+ COMMAND_PARSER_CREATED = 'CommandParser.Created'
+ COMMAND_PARSER_LOADED = 'CommandParser.Loaded'
+ COMMAND_PARSER_PARSED = 'CommandParser.Parsed'
+
+ def __init__(self, configuration=None):
+ self._event_handlers = defaultdict(lambda: [])
+ self.configuration = configuration or Configuration()
# Register presence of and handlers for global parameters
- self.session.register('GlobalParser.Created', self._register_builtin_arguments)
- self.session.register('CommandParser.Parsed', self._handle_builtin_arguments)
+ self.register(self.GLOBAL_PARSER_CREATED, Application._register_builtin_arguments)
+ self.register(self.COMMAND_PARSER_LOADED, Application._enable_autocomplete)
+ self.register(self.COMMAND_PARSER_PARSED, self._handle_builtin_arguments)
# Let other extensions make their presence known
azure.cli.extensions.register_extensions(self)
self.global_parser = AzCliCommandParser(prog='az', add_help=False)
- self.session.raise_event('GlobalParser.Created', self.global_parser)
+ self.raise_event(self.GLOBAL_PARSER_CREATED, self.global_parser)
self.parser = AzCliCommandParser(prog='az', parents=[self.global_parser])
- self.session.raise_event('CommandParser.Created', self.parser)
-
- def load_commands(self, argv):
- import azure.cli.commands as commands
-
- # Find the first noun on the command line and only load commands from that
- # module to improve startup time.
- for a in argv:
- if not a.startswith('-'):
- commands.add_to_parser(self.parser, self.session, a)
- break
- else:
- # No noun found, so load all commands.
- commands.add_to_parser(self.parser, self.session)
+ self.raise_event(self.COMMAND_PARSER_CREATED, self.parser)
- self.session.raise_event('CommandParser.Loaded', self.parser)
- argcomplete.autocomplete(self.parser)
+ def load_commands(self):
+ self.parser.load_command_table(self.configuration.get_command_table())
+ self.raise_event(self.COMMAND_PARSER_LOADED, self.parser)
def execute(self, argv):
args = self.parser.parse_args(argv)
- self.session.raise_event('CommandParser.Parsed', args)
+ self.raise_event(self.COMMAND_PARSER_PARSED, args)
# Consider - we are using any args that start with an underscore (_) as 'private'
# arguments and remove them from the arguments that we pass to the actual function.
# This does not feel quite right.
params = dict([(key, value)
for key, value in args.__dict__.items() if not key.startswith('_')])
- result = args.func(params, {}) # TODO: Unexpected parameters passed in?
- return result
+ result = args.func(params)
+
+ result = self.todict(result)
+ event_data = {'result': result}
+ self.raise_event(self.TRANSFORM_RESULT, event_data)
+ self.raise_event(self.FILTER_RESULT, event_data)
+ return event_data['result']
+
+ def raise_event(self, name, event_data):
+ '''Raise the event `name`.
+ '''
+ for func in self._event_handlers[name]:
+ func(event_data)
+
+ def register(self, name, handler):
+ '''Register a callable that will be called when the
+ event `name` is raised.
+
+ param: name: The name of the event
+ param: handler: Function that takes two parameters;
+ name: name of the event raised
+ event_data: `dict` with event specific data.
+ '''
+ self._event_handlers[name].append(handler)
- def _register_builtin_arguments(self, name, parser):
+ KEYS_CAMELCASE_PATTERN = re.compile('(?!^)_([a-zA-Z])')
+ def todict(self, obj): #pylint: disable=too-many-return-statements
+
+ def to_camelcase(s):
+ return re.sub(Application.KEYS_CAMELCASE_PATTERN, lambda x: x.group(1).upper(), s)
+
+ if isinstance(obj, dict):
+ return {k: self.todict(v) for (k, v) in obj.items()}
+ elif isinstance(obj, list):
+ return [self.todict(a) for a in obj]
+ elif isinstance(obj, Enum):
+ return obj.value
+ elif isinstance(obj, datetime):
+ return obj.isoformat()
+ elif hasattr(obj, '_asdict'):
+ return self.todict(obj._asdict())
+ elif hasattr(obj, '__dict__'):
+ return dict([(to_camelcase(k), self.todict(v))
+ for k, v in obj.__dict__.items()
+ if not callable(v) and not k.startswith('_')])
+ else:
+ return obj
+
+ @staticmethod
+ def _enable_autocomplete(parser):
+ import argcomplete
+ argcomplete.autocomplete(parser)
+
+ @staticmethod
+ def _register_builtin_arguments(parser):
parser.add_argument('--subscription', dest='_subscription_id', help=argparse.SUPPRESS)
- parser.add_argument('--output', '-o', dest='_output_format', choices=['list', 'json'])
-
- def _handle_builtin_arguments(self, name, args):
- try:
- self.session.output_format = args._output_format #pylint: disable=protected-access
- del args._output_format
- except Exception:
- pass
+ parser.add_argument('--output', '-o', dest='_output_format', choices=['list', 'json'],
+ help=argparse.SUPPRESS)
+
+ def _handle_builtin_arguments(self, args):
+ self.configuration.output_format = args._output_format #pylint: disable=protected-access
+ del args._output_format
diff --git a/src/azure/cli/commands/__init__.py b/src/azure/cli/commands/__init__.py
index e018fdf870f..40dec44f2fd 100644
--- a/src/azure/cli/commands/__init__.py
+++ b/src/azure/cli/commands/__init__.py
@@ -13,7 +13,7 @@
COMMON_PARAMETERS = {
'resource_group_name': {
- 'name': '--resourcegroup --rg',
+ 'name': '--resourcegroup -g',
'metavar': 'RESOURCE GROUP',
'help': 'Name of resource group',
'required': True
@@ -39,7 +39,7 @@ class CommandTable(defaultdict):
ArgumentParser.add_parser.
"""
def __init__(self):
- super(CommandTable, self).__init__(lambda: {'options': []})
+ super(CommandTable, self).__init__(lambda: {'arguments': []})
def command(self, name, **kwargs):
def wrapper(func):
@@ -58,19 +58,19 @@ def option(self, name, **kwargs):
def wrapper(func):
opt = dict(kwargs)
opt['name'] = name
- self[func]['options'].append(opt)
+ self[func]['arguments'].append(opt)
return func
return wrapper
-def get_command_table(command_name):
+def _get_command_table(command_name):
module = __import__('azure.cli.commands.' + command_name)
for part in ('cli.commands.' + command_name).split('.'):
module = getattr(module, part)
return module.get_command_table()
-def add_to_parser(parser, session, module_name=None):
- '''Loads commands into the parser
+def get_command_table(module_name=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.
@@ -78,13 +78,15 @@ def add_to_parser(parser, session, module_name=None):
loaded = False
if module_name:
try:
- parser.load_command_table(session, get_command_table(module_name))
+ command_table = _get_command_table(module_name)
loaded = True
except ImportError:
# Unknown command - we'll load all below
pass
if not loaded:
+ command_table = {}
for mod in COMMAND_MODULES:
- parser.load_command_table(session, get_command_table(mod))
+ command_table.update(_get_command_table(mod))
loaded = True
+ return command_table
diff --git a/src/azure/cli/commands/_auto_command.py b/src/azure/cli/commands/_auto_command.py
index ba0c2f0bfc0..cefeabc9987 100644
--- a/src/azure/cli/commands/_auto_command.py
+++ b/src/azure/cli/commands/_auto_command.py
@@ -5,9 +5,7 @@
from msrest.paging import Paged
from msrest.exceptions import ClientException
from azure.cli.parser import IncorrectUsageError
-from ..commands import CommandTable, COMMON_PARAMETERS
-
-from argcomplete import warn
+from ..commands import COMMON_PARAMETERS
EXCLUDED_PARAMS = frozenset(['self', 'raw', 'custom_headers', 'operation_config'])
@@ -51,7 +49,7 @@ def _get_member(obj, path):
return obj
def _make_func(client_factory, member_path, return_type_or_func, unbound_func):
- def call_client(args, unexpected): #pylint: disable=unused-argument
+ def call_client(args):
client = client_factory()
ops_instance = _get_member(client, member_path)
try:
@@ -110,6 +108,6 @@ def build_operation(command_table, command_name, member_path, client_type, opera
command_table[func] = {
'name': ' '.join([command_name, opname]),
'handler': func,
- 'options': options
+ 'arguments': options
}
diff --git a/src/azure/cli/commands/account.py b/src/azure/cli/commands/account.py
index f1c36f1486d..ab283db0b27 100644
--- a/src/azure/cli/commands/account.py
+++ b/src/azure/cli/commands/account.py
@@ -4,19 +4,20 @@
command_table = CommandTable()
-def get_command_table():
+def get_command_table(): # pylint:disable=duplicate-code
return command_table
@command_table.command('account list', description=L('List the imported subscriptions.'))
-def list_subscriptions(args, unexpected): #pylint: disable=unused-argument
+def list_subscriptions(_):
profile = Profile()
subscriptions = profile.load_subscriptions()
return subscriptions
-@command_table.option('--subscription-id -n', metavar='SUBSCRIPTION_ID', dest='subscription_id', help=L('Subscription Id, unique name also works.'))
+@command_table.option('--subscription-id -n', metavar='SUBSCRIPTION_ID', dest='subscription_id',
+ help=L('Subscription Id, unique name also works.'))
@command_table.command('account set', description=L('Set the current subscription'))
-def set_active_subscription(args, unexpected): #pylint: disable=unused-argument
+def set_active_subscription(args):
subscription_id = args.get('subscription-id')
if not id:
raise ValueError(L('Please provide subscription id or unique name.'))
diff --git a/src/azure/cli/commands/login.py b/src/azure/cli/commands/login.py
index e7cb086aca5..0b136e1ddd9 100644
--- a/src/azure/cli/commands/login.py
+++ b/src/azure/cli/commands/login.py
@@ -15,17 +15,20 @@
command_table = CommandTable()
-def get_command_table():
+def get_command_table(): # pylint:disable=duplicate-code
return command_table
@command_table.option('--username -u',
- help=L('organization Id or service principal. Microsoft Account is not yet supported.'))
-@command_table.option('--password -p', help=L('user password or client secret, will prompt if not given.'))
-@command_table.option('--service-principal', help=L('the credential represents a service principal.'))
+ help=L('organization Id or service principal. Microsoft Account is not yet supported.')) # pylint: disable=line-too-long
+@command_table.option('--password -p',
+ help=L('user password or client secret, will prompt if not given.'))
+@command_table.option('--service-principal',
+ help=L('the credential represents a service principal.'))
@command_table.option('--tenant -t', help=L('the tenant associated with the service principal.'))
-@command_table.command('login', description=L('log in to an Azure subscription using Active Directory Organization Id'))
-def login(args, unexpected): #pylint: disable=unused-argument
+@command_table.command('login',
+ description=L('log in to an Azure subscription using Active Directory Organization Id')) # pylint: disable=line-too-long
+def login(args):
interactive = False
username = args.get('username')
diff --git a/src/azure/cli/commands/logout.py b/src/azure/cli/commands/logout.py
index 3242ec4d704..847e3f3af82 100644
--- a/src/azure/cli/commands/logout.py
+++ b/src/azure/cli/commands/logout.py
@@ -4,12 +4,14 @@
command_table = CommandTable()
-def get_command_table():
+def get_command_table(): # pylint:disable=duplicate-code
return command_table
-@command_table.option('--username -u', help=L('User name used to log out from Azure Active Directory.'),
+@command_table.option('--username -u',
+ help=L('User name used to log out from Azure Active Directory.'),
required=True)
-@command_table.command('logout', description=L('Log out from Azure subscription using Active Directory.'))
-def logout(args, unexpected): #pylint: disable=unused-argument
+@command_table.command('logout',
+ description=L('Log out from Azure subscription using Active Directory.'))
+def logout(args):
profile = Profile()
profile.logout(args['username'])
diff --git a/src/azure/cli/commands/network.py b/src/azure/cli/commands/network.py
index 0a509bf8705..9124616f26b 100644
--- a/src/azure/cli/commands/network.py
+++ b/src/azure/cli/commands/network.py
@@ -255,7 +255,7 @@ def _network_client_factory():
@command_table.option('--address-space -a', metavar='ADDRESS SPACE', help=L('the VNet address-space in CIDR notation or multiple address-spaces, quoted and space-separated'), required=True)
@command_table.option('--dns-servers -d', metavar='DNS SERVERS', help=L('the VNet DNS servers, quoted and space-separated'))
@command_table.command('network vnet create')
-def create_update_vnet(args, unexpected): #pylint: disable=unused-argument
+def create_update_vnet(args):
from azure.mgmt.network.models import AddressSpace, DhcpOptions, VirtualNetwork
resource_group = args.get('resource-group')
@@ -279,7 +279,7 @@ def create_update_vnet(args, unexpected): #pylint: disable=unused-argument
@command_table.option('--vnet -v', help=L('the name of the subnet vnet'), required=True)
@command_table.option('--address-prefix -a', help=L('the the address prefix in CIDR format'), required=True)
@command_table.command('network subnet create')
-def create_update_subnet(args, unexpected): #pylint: disable=unused-argument
+def create_update_subnet(args):
from azure.mgmt.network.models import Subnet
resource_group = args.get('resource-group')
diff --git a/src/azure/cli/commands/resource.py b/src/azure/cli/commands/resource.py
index 2b651b631e9..055817934ed 100644
--- a/src/azure/cli/commands/resource.py
+++ b/src/azure/cli/commands/resource.py
@@ -14,7 +14,7 @@ def get_command_table():
@command_table.option('--tag-name -tn', help=L("the resource group's tag name"))
@command_table.option('--tag-value -tv', help=L("the resource group's tag value"))
@command_table.command('resource group list', description=L('List resource groups'))
-def list_groups(args, unexpected): #pylint: disable=unused-argument
+def list_groups(args):
from azure.mgmt.resource.resources.models import ResourceGroup, ResourceGroupFilter
rmc = get_mgmt_service_client(ResourceManagementClient, ResourceManagementClientConfiguration)
@@ -40,8 +40,8 @@ def list_groups(args, unexpected): #pylint: disable=unused-argument
required=True)
@command_table.option('--api-version -o', help=L('the API version of the resource provider'))
@command_table.option('--parent',
- help=L('the name of the parent resource (if needed), in / format'))
-def show_resource(args, unexpected): #pylint: disable=unused-argument
+ help=L('the name of the parent resource (if needed), in / format')) # pylint: disable=line-too-long
+def show_resource(args):
rmc = get_mgmt_service_client(ResourceManagementClient, ResourceManagementClientConfiguration)
full_type = args.get('resource-type').split('/')
diff --git a/src/azure/cli/commands/storage.py b/src/azure/cli/commands/storage.py
index 49b5d3acd71..ab1a5e2b5b0 100644
--- a/src/azure/cli/commands/storage.py
+++ b/src/azure/cli/commands/storage.py
@@ -7,7 +7,6 @@
from azure.mgmt.storage import StorageManagementClient, StorageManagementClientConfiguration
from azure.mgmt.storage.operations import StorageAccountsOperations
-from ..parser import IncorrectUsageError
from ..commands import CommandTable, COMMON_PARAMETERS as GLOBAL_COMMON_PARAMETERS
from ._command_creation import get_mgmt_service_client, get_data_service_client
from ..commands._auto_command import build_operation
@@ -23,31 +22,39 @@ def extend_parameter(parameter_metadata, **kwargs):
COMMON_PARAMETERS = GLOBAL_COMMON_PARAMETERS.copy()
COMMON_PARAMETERS.update({
'account-name': {
- 'name': '--account-name -n',
- 'help': L('the storage account name'),
- 'required': not environ.get('AZURE_STORAGE_ACCOUNT'),
- 'default': environ.get('AZURE_STORAGE_ACCOUNT')
+ 'name': '--account-name -n',
+ 'help': L('the storage account name'),
+ # While account name *may* actually be required if the environment variable hasn't been
+ # specified, it is only required unless the connection string has been specified
+ 'required': False,
+ 'default': environ.get('AZURE_STORAGE_ACCOUNT')
},
- 'optional_resource_group_name': extend_parameter(GLOBAL_COMMON_PARAMETERS['resource_group_name'], required=False),
+ 'optional_resource_group_name':
+ extend_parameter(GLOBAL_COMMON_PARAMETERS['resource_group_name'], required=False),
'account_key': {
- 'name': '--account-key -k',
- 'help': L('the storage account key'),
- 'required': not environ.get('AZURE_STORAGE_ACCESS_KEY'),
- 'default': environ.get('AZURE_STORAGE_ACCESS_KEY')
+ 'name': '--account-key -k',
+ 'help': L('the storage account key'),
+ # While account key *may* actually be required if the environment variable hasn't been
+ # specified, it is only required unless the connection string has been specified
+ 'required': False,
+ 'default': environ.get('AZURE_STORAGE_ACCESS_KEY')
},
'blob-name': {
'name': '--blob-name -bn',
- 'help': L('the name of the blob'),
+ 'help': L('the name of the blob'),
'required': True
},
'container-name': {
'name': '--container-name -c',
'required': True
- },
+ },
'connection-string': {
'name': '--connection-string -t',
'help': L('the storage connection string'),
- 'required': not environ.get('AZURE_STORAGE_CONNECTION_STRING'),
+ # You can either specify connection string or name/key. There is no convenient way
+ # to express this kind of relationship in argparse...
+ # TODO: Move to exclusive group
+ 'required': False,
'default': environ.get('AZURE_STORAGE_CONNECTION_STRING')
}
})
@@ -73,7 +80,7 @@ def _storage_client_factory():
@command_table.command('storage account list', description=L('List storage accounts.'))
@command_table.option(**COMMON_PARAMETERS['optional_resource_group_name'])
-def list_accounts(args, unexpected): #pylint: disable=unused-argument
+def list_accounts(args):
from azure.mgmt.storage.models import StorageAccount
from msrestazure.azure_active_directory import UserPassCredentials
smc = _storage_client_factory()
@@ -88,9 +95,9 @@ def list_accounts(args, unexpected): #pylint: disable=unused-argument
@command_table.description(L('Regenerate one or both keys for a storage account.'))
@command_table.option(**COMMON_PARAMETERS['resource_group_name'])
@command_table.option(**COMMON_PARAMETERS['account-name'])
-@command_table.option('--key -y', default=['key1', 'key2'],
+@command_table.option('--key -y', default=['key1', 'key2'],
choices=['key1', 'key2'], help=L('Key to renew'))
-def renew_account_keys(args, unexpected): #pylint: disable=unused-argument
+def renew_account_keys(args):
smc = _storage_client_factory()
for key in args.get('key'):
result = smc.storage_accounts.regenerate_key(
@@ -103,7 +110,7 @@ def renew_account_keys(args, unexpected): #pylint: disable=unused-argument
@command_table.command('storage account usage')
@command_table.description(
L('Show the current count and limit of the storage accounts under the subscription.'))
-def show_account_usage(args, unexpected): #pylint: disable=unused-argument
+def show_account_usage(_):
smc = _storage_client_factory()
return next((x for x in smc.usage.list() if x.name.value == 'StorageAccounts'), None)
@@ -113,10 +120,10 @@ def show_account_usage(args, unexpected): #pylint: disable=unused-argument
@command_table.option(**COMMON_PARAMETERS['account-name'])
@command_table.option('--use-http', action='store_const', const='http', default='https',
help=L('use http as the default endpoint protocol'))
-def show_storage_connection_string(args, unexpected): #pylint: disable=unused-argument
+def show_storage_connection_string(args):
smc = _storage_client_factory()
- endpoint_protocol = args.get('use-http')
+ endpoint_protocol = args.get('use-http')
storage_account = args.get('account-name')
keys = smc.storage_accounts.list_keys(args.get('resource_group_name'), storage_account)
@@ -138,9 +145,9 @@ def show_storage_connection_string(args, unexpected): #pylint: disable=unused-ar
@command_table.option(**COMMON_PARAMETERS['account-name'])
@command_table.option(**COMMON_PARAMETERS['account_key'])
@command_table.option(**COMMON_PARAMETERS['connection-string'])
-@command_table.option('--public-access -p', default=None, choices=public_access_types.keys(),
+@command_table.option('--public-access -p', default=None, choices=public_access_types.keys(),
type=lambda x: public_access_types[x])
-def create_container(args, unexpected): #pylint: disable=unused-argument
+def create_container(args):
bbs = _get_blob_service_client(args)
public_access = args.get('public-access')
@@ -154,7 +161,7 @@ def create_container(args, unexpected): #pylint: disable=unused-argument
@command_table.option(**COMMON_PARAMETERS['account_key'])
@command_table.option(**COMMON_PARAMETERS['connection-string'])
@command_table.option('--force -f', help=L('supress delete confirmation prompt'))
-def delete_container(args, unexpected): #pylint: disable=unused-argument
+def delete_container(args):
bbs = _get_blob_service_client(args)
container_name = args.get('container-name')
prompt_for_delete = args.get('force') is None
@@ -173,7 +180,7 @@ def delete_container(args, unexpected): #pylint: disable=unused-argument
@command_table.option(**COMMON_PARAMETERS['account_key'])
@command_table.option(**COMMON_PARAMETERS['connection-string'])
@command_table.option('--prefix -p', help=L('container name prefix to filter by'))
-def list_containers(args, unexpected): #pylint: disable=unused-argument
+def list_containers(args):
bbs = _get_blob_service_client(args)
results = bbs.list_containers(args.get('prefix'))
return results
@@ -184,7 +191,7 @@ def list_containers(args, unexpected): #pylint: disable=unused-argument
@command_table.option(**COMMON_PARAMETERS['account-name'])
@command_table.option(**COMMON_PARAMETERS['account_key'])
@command_table.option(**COMMON_PARAMETERS['connection-string'])
-def show_container(args, unexpected): #pylint: disable=unused-argument
+def show_container(args):
bbs = _get_blob_service_client(args)
result = bbs.get_container_properties(args.get('container-name'))
return result
@@ -200,7 +207,8 @@ def show_container(args, unexpected): #pylint: disable=unused-argument
@command_table.option(**COMMON_PARAMETERS['account-name'])
@command_table.option(**COMMON_PARAMETERS['account_key'])
@command_table.option(**COMMON_PARAMETERS['connection-string'])
-@command_table.option('--container.public-access -cpa', default=None, choices=public_access_types.keys(),
+@command_table.option('--container.public-access -cpa', default=None,
+ choices=public_access_types.keys(),
type=lambda x: public_access_types[x])
@command_table.option('--content.type -cst')
@command_table.option('--content.disposition -csd')
@@ -208,7 +216,7 @@ def show_container(args, unexpected): #pylint: disable=unused-argument
@command_table.option('--content.language -csl')
@command_table.option('--content.md5 -csm')
@command_table.option('--content.cache-control -cscc')
-def create_block_blob(args, unexpected): #pylint: disable=unused-argument
+def create_block_blob(args):
from azure.storage.blob import ContentSettings
bbs = _get_blob_service_client(args)
public_access = args.get('container.public-access')
@@ -234,7 +242,7 @@ def create_block_blob(args, unexpected): #pylint: disable=unused-argument
@command_table.option(**COMMON_PARAMETERS['account_key'])
@command_table.option(**COMMON_PARAMETERS['connection-string'])
@command_table.option('--prefix -p', help=L('blob name prefix to filter by'))
-def list_blobs(args, unexpected): #pylint: disable=unused-argument
+def list_blobs(args):
bbs = _get_blob_service_client(args)
blobs = bbs.list_blobs(args.get('container-name'),
prefix=args.get('prefix'))
@@ -244,7 +252,7 @@ def list_blobs(args, unexpected): #pylint: disable=unused-argument
@command_table.description(L('Delete a blob from a container.'))
@command_table.option(**COMMON_PARAMETERS['container-name'])
@command_table.option(**COMMON_PARAMETERS['blob-name'])
-def delete_blob(args, unexpected): #pylint: disable=unused-argument
+def delete_blob(args):
bbs = _get_blob_service_client(args)
return bbs.delete_blob(args.get('container-name'), args.get('blob-name'))
@@ -252,7 +260,7 @@ def delete_blob(args, unexpected): #pylint: disable=unused-argument
@command_table.description(L('Show properties of the specified blob.'))
@command_table.option(**COMMON_PARAMETERS['container-name'])
@command_table.option(**COMMON_PARAMETERS['blob-name'])
-def show_blob(args, unexpected): #pylint: disable=unused-argument
+def show_blob(args):
bbs = _get_blob_service_client(args)
return bbs.get_blob_properties(args.get('container-name'), args.get('blob-name'))
@@ -261,7 +269,7 @@ def show_blob(args, unexpected): #pylint: disable=unused-argument
@command_table.option(**COMMON_PARAMETERS['container-name'])
@command_table.option(**COMMON_PARAMETERS['blob-name'])
@command_table.option('--download-to -dt', help=L('the file path to download to'), required=True)
-def download_blob(args, unexpected): #pylint: disable=unused-argument
+def download_blob(args):
bbs = _get_blob_service_client(args)
# show dot indicator of download progress (one for every 10%)
@@ -280,7 +288,7 @@ def download_blob(args, unexpected): #pylint: disable=unused-argument
@command_table.option(**COMMON_PARAMETERS['account-name'])
@command_table.option(**COMMON_PARAMETERS['account_key'])
@command_table.option(**COMMON_PARAMETERS['container-name'])
-def storage_file_create(args, unexpected): #pylint: disable=unused-argument
+def storage_file_create(args):
fsc = _get_file_service_client(args)
fsc.create_file_from_path(args.get('share-name'),
args.get('directory-name'),
@@ -292,16 +300,16 @@ def storage_file_create(args, unexpected): #pylint: disable=unused-argument
def _get_blob_service_client(args):
from azure.storage.blob import BlockBlobService
return get_data_service_client(BlockBlobService,
- args['storage-account'],
- args['storage-account-key'],
- args['connection-string'])
+ args.get('storage-account', None),
+ args.get('storage-account-key', None),
+ args.get('connection-string', None))
def _get_file_service_client(args):
from azure.storage.file import FileService
return get_data_service_client(FileService,
- args['storage-account'],
- args['storage-account-key'],
- args['connection-string'])
+ args.get('storage-account', None),
+ args.get('storage-account-key', None),
+ args.get('connection-string', None))
def _update_progress(current, total):
if total:
diff --git a/src/azure/cli/extensions/__init__.py b/src/azure/cli/extensions/__init__.py
index 094ccbf7fd9..84dffe9399f 100644
--- a/src/azure/cli/extensions/__init__.py
+++ b/src/azure/cli/extensions/__init__.py
@@ -1,8 +1,6 @@
from .query import register as register_query
from .transform import register as register_transform
-from .experimental import register as register_experimental
def register_extensions(application):
- register_query(application.session)
- register_transform(application.session)
- register_experimental(application.session)
+ register_query(application)
+ register_transform(application)
diff --git a/src/azure/cli/extensions/query.py b/src/azure/cli/extensions/query.py
index 960caa174d7..bbcd29e5c07 100644
--- a/src/azure/cli/extensions/query.py
+++ b/src/azure/cli/extensions/query.py
@@ -1,19 +1,24 @@
-def register(event_dispatcher):
- def register_global_parameter(self, parser):
+def register(application):
+ def register_global_parameter(parser):
# Let the program know that we are adding a parameter --query
- parser.add_argument('--query', dest='_jmespath_query', metavar='JMESPATH', help='JMESPath query string. See http://jmespath.org/ for more information and examples.')
+ parser.add_argument('--query', dest='_jmespath_query', metavar='JMESPATH',
+ help='JMESPath query string. See http://jmespath.org/ for more information and examples.') # pylint: disable=line-too-long
- def handle_query_parameter(_, args):
+ def handle_query_parameter(args):
# Of there is a query specified on the command line, we'll take care of that!
- query_value = args._jmespath_query
- del(args._jmespath_query)
+ try:
+ query_value = args._jmespath_query # pylint: disable=protected-access
+ del args._jmespath_query
- if query_value:
- def filter_output(_, event_data):
- import jmespath
- event_data['result'] = jmespath.search(query_value, event_data['result'])
+ if query_value:
+ def filter_output(event_data):
+ import jmespath
+ event_data['result'] = jmespath.search(query_value, event_data['result'])
- event_dispatcher.register(event_dispatcher.FILTER_RESULT, filter_output)
+ application.register(application.FILTER_RESULT, filter_output)
- event_dispatcher.register('GlobalParser.Created', register_global_parameter)
- event_dispatcher.register('CommandParser.Parsed', handle_query_parameter)
+ except AttributeError:
+ pass
+
+ application.register(application.GLOBAL_PARSER_CREATED, register_global_parameter)
+ application.register(application.COMMAND_PARSER_PARSED, handle_query_parameter)
diff --git a/src/azure/cli/extensions/transform.py b/src/azure/cli/extensions/transform.py
index f9152e1f816..5804d22b607 100644
--- a/src/azure/cli/extensions/transform.py
+++ b/src/azure/cli/extensions/transform.py
@@ -1,8 +1,7 @@
import re
def register(event_dispatcher):
- @event_dispatcher.event_handler(event_dispatcher.TRANSFORM_RESULT)
- def resource_group_transform(_, event_data): # pylint: disable=unused-variable
+ def resource_group_transform(event_data):
def parse_id(strid):
parsed = {}
parts = re.split('/', strid)
@@ -25,3 +24,4 @@ def add_resource_group(obj):
add_resource_group(obj[item_key])
add_resource_group(event_data['result'])
+ event_dispatcher.register('TransformResult', resource_group_transform)
diff --git a/src/azure/cli/main.py b/src/azure/cli/main.py
index ba3c4536424..063a71ca2ea 100644
--- a/src/azure/cli/main.py
+++ b/src/azure/cli/main.py
@@ -1,7 +1,6 @@
import os
import sys
-from .parser import AzCliCommandParser
from .application import Application
from ._logging import configure_logging, logger
@@ -26,14 +25,14 @@ def main(args, file=sys.stdout): #pylint: disable=redefined-builtin
CONFIG.get('locale', 'en-US')))
app = Application()
- app.load_commands(args)
+ app.load_commands()
try:
cmd_result = app.execute(args)
# Commands can return a dictionary/list of results
# If they do, we print the results.
if cmd_result:
- formatter = OutputProducer.get_formatter(app.session.output_format)
- OutputProducer(formatter=formatter, file=file).out(app.session, cmd_result)
+ formatter = OutputProducer.get_formatter(app.configuration.output_format)
+ OutputProducer(formatter=formatter, file=file).out(cmd_result)
except RuntimeError as ex:
logger.error(ex.args[0])
return ex.args[1] if len(ex.args) >= 2 else -1
diff --git a/src/azure/cli/parser.py b/src/azure/cli/parser.py
index ee19c016bf9..6718dcbd265 100644
--- a/src/azure/cli/parser.py
+++ b/src/azure/cli/parser.py
@@ -15,7 +15,7 @@ def __init__(self, **kwargs):
self.subparsers = {}
self.parents = kwargs.get('parents', None)
- def load_command_table(self, session, command_table):
+ def load_command_table(self, command_table):
"""Load a command table into our parser.
"""
# If we haven't already added a subparser, we
@@ -31,11 +31,10 @@ def load_command_table(self, session, command_table):
# To work around http://bugs.python.org/issue9253, we artificially add any new
# parsers we add to the "choices" section of the subparser.
subparser.choices[command_name] = command_name
- command_parser = subparser.add_parser(command_name, description=metadata.get('description'),
+ command_parser = subparser.add_parser(command_name,
+ description=metadata.get('description'),
parents=self.parents, conflict_handler='resolve')
- session.raise_event('AzCliCommandParser.SubparserCreated',
- {'parser': command_parser, 'metadata': metadata})
- for arg in metadata['options']:
+ for arg in metadata['arguments']:
names = arg.pop('name').split()
command_parser.add_argument(*names,
**arg)
diff --git a/src/azure/cli/tests/test_argparse.py b/src/azure/cli/tests/test_argparse.py
deleted file mode 100644
index ba5265b75e1..00000000000
--- a/src/azure/cli/tests/test_argparse.py
+++ /dev/null
@@ -1,207 +0,0 @@
-import unittest
-from six import StringIO
-
-from azure.cli._argparse import ArgumentParser, IncorrectUsageError
-from azure.cli._logging import logger
-import logging
-import azure.cli._util as util
-
-class Test_argparse(unittest.TestCase):
- @classmethod
- def setUpClass(cls):
- # Ensure initialization has occurred correctly
- import azure.cli.main
- logging.basicConfig(level=logging.DEBUG)
-
- @classmethod
- def tearDownClass(cls):
- logging.shutdown()
-
- def test_nouns(self):
- p = ArgumentParser('test')
- res = [False, False, False]
- def set_n1(a, b): res[0] = True
- def set_n2(a, b): res[1] = True
- def set_n3(a, b): res[2] = True
- p.add_command(set_n1, 'n1')
- p.add_command(set_n2, 'n1 n2')
- p.add_command(set_n3, 'n1 n2 n3')
-
- p.execute('n1 n2 n3'.split())
- self.assertSequenceEqual(res, (False, False, True))
- p.execute('n1'.split())
- self.assertSequenceEqual(res, (True, False, True))
- res[0] = False
- p.execute('n1 n2'.split())
- self.assertSequenceEqual(res, (False, True, True))
-
- def test_args(self):
- p = ArgumentParser('test')
- p.add_command(lambda a, b: (a, b), 'n1', args=[('--arg -a', '', False, None), ('-b ', '', False, None)])
-
- cmd_result = p.execute('n1 -a x'.split())
- res, other = cmd_result.result
- self.assertTrue(res.arg)
- self.assertSequenceEqual(res.positional, ['x'])
-
- # Should recognize args with alternate prefix
- cmd_result = p.execute('n1 /a'.split())
- res, other = cmd_result.result
- self.assertTrue(res.arg)
- cmd_result = p.execute('n1 /arg'.split())
- res, other = cmd_result.result
- self.assertTrue(res.arg)
-
- # Should not recognize "------a"
- cmd_result = p.execute('n1 ------a'.split())
- res, other = cmd_result.result
- self.assertNotIn('arg', res)
- # First two '--' match, so '----a' is added to dict
- self.assertIn('----a', other)
-
- cmd_result = p.execute('n1 -a:x'.split())
- res = cmd_result.result
- self.assertIsNone(res)
-
- cmd_result = p.execute('n1 -b -a x'.split())
- res, other = cmd_result.result
- self.assertEqual(res.b, '-a')
- self.assertSequenceEqual(res.positional, ['x'])
- self.assertRaises(IncorrectUsageError, lambda: res.arg)
-
- cmd_result = p.execute('n1 -b:-a x'.split())
- res, other = cmd_result.result
- self.assertEqual(res.b, '-a')
- self.assertSequenceEqual(res.positional, ['x'])
- self.assertRaises(IncorrectUsageError, lambda: res.arg)
-
- def test_unexpected_args(self):
- p = ArgumentParser('test')
- p.add_command(lambda a, b: (a, b), 'n1', args=[('-a', '', False, None)])
-
- cmd_result = p.execute('n1 -b=2'.split())
- res, other = cmd_result.result
- self.assertFalse(res)
- self.assertEqual('2', other.b)
-
- cmd_result = p.execute('n1 -b.c.d=2'.split())
- res, other = cmd_result.result
- self.assertFalse(res)
- self.assertEqual('2', other.b.c.d)
-
- cmd_result = p.execute('n1 -b.c.d 2 -b.c.e:3'.split())
- res, other = cmd_result.result
- self.assertFalse(res)
- self.assertEqual('2', other.b.c.d)
- self.assertEqual('3', other.b.c.e)
-
- def test_required_args(self):
- p = ArgumentParser('test')
- p.add_command(lambda a, b: (a, b),
- 'n1', args=[('--arg -a', '', True, None),
- ('-b ', '', False, None)])
-
- cmd_result = p.execute('n1 -a x'.split())
- res, other = cmd_result.result
- self.assertTrue(res.arg)
- self.assertSequenceEqual(res.positional, ['x'])
-
- io = StringIO()
- cmd_result = p.execute('n1 -b x'.split(), out=io)
- self.assertIsNone(cmd_result.result)
- self.assertTrue(io.getvalue().startswith("Missing required argument 'arg'"))
- io.close()
-
- def test_specify_output_format(self):
- p = ArgumentParser('test')
- p.add_command(lambda a, b: (a, b), 'n1', args=[('--arg -a', '', True, None), ('-b ', '', False, None)])
-
- cmd_result = p.execute('n1 -a x'.split())
- self.assertEqual(cmd_result.output_format, None)
-
- cmd_result = p.execute('n1 -a x --output json'.split())
- self.assertEqual(cmd_result.output_format, 'json')
-
- cmd_result = p.execute('n1 -a x --output table'.split())
- self.assertEqual(cmd_result.output_format, 'table')
-
- cmd_result = p.execute('n1 -a x --output text'.split())
- self.assertEqual(cmd_result.output_format, 'text')
-
- # Invalid format
- io = StringIO()
- cmd_res = p.execute('n1 -a x --output unknown'.split(), out=io)
- self.assertIsNone(cmd_res.output_format)
- self.assertTrue(io.getvalue().startswith("Invalid output format 'unknown'"))
- io.close()
-
- # Invalid format
- cmd_result = p.execute('n1 -a x --output'.split())
- self.assertEqual(cmd_result.output_format, None)
-
- def test_args_completion(self):
- p = ArgumentParser('test')
- p.add_command(lambda a, b: (a, b), 'n1', args=[('--arg -a', '', True, None), ('-b ', '', False, None)])
-
- # Can't use "with StringIO() as ...", as Python2/StringIO doesn't have __exit__.
- io = StringIO()
- p.execute('n1 - --complete'.split(),
- show_usage=False,
- show_completions=True,
- out=io)
- candidates = util.normalize_newlines(io.getvalue())
- io.close()
- self.assertEqual(candidates, '--arg\n-a\n-b\n')
-
- #matching '--arg for '--a'
- io=StringIO()
- p.execute('n1 --a --complete'.split(),
- show_usage=False,
- out=io)
- candidates = util.normalize_newlines(io.getvalue())
- io.close()
- self.assertEqual(candidates, '--arg\n')
-
- #matching 'n1' for 'n'
- io = StringIO()
- p.execute('n --complete'.split(),
- show_usage=False,
- show_completions=True,
- out=io)
- candidates = util.normalize_newlines(io.getvalue())
- io.close()
- self.assertEqual(candidates, 'n1\n')
-
- #if --arg is used, then both '-a' and "--arg" should not be in the
- #candidate list
- io = StringIO()
- p.execute('n1 --arg hello - --complete'.split(),
- show_usage=False,
- show_completions=True,
- out=io)
- candidates = util.normalize_newlines(io.getvalue())
- io.close()
- self.assertEqual(candidates, '-b\n')
-
- #if all argument are used, candidate list is empty
- io = StringIO()
- p.execute('n1 -a -b --complete'.split(),
- show_usage=False,
- show_completions=True,
- out=io)
- candidates = util.normalize_newlines(io.getvalue())
- io.close()
- self.assertEqual(candidates, '\n')
-
- #if at parameter value level, get nothing for N.Y.I.
- io = StringIO()
- p.execute('n1 -a --complete'.split(),
- show_usage=False,
- show_completions=True,
- out=io)
- candidates = util.normalize_newlines(io.getvalue())
- io.close()
- self.assertEqual(candidates, '\n')
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/src/azure/cli/tests/test_connection_verify.py b/src/azure/cli/tests/test_connection_verify.py
index 7a6b1c6613b..ca6444f7ddf 100644
--- a/src/azure/cli/tests/test_connection_verify.py
+++ b/src/azure/cli/tests/test_connection_verify.py
@@ -5,7 +5,7 @@
except ImportError:
from mock import MagicMock
-from azure.cli._argparse import ArgumentParser, IncorrectUsageError
+from azure.cli.parser import IncorrectUsageError
from azure.cli._logging import logger
import logging
import azure.cli._debug as _debug