def __init__(self, parser, target_language, settings=None): CCodePrinter.__init__(self, parser, settings) self._target_language = target_language self._cast_functions_dict = OrderedDict() self._to_free_PyObject_list = [] self._function_wrapper_names = dict() self._global_names = set() self._module_name = None
def function_signature(self, expr): args = list(expr.arguments) if any([isinstance(a, FunctionAddress) for a in args]): # Functions with function addresses as arguments cannot be # exposed to python so there is no need to print their signature return '' else: return CCodePrinter.function_signature(self, expr)
def _print_FunctionDef(self, expr): # Save all used names used_names = set([a.name for a in expr.arguments] + [r.name for r in expr.results] + [expr.name.name]) # Find a name for the wrapper function wrapper_name = self._get_wrapper_name(used_names, expr) used_names.add(wrapper_name) # Collect local variables wrapper_vars = {a.name: a for a in expr.arguments} wrapper_vars.update({r.name: r for r in expr.results}) python_func_args = self.get_new_PyObject("args", used_names) python_func_kwargs = self.get_new_PyObject("kwargs", used_names) python_func_selfarg = self.get_new_PyObject("self", used_names) # Collect arguments and results wrapper_args = [ python_func_selfarg, python_func_args, python_func_kwargs ] wrapper_results = [self.get_new_PyObject("result", used_names)] if expr.is_private: wrapper_func = FunctionDef( name=wrapper_name, arguments=wrapper_args, results=wrapper_results, body=[ PyErr_SetString( 'PyExc_NotImplementedError', '"Private functions are not accessible from python"'), AliasAssign(wrapper_results[0], Nil()), Return(wrapper_results) ]) return CCodePrinter._print_FunctionDef(self, wrapper_func) if any(isinstance(arg, FunctionAddress) for arg in expr.arguments): wrapper_func = FunctionDef( name=wrapper_name, arguments=wrapper_args, results=wrapper_results, body=[ PyErr_SetString('PyExc_NotImplementedError', '"Cannot pass a function as an argument"'), AliasAssign(wrapper_results[0], Nil()), Return(wrapper_results) ]) return CCodePrinter._print_FunctionDef(self, wrapper_func) # Collect argument names for PyArgParse arg_names = [a.name for a in expr.arguments] keyword_list_name = self.get_new_name(used_names, 'kwlist') keyword_list = PyArgKeywords(keyword_list_name, arg_names) wrapper_body = [keyword_list] wrapper_body_translations = [] parse_args = [] collect_vars = {} for arg in expr.arguments: collect_var, cast_func = self.get_PyArgParseType(used_names, arg) collect_vars[arg] = collect_var body, tmp_variable = self._body_management(used_names, arg, collect_var, cast_func, True) if tmp_variable: wrapper_vars[tmp_variable.name] = tmp_variable # If the variable cannot be collected from PyArgParse directly wrapper_vars[collect_var.name] = collect_var # Save cast to argument variable wrapper_body_translations.extend(body) parse_args.append(collect_var) # Write default values if isinstance(arg, ValuedVariable): wrapper_body.append( self.get_default_assign(parse_args[-1], arg)) # Parse arguments parse_node = PyArg_ParseTupleNode(python_func_args, python_func_kwargs, expr.arguments, parse_args, keyword_list) wrapper_body.append(If((PyccelNot(parse_node), [Return([Nil()])]))) wrapper_body.extend(wrapper_body_translations) # Call function static_function, static_args, additional_body = self._get_static_function( used_names, expr, collect_vars) wrapper_body.extend(additional_body) for var in static_args: wrapper_vars[var.name] = var if len(expr.results) == 0: func_call = FunctionCall(static_function, static_args) else: results = expr.results if len( expr.results) > 1 else expr.results[0] func_call = Assign(results, FunctionCall(static_function, static_args)) wrapper_body.append(func_call) # Loop over results to carry out necessary casts and collect Py_BuildValue type string res_args = [] for a in expr.results: collect_var, cast_func = self.get_PyBuildValue(used_names, a) if cast_func is not None: wrapper_vars[collect_var.name] = collect_var wrapper_body.append(AliasAssign(collect_var, cast_func)) res_args.append( VariableAddress(collect_var) if collect_var. is_pointer else collect_var) # Call PyBuildNode wrapper_body.append( AliasAssign(wrapper_results[0], PyBuildValueNode(res_args))) # Call free function for python type wrapper_body += [ FunctionCall(Py_DECREF, [i]) for i in self._to_free_PyObject_list ] self._to_free_PyObject_list.clear() #Return wrapper_body.append(Return(wrapper_results)) # Create FunctionDef and write using classic method wrapper_func = FunctionDef(name=wrapper_name, arguments=wrapper_args, results=wrapper_results, body=wrapper_body, local_vars=wrapper_vars.values()) return CCodePrinter._print_FunctionDef(self, wrapper_func)
def stored_in_c_pointer(self, a): stored_in_c = CCodePrinter.stored_in_c_pointer(self, a) if self._target_language == 'fortran': return stored_in_c or (isinstance(a, Variable) and a.rank > 0) else: return stored_in_c
def _print_Interface(self, expr): # Collecting all functions funcs = expr.functions # Save all used names used_names = set(n.name for n in funcs) # Find a name for the wrapper function wrapper_name = self._get_wrapper_name(used_names, expr) self._global_names.add(wrapper_name) # Collect local variables python_func_args = self.get_new_PyObject("args", used_names) python_func_kwargs = self.get_new_PyObject("kwargs", used_names) python_func_selfarg = self.get_new_PyObject("self", used_names) # Collect wrapper arguments and results wrapper_args = [ python_func_selfarg, python_func_args, python_func_kwargs ] wrapper_results = [self.get_new_PyObject("result", used_names)] # Collect parser arguments wrapper_vars = {} # Collect argument names for PyArgParse arg_names = [a.name for a in funcs[0].arguments] keyword_list_name = self.get_new_name(used_names, 'kwlist') keyword_list = PyArgKeywords(keyword_list_name, arg_names) wrapper_body = [keyword_list] wrapper_body_translations = [] body_tmp = [] # To store the mini function responsible of collecting value and calling interfaces functions and return the builded value funcs_def = [] default_value = { } # dict to collect all initialisation needed in the wrapper check_var = Variable(dtype=NativeInteger(), name=self.get_new_name(used_names, "check")) wrapper_vars[check_var.name] = check_var types_dict = OrderedDict( (a, set()) for a in funcs[0].arguments ) #dict to collect each variable possible type and the corresponding flags # collect parse arg parse_args = [ Variable(dtype=PyccelPyArrayObject(), is_pointer=True, rank=a.rank, order=a.order, name=self.get_new_name(used_names, a.name + "_tmp")) if a.rank > 0 else Variable(dtype=PyccelPyObject(), name=self.get_new_name(used_names, a.name + "_tmp"), is_pointer=True) for a in funcs[0].arguments ] # Managing the body of wrapper for func in funcs: mini_wrapper_func_body = [] res_args = [] mini_wrapper_func_vars = {a.name: a for a in func.arguments} flags = 0 collect_vars = {} # Loop for all args in every functions and create the corresponding condition and body for p_arg, f_arg in zip(parse_args, func.arguments): collect_vars[f_arg] = p_arg body, tmp_variable = self._body_management( used_names, f_arg, p_arg, None) if tmp_variable: mini_wrapper_func_vars[tmp_variable.name] = tmp_variable # get check type function check = self._get_check_type_statement(f_arg, p_arg) # If the variable cannot be collected from PyArgParse directly wrapper_vars[p_arg.name] = p_arg # Save the body wrapper_body_translations.extend(body) # Write default values if isinstance(f_arg, ValuedVariable): wrapper_body.append( self.get_default_assign(parse_args[-1], f_arg)) flag_value = flags_registry[(f_arg.dtype, f_arg.precision)] flags = (flags << 4) + flag_value # shift by 4 to the left types_dict[f_arg].add( (f_arg, check, flag_value)) # collect variable type for each arguments mini_wrapper_func_body += body # create the corresponding function call static_function, static_args, additional_body = self._get_static_function( used_names, func, collect_vars) mini_wrapper_func_body.extend(additional_body) for var in static_args: mini_wrapper_func_vars[var.name] = var if len(func.results) == 0: func_call = FunctionCall(static_function, static_args) else: results = func.results if len( func.results) > 1 else func.results[0] func_call = Assign(results, FunctionCall(static_function, static_args)) mini_wrapper_func_body.append(func_call) # Loop for all res in every functions and create the corresponding body and cast for r in func.results: collect_var, cast_func = self.get_PyBuildValue(used_names, r) mini_wrapper_func_vars[collect_var.name] = collect_var if cast_func is not None: mini_wrapper_func_vars[r.name] = r mini_wrapper_func_body.append( AliasAssign(collect_var, cast_func)) res_args.append( VariableAddress(collect_var) if collect_var. is_pointer else collect_var) # Building PybuildValue and freeing the allocated variable after. mini_wrapper_func_body.append( AliasAssign(wrapper_results[0], PyBuildValueNode(res_args))) mini_wrapper_func_body += [ FunctionCall(Py_DECREF, [i]) for i in self._to_free_PyObject_list ] mini_wrapper_func_body.append(Return(wrapper_results)) self._to_free_PyObject_list.clear() # Building Mini wrapper function mini_wrapper_func_name = self.get_new_name( used_names.union(self._global_names), func.name.name + '_mini_wrapper') self._global_names.add(mini_wrapper_func_name) mini_wrapper_func_def = FunctionDef( name=mini_wrapper_func_name, arguments=parse_args, results=wrapper_results, body=mini_wrapper_func_body, local_vars=mini_wrapper_func_vars.values()) funcs_def.append(mini_wrapper_func_def) # append check condition to the functioncall body_tmp.append((PyccelEq(check_var, LiteralInteger(flags)), [ AliasAssign(wrapper_results[0], FunctionCall(mini_wrapper_func_def, parse_args)) ])) # Errors / Types management # Creating check_type function check_func_def = self._create_wrapper_check(check_var, parse_args, types_dict, used_names, funcs[0].name.name) funcs_def.append(check_func_def) # Create the wrapper body with collected informations body_tmp = [((PyccelNot(check_var), [Return([Nil()])]))] + body_tmp body_tmp.append((LiteralTrue(), [ PyErr_SetString('PyExc_TypeError', '"Arguments combinations don\'t exist"'), Return([Nil()]) ])) wrapper_body_translations = [If(*body_tmp)] # Parsing Arguments parse_node = PyArg_ParseTupleNode(python_func_args, python_func_kwargs, funcs[0].arguments, parse_args, keyword_list, True) wrapper_body += list(default_value.values()) wrapper_body.append(If((PyccelNot(parse_node), [Return([Nil()])]))) #finishing the wrapper body wrapper_body.append( Assign(check_var, FunctionCall(check_func_def, parse_args))) wrapper_body.extend(wrapper_body_translations) wrapper_body.append(Return(wrapper_results)) # Return # Create FunctionDef funcs_def.append( FunctionDef(name=wrapper_name, arguments=wrapper_args, results=wrapper_results, body=wrapper_body, local_vars=wrapper_vars.values())) sep = self._print(SeparatorComment(40)) return sep + '\n'.join( CCodePrinter._print_FunctionDef(self, f) for f in funcs_def)
def _print_Module(self, expr): self._global_names = set(f.name.name for f in expr.funcs) self._module_name = expr.name sep = self._print(SeparatorComment(40)) if self._target_language == 'fortran': static_funcs = [ as_static_function_call(f, expr.name, name=f.name) for f in expr.funcs ] else: static_funcs = expr.funcs function_signatures = '\n'.join( '{};'.format(self.function_signature(f)) for f in static_funcs) interfaces = expr.interfaces interface_funcs = [f.name for i in interfaces for f in i.functions] funcs = interfaces + [ f for f in expr.funcs if f.name not in interface_funcs ] function_defs = '\n\n'.join(self._print(f) for f in funcs) cast_functions = '\n\n'.join( CCodePrinter._print_FunctionDef(self, f) for f in self._cast_functions_dict.values()) method_def_func = ',\n'.join(('{{\n' '"{name}",\n' '(PyCFunction){wrapper_name},\n' 'METH_VARARGS | METH_KEYWORDS,\n' '{doc_string}\n' '}}').format( name = f.name, wrapper_name = self._function_wrapper_names[f.name], doc_string = self._print(LiteralString('\n'.join(f.doc_string.comments))) \ if f.doc_string else '""') for f in funcs) method_def_name = self.get_new_name(self._global_names, '{}_methods'.format(expr.name)) method_def = ('static PyMethodDef {method_def_name}[] = {{\n' '{method_def_func},\n' '{{ NULL, NULL, 0, NULL}}\n' '}};'.format(method_def_name=method_def_name, method_def_func=method_def_func)) module_def_name = self.get_new_name(self._global_names, '{}_module'.format(expr.name)) module_def = ( 'static struct PyModuleDef {module_def_name} = {{\n' 'PyModuleDef_HEAD_INIT,\n' '/* name of module */\n' '\"{mod_name}\",\n' '/* module documentation, may be NULL */\n' 'NULL,\n' #TODO: Add documentation '/* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */\n' '-1,\n' '{method_def_name}\n' '}};'.format(module_def_name=module_def_name, mod_name=expr.name, method_def_name=method_def_name)) init_func = ('PyMODINIT_FUNC PyInit_{mod_name}(void)\n{{\n' 'PyObject *m;\n\n' 'import_array();\n\n' 'm = PyModule_Create(&{module_def_name});\n' 'if (m == NULL) return NULL;\n\n' 'return m;\n}}'.format(mod_name=expr.name, module_def_name=module_def_name)) # Print imports last to be sure that all additional_imports have been collected imports = [Import(s) for s in self._additional_imports] imports += [Import('Python')] imports += [Import('numpy/arrayobject')] imports = '\n'.join(self._print(i) for i in imports) numpy_max_acceptable_version = [1, 19] numpy_current_version = [ int(v) for v in np.version.version.split('.')[:2] ] numpy_api_macro = '#define NPY_NO_DEPRECATED_API NPY_{}_{}_API_VERSION'.format( min(numpy_max_acceptable_version[0], numpy_current_version[0]), min(numpy_max_acceptable_version[1], numpy_current_version[1])) return ('#define PY_SSIZE_T_CLEAN\n' '{numpy_api_macro}\n' '{imports}\n\n' '{function_signatures}\n\n' '{sep}\n\n' '{cast_functions}\n\n' '{sep}\n\n' '{function_defs}\n\n' '{method_def}\n\n' '{sep}\n\n' '{module_def}\n\n' '{sep}\n\n' '{init_func}\n'.format(numpy_api_macro=numpy_api_macro, imports=imports, function_signatures=function_signatures, sep=sep, cast_functions=cast_functions, function_defs=function_defs, method_def=method_def, module_def=module_def, init_func=init_func))
def find_in_dtype_registry(self, dtype, prec): try: return dtype_registry[(dtype, prec)] except KeyError: return CCodePrinter.find_in_dtype_registry(self, dtype, prec)