From 0d8b4abd89d9e98d38f206a0cd6a378e2351d810 Mon Sep 17 00:00:00 2001 From: kai ru <69238381+kairu-ms@users.noreply.github.com> Date: Wed, 20 Jul 2022 14:52:39 +0800 Subject: [PATCH] {Core} `aaz`: Improve shorthand syntax (#23268) * support blank expression in 'full value' format * 1.support partial value split in AAZShortHandSyntaxParser 2.support Single Quotes String in partial value keys * fix style issue --- .../azure/cli/core/aaz/_arg_action.py | 67 +- .../azure/cli/core/aaz/_base.py | 40 ++ .../azure/cli/core/aaz/_utils.py | 111 ++- .../azure/cli/core/tests/test_aaz_arg.py | 672 ++++++++++-------- 4 files changed, 536 insertions(+), 354 deletions(-) diff --git a/src/azure-cli-core/azure/cli/core/aaz/_arg_action.py b/src/azure-cli-core/azure/cli/core/aaz/_arg_action.py index 225654084e4..edd73c6fb5e 100644 --- a/src/azure-cli-core/azure/cli/core/aaz/_arg_action.py +++ b/src/azure-cli-core/azure/cli/core/aaz/_arg_action.py @@ -6,14 +6,13 @@ # pylint: disable=protected-access import os -import re from argparse import Action from collections import OrderedDict from knack.log import get_logger from azure.cli.core import azclierror -from ._base import AAZUndefined +from ._base import AAZUndefined, AAZBlankArgValue from ._help import AAZShowHelp from ._utils import AAZShortHandSyntaxParser from .exceptions import AAZInvalidShorthandSyntaxError, AAZInvalidValueError @@ -94,9 +93,7 @@ def setup_operations(cls, dest_ops, values, prefix_keys=None): if prefix_keys is None: prefix_keys = [] if values is None: - if cls._schema._blank == AAZUndefined: - raise AAZInvalidValueError("argument cannot be blank") - data = cls._schema._blank # use blank data when values string is None + data = AAZBlankArgValue # use blank data when values string is None else: if isinstance(values, list): assert prefix_keys # the values will be input as an list when parse singular option of a list argument @@ -112,11 +109,16 @@ def setup_operations(cls, dest_ops, values, prefix_keys=None): raise aaz_help else: data = values - data = cls.format_data(data) + data = cls.format_data(data) dest_ops.add(data, *prefix_keys) @classmethod def format_data(cls, data): + if data == AAZBlankArgValue: + if cls._schema._blank == AAZUndefined: + raise AAZInvalidValueError("argument value cannot be blank") + data = cls._schema._blank + if isinstance(data, str): # transfer string into correct data if cls._schema.enum: @@ -136,10 +138,6 @@ def format_data(cls, data): class AAZCompoundTypeArgAction(AAZArgAction): # pylint: disable=abstract-method - key_pattern = re.compile( - r'^(((\[-?[0-9]+])|(([a-zA-Z0-9_\-]+)(\[-?[0-9]+])?))(\.([a-zA-Z0-9_\-]+)(\[-?[0-9]+])?)*)=(.*)$' - ) # 'Partial Value' format - @classmethod def setup_operations(cls, dest_ops, values, prefix_keys=None): if prefix_keys is None: @@ -161,38 +159,10 @@ def setup_operations(cls, dest_ops, values, prefix_keys=None): @classmethod def decode_values(cls, values): for v in values: - key, key_parts, v = cls._split_value_str(v) + key, key_parts, v = cls._str_parser.split_partial_value(v) v = cls._decode_value(key, key_parts, v) yield key, key_parts, v - @classmethod - def _split_value_str(cls, v): - """ split 'Partial Value' format """ - assert isinstance(v, str) - match = cls.key_pattern.fullmatch(v) - if not match: - key = None - else: - key = match[1] - v = match[len(match.regs) - 1] - key_parts = cls._split_key(key) - return key, key_parts, v - - @staticmethod - def _split_key(key): - """ split index key of 'Partial Value' format """ - if key is None: - return tuple() - key_items = [] - key = key[0] + key[1:].replace('[', '.[') # transform 'ab[2]' to 'ab.[2]', keep '[1]' unchanged - for part in key.split('.'): - assert part - if part.startswith('['): - assert part.endswith(']') - part = int(part[1:-1]) - key_items.append(part) - return tuple(key_items) - @classmethod def _decode_value(cls, key, key_items, value): # pylint: disable=unused-argument from ._arg import AAZSimpleTypeArg @@ -204,7 +174,7 @@ def _decode_value(cls, key, key_items, value): # pylint: disable=unused-argumen if len(value) == 0: # the express "a=" will return the blank value of schema 'a' - return schema._blank + return AAZBlankArgValue try: if isinstance(schema, AAZSimpleTypeArg): @@ -236,6 +206,11 @@ class AAZObjectArgAction(AAZCompoundTypeArgAction): @classmethod def format_data(cls, data): + if data == AAZBlankArgValue: + if cls._schema._blank == AAZUndefined: + raise AAZInvalidValueError("argument value cannot be blank") + data = cls._schema._blank + if data is None: if cls._schema._nullable: return data @@ -258,6 +233,11 @@ class AAZDictArgAction(AAZCompoundTypeArgAction): @classmethod def format_data(cls, data): + if data == AAZBlankArgValue: + if cls._schema._blank == AAZUndefined: + raise AAZInvalidValueError("argument value cannot be blank") + data = cls._schema._blank + if data is None: if cls._schema._nullable: return data @@ -327,7 +307,7 @@ def setup_operations(cls, dest_ops, values, prefix_keys=None): # --args [val1,val2,val3] for value in values: - key, _, _ = cls._split_value_str(value) + key, _, _ = cls._str_parser.split_partial_value(value) if key is not None: # key should always be None raise ex @@ -357,6 +337,11 @@ def setup_operations(cls, dest_ops, values, prefix_keys=None): @classmethod def format_data(cls, data): + if data == AAZBlankArgValue: + if cls._schema._blank == AAZUndefined: + raise AAZInvalidValueError("argument value cannot be blank") + data = cls._schema._blank + if data is None: if cls._schema._nullable: return data diff --git a/src/azure-cli-core/azure/cli/core/aaz/_base.py b/src/azure-cli-core/azure/cli/core/aaz/_base.py index 9cda87755d6..4cfe7aa427c 100644 --- a/src/azure-cli-core/azure/cli/core/aaz/_base.py +++ b/src/azure-cli-core/azure/cli/core/aaz/_base.py @@ -99,3 +99,43 @@ def build(cls, schema): def __init__(self, data): self.data = data + + +class _AAZBlankArgValueType: + """Internal class for AAZUndefined global const""" + + def __str__(self): + return 'BlankArgValue' + + def __repr__(self): + return 'BlankArgValue' + + def __eq__(self, other): + return self is other + + def __ne__(self, other): + return self is not other + + def __bool__(self): + return False + + def __lt__(self, other): + self._cmp_err(other, '<') + + def __gt__(self, other): + self._cmp_err(other, '>') + + def __le__(self, other): + self._cmp_err(other, '<=') + + def __ge__(self, other): + self._cmp_err(other, '>=') + + def _cmp_err(self, other, op): + raise TypeError(f"unorderable types: {self.__class__.__name__}() {op} {other.__class__.__name__}()") + + +# AAZ framework defines a global const called AAZUndefined. Which is used to show a field is not defined. +# In order to different with `None` value +# This value is used in aaz package only. +AAZBlankArgValue = _AAZBlankArgValueType() diff --git a/src/azure-cli-core/azure/cli/core/aaz/_utils.py b/src/azure-cli-core/azure/cli/core/aaz/_utils.py index f6b8722f6d8..ad25e176fb8 100644 --- a/src/azure-cli-core/azure/cli/core/aaz/_utils.py +++ b/src/azure-cli-core/azure/cli/core/aaz/_utils.py @@ -2,11 +2,12 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- - +import re from collections import OrderedDict from azure.cli.core.aaz.exceptions import AAZInvalidShorthandSyntaxError from ._help import AAZShowHelp +from ._base import AAZBlankArgValue class AAZShortHandSyntaxParser: @@ -14,6 +15,10 @@ class AAZShortHandSyntaxParser: NULL_EXPRESSIONS = ('null',) # user can use "null" string to pass `None` value HELP_EXPRESSIONS = ('??', ) # the mark to show detail help. + partial_value_key_pattern = re.compile( + r"^(((\[-?[0-9]+])|((([a-zA-Z0-9_\-]+)|('([^']*)'(/([^']*)')*))(\[-?[0-9]+])?))(\.(([a-zA-Z0-9_\-]+)|('([^']*)'(/([^']*)')*))(\[-?[0-9]+])?)*)=(.*)$" # pylint: disable=line-too-long + ) # 'Partial Value' format + def __call__(self, data, is_simple=False): assert isinstance(data, str) if len(data) == 0: @@ -78,21 +83,24 @@ def parse_dict(self, remain): # pylint: disable=too-many-statements idx += length if idx < len(remain) and remain[idx] == ':': idx += 1 + if idx >= len(remain): + raise AAZInvalidShorthandSyntaxError(remain, idx, 1, "Cannot parse empty") + + try: + value, length = self.parse_value(remain[idx:]) + except AAZInvalidShorthandSyntaxError as ex: + ex.error_data = remain + ex.error_at += idx + raise ex + except AAZShowHelp as aaz_help: + aaz_help.keys = [key, *aaz_help.keys] + raise aaz_help + elif idx < len(remain) and remain[idx] in (',', '}'): + # use blank value + value = AAZBlankArgValue + length = 0 else: - raise AAZInvalidShorthandSyntaxError(remain, idx, 1, "Expect character ':'") - - if idx >= len(remain): - raise AAZInvalidShorthandSyntaxError(remain, idx, 1, "Cannot parse empty") - - try: - value, length = self.parse_value(remain[idx:]) - except AAZInvalidShorthandSyntaxError as ex: - ex.error_data = remain - ex.error_at += idx - raise ex - except AAZShowHelp as aaz_help: - aaz_help.keys = [key, *aaz_help.keys] - raise aaz_help + raise AAZInvalidShorthandSyntaxError(remain, idx, 1, "Expect characters ':' or ','") result[key] = value idx += length @@ -201,3 +209,76 @@ def parse_single_quotes_string(remain): if quote is not None: raise AAZInvalidShorthandSyntaxError(remain, idx, 1, f"Miss end quota character: {quote}") return result, idx + + @classmethod + def split_partial_value(cls, v): + """ split 'Partial Value' format """ + assert isinstance(v, str) + match = cls.partial_value_key_pattern.fullmatch(v) + if not match: + key = None + else: + key = match[1] + v = match[len(match.regs) - 1] + key_parts = cls.parse_partial_value_key(key) + return key, key_parts, v + + @classmethod + def parse_partial_value_key(cls, key): + if key is None: + return tuple() + key_items = [] + idx = 0 + while idx < len(key): + if key[idx] == '[': + try: + key_item, length = cls.parse_partial_value_idx_key(key[idx:]) + except AAZInvalidShorthandSyntaxError as ex: + ex.error_data = key + ex.error_at += idx + raise ex + idx += length + else: + try: + key_item, length = cls.parse_partial_value_prop_key(key[idx:]) + except AAZInvalidShorthandSyntaxError as ex: + ex.error_data = key + ex.error_at += idx + raise ex + idx += length + key_items.append(key_item) + return tuple(key_items) + + @classmethod + def parse_partial_value_idx_key(cls, remain): + assert remain[0] == '[' + idx = 1 + while idx < len(remain) and remain[idx] != ']': + idx += 1 + if idx < len(remain) and remain[idx] == ']': + result = remain[1:idx] + idx += 1 + else: + raise AAZInvalidShorthandSyntaxError(remain, idx, 1, "Expect character ']'") + + if len(result) == 0: + raise AAZInvalidShorthandSyntaxError(remain, 0, 2, "Miss index") + if idx < len(remain) and remain[idx] == '.': + idx += 1 + return int(result), idx + + @classmethod + def parse_partial_value_prop_key(cls, remain): + idx = 0 + if remain[0] == "'": + result, length = cls.parse_single_quotes_string(remain) + idx += length + else: + while idx < len(remain) and remain[idx] not in ('.', '['): + idx += 1 + result = remain[:idx] + if len(result) == 0: + raise AAZInvalidShorthandSyntaxError(remain, 0, idx, "Miss prop name") + if idx < len(remain) and remain[idx] == '.': + idx += 1 + return result, idx diff --git a/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg.py b/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg.py index 60bd920334a..3ab6d63f2dc 100644 --- a/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg.py +++ b/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg.py @@ -18,76 +18,76 @@ def test_aaz_shorthand_syntax_simple_value(self): parser = AAZShortHandSyntaxParser() # test null value - assert parser("null", is_simple=True) is None - assert parser("'null'", is_simple=True) == 'null' + self.assertEqual(parser("null", is_simple=True), None) + self.assertEqual(parser("'null'", is_simple=True), 'null') - assert parser("None", is_simple=True) == 'None' - assert parser("'None'", is_simple=True) == 'None' + self.assertEqual(parser("None", is_simple=True), 'None') + self.assertEqual(parser("'None'", is_simple=True), 'None') # test single quota - assert parser("''/'", is_simple=True) == "'" - assert parser("'/'", is_simple=True) == "/" - assert parser("'\"'", is_simple=True) == '"' + self.assertEqual(parser("''/'", is_simple=True), "'") + self.assertEqual(parser("'/'", is_simple=True), "/") + self.assertEqual(parser("'\"'", is_simple=True), '"') - assert parser('"\'"', is_simple=True) == '"\'"' - assert parser('"\""', is_simple=True) == '"\""' - assert parser("'https://azure.microsoft.com/en-us/'", is_simple=True) == "https://azure.microsoft.com/en-us/" - assert parser("'C:\\Program Files (x86)\\'", is_simple=True) == "C:\\Program Files (x86)\\" - assert parser("'/usr/lib/python3.8/'", is_simple=True) == "/usr/lib/python3.8/" + self.assertEqual(parser('"\'"', is_simple=True), '"\'"') + self.assertEqual(parser('"\""', is_simple=True), '"\""') + self.assertEqual(parser("'https://azure.microsoft.com/en-us/'", is_simple=True), "https://azure.microsoft.com/en-us/") + self.assertEqual(parser("'C:\\Program Files (x86)\\'", is_simple=True), "C:\\Program Files (x86)\\") + self.assertEqual(parser("'/usr/lib/python3.8/'", is_simple=True), "/usr/lib/python3.8/") # test escape character - assert parser("'\"'", is_simple=True) == "\"" - assert parser("'\'/'", is_simple=True) == "\'" - - assert parser("'\b'", is_simple=True) == "\b" - assert parser("'\f'", is_simple=True) == "\f" - assert parser("'\n'", is_simple=True) == "\n" - assert parser("'\r'", is_simple=True) == "\r" - assert parser("'\t'", is_simple=True) == "\t" - - assert parser('\b', is_simple=True) == '\b' - assert parser('\f', is_simple=True) == '\f' - assert parser('\n', is_simple=True) == '\n' - assert parser('\r', is_simple=True) == '\r' - assert parser('\t', is_simple=True) == '\t' - - assert parser('"\b"', is_simple=True) == '"\b"' - assert parser('"\f"', is_simple=True) == '"\f"' - assert parser('"\n"', is_simple=True) == '"\n"' - assert parser('"\r"', is_simple=True) == '"\r"' - assert parser('"\t"', is_simple=True) == '"\t"' + self.assertEqual(parser("'\"'", is_simple=True), "\"") + self.assertEqual(parser("'\'/'", is_simple=True), "\'") + + self.assertEqual(parser("'\b'", is_simple=True), "\b") + self.assertEqual(parser("'\f'", is_simple=True), "\f") + self.assertEqual(parser("'\n'", is_simple=True), "\n") + self.assertEqual(parser("'\r'", is_simple=True), "\r") + self.assertEqual(parser("'\t'", is_simple=True), "\t") + + self.assertEqual(parser('\b', is_simple=True), '\b') + self.assertEqual(parser('\f', is_simple=True), '\f') + self.assertEqual(parser('\n', is_simple=True), '\n') + self.assertEqual(parser('\r', is_simple=True), '\r') + self.assertEqual(parser('\t', is_simple=True), '\t') + + self.assertEqual(parser('"\b"', is_simple=True), '"\b"') + self.assertEqual(parser('"\f"', is_simple=True), '"\f"') + self.assertEqual(parser('"\n"', is_simple=True), '"\n"') + self.assertEqual(parser('"\r"', is_simple=True), '"\r"') + self.assertEqual(parser('"\t"', is_simple=True), '"\t"') # whitespace character should be warped in single quotes # test normal string - assert parser("abc", is_simple=True) == "abc" - assert parser("1", is_simple=True) == "1" - assert parser("_", is_simple=True) == "_" - assert parser("''", is_simple=True) == '' - assert parser('""', is_simple=True) == '""' - assert parser("'{'", is_simple=True) == "{" - assert parser('"{"', is_simple=True) == '"{"' - assert parser("'}'", is_simple=True) == "}" - assert parser('"}"', is_simple=True) == '"}"' - assert parser(' ', is_simple=True) == ' ' - assert parser("' '", is_simple=True) == ' ' - - assert parser("'{abc'", is_simple=True) == "{abc" - assert parser("'abc}'", is_simple=True) == "abc}" - assert parser("'abc{'", is_simple=True) == "abc{" - assert parser("'}abc'", is_simple=True) == "}abc" - assert parser("'{a:1, b:2}'", is_simple=True) == "{a:1, b:2}" - - assert parser("'['", is_simple=True) == "[" - assert parser('"["', is_simple=True) == '"["' - assert parser("']'", is_simple=True) == "]" - assert parser('"]"', is_simple=True) == '"]"' - - assert parser("'[abc'", is_simple=True) == "[abc" - assert parser("'abc['", is_simple=True) == "abc[" - assert parser("'abc]'", is_simple=True) == "abc]" - assert parser("']abc'", is_simple=True) == "]abc" - assert parser("'[1, 2, 3]'", is_simple=True) == "[1, 2, 3]" + self.assertEqual(parser("abc", is_simple=True), "abc") + self.assertEqual(parser("1", is_simple=True), "1") + self.assertEqual(parser("_", is_simple=True), "_") + self.assertEqual(parser("''", is_simple=True), '') + self.assertEqual(parser('""', is_simple=True), '""') + self.assertEqual(parser("'{'", is_simple=True), "{") + self.assertEqual(parser('"{"', is_simple=True), '"{"') + self.assertEqual(parser("'}'", is_simple=True), "}") + self.assertEqual(parser('"}"', is_simple=True), '"}"') + self.assertEqual(parser(' ', is_simple=True), ' ') + self.assertEqual(parser("' '", is_simple=True), ' ') + + self.assertEqual(parser("'{abc'", is_simple=True), "{abc") + self.assertEqual(parser("'abc}'", is_simple=True), "abc}") + self.assertEqual(parser("'abc{'", is_simple=True), "abc{") + self.assertEqual(parser("'}abc'", is_simple=True), "}abc") + self.assertEqual(parser("'{a:1, b:2}'", is_simple=True), "{a:1, b:2}") + + self.assertEqual(parser("'['", is_simple=True), "[") + self.assertEqual(parser('"["', is_simple=True), '"["') + self.assertEqual(parser("']'", is_simple=True), "]") + self.assertEqual(parser('"]"', is_simple=True), '"]"') + + self.assertEqual(parser("'[abc'", is_simple=True), "[abc") + self.assertEqual(parser("'abc['", is_simple=True), "abc[") + self.assertEqual(parser("'abc]'", is_simple=True), "abc]") + self.assertEqual(parser("']abc'", is_simple=True), "]abc") + self.assertEqual(parser("'[1, 2, 3]'", is_simple=True), "[1, 2, 3]") with self.assertRaises(AAZInvalidShorthandSyntaxError): parser("", is_simple=True) @@ -102,83 +102,100 @@ def test_aaz_shorthand_syntax_simple_value(self): parser("'A'bc", is_simple=True) def test_aaz_shorthand_syntax_dict_value(self): - from azure.cli.core.aaz._utils import AAZShortHandSyntaxParser + from azure.cli.core.aaz._utils import AAZShortHandSyntaxParser, AAZBlankArgValue from azure.cli.core.aaz.exceptions import AAZInvalidShorthandSyntaxError parser = AAZShortHandSyntaxParser() - assert parser("{}") == {} - assert parser("{a:1,b:2,c:null,d:''}") == { + self.assertEqual(parser("{}"), {}) + self.assertEqual(parser("{a:1,b:2,c:null,d:''}"), { "a": "1", "b": "2", "c": None, "d": '' - } - assert parser("{a:1,b:2,c:None,d:'',}") == { + }) + self.assertEqual(parser("{a:1,b:2,c:None,d:'',}"), { "a": "1", "b": "2", "c": "None", "d": '' - } - assert parser("{a:1,b:''/'}") == { + }) + self.assertEqual(parser("{a:1,b:''/'}"), { "a": "1", "b": "'" - } + }) - assert parser("{a:1,b:'/'}") == { + self.assertEqual(parser("{a:1,b:'/'}"), { "a": "1", "b": "/" - } + }) - assert parser("{a:1,b:'//'}") == { + self.assertEqual(parser("{a:1,b:'//'}"), { "a": "1", "b": "//" - } + }) - assert parser("{a:1,b:/}") == { + self.assertEqual(parser("{a:1,b:/}"), { "a": "1", "b": "/" - } + }) - assert parser("{a:1,b:2,c:Null,d:''}") == { + self.assertEqual(parser("{a:1,b:2,c:Null,d:''}"), { "a": "1", "b": "2", "c": "Null", "d": '' - } - assert parser("{a:1,b:2,c:none,d:'',}") == { + }) + self.assertEqual(parser("{a:1,b:2,c:none,d:'',}"), { "a": "1", "b": "2", "c": "none", "d": '' - } + }) - assert parser("{a:{a1:' \n '/',a2:2}}") == { + self.assertEqual(parser("{a:{a1:' \n '/',a2:2}}"), { "a": { "a1": " \n '", "a2": "2", } - } + }) - assert parser("{a:{a1:1,a2:2}}") == { + self.assertEqual(parser("{a:{a1:1,a2:2}}"), { "a": { "a1": "1", "a2": "2", } - } - assert parser("{a:{a1:1,a2:2},}") == { + }) + self.assertEqual(parser("{a:{a1:1,a2:2},}"), { "a": { "a1": "1", "a2": "2", } - } + }) - assert parser("{a:{a1:{},a2:2}}") == { + self.assertEqual(parser("{a:{a1:{},a2:2}}"), { "a": { "a1": {}, "a2": "2", } - } + }) + + self.assertEqual(parser("{a:{a1,a2:2,c},a3,a4:''}"), { + "a": { + "a1": AAZBlankArgValue, + "a2": "2", + "c": AAZBlankArgValue, + }, + "a3": AAZBlankArgValue, + "a4": '', + }) - assert parser("{a:[{prop1:1,prop2:2},{a1:[b,null,c,'d:e \"]'],a2:'2 3\t\"}',a4:'',a3:null,}],e:f,g:null}") == { + self.assertEqual(parser("{a:1,'',c:1,e}"), { + "a": "1", + "": AAZBlankArgValue, + "c": "1", + "e": AAZBlankArgValue, + }) + + self.assertEqual(parser("{a:[{prop1:1,prop2:2},{a1:[b,null,c,'d:e \"]'],a2:'2 3\t\"}',a4:'',a3:null,}],e:f,g:null}"), { "a": [ { "prop1": "1", @@ -193,12 +210,12 @@ def test_aaz_shorthand_syntax_dict_value(self): ], "e": "f", "g": None, - } + }) # compare with json dt = {} s = json.dumps(dt, separators=(',', ':')) - assert parser(s) == dt + self.assertEqual(parser(s), dt) dt = {"a": 1} s = json.dumps(dt, separators=(',', ':')) @@ -209,6 +226,9 @@ def test_aaz_shorthand_syntax_dict_value(self): with self.assertRaises(AAZInvalidShorthandSyntaxError): parser("{a:1,b:}") + with self.assertRaises(AAZInvalidShorthandSyntaxError): + parser("{a:1,,c:1}") + with self.assertRaises(AAZInvalidShorthandSyntaxError): parser("{a:1,") @@ -221,9 +241,6 @@ def test_aaz_shorthand_syntax_dict_value(self): with self.assertRaises(AAZInvalidShorthandSyntaxError): parser("{a:1,b:]") - with self.assertRaises(AAZInvalidShorthandSyntaxError): - parser("{a:1,b}") - with self.assertRaises(AAZInvalidShorthandSyntaxError): parser("{a:1b:}") @@ -286,50 +303,50 @@ def test_aaz_shorthand_syntax_list_value(self): from azure.cli.core.aaz.exceptions import AAZInvalidShorthandSyntaxError parser = AAZShortHandSyntaxParser() - assert parser("[]") == [] + self.assertEqual(parser("[]"), []) - assert parser("[c]") == ["c"] + self.assertEqual(parser("[c]"), ["c"]) - assert parser("['c']") == ["c"] + self.assertEqual(parser("['c']"), ["c"]) - assert parser("[c,]") == ["c"] + self.assertEqual(parser("[c,]"), ["c"]) - assert parser("[null]") == [None] - assert parser("[null,]") == [None] - assert parser("[None]") == ['None'] - assert parser("[None,]") == ['None'] + self.assertEqual(parser("[null]"), [None]) + self.assertEqual(parser("[null,]"), [None]) + self.assertEqual(parser("[None]"), ['None']) + self.assertEqual(parser("[None,]"), ['None']) - assert parser("['null',]") == ['null'] + self.assertEqual(parser("['null',]"), ['null']) - assert parser("['None',]") == ['None'] + self.assertEqual(parser("['None',]"), ['None']) - assert parser("[' ',]") == [' '] + self.assertEqual(parser("[' ',]"), [' ']) - assert parser("[a,b,c,d]") == [ + self.assertEqual(parser("[a,b,c,d]"), [ "a", "b", "c", "d" - ] - assert parser("[a,null,'',d]") == [ + ]) + self.assertEqual(parser("[a,null,'',d]"), [ "a", None, "", "d" - ] - assert parser("[a,None,'',d,]") == [ + ]) + self.assertEqual(parser("[a,None,'',d,]"), [ "a", 'None', "", "d" - ] - assert parser("[a,[],{},[b,c,d],]") == [ + ]) + self.assertEqual(parser("[a,[],{},[b,c,d],]"), [ "a", [], {}, ["b", "c", "d"] - ] + ]) # compare with json ls = [] s = json.dumps(ls, separators=(',', ':')) - assert parser(s) == ls + self.assertEqual(parser(s), ls) ls = [None, {}] s = json.dumps(ls, separators=(',', ':')) - assert parser(s) == ls + self.assertEqual(parser(s), ls) ls = [1, 2, 3, 4] s = json.dumps(ls, separators=(',', ':')) - assert parser(s) == ['1', '2', '3', '4'] + self.assertEqual(parser(s), ['1', '2', '3', '4']) ls = ["1", "2"] s = json.dumps(ls, separators=(',', ':')) @@ -370,76 +387,84 @@ def test_aaz_shorthand_syntax_list_value(self): class TestAAZArg(unittest.TestCase): def test_aaz_compound_type_action_key_pattern(self): - from azure.cli.core.aaz._arg_action import AAZCompoundTypeArgAction - key_pattern = AAZCompoundTypeArgAction.key_pattern + from azure.cli.core.aaz._utils import AAZShortHandSyntaxParser + key_pattern = AAZShortHandSyntaxParser.partial_value_key_pattern test_value = "a.b.c=" match = key_pattern.fullmatch(test_value) - assert match[1] == 'a.b.c' - assert match[len(match.regs) - 1] == '' + self.assertEqual(match[1], 'a.b.c') + self.assertEqual(match[len(match.regs) - 1], '') test_value = "a_b-C=aaa" match = key_pattern.fullmatch(test_value) - assert match[1] == 'a_b-C' - assert match[len(match.regs) - 1] == 'aaa' + self.assertEqual(match[1], 'a_b-C') + self.assertEqual(match[len(match.regs) - 1], 'aaa') test_value = "[10]=aaa" match = key_pattern.fullmatch(test_value) - assert match[1] == '[10]' - assert match[len(match.regs) - 1] == 'aaa' + self.assertEqual(match[1], '[10]') + self.assertEqual(match[len(match.regs) - 1], 'aaa') test_value = "a_b.C[10]=aaa" match = key_pattern.fullmatch(test_value) - assert match[1] == 'a_b.C[10]' - assert match[len(match.regs) - 1] == 'aaa' + self.assertEqual(match[1], 'a_b.C[10]') + self.assertEqual(match[len(match.regs) - 1], 'aaa') test_value = "a_b.C[10].D-e_f=aaa" match = key_pattern.fullmatch(test_value) - assert match[1] == 'a_b.C[10].D-e_f' - assert match[len(match.regs) - 1] == 'aaa' + self.assertEqual(match[1], 'a_b.C[10].D-e_f') + self.assertEqual(match[len(match.regs) - 1], 'aaa') test_value = "a_b-C={aaa:b}" match = key_pattern.fullmatch(test_value) - assert match[1] == 'a_b-C' - assert match[len(match.regs) - 1] == '{aaa:b}' + self.assertEqual(match[1], 'a_b-C') + self.assertEqual(match[len(match.regs) - 1], '{aaa:b}') test_value = "a_b-C=[aaa,b]" match = key_pattern.fullmatch(test_value) - assert match[1] == 'a_b-C' - assert match[len(match.regs) - 1] == '[aaa,b]' + self.assertEqual(match[1], 'a_b-C') + self.assertEqual(match[len(match.regs) - 1], '[aaa,b]') + + test_value = "a.'/bc sef'/.sss[2]'[1]='/a='" + match = key_pattern.fullmatch(test_value) + self.assertEqual(match[1], "a.'/bc sef'/.sss[2]'[1]") + self.assertEqual(match[len(match.regs) - 1], "'/a='") test_value = "{a_b:aaa}" match = key_pattern.fullmatch(test_value) - assert match is None + self.assertEqual(match, None) test_value = "[aaa,bbb]" match = key_pattern.fullmatch(test_value) - assert match is None + self.assertEqual(match, None) test_value = "a_b.C[abc]=aaa" match = key_pattern.fullmatch(test_value) - assert match is None + self.assertEqual(match, None) test_value = "a_b.C[10].D-e_f" match = key_pattern.fullmatch(test_value) - assert match is None + self.assertEqual(match, None) def test_aaz_compound_type_split_key(self): - from azure.cli.core.aaz._arg_action import AAZCompoundTypeArgAction - split_key = AAZCompoundTypeArgAction._split_key - assert split_key("a.b.c") == ("a", "b", "c") - assert split_key("[1]") == (1,) - assert split_key("[-10]") == (-10,) - assert split_key("a1[5].b[2].c") == ("a1", 5, "b", 2, "c") + from azure.cli.core.aaz._utils import AAZShortHandSyntaxParser, AAZInvalidShorthandSyntaxError + parse_partial_value_key = AAZShortHandSyntaxParser.parse_partial_value_key + self.assertEqual(parse_partial_value_key("a.b.c"), ("a", "b", "c")) + self.assertEqual(parse_partial_value_key("[1]"), (1,)) + self.assertEqual(parse_partial_value_key("[-10]"), (-10,)) + self.assertEqual(parse_partial_value_key("a1[5].b[2].c"), ("a1", 5, "b", 2, "c")) + self.assertEqual(parse_partial_value_key("a.'/bc sef'/.sss[2]'[1]"), ("a", "/bc sef'.sss[2]", 1)) + self.assertEqual(parse_partial_value_key("'/bc sef'/.sss[2]'"), ("/bc sef'.sss[2]", )) + self.assertEqual(parse_partial_value_key("'/bc sef'/.sss[2]'.c"), ("/bc sef'.sss[2]", "c")) - with self.assertRaises(AssertionError): - split_key(".a") + with self.assertRaises(AAZInvalidShorthandSyntaxError): + parse_partial_value_key(".a") - with self.assertRaises(AssertionError): - split_key(".[5]") + with self.assertRaises(AAZInvalidShorthandSyntaxError): + parse_partial_value_key(".[5]") - with self.assertRaises(AssertionError): - split_key("b.a.[5]") + with self.assertRaises(AAZInvalidShorthandSyntaxError): + parse_partial_value_key("b.a.'/bc sef'/.sss[2]") def test_aaz_str_arg(self): from azure.cli.core.aaz._arg import AAZStrArg, AAZArgumentsSchema @@ -469,73 +494,73 @@ def test_aaz_str_arg(self): blank="Sunday" ) arg = schema.work_day.to_cmd_arg("work_day") - assert len(arg.choices) == 14 + self.assertEqual(len(arg.choices), 14) action = arg.type.settings["action"] dest_ops = AAZArgActionOperations() - assert len(dest_ops._ops) == 0 + self.assertEqual(len(dest_ops._ops), 0) action.setup_operations(dest_ops, "1") - assert len(dest_ops._ops) == 1 + self.assertEqual(len(dest_ops._ops), 1) dest_ops.apply(v, "work_day") - assert v.work_day == "Monday" + self.assertEqual(v.work_day, "Monday") action.setup_operations(dest_ops, "2") - assert len(dest_ops._ops) == 2 + self.assertEqual(len(dest_ops._ops), 2) dest_ops.apply(v, "work_day") - assert v.work_day == "Tuesday" + self.assertEqual(v.work_day, "Tuesday") action.setup_operations(dest_ops, "Thu") - assert len(dest_ops._ops) == 3 + self.assertEqual(len(dest_ops._ops), 3) dest_ops.apply(v, "work_day") - assert v.work_day == "Thursday" + self.assertEqual(v.work_day, "Thursday") action.setup_operations(dest_ops, "fri") - assert len(dest_ops._ops) == 4 + self.assertEqual(len(dest_ops._ops), 4) dest_ops.apply(v, "work_day") - assert v.work_day == "Friday" + self.assertEqual(v.work_day, "Friday") # null value action.setup_operations(dest_ops, 'null') - assert len(dest_ops._ops) == 5 + self.assertEqual(len(dest_ops._ops), 5) dest_ops.apply(v, "work_day") - assert v.work_day == None # must use '== None', because 'is None' will not work + self.assertEqual(v.work_day, None) # must use '== None', because 'is None' will not work # blank value action.setup_operations(dest_ops, None) - assert len(dest_ops._ops) == 6 + self.assertEqual(len(dest_ops._ops), 6) dest_ops.apply(v, "work_day") - assert v.work_day == "Sunday" + self.assertEqual(v.work_day, "Sunday") # null value action.setup_operations(dest_ops, 'null') - assert len(dest_ops._ops) == 7 + self.assertEqual(len(dest_ops._ops), 7) dest_ops.apply(v, "work_day") - assert v.work_day == None + self.assertEqual(v.work_day, None) # test invalid operations with self.assertRaises(azclierror.InvalidArgumentValueError): action.setup_operations(dest_ops, '1234') - assert len(dest_ops._ops) == 7 + self.assertEqual(len(dest_ops._ops), 7) dest_ops.apply(v, "work_day") - assert v.work_day == None + self.assertEqual(v.work_day, None) # New argument schema.name = AAZStrArg(options=["--name", "-n"]) arg = schema.name.to_cmd_arg("work_day") action = arg.type.settings["action"] dest_ops = AAZArgActionOperations() - assert len(dest_ops._ops) == 0 + self.assertEqual(len(dest_ops._ops), 0) action.setup_operations(dest_ops, "test name") - assert len(dest_ops._ops) == 1 + self.assertEqual(len(dest_ops._ops), 1) dest_ops.apply(v, "name") - assert v.name == "test name" + self.assertEqual(v.name, "test name") action.setup_operations(dest_ops, "") - assert len(dest_ops._ops) == 2 + self.assertEqual(len(dest_ops._ops), 2) dest_ops.apply(v, "name") - assert v.name == "" + self.assertEqual(v.name, "") with self.assertRaises(aazerror.AAZInvalidValueError): action.setup_operations(dest_ops, "null") @@ -544,14 +569,14 @@ def test_aaz_str_arg(self): with self.assertRaises(aazerror.AAZInvalidValueError): action.setup_operations(dest_ops, None) - assert len(dest_ops._ops) == 2 + self.assertEqual(len(dest_ops._ops), 2) dest_ops.apply(v, "name") - assert v.name == "" + self.assertEqual(v.name, "") action.setup_operations(dest_ops, " aa' l_;{]'") - assert len(dest_ops._ops) == 3 + self.assertEqual(len(dest_ops._ops), 3) dest_ops.apply(v, "name") - assert v.name == " aa' l_;{]'" + self.assertEqual(v.name, " aa' l_;{]'") def test_aaz_int_arg(self): from azure.cli.core.aaz._arg import AAZIntArg, AAZArgumentsSchema @@ -571,34 +596,34 @@ def test_aaz_int_arg(self): blank=0 ) arg = schema.score.to_cmd_arg("score") - assert len(arg.choices) == 4 + self.assertEqual(len(arg.choices), 4) action = arg.type.settings["action"] dest_ops = AAZArgActionOperations() - assert len(dest_ops._ops) == 0 + self.assertEqual(len(dest_ops._ops), 0) action.setup_operations(dest_ops, "A") - assert len(dest_ops._ops) == 1 + self.assertEqual(len(dest_ops._ops), 1) dest_ops.apply(v, "score") - assert v.score == 100 + self.assertEqual(v.score, 100) # null value action.setup_operations(dest_ops, "null") - assert len(dest_ops._ops) == 2 + self.assertEqual(len(dest_ops._ops), 2) dest_ops.apply(v, "score") - assert v.score == None + self.assertEqual(v.score, None) # blank value action.setup_operations(dest_ops, None) - assert len(dest_ops._ops) == 3 + self.assertEqual(len(dest_ops._ops), 3) dest_ops.apply(v, "score") - assert v.score == 0 + self.assertEqual(v.score, 0) # null value action.setup_operations(dest_ops, "null") - assert len(dest_ops._ops) == 4 + self.assertEqual(len(dest_ops._ops), 4) dest_ops.apply(v, "score") - assert v.score == None + self.assertEqual(v.score, None) # credit argument schema.credit = AAZIntArg(options=["--credit", "-c"]) @@ -606,27 +631,27 @@ def test_aaz_int_arg(self): action = arg.type.settings["action"] dest_ops = AAZArgActionOperations() - assert len(dest_ops._ops) == 0 + self.assertEqual(len(dest_ops._ops), 0) action.setup_operations(dest_ops, "-100") - assert len(dest_ops._ops) == 1 + self.assertEqual(len(dest_ops._ops), 1) dest_ops.apply(v, "credit") - assert v.credit == -100 + self.assertEqual(v.credit, -100) action.setup_operations(dest_ops, "0") - assert len(dest_ops._ops) == 2 + self.assertEqual(len(dest_ops._ops), 2) dest_ops.apply(v, "credit") - assert v.credit == 0 + self.assertEqual(v.credit, 0) action.setup_operations(dest_ops, "100") - assert len(dest_ops._ops) == 3 + self.assertEqual(len(dest_ops._ops), 3) dest_ops.apply(v, "credit") - assert v.credit == 100 + self.assertEqual(v.credit, 100) action.setup_operations(dest_ops, "'10'") - assert len(dest_ops._ops) == 4 + self.assertEqual(len(dest_ops._ops), 4) dest_ops.apply(v, "credit") - assert v.credit == 10 + self.assertEqual(v.credit, 10) # test blank with self.assertRaises(aazerror.AAZInvalidValueError): @@ -660,34 +685,34 @@ def test_aaz_float_arg(self): blank=0.0 ) arg = schema.score.to_cmd_arg("score") - assert len(arg.choices) == 4 + self.assertEqual(len(arg.choices), 4) action = arg.type.settings["action"] dest_ops = AAZArgActionOperations() - assert len(dest_ops._ops) == 0 + self.assertEqual(len(dest_ops._ops), 0) action.setup_operations(dest_ops, "A") - assert len(dest_ops._ops) == 1 + self.assertEqual(len(dest_ops._ops), 1) dest_ops.apply(v, "score") - assert v.score == 100.0 + self.assertEqual(v.score, 100.0) # null value action.setup_operations(dest_ops, "null") - assert len(dest_ops._ops) == 2 + self.assertEqual(len(dest_ops._ops), 2) dest_ops.apply(v, "score") - assert v.score == None + self.assertEqual(v.score, None) # blank value action.setup_operations(dest_ops, None) - assert len(dest_ops._ops) == 3 + self.assertEqual(len(dest_ops._ops), 3) dest_ops.apply(v, "score") - assert v.score == 0.0 + self.assertEqual(v.score, 0.0) # null value action.setup_operations(dest_ops, "null") - assert len(dest_ops._ops) == 4 + self.assertEqual(len(dest_ops._ops), 4) dest_ops.apply(v, "score") - assert v.score == None + self.assertEqual(v.score, None) # credit argument schema.credit = AAZFloatArg(options=["--credit", "-c"]) @@ -695,27 +720,27 @@ def test_aaz_float_arg(self): action = arg.type.settings["action"] dest_ops = AAZArgActionOperations() - assert len(dest_ops._ops) == 0 + self.assertEqual(len(dest_ops._ops), 0) action.setup_operations(dest_ops, "-100") - assert len(dest_ops._ops) == 1 + self.assertEqual(len(dest_ops._ops), 1) dest_ops.apply(v, "credit") - assert v.credit == -100.0 + self.assertEqual(v.credit, -100.0) action.setup_operations(dest_ops, "0.23") - assert len(dest_ops._ops) == 2 + self.assertEqual(len(dest_ops._ops), 2) dest_ops.apply(v, "credit") - assert v.credit == 0.23 + self.assertEqual(v.credit, 0.23) action.setup_operations(dest_ops, "100.1") - assert len(dest_ops._ops) == 3 + self.assertEqual(len(dest_ops._ops), 3) dest_ops.apply(v, "credit") - assert v.credit == 100.1 + self.assertEqual(v.credit, 100.1) action.setup_operations(dest_ops, "'10.123'") - assert len(dest_ops._ops) == 4 + self.assertEqual(len(dest_ops._ops), 4) dest_ops.apply(v, "credit") - assert v.credit == 10.123 + self.assertEqual(v.credit, 10.123) # test blank with self.assertRaises(aazerror.AAZInvalidValueError): @@ -736,66 +761,66 @@ def test_aaz_bool_arg(self): schema.enable = AAZBoolArg(options=["--enable", "-e"]) arg = schema.enable.to_cmd_arg("enable") - assert len(arg.choices) == 10 + self.assertEqual(len(arg.choices), 10) action = arg.type.settings["action"] dest_ops = AAZArgActionOperations() - assert len(dest_ops._ops) == 0 + self.assertEqual(len(dest_ops._ops), 0) action.setup_operations(dest_ops, None) - assert len(dest_ops._ops) == 1 + self.assertEqual(len(dest_ops._ops), 1) dest_ops.apply(v, "enable") - assert v.enable == True + self.assertEqual(v.enable, True) action.setup_operations(dest_ops, "false") - assert len(dest_ops._ops) == 2 + self.assertEqual(len(dest_ops._ops), 2) dest_ops.apply(v, "enable") - assert v.enable == False + self.assertEqual(v.enable, False) action.setup_operations(dest_ops, "true") - assert len(dest_ops._ops) == 3 + self.assertEqual(len(dest_ops._ops), 3) dest_ops.apply(v, "enable") - assert v.enable == True + self.assertEqual(v.enable, True) action.setup_operations(dest_ops, "f") - assert len(dest_ops._ops) == 4 + self.assertEqual(len(dest_ops._ops), 4) dest_ops.apply(v, "enable") - assert v.enable == False + self.assertEqual(v.enable, False) action.setup_operations(dest_ops, "t") - assert len(dest_ops._ops) == 5 + self.assertEqual(len(dest_ops._ops), 5) dest_ops.apply(v, "enable") - assert v.enable == True + self.assertEqual(v.enable, True) action.setup_operations(dest_ops, "no") - assert len(dest_ops._ops) == 6 + self.assertEqual(len(dest_ops._ops), 6) dest_ops.apply(v, "enable") - assert v.enable == False + self.assertEqual(v.enable, False) action.setup_operations(dest_ops, "yes") - assert len(dest_ops._ops) == 7 + self.assertEqual(len(dest_ops._ops), 7) dest_ops.apply(v, "enable") - assert v.enable == True + self.assertEqual(v.enable, True) action.setup_operations(dest_ops, "n") - assert len(dest_ops._ops) == 8 + self.assertEqual(len(dest_ops._ops), 8) dest_ops.apply(v, "enable") - assert v.enable == False + self.assertEqual(v.enable, False) action.setup_operations(dest_ops, "y") - assert len(dest_ops._ops) == 9 + self.assertEqual(len(dest_ops._ops), 9) dest_ops.apply(v, "enable") - assert v.enable == True + self.assertEqual(v.enable, True) action.setup_operations(dest_ops, "0") - assert len(dest_ops._ops) == 10 + self.assertEqual(len(dest_ops._ops), 10) dest_ops.apply(v, "enable") - assert v.enable == False + self.assertEqual(v.enable, False) action.setup_operations(dest_ops, "1") - assert len(dest_ops._ops) == 11 + self.assertEqual(len(dest_ops._ops), 11) dest_ops.apply(v, "enable") - assert v.enable == True + self.assertEqual(v.enable, True) # null value with self.assertRaises(aazerror.AAZInvalidValueError): @@ -811,37 +836,37 @@ def test_aaz_bool_arg(self): blank=False, ) arg = schema.started.to_cmd_arg("started") - assert len(arg.choices) == 10 + self.assertEqual(len(arg.choices), 10) action = arg.type.settings["action"] dest_ops = AAZArgActionOperations() - assert len(dest_ops._ops) == 0 + self.assertEqual(len(dest_ops._ops), 0) # null value action.setup_operations(dest_ops, "null") - assert len(dest_ops._ops) == 1 + self.assertEqual(len(dest_ops._ops), 1) dest_ops.apply(v, "started") - assert v.started == None + self.assertEqual(v.started, None) # blank value action.setup_operations(dest_ops, None) - assert len(dest_ops._ops) == 2 + self.assertEqual(len(dest_ops._ops), 2) dest_ops.apply(v, "started") - assert v.started == False + self.assertEqual(v.started, False) action.setup_operations(dest_ops, "TRUE") - assert len(dest_ops._ops) == 3 + self.assertEqual(len(dest_ops._ops), 3) dest_ops.apply(v, "started") - assert v.started == True + self.assertEqual(v.started, True) - # assert len(dest_ops._ops) == 12 + # self.assertEqual(len(dest_ops._ops), 12) # dest_ops.apply(v, "enable") - # assert v.enable == False + # self.assertEqual(v.enable, False) # # action.setup_operations(dest_ops, "true") - # assert len(dest_ops._ops) == 3 + # self.assertEqual(len(dest_ops._ops), 3) # dest_ops.apply(v, "enable") - # assert v.enable == True + # self.assertEqual(v.enable, True) def test_aaz_list_arg(self): from azure.cli.core.aaz._arg import AAZListArg, AAZStrArg, AAZArgumentsSchema @@ -862,80 +887,80 @@ def test_aaz_list_arg(self): action = arg.type.settings["action"] dest_ops = AAZArgActionOperations() - assert len(dest_ops._ops) == 0 + self.assertEqual(len(dest_ops._ops), 0) # null value action.setup_operations(dest_ops, ["null"]) - assert len(dest_ops._ops) == 1 + self.assertEqual(len(dest_ops._ops), 1) dest_ops.apply(v, "names") - assert v.names == [None, ] + self.assertEqual(v.names, [None, ]) action.setup_operations(dest_ops, ["[a,b,'c',' ']"]) - assert len(dest_ops._ops) == 2 + self.assertEqual(len(dest_ops._ops), 2) dest_ops.apply(v, "names") - assert v.names == ['a', 'b', 'c', ' '] + self.assertEqual(v.names, ['a', 'b', 'c', ' ']) action.setup_operations(dest_ops, ["[2]=efg", "[-1]='null'", "[0]="]) - assert len(dest_ops._ops) == 5 + self.assertEqual(len(dest_ops._ops), 5) dest_ops.apply(v, "names") - assert v.names == ['a blank value', 'b', 'efg', 'null'] + self.assertEqual(v.names, ['a blank value', 'b', 'efg', 'null']) action.setup_operations(dest_ops, ["c", "d"]) - assert len(dest_ops._ops) == 6 + self.assertEqual(len(dest_ops._ops), 6) dest_ops.apply(v, "names") - assert v.names == ['c', 'd'] + self.assertEqual(v.names, ['c', 'd']) action.setup_operations(dest_ops, ["[]"]) - assert len(dest_ops._ops) == 7 + self.assertEqual(len(dest_ops._ops), 7) dest_ops.apply(v, "names") - assert v.names == [] + self.assertEqual(v.names, []) action.setup_operations(dest_ops, ["a"]) - assert len(dest_ops._ops) == 8 + self.assertEqual(len(dest_ops._ops), 8) dest_ops.apply(v, "names") - assert v.names == ["a"] + self.assertEqual(v.names, ["a"]) action.setup_operations(dest_ops, ["", "''"]) - assert len(dest_ops._ops) == 9 + self.assertEqual(len(dest_ops._ops), 9) dest_ops.apply(v, "names") - assert v.names == ["", ""] + self.assertEqual(v.names, ["", ""]) action.setup_operations(dest_ops, ["a", 'null', 'None', "b", ""]) - assert len(dest_ops._ops) == 10 + self.assertEqual(len(dest_ops._ops), 10) dest_ops.apply(v, "names") - assert v.names == ["a", None, 'None', "b", ""] + self.assertEqual(v.names, ["a", None, 'None', "b", ""]) # blank value with self.assertRaises(aazerror.AAZInvalidValueError): action.setup_operations(dest_ops, None) action.setup_operations(dest_ops, ["[]"]) - assert len(dest_ops._ops) == 11 + self.assertEqual(len(dest_ops._ops), 11) dest_ops.apply(v, "names") - assert v.names == [] + self.assertEqual(v.names, []) # test singular action singular_action = schema.names.Element._build_cmd_action() singular_action.setup_operations(dest_ops, ["a"], prefix_keys=[_ELEMENT_APPEND_KEY]) - assert len(dest_ops._ops) == 12 + self.assertEqual(len(dest_ops._ops), 12) dest_ops.apply(v, "names") - assert v.names == ["a"] + self.assertEqual(v.names, ["a"]) singular_action.setup_operations(dest_ops, ["b"], prefix_keys=[_ELEMENT_APPEND_KEY]) - assert len(dest_ops._ops) == 13 + self.assertEqual(len(dest_ops._ops), 13) dest_ops.apply(v, "names") - assert v.names == ["a", "b"] + self.assertEqual(v.names, ["a", "b"]) singular_action.setup_operations(dest_ops, None, prefix_keys=[_ELEMENT_APPEND_KEY]) - assert len(dest_ops._ops) == 14 + self.assertEqual(len(dest_ops._ops), 14) dest_ops.apply(v, "names") - assert v.names == ["a", "b", "a blank value"] + self.assertEqual(v.names, ["a", "b", "a blank value"]) singular_action.setup_operations(dest_ops, [""], prefix_keys=[_ELEMENT_APPEND_KEY]) - assert len(dest_ops._ops) == 15 + self.assertEqual(len(dest_ops._ops), 15) dest_ops.apply(v, "names") - assert v.names == ["a", "b", "a blank value", ""] + self.assertEqual(v.names, ["a", "b", "a blank value", ""]) def test_aaz_dict_arg(self): from azure.cli.core.aaz._arg import AAZDictArg, AAZStrArg, AAZArgumentsSchema @@ -955,28 +980,28 @@ def test_aaz_dict_arg(self): action = arg.type.settings["action"] dest_ops = AAZArgActionOperations() - assert len(dest_ops._ops) == 0 + self.assertEqual(len(dest_ops._ops), 0) # null value action.setup_operations(dest_ops, ["a=null", "b=None"]) - assert len(dest_ops._ops) == 2 + self.assertEqual(len(dest_ops._ops), 2) dest_ops.apply(v, "tags") - assert v.tags == {"a": None, "b": 'None'} + self.assertEqual(v.tags, {"a": None, "b": 'None'}) action.setup_operations(dest_ops, ["b=6", "c="]) - assert len(dest_ops._ops) == 4 + self.assertEqual(len(dest_ops._ops), 4) dest_ops.apply(v, "tags") - assert v.tags == {"a": None, "b": "6", "c": "a blank value"} + self.assertEqual(v.tags, {"a": None, "b": "6", "c": "a blank value"}) action.setup_operations(dest_ops, ["{ab:1,bc:2,cd:'null'}"]) - assert len(dest_ops._ops) == 5 + self.assertEqual(len(dest_ops._ops), 5) dest_ops.apply(v, "tags") - assert v.tags == {"ab": '1', "bc": '2', 'cd': 'null'} + self.assertEqual(v.tags, {"ab": '1', "bc": '2', 'cd': 'null'}) action.setup_operations(dest_ops, ["{}"]) - assert len(dest_ops._ops) == 6 + self.assertEqual(len(dest_ops._ops), 6) dest_ops.apply(v, "tags") - assert v.tags == {} + self.assertEqual(v.tags, {}) with self.assertRaises(aazerror.AAZInvalidValueError): action.setup_operations(dest_ops, ["=1"]) @@ -1010,7 +1035,16 @@ def test_aaz_object_arg(self): options=["tags"], nullable=True ) - schema.properties.tags.Element = AAZIntArg() + schema.properties.identities = AAZDictArg( + options=["identities"], + ) + schema.properties.identities.Element = AAZObjectArg( + blank={}, + nullable=True, + ) + schema.properties.tags.Element = AAZIntArg( + blank="0" + ) schema.properties.vnets = AAZListArg( options=["vnets"], nullable=True @@ -1018,6 +1052,7 @@ def test_aaz_object_arg(self): schema.properties.vnets.Element = AAZObjectArg() schema.properties.vnets.Element.id = AAZStrArg( options=["id"], + blank="666" ) schema.properties.pt = AAZFloatArg( @@ -1030,12 +1065,12 @@ def test_aaz_object_arg(self): action = arg.type.settings["action"] dest_ops = AAZArgActionOperations() - assert len(dest_ops._ops) == 0 + self.assertEqual(len(dest_ops._ops), 0) action.setup_operations(dest_ops, ["{enable:false,tags:{a:1,3:2},vnets:[{id:/123}],pt:12.123}"]) - assert len(dest_ops._ops) == 1 + self.assertEqual(len(dest_ops._ops), 1) dest_ops.apply(v, "properties") - assert v.properties == { + self.assertEqual(v.properties, { "enable": False, "tags": { "a": 1, @@ -1045,12 +1080,12 @@ def test_aaz_object_arg(self): {"id": "/123"}, ], "pt": 12.123 - } + }) action.setup_operations(dest_ops, ["pt=", "enable=null", "vnets=[]"]) - assert len(dest_ops._ops) == 4 + self.assertEqual(len(dest_ops._ops), 4) dest_ops.apply(v, "properties") - assert v.properties == { + self.assertEqual(v.properties, { "enable": None, "tags": { "a": 1, @@ -1058,24 +1093,65 @@ def test_aaz_object_arg(self): }, "vnets": [], "pt": 0.1 - } + }) + + action.setup_operations(dest_ops, ["{enable:false,pt,tags:{a:1,3:2,c},vnets:[{id}],identities:{a:{},'http://b/c/d/e'}}"]) + self.assertEqual(len(dest_ops._ops), 5) + dest_ops.apply(v, "properties") + self.assertEqual(v.properties, { + "enable": False, + "tags": { + "a": 1, + "3": 2, + "c": 0, + }, + "vnets": [ + {"id": "666"}, + ], + "identities": { + "a": {}, + "http://b/c/d/e": {}, + }, + "pt": 0.1 + }) + + action.setup_operations(dest_ops, ["identities.'http://b.p['/]/c'=", "identities.a=null"]) + self.assertEqual(len(dest_ops._ops), 7) + dest_ops.apply(v, "properties") + self.assertEqual(v.properties, { + "enable": False, + "tags": { + "a": 1, + "3": 2, + "c": 0, + }, + "vnets": [ + {"id": "666"}, + ], + "identities": { + "a": None, + "http://b/c/d/e": {}, + "http://b.p[']/c": {}, + }, + "pt": 0.1 + }) action.setup_operations(dest_ops, ["{}"]) - assert len(dest_ops._ops) == 5 + self.assertEqual(len(dest_ops._ops), 8) dest_ops.apply(v, "properties") - assert v.properties == {} + self.assertEqual(v.properties, {}) action.setup_operations(dest_ops, ["null"]) - assert len(dest_ops._ops) == 6 + self.assertEqual(len(dest_ops._ops), 9) dest_ops.apply(v, "properties") - assert v.properties == None + self.assertEqual(v.properties, None) action.setup_operations(dest_ops, ["{enable:True,tags:null,vnets:null,pt:12.123}"]) - assert len(dest_ops._ops) == 7 + self.assertEqual(len(dest_ops._ops), 10) dest_ops.apply(v, "properties") - assert v.properties == { + self.assertEqual(v.properties, { "enable": True, "tags": None, "vnets": None, "pt": 12.123 - } + })