1from datetime import datetime, timezone 2 3from _sequoia import ffi, lib 4from . import error 5 6def invoke(fun, *args): 7 """Invokes the given FFI function. 8 9 This function invokes the given FFI function. It must only be 10 used for functions that expect an error pointer as first argument. 11 12 If an error is encountered, an exception is raised. 13 """ 14 err = ffi.new("pgp_error_t[1]") 15 result = fun(err, *args) 16 if err[0] != ffi.NULL: 17 raise Error._from(err[0]) 18 return result 19 20class SQObject(object): 21 # These class attributes determine what features the wrapper class 22 # implements. They must be set to the relevant Sequoia functions. 23 # 24 # XXX: Once we can assume Python3.6 we can use '__init_subclass__' 25 # and reflection on the 'lib' object to set them automatically 26 # using the type name. 27 _del = None 28 _clone = None 29 _eq = None 30 _str = None 31 _debug = None 32 _hash = None 33 34 def __init__(self, o, context=None, owner=None, references=None): 35 if o == ffi.NULL: 36 raise error.Error._last(context) 37 self.__o = None 38 self.ref_replace(o, owner=owner, references=references) 39 self.__ctx = context 40 if self.__class__._hash is None and not hasattr(self.__class__, '__hash__'): 41 # Unhashable types must have '__hash__' set to None. 42 # Until we can use '__init_subclass__', we need to patch 43 # the class here. Yuck. 44 self.__class__.__hash__ = None 45 46 def ref(self): 47 return self.__o 48 49 def ref_consume(self): 50 ref = self.ref() 51 self._delete(skip_free=True) 52 return ref 53 54 def ref_replace(self, new, owner=None, references=None): 55 old = self.ref_consume() 56 if self._del and owner == None: 57 # There is a destructor and We own the referenced object 58 # new. 59 self.__o = ffi.gc(new, self._del) 60 else: 61 self.__o = new 62 self.__owner = owner 63 self.__references = references 64 return old 65 66 def _delete(self, skip_free=False): 67 if not self.__o: 68 return 69 if self._del and skip_free: 70 ffi.gc(self.__o, None) 71 self.__o = None 72 self.__owner = None 73 self.__references = None 74 75 def context(self): 76 return self.__ctx 77 78 def __str__(self): 79 if self._str: 80 return _str(self._str(self.ref())) 81 else: 82 return repr(self) 83 84 def __eq__(self, other): 85 if self._eq: 86 return (isinstance(other, self.__class__) 87 and bool(self._eq(self.ref(), other.ref()))) 88 else: 89 return NotImplemented 90 91 def copy(self): 92 if self._clone: 93 return self.__class__(self._clone(self.ref())) 94 else: 95 raise NotImplementedError() 96 97 def __hash__(self): 98 return self._hash(self.ref()) 99 100 def debug(self): 101 if self._debug: 102 return _str(self._debug(self.ref())) 103 else: 104 raise NotImplementedError() 105 106def sq_str(s): 107 t = ffi.string(s).decode() 108 lib.free(s) 109 return t 110_str = sq_str 111 112def sq_static_str(s): 113 return ffi.string(s).decode() 114_static_str = sq_static_str 115 116def sq_iterator(iterator, next_fn, map=lambda x: x): 117 while True: 118 entry = next_fn(iterator) 119 if entry == ffi.NULL: 120 break 121 yield map(entry) 122 123def sq_time(t): 124 if t == 0: 125 return None 126 else: 127 return datetime.fromtimestamp(t, timezone.utc) 128