From fb50ae9211eb4f8043fd37c5c7a031cfbbfc66fa Mon Sep 17 00:00:00 2001 From: codejune Date: Sun, 3 Oct 2021 15:31:48 +0900 Subject: [PATCH 1/5] Refactor AzureJSONEncoder --- .../azure-core/azure/core/serialization.py | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/sdk/core/azure-core/azure/core/serialization.py b/sdk/core/azure-core/azure/core/serialization.py index ef388f14cf26..e894c31f7493 100644 --- a/sdk/core/azure-core/azure/core/serialization.py +++ b/sdk/core/azure-core/azure/core/serialization.py @@ -82,7 +82,19 @@ def _timedelta_as_isostr(value): return "P" + date + time - +def _timestr_as_isostr(o): + # First try datetime.datetime + if hasattr(o, "year") and hasattr(o, "hour"): + # astimezone() fails for naive times in Python 2.7, so make make sure o is aware (tzinfo is set) + if not o.tzinfo: + iso_formatted = o.replace(tzinfo=TZ_UTC).isoformat() + else: + iso_formatted = o.astimezone(TZ_UTC).isoformat() + # Replace the trailing "+00:00" UTC offset with "Z" (RFC 3339: https://www.ietf.org/rfc/rfc3339.txt) + return iso_formatted.replace("+00:00", "Z") + # Next try datetime.date or datetime.time + return o.isoformat() + try: from datetime import timezone @@ -95,29 +107,16 @@ class AzureJSONEncoder(JSONEncoder): """A JSON encoder that's capable of serializing datetime objects and bytes.""" def default(self, o): # pylint: disable=too-many-return-statements + if isinstance(o, (bytes, bytearray)): + return base64.b64encode(o).decode() + try: + return _timestr_as_isostr(o) + except AttributeError: + pass + # Last, try datetime.timedelta try: - return super(AzureJSONEncoder, self).default(o) - except TypeError: - if isinstance(o, (bytes, bytearray)): - return base64.b64encode(o).decode() - try: - # First try datetime.datetime - if hasattr(o, "year") and hasattr(o, "hour"): - # astimezone() fails for naive times in Python 2.7, so make make sure o is aware (tzinfo is set) - if not o.tzinfo: - iso_formatted = o.replace(tzinfo=TZ_UTC).isoformat() - else: - iso_formatted = o.astimezone(TZ_UTC).isoformat() - # Replace the trailing "+00:00" UTC offset with "Z" (RFC 3339: https://www.ietf.org/rfc/rfc3339.txt) - return iso_formatted.replace("+00:00", "Z") - # Next try datetime.date or datetime.time - return o.isoformat() - except AttributeError: - pass - # Last, try datetime.timedelta - try: - return _timedelta_as_isostr(o) - except AttributeError: - # This will be raised when it hits value.total_seconds in the method above - pass - return super(AzureJSONEncoder, self).default(o) + return _timedelta_as_isostr(o) + except AttributeError: + # This will be raised when it hits value.total_seconds in the method above + pass + return super(AzureJSONEncoder, self).default(o) From e68b8d822c46be362ce8fc7cba6dc505321ec8a6 Mon Sep 17 00:00:00 2001 From: Codejune Date: Sat, 9 Oct 2021 19:31:21 +0900 Subject: [PATCH 2/5] Improved readability of AzureJSONEncoder datetime handling * Rename _timestr_as_isostr() to _datetime_as_isostr() * Move datetime.timedelta object handling function call (_timedelta_as_str()) to _datetime_as_isostr() inside * Rename private method (_datetime_as_isostr, _timedelta_as_str) parameter value, o to dt, td --- .../azure-core/azure/core/serialization.py | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/sdk/core/azure-core/azure/core/serialization.py b/sdk/core/azure-core/azure/core/serialization.py index e894c31f7493..24c9f25adcc8 100644 --- a/sdk/core/azure-core/azure/core/serialization.py +++ b/sdk/core/azure-core/azure/core/serialization.py @@ -32,7 +32,7 @@ def __bool__(self): """ -def _timedelta_as_isostr(value): +def _timedelta_as_str(td): # type: (timedelta) -> str """Converts a datetime.timedelta object into an ISO 8601 formatted string, e.g. 'P4DT12H30M05S' @@ -40,7 +40,7 @@ def _timedelta_as_isostr(value): """ # Split seconds to larger units - seconds = value.total_seconds() + seconds = td.total_seconds() minutes, seconds = divmod(seconds, 60) hours, minutes = divmod(minutes, 60) days, hours = divmod(hours, 24) @@ -82,19 +82,25 @@ def _timedelta_as_isostr(value): return "P" + date + time -def _timestr_as_isostr(o): + +def _datetime_as_isostr(dt): # First try datetime.datetime - if hasattr(o, "year") and hasattr(o, "hour"): + if hasattr(dt, "year") and hasattr(dt, "hour"): # astimezone() fails for naive times in Python 2.7, so make make sure o is aware (tzinfo is set) - if not o.tzinfo: - iso_formatted = o.replace(tzinfo=TZ_UTC).isoformat() + if not dt.tzinfo: + iso_formatted = dt.replace(tzinfo=TZ_UTC).isoformat() else: - iso_formatted = o.astimezone(TZ_UTC).isoformat() + iso_formatted = dt.astimezone(TZ_UTC).isoformat() # Replace the trailing "+00:00" UTC offset with "Z" (RFC 3339: https://www.ietf.org/rfc/rfc3339.txt) return iso_formatted.replace("+00:00", "Z") # Next try datetime.date or datetime.time - return o.isoformat() - + try: + return dt.isoformat() + # Last, try datetime.timedelta + except AttributeError: + return _timedelta_as_str(dt) + + try: from datetime import timezone @@ -110,13 +116,7 @@ def default(self, o): # pylint: disable=too-many-return-statements if isinstance(o, (bytes, bytearray)): return base64.b64encode(o).decode() try: - return _timestr_as_isostr(o) - except AttributeError: - pass - # Last, try datetime.timedelta - try: - return _timedelta_as_isostr(o) + return _datetime_as_isostr(o) except AttributeError: - # This will be raised when it hits value.total_seconds in the method above pass return super(AzureJSONEncoder, self).default(o) From efe1af8b4558ff7ed4606d2ec928c3ece987e7cf Mon Sep 17 00:00:00 2001 From: Codejune Date: Sat, 9 Oct 2021 23:57:23 +0900 Subject: [PATCH 3/5] Add some docstring --- sdk/core/azure-core/azure/core/serialization.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/core/azure-core/azure/core/serialization.py b/sdk/core/azure-core/azure/core/serialization.py index 24c9f25adcc8..ab3ffdfd0c99 100644 --- a/sdk/core/azure-core/azure/core/serialization.py +++ b/sdk/core/azure-core/azure/core/serialization.py @@ -84,6 +84,7 @@ def _timedelta_as_str(td): def _datetime_as_isostr(dt): + """Converts a datetime.(datetime|date|time|timedelta) object into an ISO 8601 formatted string""" # First try datetime.datetime if hasattr(dt, "year") and hasattr(dt, "hour"): # astimezone() fails for naive times in Python 2.7, so make make sure o is aware (tzinfo is set) @@ -118,5 +119,6 @@ def default(self, o): # pylint: disable=too-many-return-statements try: return _datetime_as_isostr(o) except AttributeError: + # This will be raised when it hits value.total_seconds in the method above pass return super(AzureJSONEncoder, self).default(o) From 76fc86ecd48d104eeebe03a84da23376e4a8bef9 Mon Sep 17 00:00:00 2001 From: codejune Date: Thu, 14 Oct 2021 14:10:42 +0900 Subject: [PATCH 4/5] Fix typo and add type checking * Fix typo some docstring * Renamed _timedelta_as_str to _timedelta_as_isostr * Add type checking in _datetime_as_isostr --- sdk/core/azure-core/azure/core/serialization.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sdk/core/azure-core/azure/core/serialization.py b/sdk/core/azure-core/azure/core/serialization.py index ab3ffdfd0c99..01e465342bb9 100644 --- a/sdk/core/azure-core/azure/core/serialization.py +++ b/sdk/core/azure-core/azure/core/serialization.py @@ -6,12 +6,12 @@ # -------------------------------------------------------------------------- import base64 from json import JSONEncoder -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Union from .utils._utils import _FixedOffset if TYPE_CHECKING: - from datetime import timedelta + from datetime import datetime, date, time, timedelta __all__ = ["NULL", "AzureJSONEncoder"] @@ -32,7 +32,7 @@ def __bool__(self): """ -def _timedelta_as_str(td): +def _timedelta_as_isostr(td): # type: (timedelta) -> str """Converts a datetime.timedelta object into an ISO 8601 formatted string, e.g. 'P4DT12H30M05S' @@ -84,10 +84,11 @@ def _timedelta_as_str(td): def _datetime_as_isostr(dt): + # type: (Union[datetime, date, time, timedelta]) -> str """Converts a datetime.(datetime|date|time|timedelta) object into an ISO 8601 formatted string""" # First try datetime.datetime if hasattr(dt, "year") and hasattr(dt, "hour"): - # astimezone() fails for naive times in Python 2.7, so make make sure o is aware (tzinfo is set) + # astimezone() fails for naive times in Python 2.7, so make make sure dt is aware (tzinfo is set) if not dt.tzinfo: iso_formatted = dt.replace(tzinfo=TZ_UTC).isoformat() else: @@ -99,7 +100,7 @@ def _datetime_as_isostr(dt): return dt.isoformat() # Last, try datetime.timedelta except AttributeError: - return _timedelta_as_str(dt) + return _timedelta_as_isostr(dt) try: @@ -119,6 +120,5 @@ def default(self, o): # pylint: disable=too-many-return-statements try: return _datetime_as_isostr(o) except AttributeError: - # This will be raised when it hits value.total_seconds in the method above pass return super(AzureJSONEncoder, self).default(o) From 065e4f29d8cf6c56a8bef7c0f5c6c2ec0b5ec321 Mon Sep 17 00:00:00 2001 From: codejune Date: Fri, 15 Oct 2021 04:08:06 +0900 Subject: [PATCH 5/5] Improve type checking and rename some inner variables --- .../azure-core/azure/core/serialization.py | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/sdk/core/azure-core/azure/core/serialization.py b/sdk/core/azure-core/azure/core/serialization.py index 01e465342bb9..4cb06d6437dc 100644 --- a/sdk/core/azure-core/azure/core/serialization.py +++ b/sdk/core/azure-core/azure/core/serialization.py @@ -6,12 +6,10 @@ # -------------------------------------------------------------------------- import base64 from json import JSONEncoder -from typing import TYPE_CHECKING, Union - +from typing import Union, cast +from datetime import datetime, date, time, timedelta from .utils._utils import _FixedOffset -if TYPE_CHECKING: - from datetime import datetime, date, time, timedelta __all__ = ["NULL", "AzureJSONEncoder"] @@ -49,22 +47,22 @@ def _timedelta_as_isostr(td): seconds = round(seconds, 6) # Build date - date = "" + date_str = "" if days: - date = "%sD" % days + date_str = "%sD" % days # Build time - time = "T" + time_str = "T" # Hours - bigger_exists = date or hours + bigger_exists = date_str or hours if bigger_exists: - time += "{:02}H".format(hours) + time_str += "{:02}H".format(hours) # Minutes bigger_exists = bigger_exists or minutes if bigger_exists: - time += "{:02}M".format(minutes) + time_str += "{:02}M".format(minutes) # Seconds try: @@ -78,9 +76,9 @@ def _timedelta_as_isostr(td): except AttributeError: # int.is_integer() raises seconds_string = "{:02}".format(seconds) - time += "{}S".format(seconds_string) + time_str += "{}S".format(seconds_string) - return "P" + date + time + return "P" + date_str + time_str def _datetime_as_isostr(dt): @@ -88,6 +86,7 @@ def _datetime_as_isostr(dt): """Converts a datetime.(datetime|date|time|timedelta) object into an ISO 8601 formatted string""" # First try datetime.datetime if hasattr(dt, "year") and hasattr(dt, "hour"): + dt = cast(datetime, dt) # astimezone() fails for naive times in Python 2.7, so make make sure dt is aware (tzinfo is set) if not dt.tzinfo: iso_formatted = dt.replace(tzinfo=TZ_UTC).isoformat() @@ -97,9 +96,11 @@ def _datetime_as_isostr(dt): return iso_formatted.replace("+00:00", "Z") # Next try datetime.date or datetime.time try: + dt = cast(Union[date, time], dt) return dt.isoformat() # Last, try datetime.timedelta except AttributeError: + dt = cast(timedelta, dt) return _timedelta_as_isostr(dt)