diff options
Diffstat (limited to 'cython/plist.pyx')
| -rw-r--r-- | cython/plist.pyx | 371 |
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() |
