Skip to content

Commit

Permalink
Add JSONEncoder and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mccoyp committed Jul 7, 2021
1 parent 4134479 commit e4e71cd
Show file tree
Hide file tree
Showing 3 changed files with 449 additions and 5 deletions.
103 changes: 100 additions & 3 deletions sdk/core/azure-core/azure/core/serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,117 @@
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
import base64
import datetime
from json import JSONEncoder

__all__ = ["NULL"]


class _Null(object):
"""To create a Falsy object
"""
"""To create a Falsy object"""

def __bool__(self):
return False

__nonzero__ = __bool__ # Python2 compatibility
__nonzero__ = __bool__ # Python2 compatibility


NULL = _Null()
"""
A falsy sentinel object which is supposed to be used to specify attributes
with no data. This gets serialized to `null` on the wire.
"""


def iso_timedelta(value):
"""Represent a timedelta in ISO 8601 format.
Function from the Tin Can Python project: https://github.com/RusticiSoftware/TinCanPython
"""

# split seconds to larger units
seconds = value.total_seconds()
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)
days, hours = divmod(hours, 24)

days, hours, minutes = list(map(int, (days, hours, minutes)))
seconds = round(seconds, 6)

# build date
date = ''
if days:
date = '%sD' % days

# build time
time = 'T'

# hours
bigger_exists = date or hours
if bigger_exists:
time += '{:02}H'.format(hours)

# minutes
bigger_exists = bigger_exists or minutes
if bigger_exists:
time += '{:02}M'.format(minutes)

# seconds
if seconds.is_integer():
seconds = '{:02}'.format(int(seconds))
else:
# 9 chars long w/leading 0, 6 digits after decimal
seconds = '%09.6f' % seconds
# remove trailing zeros
seconds = seconds.rstrip('0')

time += '{}S'.format(seconds)

return 'P' + date + time


class UTC(datetime.tzinfo):
"""Time Zone info for handling UTC"""

def utcoffset(self, dt):
"""UTF offset for UTC is 0."""
return datetime.timedelta(0)

def tzname(self, dt):
"""Timestamp representation."""
return "Z"

def dst(self, dt):
"""No daylight saving for UTC."""
return datetime.timedelta(hours=1)


try:
from datetime import timezone

TZ_UTC = timezone.utc # type: ignore
except ImportError:
TZ_UTC = UTC() # type: ignore


class ComplexEncoder(JSONEncoder):
"""A JSON encoder that's capable of serializing datetime objects and bytes."""

def default(self, obj):
try:
return super(ComplexEncoder, self).default(obj)
except TypeError:
obj_type = type(obj)
if obj_type is datetime.date or obj_type is datetime.time:
return obj.isoformat()
elif obj_type is datetime.datetime:
if not obj.tzinfo: # astimezone() fails for naive times in Python 2.7
return obj.replace(tzinfo=TZ_UTC).isoformat()
return obj.astimezone(TZ_UTC).isoformat()
elif obj_type is datetime.timedelta:
return iso_timedelta(obj)
elif obj_type is bytes or obj_type is bytearray:
return base64.b64encode(obj).decode()
else:
return super(ComplexEncoder, self).default(obj)
1 change: 1 addition & 0 deletions sdk/core/azure-core/dev_requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
trio; python_version >= '3.5'
aiohttp>=3.0; python_version >= '3.5'
isodate>=0.6.0
typing_extensions>=3.7.2
opencensus>=0.6.0
opencensus-ext-azure>=0.3.1
Expand Down
Loading

0 comments on commit e4e71cd

Please sign in to comment.