__all__ = ['to_plist']
from xml.sax.saxutils import escape
from base64 import b64encode
from datetime import date, datetime, timezone
_header = (
'\n'
'\n'
'\n'
)
_trailer = '\n'
INDENT = " "
def _bad(x, akku, indent=""):
raise ValueError("plist: bad type: {} ({})".format(type(x), x))
def _encode_array(x, akku, indent=""):
indent2 = indent + INDENT
akku.append(indent + "\n")
for v in x:
_encoder.get(type(v), _bad)(v, akku, indent2)
akku.append(indent + "\n")
def _encode_dict(x, akku, indent=""):
indent2 = indent + INDENT
akku.append(indent + "\n")
for k,v in x.items():
# key must be string?
if type(k) != str:
raise ValueError("plist: dictionary key must be string: {}: {}".format(type(k), k))
akku.append("{}{}\n".format(indent2, escape(k)))
_encoder.get(type(v), _bad)(v, akku, indent2)
akku.append(indent + "\n")
def _encode_bool(x, akku, indent=""):
if x: akku.append(indent + "\n")
else: akku.append(indent + "\n")
def _encode_integer(x, akku, indent=""):
akku.append("{}{}\n".format(indent, x))
def _encode_real(x, akku, indent=""):
akku.append("{}{}\n".format(indent, x))
def _encode_string(x, akku, indent=""):
akku.append("{}{}\n".format(indent, escape(x)))
# data is YYYY-MM-DD T HH:MM:SS Z
def _encode_date(x, akku, indent=""):
s = x.strftime('%Y-%m-%d')
akku.append("{}{}\n".format(indent, s))
def _encode_datetime(x, akku, indent=""):
# if not x.tzinfo
# raise ValueError("plist: datetime must have tzinfo: {}".format(x))
# if x.tzinfo.utcoffset(x) == None:
# raise ValueError("plist: datetime must have utc offset: {}".format(x))
utc = x.astimezone(timezone.utc)
s = utc.strftime('%Y-%m-%dT%H:%M:%SZ')
akku.append("{}{}\n".format(indent, s))
def _encode_data(x, akku, indent=""):
# data is base64 encoded
CHUNKSIZE = 32
if len(x) < CHUNKSIZE:
akku.append("{}{}\n".format(indent, b64encode(x).encode('ascii')))
return
indent2 = indent + INDENT
akku.append(indent + "\n")
for i in range(0, len(x), CHUNKSIZE):
akku.append(
"{}{}\n".format(
indent2,
b64encode(x[i:i+CHUNKSIZE]).encode('ascii')
)
)
akku.append(indent + "\n")
# data, data not yet supported.
_encoder = {
str: _encode_string,
float: _encode_real,
int: _encode_integer,
bool: _encode_bool,
tuple: _encode_array,
list: _encode_array,
dict: _encode_dict,
bytes: _encode_data,
bytearray: _encode_data,
date: _encode_date,
datetime: _encode_datetime,
}
def to_plist(x):
akku = []
akku.append(_header)
_encoder.get(type(x), _bad)(x, akku, INDENT)
akku.append(_trailer)
return ''.join(akku)