Skip to content
Merged
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
29 changes: 28 additions & 1 deletion src/azure/cli/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@

from __future__ import print_function
from codecs import open as codecs_open
from datetime import datetime, timedelta
import json
import sys
import platform
import re
import sys
from enum import Enum

CLI_PACKAGE_NAME = 'azure-cli'
COMPONENT_PREFIX = 'azure-cli-'
Expand Down Expand Up @@ -60,3 +63,27 @@ def get_file_json(file_path):
raise CLIError("File '{}' contains error: {}".format(file_path, str(ex)))

raise CLIError('Failed to decode file {} - unknown decoding'.format(file_path))

KEYS_CAMELCASE_PATTERN = re.compile('(?!^)_([a-zA-Z])')
def todict(obj): #pylint: disable=too-many-return-statements
def to_camelcase(s):
return re.sub(KEYS_CAMELCASE_PATTERN, lambda x: x.group(1).upper(), s)

if isinstance(obj, dict):
return {k: todict(v) for (k, v) in obj.items()}
elif isinstance(obj, list):
return [todict(a) for a in obj]
elif isinstance(obj, Enum):
return obj.value
elif isinstance(obj, datetime):
return obj.isoformat()
elif isinstance(obj, timedelta):
return str(obj)
elif hasattr(obj, '_asdict'):
return todict(obj._asdict())
elif hasattr(obj, '__dict__'):
return dict([(to_camelcase(k), todict(v))
for k, v in obj.__dict__.items()
if not callable(v) and not k.startswith('_')])
else:
return obj
32 changes: 2 additions & 30 deletions src/azure/cli/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,16 @@
#---------------------------------------------------------------------------------------------

from collections import defaultdict
from datetime import datetime, timedelta
import sys
import os
import re
import uuid
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
from azure.cli._util import todict

logger = _logging.get_az_logger(__name__)

Expand Down Expand Up @@ -117,7 +115,7 @@ def execute(self, argv):
params.pop('command', None)

result = expanded_arg.func(params)
result = self.todict(result)
result = todict(result)
results.append(result)

if len(results) == 1:
Expand Down Expand Up @@ -162,32 +160,6 @@ def remove(self, name, handler):
self._event_handlers[name].remove(handler)
logger.info("Removed application event handler '%s' at %s", name, handler)

KEYS_CAMELCASE_PATTERN = re.compile('(?!^)_([a-zA-Z])')
@classmethod
def todict(cls, obj): #pylint: disable=too-many-return-statements

def to_camelcase(s):
return re.sub(cls.KEYS_CAMELCASE_PATTERN, lambda x: x.group(1).upper(), s)

if isinstance(obj, dict):
return {k: cls.todict(v) for (k, v) in obj.items()}
elif isinstance(obj, list):
return [cls.todict(a) for a in obj]
elif isinstance(obj, Enum):
return obj.value
elif isinstance(obj, datetime):
return obj.isoformat()
elif isinstance(obj, timedelta):
return str(obj)
elif hasattr(obj, '_asdict'):
return cls.todict(obj._asdict())
elif hasattr(obj, '__dict__'):
return dict([(to_camelcase(k), cls.todict(v))
for k, v in obj.__dict__.items()
if not callable(v) and not k.startswith('_')])
else:
return obj

@staticmethod
def _register_builtin_arguments(**kwargs):
global_group = kwargs['global_group']
Expand Down
40 changes: 0 additions & 40 deletions src/azure/cli/tests/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#---------------------------------------------------------------------------------------------

import unittest
from collections import namedtuple

from six import StringIO

Expand All @@ -27,45 +26,6 @@ def setUp(self):
def tearDown(self):
self.io.close()

def test_application_todict_none(self):
the_input = None
actual = Application.todict(the_input)
expected = None
self.assertEqual(actual, expected)

def test_application_todict_dict_empty(self):
the_input = {}
actual = Application.todict(the_input)
expected = {}
self.assertEqual(actual, expected)

def test_application_todict_dict(self):
the_input = {'a': 'b'}
actual = Application.todict(the_input)
expected = {'a': 'b'}
self.assertEqual(actual, expected)

def test_application_todict_list(self):
the_input = [{'a': 'b'}]
actual = Application.todict(the_input)
expected = [{'a': 'b'}]
self.assertEqual(actual, expected)

def test_application_todict_obj(self):
MyObject = namedtuple('MyObject', 'a b')
the_input = MyObject('x', 'y')
actual = Application.todict(the_input)
expected = {'a': 'x', 'b': 'y'}
self.assertEqual(actual, expected)

def test_application_todict_dict_with_obj(self):
MyObject = namedtuple('MyObject', 'a b')
mo = MyObject('x', 'y')
the_input = {'a': mo}
actual = Application.todict(the_input)
expected = {'a': {'a': 'x', 'b': 'y'}}
self.assertEqual(actual, expected)

def test_application_register_and_call_handlers(self):
handler_called = [False]

Expand Down
42 changes: 41 additions & 1 deletion src/azure/cli/tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,53 @@
#---------------------------------------------------------------------------------------------

# pylint: disable=line-too-long
from collections import namedtuple
import unittest
import tempfile

from azure.cli._util import get_file_json
from azure.cli._util import get_file_json, todict

class TestUtils(unittest.TestCase):

def test_application_todict_none(self):
the_input = None
actual = todict(the_input)
expected = None
self.assertEqual(actual, expected)

def test_application_todict_dict_empty(self):
the_input = {}
actual = todict(the_input)
expected = {}
self.assertEqual(actual, expected)

def test_application_todict_dict(self):
the_input = {'a': 'b'}
actual = todict(the_input)
expected = {'a': 'b'}
self.assertEqual(actual, expected)

def test_application_todict_list(self):
the_input = [{'a': 'b'}]
actual = todict(the_input)
expected = [{'a': 'b'}]
self.assertEqual(actual, expected)

def test_application_todict_obj(self):
MyObject = namedtuple('MyObject', 'a b')
the_input = MyObject('x', 'y')
actual = todict(the_input)
expected = {'a': 'x', 'b': 'y'}
self.assertEqual(actual, expected)

def test_application_todict_dict_with_obj(self):
MyObject = namedtuple('MyObject', 'a b')
mo = MyObject('x', 'y')
the_input = {'a': mo}
actual = todict(the_input)
expected = {'a': {'a': 'x', 'b': 'y'}}
self.assertEqual(actual, expected)

def test_load_json_from_file(self):
_, pathname = tempfile.mkstemp()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
#---------------------------------------------------------------------------------------------

#pylint: disable=line-too-long
import azure.cli.commands.parameters #pylint: disable=unused-import

from azure.cli.commands import register_cli_argument
Expand All @@ -15,9 +15,23 @@
register_cli_argument('ad', 'spn', help='service principal name')
register_cli_argument('ad', 'upn', help='user principal name, e.g. [email protected]')
register_cli_argument('ad', 'query_filter', options_list=('--filter',), help='OData filter')
register_cli_argument('role assignment', 'role_assignment_name',
options_list=('--role-assignment-name', '-n'))
register_cli_argument('role assignment', 'role', help='role name or id')
register_cli_argument('ad user', 'mail_nickname',
help='mail alias. Defaults to user principal name')
register_cli_argument('ad user', 'force_change_password_next_login', action='store_true')
register_cli_argument('role assignment', 'role_assignment_name',
options_list=('--role-assignment-name', '-n'))
register_cli_argument('role assignment', 'role', help='role name or id')
register_cli_argument('role assignment', 'show_all', options_list=('--all',), action='store_true',
help='show all assignments under the current subscription')
register_cli_argument('role assignment', 'include_inherited', action='store_true',
help='include assignments applied on parent scopes')
register_cli_argument('role assignment', 'assignee', help='represent a user, group, or service principal. supported format: object id, user sign-in name, or service principal name')
register_cli_argument('role assignment', 'ids', nargs='+', help='space separated role assignment ids')
register_cli_argument('role', 'role_id', help='the role id')
register_cli_argument('role', 'resource_group_name', options_list=('--resource-group', '-g'),
help='use it only if the role or assignment was added at the level of a resource group')
register_cli_argument('role', 'resource_id', help='use it only if the role or assignment was added at the level of a resource')
register_cli_argument('role', 'custom_role_only', action='store_true', help='custom roles only(vs. build-in ones)')
register_cli_argument('role', 'role_definition', help="json formatted content which defines the new role. run 'show-create-template' to get samples")
Copy link
Member

Choose a reason for hiding this comment

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

help="json formatted content which defines the new role. run 'show-create-template' to get samples [](start = 49, length = 98)

Should be more clear. It is either the path to the JSON definition file or the raw JSON definition itself.

register_cli_argument('role', 'name', options_list=('--name', '-n'), help="the role's logical name")

Loading