-
Notifications
You must be signed in to change notification settings - Fork 3.1k
[WIP]Refactor AzureJSONEncoder #21028
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
fb50ae9
e68b8d8
efe1af8
76fc86e
065e4f2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,15 +32,15 @@ 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' | ||
|
||
Function adapted from the Tin Can Python project: https://github.com/RusticiSoftware/TinCanPython | ||
""" | ||
|
||
# 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) | ||
|
@@ -83,6 +83,25 @@ def _timedelta_as_isostr(value): | |
return "P" + date + time | ||
|
||
|
||
def _datetime_as_isostr(dt): | ||
"""Converts a datetime.(datetime|date|time|timedelta) object into an ISO 8601 formatted string""" | ||
Codejune marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# 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) | ||
Codejune marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if not dt.tzinfo: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mypy error: error: Item "date" of "Union[datetime, date, time, timedelta]" has no attribute "tzinfo" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering what the best way to resolve these would be... maybe if not cast(datetime, dt).tzinfo: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should check using typing.Newtype() for datetime, date, time, and timedelta. Something like from typing import TYPE_CHECKING, Union, cast
...
if TYPE_CHECKING:
from datetime import datetime, date, time, timedelta
datetime_type = typing.NewType('datetime_type', datetime)
date_type = typing.NewType('datetime_type', date)
time_type = typing.NewType('datetime_type', time)
timedelta_type = typing.NewType('datetime_type', timedelta)
...
def _datetime_as_isostr(dt):
# type: (Union[datetime, date, time, timedelta]) -> str
# First try datetime.datetime
if hasattr(dt, "year") and hasattr(dt, "hour"):
dt = cast(datetime_type, dt)
if not dt.tzinfo:
iso_formatted = dt.replace(tzinfo=TZ_UTC).isoformat()
else:
iso_formatted = dt.astimezone(TZ_UTC).isoformat()
return iso_formatted.replace("+00:00", "Z")
# Next try datetime.date or datetime.time
try:
dt = cast(Union[date_type, time_type], dt)
return dt.isoformat()
# Last, try datetime.timedelta
except AttributeError:
dt = cast(timedelta_type, dt)
return _timedelta_as_isostr(dt) This method passed mypy run-test: commands[0] | /Users/codejune/Develop/azure-sdk-for-python/sdk/core/azure-core/.tox/mypy/bin/python /Users/codejune/Develop/azure-sdk-for-python/sdk/core/azure-core/../../../eng/tox/run_mypy.py -t /Users/codejune/Develop/azure-sdk-for-python/sdk/core/azure-core
INFO:root:Package azure-core has opted to run mypy
Success: no issues found in 71 source files
_________________________________________________________________________________________________ summary _________________________________________________________________________________________________
mypy: commands succeeded
congratulations :) But it looks like hardcoded. So I'm thinking of a better way to do this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typing.NewType is introduced in python 3, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. It was added in Python 3. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately, we need to support 2.7 as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. By previous comments I have know that the sdk should support python 2.7. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about not using from typing import Union, cast
# if TYPE_CHECKING:
from datetime import datetime, date, time, timedelta
...
def _datetime_as_isostr(dt):
# type: (Union[datetime, date, time, timedelta]) -> str
# First try datetime.datetime
if hasattr(dt, "year") and hasattr(dt, "hour"):
dt = cast(datetime, dt)
...
# Next try datetime.date or datetime.time
try:
dt = cast(Union[date, time], dt)
...
# Last, try datetime.timedelta
except AttributeError:
dt = cast(timedelta, dt)
... It passed all my local There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is too much for a developer to test all the combinations, so we setup the pipeline. :) Feel free to leverage the pipeline to test py2.7. We don't encourage removing type checking because sometimes it does find real issues. Does this make sense? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great! :) |
||
iso_formatted = dt.replace(tzinfo=TZ_UTC).isoformat() | ||
Codejune marked this conversation as resolved.
Show resolved
Hide resolved
|
||
else: | ||
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 | ||
try: | ||
return dt.isoformat() | ||
Codejune marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# Last, try datetime.timedelta | ||
except AttributeError: | ||
return _timedelta_as_str(dt) | ||
|
||
|
||
try: | ||
from datetime import timezone | ||
|
||
|
@@ -95,29 +114,11 @@ 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)): | ||
Codejune marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return base64.b64encode(o).decode() | ||
try: | ||
Codejune marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 _datetime_as_isostr(o) | ||
except AttributeError: | ||
# This will be raised when it hits value.total_seconds in the method above | ||
Codejune marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pass | ||
return super(AzureJSONEncoder, self).default(o) |
Uh oh!
There was an error while loading. Please reload this page.