def test_function_pointer(self): ffi = FFI(backend=self.Backend()) def cb(charp): assert repr(charp).startswith("<cdata 'char *' 0x") return 42 fptr = ffi.callback("int(*)(const char *txt)", cb) assert fptr != ffi.callback("int(*)(const char *)", cb) assert repr(fptr) == "<cdata 'int(*)(char *)' calling %r>" % (cb,) res = fptr(b"Hello") assert res == 42 # if not sys.platform.startswith('linux'): py.test.skip("probably no symbol 'stderr' in the lib") ffi.cdef(""" int fputs(const char *, void *); void *stderr; """) ffi.C = ffi.dlopen(None) fptr = ffi.cast("int(*)(const char *txt, void *)", ffi.C.fputs) assert fptr == ffi.C.fputs assert repr(fptr).startswith("<cdata 'int(*)(char *, void *)' 0x") with FdWriteCapture() as fd: fptr(b"world\n", ffi.C.stderr) res = fd.getvalue() assert res == b'world\n'
def test_function_pointer(self): ffi = FFI(backend=self.Backend()) def cb(charp): assert repr(charp).startswith("<cdata 'char *' 0x") return 42 fptr = ffi.callback("int(*)(const char *txt)", cb) assert fptr != ffi.callback("int(*)(const char *)", cb) assert repr(fptr) == "<cdata 'int(*)(char *)' calling %r>" % (cb, ) res = fptr(b"Hello") assert res == 42 # if not sys.platform.startswith('linux'): py.test.skip("probably no symbol 'stderr' in the lib") ffi.cdef(""" int fputs(const char *, void *); void *stderr; """) ffi.C = ffi.dlopen(None) fptr = ffi.cast("int(*)(const char *txt, void *)", ffi.C.fputs) assert fptr == ffi.C.fputs assert repr(fptr).startswith("<cdata 'int(*)(char *, void *)' 0x") with FdWriteCapture() as fd: fptr(b"world\n", ffi.C.stderr) res = fd.getvalue() assert res == b'world\n'
def test_callback_returning_struct_three_bytes(self): if self.Backend is CTypesBackend: py.test.skip("not supported with the ctypes backend") ffi = FFI(backend=self.Backend()) ffi.cdef(""" typedef struct { unsigned char a, b, c; } THREEBYTES; """) def cb(): return (12, 34, 56) fptr = ffi.callback("THREEBYTES(*)(void)", cb) tb = fptr() assert tb.a == 12 assert tb.b == 34 assert tb.c == 56
def test_callback_returning_void(self): ffi = FFI(backend=self.Backend()) for returnvalue in [None, 42]: def cb(): return returnvalue fptr = ffi.callback("void(*)(void)", cb) with StdErrCapture() as f: returned = fptr() printed = f.getvalue() assert returned is None if returnvalue is None: assert printed == '' else: assert "None" in printed
def test_callback_returning_void(self): ffi = FFI(backend=self.Backend()) for returnvalue in [None, 42]: def cb(): return returnvalue fptr = ffi.callback("void(*)(void)", cb) old_stderr = sys.stderr try: sys.stderr = StringIO() returned = fptr() printed = sys.stderr.getvalue() finally: sys.stderr = old_stderr assert returned is None if returnvalue is None: assert printed == '' else: assert "None" in printed
def test_callback_onerror(self): ffi = FFI(backend=self.Backend()) seen = [] def oops(*args): seen.append(args) def otherfunc(): raise LookupError def cb(n): otherfunc() a = ffi.callback("int(*)(int)", cb, error=42, onerror=oops) res = a(234) assert res == 42 assert len(seen) == 1 exc, val, tb = seen[0] assert exc is LookupError assert isinstance(val, LookupError) assert tb.tb_frame.f_code.co_name == 'cb' assert tb.tb_frame.f_locals['n'] == 234
LEVEL_MESSAGE: 20, LEVEL_WARNING: 30, LEVEL_ERROR: 40, LEVEL_CRITICAL: 50, } def _log_handler(domain, level, message, user_data): logger.log( GLogLevelFlags.LEVEL_TO_LOGGER[level], '{0}: {1}'.format(_to_string(ffi.string(domain)), _to_string(ffi.string(message)))) # keep a ref to the cb to stop it being GCd _log_handler_cb = ffi.callback('GLogFunc', _log_handler) _log_handler_id = glib_lib.g_log_set_handler( _to_bytes('VIPS'), GLogLevelFlags.LEVEL_DEBUG | GLogLevelFlags.LEVEL_INFO | GLogLevelFlags.LEVEL_MESSAGE | GLogLevelFlags.LEVEL_WARNING | GLogLevelFlags.LEVEL_CRITICAL | GLogLevelFlags.LEVEL_ERROR | GLogLevelFlags.FLAG_FATAL | GLogLevelFlags.FLAG_RECURSION, _log_handler_cb, ffi.NULL) # ffi doesn't like us looking up methods during shutdown: make a note of the # remove handler here _remove_handler = glib_lib.g_log_remove_handler # we must remove the handler on exit or libvips may try to run the callback # during shutdown def _remove_log_handler():
def test_callback(): ffi = FFI() cb = ffi.callback("int(int)", lambda x: x + 42) # unicode literal assert cb(5) == 47
r = call_no_check_errors(name, *args, **kwargs) finally: # always clear the error if the function raised something error, _current_error = _current_error, None if error: error, description = error if error in error_code_map: raise error_code_map[error](name, description) else: raise UnknownError(error, name, description) return r # TODO consider requiring manual initialisation or initialising somewhere else? def _on_error(error, description): global _current_error if not _current_error: _current_error = (error, description) call_no_check_errors("glfwSetErrorCallback", ffi.callback('GLFWerrorfun', _on_error)) if not call('glfwInit'): raise Exception() atexit.register(partial(call, 'glfwTerminate'))
# All watcher subclasses must be declared above. Now we do some # initialization; this is not only a minor optimization, it protects # against later runtime typos and attribute errors watcher._init_subclasses() def _syserr_cb(msg): try: msg = ffi.string(msg) __SYSERR_CALLBACK(msg, ffi.errno) except: set_syserr_cb(None) raise # let cffi print the traceback _syserr_cb._cb = ffi.callback("void(*)(char *msg)", _syserr_cb) def set_syserr_cb(callback): global __SYSERR_CALLBACK if callback is None: libev.ev_set_syserr_cb(ffi.NULL) __SYSERR_CALLBACK = None elif callable(callback): libev.ev_set_syserr_cb(_syserr_cb._cb) __SYSERR_CALLBACK = callback else: raise TypeError('Expected callable or None, got %r' % (callback, )) __SYSERR_CALLBACK = None
print("moo") from cffi import FFI ffi = FFI() ffi.cdef("""void (*pydo)(void); \n """) def pydo(): print("erm") thing = ffi.verify("""void (*pydo)(void);""") cb = ffi.callback("void()", pydo) thing.pydo = cb print("oooooo")
class LibGraphqlParser: def __init__(self): self._ffi = FFI() self._ffi.cdef(CDEFS_LIBGRAPHQL) # TODO do a conf of this ? try: self._lib = self._ffi.dlopen("/usr/local/lib/libgraphqlparser.so") except OSError: self._lib = self._ffi.dlopen("/usr/local/lib/libgraphqlparser.dylib") self._lib_callbacks = self._ffi.new( "struct GraphQLAstVisitorCallbacks *" ) self._errors = self._ffi.new("char **") self._callbacks = [] self._interested_by = {} self._default_visitor_cls = Visitor self._creates_callbacks() def _create_visitor_element(self, libgraphql_type, element): try: return _LIBGRAPHQL_TYPE_TO_CLASS[libgraphql_type]( self._lib, self._ffi, element) except KeyError: pass return _VisitorElement(self._lib, self._ffi, libgraphql_type, element) def _callback_enter(self, libgraphql_type, element, udata): context = self._ffi.from_handle(udata) context.update( Visitor.IN, self._create_visitor_element(libgraphql_type, element) ) return context.continue_child def _callback_exit(self, libgraphql_type, element, udata): context = self._ffi.from_handle(udata) if context.continue_child: context.update( Visitor.OUT, self._create_visitor_element(libgraphql_type, element) ) return None def _set_callback(self, proto, func, attr): c_func = self._ffi.callback(proto)(func) # Keep the callbacks alive in this list # to keep the underlying cdata alive. # because we do it with reflexion and # not with decoration self._callbacks.append(c_func) setattr(self._lib_callbacks, attr, c_func) def _set_exit_callback(self, typee): self._set_callback( "void(struct GraphQLAst%s *, void *)" % typee[0], partial(self._callback_exit, typee[0]), "end_visit_%s" % typee[1] ) def _set_enter_callback(self, typee): self._set_callback( "int(struct GraphQLAst%s *, void *)" % typee[0], partial(self._callback_enter, typee[0]), "visit_%s" % typee[1] ) def _creates_callbacks(self): for typee in TYPES_LIBGRAPHQL: if typee[0] not in ["Name"]: self._set_enter_callback(typee) self._set_exit_callback(typee) def _parse(self, query): if isinstance(query, str): # TODO don't replace here. query = query.replace("\n", " ").encode("UTF-8") c_query = self._ffi.new("char[]", query) parsed_data = _ParsedData( self._lib.graphql_parse_string(c_query, self._errors), self._lib.graphql_node_free ) if self._errors[0] != self._ffi.NULL: # TODO specialize Exception here e = Exception( self._ffi.string(self._errors[0]).decode('UTF-8', 'replace') ) self._lib.graphql_error_free(self._errors[0]) raise e return parsed_data def parse_and_visit(self, query, ctx=None): if not ctx: ctx = self._default_visitor_cls() with self._parse(query) as parsed: self._lib.graphql_node_visit( parsed, self._lib_callbacks, self._ffi.new_handle(ctx) ) def parse_and_jsonify(self, query): with self._parse(query) as parsed: return self._ffi.string(self._lib.graphql_ast_to_json(parsed))
def test_callback(): ffi = FFI() cb = ffi.callback("int(int)", # unicode literal lambda x: x + 42) assert cb(5) == 47
class DTrace: ## /usr/src/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h DTRACE_PROBESPEC_NONE = -1 DTRACE_PROBESPEC_PROVIDER = 0 DTRACE_PROBESPEC_MOD = 1 DTRACE_PROBESPEC_FUNC = 2 DTRACE_PROBESPEC_NAME = 3 def __init__(self): self._DTRACE_VERSION = 3 self._ffi = FFI() self._C = self._ffi.dlopen(None) # print "X"*10, self._C.stdout self._dtrace = get_dtrace(self._ffi) self._dtp = None self._fd = None def get_error(self): if self._dtp: err_no = self._dtrace.dtrace_errno(self._dtp) err_msg_c = self._dtrace.dtrace_errmsg(self._dtp, err_no) err_msg = self._ffi.string(err_msg_c).decode("utf8") return err_no, err_msg else: return 0, None def open(self): error = self._ffi.new("int*") self._dtp = self._dtrace.dtrace_open(self._DTRACE_VERSION, 0, error) if self._dtp == self._ffi.NULL: err_msg_c = self._dtrace.dtrace_errmsg(self._dtp, error) err_msg = self._ffi.string(err_msg_c).decode("utf8") raise Exception(err_msg) def set_option(self, key, value): if self._dtp: res = self._dtrace.dtrace_setopt(self._dtp, key.encode("utf8"), value.encode("utf8")) if res == -1: err_no, err_msg = self.get_error() raise Exception("could not set DTrace option '{}' to '{}' - {}".format(key, value, err_msg)) def compile(self, program): prog = self._dtrace.dtrace_program_strcompile( self._dtp, program.encode("utf8"), self._dtrace.DTRACE_PROBESPEC_NAME, 0, 0, self._ffi.NULL ) if prog == self._ffi.NULL: err_no, err_msg = self.get_error() raise Exception("could not compile D program - {}".format(err_msg)) return DTraceProgram(self, prog) def go(self, fd=None): if self._dtp: fd = fd or sys.stdout self._fd = self._ffi.cast("FILE*", fd) self._chew = self._ffi.callback( "int (const dtrace_probedata_t*, const dtrace_recdesc_t*, void*)", self.chew ) res = self._dtrace.dtrace_go(self._dtp) if res != 0: err_no, err_msg = self.get_error() raise Exception("could not start instrumentation: {}".format(e)) else: print ("instrumentation started") def work(self): if self._dtp: res = self._dtrace.dtrace_work(self._dtp, self._fd, self._ffi.NULL, self._chew, self._ffi.NULL) print ("work result: {}".format(res)) def sleep(self): if self._dtp: res = self._dtrace.dtrace_sleep(self._dtp) print ("woke up") def stop(self): if self._dtp: res = self._dtrace.dtrace_stop(self._dtp) if res == -1: err_no, err_msg = self.get_error() raise Exception("could not stop instrumentation: {}".format(err_msg)) else: print ("instrumentation stopped") def close(self): if self._dtp: self._dtrace.dtrace_close(self._dtp) self._dtp = None def chew(self, data, rec, arg): print ("chew!", data, rec, arg) # A NULL rec indicates that we've processed the last record. if rec == self._ffi.NULL: return self._dtrace.DTRACE_CONSUME_NEXT return self._dtrace.DTRACE_CONSUME_THIS
if __name__ == '__main__': ctx = ffi.new("struct sr_context **") def callback_imp(sdi, packet, private_data): global count count += 1 if packet.type == sigrok.SR_DF_LOGIC: data = ffi.cast("struct sr_datafeed_logic*", packet.payload) print(data.length, data.unitsize) buf = ffi.buffer(data.data, data.length) for read in map(bin, map(ord, buf[:])): print(read) callback = ffi.callback("sr_datafeed_callback_t", callback_imp) assert sigrok.sr_init(ctx) == sigrok.SR_OK try: dlist = sigrok.sr_driver_list() i = 0 while True: driver = dlist[i] i += 1 if driver == ffi.NULL: break assert sigrok.sr_driver_init(ctx[0], driver) == sigrok.SR_OK res = sigrok.sr_driver_scan(driver, ffi.NULL) if res != ffi.NULL: name = ffi.string(driver[0].name)
def test_types(tp_args, tp_result): global TEST_RUN_COUNTER print(tp_args, tp_result) cdefs = [] structs = {} def build_type(tp): if type(tp) is list: field_types = [build_type(tp1) for tp1 in tp] fields = ['%s f%d;' % (ftp, j) for (j, ftp) in enumerate(field_types)] fields = '\n '.join(fields) name = 's%d' % len(cdefs) cdefs.append("typedef struct {\n %s\n} %s;" % (fields, name)) structs[name] = field_types return name else: return tp args = [build_type(tp) for tp in tp_args] result = build_type(tp_result) TEST_RUN_COUNTER += 1 signature = "%s testfargs(%s)" % (result, ', '.join(['%s a%d' % (arg, i) for (i, arg) in enumerate(args)]) or 'void') source = list(cdefs) cdefs.append("%s;" % signature) cdefs.append("extern %s testfargs_result;" % result) for i, arg in enumerate(args): cdefs.append("extern %s testfargs_arg%d;" % (arg, i)) source.append("%s testfargs_result;" % result) for i, arg in enumerate(args): source.append("%s testfargs_arg%d;" % (arg, i)) source.append(signature) source.append("{") for i, arg in enumerate(args): source.append(" testfargs_arg%d = a%d;" % (i, i)) source.append(" return testfargs_result;") source.append("}") typedef_line = "typedef %s;" % (signature.replace('testfargs', '(*mycallback_t)'),) assert signature.endswith(')') sig_callback = "%s testfcallback(mycallback_t callback)" % result cdefs.append(typedef_line) cdefs.append("%s;" % sig_callback) source.append(typedef_line) source.append(sig_callback) source.append("{") source.append(" return callback(%s);" % ', '.join(["testfargs_arg%d" % i for i in range(len(args))])) source.append("}") ffi = FFI() ffi.cdef("\n".join(cdefs)) lib = verify(ffi, 'test_function_args_%d' % TEST_RUN_COUNTER, "\n".join(source), no_cpp=True) # when getting segfaults, enable this: if False: from extra_tests.cffi_tests.udir import udir import subprocess f = open(str(udir.join('run1.py')), 'w') f.write('import sys; sys.path = %r\n' % (sys.path,)) f.write('from _CFFI_test_function_args_%d import ffi, lib\n' % TEST_RUN_COUNTER) for i in range(len(args)): f.write('a%d = ffi.new("%s *")\n' % (i, args[i])) aliststr = ', '.join(['a%d[0]' % i for i in range(len(args))]) f.write('lib.testfargs(%s)\n' % aliststr) f.write('ffi.addressof(lib, "testfargs")(%s)\n' % aliststr) f.close() print("checking for segfault for direct call...") rc = subprocess.call([sys.executable, 'run1.py'], cwd=str(udir)) assert rc == 0, rc def make_arg(tp): if tp in structs: return [make_arg(tp1) for tp1 in structs[tp]] else: return draw_primitive(ffi, tp) passed_args = [make_arg(arg) for arg in args] returned_value = make_arg(result) def write(p, v): if type(v) is list: for i, v1 in enumerate(v): write(ffi.addressof(p, 'f%d' % i), v1) else: p[0] = v write(ffi.addressof(lib, 'testfargs_result'), returned_value) ## CALL forcing libffi print("CALL forcing libffi") received_return = ffi.addressof(lib, 'testfargs')(*passed_args) ## _tp_long_double = ffi.typeof("long double") def check(p, v): if type(v) is list: for i, v1 in enumerate(v): check(ffi.addressof(p, 'f%d' % i), v1) else: if ffi.typeof(p).item is _tp_long_double: assert ffi.cast("double", p[0]) == v else: assert p[0] == v for i, arg in enumerate(passed_args): check(ffi.addressof(lib, 'testfargs_arg%d' % i), arg) ret = ffi.new(result + "*", received_return) check(ret, returned_value) ## CALLBACK def expand(value): if isinstance(value, ffi.CData): t = ffi.typeof(value) if t is _tp_long_double: return float(ffi.cast("double", value)) return [expand(getattr(value, 'f%d' % i)) for i in range(len(t.fields))] else: return value # when getting segfaults, enable this: if False: from extra_tests.cffi_tests.udir import udir import subprocess f = open(str(udir.join('run1.py')), 'w') f.write('import sys; sys.path = %r\n' % (sys.path,)) f.write('from _CFFI_test_function_args_%d import ffi, lib\n' % TEST_RUN_COUNTER) f.write('def callback(*args): return ffi.new("%s *")[0]\n' % result) f.write('fptr = ffi.callback("%s(%s)", callback)\n' % (result, ','.join(args))) f.write('print(lib.testfcallback(fptr))\n') f.close() print("checking for segfault for callback...") rc = subprocess.call([sys.executable, 'run1.py'], cwd=str(udir)) assert rc == 0, rc seen_args = [] def callback(*args): seen_args.append([expand(arg) for arg in args]) return returned_value fptr = ffi.callback("%s(%s)" % (result, ','.join(args)), callback) print("CALL with callback") received_return = lib.testfcallback(fptr) assert len(seen_args) == 1 assert passed_args == seen_args[0] ret = ffi.new(result + "*", received_return) check(ret, returned_value)