1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
from datetime import datetime, timezone
from _sequoia import ffi, lib
from . import error
class SQObject(object):
# These class attributes determine what features the wrapper class
# implements. They must be set to the relevant Sequoia functions.
#
# XXX: Once we can assume Python3.6 we can use '__init_subclass__'
# and reflection on the 'lib' object to set them automatically
# using the type name.
_del = None
_clone = None
_eq = None
_str = None
_hash = None
def __init__(self, o, context=None, owner=None, references=None):
if o == ffi.NULL:
raise error.Error._last(context)
self.__o = None
self.ref_replace(o, owner=owner, references=references)
self.__ctx = context
if self.__class__._hash is None and not hasattr(self.__class__, '__hash__'):
# Unhashable types must have '__hash__' set to None.
# Until we can use '__init_subclass__', we need to patch
# the class here. Yuck.
self.__class__.__hash__ = None
def ref(self):
return self.__o
def ref_consume(self):
ref = self.ref()
self._delete(skip_free=True)
return ref
def ref_replace(self, new, owner=None, references=None):
old = self.ref_consume()
if self._del and owner == None:
# There is a destructor and We own the referenced object
# new.
self.__o = ffi.gc(new, self._del)
else:
self.__o = new
self.__owner = owner
self.__references = references
return old
def _delete(self, skip_free=False):
if not self.__o:
return
if self._del and skip_free:
ffi.gc(self.__o, None)
self.__o = None
self.__owner = None
self.__references = None
def context(self):
return self.__ctx
def __str__(self):
if self._str:
return _str(self._str(self.ref()))
else:
return repr(self)
def __eq__(self, other):
if self._eq:
return (isinstance(other, self.__class__)
and bool(self._eq(self.ref(), other.ref())))
else:
return NotImplemented
def copy(self):
if self._clone:
return self.__class__(self._clone(self.ref()))
else:
raise NotImplementedError()
def __hash__(self):
return self._hash(self.ref())
def sq_str(s):
t = ffi.string(s).decode()
lib.sq_string_free(s)
return t
_str = sq_str
def sq_iterator(iterator, next_fn, map=lambda x: x):
while True:
entry = next_fn(iterator)
if entry == ffi.NULL:
break
yield map(entry)
def sq_time(t):
if t == 0:
return None
else:
return datetime.fromtimestamp(t, timezone.utc)
|