From 0da18a10ff62f7902f05189a459d039d6162766e Mon Sep 17 00:00:00 2001 From: Kai Ru Date: Wed, 20 Jul 2022 17:17:55 +0800 Subject: [PATCH 1/4] support AAZUuidArg and AAZUuidFormat --- .../azure/cli/core/aaz/__init__.py | 5 +- src/azure-cli-core/azure/cli/core/aaz/_arg.py | 31 +++++- .../azure/cli/core/aaz/_arg_fmt.py | 64 +++++++++++ .../azure/cli/core/tests/test_aaz_arg_fmt.py | 101 ++++++++++++++++++ 4 files changed, 198 insertions(+), 3 deletions(-) diff --git a/src/azure-cli-core/azure/cli/core/aaz/__init__.py b/src/azure-cli-core/azure/cli/core/aaz/__init__.py index 3b41646a2a0..8c3e3ee9896 100644 --- a/src/azure-cli-core/azure/cli/core/aaz/__init__.py +++ b/src/azure-cli-core/azure/cli/core/aaz/__init__.py @@ -11,9 +11,10 @@ from ._arg import has_value, AAZArgumentsSchema, AAZArgEnum, AAZStrArg, AAZIntArg, AAZObjectArg, AAZDictArg, \ AAZFloatArg, AAZBaseArg, AAZBoolArg, AAZListArg, AAZResourceGroupNameArg, AAZResourceLocationArg, \ - AAZResourceIdArg, AAZSubscriptionIdArg + AAZResourceIdArg, AAZSubscriptionIdArg, AAZUuidArg from ._arg_fmt import AAZStrArgFormat, AAZIntArgFormat, AAZFloatArgFormat, AAZBoolArgFormat, AAZObjectArgFormat, \ - AAZDictArgFormat, AAZListArgFormat, AAZResourceLocationArgFormat, AAZResourceIdArgFormat, AAZSubscriptionIdArgFormat + AAZDictArgFormat, AAZListArgFormat, AAZResourceLocationArgFormat, AAZResourceIdArgFormat, \ + AAZSubscriptionIdArgFormat, AAZUuidFormat from ._base import AAZValuePatch, AAZUndefined from ._command import AAZCommand, AAZWaitCommand, AAZCommandGroup, \ register_command, register_command_group, load_aaz_command_table diff --git a/src/azure-cli-core/azure/cli/core/aaz/_arg.py b/src/azure-cli-core/azure/cli/core/aaz/_arg.py index bda2983d08c..3ef731775c2 100644 --- a/src/azure-cli-core/azure/cli/core/aaz/_arg.py +++ b/src/azure-cli-core/azure/cli/core/aaz/_arg.py @@ -14,7 +14,7 @@ AAZSimpleType from ._field_value import AAZObject from ._arg_fmt import AAZObjectArgFormat, AAZListArgFormat, AAZDictArgFormat, AAZSubscriptionIdArgFormat, \ - AAZResourceLocationArgFormat, AAZResourceIdArgFormat + AAZResourceLocationArgFormat, AAZResourceIdArgFormat, AAZUuidFormat # pylint: disable=redefined-builtin, protected-access @@ -166,6 +166,35 @@ def _type_in_help(self): return "String" +class AAZDurationArg(AAZStrArg): + + @property + def _type_in_help(self): + return "Duration" + +class AAZDateArg(AAZStrArg): + + @property + def _type_in_help(self): + return "Date" + +class AAZDateTimeArg(AAZStrArg): + + @property + def _type_in_help(self): + return "DateTime" + +class AAZUuidArg(AAZStrArg): + + def __init__(self, fmt=None, **kwargs): + fmt = fmt or AAZUuidFormat() + super().__init__(fmt=fmt, **kwargs) + + @property + def _type_in_help(self): + return "GUID/UUID" + + class AAZIntArg(AAZSimpleTypeArg, AAZIntType): @property diff --git a/src/azure-cli-core/azure/cli/core/aaz/_arg_fmt.py b/src/azure-cli-core/azure/cli/core/aaz/_arg_fmt.py index 3f3c158186f..aeda4f33b80 100644 --- a/src/azure-cli-core/azure/cli/core/aaz/_arg_fmt.py +++ b/src/azure-cli-core/azure/cli/core/aaz/_arg_fmt.py @@ -9,6 +9,7 @@ import math from knack.log import get_logger +# from msrest.serialization import from ._command_ctx import AAZCommandCtx from ._field_type import AAZSimpleType @@ -56,6 +57,69 @@ def __call__(self, ctx, value): return value +class AAZUuidFormat(AAZBaseArgFormat): + + _uuid_pattern = re.compile(r'^[{(]?[0-9a-fA-F]{8}([-]?[0-9a-fA-F]{4}){3}[-]?[0-9a-fA-F]{12}[)}]?$') + + def __init__(self, case=None, braces_removed=True, hyphens_filled=True): + """ + :param case: 'upper' to format data into upper case, 'lower' to format data into lower case + """ + self.case = case + self.braces_removed = braces_removed + self.hyphens_filled = hyphens_filled + + def __call__(self, ctx, value): + assert isinstance(value, AAZSimpleValue) + data = value._data + if data == AAZUndefined or data is None or value._is_patch: + return value + + assert isinstance(data, str) + if not self._uuid_pattern.fullmatch(data): + raise AAZInvalidArgValueError( + f"Invalid format: '{data}' is not a valid GUID or UUID" + ) + + if '-' in data and data.count('-') != 4: + raise AAZInvalidArgValueError( + f"Invalid format: '{data}' is not a valid GUID or UUID" + ) + + if data.startswith('{') or data.endswith('}'): + # remove braces + if not (data.startswith('{') and data.endswith('}')): + raise AAZInvalidArgValueError( + f"Invalid format: '{data}' is not a valid GUID or UUID" + ) + if self.braces_removed: + data = data[1:-1] + + elif data.startswith('(') or data.endswith(')'): + # remove parentheses + if not (data.startswith('(') and data.endswith(')')): + raise AAZInvalidArgValueError( + f"Invalid format: '{data}' is not a valid GUID or UUID" + ) + if self.braces_removed: + data = data[1:-1] + + if '-' not in data and self.hyphens_filled: + # add '-' in data + if data[0] in ('{', '('): + data = f'{data[:9]}-{data[9:13]}-{data[13:17]}-{data[17:21]}-{data[21:]}' + else: + data = f'{data[:8]}-{data[8:12]}-{data[12:16]}-{data[16:20]}-{data[20:]}' + + if self.case == 'upper': + data = data.upper() + elif self.case == 'lower': + data = data.lower() + + value._data = data + return value + + class AAZIntArgFormat(AAZBaseArgFormat): def __init__(self, multiple_of=None, maximum=None, minimum=None): diff --git a/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg_fmt.py b/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg_fmt.py index d4c4024f1dd..d7869531cf0 100644 --- a/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg_fmt.py +++ b/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg_fmt.py @@ -57,6 +57,107 @@ def test_str_fmt(self): args = self.format_arg(schema, {"str1": None}) self.assertEqual(args.str1, None) + def test_uuid_fmt(self): + from azure.cli.core.aaz import AAZUuidArg, AAZUuidFormat + schema = AAZArgumentsSchema() + schema.uuid = AAZUuidArg( + nullable=True + ) + + with self.assertRaises(azclierror.InvalidArgumentValueError): + self.format_arg(schema, {"uuid": ""}) + + with self.assertRaises(azclierror.InvalidArgumentValueError): + self.format_arg(schema, {"uuid": "a8577a7a-4f31-40ab-bb00-3557df24a7e"}) + + with self.assertRaises(azclierror.InvalidArgumentValueError): + self.format_arg(schema, {"uuid": "a8577a7a-4f31-40ab-bb003557df24a7ea"}) + + with self.assertRaises(azclierror.InvalidArgumentValueError): + self.format_arg(schema, {"uuid": "a8577Z7Z-4f31-40ab-bb00-3557df24a7ea"}) + + with self.assertRaises(azclierror.InvalidArgumentValueError): + self.format_arg(schema, {"uuid": "{a8577a7a-4f31-40ab-bb00-3557df24a7ea"}) + + with self.assertRaises(azclierror.InvalidArgumentValueError): + self.format_arg(schema, {"uuid": "a8577a7a-4f31-40ab-bb00-3557df24a7ea}"}) + + with self.assertRaises(azclierror.InvalidArgumentValueError): + self.format_arg(schema, {"uuid": "(a8577a7a-4f31-40ab-bb00-3557df24a7ea"}) + + with self.assertRaises(azclierror.InvalidArgumentValueError): + self.format_arg(schema, {"uuid": "a8577a7a-4f31-40ab-bb00-3557df24a7ea)"}) + + args = self.format_arg(schema, {"uuid": "a8577a7a-4f31-40ab-bb00-3557df24a7ea"}) + self.assertEqual(args.uuid, "a8577a7a-4f31-40ab-bb00-3557df24a7ea") + + args = self.format_arg(schema, {"uuid": "FF0B2DF5-97EB-4E45-B028-8B0BEC803ABE"}) + self.assertEqual(args.uuid, "FF0B2DF5-97EB-4E45-B028-8B0BEC803ABE") + + args = self.format_arg(schema, {"uuid": "a8577A7a-4f31-40Ab-bb00-3557Df24a7Ea"}) + self.assertEqual(args.uuid, "a8577A7a-4f31-40Ab-bb00-3557Df24a7Ea") + + args = self.format_arg(schema, {"uuid": "{a8577a7a-4f31-40ab-bb00-3557df24a7ea}"}) + self.assertEqual(args.uuid, "a8577a7a-4f31-40ab-bb00-3557df24a7ea") + + args = self.format_arg(schema, {"uuid": "(a8577a7a-4f31-40ab-bb00-3557df24a7ea)"}) + self.assertEqual(args.uuid, "a8577a7a-4f31-40ab-bb00-3557df24a7ea") + + args = self.format_arg(schema, {"uuid": "a8577a7a4f3140abBb003557df24a7eA"}) + self.assertEqual(args.uuid, "a8577a7a-4f31-40ab-Bb00-3557df24a7eA") + + args = self.format_arg(schema, {"uuid": "{a8577a7a4f3140abbb003557df24a7ea}"}) + self.assertEqual(args.uuid, "a8577a7a-4f31-40ab-bb00-3557df24a7ea") + + args = self.format_arg(schema, {"uuid": "(a8577a7a4f3140abbb003557df24a7ea)"}) + self.assertEqual(args.uuid, "a8577a7a-4f31-40ab-bb00-3557df24a7ea") + + schema = AAZArgumentsSchema() + schema.uuid = AAZUuidArg( + fmt=AAZUuidFormat(case='upper'), + nullable=True + ) + + args = self.format_arg(schema, {"uuid": "(a8577a7A4f3140Abbb003557df24a7ea)"}) + self.assertEqual(args.uuid, "A8577A7A-4F31-40AB-BB00-3557DF24A7EA") + + schema = AAZArgumentsSchema() + schema.uuid = AAZUuidArg( + fmt=AAZUuidFormat(case='lower'), + nullable=True + ) + + args = self.format_arg(schema, {"uuid": "(A8577A7A4F3140Abbb003557DF24A7EA)"}) + self.assertEqual(args.uuid, "a8577a7a-4f31-40ab-bb00-3557df24a7ea") + + schema = AAZArgumentsSchema() + schema.uuid = AAZUuidArg( + fmt=AAZUuidFormat(hyphens_filled=False), + nullable=True + ) + + args = self.format_arg(schema, {"uuid": "(A8577A7A4F3140Abbb003557DF24A7EA)"}) + self.assertEqual(args.uuid, "A8577A7A4F3140Abbb003557DF24A7EA") + + schema = AAZArgumentsSchema() + schema.uuid = AAZUuidArg( + fmt=AAZUuidFormat(braces_removed=False), + nullable=True + ) + + args = self.format_arg(schema, {"uuid": "(a8577a7a4f3140abbb003557df24a7ea)"}) + self.assertEqual(args.uuid, "(a8577a7a-4f31-40ab-bb00-3557df24a7ea)") + + schema = AAZArgumentsSchema() + schema.uuid = AAZUuidArg( + fmt=AAZUuidFormat(braces_removed=False, hyphens_filled=False), + nullable=True + ) + + args = self.format_arg(schema, {"uuid": "(A8577A7A4F3140Abbb003557DF24A7EA)"}) + self.assertEqual(args.uuid, "(A8577A7A4F3140Abbb003557DF24A7EA)") + + def test_int_fmt(self): from azure.cli.core.aaz import AAZIntArg, AAZIntArgFormat From c5509ce221c38ab4cf226f1a01fa3879de21533d Mon Sep 17 00:00:00 2001 From: Kai Ru Date: Wed, 20 Jul 2022 22:44:17 +0800 Subject: [PATCH 2/4] support AAZDurationArg, AAZDateArg, AAZTimeArg and AAZDateTimeArg --- .../azure/cli/core/aaz/__init__.py | 4 +- src/azure-cli-core/azure/cli/core/aaz/_arg.py | 25 ++- .../azure/cli/core/aaz/_arg_fmt.py | 149 +++++++++++++++- .../azure/cli/core/tests/test_aaz_arg_fmt.py | 165 ++++++++++++++++++ 4 files changed, 339 insertions(+), 4 deletions(-) diff --git a/src/azure-cli-core/azure/cli/core/aaz/__init__.py b/src/azure-cli-core/azure/cli/core/aaz/__init__.py index 8c3e3ee9896..0f0181a185e 100644 --- a/src/azure-cli-core/azure/cli/core/aaz/__init__.py +++ b/src/azure-cli-core/azure/cli/core/aaz/__init__.py @@ -11,10 +11,10 @@ from ._arg import has_value, AAZArgumentsSchema, AAZArgEnum, AAZStrArg, AAZIntArg, AAZObjectArg, AAZDictArg, \ AAZFloatArg, AAZBaseArg, AAZBoolArg, AAZListArg, AAZResourceGroupNameArg, AAZResourceLocationArg, \ - AAZResourceIdArg, AAZSubscriptionIdArg, AAZUuidArg + AAZResourceIdArg, AAZSubscriptionIdArg, AAZUuidArg, AAZDateArg, AAZTimeArg, AAZDateTimeArg, AAZDurationArg from ._arg_fmt import AAZStrArgFormat, AAZIntArgFormat, AAZFloatArgFormat, AAZBoolArgFormat, AAZObjectArgFormat, \ AAZDictArgFormat, AAZListArgFormat, AAZResourceLocationArgFormat, AAZResourceIdArgFormat, \ - AAZSubscriptionIdArgFormat, AAZUuidFormat + AAZSubscriptionIdArgFormat, AAZUuidFormat, AAZDateFormat, AAZTimeFormat, AAZDateTimeFormat, AAZDurationFormat from ._base import AAZValuePatch, AAZUndefined from ._command import AAZCommand, AAZWaitCommand, AAZCommandGroup, \ register_command, register_command_group, load_aaz_command_table diff --git a/src/azure-cli-core/azure/cli/core/aaz/_arg.py b/src/azure-cli-core/azure/cli/core/aaz/_arg.py index 3ef731775c2..672b4785a35 100644 --- a/src/azure-cli-core/azure/cli/core/aaz/_arg.py +++ b/src/azure-cli-core/azure/cli/core/aaz/_arg.py @@ -14,7 +14,8 @@ AAZSimpleType from ._field_value import AAZObject from ._arg_fmt import AAZObjectArgFormat, AAZListArgFormat, AAZDictArgFormat, AAZSubscriptionIdArgFormat, \ - AAZResourceLocationArgFormat, AAZResourceIdArgFormat, AAZUuidFormat + AAZResourceLocationArgFormat, AAZResourceIdArgFormat, AAZUuidFormat, AAZDateFormat, AAZTimeFormat, \ + AAZDateTimeFormat, AAZDurationFormat # pylint: disable=redefined-builtin, protected-access @@ -168,18 +169,40 @@ def _type_in_help(self): class AAZDurationArg(AAZStrArg): + def __init__(self, fmt=None, **kwargs): + fmt = fmt or AAZDurationFormat() + super().__init__(fmt=fmt, **kwargs) + @property def _type_in_help(self): return "Duration" class AAZDateArg(AAZStrArg): + def __init__(self, fmt=None, **kwargs): + fmt = fmt or AAZDateFormat() + super().__init__(fmt=fmt, **kwargs) + @property def _type_in_help(self): return "Date" +class AAZTimeArg(AAZStrArg): + + def __init__(self, fmt=None, **kwargs): + fmt = fmt or AAZTimeFormat() + super().__init__(fmt=fmt, **kwargs) + + @property + def _type_in_help(self): + return "Time" + class AAZDateTimeArg(AAZStrArg): + def __init__(self, fmt=None, **kwargs): + fmt = fmt or AAZDateTimeFormat() + super().__init__(fmt=fmt, **kwargs) + @property def _type_in_help(self): return "DateTime" diff --git a/src/azure-cli-core/azure/cli/core/aaz/_arg_fmt.py b/src/azure-cli-core/azure/cli/core/aaz/_arg_fmt.py index aeda4f33b80..5a24a10c8bd 100644 --- a/src/azure-cli-core/azure/cli/core/aaz/_arg_fmt.py +++ b/src/azure-cli-core/azure/cli/core/aaz/_arg_fmt.py @@ -9,7 +9,6 @@ import math from knack.log import get_logger -# from msrest.serialization import from ._command_ctx import AAZCommandCtx from ._field_type import AAZSimpleType @@ -57,6 +56,154 @@ def __call__(self, ctx, value): return value +class AAZDurationFormat(AAZBaseArgFormat): + + def __call__(self, ctx, value): + assert isinstance(value, AAZSimpleValue) + data = value._data + if data == AAZUndefined or data is None or value._is_patch: + return value + + assert isinstance(data, str) + from msrest.serialization import Serializer + from isodate.isoerror import ISO8601Error + + try: + data = Serializer.serialize_duration(data.upper()) + except ISO8601Error as err: + try: + # parse '##DT##H##M##S + data = Serializer.serialize_duration(f'P0M{data.upper()}') + except ISO8601Error as err: + try: + # parse '##H##M##S' + data = Serializer.serialize_duration(f'PT{data.upper()}') + except ISO8601Error as err: + raise AAZInvalidArgValueError( + f"Invalid format: '{data}' should be of the form " + f"'##dT##h##m##s', '##h##m##s' or ISO8601 duration" + ) + value._data = data + return value + +class AAZDateFormat(AAZBaseArgFormat): + + def __init__(self): + self.help_string = 'Format: date (yyyy-mm-dd)' + + def __call__(self, ctx, value): + assert isinstance(value, AAZSimpleValue) + data = value._data + if data == AAZUndefined or data is None or value._is_patch: + return value + + assert isinstance(data, str) + import dateutil.parser + import dateutil.tz + + dt_val = None + try: + dt_val = dateutil.parser.parse(data) + except ValueError: + pass + + if not dt_val: + raise AAZInvalidArgValueError( + f"Unable to parse: '{data}'. Expected format: {self.help_string}" + ) + + if any([dt_val.hour, dt_val.minute, dt_val.second, dt_val.microsecond]): + logger.warning('Time info will be ignored in %s.', data) + + if dt_val.tzinfo: + logger.warning('Timezone info will be ignored in %s.', data) + + data = "{:04}-{:02}-{:02}".format(dt_val.year, dt_val.month, dt_val.day) + value._data = data + return value + +class AAZTimeFormat(AAZBaseArgFormat): + + def __init__(self): + self.help_string = 'Format: time (hh:mm:ss.xxxxxx)' + + def __call__(self, ctx, value): + assert isinstance(value, AAZSimpleValue) + data = value._data + if data == AAZUndefined or data is None or value._is_patch: + return value + + assert isinstance(data, str) + import dateutil.parser + import dateutil.tz + + dt_val = None + try: + dt_val = dateutil.parser.parse(data) + except ValueError: + pass + + if not dt_val: + raise AAZInvalidArgValueError( + f"Unable to parse: '{data}'. Expected format: {self.help_string}" + ) + + if any([dt_val.day, dt_val.month, dt_val.year]): + logger.warning('Date info will be ignored in %s.', data) + + if dt_val.tzinfo: + logger.warning('Timezone info will be ignored in %s.', data) + + data = "{:02}:{:02}:{:02}".format(dt_val.hour, dt_val.minute, dt_val.second) + if dt_val.microsecond: + microseconds = str(dt_val.microsecond).rjust(6, '0').rstrip('0').ljust(3, '0') + data += '.' + microseconds + + value._data = data + return value + +class AAZDateTimeFormat(AAZBaseArgFormat): + + def __init__(self, protocol="iso"): + self.protocol = protocol + self.help_string = 'Format: date (yyyy-mm-dd) time (hh:mm:ss.xxxxxx) timezone (+/-hh:mm)' + + def __call__(self, ctx, value): + assert isinstance(value, AAZSimpleValue) + data = value._data + if data == AAZUndefined or data is None or value._is_patch: + return value + + assert isinstance(data, str) + import dateutil.parser + import dateutil.tz + from msrest.serialization import Serializer + + dt_val = None + try: + dt_val = dateutil.parser.parse(data) + except ValueError: + pass + + if not dt_val: + raise AAZInvalidArgValueError( + f"Unable to parse: '{data}'. Expected format: {self.help_string}" + ) + + if not dt_val.tzinfo: + dt_val = dt_val.replace(tzinfo=dateutil.tz.tzlocal()) + + if self.protocol == "iso": + data = Serializer.serialize_iso(dt_val) + elif self.protocol == "rfc": + data = Serializer.serialize_rfc(dt_val) + else: + raise NotImplementedError() + + value._data = data + return value + + class AAZUuidFormat(AAZBaseArgFormat): _uuid_pattern = re.compile(r'^[{(]?[0-9a-fA-F]{8}([-]?[0-9a-fA-F]{4}){3}[-]?[0-9a-fA-F]{12}[)}]?$') diff --git a/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg_fmt.py b/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg_fmt.py index d7869531cf0..42cf7ffd7c4 100644 --- a/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg_fmt.py +++ b/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg_fmt.py @@ -57,6 +57,169 @@ def test_str_fmt(self): args = self.format_arg(schema, {"str1": None}) self.assertEqual(args.str1, None) + def test_duration_fmt(self): + from azure.cli.core.aaz import AAZDurationArg, AAZDurationFormat + schema = AAZArgumentsSchema() + schema.duration = AAZDurationArg( + nullable=True + ) + + args = self.format_arg(schema, {"duration": "1s"}) + self.assertEqual(args.duration, "PT1S") + + args = self.format_arg(schema, {"duration": "1m"}) + self.assertEqual(args.duration, "PT1M") + + args = self.format_arg(schema, {"duration": "1h"}) + self.assertEqual(args.duration, "PT1H") + + args = self.format_arg(schema, {"duration": "1d"}) + self.assertEqual(args.duration, "P1D") + + args = self.format_arg(schema, {"duration": "0h1s"}) + self.assertEqual(args.duration, "PT1S") + + args = self.format_arg(schema, {"duration": "1dt1h1m1s"}) + self.assertEqual(args.duration, "P1DT1H1M1S") + + args = self.format_arg(schema, {"duration": "pt1h"}) + self.assertEqual(args.duration, "PT1H") + + args = self.format_arg(schema, {"duration": "p1yt1h"}) + self.assertEqual(args.duration, "P1YT1H") + + args = self.format_arg(schema, {"duration": "p1y"}) + self.assertEqual(args.duration, "P1Y") + + args = self.format_arg(schema, {"duration": "p1m"}) + self.assertEqual(args.duration, "P1M") + + args = self.format_arg(schema, {"duration": "P1Y1M100DT1h1s"}) + self.assertEqual(args.duration, "P1Y1M100DT1H1S") + + args = self.format_arg(schema, {"duration": None}) + self.assertEqual(args.duration, None) + + with self.assertRaises(azclierror.InvalidArgumentValueError): + self.format_arg(schema, {"duration": "1m1d"}) + + with self.assertRaises(azclierror.InvalidArgumentValueError): + self.format_arg(schema, {"duration": "1x"}) + + with self.assertRaises(azclierror.InvalidArgumentValueError): + self.format_arg(schema, {"duration": "-1m"}) + + with self.assertRaises(azclierror.InvalidArgumentValueError): + self.format_arg(schema, {"duration": "m1s"}) + + def test_date_fmt(self): + from azure.cli.core.aaz import AAZDateArg, AAZDateFormat + schema = AAZArgumentsSchema() + schema.date = AAZDateArg( + nullable=True + ) + + args = self.format_arg(schema, {"date": "2010-01-02 01:11:11"}) + self.assertEqual(args.date, "2010-01-02") + + args = self.format_arg(schema, {"date": "2010/01/02"}) + self.assertEqual(args.date, "2010-01-02") + + args = self.format_arg(schema, {"date": "October 1 1989"}) + self.assertEqual(args.date, "1989-10-01") + + args = self.format_arg(schema, {"date": "2010/01/02 01:11:11"}) + self.assertEqual(args.date, "2010-01-02") + + args = self.format_arg(schema, {"date": None}) + self.assertEqual(args.date, None) + + with self.assertRaises(InvalidArgumentValue): + args = self.format_arg(schema, {"date": "aaaa"}) + + def test_time_fmt(self): + from azure.cli.core.aaz import AAZTimeArg, AAZTimeFormat + schema = AAZArgumentsSchema() + schema.time = AAZTimeArg( + nullable=True + ) + + args = self.format_arg(schema, {"time": "2010-01-02 01:11:11.12345678"}) + self.assertEqual(args.time, "01:11:11.123456") + + args = self.format_arg(schema, {"time": "01:11:11.0001"}) + self.assertEqual(args.time, "01:11:11.0001") + + args = self.format_arg(schema, {"time": "21:11:11.1"}) + self.assertEqual(args.time, "21:11:11.100") + + args = self.format_arg(schema, {"time": "21:01:51.00"}) + self.assertEqual(args.time, "21:01:51") + + args = self.format_arg(schema, {"time": None}) + self.assertEqual(args.time, None) + + with self.assertRaises(azclierror.InvalidArgumentValueError): + self.format_arg(schema, {"time": "21:01:151.00"}) + + def test_datetime_fmt(self): + from azure.cli.core.aaz import AAZDateTimeArg, AAZDateTimeFormat + schema = AAZArgumentsSchema() + schema.datetime = AAZDateTimeArg( + nullable=True + ) + schema.rfc = AAZDateTimeArg( + fmt=AAZDateTimeFormat(protocol='rfc'), + nullable=True + ) + + args = self.format_arg(schema, { + "datetime": "2010-01-02 01:11:11.12345678 -06:30", + "rfc": "2010-01-02 01:11:11.12345678 -06:30", + }) + self.assertEqual(args.datetime, "2010-01-02T07:41:11.123456Z") + self.assertEqual(args.rfc, "Sat, 02 Jan 2010 07:41:11 GMT") + + args = self.format_arg(schema, { + "datetime": "Sat, 02 Jan 2010 07:41:11 GMT", + "rfc": "2010-01-02T07:41:11.123456Z", + }) + self.assertEqual(args.datetime, "2010-01-02T07:41:11.000Z") + self.assertEqual(args.rfc, "Sat, 02 Jan 2010 07:41:11 GMT") + + args = self.format_arg(schema, { + "datetime": "2010/01/02 01:11:11.12345678 +06:30", + "rfc": "2010/01/02 01:11:11.12345678 +06:30", + }) + self.assertEqual(args.datetime, "2010-01-01T18:41:11.123456Z") + self.assertEqual(args.rfc, "Fri, 01 Jan 2010 18:41:11 GMT") + + args = self.format_arg(schema, { + "datetime": "2010-01-02T00:00:00Z", + "rfc": "2010-01-02T00:00:00Z", + }) + self.assertEqual(args.datetime, "2010-01-02T00:00:00.000Z") + self.assertEqual(args.rfc, "Sat, 02 Jan 2010 00:00:00 GMT") + + args = self.format_arg(schema, { + "datetime": "2010-01-02 10:00:00 +00:00", + "rfc": "2010-01-02 10:00:00 +00:00", + }) + self.assertEqual(args.datetime, "2010-01-02T10:00:00.000Z") + self.assertEqual(args.rfc, "Sat, 02 Jan 2010 10:00:00 GMT") + + args = self.format_arg(schema, { + "datetime": None, + "rfc": None, + }) + self.assertEqual(args.datetime, None) + self.assertEqual(args.rfc, None) + + with self.assertRaises(azclierror.InvalidArgumentValueError): + args = self.format_arg(schema, { + "datetime": "-2010-01-a2 10:00:00 +00:00", + }) + def test_uuid_fmt(self): from azure.cli.core.aaz import AAZUuidArg, AAZUuidFormat schema = AAZArgumentsSchema() @@ -157,6 +320,8 @@ def test_uuid_fmt(self): args = self.format_arg(schema, {"uuid": "(A8577A7A4F3140Abbb003557DF24A7EA)"}) self.assertEqual(args.uuid, "(A8577A7A4F3140Abbb003557DF24A7EA)") + args = self.format_arg(schema, {"uuid": None}) + self.assertEqual(args.uuid, None) def test_int_fmt(self): from azure.cli.core.aaz import AAZIntArg, AAZIntArgFormat From 19f5ba7be360c2aa5a300510e87f57c6e0fa9080 Mon Sep 17 00:00:00 2001 From: Kai Ru Date: Wed, 20 Jul 2022 23:02:09 +0800 Subject: [PATCH 3/4] fix test --- src/azure-cli-core/azure/cli/core/tests/test_aaz_arg_fmt.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg_fmt.py b/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg_fmt.py index 42cf7ffd7c4..697aedbd98c 100644 --- a/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg_fmt.py +++ b/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg_fmt.py @@ -119,7 +119,7 @@ def test_date_fmt(self): nullable=True ) - args = self.format_arg(schema, {"date": "2010-01-02 01:11:11"}) + args = self.format_arg(schema, {"date": "2010-01-02 01:11:11 +02:00"}) self.assertEqual(args.date, "2010-01-02") args = self.format_arg(schema, {"date": "2010/01/02"}) @@ -134,7 +134,7 @@ def test_date_fmt(self): args = self.format_arg(schema, {"date": None}) self.assertEqual(args.date, None) - with self.assertRaises(InvalidArgumentValue): + with self.assertRaises(azclierror.InvalidArgumentValueError): args = self.format_arg(schema, {"date": "aaaa"}) def test_time_fmt(self): @@ -144,7 +144,7 @@ def test_time_fmt(self): nullable=True ) - args = self.format_arg(schema, {"time": "2010-01-02 01:11:11.12345678"}) + args = self.format_arg(schema, {"time": "2010-01-02 01:11:11.12345678 +01:00"}) self.assertEqual(args.time, "01:11:11.123456") args = self.format_arg(schema, {"time": "01:11:11.0001"}) From 9abf1bc8cd71301c16592e672ec56afd5b6b99e7 Mon Sep 17 00:00:00 2001 From: Kai Ru Date: Wed, 20 Jul 2022 23:17:38 +0800 Subject: [PATCH 4/4] fix style issue --- src/azure-cli-core/azure/cli/core/aaz/_arg.py | 4 ++++ src/azure-cli-core/azure/cli/core/aaz/_arg_fmt.py | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/azure-cli-core/azure/cli/core/aaz/_arg.py b/src/azure-cli-core/azure/cli/core/aaz/_arg.py index 672b4785a35..fda163f25aa 100644 --- a/src/azure-cli-core/azure/cli/core/aaz/_arg.py +++ b/src/azure-cli-core/azure/cli/core/aaz/_arg.py @@ -177,6 +177,7 @@ def __init__(self, fmt=None, **kwargs): def _type_in_help(self): return "Duration" + class AAZDateArg(AAZStrArg): def __init__(self, fmt=None, **kwargs): @@ -187,6 +188,7 @@ def __init__(self, fmt=None, **kwargs): def _type_in_help(self): return "Date" + class AAZTimeArg(AAZStrArg): def __init__(self, fmt=None, **kwargs): @@ -197,6 +199,7 @@ def __init__(self, fmt=None, **kwargs): def _type_in_help(self): return "Time" + class AAZDateTimeArg(AAZStrArg): def __init__(self, fmt=None, **kwargs): @@ -207,6 +210,7 @@ def __init__(self, fmt=None, **kwargs): def _type_in_help(self): return "DateTime" + class AAZUuidArg(AAZStrArg): def __init__(self, fmt=None, **kwargs): diff --git a/src/azure-cli-core/azure/cli/core/aaz/_arg_fmt.py b/src/azure-cli-core/azure/cli/core/aaz/_arg_fmt.py index 5a24a10c8bd..b222bd9689f 100644 --- a/src/azure-cli-core/azure/cli/core/aaz/_arg_fmt.py +++ b/src/azure-cli-core/azure/cli/core/aaz/_arg_fmt.py @@ -70,15 +70,15 @@ def __call__(self, ctx, value): try: data = Serializer.serialize_duration(data.upper()) - except ISO8601Error as err: + except ISO8601Error: try: # parse '##DT##H##M##S data = Serializer.serialize_duration(f'P0M{data.upper()}') - except ISO8601Error as err: + except ISO8601Error: try: # parse '##H##M##S' data = Serializer.serialize_duration(f'PT{data.upper()}') - except ISO8601Error as err: + except ISO8601Error: raise AAZInvalidArgValueError( f"Invalid format: '{data}' should be of the form " f"'##dT##h##m##s', '##h##m##s' or ISO8601 duration" @@ -86,6 +86,7 @@ def __call__(self, ctx, value): value._data = data return value + class AAZDateFormat(AAZBaseArgFormat): def __init__(self): @@ -122,6 +123,7 @@ def __call__(self, ctx, value): value._data = data return value + class AAZTimeFormat(AAZBaseArgFormat): def __init__(self): @@ -162,6 +164,7 @@ def __call__(self, ctx, value): value._data = data return value + class AAZDateTimeFormat(AAZBaseArgFormat): def __init__(self, protocol="iso"):