summaryrefslogtreecommitdiffstats
path: root/cython/plist.pyx
diff options
context:
space:
mode:
Diffstat (limited to 'cython/plist.pyx')
-rw-r--r--cython/plist.pyx371
1 files changed, 314 insertions, 57 deletions
diff --git a/cython/plist.pyx b/cython/plist.pyx
index 3716a9c..73912d8 100644
--- a/cython/plist.pyx
+++ b/cython/plist.pyx
@@ -1,21 +1,11 @@
-cdef extern from *:
- ctypedef unsigned char uint8_t
- ctypedef short int int16_t
- ctypedef unsigned short int uint16_t
- ctypedef unsigned int uint32_t
- ctypedef int int32_t
-IF UNAME_MACHINE == 'x86_64':
- ctypedef unsigned long int uint64_t
-ELSE:
- ctypedef unsigned long long int uint64_t
-
cimport cpython
cimport libc.stdlib
+from libc.stdint cimport *
cdef extern from *:
ctypedef enum plist_type:
PLIST_BOOLEAN,
- PLIST_UINT,
+ PLIST_INT,
PLIST_REAL,
PLIST_STRING,
PLIST_ARRAY,
@@ -23,6 +13,8 @@ cdef extern from *:
PLIST_DATE,
PLIST_DATA,
PLIST_KEY,
+ PLIST_UID,
+ PLIST_NULL,
PLIST_NONE
plist_t plist_new_bool(uint8_t val)
@@ -33,13 +25,24 @@ cdef extern from *:
void plist_get_uint_val(plist_t node, uint64_t *val)
void plist_set_uint_val(plist_t node, uint64_t val)
+ plist_t plist_new_int(int64_t val)
+ void plist_get_int_val(plist_t node, int64_t *val)
+ void plist_set_int_val(plist_t node, int64_t val)
+
plist_t plist_new_real(double val)
void plist_get_real_val(plist_t node, double *val)
void plist_set_real_val(plist_t node, double val)
- plist_t plist_new_date(int32_t sec, int32_t usec)
- void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec)
- void plist_set_date_val(plist_t node, int32_t sec, int32_t usec)
+ plist_t plist_new_unix_date(int64_t sec)
+ void plist_get_unix_date_val(plist_t node, int64_t *sec)
+ void plist_set_unix_date_val(plist_t node, int64_t sec)
+
+ void plist_get_key_val(plist_t node, char **val)
+ void plist_set_key_val(plist_t node, char *val)
+
+ plist_t plist_new_uid(uint64_t val)
+ void plist_get_uid_val(plist_t node, uint64_t *val)
+ void plist_set_uid_val(plist_t node, uint64_t val)
plist_t plist_new_string(char *val)
void plist_get_string_val(plist_t node, char **val)
@@ -49,6 +52,8 @@ cdef extern from *:
void plist_get_data_val(plist_t node, char **val, uint64_t * length)
void plist_set_data_val(plist_t node, char *val, uint64_t length)
+ plist_t plist_new_null();
+
plist_t plist_new_dict()
int plist_dict_get_size(plist_t node)
plist_t plist_dict_get_item(plist_t node, char* key)
@@ -76,11 +81,11 @@ cdef extern from *:
plist_t plist_get_parent(plist_t node)
plist_type plist_get_node_type(plist_t node)
- void plist_set_type(plist_t node, plist_type type)
-
void plist_from_xml(char *plist_xml, uint32_t length, plist_t * plist)
void plist_from_bin(char *plist_bin, uint32_t length, plist_t * plist)
+ int plist_int_val_is_negative(plist_t node);
+
cdef class Node:
def __init__(self, *args, **kwargs):
self._c_managed = True
@@ -116,7 +121,7 @@ cdef class Node:
plist_to_bin(self._c_node, &out, &length)
try:
- return cpython.PyString_FromStringAndSize(out, length)
+ return bytes(out[:length])
finally:
if out != NULL:
libc.stdlib.free(out)
@@ -181,19 +186,22 @@ cdef Bool Bool_factory(plist_t c_node, bint managed=True):
cdef class Integer(Node):
def __cinit__(self, object value=None, *args, **kwargs):
if value is None:
- self._c_node = plist_new_uint(0)
+ self._c_node = plist_new_int(0)
else:
- self._c_node = plist_new_uint(int(value))
+ if value < 0 or value <= INT64_MAX:
+ self._c_node = plist_new_int(int(value))
+ else:
+ self._c_node = plist_new_uint(int(value))
def __repr__(self):
- cdef int i = self.get_value()
- return '<Integer: %s>' % i
+ return '<Integer: %s>' % self.get_value()
def __int__(self):
return self.get_value()
def __float__(self):
- return float(self.get_value())
+ v = self.get_value()
+ return float(v)
def __richcmp__(self, other, op):
cdef int i = self.get_value()
@@ -213,10 +221,18 @@ cdef class Integer(Node):
cpdef set_value(self, object value):
plist_set_uint_val(self._c_node, int(value))
- cpdef int get_value(self):
- cdef uint64_t value
- plist_get_uint_val(self._c_node, &value)
- return value
+ cpdef get_value(self):
+ cdef int64_t ivalue
+ cdef uint64_t uvalue
+ if self.is_negative():
+ plist_get_int_val(self._c_node, &ivalue)
+ return int(ivalue)
+ else:
+ plist_get_uint_val(self._c_node, &uvalue)
+ return int(uvalue)
+
+ cpdef bint is_negative(self):
+ return plist_int_val_is_negative(self._c_node);
cdef Integer Integer_factory(plist_t c_node, bint managed=True):
cdef Integer instance = Integer.__new__(Integer)
@@ -270,8 +286,139 @@ cdef Real Real_factory(plist_t c_node, bint managed=True):
instance._c_node = c_node
return instance
+cdef class Uid(Node):
+ def __cinit__(self, object value=None, *args, **kwargs):
+ if value is None:
+ self._c_node = plist_new_uid(0)
+ else:
+ self._c_node = plist_new_uid(int(value))
+
+ def __repr__(self):
+ cdef uint64_t i = self.get_value()
+ return '<Uid: %s>' % i
+
+ def __int__(self):
+ return self.get_value()
+
+ def __float__(self):
+ v = self.get_value()
+ return float(v)
+
+ def __richcmp__(self, other, op):
+ cdef int i = self.get_value()
+ if op == 0:
+ return i < other
+ if op == 1:
+ return i <= other
+ if op == 2:
+ return i == other
+ if op == 3:
+ return i != other
+ if op == 4:
+ return i > other
+ if op == 5:
+ return i >= other
+
+ cpdef set_value(self, object value):
+ plist_set_uid_val(self._c_node, int(value))
+
+ cpdef uint64_t get_value(self):
+ cdef uint64_t value
+ plist_get_uid_val(self._c_node, &value)
+ return value
+
+cdef Uid Uid_factory(plist_t c_node, bint managed=True):
+ cdef Uid instance = Uid.__new__(Uid)
+ instance._c_managed = managed
+ instance._c_node = c_node
+ return instance
+
+cdef class Null(Node):
+ def __cinit__(self, object value=None, *args, **kwargs):
+ self._c_node = plist_new_null()
+
+ def __repr__(self):
+ cdef uint64_t i = self.get_value()
+ return '<Null>'
+
+cdef Null Null_factory(plist_t c_node, bint managed=True):
+ cdef Null instance = Null.__new__(Null)
+ instance._c_managed = managed
+ instance._c_node = c_node
+ return instance
+
from cpython cimport PY_MAJOR_VERSION
+cdef class Key(Node):
+ def __cinit__(self, object value=None, *args, **kwargs):
+ cdef:
+ char* c_utf8_data = NULL
+ bytes utf8_data
+ if value is None:
+ raise ValueError("Requires a value")
+ else:
+ if isinstance(value, unicode):
+ utf8_data = value.encode('utf-8')
+ elif (PY_MAJOR_VERSION < 3) and isinstance(value, str):
+ value.encode('ascii') # trial decode
+ utf8_data = value.encode('ascii')
+ else:
+ raise ValueError("Requires unicode input, got %s" % type(value))
+ c_utf8_data = utf8_data
+ self._c_node = plist_new_string("")
+ plist_set_key_val(self._c_node, c_utf8_data)
+
+ def __repr__(self):
+ s = self.get_value()
+ return '<Key: %s>' % s.encode('utf-8')
+
+ def __richcmp__(self, other, op):
+ cdef unicode s = self.get_value()
+ if op == 0:
+ return s < other
+ if op == 1:
+ return s <= other
+ if op == 2:
+ return s == other
+ if op == 3:
+ return s != other
+ if op == 4:
+ return s > other
+ if op == 5:
+ return s >= other
+
+ cpdef set_value(self, object value):
+ cdef:
+ char* c_utf8_data = NULL
+ bytes utf8_data
+ if value is None:
+ plist_set_key_val(self._c_node, c_utf8_data)
+ else:
+ if isinstance(value, unicode):
+ utf8_data = value.encode('utf-8')
+ elif (PY_MAJOR_VERSION < 3) and isinstance(value, str):
+ value.encode('ascii') # trial decode
+ utf8_data = value.encode('ascii')
+ else:
+ raise ValueError("Requires unicode input, got %s" % type(value))
+ c_utf8_data = utf8_data
+ plist_set_key_val(self._c_node, c_utf8_data)
+
+ cpdef unicode get_value(self):
+ cdef:
+ char* c_value = NULL
+ plist_get_key_val(self._c_node, &c_value)
+ try:
+ return cpython.PyUnicode_DecodeUTF8(c_value, len(c_value), 'strict')
+ finally:
+ libc.stdlib.free(c_value)
+
+cdef Key Key_factory(plist_t c_node, bint managed=True):
+ cdef Key instance = Key.__new__(Key)
+ instance._c_managed = managed
+ instance._c_node = c_node
+ return instance
+
cdef class String(Node):
def __cinit__(self, object value=None, *args, **kwargs):
cdef:
@@ -342,19 +489,20 @@ cdef String String_factory(plist_t c_node, bint managed=True):
return instance
cdef extern from "plist_util.h":
- void datetime_to_ints(object obj, int32_t* sec, int32_t* usec)
- object ints_to_datetime(int32_t sec, int32_t usec)
+ int64_t datetime_to_timestamp(object obj)
+ object timestamp_to_datetime(int64_t sec)
int check_datetime(object obj)
cdef plist_t create_date_plist(value=None):
cdef plist_t node = NULL
- cdef int32_t secs
- cdef int32_t usecs
if value is None:
- node = plist_new_date(0, 0)
+ node = plist_new_unix_date(0)
+ elif isinstance(value, int):
+ node = plist_new_unix_date(value)
+ elif isinstance(value, float):
+ node = plist_new_unix_date(int(value))
elif check_datetime(value):
- datetime_to_ints(value, &secs, &usecs)
- node = plist_new_date(secs, usecs)
+ node = plist_new_unix_date(datetime_to_timestamp(value))
return node
cdef class Date(Node):
@@ -381,19 +529,21 @@ cdef class Date(Node):
return d >= other
cpdef object get_value(self):
- cdef int32_t secs = 0
- cdef int32_t usecs = 0
- cdef object result
- plist_get_date_val(self._c_node, &secs, &usecs)
- return ints_to_datetime(secs, usecs)
+ cdef int64_t secs = 0
+ plist_get_unix_date_val(self._c_node, &secs)
+ return timestamp_to_datetime(secs)
cpdef set_value(self, object value):
- cdef int32_t secs
- cdef int32_t usecs
- if not check_datetime(value):
- raise ValueError("Expected a datetime")
- datetime_to_ints(value, &secs, &usecs)
- plist_set_date_val(self._c_node, secs, usecs)
+ cdef int64_t secs = 0
+ if isinstance(value, int):
+ secs = value
+ elif isinstance(value, float):
+ secs = int(value)
+ elif check_datetime(value):
+ secs = datetime_to_timestamp(value)
+ else:
+ raise ValueError("Expected int or datetime")
+ plist_set_unix_date_val(self._c_node, secs)
cdef Date Date_factory(plist_t c_node, bint managed=True):
cdef Date instance = Date.__new__(Date)
@@ -434,7 +584,7 @@ cdef class Data(Node):
plist_get_data_val(self._c_node, &val, &length)
try:
- return cpython.PyString_FromStringAndSize(val, length)
+ return bytes(val[:length])
finally:
libc.stdlib.free(val)
@@ -456,7 +606,7 @@ cdef plist_t create_dict_plist(object value=None):
if value is not None and isinstance(value, dict):
for key, item in value.items():
c_node = native_to_plist_t(item)
- plist_dict_insert_item(node, key, c_node)
+ plist_dict_set_item(node, key, c_node)
c_node = NULL
return node
@@ -480,7 +630,12 @@ cdef class Dict(Node):
plist_dict_next_item(self._c_node, it, &key, &subnode);
while subnode is not NULL:
- cpython.PyDict_SetItem(self._map, key, plist_t_to_node(subnode, False))
+ py_key = key
+
+ if PY_MAJOR_VERSION >= 3:
+ py_key = py_key.decode('utf-8')
+
+ cpython.PyDict_SetItem(self._map, py_key, plist_t_to_node(subnode, False))
subnode = NULL
libc.stdlib.free(key)
key = NULL
@@ -512,9 +667,7 @@ cdef class Dict(Node):
return '<Dict: %s>' % self._map
cpdef dict get_value(self):
- cdef dict result = cpython.PyDict_New()
return dict([(key, value.get_value()) for key, value in self.items()])
- return result
cpdef set_value(self, dict value):
plist_free(self._c_node)
@@ -546,7 +699,6 @@ cdef class Dict(Node):
cpdef list values(self):
return cpython.PyDict_Values(self._map)
- return self._map.values()
cpdef object itervalues(self):
return self._map.itervalues()
@@ -561,12 +713,12 @@ cdef class Dict(Node):
else:
n = plist_t_to_node(native_to_plist_t(value), False)
- plist_dict_insert_item(self._c_node, key, n._c_node)
+ plist_dict_set_item(self._c_node, key, n._c_node)
self._map[key] = n
def __delitem__(self, key):
cpython.PyDict_DelItem(self._map, key)
- plist_dict_remove_item(self._c_node, key)
+ plist_dict_remove_item(self._c_node, key.encode('utf-8'))
cdef Dict Dict_factory(plist_t c_node, bint managed=True):
cdef Dict instance = Dict.__new__(Dict)
@@ -598,7 +750,7 @@ cdef class Array(Node):
cdef uint32_t size = plist_array_get_size(self._c_node)
cdef plist_t subnode = NULL
- for i from 0 <= i < size:
+ for i in range(size):
subnode = plist_array_get_item(self._c_node, i)
self._array.append(plist_t_to_node(subnode, False))
@@ -637,7 +789,10 @@ cdef class Array(Node):
return self._array.__iter__()
def __getitem__(self, index):
- return self._array[index]
+ value = self._array[index]
+ if isinstance(value, list):
+ return [item.copy() for item in value]
+ return value.copy()
def __setitem__(self, index, value):
cdef Node n
@@ -699,7 +854,7 @@ cdef plist_t native_to_plist_t(object native):
return plist_new_string(native)
if isinstance(native, bool):
return plist_new_bool(<bint>native)
- if isinstance(native, int) or isinstance(native, long):
+ if isinstance(native, int):
return plist_new_uint(native)
if isinstance(native, float):
return plist_new_real(native)
@@ -714,8 +869,10 @@ cdef object plist_t_to_node(plist_t c_plist, bint managed=True):
cdef plist_type t = plist_get_node_type(c_plist)
if t == PLIST_BOOLEAN:
return Bool_factory(c_plist, managed)
- if t == PLIST_UINT:
+ if t == PLIST_INT:
return Integer_factory(c_plist, managed)
+ if t == PLIST_KEY:
+ return Key_factory(c_plist, managed)
if t == PLIST_REAL:
return Real_factory(c_plist, managed)
if t == PLIST_STRING:
@@ -728,5 +885,105 @@ cdef object plist_t_to_node(plist_t c_plist, bint managed=True):
return Date_factory(c_plist, managed)
if t == PLIST_DATA:
return Data_factory(c_plist, managed)
+ if t == PLIST_UID:
+ return Uid_factory(c_plist, managed)
+ if t == PLIST_NULL:
+ return Null_factory(c_plist, managed)
if t == PLIST_NONE:
return None
+
+# This is to match up with the new plistlib API
+# http://docs.python.org/dev/library/plistlib.html
+# dump() and dumps() are not yet implemented
+FMT_XML = 1
+FMT_BINARY = 2
+
+cpdef object load(fp, fmt=None, use_builtin_types=True, dict_type=dict):
+ is_binary = fp.read(6) == 'bplist'
+ fp.seek(0)
+
+ cdef object cb = None
+
+ if not fmt:
+ if is_binary:
+ if 'b' not in fp.mode:
+ raise IOError('File handle must be opened in binary (b) mode to read binary property lists')
+ cb = from_bin
+ else:
+ cb = from_xml
+ else:
+ if fmt not in (FMT_XML, FMT_BINARY):
+ raise ValueError('Format must be constant FMT_XML or FMT_BINARY')
+ if fmt == FMT_BINARY:
+ cb = from_bin
+ elif fmt == FMT_XML:
+ cb = from_xml
+
+ if is_binary and fmt == FMT_XML:
+ raise ValueError('Cannot parse binary property list as XML')
+ elif not is_binary and fmt == FMT_BINARY:
+ raise ValueError('Cannot parse XML property list as binary')
+
+ return cb(fp.read())
+
+cpdef object loads(data, fmt=None, use_builtin_types=True, dict_type=dict):
+ is_binary = data[0:6] == 'bplist'
+
+ cdef object cb = None
+
+ if fmt is not None:
+ if fmt not in (FMT_XML, FMT_BINARY):
+ raise ValueError('Format must be constant FMT_XML or FMT_BINARY')
+ if fmt == FMT_BINARY:
+ cb = from_bin
+ else:
+ cb = from_xml
+ else:
+ if is_binary:
+ cb = from_bin
+ else:
+ cb = from_xml
+
+ if is_binary and fmt == FMT_XML:
+ raise ValueError('Cannot parse binary property list as XML')
+ elif not is_binary and fmt == FMT_BINARY:
+ raise ValueError('Cannot parse XML property list as binary')
+
+ return cb(data)
+
+cpdef object dump(value, fp, fmt=FMT_XML, sort_keys=True, skipkeys=False):
+ fp.write(dumps(value, fmt=fmt))
+
+cpdef object dumps(value, fmt=FMT_XML, sort_keys=True, skipkeys=False):
+ if fmt not in (FMT_XML, FMT_BINARY):
+ raise ValueError('Format must be constant FMT_XML or FMT_BINARY')
+
+ if check_datetime(value):
+ node = Date(value)
+ elif isinstance(value, unicode):
+ node = String(value)
+ elif PY_MAJOR_VERSION >= 3 and isinstance(value, bytes):
+ node = Data(value)
+ elif isinstance(value, str):
+ # See if this is binary
+ try:
+ node = String(value)
+ except ValueError:
+ node = Data(value)
+ elif isinstance(value, bool):
+ node = Bool(value)
+ elif isinstance(value, int):
+ node = Integer(value)
+ elif isinstance(value, float):
+ node = Real(value)
+ elif isinstance(value, dict):
+ node = Dict(value)
+ elif type(value) in (list, set, tuple):
+ node = Array(value)
+ else:
+ node = value
+
+ if fmt == FMT_XML:
+ return node.to_xml().encode('utf-8')
+
+ return node.to_bin()