def _generate_signal_callback(backend, info, args, arg_types): sig_args = [] ret_type = info.get_return_type() cls = get_cbreturn_class(ret_type) return_value = cls(backend, info, ret_type) for arg, type_ in zip(args, arg_types): cls = get_cbarg_class(type_) excaped_name = escape_identifier(arg.name) sig_arg = cls(backend, arg, type_, excaped_name) sig_args.append(sig_arg) for arg in sig_args: arg.setup() body = CodeBlock() outs_vars = [] for arg in sig_args: if arg.is_aux: continue block, out = arg.process() if block: block.write_into(body) outs_vars.append(out) argument_list = ", ".join([a.name for a in sig_args]) forward_arguments = ", ".join(outs_vars) func_name = escape_parameter(info.name) cb_name = backend.var() return_var = backend.var() return_block, out_var = return_value.process(return_var) return_block = return_block or CodeBlock() block, var = backend.parse(""" def $cb_wrapper($dummy, $args): $body $ret = $callback($out_args) $post return $out """, args=argument_list, out_args=forward_arguments, cb_wrapper=func_name, callback=cb_name, body=body, post=return_block, out=out_var, ret=return_var) def create_sig_for_func(real_func): f = block.compile(**{cb_name: real_func})[func_name] return backend.get_callback(f, sig_args, return_value, is_signal=True) return create_sig_for_func
def test_escape_identifier(self): self.assertEqual(escape_identifier("class"), "class_") self.assertEqual(escape_identifier("2BUTTON_PRESS"), "_2BUTTON_PRESS") # most things from the gir are partial identifiers, so an empty # string can occur self.assertEqual(escape_identifier(""), "_")
def generate_dummy_callable(info, func_name, method=False): """Takes a GICallableInfo and generates a dummy callback function which just raises but has a correct docstring. They are mainly accessible for documentation, so the API reference can reference a real thing. func_name can be different than info.name because vfuncs, for example, get prefixed with 'do_' when exposed in Python. """ assert isinstance(info, GICallableInfo) # FIXME: handle out args and trailing user_data ? arg_infos = list(info.get_args()) arg_types = [a.get_type() for a in arg_infos] return_type = info.get_return_type() # the null backend is good enough here backend = get_backend("null")() args = [] for arg_info, arg_type in zip(arg_infos, arg_types): cls = get_argument_class(arg_type) name = escape_identifier(arg_info.name) name = escape_parameter(name) args.append(cls(name, args, backend, arg_info, arg_type)) cls = get_return_class(return_type) return_value = cls(info, return_type, args, backend) for arg in args: arg.setup() return_value.setup() func_name = escape_identifier(func_name) docstring = build_docstring(func_name, args, return_value, False) in_args = [a for a in args if not a.is_aux and a.in_var] in_names = [a.in_var for a in in_args] var_fac = backend.var var_fac.add_blacklist(in_names) self_name = "" if method: self_name = var_fac.request_name("self") in_names.insert(0, self_name) main, var = backend.parse(""" def $func_name($func_args): '''$docstring''' raise NotImplementedError("This is just a dummy callback function") """, func_args=", ".join(in_names), docstring=docstring, func_name=func_name) func = main.compile()[func_name] func._code = main func.__doc__ = docstring func.__module__ = info.namespace return func
def generate_callback_wrapper(info): backend = get_backend("ctypes")() args = list(info.get_args()) arg_types = [a.get_type() for a in args] ret_type = info.get_return_type() cls = get_cbreturn_class(ret_type) return_value = cls(backend, info, ret_type) cb_args = [] for arg, type_ in zip(args, arg_types): cls = get_cbarg_class(type_) excaped_name = escape_identifier(arg.name) cb_arg = cls(backend, arg, type_, excaped_name) cb_args.append(cb_arg) for arg in cb_args: arg.setup() body = CodeBlock() outs_vars = [] for arg in cb_args: if arg.is_aux: continue block, out = arg.process() if block: block.write_into(body) outs_vars.append(out) arg_names = [a.name for a in cb_args] backend.var.add_blacklist(arg_names) argument_list = ", ".join(arg_names) forward_arguments = ", ".join(outs_vars) func_name = escape_parameter(info.name) cb_name = backend.var() return_var = backend.var() return_block, out_var = return_value.process(return_var) return_block = return_block or CodeBlock() docstring = build_docstring(func_name, cb_args) block, var = backend.parse(""" def $cb_wrapper($args): $body # $docstring $ret = $callback($out_args) $post return $out """, args=argument_list, out_args=forward_arguments, cb_wrapper=func_name, callback=cb_name, body=body, docstring=docstring, ret=return_var, out=out_var, post=return_block) def create_cb_for_func(real_func): if real_func is not None: # binds the callback to the block and compiles it func = block.compile(**{cb_name: real_func})[func_name] else: func = None return backend.get_callback(func, cb_args, return_value) return create_cb_for_func, docstring
def _generate_function(backend, info, arg_infos, arg_types, return_type, method): args = [] for arg_info, arg_type in zip(arg_infos, arg_types): cls = get_argument_class(arg_type) name = escape_identifier(arg_info.name) args.append(cls(name, args, backend, arg_info, arg_type)) cls = get_return_class(return_type) return_value = cls(info, return_type, args, backend) throws = info.flags.value & GIFunctionInfoFlags.THROWS if throws: args.append(ErrorArgument(args, backend)) # setup for arg in args: arg.setup() return_value.setup() # in args in_args = [a for a in args if not a.is_aux and a.in_var] # set description used for exceptions for i, arg in enumerate(in_args): arg.desc = "%s() argument '%s'(%d)" % (info.name, arg.in_var, i + 1) # if the last in argument is a user data, make it a positional argument if in_args and in_args[-1].is_userdata: name = in_args[-1].in_var in_args[-1].in_var = "*" + name in_names = [a.in_var for a in in_args] var_fac = backend.var var_fac.add_blacklist([n.strip("*") for n in in_names]) self_name = "" if method: self_name = var_fac.request_name("self") in_names.insert(0, self_name) in_names = ", ".join(in_names) # pre call body = CodeBlock() for arg in args: if arg.is_aux or arg.is_userdata: continue block = arg.pre_call() if block: block.write_into(body) block = return_value.pre_call() if block: block.write_into(body) # generate call lib = backend.get_library(info.namespace) symbol = info.symbol block, func = backend.get_function(lib, symbol, args, return_value, method, throws) if block: block.write_into(body) # do the call call_vars = [a.call_var for a in args if a.call_var] if method: # there are not unbound method in Python 3 and also no # type checking for the "self" object, but at least raise # TypeError if getting the pointer value fails which # should cover most cases if PY3: block, var = backend.parse(""" try: $ptr = %s._obj except AttributeError: raise TypeError """ % self_name) else: block, var = backend.parse("$ptr = %s._obj" % self_name) block.write_into(body) call_vars.insert(0, var["ptr"]) call_block, var = backend.parse("$ret = $func($args)", func=func, args=", ".join(call_vars)) call_block.write_into(body) ret = var["ret"] out = [] # handle errors first if throws: error_arg = args.pop() block = error_arg.post_call() if block: block.write_into(body) # process return value if not return_value.ignore: block, return_var = return_value.post_call(ret) assert return_var if block: block.write_into(body) out.append((return_var, None)) # process out args for arg in args: if arg.is_aux or arg.is_userdata: continue block = arg.post_call() if block: block.write_into(body) if arg.out_var: out.append((arg.out_var, arg.name)) if len(out) == 1: body.write_line("return %s" % out[0][0]) elif len(out) > 1: # for more than one value use a named tuple. out_vars, out_names = zip(*out) ntuple = create_return_tuple(out_names) block, var = backend.parse(""" return $ntuple((%s)) """ % ", ".join(out_vars), ntuple=ntuple) block.write_into(body) # build final function block func_name = escape_identifier(info.name) docstring = build_docstring(func_name, args, return_value, throws) main, var = backend.parse(""" # backend: $backend_name def $func_name($func_args): '''$docstring''' $func_body """, backend_name=backend.NAME, func_args=in_names, docstring=docstring, func_body=body, func_name=func_name) func = main.compile()[func_name] func._code = main func.__doc__ = docstring func.__module__ = info.namespace return func