from decimal import Decimal
|
|
from uuid import UUID
|
|
import datetime
|
|
|
|
from dateutil.parser import parse as parse_date
|
|
from requests.compat import urlparse
|
|
|
|
|
|
TRUTHY_VALS = {'true', 'yes', '1'}
|
|
|
|
DT_RET = {'char', 'string', 'bin.base64', 'bin.hex'}
|
|
DT_INT = {'ui1', 'ui2', 'ui4', 'i1', 'i2', 'i4'}
|
|
DT_DECIMAL = {'r4', 'r8', 'number', 'float', 'fixed.14.4'}
|
|
DT_DATE = {'date'}
|
|
DT_DATETIME = {'dateTime', 'dateTime.tz'}
|
|
DT_TIME = {'time', 'time.tz'}
|
|
DT_BOOL = {'boolean'}
|
|
DT_URI = {'uri'}
|
|
DT_UUID = {'uuid'}
|
|
|
|
|
|
def parse_time(val):
|
|
"""
|
|
Parse a time to a `datetime.time` value.
|
|
Can't just use `dateutil.parse.parser(val).time()` because that doesn't preserve tzinfo.
|
|
"""
|
|
dt = parse_date(val)
|
|
if dt.tzinfo is None:
|
|
return dt.time()
|
|
return datetime.time(dt.hour, dt.minute, dt.second, dt.microsecond, dt.tzinfo)
|
|
|
|
|
|
MARSHAL_FUNCTIONS = (
|
|
(DT_RET, lambda x: x),
|
|
(DT_INT, int),
|
|
(DT_DECIMAL, Decimal),
|
|
(DT_DATE, lambda x: parse_date(x).date()),
|
|
(DT_DATETIME, parse_date),
|
|
(DT_TIME, parse_time),
|
|
(DT_BOOL, lambda x: x.lower() in TRUTHY_VALS),
|
|
(DT_URI, urlparse),
|
|
(DT_UUID, UUID)
|
|
)
|
|
|
|
|
|
def marshal_value(datatype, value):
|
|
"""
|
|
Marshal a given string into a relevant Python type given the uPnP datatype.
|
|
Assumes that the value has been pre-validated, so performs no checks.
|
|
Returns a tuple pair of a boolean to say whether the value was marshalled and the (un)marshalled
|
|
value.
|
|
"""
|
|
for types, func in MARSHAL_FUNCTIONS:
|
|
if datatype in types:
|
|
return True, func(value)
|
|
return False, value
|