def _write_executable_f90(self, execname, lines): """Appends the Fortran code to write the wrapper executable to the specified 'lines' list. :arg execname: the name of the executable in the self.module.executables to write. """ executable = self.module.executables[execname] #The 14 in the formatting indent comes from 4 spaces, 2 from "_c", 1 from the spacing #between 'subroutine' and name of the executable, 10 from subroutine, 1 from the (" cparams = present_params(_ctypes_ex_parameters(executable), len(execname) + 14) lines.append(" subroutine {}_c({}) BIND(C)".format(execname, cparams)) #Next, we add the variables declarations, the call to the original executable and then #the handling of the output variables. lines.append(" " + "\n ".join(_ctypes_ex_variables(executable))) lines.append(" " + "\n ".join(_ctypes_ex_compatvars(executable))) #Add assignment/allocate statements for the intent(in*) paramaters *local* variable #declarations so that we can match the signature exactly. lines.extend(_ctypes_ex_assign(executable)) if type(executable).__name__ == "Subroutine": prefix = "call " else: prefix = "{}_f = ".format(execname) spacing = len(list(prefix)) + len(list(execname)) + 4 lines.append(" {}{}({})".format( prefix, execname, present_params(executable.paramorder, spacing, 90))) lines.append("") lines.extend(_ctypes_ex_clean(executable)) lines.append(" end subroutine {}_c\n".format(execname))
def _write_executable_f90(self, execname, lines): """Appends the Fortran code to write the wrapper executable to the specified 'lines' list. :arg execname: the name of the executable in the self.module.executables to write. """ executable = self.module.executables[execname] #The 14 in the formatting indent comes from 4 spaces, 2 from "_c", 1 from the spacing #between 'subroutine' and name of the executable, 10 from subroutine, 1 from the (" cparams = present_params(_ctypes_ex_parameters(executable), len(execname) + 14) lines.append(" subroutine {}_c({}) BIND(C)".format(execname, cparams)) #Next, we add the variables declarations, the call to the original executable and then #the handling of the output variables. lines.append(" " + "\n ".join(_ctypes_ex_variables(executable))) lines.append(" " + "\n ".join(_ctypes_ex_compatvars(executable))) #Add assignment/allocate statements for the intent(in*) paramaters *local* variable #declarations so that we can match the signature exactly. lines.extend(_ctypes_ex_assign(executable)) if type(executable).__name__ == "Subroutine": prefix = "call " else: prefix = "{}_f = ".format(execname) spacing = len(list(prefix)) + len(list(execname)) + 4 lines.append(" {}{}({})".format(prefix, execname, present_params(executable.paramorder, spacing, 90))) lines.append("") lines.extend(_ctypes_ex_clean(executable)) lines.append(" end subroutine {}_c\n".format(execname))
def _write_executable_py(self, execname, lines): """Writes the python executable definition for interacting with ctypes. :arg execname: the name of the executable to write. :arg lines: the lines of python code are appended to this list. """ executable = self.module.executables[execname] #The 5 in the formatting indent comes from 1 space, 3 from the 'def' #and 1 from '('. inparams = [] for p in executable.ordered_parameters: if "in" in p.direction: inparams.append(p.lname) pyparams = present_params(inparams, len(execname) + 5, linecont=", ") lines.append("def {}({}):".format(execname.lower(), pyparams)) tab = " " #Next, we want to append the docstring information from the XML tags #that were parsed by fortpy. docs = ['"""'] summary = self._format_docstring(executable.summary) if len(summary) > 0: docs[0] += summary[0] docs.extend(summary[1::]) docs.append("") else: docs[0] += "No XML documentation summary." for p in executable.ordered_parameters: doc = ":arg {}: {}; {}".format(p.name, p.strtype, p.summary) docs.extend(self._format_docstring(doc)) lines.extend([tab + l for l in docs]) lines.append(tab + '"""') pathstr = '{}libpath = path.join(path.dirname(__file__), "{}")' lines.append(pathstr.format(tab, self.libname)) methparams = '"{}_c", "{}_c", libpath, True'.format(self.module.name, execname) lines.append("{}method = static_symbol({})".format(tab, methparams)) argtypes = _py_ex_argtype(executable) spacing = len(tab) + 6 + 8 + 3 lines.append("{}method.argtypes = [{}]".format(tab, present_params(argtypes, spacing, linecont=", "))) lines.append("") lparams = [] _py_code_variables(lines, executable, lparams, tab) #This is the line that calls the shared library executable. methparams = present_params(lparams, len(tab) + 5, linecont=", ") lines.append("{}method({})\n".format(tab, methparams)) #Now we clean up the output variables to turn them into numpy arrays etc. constructor = '{}result = FtypesResult("{}", "{}", "{}")' lines.append(constructor.format(tab, self.module.name, execname, type(executable).__name__)) count = _py_code_clean(lines, tab, executable) if count > 0: lines.append("") lines.append("{}return result\n".format(tab)) else: lines.append("{}return None\n".format(tab))
def generate(parser, coderoot, stagedir, compiler=None, debug=False, profile=False, strict=False, docompile=True): """Generates the f90 module file for all members referenced in the parser's modules list. """ import fortpy from fortpy.utility import get_fortpy_templates_dir from fortpy.testing.elements import AutoClasser members = _find_members(parser, coderoot) folder = "filename" classers = [AutoClasser(m, folder, m.name, [], coderoot, True) for m in members] from os import path templates = get_fortpy_templates_dir() statpath = path.join(templates, "fpy_auxiliary.f90") with open(statpath) as f: static = f.read() from fortpy.printing.formatting import present_params xnames, modcode, routines, modules = _generate_single(classers) from fortpy.code import order_module_dependencies modules = order_module_dependencies(modules, parser) from os import mkdir, path if docompile: auxdir = path.join(stagedir, "fortpy.aux") if not path.isdir(auxdir): mkdir(auxdir) else: auxdir = coderoot if docompile and not _should_recompile(auxdir, parser, modules, compiler): msg.okay("The current version of fpy_auxiliary.f90 is up-to-date.") from fortpy.testing.compilers import replace target = replace(auxdir + ".[c]", compiler) return (0, True, target) static = static.replace("__auxsave__", present_params(xnames, 21)) static = static.replace("__aux_uses__", '\n'.join(modcode)) static = static.replace("__version__", fortpy.__version__) static = static.replace("__fxauxsave__", '\n'.join(routines)) xnames, modcode, routines, dmods = _generate_single(classers, False) static = static.replace("__auxread__", present_params(xnames, 21)) static = static.replace("__fxauxread__", '\n'.join(routines)) fortpath = path.join(auxdir, "fpy_auxiliary.f90") with open(fortpath, 'w') as f: f.write(static) if docompile: _prepare_dir(parser, modules, auxdir) return _compile(auxdir, compiler, debug, profile)
def _py_check_optional(parameter, tab, assign): """Returns the code to check for actual values when an out parameter is pure out/optional and doesn't have anything changed. :arg assign: the actual variable assignment value if the conditions pass. """ if parameter.direction == "(out)": result = [] checks = ["{0}_{1:d}.value==0".format(parameter.lname, i) for i in range(parameter.D)] spacing = len(tab) + 2 + 3 + 3 + 2 result.append("{}if not all([{}]):".format(tab, present_params(checks, spacing, linecont=", "))) for line in assign: if assign is not None: result.append("{0}{1}".format(tab, line)) result.append("{}else:".format(tab)) result.append('{0}{0}result.add("{1}", None)'.format(tab, parameter.lname)) return '\n'.join(result) else: return '\n'.join(assign)
def fpy_interface(fpy, static, interface, typedict): """Splices the full list of subroutines and the module procedure list into the static.f90 file. :arg static: the string contents of the static.f90 file. :arg interface: the name of the interface *field* being replaced. :arg typedict: the dictionary of dtypes and their kind and suffix combos. """ modprocs = [] subtext = [] for dtype, combos in list(typedict.items()): for tcombo in combos: kind, suffix = tcombo xnames, sub = fpy_interface_sub(fpy, dtype, kind, suffix) modprocs.extend(xnames) subtext.append(sub) subtext.append("\n") #Next, chunk the names of the module procedures into blocks of five #so that they display nicely for human readability. from fortpy.printing.formatting import present_params splice = static.replace(interface, present_params(modprocs, 21)) return splice.replace(interface.replace("py", "xpy"), ''.join(subtext))
def _py_check_optional(parameter, tab, assign): """Returns the code to check for actual values when an out parameter is pure out/optional and doesn't have anything changed. :arg assign: the actual variable assignment value if the conditions pass. """ if parameter.direction == "(out)": result = [] checks = [ "{0}_{1:d}.value==0".format(parameter.lname, i) for i in range(parameter.D) ] spacing = len(tab) + 2 + 3 + 3 + 2 result.append("{}if not all([{}]):".format( tab, present_params(checks, spacing, linecont=", "))) for line in assign: if assign is not None: result.append("{0}{1}".format(tab, line)) result.append("{}else:".format(tab)) result.append('{0}{0}result.add("{1}", None)'.format( tab, parameter.lname)) return '\n'.join(result) else: return '\n'.join(assign)
def _write_executable_py(self, execname, lines): """Writes the python executable definition for interacting with ctypes. :arg execname: the name of the executable to write. :arg lines: the lines of python code are appended to this list. """ executable = self.module.executables[execname] #The 5 in the formatting indent comes from 1 space, 3 from the 'def' #and 1 from '('. inparams = [] for p in executable.ordered_parameters: if "in" in p.direction: inparams.append(p.lname) pyparams = present_params(inparams, len(execname) + 5, linecont=", ") lines.append("def {}({}):".format(execname.lower(), pyparams)) tab = " " #Next, we want to append the docstring information from the XML tags #that were parsed by fortpy. docs = ['"""'] summary = self._format_docstring(executable.summary) if len(summary) > 0: docs[0] += summary[0] docs.extend(summary[1::]) docs.append("") else: docs[0] += "No XML documentation summary." for p in executable.ordered_parameters: doc = ":arg {}: {}; {}".format(p.name, p.strtype, p.summary) docs.extend(self._format_docstring(doc)) lines.extend([tab + l for l in docs]) lines.append(tab + '"""') pathstr = '{}libpath = path.join(path.dirname(__file__), "{}")' lines.append(pathstr.format(tab, self.libname)) methparams = '"{}_c", "{}_c", libpath, True'.format( self.module.name, execname) lines.append("{}method = static_symbol({})".format(tab, methparams)) argtypes = _py_ex_argtype(executable) spacing = len(tab) + 6 + 8 + 3 lines.append("{}method.argtypes = [{}]".format( tab, present_params(argtypes, spacing, linecont=", "))) lines.append("") lparams = [] _py_code_variables(lines, executable, lparams, tab) #This is the line that calls the shared library executable. methparams = present_params(lparams, len(tab) + 5, linecont=", ") lines.append("{}method({})\n".format(tab, methparams)) #Now we clean up the output variables to turn them into numpy arrays etc. constructor = '{}result = FtypesResult("{}", "{}", "{}")' lines.append( constructor.format(tab, self.module.name, execname, type(executable).__name__)) count = _py_code_clean(lines, tab, executable) if count > 0: lines.append("") lines.append("{}return result\n".format(tab)) else: lines.append("{}return None\n".format(tab))