NumPy 源码解析(九)
.\numpy\numpy\f2py\diagnose.py
import os
import sys
import tempfile
def run_command(cmd):
print('Running %r:' % (cmd))
os.system(cmd)
print('------')
def run():
_path = os.getcwd()
os.chdir(tempfile.gettempdir())
print('------')
print('os.name=%r' % (os.name))
print('------')
print('sys.platform=%r' % (sys.platform))
print('------')
print('sys.version:')
print(sys.version)
print('------')
print('sys.prefix:')
print(sys.prefix)
print('------')
print('sys.path=%r' % (':'.join(sys.path)))
print('------')
try:
import numpy
has_newnumpy = 1
except ImportError as e:
print('Failed to import new numpy:', e)
has_newnumpy = 0
try:
from numpy.f2py import f2py2e
has_f2py2e = 1
except ImportError as e:
print('Failed to import f2py2e:', e)
has_f2py2e = 0
try:
import numpy.distutils
has_numpy_distutils = 2
except ImportError:
try:
import numpy_distutils
has_numpy_distutils = 1
except ImportError as e:
print('Failed to import numpy_distutils:', e)
has_numpy_distutils = 0
if has_newnumpy:
try:
print('Found new numpy version %r in %s' %
(numpy.__version__, numpy.__file__))
except Exception as msg:
print('error:', msg)
print('------')
if has_f2py2e:
try:
print('Found f2py2e version %r in %s' %
(f2py2e.__version__.version, f2py2e.__file__))
except Exception as msg:
print('error:', msg)
print('------')
os.chdir(_path)
if __name__ == "__main__":
run()
.\numpy\numpy\f2py\f2py2e.py
"""
f2py2e - Fortran to Python C/API generator. 2nd Edition.
See __usage__ below.
Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
Copyright 2011 -- present NumPy Developers.
Permission to use, modify, and distribute this software is given under the
terms of the NumPy License.
NO WARRANTY IS EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
"""
import sys
import os
import pprint
import re
from pathlib import Path
from itertools import dropwhile
import argparse
import copy
from . import crackfortran
from . import rules
from . import cb_rules
from . import auxfuncs
from . import cfuncs
from . import f90mod_rules
from . import __version__
from . import capi_maps
from numpy.f2py._backends import f2py_build_generator
f2py_version = __version__.version
numpy_version = __version__.version
errmess = sys.stderr.write
show = pprint.pprint
outmess = auxfuncs.outmess
MESON_ONLY_VER = (sys.version_info >= (3, 12))
__usage__ =\
f"""Usage:
1) To construct extension module sources:
f2py [<options>] <fortran files> [[[only:]||[skip:]] \\
<fortran functions> ] \\
[: <fortran files> ...]
2) To compile fortran files and build extension modules:
f2py -c [<options>, <build_flib options>, <extra options>] <fortran files>
3) To generate signature files:
f2py -h <filename.pyf> ...< same options as in (1) >
Description: This program generates a Python C/API file (<modulename>module.c)
that contains wrappers for given fortran functions so that they
can be called from Python. With the -c option the corresponding
extension modules are built.
"""
Options:
Version: {f2py_version}
numpy Version: {numpy_version}
License: NumPy license (see LICENSE.txt in the NumPy source code)
Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
def scaninputline(inputline):
files, skipfuncs, onlyfuncs, debug = [], [], [], []
f, f2, f3, f5, f6, f8, f9, f10 = 1, 0, 0, 0, 0, 0, 0, 0
verbose = 1
emptygen = True
dolc = -1
dolatexdoc = 0
dorestdoc = 0
wrapfuncs = 1
buildpath = '.'
include_paths, inputline = get_includes(inputline)
signsfile, modulename = None, None
options = {'buildpath': buildpath,
'coutput': None,
'f2py_wrapper_output': None}
for l in inputline:
if l == '':
pass
elif l == 'only:':
f = 0
elif l == 'skip:':
f = -1
elif l == ':':
f = 1
elif l[:8] == '--debug-':
debug.append(l[8:])
elif l == '--lower':
dolc = 1
elif l == '--build-dir':
f6 = 1
elif l == '--no-lower':
dolc = 0
elif l == '--quiet':
verbose = 0
elif l == '--verbose':
verbose += 1
elif l == '--latex-doc':
dolatexdoc = 1
elif l == '--no-latex-doc':
dolatexdoc = 0
elif l == '--rest-doc':
dorestdoc = 1
elif l == '--no-rest-doc':
dorestdoc = 0
elif l == '--wrap-functions':
wrapfuncs = 1
elif l == '--no-wrap-functions':
wrapfuncs = 0
elif l == '--short-latex':
options['shortlatex'] = 1
elif l == '--coutput':
f8 = 1
elif l == '--f2py-wrapper-output':
f9 = 1
elif l == '--f2cmap':
f10 = 1
elif l == '--overwrite-signature':
options['h-overwrite'] = 1
elif l == '-h':
f2 = 1
elif l == '-m':
f3 = 1
elif l[:2] == '-v':
print(f2py_version)
sys.exit()
elif l == '--show-compilers':
f5 = 1
elif l[:8] == '-include':
cfuncs.outneeds['userincludes'].append(l[9:-1])
cfuncs.userincludes[l[9:-1]] = '#include ' + l[8:]
elif l == '--skip-empty-wrappers':
emptygen = False
elif l[0] == '-':
errmess('Unknown option %s\n' % repr(l))
sys.exit()
elif f2:
f2 = 0
signsfile = l
elif f3:
f3 = 0
modulename = l
elif f6:
f6 = 0
buildpath = l
elif f8:
f8 = 0
options["coutput"] = l
elif f9:
f9 = 0
options["f2py_wrapper_output"] = l
elif f10:
f10 = 0
options["f2cmap_file"] = l
elif f == 1:
try:
with open(l):
pass
files.append(l)
except OSError as detail:
errmess(f'OSError: {detail!s}. Skipping file "{l!s}".\n')
elif f == -1:
skipfuncs.append(l)
if not f5 and not files and not modulename:
print(__usage__)
sys.exit()
if not os.path.isdir(buildpath):
if not verbose:
outmess('Creating build directory %s\n' % (buildpath))
os.mkdir(buildpath)
if signsfile:
signsfile = os.path.join(buildpath, signsfile)
if signsfile and os.path.isfile(signsfile) and 'h-overwrite' not in options:
errmess(
'Signature file "%s" exists!!! Use --overwrite-signature to overwrite.\n' % (signsfile))
sys.exit()
options['emptygen'] = emptygen
options['debug'] = debug
options['verbose'] = verbose
if dolc == -1 and not signsfile:
options['do-lower'] = 0
else:
options['do-lower'] = dolc
if modulename:
options['module'] = modulename
if signsfile:
options['signsfile'] = signsfile
if onlyfuncs:
options['onlyfuncs'] = onlyfuncs
if skipfuncs:
options['skipfuncs'] = skipfuncs
options['dolatexdoc'] = dolatexdoc
options['dorestdoc'] = dorestdoc
options['wrapfuncs'] = wrapfuncs
options['buildpath'] = buildpath
options['include_paths'] = include_paths
options.setdefault('f2cmap_file', None)
return files, options
def callcrackfortran(files, options):
rules.options = options
crackfortran.debug = options['debug']
crackfortran.verbose = options['verbose']
if 'module' in options:
crackfortran.f77modulename = options['module']
if 'skipfuncs' in options:
crackfortran.skipfuncs = options['skipfuncs']
if 'onlyfuncs' in options:
crackfortran.onlyfuncs = options['onlyfuncs']
crackfortran.include_paths[:] = options['include_paths']
crackfortran.dolowercase = options['do-lower']
postlist = crackfortran.crackfortran(files)
if 'signsfile' in options:
outmess('Saving signatures to file "%s"\n' % (options['signsfile']))
pyf = crackfortran.crack2fortran(postlist)
if options['signsfile'][-6:] == 'stdout':
sys.stdout.write(pyf)
else:
with open(options['signsfile'], 'w') as f:
f.write(pyf)
if options["coutput"] is None:
for mod in postlist:
mod["coutput"] = "%smodule.c" % mod["name"]
else:
for mod in postlist:
mod["coutput"] = options["coutput"]
if options["f2py_wrapper_output"] is None:
for mod in postlist:
mod["f2py_wrapper_output"] = "%s-f2pywrappers.f" % mod["name"]
else:
for mod in postlist:
mod["f2py_wrapper_output"] = options["f2py_wrapper_output"]
return postlist
def buildmodules(lst):
cfuncs.buildcfuncs()
outmess('Building modules...\n')
modules, mnames, isusedby = [], [], {}
for item in lst:
if '__user__' in item['name']:
cb_rules.buildcallbacks(item)
else:
if 'use' in item:
for u in item['use'].keys():
if u not in isusedby:
isusedby[u] = []
isusedby[u].append(item['name'])
modules.append(item)
mnames.append(item['name'])
ret = {}
for module, name in zip(modules, mnames):
if name in isusedby:
outmess('\tSkipping module "%s" which is used by %s.\n' % (
name, ','.join('"%s"' % s for s in isusedby[name])))
else:
um = []
if 'use' in module:
for u in module['use'].keys():
if u in isusedby and u in mnames:
um.append(modules[mnames.index(u)])
else:
outmess(
f'\tModule "{name}" uses nonexisting "{u}" '
'which will be ignored.\n')
ret[name] = {}
dict_append(ret[name], rules.buildmodule(module, um))
return ret
def dict_append(d_out, d_in):
for (k, v) in d_in.items():
if k not in d_out:
d_out[k] = []
if isinstance(v, list):
d_out[k] = d_out[k] + v
else:
d_out[k].append(v)
def run_main(comline_list):
"""
Equivalent to running::
f2py <args>
"""
crackfortran.reset_global_f2py_vars()
f2pydir = os.path.dirname(os.path.abspath(cfuncs.__file__))
fobjhsrc = os.path.join(f2pydir, 'src', 'fortranobject.h')
fobjcsrc = os.path.join(f2pydir, 'src', 'fortranobject.c')
parser = make_f2py_compile_parser()
args, comline_list = parser.parse_known_args(comline_list)
pyf_files, _ = filter_files("", "[.]pyf([.]src|)", comline_list)
if args.module_name:
if "-h" in comline_list:
modname = (
args.module_name
)
else:
modname = validate_modulename(
pyf_files, args.module_name
)
comline_list += ['-m', modname]
files, options = scaninputline(comline_list)
auxfuncs.options = options
capi_maps.load_f2cmap_file(options['f2cmap_file'])
postlist = callcrackfortran(files, options)
isusedby = {}
for plist in postlist:
if 'use' in plist:
for u in plist['use'].keys():
if u not in isusedby:
isusedby[u] = []
isusedby[u].append(plist['name'])
for plist in postlist:
if plist['block'] == 'python module' and '__user__' in plist['name']:
if plist['name'] in isusedby:
outmess(
f'Skipping Makefile build for module "{plist["name"]}" '
'which is used by {}\n'.format(
','.join(f'"{s}"' for s in isusedby[plist['name']])))
if 'signsfile' in options:
if options['verbose'] > 1:
outmess(
'Stopping. Edit the signature file and then run f2py on the signature file: ')
outmess('%s %s\n' %
(os.path.basename(sys.argv[0]), options['signsfile']))
return
for plist in postlist:
if plist['block'] != 'python module':
if 'python module' not in options:
errmess('Tip: If your original code is Fortran source then you must use -m option.\n')
raise TypeError('All blocks must be python module blocks but got %s' % (repr(plist['block'])))
auxfuncs.debugoptions = options['debug']
f90mod_rules.options = options
auxfuncs.wrapfuncs = options['wrapfuncs']
ret = buildmodules(postlist)
for mn in ret.keys():
dict_append(ret[mn], {'csrc': fobjcsrc, 'h': fobjhsrc})
return ret
def filter_files(prefix, suffix, files, remove_prefix=None):
"""
Filter files by prefix and suffix.
prefix: str, prefix string to match at the beginning of filenames.
suffix: str, suffix string to match at the end of filenames.
files: list of str, list of filenames to filter.
remove_prefix: str or None, if provided, the prefix to remove from filtered filenames.
Returns:
filtered: list of str, filenames that match the prefix and suffix criteria.
rest: list of str, filenames that do not match the criteria.
"""
filtered, rest = [], []
match = re.compile(prefix + r'.*' + suffix + r'\Z').match
if remove_prefix:
ind = len(prefix)
else:
ind = 0
for file in [x.strip() for x in files]:
if match(file):
filtered.append(file[ind:])
else:
rest.append(file)
return filtered, rest
def get_prefix(module):
"""
Retrieve the parent directory of the parent directory of the module file.
module: module object, the module whose parent directories need to be retrieved.
Returns:
p: str, parent directory path.
"""
p = os.path.dirname(os.path.dirname(module.__file__))
return p
class CombineIncludePaths(argparse.Action):
"""
Custom action to combine multiple include paths into a set.
Appends each new include path to the existing set of include paths.
"""
def __call__(self, parser, namespace, values, option_string=None):
include_paths_set = set(getattr(namespace, 'include_paths', []) or [])
if option_string == "--include_paths":
outmess("Use --include-paths or -I instead of --include_paths which will be removed")
if option_string == "--include-paths" or option_string == "--include_paths":
include_paths_set.update(values.split(':'))
else:
include_paths_set.add(values)
setattr(namespace, 'include_paths', list(include_paths_set))
def include_parser():
"""
Create an ArgumentParser for handling include paths.
Returns:
parser: ArgumentParser object configured for include paths.
"""
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument("-I", dest="include_paths", action=CombineIncludePaths)
parser.add_argument("--include-paths", dest="include_paths", action=CombineIncludePaths)
parser.add_argument("--include_paths", dest="include_paths", action=CombineIncludePaths)
return parser
def get_includes(iline):
"""
Parse a list of command-line arguments to extract include paths.
iline: list of str, command-line arguments.
Returns:
ipaths: list of str, include paths extracted from the command-line.
remain: list of str, remaining command-line arguments after parsing include paths.
"""
iline = (' '.join(iline)).split()
parser = include_parser()
args, remain = parser.parse_known_args(iline)
ipaths = args.include_paths
if args.include_paths is None:
ipaths = []
return ipaths, remain
def make_f2py_compile_parser():
"""
Create an ArgumentParser for parsing f2py compilation options.
Returns:
parser: ArgumentParser object configured for f2py compilation options.
"""
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument("--dep", action="append", dest="dependencies")
parser.add_argument("--backend", choices=['meson', 'distutils'], default='distutils')
parser.add_argument("-m", dest="module_name")
return parser
def preparse_sysargv():
"""
Prepare sys.argv for f2py compilation.
Parses sys.argv using a predefined parser and adjusts it accordingly.
"""
parser = make_f2py_compile_parser()
args, remaining_argv = parser.parse_known_args()
sys.argv = [sys.argv[0]] + remaining_argv
backend_key = args.backend
if MESON_ONLY_VER and backend_key == 'distutils':
outmess("Cannot use distutils backend with Python>=3.12,"
" using meson backend instead.\n")
backend_key = "meson"
return {
"dependencies": args.dependencies or [],
"backend": backend_key,
"modulename": args.module_name,
}
def run_compile():
"""
Perform all necessary actions for f2py compilation in one function call.
Collects dependency flags, preprocesses sys.argv, and retrieves the module name.
"""
import tempfile
argy = preparse_sysargv()
modulename = argy["modulename"]
if modulename is None:
modulename = 'untitled'
dependencies = argy["dependencies"]
backend_key = argy["backend"]
build_backend = f2py_build_generator(backend_key)
i = sys.argv.index('-c')
del sys.argv[i]
remove_build_dir = 0
try:
i = sys.argv.index('--build-dir')
except ValueError:
i = None
if i is not None:
build_dir = sys.argv[i + 1]
del sys.argv[i + 1]
del sys.argv[i]
else:
remove_build_dir = 1
build_dir = tempfile.mkdtemp()
_reg1 = re.compile(r'--link-')
sysinfo_flags = [_m for _m in sys.argv[1:] if _reg1.match(_m)]
sys.argv = [_m for _m in sys.argv if _m not in sysinfo_flags]
if sysinfo_flags:
sysinfo_flags = [f[7:] for f in sysinfo_flags]
_reg2 = re.compile(
r'--((no-|)(wrap-functions|lower)|debug-capi|quiet|skip-empty-wrappers)|-include')
f2py_flags = [_m for _m in sys.argv[1:] if _reg2.match(_m)]
sys.argv = [_m for _m in sys.argv if _m not in f2py_flags]
f2py_flags2 = []
fl = 0
for a in sys.argv[1:]:
if a in ['only:', 'skip:']:
fl = 1
elif a == ':':
fl = 0
if fl or a == ':':
f2py_flags2.append(a)
if f2py_flags2 and f2py_flags2[-1] != ':':
f2py_flags2.append(':')
f2py_flags.extend(f2py_flags2)
sys.argv = [_m for _m in sys.argv if _m not in f2py_flags2]
_reg3 = re.compile(
r'--((f(90)?compiler(-exec|)|compiler)=|help-compiler)')
flib_flags = [_m for _m in sys.argv[1:] if _reg3.match(_m)]
sys.argv = [_m for _m in sys.argv if _m not in flib_flags]
reg_f77_f90_flags = re.compile(r'--f(77|90)flags=')
reg_distutils_flags = re.compile(r'--((f(77|90)exec|opt|arch)=|(debug|noopt|noarch|help-fcompiler))')
fc_flags = [_m for _m in sys.argv[1:] if reg_f77_f90_flags.match(_m)]
distutils_flags = [_m for _m in sys.argv[1:] if reg_distutils_flags.match(_m)]
if not (MESON_ONLY_VER or backend_key == 'meson'):
fc_flags.extend(distutils_flags)
sys.argv = [_m for _m in sys.argv if _m not in (fc_flags + distutils_flags)]
del_list = []
for s in flib_flags:
v = '--fcompiler='
if s[:len(v)] == v:
if MESON_ONLY_VER or backend_key == 'meson':
outmess(
"--fcompiler cannot be used with meson,"
"set compiler with the FC environment variable\n"
)
else:
from numpy.distutils import fcompiler
fcompiler.load_all_fcompiler_classes()
allowed_keys = list(fcompiler.fcompiler_class.keys())
nv = ov = s[len(v):].lower()
if ov not in allowed_keys:
vmap = {}
try:
nv = vmap[ov]
except KeyError:
if ov not in vmap.values():
print('Unknown vendor: "%s"' % (s[len(v):]))
nv = ov
i = flib_flags.index(s)
flib_flags[i] = '--fcompiler=' + nv
continue
for s in del_list:
i = flib_flags.index(s)
del flib_flags[i]
assert len(flib_flags) <= 2, repr(flib_flags)
_reg5 = re.compile(r'--(verbose)')
setup_flags = [_m for _m in sys.argv[1:] if _reg5.match(_m)]
sys.argv = [_m for _m in sys.argv if _m not in setup_flags]
if '--quiet' in f2py_flags:
setup_flags.append('--quiet')
sources = sys.argv[1:]
f2cmapopt = '--f2cmap'
if f2cmapopt in sys.argv:
i = sys.argv.index(f2cmapopt)
f2py_flags.extend(sys.argv[i:i + 2])
del sys.argv[i + 1], sys.argv[i]
sources = sys.argv[1:]
pyf_files, _sources = filter_files("", "[.]pyf([.]src|)", sources)
sources = pyf_files + _sources
modulename = validate_modulename(pyf_files, modulename)
extra_objects, sources = filter_files('', '[.](o|a|so|dylib)', sources)
library_dirs, sources = filter_files('-L', '', sources, remove_prefix=1)
libraries, sources = filter_files('-l', '', sources, remove_prefix=1)
undef_macros, sources = filter_files('-U', '', sources, remove_prefix=1)
define_macros, sources = filter_files('-D', '', sources, remove_prefix=1)
for i in range(len(define_macros)):
name_value = define_macros[i].split('=', 1)
if len(name_value) == 1:
name_value.append(None)
if len(name_value) == 2:
define_macros[i] = tuple(name_value)
else:
print('Invalid use of -D:', name_value)
if backend_key == 'meson':
if not pyf_files:
outmess('Using meson backend\nWill pass --lower to f2py\nSee https://numpy.org/doc/stable/f2py/buildtools/meson.html\n')
f2py_flags.append('--lower')
run_main(f" {' '.join(f2py_flags)} -m {modulename} {' '.join(sources)}".split())
else:
run_main(f" {' '.join(f2py_flags)} {' '.join(pyf_files)}".split())
include_dirs, sources = get_includes(sources)
builder = build_backend(
modulename,
sources,
extra_objects,
build_dir,
include_dirs,
library_dirs,
libraries,
define_macros,
undef_macros,
f2py_flags,
sysinfo_flags,
fc_flags,
flib_flags,
setup_flags,
remove_build_dir,
{"dependencies": dependencies},
)
builder.compile()
def validate_modulename(pyf_files, modulename='untitled'):
if len(pyf_files) > 1:
raise ValueError("Only one .pyf file per call")
if pyf_files:
pyff = pyf_files[0]
pyf_modname = auxfuncs.get_f2py_modulename(pyff)
if modulename != pyf_modname:
outmess(
f"Ignoring -m {modulename}.\n"
f"{pyff} defines {pyf_modname} to be the modulename.\n"
)
modulename = pyf_modname
return modulename
def main():
if '--help-link' in sys.argv[1:]:
sys.argv.remove('--help-link')
if MESON_ONLY_VER:
outmess("Use --dep for meson builds\n")
else:
from numpy.distutils.system_info import show_all
show_all()
return
if '-c' in sys.argv[1:]:
run_compile()
else:
run_main(sys.argv[1:])
.\numpy\numpy\f2py\f90mod_rules.py
"""
Build F90 module support for f2py2e.
Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
Copyright 2011 -- present NumPy Developers.
Permission to use, modify, and distribute this software is given under the
terms of the NumPy License.
NO WARRANTY IS EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
"""
__version__ = "$Revision: 1.27 $"[10:-1]
f2py_version = 'See `f2py -v`'
import numpy as np
from . import capi_maps
from . import func2subr
from .crackfortran import undo_rmbadname, undo_rmbadname1
from .auxfuncs import *
options = {}
def findf90modules(m):
if ismodule(m):
return [m]
if not hasbody(m):
return []
ret = []
for b in m['body']:
if ismodule(b):
ret.append(b)
else:
ret = ret + findf90modules(b)
return ret
fgetdims1 = """\
external f2pysetdata
logical ns
integer r,i
integer(%d) s(*)
ns = .FALSE.
if (allocated(d)) then
do i=1,r
if ((size(d,i).ne.s(i)).and.(s(i).ge.0)) then
ns = .TRUE.
end if
end do
if (ns) then
deallocate(d)
end if
end if
if ((.not.allocated(d)).and.(s(1).ge.1)) then""" % np.intp().itemsize
fgetdims2 = """\
end if
if (allocated(d)) then
do i=1,r
s(i) = size(d,i)
end do
end if
flag = 1
call f2pysetdata(d,allocated(d))"""
fgetdims2_sa = """\
end if
if (allocated(d)) then
do i=1,r
s(i) = size(d,i)
end do
!s(r) must be equal to len(d(1))
end if
flag = 2
call f2pysetdata(d,allocated(d))"""
def buildhooks(pymod):
from . import rules
ret = {'f90modhooks': [], 'initf90modhooks': [], 'body': [],
'need': ['F_FUNC', 'arrayobject.h'],
'separatorsfor': {'includes0': '\n', 'includes': '\n'},
'docs': ['"Fortran 90/95 modules:\\n"'],
'latexdoc': []}
fhooks = ['']
def fadd(line, s=fhooks):
s[0] = '%s\n %s' % (s[0], line)
doc = ['']
def dadd(line, s=doc):
s[0] = '%s\n%s' % (s[0], line)
usenames = getuseblocks(pymod)
ret['routine_defs'] = ''
ret['doc'] = []
ret['docshort'] = []
ret['latexdoc'] = doc[0]
if len(ret['docs']) <= 1:
ret['docs'] = ''
return ret, fhooks[0]
.\numpy\numpy\f2py\func2subr.py
"""
Rules for building C/API module with f2py2e.
Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
Copyright 2011 -- present NumPy Developers.
Permission to use, modify, and distribute this software is given under the
terms of the NumPy License.
NO WARRANTY IS EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
"""
import copy
from .auxfuncs import (
getfortranname, isexternal, isfunction, isfunction_wrap, isintent_in,
isintent_out, islogicalfunction, ismoduleroutine, isscalar,
issubroutine, issubroutine_wrap, outmess, show
)
from ._isocbind import isoc_kindmap
def var2fixfortran(vars, a, fa=None, f90mode=None):
if fa is None:
fa = a
if a not in vars:
show(vars)
outmess('var2fixfortran: No definition for argument "%s".\n' % a)
return ''
if 'typespec' not in vars[a]:
show(vars[a])
outmess('var2fixfortran: No typespec for argument "%s".\n' % a)
return ''
vardef = vars[a]['typespec']
if vardef == 'type' and 'typename' in vars[a]:
vardef = '%s(%s)' % (vardef, vars[a]['typename'])
selector = {}
lk = ''
if 'kindselector' in vars[a]:
selector = vars[a]['kindselector']
lk = 'kind'
elif 'charselector' in vars[a]:
selector = vars[a]['charselector']
lk = 'len'
if '*' in selector:
if f90mode:
if selector['*'] in ['*', ':', '(*)']:
vardef = '%s(len=*)' % (vardef)
else:
vardef = '%s(%s=%s)' % (vardef, lk, selector['*'])
else:
if selector['*'] in ['*', ':']:
vardef = '%s*(%s)' % (vardef, selector['*'])
else:
vardef = '%s*%s' % (vardef, selector['*'])
else:
if 'len' in selector:
vardef = '%s(len=%s' % (vardef, selector['len'])
if 'kind' in selector:
vardef = '%s,kind=%s)' % (vardef, selector['kind'])
else:
vardef = '%s)' % (vardef)
elif 'kind' in selector:
vardef = '%s(kind=%s)' % (vardef, selector['kind'])
vardef = '%s %s' % (vardef, fa)
if 'dimension' in vars[a]:
vardef = '%s(%s)' % (vardef, ','.join(vars[a]['dimension']))
return vardef
def useiso_c_binding(rout):
useisoc = False
for key, value in rout['vars'].items():
kind_value = value.get('kindselector', {}).get('kind')
if kind_value in isoc_kindmap:
return True
return useisoc
def createfuncwrapper(rout, signature=0):
assert isfunction(rout)
extra_args = []
vars = rout['vars']
for a in rout['args']:
v = rout['vars'][a]
for i, d in enumerate(v.get('dimension', [])):
if d == ':':
dn = 'f2py_%s_d%s' % (a, i)
dv = dict(typespec='integer', intent=['hide'])
dv['='] = 'shape(%s, %s)' % (a, i)
extra_args.append(dn)
vars[dn] = dv
v['dimension'][i] = dn
rout['args'].extend(extra_args)
need_interface = bool(extra_args)
ret = ['']
def add(line, ret=ret):
ret[0] = '%s\n %s' % (ret[0], line)
name = rout['name']
fortranname = getfortranname(rout)
f90mode = ismoduleroutine(rout)
newname = '%sf2pywrap' % (name)
if newname not in vars:
vars[newname] = vars[name]
args = [newname] + rout['args'][1:]
else:
args = [newname] + rout['args']
l_tmpl = var2fixfortran(vars, name, '@@@NAME@@@', f90mode)
if l_tmpl[:13] == 'character*(*)':
if f90mode:
l_tmpl = 'character(len=10)' + l_tmpl[13:]
else:
l_tmpl = 'character*10' + l_tmpl[13:]
charselect = vars[name]['charselector']
if charselect.get('*', '') == '(*)':
charselect['*'] = '10'
l1 = l_tmpl.replace('@@@NAME@@@', newname)
rl = None
useisoc = useiso_c_binding(rout)
sargs = ', '.join(args)
if f90mode:
sargs = sargs.replace(f"{name}, ", '')
args = [arg for arg in args if arg != name]
rout['args'] = args
add('subroutine f2pywrap_%s_%s (%s)' %
(rout['modulename'], name, sargs))
if not signature:
add('use %s, only : %s' % (rout['modulename'], fortranname))
if useisoc:
add('use iso_c_binding')
else:
add('subroutine f2pywrap%s (%s)' % (name, sargs))
if useisoc:
add('use iso_c_binding')
if not need_interface:
add('external %s' % (fortranname))
rl = l_tmpl.replace('@@@NAME@@@', '') + ' ' + fortranname
if need_interface:
for line in rout['saved_interface'].split('\n'):
if line.lstrip().startswith('use ') and '__user__' not in line:
add(line)
args = args[1:]
dumped_args = []
for a in args:
if isexternal(vars[a]):
add('external %s' % (a))
dumped_args.append(a)
for a in args:
if a in dumped_args:
continue
if isscalar(vars[a]):
add(var2fixfortran(vars, a, f90mode=f90mode))
dumped_args.append(a)
for a in args:
if a in dumped_args:
continue
if isintent_in(vars[a]):
add(var2fixfortran(vars, a, f90mode=f90mode))
dumped_args.append(a)
for a in args:
if a in dumped_args:
continue
add(var2fixfortran(vars, a, f90mode=f90mode))
add(l1)
if rl is not None:
add(rl)
if need_interface:
if f90mode:
pass
else:
add('interface')
add(rout['saved_interface'].lstrip())
add('end interface')
sargs = ', '.join([a for a in args if a not in extra_args])
if not signature:
if islogicalfunction(rout):
add('%s = .not.(.not.%s(%s))' % (newname, fortranname, sargs))
else:
add('%s = %s(%s)' % (newname, fortranname, sargs))
if f90mode:
add('end subroutine f2pywrap_%s_%s' % (rout['modulename'], name))
else:
add('end')
return ret[0]
def createsubrwrapper(rout, signature=0):
assert issubroutine(rout)
extra_args = []
vars = rout['vars']
for a in rout['args']:
v = rout['vars'][a]
for i, d in enumerate(v.get('dimension', [])):
if d == ':':
dn = 'f2py_%s_d%s' % (a, i)
dv = dict(typespec='integer', intent=['hide'])
dv['='] = 'shape(%s, %s)' % (a, i)
extra_args.append(dn)
vars[dn] = dv
v['dimension'][i] = dn
rout['args'].extend(extra_args)
need_interface = bool(extra_args)
ret = ['']
def add(line, ret=ret):
ret[0] = '%s\n %s' % (ret[0], line)
name = rout['name']
fortranname = getfortranname(rout)
f90mode = ismoduleroutine(rout)
args = rout['args']
useisoc = useiso_c_binding(rout)
sargs = ', '.join(args)
if f90mode:
add('subroutine f2pywrap_%s_%s (%s)' %
(rout['modulename'], name, sargs))
if useisoc:
add('use iso_c_binding')
if not signature:
add('use %s, only : %s' % (rout['modulename'], fortranname))
else:
add('subroutine f2pywrap%s (%s)' % (name, sargs))
if useisoc:
add('use iso_c_binding')
if not need_interface:
add('external %s' % (fortranname))
if need_interface:
for line in rout['saved_interface'].split('\n'):
if line.lstrip().startswith('use ') and '__user__' not in line:
add(line)
dumped_args = []
for a in args:
if isexternal(vars[a]):
add('external %s' % (a))
dumped_args.append(a)
for a in args:
if a in dumped_args:
continue
if isscalar(vars[a]):
add(var2fixfortran(vars, a, f90mode=f90mode))
dumped_args.append(a)
for a in args:
if a in dumped_args:
continue
add(var2fixfortran(vars, a, f90mode=f90mode))
if need_interface:
if f90mode:
pass
else:
add('interface')
for line in rout['saved_interface'].split('\n'):
if line.lstrip().startswith('use ') and '__user__' in line:
continue
add(line)
add('end interface')
sargs = ', '.join([a for a in args if a not in extra_args])
if not signature:
add('call %s(%s)' % (fortranname, sargs))
if f90mode:
add('end subroutine f2pywrap_%s_%s' % (rout['modulename'], name))
else:
add('end')
return ret[0]
if isfunction_wrap(rout):
fortranname = getfortranname(rout)
name = rout['name']
outmess('\t\tCreating wrapper for Fortran function "%s"("%s")...\n' % (
name, fortranname))
rout = copy.copy(rout)
fname = name
rname = fname
if 'result' in rout:
rname = rout['result']
rout['vars'][fname] = rout['vars'][rname]
fvar = rout['vars'][fname]
if not isintent_out(fvar):
if 'intent' not in fvar:
fvar['intent'] = []
fvar['intent'].append('out')
flag = 1
for i in fvar['intent']:
if i.startswith('out='):
flag = 0
break
if flag:
fvar['intent'].append('out=%s' % (rname))
rout['args'][:] = [fname] + rout['args']
return rout, createfuncwrapper(rout)
if issubroutine_wrap(rout):
fortranname = getfortranname(rout)
name = rout['name']
outmess('\t\tCreating wrapper for Fortran subroutine "%s"("%s")...\n'
% (name, fortranname))
rout = copy.copy(rout)
return rout, createsubrwrapper(rout)
return rout, ''
.\numpy\numpy\f2py\rules.py
"""
Rules for building C/API module with f2py2e.
Here is a skeleton of a new wrapper function (13Dec2001):
wrapper_function(args)
declarations
get_python_arguments, say, `a' and `b'
get_a_from_python
if (successful) {
get_b_from_python
if (successful) {
callfortran
if (successful) {
put_a_to_python
if (successful) {
put_b_to_python
if (successful) {
buildvalue = ...
}
}
}
}
cleanup_b
}
cleanup_a
return buildvalue
Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
Copyright 2011 -- present NumPy Developers.
Permission to use, modify, and distribute this software is given under the
terms of the NumPy License.
NO WARRANTY IS EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
"""
import os, sys
import time
import copy
from pathlib import Path
from . import __version__
from .auxfuncs import (
applyrules, debugcapi, dictappend, errmess, gentitle, getargs2,
hascallstatement, hasexternals, hasinitvalue, hasnote,
hasresultnote, isarray, isarrayofstrings, ischaracter,
ischaracterarray, ischaracter_or_characterarray, iscomplex,
iscomplexarray, iscomplexfunction, iscomplexfunction_warn,
isdummyroutine, isexternal, isfunction, isfunction_wrap, isint1,
isint1array, isintent_aux, isintent_c, isintent_callback,
isintent_copy, isintent_hide, isintent_inout, isintent_nothide,
isintent_out, isintent_overwrite, islogical, islong_complex,
islong_double, islong_doublefunction, islong_long,
islong_longfunction, ismoduleroutine, isoptional, isrequired,
isscalar, issigned_long_longarray, isstring, isstringarray,
isstringfunction, issubroutine, isattr_value,
issubroutine_wrap, isthreadsafe, isunsigned, isunsigned_char,
isunsigned_chararray, isunsigned_long_long,
isunsigned_long_longarray, isunsigned_short, isunsigned_shortarray,
l_and, l_not, l_or, outmess, replace, stripcomma, requiresf90wrapper
)
from . import capi_maps
from . import cfuncs
from . import common_rules
from . import use_rules
from . import f90mod_rules
from . import func2subr
f2py_version = __version__.version
numpy_version = __version__.version
options = {}
sepdict = {}
for k in ['decl',
'frompyobj',
'cleanupfrompyobj',
'topyarr', 'method',
'pyobjfrom', 'closepyobjfrom',
'freemem',
'userincludes',
'includes0', 'includes', 'typedefs', 'typedefs_generated',
'cppmacros', 'cfuncs', 'callbacks',
'latexdoc',
'restdoc',
'routine_defs', 'externroutines',
'initf2pywraphooks',
'commonhooks', 'initcommonhooks',
'f90modhooks', 'initf90modhooks']:
sepdict[k] = '\n'
generationtime = int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))
module_rules = {
'modulebody': """\
/* File: #modulename#module.c
* This file is auto-generated with f2py (version:#f2py_version#).
* f2py is a Fortran to Python Interface Generator (FPIG), Second Edition,
* written by Pearu Peterson <pearu@cens.ioc.ee>.
* Generation date: """ + time.asctime(time.gmtime(generationtime)) + """
* Do not edit this file directly unless you know what you are doing!!!
*/
#ifdef __cplusplus
extern \"C\" {
#endif
#ifndef PY_SSIZE_T_CLEAN
#define PY_SSIZE_T_CLEAN
#endif /* PY_SSIZE_T_CLEAN */
/* Unconditionally included */
#include <Python.h>
#include <numpy/npy_os.h>
""" + gentitle("See f2py2e/cfuncs.py: includes") + """
#includes#
#includes0#
""" + gentitle("See f2py2e/rules.py: mod_rules['modulebody']") + """
static PyObject *#modulename#_error;
static PyObject *#modulename#_module;
""" + gentitle("See f2py2e/cfuncs.py: typedefs") + """
#typedefs#
""" + gentitle("See f2py2e/cfuncs.py: typedefs_generated") + """
#typedefs_generated#
""" + gentitle("See f2py2e/cfuncs.py: cppmacros") + """
#cppmacros#
""" + gentitle("See f2py2e/cfuncs.py: cfuncs") + """
#cfuncs#
""" + gentitle("See f2py2e/cfuncs.py: userincludes") + """
#userincludes#
""" + gentitle("See f2py2e/capi_rules.py: usercode") + """
#usercode#
/* See f2py2e/rules.py */
#externroutines#
""" + gentitle("See f2py2e/capi_rules.py: usercode1") + """
#usercode1#
""" + gentitle("See f2py2e/cb_rules.py: buildcallback") + """
#callbacks#
""" + gentitle("See f2py2e/rules.py: buildapi") + """
#body#
""" + gentitle("See f2py2e/f90mod_rules.py: buildhooks") + """
#f90modhooks#
""" + gentitle("See f2py2e/rules.py: module_rules['modulebody']") + """
""" + gentitle("See f2py2e/common_rules.py: buildhooks") + """
#commonhooks#
""" + gentitle("See f2py2e/rules.py") + """
static FortranDataDef f2py_routine_defs[] = {
#routine_defs#
{NULL}
};
static PyMethodDef f2py_module_methods[] = {
#pymethoddef#
{NULL,NULL}
};
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"#modulename#",
NULL,
-1,
f2py_module_methods,
NULL,
NULL,
NULL,
NULL
};
PyMODINIT_FUNC PyInit_#modulename#(void) {
int i;
PyObject *m,*d, *s, *tmp;
m = #modulename#_module = PyModule_Create(&moduledef);
Py_SET_TYPE(&PyFortran_Type, &PyType_Type);
import_array();
if (PyErr_Occurred())
{PyErr_SetString(PyExc_ImportError, \"can't initialize module #modulename# (failed to import numpy)\"); return m;}
d = PyModule_GetDict(m);
s = PyUnicode_FromString(\"#f2py_version#\");
PyDict_SetItemString(d, \"__version__\", s);
Py_DECREF(s);
s = PyUnicode_FromString(
\"This module '#modulename#' is auto-generated with f2py (version:#f2py_version#).\\nFunctions:\\n\"\n#docs#\".\");
PyDict_SetItemString(d, \"__doc__\", s);
Py_DECREF(s);
s = PyUnicode_FromString(\"""" + numpy_version + """\");
# 将字符串键为 "__f2py_numpy_version__" 的项设置为变量 s 所指向的对象
PyDict_SetItemString(d, "__f2py_numpy_version__", s);
# 递减引用计数,释放对象 s
Py_DECREF(s);
#modulename#_error = PyErr_NewException ("#modulename#.error", NULL, NULL);
/*
* 将错误对象 #modulename#_error 存储在字典 d 中,以便它可以被释放。
* 在实践中,这是一个模块,所以它可能不会也不应该被释放。
*/
PyDict_SetItemString(d, "_#modulename#_error", #modulename#_error);
# 递减引用计数,释放对象 #modulename#_error
Py_DECREF(#modulename#_error);
for(i=0;f2py_routine_defs[i].name!=NULL;i++) {
// 为 f2py_routine_defs 数组中每个函数定义创建一个 PyFortranObject 对象,并作为属性存储在 tmp 中
tmp = PyFortranObject_NewAsAttr(&f2py_routine_defs[i]);
// 将 tmp 对象添加到字典 d 中,键为 f2py_routine_defs[i].name
PyDict_SetItemString(d, f2py_routine_defs[i].name, tmp);
// 递减引用计数,释放 tmp 对象
Py_DECREF(tmp);
}
# 初始化F2PY包装钩子
defmod_rules = [
{
'body': '/*eof body*/', # 模块主体结束标志
'method': '/*eof method*/', # 方法结束标志
'externroutines': '/*eof externroutines*/', # 外部函数声明结束标志
'routine_defs': '/*eof routine_defs*/', # 函数定义结束标志
'initf90modhooks': '/*eof initf90modhooks*/', # 初始化F90模块钩子结束标志
'initf2pywraphooks': '/*eof initf2pywraphooks*/', # 初始化F2PY包装钩子结束标志
'initcommonhooks': '/*eof initcommonhooks*/', # 初始化公共钩子结束标志
'latexdoc': '', # LaTeX文档内容为空
'restdoc': '', # reStructuredText文档内容为空
'modnote': {hasnote: '#note#', l_not(hasnote): ''}, # 模块注释存在与不存在的情况分别为“#note#”和空字符串
}
]
routine_rules = {
'separatorsfor': sepdict, # 用于分隔符的字典
'body': """
static char doc_
/*
static PyObject *
PyObject *capi_args,
PyObject *capi_keywds,
PyObject * volatile capi_buildvalue = NULL;
volatile int f2py_success = 1;
static char *capi_kwlist[] = {
f2py_start_clock();
if (!PyArg_ParseTupleAndKeywords(capi_args,capi_keywds,\\
\"#argformat#|#keyformat##xaformat#:#pyname#\",\\
capi_kwlist#args_capi##keys_capi##keys_xa#))\n return NULL;
#frompyobj#
/*end of frompyobj*/
#ifdef F2PY_REPORT_ATEXIT
f2py_start_call_clock();
#endif
#callfortranroutine#
if (PyErr_Occurred())
f2py_success = 0;
#ifdef F2PY_REPORT_ATEXIT
f2py_stop_call_clock();
#endif
/*end of callfortranroutine*/
if (f2py_success) {
#pyobjfrom#
/*end of pyobjfrom*/
CFUNCSMESS(\"Building return value.\\n\");
capi_buildvalue = Py_BuildValue(\"#returnformat#\"#return#);
/*closepyobjfrom*/
#closepyobjfrom#
} /*if (f2py_success) after callfortranroutine*/
/*cleanupfrompyobj*/
#cleanupfrompyobj#
if (capi_buildvalue == NULL) {
#routdebugfailure#
} else {
#routdebugleave#
}
CFUNCSMESS(\"Freeing memory.\\n\");
#freemem#
#ifdef F2PY_REPORT_ATEXIT
f2py_stop_clock();
#endif
return capi_buildvalue;
}
#endtitle#
""",
'routine_defs': '#routine_def#',
'initf2pywraphooks': '#initf2pywraphook#',
'externroutines': '#declfortranroutine#',
'doc': '#docreturn##name#(#docsignature#)',
'docshort': '#docreturn##name#(#docsignatureshort#)',
'docs': '" #docreturn##name#(#docsignature#)\\n"\n',
}
'need': ['arrayobject.h', 'CFUNCSMESS', 'MINMAX'],
'cppmacros': {debugcapi: '#define DEBUGCFUNCS'},
'latexdoc': ['\\subsection{Wrapper function \\texttt{#texname#}}\n',
"""
# 此处可能包含更多的 LaTeX 文档内容,未在示例中显示完整
"""
"""
# 定义文档字符串和注释相关的字符串常量和模板
# 注意:这些字符串是用于生成函数文档和注释的模板
# 定义函数的规则和参数配置
rout_rules = [
{ # Init
# 指定不同类型的分隔符,用于各种函数和调试信息的分隔
'separatorsfor': {
'callfortranroutine': '\n',
'routdebugenter': '\n',
'decl': '\n',
'routdebugleave': '\n',
'routdebugfailure': '\n',
'setjmpbuf': ' || ',
'docstrreq': '\n',
'docstropt': '\n',
'docstrout': '\n',
'docstrcbs': '\n',
'docstrsigns': '\\n"\n"',
'latexdocstrsigns': '\n',
'latexdocstrreq': '\n',
'latexdocstropt': '\n',
'latexdocstrout': '\n',
'latexdocstrcbs': '\n',
},
'kwlist': '',
'kwlistopt': '',
'callfortran': '',
'callfortranappend': '',
'docsign': '',
'docsignopt': '',
'decl': '/*decl*/', # 在声明时添加注释
'freemem': '/*freemem*/', # 释放内存时添加注释
'docsignshort': '',
'docsignoptshort': '',
'docstrsigns': '', # 文档字符串中的标志
'latexdocstrsigns': '',
'docstrreq': '\\nParameters\\n----------', # 必需参数的文档字符串格式
'docstropt': '\\nOther Parameters\\n----------------', # 可选参数的文档字符串格式
'docstrout': '\\nReturns\\n-------', # 返回值的文档字符串格式
'docstrcbs': '\\nNotes\\n-----\\nCall-back functions::\\n', # 回调函数的文档字符串格式
'latexdocstrreq': '\\noindent Required arguments:', # LaTeX 格式下的必需参数说明
'latexdocstropt': '\\noindent Optional arguments:', # LaTeX 格式下的可选参数说明
'latexdocstrout': '\\noindent Return objects:', # LaTeX 格式下的返回对象说明
'latexdocstrcbs': '\\noindent Call-back functions:', # LaTeX 格式下的回调函数说明
'args_capi': '',
'keys_capi': '',
'functype': '',
'frompyobj': '/*frompyobj*/', # 从 Python 对象转换时的注释
'cleanupfrompyobj': ['/*end of cleanupfrompyobj*/'], # 清理从 Python 对象转换的结尾注释
'pyobjfrom': '/*pyobjfrom*/', # 转换为 Python 对象时的注释
'closepyobjfrom': ['/*end of closepyobjfrom*/'], # 关闭 Python 对象转换时的结尾注释
'topyarr': '/*topyarr*/', # 转换为数组时的注释
'routdebugleave': '/*routdebugleave*/', # 函数调试离开时的注释
'routdebugenter': '/*routdebugenter*/', # 函数调试进入时的注释
'routdebugfailure': '/*routdebugfailure*/', # 函数调试失败时的注释
'callfortranroutine': '/*callfortranroutine*/', # 调用 Fortran 程序时的注释
'argformat': '',
'keyformat': '',
'need_cfuncs': '',
'docreturn': '', # 文档返回值
'return': '', # 返回值
'returnformat': '',
'rformat': '',
'kwlistxa': '',
'keys_xa': '',
'xaformat': '',
'docsignxa': '',
'docsignxashort': '',
'initf2pywraphook': '',
'routnote': {hasnote: '--- #note#', l_not(hasnote): ''}, # 函数的注释和说明
},
{
'apiname': 'f2py_rout_#modulename#_#name#', # 函数 API 名称
'pyname': '#modulename#.#name#', # Python 函数名称
'decl': '',
'_check': l_not(ismoduleroutine), # 是否为模块化的函数
},
{
'apiname': 'f2py_rout_#modulename#_#f90modulename#_#name#', # 函数 API 名称
'pyname': '#modulename#.#f90modulename#.#name#', # Python 函数名称
'decl': '',
'_check': ismoduleroutine, # 是否为模块化的函数
}
]
}, { # Subroutine
'functype': 'void',
'declfortranroutine': {l_and(l_not(l_or(ismoduleroutine, isintent_c)), l_not(isdummyroutine)): 'extern void #F_FUNC#(#fortranname#,#FORTRANNAME#)(#callprotoargument#);',
l_and(l_not(ismoduleroutine), isintent_c, l_not(isdummyroutine)): 'extern void #fortranname#(#callprotoargument#);',
ismoduleroutine: '', # 如果是模块中的例行程序,不需要任何声明
isdummyroutine: '' # 如果是虚拟例行程序,也不需要任何声明
},
'routine_def': {
l_not(l_or(ismoduleroutine, isintent_c, isdummyroutine)):
' {\"#name#\",-1,{{-1}},0,0,(char *)'
' #F_FUNC#(#fortranname#,#FORTRANNAME#),'
' (f2py_init_func)#apiname#,doc_#apiname#},', # 定义非模块例行程序的例行程序的初始化结构体
l_and(l_not(ismoduleroutine), isintent_c, l_not(isdummyroutine)):
' {\"#name#\",-1,{{-1}},0,0,(char *)#fortranname#,'
' (f2py_init_func)#apiname#,doc_#apiname#},', # 定义 C 意图的例行程序的初始化结构体
l_and(l_not(ismoduleroutine), isdummyroutine):
' {\"#name#\",-1,{{-1}},0,0,NULL,'
' (f2py_init_func)#apiname#,doc_#apiname#},', # 定义虚拟例行程序的初始化结构体
},
'need': {l_and(l_not(l_or(ismoduleroutine, isintent_c)), l_not(isdummyroutine)): 'F_FUNC'}, # 非模块例行程序需要 F_FUNC
'callfortranroutine': [
{debugcapi: [
""" fprintf(stderr,\"debug-capi:Fortran subroutine `#fortranname#(#callfortran#)\'\\n\");"""]},
{hasexternals: """\
if (#setjmpbuf#) {
f2py_success = 0;
} else {"""},
{isthreadsafe: ' Py_BEGIN_ALLOW_THREADS'},
{hascallstatement: ''' #callstatement#;
/*(*f2py_func)(#callfortran#);*/'''},
{l_not(l_or(hascallstatement, isdummyroutine))
: ' (*f2py_func)(#callfortran#);'},
{isthreadsafe: ' Py_END_ALLOW_THREADS'},
{hasexternals: """ }"""}
],
'_check': l_and(issubroutine, l_not(issubroutine_wrap)),
}, {
'functype': 'void',
'declfortranroutine': {l_not(l_or(ismoduleroutine, isdummyroutine)): 'extern void #F_WRAPPEDFUNC#(#name_lower#,#NAME#)(#callprotoargument#);',
isdummyroutine: ''
},
'routine_def': {
l_not(l_or(ismoduleroutine, isdummyroutine)):
' {\"#name#\",-1,{{-1}},0,0,(char *)'
' #F_WRAPPEDFUNC#(#name_lower#,#NAME#),'
' (f2py_init_func)#apiname#,doc_#apiname#},',
isdummyroutine:
' {\"#name#\",-1,{{-1}},0,0,NULL,'
' (f2py_init_func)#apiname#,doc_#apiname#},',
},
'initf2pywraphook': {l_not(l_or(ismoduleroutine, isdummyroutine)): '''
{
// 声明一个外部 C 函数原型,其返回类型为 ctype,函数名为 F_FUNC,参数为 name_lower 和 NAME
extern #ctype# #F_FUNC#(#name_lower#,#NAME#)(void);
// 从 Python 字典 d 中获取键名为 "#name#" 的项,并赋给 PyObject 指针 o
PyObject* o = PyDict_GetItemString(d,"#name#");
// 使用 F2PyCapsule_FromVoidPtr 函数将 #F_FUNC#(#name_lower#,#NAME#) 的返回值转换为 Python capsule 对象 tmp
tmp = F2PyCapsule_FromVoidPtr((void*)#F_FUNC#(#name_lower#,#NAME#),NULL);
// 将 tmp 设置为 o 对象的 "_cpointer" 属性
PyObject_SetAttrString(o,"_cpointer", tmp);
// 释放 tmp 对象的引用计数
Py_DECREF(tmp);
// 创建字符串对象 s,内容为 "#name#",并赋给 s
s = PyUnicode_FromString("#name#");
// 将 s 设置为 o 对象的 "__name__" 属性
PyObject_SetAttrString(o,"__name__", s);
// 释放 s 对象的引用计数
Py_DECREF(s);
}
'''},
// 当 l_not(l_or(ismoduleroutine, isdummyroutine)) 不成立时,需包含 'F_WRAPPEDFUNC' 和 'F_FUNC' 两个字段
'need': {l_not(l_or(ismoduleroutine, isdummyroutine)): ['F_WRAPPEDFUNC', 'F_FUNC']},
// 若 debugcapi 为真,则输出格式化的调试信息到标准错误流 stderr
'callfortranroutine': [
{debugcapi: [
""" fprintf(stderr,\"debug-capi:Fortran subroutine `f2pywrap#name_lower#(#callfortran#)\'\\n\");"""]},
// 若 hasexternals 为真,则根据条件设置 f2py_success 的值
{hasexternals: """\
if (#setjmpbuf#) {
f2py_success = 0;
} else {"""},
// 若 isthreadsafe 为真,则开始允许 Python 线程
{isthreadsafe: ' Py_BEGIN_ALLOW_THREADS'},
// 若 l_not(l_or(hascallstatement, isdummyroutine)) 不成立时,调用指针 f2py_func 指向的函数
{l_not(l_or(hascallstatement, isdummyroutine))
: ' (*f2py_func)(#callfortran#);'},
// 若 hascallstatement 为真时,执行
{hascallstatement:
' #callstatement#;\n /*(*f2py_func)(#callfortran#);*/'},
// 若 isthreadsafe 为真,则结束允许 Python 线程
{isthreadsafe: ' Py_END_ALLOW_THREADS'},
// 若 hasexternals 为真时,结束条件分支
{hasexternals: ' }'}
],
// 检查对象是否为函数包装
'_check': isfunction_wrap,
}, { // Wrapped subroutine
// 函数类型为 void
'functype': 'void',
// 定义 Fortran 子例程声明,根据条件设置不同的值
'declfortranroutine': {l_not(l_or(ismoduleroutine, isdummyroutine)): 'extern void #F_WRAPPEDFUNC#(#name_lower#,#NAME#)(#callprotoargument#);',
isdummyroutine: '',
},
// Fortran 子例程定义,根据条件设置不同的值
'routine_def': {
l_not(l_or(ismoduleroutine, isdummyroutine)):
' {\"#name#\",-1,{{-1}},0,0,(char *)'
' #F_WRAPPEDFUNC#(#name_lower#,#NAME#),'
' (f2py_init_func)#apiname#,doc_#apiname#},',
isdummyroutine:
' {\"#name#\",-1,{{-1}},0,0,NULL,'
' (f2py_init_func)#apiname#,doc_#apiname#},',
},
// 初始化 F2Py 包装钩子,根据条件设置不同的值
'initf2pywraphook': {l_not(l_or(ismoduleroutine, isdummyroutine)): '''
{
// 声明一个外部函数原型 #F_FUNC#,其返回类型为 void,参数为 #name_lower# 和 #NAME#
extern void #F_FUNC#(#name_lower#,#NAME#)(void);
// 从 Python 字典 d 中获取键名为 "#name#" 的项,并赋给 PyObject 指针 o
PyObject* o = PyDict_GetItemString(d,"#name#");
// 使用 F2PyCapsule_FromVoidPtr 函数将 #F_FUNC#(#name_lower#,#NAME#) 的返回值转换为 Python capsule 对象 tmp
tmp = F2PyCapsule_FromVoidPtr((void*)#F_FUNC#(#name_lower#,#NAME#),NULL);
// 将 tmp 设置为 o 对象的 "_cpointer" 属性
PyObject_SetAttrString(o,"_cpointer", tmp);
// 释放 tmp 对象的引用计数
Py_DECREF(tmp);
// 创建字符串对象 s,内容为 "#name#",并赋给 s
s = PyUnicode_FromString("#name#");
// 将 s 设置为 o 对象的 "__name__" 属性
PyObject_SetAttrString(o,"__name__", s);
// 释放 s 对象的引用计数
Py_DECREF(s);
}
'''},
// 当 l_not(l_or(ismoduleroutine, isdummyroutine)) 不成立时,需包含 'F_WRAPPEDFUNC' 和 'F_FUNC' 两个字段
'need': {l_not(l_or(ismoduleroutine, isdummyroutine)): ['F_WRAPPEDFUNC', 'F_FUNC']},
// 调用 Fortran 子例程,根据条件设置不同的值
'callfortranroutine': [
// 若 debugcapi 为真,则输出格式化的调试信息到标准错误流 stderr
{debugcapi: [
""" fprintf(stderr,\"debug-capi:Fortran subroutine `f2pywrap#name_lower#(#callfortran#)\'\\n\");"""]},
// 若 hasexternals 为真,则根据条件设置 f2py_success 的值
{hasexternals: """\
if (#setjmpbuf#) {
f2py_success = 0;
} else {"""},
// 若 isthreadsafe 为真,则开始允许 Python 线程
{isthreadsafe: ' Py_BEGIN_ALLOW_THREADS'},
// 若 l_not(l_or(hascallstatement, isdummyroutine)) 不成立时,调用指针 f2py_func 指向的函数
{l_not(l_or(hascallstatement, isdummyroutine))
: ' (*f2py_func)(#callfortran#);'},
// 若 hascallstatement 为真时,执行
{hascallstatement:
' #callstatement#;\n /*(*f2py_func)(#callfortran#);*/'},
// 若 isthreadsafe 为真,则结束允许 Python 线程
{isthreadsafe: ' Py_END_ALLOW_THREADS'},
// 若 hasexternals 为真时,结束条件分支
{hasexternals: ' }'}
],
// 检查对象是否为函数包装
'_check': isfunction_wrap,
}
} else {"""
# 如果不是线程安全的情况下,进行特定处理
{isthreadsafe: ' Py_BEGIN_ALLOW_THREADS'},
# 如果不是调用语句或者是虚拟例程,调用Fortran函数
{l_not(l_or(hascallstatement, isdummyroutine))
: ' (*f2py_func)(#callfortran#);'},
# 如果有调用语句,执行调用语句并注释掉调用Fortran函数
{hascallstatement:
' #callstatement#;\n /*(*f2py_func)(#callfortran#);*/'},
# 如果是线程安全的情况下,结束线程保护区域
{isthreadsafe: ' Py_END_ALLOW_THREADS'},
# 如果有外部引用,进行特定处理
{hasexternals: ' }'}
],
'_check': issubroutine_wrap,
}, { # Function
'functype': '#ctype#',
'docreturn': {l_not(isintent_hide): '#rname#,'},
'docstrout': '#pydocsignout#',
'latexdocstrout': ['\\item[]{{}\\verb@#pydocsignout#@{}}',
{hasresultnote: '--- #resultnote#'}],
'callfortranroutine': [{l_and(debugcapi, isstringfunction): """\
// 如果定义了 USESCOMPAQFORTRAN 宏,则打印使用 Compaq Fortran 的调试信息
fprintf(stderr,"debug-capi:Fortran function #ctype# #fortranname#(#callcompaqfortran#)\n");
// 否则,打印使用普通 Fortran 的调试信息
fprintf(stderr,"debug-capi:Fortran function #ctype# #fortranname#(#callfortran#)\n");
'''},
{l_not(l_or(hascallstatement, isdummyroutine))
: ' #name#_return_value = (*f2py_func)(#callfortran#);'},
{isthreadsafe: ' Py_END_ALLOW_THREADS'},
{hasexternals: ' }'},
{l_and(debugcapi, iscomplexfunction)
: ' fprintf(stderr,"#routdebugshowvalue#\\n",#name#_return_value.r,#name#_return_value.i);'},
{l_and(debugcapi, l_not(iscomplexfunction)): ' fprintf(stderr,"#routdebugshowvalue#\\n",#name#_return_value);'}],
'pyobjfrom': {iscomplexfunction: ' #name#_return_value_capi = pyobj_from_#ctype#1(#name#_return_value);'},
'need': [{l_not(isdummyroutine): 'F_FUNC'},
{iscomplexfunction: 'pyobj_from_#ctype#1'},
{islong_longfunction: 'long_long'},
{islong_doublefunction: 'long_double'}],
'returnformat': {l_not(isintent_hide): '#rformat#'},
'return': {iscomplexfunction: ',#name#_return_value_capi',
l_not(l_or(iscomplexfunction, isintent_hide)): ',#name#_return_value'},
'_check': l_and(isfunction, l_not(isstringfunction), l_not(isfunction_wrap))
}, { # String function # in use for --no-wrap
'declfortranroutine': 'extern void #F_FUNC#(#fortranname#,#FORTRANNAME#)(#callprotoargument#);',
'routine_def': {l_not(l_or(ismoduleroutine, isintent_c)):
' {\"#name#\",-1,{{-1}},0,0,(char *)#F_FUNC#(#fortranname#,#FORTRANNAME#),(f2py_init_func)#apiname#,doc_#apiname#},',
l_and(l_not(ismoduleroutine), isintent_c):
' {\"#name#\",-1,{{-1}},0,0,(char *)#fortranname#,(f2py_init_func)#apiname#,doc_#apiname#},'
},
'decl': [' #ctype# #name#_return_value = NULL;', # 声明返回值变量
' int #name#_return_value_len = 0;'], # 声明返回值长度变量
'callfortran':'#name#_return_value,#name#_return_value_len,', # 调用Fortran函数时传递返回值和长度
'callfortranroutine':[' #name#_return_value_len = #rlength#;', # 设置返回值的长度
' if ((#name#_return_value = (string)malloc(' # 分配返回值内存
+ '#name#_return_value_len+1) == NULL) {', # 检查内存分配是否成功
' PyErr_SetString(PyExc_MemoryError, \"out of memory\");', # 内存分配失败时设置异常
' f2py_success = 0;', # 设置f2py_success为0表示失败
' } else {', # 如果内存分配成功
" (#name#_return_value)[#name#_return_value_len] = '\\0';", # 将返回值字符串结束符设为'\0'
' }',
' if (f2py_success) {', # 如果f2py执行成功
{hasexternals: """\
if (#setjmpbuf#) { # 如果存在externals并且setjmpbuf为真
f2py_success = 0; # 设置f2py_success为0表示失败
} else {"""}, # 否则开始执行以下操作
{isthreadsafe: ' Py_BEGIN_ALLOW_THREADS'}, # 如果是线程安全的,则开始线程
"""\
#ifdef USESCOMPAQFORTRAN
(*f2py_func)(#callcompaqfortran#); # 调用Compaq Fortran代码段
#else
(*f2py_func)(#callfortran#); # 调用Fortran代码段
#endif
[
{ # Code block definition for C/API function argument rules
'separatorsfor': sepdict
},
{ # Common rules for processing auxiliary variables from Python objects
'frompyobj': [
' /* Processing auxiliary variable #varname# */',
{debugcapi: ' fprintf(stderr,"#vardebuginfo#\\n");'},
],
'cleanupfrompyobj': ' /* End of cleaning variable #varname# */',
'need': typedef_need_dict,
},
{ # Declaration and initialization of non-complex scalar variables
'decl': ' #ctype# #varname# = 0;',
'need': {hasinitvalue: 'math.h'},
'frompyobj': {hasinitvalue: ' #varname# = #init#;'},
'_check': l_and(isscalar, l_not(iscomplex)),
},
{ # Return statement configuration for non-complex scalar variables
'return': ',#varname#',
'docstrout': '#pydocsignout#',
'docreturn': '#outvarname#,',
'returnformat': '#varrformat#',
'_check': l_and(isscalar, l_not(iscomplex), isintent_out),
},
{ # Declaration of complex scalar variables
'decl': ' #ctype# #varname#;',
'frompyobj': {hasinitvalue: ' #varname#.r = #init.r#, #varname#.i = #init.i#;'},
'_check': iscomplex
}
]
# String
{ # Common
'decl': [' #ctype# #varname# = NULL;', # 声明一个空指针,类型为 #ctype#
' int slen(#varname#);', # 声明一个函数原型,函数名为 slen,参数为 #varname#,返回类型为 int
],
'need':['len..'], # 需要长度信息
'_check': isstring # 执行 isstring 函数进行检查
},
# Array
{ # Common
'decl': [' #ctype# *#varname# = NULL;', # 声明一个空指针,类型为 #ctype#
' npy_intp #varname#_Dims[#rank#] = {#rank*[-1]#};', # 声明一个数组,其维度为 #rank#,每个维度大小为 -1
' const int #varname#_Rank = #rank#;', # 声明一个常量整数,值为 #rank#
],
'need':['len..', {hasinitvalue: 'forcomb'}, {hasinitvalue: 'CFUNCSMESS'}], # 需要长度信息,以及某些初始化值
'_check': isarray # 执行 isarray 函数进行检查
},
# Scalararray
{ # Common
'_check': l_and(isarray, l_not(iscomplexarray)) # 检查是否是标量数组且不是复数数组
}, { # Not hidden
'_check': l_and(isarray, l_not(iscomplexarray), isintent_nothide) # 检查是否是标量数组、非复数数组且意图不隐藏
},
# Integer*1 array
{'need': '#ctype#', # 需要类型为 #ctype# 的数据
'_check': isint1array, # 执行 isint1array 函数进行检查
'_depend': '' # 依赖为空
},
# Integer*-1 array
{'need': '#ctype#', # 需要类型为 #ctype# 的数据
'_check': l_or(isunsigned_chararray, isunsigned_char), # 检查是否是无符号 char 数组或者无符号 char
'_depend': '' # 依赖为空
},
# Integer*-2 array
{'need': '#ctype#', # 需要类型为 #ctype# 的数据
'_check': isunsigned_shortarray, # 检查是否是无符号 short 数组
'_depend': '' # 依赖为空
},
# Integer*-8 array
{'need': '#ctype#', # 需要类型为 #ctype# 的数据
'_check': isunsigned_long_longarray, # 检查是否是无符号 long long 数组
'_depend': '' # 依赖为空
},
# Complexarray
{'need': '#ctype#', # 需要类型为 #ctype# 的数据
'_check': iscomplexarray, # 检查是否是复数数组
'_depend': '' # 依赖为空
},
# Stringarray
{
'callfortranappend': {isarrayofstrings: 'flen(#varname#),'}, # 如果是字符串数组,调用 Fortran 函数追加字符串长度信息
'need': 'string', # 需要字符串类型
'_check': isstringarray # 检查是否是字符串数组
}
]
arg_rules = [
{ # 定义一个字典,用于指定不同规则的参数处理方式
'separatorsfor': sepdict # 指定分隔符的字典,用于某些处理
},
{ # 常见规则
'frompyobj': [ # 从 Python 对象转换过来的处理
' /* Processing variable #varname# */', # 处理变量 #varname# 的过程注释
{debugcapi: ' fprintf(stderr,"#vardebuginfo#\\n");'}, # 如果启用了 debugcapi,输出变量的调试信息到 stderr
],
'cleanupfrompyobj': ' /* End of cleaning variable #varname# */', # 清理变量 #varname# 结束的注释
'_depend': '', # 依赖项为空字符串
'need': typedef_need_dict, # 指定需要的类型定义的字典
},
# 文档签名
{
'docstropt': {l_and(isoptional, isintent_nothide): '#pydocsign#'}, # 可选参数且意图不隐藏时的文档字符串签名
'docstrreq': {l_and(isrequired, isintent_nothide): '#pydocsign#'}, # 必需参数且意图不隐藏时的文档字符串签名
'docstrout': {isintent_out: '#pydocsignout#'}, # 输出参数的文档字符串签名
'latexdocstropt': { # 可选参数且意图不隐藏时的 LaTeX 文档字符串签名
l_and(isoptional, isintent_nothide): ['\\item[]{{}\\verb@#pydocsign#@{}}', # LaTeX 格式化输出
{hasnote: '--- #note#'} # 如果有注释,添加注释信息
]
},
'latexdocstrreq': { # 必需参数且意图不隐藏时的 LaTeX 文档字符串签名
l_and(isrequired, isintent_nothide): ['\\item[]{{}\\verb@#pydocsign#@{}}', # LaTeX 格式化输出
{hasnote: '--- #note#'} # 如果有注释,添加注释信息
]
},
'latexdocstrout': { # 输出参数的 LaTeX 文档字符串签名
isintent_out: ['\\item[]{{}\\verb@#pydocsignout#@{}}', # LaTeX 格式化输出
{l_and(hasnote, isintent_hide): '--- #note#', # 如果有注释且意图隐藏,添加注释信息
l_and(hasnote, isintent_nothide): '--- See above.' # 如果有注释且意图不隐藏,指向上述信息
}]
},
'depend': '' # 依赖项为空字符串
},
# 必需/可选参数
{
'kwlist': '"#varname#",', # 关键字参数列表的格式化输出
'docsign': '#varname#,', # 文档签名中的变量名格式化输出
'_check': l_and(isintent_nothide, l_not(isoptional)) # 检查意图不隐藏且不是可选参数的条件
},
{
'kwlistopt': '"#varname#",', # 可选关键字参数列表的格式化输出
'docsignopt': '#varname#=#showinit#,', # 可选参数的文档签名格式化输出(包括初始值)
'docsignoptshort': '#varname#,', # 简短格式的可选参数的文档签名格式化输出
'_check': l_and(isintent_nothide, isoptional) # 检查意图不隐藏且是可选参数的条件
},
# 文档字符串/BuildValue
{
'docreturn': '#outvarname#,', # 返回值的文档字符串格式化输出
'returnformat': '#varrformat#', # 返回格式的变量格式化输出
'_check': isintent_out # 检查是否是输出参数的条件
},
# 外部调用(回调函数)
{ # Common
'docsignxa': {isintent_nothide: '#varname#_extra_args=(),'},
# 定义 'docsignxa' 键的值为包含 isintent_nothide 的字典,表示额外参数默认为空元组。
'docsignxashort': {isintent_nothide: '#varname#_extra_args,'},
# 定义 'docsignxashort' 键的值为包含 isintent_nothide 的字典,表示额外参数的简短形式。
'docstropt': {isintent_nothide: '#varname#_extra_args : input tuple, optional\\n Default: ()'},
# 定义 'docstropt' 键的值为包含 isintent_nothide 的字典,表示可选输入元组形式的额外参数,默认为空。
'docstrcbs': '#cbdocstr#',
# 定义 'docstrcbs' 键的值为 '#cbdocstr#',可能用于文档字符串中的回调描述。
'latexdocstrcbs': '\\item[] #cblatexdocstr#',
# 定义 'latexdocstrcbs' 键的值为 LaTeX 风格的表项,使用 #cblatexdocstr# 描述。
'latexdocstropt': {isintent_nothide: '\\item[]{{}\\verb@#varname#_extra_args := () input tuple@{}} --- Extra arguments for call-back function {{}\\verb@#varname#@{}}.'},
# 定义 'latexdocstropt' 键的值为包含 isintent_nothide 的字典,为回调函数描述提供 LaTeX 格式的额外参数说明。
'decl': [' #cbname#_t #varname#_cb = { Py_None, NULL, 0 };',
' #cbname#_t *#varname#_cb_ptr = &#varname#_cb;',
' PyTupleObject *#varname#_xa_capi = NULL;',
{l_not(isintent_callback):
' #cbname#_typedef #varname#_cptr;'}
],
# 定义 'decl' 键的值为包含多个声明字符串的列表,其中包括回调函数的默认初始化、指针和可能的类型定义。
'kwlistxa': {isintent_nothide: '"#varname#_extra_args",'},
# 定义 'kwlistxa' 键的值为包含 isintent_nothide 的字典,表示关键字参数列表中包含额外参数的名称。
'argformat': {isrequired: 'O'},
# 定义 'argformat' 键的值为包含 isrequired 的字典,指示参数的格式要求为一个对象。
'keyformat': {isoptional: 'O'},
# 定义 'keyformat' 键的值为包含 isoptional 的字典,指示关键字的格式要求为一个对象。
'xaformat': {isintent_nothide: 'O!'},
# 定义 'xaformat' 键的值为包含 isintent_nothide 的字典,指示额外参数的格式要求为对象且必须存在。
'args_capi': {isrequired: ',&#varname#_cb.capi'},
# 定义 'args_capi' 键的值为包含 isrequired 的字典,表示参数列表中包含 C API 的参数。
'keys_capi': {isoptional: ',&#varname#_cb.capi'},
# 定义 'keys_capi' 键的值为包含 isoptional 的字典,表示关键字参数中包含 C API 的参数。
'keys_xa': ',&PyTuple_Type,&#varname#_xa_capi',
# 定义 'keys_xa' 键的值为字符串,包含元组类型的 Pyhton 类型和对应的 C API 参数。
'setjmpbuf': '(setjmp(#varname#_cb.jmpbuf))',
# 定义 'setjmpbuf' 键的值为 setjmp 函数对给定的跳转缓冲区(jmpbuf)进行调用。
'callfortran': {l_not(isintent_callback): '#varname#_cptr,'},
# 定义 'callfortran' 键的值为包含 l_not(isintent_callback) 的字典,表示用于调用 Fortran 的函数指针。
'need': ['#cbname#', 'setjmp.h'],
# 定义 'need' 键的值为列表,包含所需的回调函数和头文件 'setjmp.h'。
'_check': isexternal
# 定义 '_check' 键的值为 isexternal,可能用于指示外部检查的标志。
},
{ # Specific block
'frompyobj': [{l_not(isintent_callback): """\
# 检查是否符合 F2PyCapsule 的检查条件
if(F2PyCapsule_Check(#varname#_cb.capi)) {
#varname#_cptr = F2PyCapsule_AsVoidPtr(#varname#_cb.capi);
} else {
#varname#_cptr = #cbname#;
}
# 如果回调函数的 C API 是 Py_None,则尝试从指定模块中获取回调函数 #varname#
if (#varname#_cb.capi==Py_None) {
#varname#_cb.capi = PyObject_GetAttrString(#modulename#_module,\"#varname#\");
if (#varname#_cb.capi) {
# 检查是否需要额外的参数
if (#varname#_xa_capi==NULL) {
# 如果模块中存在 #varname#_extra_args 属性,则将其转换为元组
if (PyObject_HasAttrString(#modulename#_module,\"#varname#_extra_args\")) {
PyObject* capi_tmp = PyObject_GetAttrString(#modulename#_module,\"#varname#_extra_args\");
if (capi_tmp) {
#varname#_xa_capi = (PyTupleObject *)PySequence_Tuple(capi_tmp);
Py_DECREF(capi_tmp);
}
else {
# 如果未能成功获取 #varname#_extra_args 属性,则创建一个空元组
#varname#_xa_capi = (PyTupleObject *)Py_BuildValue(\"()\");
}
# 检查是否成功转换为元组
if (#varname#_xa_capi==NULL) {
PyErr_SetString(#modulename#_error,\"Failed to convert #modulename#.#varname#_extra_args to tuple.\\n\");
return NULL;
}
}
}
}
# 如果未能成功获取回调函数 #varname#,则报错
if (#varname#_cb.capi==NULL) {
PyErr_SetString(#modulename#_error,\"Callback #varname# not defined (as an argument or module #modulename# attribute).\\n\");
return NULL;
}
}
# 创建回调函数的参数列表,如果失败则报错
if (create_cb_arglist(#varname#_cb.capi,#varname#_xa_capi,#maxnofargs#,#nofoptargs#,&#varname#_cb.nofargs,&#varname#_cb.args_capi,\"failed in processing argument list for call-back #varname#.\")) {
# 输出调试信息,显示回调函数的预期参数数量
fprintf(stderr,\"debug-capi:Assuming %d arguments; at most #maxnofargs#(-#nofoptargs#) is expected.\\n\",#varname#_cb.nofargs);
# 打印调试信息,显示回调函数的当前值
CFUNCSMESSPY(\"for #varname#=\",#varname#_cb.capi);
# 打印消息,表示正在保存回调函数的变量
CFUNCSMESS(\"Saving callback variables for `#varname#`.\\n\");
# 交换活跃的回调函数指针
#varname#_cb_ptr = swap_active_#cbname#(#varname#_cb_ptr);
# 打印消息,表示正在恢复回调函数的变量
CFUNCSMESS(\"Restoring callback variables for `#varname#`.\\n\");
# 交换活跃的回调函数指针
#varname#_cb_ptr = swap_active_#cbname#(#varname#_cb_ptr);
# 释放回调函数参数列表的 Python 对象引用
Py_DECREF(#varname#_cb.args_capi);
}, { # Not hidden
'decl': ' PyObject *#varname#_capi = Py_None;',
# 声明一个名为 #varname#_capi 的 PyObject 指针变量,初始化为 Py_None
'argformat': {isrequired: 'O'},
# 指定参数格式,isrequired 对应 'O' 表示必须的参数类型为 PyObject
'keyformat': {isoptional: 'O'},
# 指定关键字参数格式,isoptional 对应 'O' 表示可选的参数类型为 PyObject
'args_capi': {isrequired: ',&#varname#_capi'},
# 定义参数列表中的 #varname#_capi,作为必需参数
'keys_capi': {isoptional: ',&#varname#_capi'},
# 定义关键字参数中的 #varname#_capi,作为可选参数
'pyobjfrom': {isintent_inout: """\
f2py_success = try_pyarr_from_#ctype#(#varname#_capi,&#varname#);
if (f2py_success) {"""},
# 根据 isintent_inout 条件,执行从 PyObject 到 #ctype# 的转换,检查转换成功后的处理逻辑
'closepyobjfrom': {isintent_inout: " } /*if (f2py_success) of #varname# pyobjfrom*/"},
# 根据 isintent_inout 条件,关闭 PyObject 到 #ctype# 的转换逻辑
'need': {isintent_inout: 'try_pyarr_from_#ctype#'},
# 指定需要进行 PyObject 到 #ctype# 转换的条件
'_check': l_and(isscalar, l_not(iscomplex), l_not(isstring),
isintent_nothide)
# 检查条件,确保是标量、非复数、非字符串,并且不隐藏意图
}, {
'frompyobj': [
# 根据条件不同,执行不同的赋值逻辑,从 PyObject 到变量 #varname# 的转换
{hasinitvalue: ' if (#varname#_capi == Py_None) #varname# = #init#; else',
'_depend': ''},
{l_and(isoptional, l_not(hasinitvalue)): ' if (#varname#_capi != Py_None)',
'_depend': ''},
{l_not(islogical): '''\
f2py_success =
if (f2py_success) {'''},
{islogical: '''\
f2py_success = 1;
if (f2py_success) {'''},
],
# 根据不同的条件,执行从 PyObject 到 #ctype# 的转换逻辑,检查转换成功后的处理逻辑
'cleanupfrompyobj': ' } /*if (f2py_success) of #varname#*/',
# 执行从 PyObject 到 #ctype# 的转换后,进行清理逻辑
'need': {l_not(islogical): '#ctype#_from_pyobj'},
# 指定需要进行 PyObject 到 #ctype# 转换的条件
'_check': l_and(isscalar, l_not(iscomplex), isintent_nothide),
# 检查条件,确保是标量、非复数,并且不隐藏意图
'_depend': ''
}, { # Hidden
'frompyobj': {hasinitvalue: ' #varname# = #init#;'},
# 根据 hasinitvalue 条件,执行隐藏意图的赋值逻辑,从 PyObject 到变量 #varname# 的转换
'need': typedef_need_dict,
# 指定需要的 typedef 字典
'_check': l_and(isscalar, l_not(iscomplex), isintent_hide),
# 检查条件,确保是标量、非复数,并且隐藏意图
'_depend': ''
}, { # Common
'frompyobj': {debugcapi: ' fprintf(stderr,"#vardebugshowvalue#\\n",#varname#);'},
# 根据 debugcapi 条件,执行常见的从 PyObject 到 #varname# 的转换逻辑
'_check': l_and(isscalar, l_not(iscomplex)),
# 检查条件,确保是标量、非复数
'_depend': ''
},
# Complex scalars
{ # Common
'decl': ' #ctype# #varname#;',
# 声明复杂标量类型的 #varname# 变量
'callfortran': {isintent_c: '#varname#,', l_not(isintent_c): '&#varname#,'},
# 根据 isintent_c 条件,生成调用 Fortran 代码的字符串
'pyobjfrom': {debugcapi: ' fprintf(stderr,"#vardebugshowvalue#\\n",#varname#.r,#varname#.i);'},
# 根据 debugcapi 条件,执行复杂标量类型的从 PyObject 到 #varname# 的转换逻辑
'return': {isintent_out: ',#varname#_capi'},
# 根据 isintent_out 条件,指定返回 #varname#_capi 的值
'_check': iscomplex
# 检查条件,确保是复杂标量
}, { # Not hidden
'decl': ' PyObject *#varname#_capi = Py_None;', # 声明一个名为 #varname#_capi 的 PyObject 指针变量,初始化为 Py_None
'argformat': {isrequired: 'O'}, # 参数格式化信息,要求为必需参数,格式为 'O'
'keyformat': {isoptional: 'O'}, # 关键字格式化信息,要求为可选参数,格式为 'O'
'args_capi': {isrequired: ',&#varname#_capi'}, # 参数的 C API 格式化信息,要求为必需参数
'keys_capi': {isoptional: ',&#varname#_capi'}, # 关键字的 C API 格式化信息,要求为可选参数
'need': {isintent_inout: 'try_pyarr_from_#ctype#'}, # 需要的信息,表明是输入输出参数,需要调用 try_pyarr_from_#ctype#
'pyobjfrom': {isintent_inout: """\ # 从 Python 对象转换而来的信息,表明是输入输出参数
f2py_success = try_pyarr_from_#ctype#(#varname#_capi,&#varname#);
if (f2py_success) {"""},
'closepyobjfrom': {isintent_inout: " } /*if (f2py_success) of #varname# pyobjfrom*/"}, # 关闭从 Python 对象转换的操作
'_check': l_and(iscomplex, isintent_nothide) # 检查条件,复杂类型且非隐藏意图
}, {
'frompyobj': [{hasinitvalue: ' if (#varname#_capi==Py_None) {#varname#.r = #init.r#, #varname#.i = #init.i#;} else'}, # 从 Python 对象转换而来的操作,具有初始值的情况下处理
{l_and(isoptional, l_not(hasinitvalue)): ' if (#varname#_capi != Py_None)'}, # 可选参数且无初始值的情况下处理
' f2py_success = #ctype#_from_pyobj(&#varname#,#varname#_capi,"#pyname#() #nth# (#varname#) can\'t be converted to #ctype#");'
'\n if (f2py_success) {'], # 调用函数尝试从 Python 对象转换为给定类型,如果成功则继续执行
'cleanupfrompyobj': ' } /*if (f2py_success) of #varname# frompyobj*/', # 清理从 Python 对象转换操作的代码块
'need': ['#ctype#_from_pyobj'], # 需要的信息,调用了 #ctype#_from_pyobj 函数
'_check': l_and(iscomplex, isintent_nothide), # 检查条件,复杂类型且非隐藏意图
'_depend': '' # 依赖信息为空
}, { # Hidden
'decl': {isintent_out: ' PyObject *#varname#_capi = Py_None;'}, # 隐藏的声明,输出意图的 PyObject 指针变量,初始化为 Py_None
'_check': l_and(iscomplex, isintent_hide) # 检查条件,复杂类型且隐藏意图
}, {
'frompyobj': {hasinitvalue: ' #varname#.r = #init.r#, #varname#.i = #init.i#;'}, # 从 Python 对象转换而来的操作,具有初始值的情况下处理
'_check': l_and(iscomplex, isintent_hide), # 检查条件,复杂类型且隐藏意图
'_depend': '' # 依赖信息为空
}, { # Common
'pyobjfrom': {isintent_out: ' #varname#_capi = pyobj_from_#ctype#1(#varname#);'}, # 通用的从 Python 对象转换操作,输出意图
'need': ['pyobj_from_#ctype#1'], # 需要的信息,调用了 pyobj_from_#ctype#1 函数
'_check': iscomplex # 检查条件,复杂类型
}, {
'frompyobj': {debugcapi: ' fprintf(stderr,"#vardebugshowvalue#\\n",#varname#.r,#varname#.i);'}, # 从 Python 对象转换而来的操作,输出调试信息
'_check': iscomplex, # 检查条件,复杂类型
'_depend': '' # 依赖信息为空
},
# String
{ # Common
'decl': [' #ctype# #varname# = NULL;', # 声明一个指向 #ctype# 类型的变量 #varname#,初始化为 NULL
' int slen(#varname#);', # 声明一个名为 slen 的整数变量,初始化为调用函数 slen(#varname#)
' PyObject *#varname#_capi = Py_None;'], # 声明一个名为 #varname#_capi 的 PyObject 指针变量,初始化为 Py_None
'callfortran':'#varname#,', # 调用 Fortran 函数时的参数信息
'callfortranappend':'slen(#varname#),', # 调用 Fortran 函数时追加的参数信息,调用函数 slen(#varname#)
'pyobjfrom':[
{debugcapi:
' fprintf(stderr,'
'"#vardebugshowvalue#\\n",slen(#varname#),#varname#);'}, # 从 Python 对象转换而来的操作,输出调试信息
{l_and(isintent_out, l_not(isintent_c)):
" STRINGPADN(#varname#, slen(#varname#), ' ', '\\0');"}, # 从 Python 对象转换而来的操作,处理输出意图且非 C 类型的情况
],
'return': {isintent_out: ',#varname#'}, # 返回值的信息,输出意图
'need': ['len..', # 需要的信息,包含字符串长度操作
{l_and(isintent_out, l_not(isintent_c)): 'STRINGPADN'}], # 需要的信息,处理输出意图且非 C 类型的情况
'_check': isstring # 检查条件,字符串类型
}, { # Common
'frompyobj': [
"""\
slen(#varname#) = #elsize#;
f2py_success = #ctype#_from_pyobj(&#varname#,&slen(#varname#),#init#,""" # 从 Python 对象转换而来的操作,设置字符串长度并调用函数尝试转换为给定类型
"""#varname#_capi,\"#ctype#_from_pyobj failed in converting #nth#"""
"""`#varname#\' of #pyname# to C #ctype#\");
if (f2py_success) {""",
# The trailing null value for Fortran is blank.
{l_not(isintent_c):
" STRINGPADN(#varname#, slen(#varname#), '\\0', ' ');"},
],
'cleanupfrompyobj': """\
STRINGFREE(#varname#);
} /*if (f2py_success) of #varname#*/""",
'need': ['#ctype#_from_pyobj', 'len..', 'STRINGFREE',
{l_not(isintent_c): 'STRINGPADN'}],
'_check':isstring,
'_depend':''
}, { # Not hidden
'argformat': {isrequired: 'O'},
'keyformat': {isoptional: 'O'},
'args_capi': {isrequired: ',&#varname#_capi'},
'keys_capi': {isoptional: ',&#varname#_capi'},
'pyobjfrom': [
{l_and(isintent_inout, l_not(isintent_c)):
" STRINGPADN(#varname#, slen(#varname#), ' ', '\\0');"},
{isintent_inout: '''\
f2py_success = try_pyarr_from_
slen(
if (f2py_success) {'''}],
'closepyobjfrom': {isintent_inout: ' } /*if (f2py_success) of #varname# pyobjfrom*/'},
'need': {isintent_inout: 'try_pyarr_from_#ctype#',
l_and(isintent_inout, l_not(isintent_c)): 'STRINGPADN'},
'_check': l_and(isstring, isintent_nothide)
}, { # Hidden
'_check': l_and(isstring, isintent_hide)
}, {
'frompyobj': {debugcapi: ' fprintf(stderr,"#vardebugshowvalue#\\n",slen(#varname#),#varname#);'},
'_check': isstring,
'_depend': ''
},
# Array
{ # Common
'decl': [' #ctype# *#varname# = NULL;',
' npy_intp #varname#_Dims[#rank#] = {#rank*[-1]#};',
' const int #varname#_Rank = #rank#;',
' PyArrayObject *capi_#varname#_as_array = NULL;',
' int capi_#varname#_intent = 0;',
{isstringarray: ' int slen(#varname#) = 0;'},
],
'callfortran':'#varname#,',
'callfortranappend': {isstringarray: 'slen(#varname#),'},
'return': {isintent_out: ',capi_#varname#_as_array'},
'need': 'len..',
'_check': isarray
}, { # intent(overwrite) array
'decl': ' int capi_overwrite_#varname# = 1;',
'kwlistxa': '"overwrite_#varname#",',
'xaformat': 'i',
'keys_xa': ',&capi_overwrite_#varname#',
'docsignxa': 'overwrite_#varname#=1,',
'docsignxashort': 'overwrite_#varname#,',
'docstropt': 'overwrite_#varname# : input int, optional\\n Default: 1',
'_check': l_and(isarray, isintent_overwrite),
}, {
'frompyobj': ' capi_#varname#_intent |= (capi_overwrite_#varname#?0:F2PY_INTENT_COPY);',
'_check': l_and(isarray, isintent_overwrite),
'_depend': '',
},
{ # intent(copy) array
'decl': ' int capi_overwrite_#varname# = 0;',
'kwlistxa': '"overwrite_#varname#",',
'xaformat': 'i',
'keys_xa': ',&capi_overwrite_#varname#',
'docsignxa': 'overwrite_#varname#=0,',
'docsignxashort': 'overwrite_#varname#,',
'docstropt': 'overwrite_#varname# : input int, optional\\n Default: 0',
'_check': l_and(isarray, isintent_copy),
}, {
'frompyobj': ' capi_#varname#_intent |= (capi_overwrite_#varname#?0:F2PY_INTENT_COPY);',
'_check': l_and(isarray, isintent_copy),
'_depend': '',
}, {
'need': [{hasinitvalue: 'forcomb'}, {hasinitvalue: 'CFUNCSMESS'}],
'_check': isarray,
'_depend': ''
}, { # Not hidden
'decl': ' PyObject *#varname#_capi = Py_None;',
'argformat': {isrequired: 'O'},
'keyformat': {isoptional: 'O'},
'args_capi': {isrequired: ',&#varname#_capi'},
'keys_capi': {isoptional: ',&#varname#_capi'},
'_check': l_and(isarray, isintent_nothide)
}, {
'frompyobj': [
' #setdims#;',
' capi_#varname#_intent |= #intent#;',
(' const char * capi_errmess = "#modulename#.#pyname#:'
' failed to create array from the #nth# `#varname#`";'),
{isintent_hide:
' capi_#varname#_as_array = ndarray_from_pyobj('
' #atype#,#elsize#,#varname#_Dims,#varname#_Rank,'
' capi_#varname#_intent,Py_None,capi_errmess);'},
{isintent_nothide:
' capi_#varname#_as_array = ndarray_from_pyobj('
' #atype#,#elsize#,#varname#_Dims,#varname#_Rank,'
' capi_#varname#_intent,#varname#_capi,capi_errmess);'},
"""\
if (capi_#varname#_as_array == NULL) {
PyObject* capi_err = PyErr_Occurred();
if (capi_err == NULL) {
capi_err = #modulename#_error;
PyErr_SetString(capi_err, capi_errmess);
}
} else {
#varname# = (#ctype# *)(PyArray_DATA(capi_#varname#_as_array));
# code block intent set
# 定义了一个名为`cleanupfrompyobj`的字典列表,用于存储特定清理操作的规则
'cleanupfrompyobj': [
' } ' # 关闭块注释
'/* if (capi_#varname#_as_array == NULL) ... else of #varname# */', # 处理特定条件下的注释
{l_not(l_or(isintent_out, isintent_hide)): """\
if((PyObject *)capi_#varname#_as_array!=#varname#_capi) {
Py_XDECREF(capi_#varname#_as_array); }"""}, # 根据条件释放特定对象
{l_and(isintent_hide, l_not(isintent_out)): """\
Py_XDECREF(capi_#varname#_as_array);"""}, # 处理隐藏变量的清理操作
{hasinitvalue: ' } /*if (f2py_success) of #varname# init*/'}, # 处理初始化成功的情况
],
# 规则用于检查数组的相关定义
'check_rules': [
{
'frompyobj': {debugcapi: ' fprintf(stderr,\"debug-capi:Checking `#check#\'\\n\");'}, # 输出用于调试的语句
'need': 'len..' # 指定检查的需求条件
}
]
}, {
# 开始定义一个新的字典条目,包含从 Python 对象到 C 代码的转换
'frompyobj': ' CHECKSCALAR(#check#,\"#check#\",\"#nth# #varname#\",\"#varshowvalue#\",#varname#) {',
# 在 C 代码中生成用于检查标量的宏定义
'cleanupfrompyobj': ' } /*CHECKSCALAR(#check#)*/',
# 需要定义一个标量检查
'need': 'CHECKSCALAR',
# 执行标量检查的条件:既不是复数,也不是复杂对象
'_check': l_and(isscalar, l_not(iscomplex)),
# 在此处没有需要中断的附加操作
'_break': ''
}, {
# 开始定义一个新的字典条目,包含从 Python 对象到 C 代码的转换
'frompyobj': ' CHECKSTRING(#check#,\"#check#\",\"#nth# #varname#\",\"#varshowvalue#\",#varname#) {',
# 在 C 代码中生成用于检查字符串的宏定义
'cleanupfrompyobj': ' } /*CHECKSTRING(#check#)*/',
# 需要定义一个字符串检查
'need': 'CHECKSTRING',
# 执行字符串检查的条件:对象是字符串
'_check': isstring,
# 在此处没有需要中断的附加操作
'_break': ''
}, {
# 需要定义一个数组检查
'need': 'CHECKARRAY',
# 在 C 代码中生成用于检查数组的宏定义
'frompyobj': ' CHECKARRAY(#check#,\"#check#\",\"#nth# #varname#\") {',
# 在 C 代码中生成用于清理数组检查的宏定义
'cleanupfrompyobj': ' } /*CHECKARRAY(#check#)*/',
# 执行数组检查的条件:对象是数组
'_check': isarray,
# 在此处没有需要中断的附加操作
'_break': ''
}, {
# 需要定义一个通用检查
'need': 'CHECKGENERIC',
# 在 C 代码中生成用于通用检查的宏定义
'frompyobj': ' CHECKGENERIC(#check#,\"#check#\",\"#nth# #varname#\") {',
# 在 C 代码中生成用于清理通用检查的宏定义
'cleanupfrompyobj': ' } /*CHECKGENERIC(#check#)*/',
# 在此处没有需要中断的附加操作
}
# 构建模块的函数,接收两个参数:m 表示模块信息,um 表示未使用的参数
def buildmodule(m, um):
"""
构建模块函数,处理模块相关的构建工作并返回结果字典。
参数:
m -- 包含模块信息的字典
um -- 未使用的参数,此处未被使用
返回:
ret -- 返回一个空字典作为占位符
"""
# 输出构建模块的信息
outmess(' Building module "%s"...\n' % (m['name']))
# 初始化返回的字典
ret = {}
# 复制模块规则列表到 mod_rules
mod_rules = defmod_rules[:]
# 将模块签名映射为字典 vrd
vrd = capi_maps.modsign2map(m)
# 构建包含 f2py 版本信息的字典 rd
rd = dictappend({'f2py_version': f2py_version}, vrd)
# 初始化函数包装器列表
funcwrappers = []
# 初始化 F90 代码的函数包装器列表
funcwrappers2 = [] # F90 codes
# 遍历模块的 interfaced 列表
for n in m['interfaced']:
nb = None
# 在模块的 body 中查找对应的接口块
for bi in m['body']:
if bi['block'] not in ['interface', 'abstract interface']:
errmess('buildmodule: Expected interface block. Skipping.\n')
continue
for b in bi['body']:
if b['name'] == n:
nb = b
break
# 如果未找到接口块,输出错误信息并跳过
if not nb:
print(
'buildmodule: Could not find the body of interfaced routine "%s". Skipping.\n' % (n), file=sys.stderr)
continue
# 创建接口块列表,包含当前接口块及其 entry 属性的深拷贝
nb_list = [nb]
if 'entry' in nb:
for k, a in nb['entry'].items():
nb1 = copy.deepcopy(nb)
del nb1['entry']
nb1['name'] = k
nb1['args'] = a
nb_list.append(nb1)
# 遍历接口块列表
for nb in nb_list:
# 检查是否需要 F90 包装器
isf90 = requiresf90wrapper(nb)
# 如果选项中包含 emptygen,生成可能为空的包装器文件
if options['emptygen']:
b_path = options['buildpath']
m_name = vrd['modulename']
outmess(' Generating possibly empty wrappers"\n')
Path(f"{b_path}/{vrd['coutput']}").touch()
if isf90:
outmess(f' Maybe empty "{m_name}-f2pywrappers2.f90"\n')
Path(f'{b_path}/{m_name}-f2pywrappers2.f90').touch()
outmess(f' Maybe empty "{m_name}-f2pywrappers.f"\n')
Path(f'{b_path}/{m_name}-f2pywrappers.f').touch()
else:
outmess(f' Maybe empty "{m_name}-f2pywrappers.f"\n')
Path(f'{b_path}/{m_name}-f2pywrappers.f').touch()
# 构建 API 和包装器
api, wrap = buildapi(nb)
# 如果存在包装器,根据是否为 F90 添加到对应列表
if wrap:
if isf90:
funcwrappers2.append(wrap)
else:
funcwrappers.append(wrap)
# 应用规则并更新 rd 字典
ar = applyrules(api, vrd)
rd = dictappend(rd, ar)
# 构建 COMMON 块支持
cr, wrap = common_rules.buildhooks(m)
if wrap:
funcwrappers.append(wrap)
ar = applyrules(cr, vrd)
rd = dictappend(rd, ar)
# 构建 F90 模块支持
mr, wrap = f90mod_rules.buildhooks(m)
if wrap:
funcwrappers2.append(wrap)
ar = applyrules(mr, vrd)
rd = dictappend(rd, ar)
# 遍历 um 列表中的元素 u
for u in um:
# 使用 use_rules.buildusevars 函数处理 u 和 m['use'][u['name']],返回结果存储在 ar 中
ar = use_rules.buildusevars(u, m['use'][u['name']])
# 将 ar 合并到 rd 中
rd = dictappend(rd, ar)
# 获取需要的信息集合
needs = cfuncs.get_needs()
# 向 needs['typedefs'] 中添加从 capi_maps.f2cmap_mapped 中选择的 cvar 元素,条件是 cvar 存在于 typedef_need_dict 的值中
needs['typedefs'] += [cvar for cvar in capi_maps.f2cmap_mapped
if cvar in typedef_need_dict.values()]
# 初始化 code 字典
code = {}
# 遍历 needs 字典的键
for n in needs.keys():
# 将每个键对应的值初始化为空列表
code[n] = []
# 遍历 needs[n] 中的每个元素 k
for k in needs[n]:
c = ''
# 根据 k 的值从不同的函数字典中获取对应的字符串 c
if k in cfuncs.includes0:
c = cfuncs.includes0[k]
elif k in cfuncs.includes:
c = cfuncs.includes[k]
elif k in cfuncs.userincludes:
c = cfuncs.userincludes[k]
elif k in cfuncs.typedefs:
c = cfuncs.typedefs[k]
elif k in cfuncs.typedefs_generated:
c = cfuncs.typedefs_generated[k]
elif k in cfuncs.cppmacros:
c = cfuncs.cppmacros[k]
elif k in cfuncs.cfuncs:
c = cfuncs.cfuncs[k]
elif k in cfuncs.callbacks:
c = cfuncs.callbacks[k]
elif k in cfuncs.f90modhooks:
c = cfuncs.f90modhooks[k]
elif k in cfuncs.commonhooks:
c = cfuncs.commonhooks[k]
else:
# 如果 k 不存在于任何函数字典中,输出错误信息
errmess('buildmodule: unknown need %s.\n' % (repr(k)))
continue
# 将获取的字符串 c 添加到 code[n] 中
code[n].append(c)
# 将 code 添加到 mod_rules 列表中
mod_rules.append(code)
# 遍历 mod_rules 列表中的元素 r
for r in mod_rules:
# 检查 r 是否包含键 '_check',并且调用 r['_check'](m) 如果条件为真,或者 r 中不包含 '_check'
if ('_check' in r and r['_check'](m)) or ('_check' not in r):
# 应用规则 r 到 vrd 和 m 上,结果存储在 ar 中
ar = applyrules(r, vrd, m)
# 将 ar 合并到 rd 中
rd = dictappend(rd, ar)
# 应用 module_rules 到 rd 上,结果存储在 ar 中
ar = applyrules(module_rules, rd)
# 构造输出文件的路径 fn
fn = os.path.join(options['buildpath'], vrd['coutput'])
# 将 ar['modulebody'] 中的内容写入文件 fn 中,替换其中的制表符为两个空格
ret['csrc'] = fn
with open(fn, 'w') as f:
f.write(ar['modulebody'].replace('\t', 2 * ' '))
# 输出信息,指示成功写入 C/API 模块到文件中
outmess(' Wrote C/API module "%s" to file "%s"\n' % (m['name'], fn))
# 如果 options['dorestdoc'] 为真
if options['dorestdoc']:
# 构造 ReST 文档文件的路径 fn
fn = os.path.join(options['buildpath'], vrd['modulename'] + 'module.rest')
# 将 ar['restdoc'] 中的内容写入文件 fn 中
with open(fn, 'w') as f:
f.write('.. -*- rest -*-\n')
f.write('\n'.join(ar['restdoc']))
# 输出信息,指示成功保存 ReST 文档文件
outmess(' ReST Documentation is saved to file "%s/%smodule.rest"\n' %
(options['buildpath'], vrd['modulename']))
# 如果 options['dolatexdoc'] 为真
if options['dolatexdoc']:
# 构造 LaTeX 文档文件的路径 fn
fn = os.path.join(options['buildpath'], vrd['modulename'] + 'module.tex')
# 将 ar['latexdoc'] 中的内容写入文件 fn 中
ret['ltx'] = fn
with open(fn, 'w') as f:
f.write(
'%% This file is auto-generated with f2py (version:%s)\n' % (f2py_version))
if 'shortlatex' not in options:
f.write(
'\\documentclass{article}\n\\usepackage{a4wide}\n\\begin{document}\n\\tableofcontents\n\n')
f.write('\n'.join(ar['latexdoc']))
if 'shortlatex' not in options:
f.write('\\end{document}')
# 输出信息,指示成功保存 LaTeX 文档文件
outmess(' Documentation is saved to file "%s/%smodule.tex"\n' %
(options['buildpath'], vrd['modulename']))
# 如果存在函数包装器
if funcwrappers:
# 拼接生成包装器文件的路径名
wn = os.path.join(options['buildpath'], vrd['f2py_wrapper_output'])
# 将文件路径添加到返回结果中的 'fsrc' 键
ret['fsrc'] = wn
# 打开文件以写入模式
with open(wn, 'w') as f:
# 写入文件头部信息
f.write('C -*- fortran -*-\n')
f.write(
'C This file is autogenerated with f2py (version:%s)\n' % (f2py_version))
f.write(
'C It contains Fortran 77 wrappers to fortran functions.\n')
# 初始化空列表 lines
lines = []
# 遍历函数包装器并生成文件内容
for l in ('\n\n'.join(funcwrappers) + '\n').split('\n'):
# 如果行中存在注释符号且位于前 66 个字符内,则不分割注释行
if 0 <= l.find('!') < 66:
# 将该行添加到 lines 列表中,并加上换行符
lines.append(l + '\n')
# 如果行不为空且以空格开头
elif l and l[0] == ' ':
# 当行长度大于等于 66 时,分割为多行,并添加到 lines 中
while len(l) >= 66:
lines.append(l[:66] + '\n &')
l = l[66:]
lines.append(l + '\n')
else:
lines.append(l + '\n')
# 将 lines 列表中的内容连接成字符串,并将多余的换行和连接符去除
lines = ''.join(lines).replace('\n &\n', '\n')
# 将处理好的文件内容写入文件中
f.write(lines)
# 输出消息,显示 Fortran 77 包装器文件已保存
outmess(' Fortran 77 wrappers are saved to "%s"\n' % (wn))
# 如果存在第二组函数包装器
if funcwrappers2:
# 拼接生成包装器文件的路径名
wn = os.path.join(
options['buildpath'], '%s-f2pywrappers2.f90' % (vrd['modulename']))
# 将文件路径添加到返回结果中的 'fsrc' 键
ret['fsrc'] = wn
# 打开文件以写入模式
with open(wn, 'w') as f:
# 写入文件头部信息
f.write('! -*- f90 -*-\n')
f.write(
'! This file is autogenerated with f2py (version:%s)\n' % (f2py_version))
f.write(
'! It contains Fortran 90 wrappers to fortran functions.\n')
# 初始化空列表 lines
lines = []
# 遍历第二组函数包装器并生成文件内容
for l in ('\n\n'.join(funcwrappers2) + '\n').split('\n'):
# 如果行中存在注释符号且位于前 72 个字符内,则不分割注释行
if 0 <= l.find('!') < 72:
# 将该行添加到 lines 列表中,并加上换行符
lines.append(l + '\n')
# 如果行长度超过 72 并且以空格开头
elif len(l) > 72 and l[0] == ' ':
# 分割为多行,并添加到 lines 中
lines.append(l[:72] + '&\n &')
l = l[72:]
while len(l) > 66:
lines.append(l[:66] + '&\n &')
l = l[66:]
lines.append(l + '\n')
else:
lines.append(l + '\n')
# 将 lines 列表中的内容连接成字符串,并将多余的换行和连接符去除
lines = ''.join(lines).replace('\n &\n', '\n')
# 将处理好的文件内容写入文件中
f.write(lines)
# 输出消息,显示 Fortran 90 包装器文件已保存
outmess(' Fortran 90 wrappers are saved to "%s"\n' % (wn))
# 返回包含生成文件路径的字典 ret
return ret
# 构建 C/API 函数的过程
# 定义一个字典,将数字转换为对应的英文序数词尾
stnd = {1: 'st', 2: 'nd', 3: 'rd', 4: 'th', 5: 'th',
6: 'th', 7: 'th', 8: 'th', 9: 'th', 0: 'th'}
# 定义构建 API 函数的主函数
def buildapi(rout):
# 将函数转换为子程序,同时获取包装信息
rout, wrap = func2subr.assubr(rout)
# 获取函数的参数列表和依赖参数
args, depargs = getargs2(rout)
# 设置全局变量 capi_maps 的依赖参数
capi_maps.depargs = depargs
# 获取函数的变量列表
var = rout['vars']
# 如果是模块函数,输出构建包装函数的消息
if ismoduleroutine(rout):
outmess(' Constructing wrapper function "%s.%s"...\n' %
(rout['modulename'], rout['name']))
else:
outmess(' Constructing wrapper function "%s"...\n' % (rout['name']))
# 根据函数签名构建路由映射
vrd = capi_maps.routsign2map(rout)
rd = dictappend({}, vrd)
# 根据规则集对函数进行处理
for r in rout_rules:
# 如果规则包含 '_check' 并且检查通过,或者规则没有 '_check',则应用规则
if ('_check' in r and r['_check'](rout)) or ('_check' not in r):
ar = applyrules(r, vrd, rout)
rd = dictappend(rd, ar)
# 处理函数的参数
nth, nthk = 0, 0
savevrd = {}
for a in args:
vrd = capi_maps.sign2map(a, var[a])
if isintent_aux(var[a]):
_rules = aux_rules
else:
_rules = arg_rules
if not isintent_hide(var[a]):
if not isoptional(var[a]):
nth = nth + 1
vrd['nth'] = repr(nth) + stnd[nth % 10] + ' argument'
else:
nthk = nthk + 1
vrd['nth'] = repr(nthk) + stnd[nthk % 10] + ' keyword'
else:
vrd['nth'] = 'hidden'
savevrd[a] = vrd
for r in _rules:
if '_depend' in r:
continue
if ('_check' in r and r['_check'](var[a])) or ('_check' not in r):
ar = applyrules(r, vrd, var[a])
rd = dictappend(rd, ar)
if '_break' in r:
break
# 处理依赖参数
for a in depargs:
if isintent_aux(var[a]):
_rules = aux_rules
else:
_rules = arg_rules
vrd = savevrd[a]
for r in _rules:
if '_depend' not in r:
continue
if ('_check' in r and r['_check'](var[a])) or ('_check' not in r):
ar = applyrules(r, vrd, var[a])
rd = dictappend(rd, ar)
if '_break' in r:
break
if 'check' in var[a]:
for c in var[a]['check']:
vrd['check'] = c
ar = applyrules(check_rules, vrd, var[a])
rd = dictappend(rd, ar)
# 反转清理和关闭 Python 对象的列表(如果存在)
if isinstance(rd['cleanupfrompyobj'], list):
rd['cleanupfrompyobj'].reverse()
if isinstance(rd['closepyobjfrom'], list):
rd['closepyobjfrom'].reverse()
# 构建函数的文档签名,并移除末尾逗号
rd['docsignature'] = stripcomma(replace('#docsign##docsignopt##docsignxa#',
{'docsign': rd['docsign'],
'docsignopt': rd['docsignopt'],
'docsignxa': rd['docsignxa']}))
# 从 rd 字典中获取 'docsignxashort' 和 'docsignoptshort' 的值,替换字符串 "#docsignopt##docsignxa#" 中的占位符,
# 使用 stripcomma 函数去除结果字符串中可能的逗号后返回。
optargs = stripcomma(replace('#docsignopt##docsignxa#',
{'docsignxa': rd['docsignxashort'],
'docsignopt': rd['docsignoptshort']}
))
# 如果 optargs 是空字符串,则从 rd 字典中获取 'docsign' 的值,替换字符串 "#docsign#" 的占位符,
# 使用 stripcomma 函数去除结果字符串中可能的逗号后赋给 rd['docsignatureshort']。
if optargs == '':
rd['docsignatureshort'] = stripcomma(
replace('#docsign#', {'docsign': rd['docsign']}))
else:
# 否则,替换字符串 "#docsign#[#docsignopt#]" 的占位符,使用 replace 函数,
# 将结果赋给 rd['docsignatureshort']。
rd['docsignatureshort'] = replace('#docsign#[#docsignopt#]',
{'docsign': rd['docsign'],
'docsignopt': optargs,
})
# 将 rd['docsignatureshort'] 中的下划线替换为 LaTeX 格式的转义序列 "\_",
# 然后将其中的逗号替换为 ", ",并将结果赋给 rd['latexdocsignatureshort']。
rd['latexdocsignatureshort'] = rd['docsignatureshort'].replace('_', '\\_')
rd['latexdocsignatureshort'] = rd[
'latexdocsignatureshort'].replace(',', ', ')
# 从 rd 字典中获取 'callfortran' 和 'callfortranappend' 的值,替换字符串 "#callfortran##callfortranappend#" 的占位符,
# 使用 stripcomma 函数去除结果字符串中可能的逗号后返回。
cfs = stripcomma(replace('#callfortran##callfortranappend#', {
'callfortran': rd['callfortran'], 'callfortranappend': rd['callfortranappend']}))
# 如果 rd['callfortranappend'] 的长度大于1,则从 rd 字典中获取 'callfortran' 和 'callfortranappend' 的值,
# 替换字符串 "#callfortran# 0,#callfortranappend#" 的占位符,使用 stripcomma 函数去除结果字符串中可能的逗号后,
# 将结果赋给 rd['callcompaqfortran'];否则,将前面计算得到的 cfs 赋给 rd['callcompaqfortran']。
if len(rd['callfortranappend']) > 1:
rd['callcompaqfortran'] = stripcomma(replace('#callfortran# 0,#callfortranappend#', {
'callfortran': rd['callfortran'], 'callfortranappend': rd['callfortranappend']}))
else:
rd['callcompaqfortran'] = cfs
# 将前面计算得到的 cfs 赋给 rd['callfortran']。
rd['callfortran'] = cfs
# 如果 rd['docreturn'] 是列表类型,则从 rd 字典中获取 'docreturn' 的值,替换字符串 "#docreturn#" 的占位符,
# 使用 stripcomma 函数去除结果字符串中可能的逗号后,再在末尾加上等号"=",将结果赋给 rd['docreturn']。
if isinstance(rd['docreturn'], list):
rd['docreturn'] = stripcomma(
replace('#docreturn#', {'docreturn': rd['docreturn']})) + ' = '
# 初始化 rd['docstrsigns'] 和 rd['latexdocstrsigns'] 为空列表。
rd['docstrsigns'] = []
rd['latexdocstrsigns'] = []
# 遍历包含字符串 'docstrreq', 'docstropt', 'docstrout', 'docstrcbs' 的列表,
# 对于每个字符串 k:
# 如果 rd 字典中存在 k,并且其值是列表类型,则将 rd[k] 中的元素追加到 rd['docstrsigns'] 中。
# 将字符串 'latex' + k 赋给 k,并检查 rd 字典中是否存在这个键,如果存在且值是列表类型,
# 则将 rd[k] 中的第一个元素追加到 rd['latexdocstrsigns'] 中,然后插入 LaTeX 描述环境的起始标记和结束标记之间的所有元素。
for k in ['docstrreq', 'docstropt', 'docstrout', 'docstrcbs']:
if k in rd and isinstance(rd[k], list):
rd['docstrsigns'] = rd['docstrsigns'] + rd[k]
k = 'latex' + k
if k in rd and isinstance(rd[k], list):
rd['latexdocstrsigns'] = rd['latexdocstrsigns'] + rd[k][0:1] +\
['\\begin{description}'] + rd[k][1:] +\
['\\end{description}']
# 使用 routine_rules 中定义的规则对 rd 进行处理,将处理结果赋给 ar。
ar = applyrules(routine_rules, rd)
# 如果 rout 是模块化的例行程序,调用 outmess 函数输出格式化后的 ar['docshort'];
# 否则,输出格式化后的 ar['docshort']。
if ismoduleroutine(rout):
outmess(' %s\n' % (ar['docshort']))
else:
outmess(' %s\n' % (ar['docshort']))
# 返回 ar 和 wrap。
return ar, wrap
# 导入内建的 os 模块
import os
# 导入内建的 re 模块(正则表达式)
import re
# 导入内建的 sys 模块
import sys
# 导入内建的 argparse 模块(命令行解析)
import argparse
# 导入内建的 itertools 模块(迭代工具)
import itertools
# 导入内建的 pathlib 模块(路径操作)
import pathlib
# 导入内建的 collections 模块
import collections
# 导入内建的 typing 模块
import typing
# 导入自定义模块或库 module
import module
.\numpy\numpy\f2py\src\fortranobject.c
/*
This file implements: FortranObject, array_from_pyobj, copy_ND_array
Author: Pearu Peterson <pearu@cens.ioc.ee>
$Revision: 1.52 $
$Date: 2005/07/11 07:44:20 $
*/
// 设置字典 dict 中名为 name 的项为 obj,若 obj 为 NULL 则打印错误信息并返回 -1
int
F2PyDict_SetItemString(PyObject *dict, char *name, PyObject *obj)
{
if (obj == NULL) {
fprintf(stderr, "Error loading %s\n", name);
// 检查是否有 Python 异常发生,如果有则打印异常信息并清除异常状态
if (PyErr_Occurred()) {
PyErr_Print();
PyErr_Clear();
}
return -1;
}
return PyDict_SetItemString(dict, name, obj);
}
/*
* Python-only fallback for thread-local callback pointers
*/
// 更新线程本地回调指针
void *
F2PySwapThreadLocalCallbackPtr(char *key, void *ptr)
{
PyObject *local_dict, *value;
void *prev;
// 获取当前线程状态的字典
local_dict = PyThreadState_GetDict();
if (local_dict == NULL) {
// 如果获取失败,触发致命错误
Py_FatalError(
"F2PySwapThreadLocalCallbackPtr: PyThreadState_GetDict "
"failed");
}
// 获取字典中 key 对应的值
value = PyDict_GetItemString(local_dict, key);
if (value != NULL) {
// 如果找到值,将其转换为 void 指针类型
prev = PyLong_AsVoidPtr(value);
if (PyErr_Occurred()) {
// 如果转换出错,触发致命错误
Py_FatalError(
"F2PySwapThreadLocalCallbackPtr: PyLong_AsVoidPtr failed");
}
}
else {
prev = NULL;
}
// 将新的指针 ptr 转换为 PyLong 对象
value = PyLong_FromVoidPtr((void *)ptr);
if (value == NULL) {
// 如果转换失败,触发致命错误
Py_FatalError(
"F2PySwapThreadLocalCallbackPtr: PyLong_FromVoidPtr failed");
}
// 将新的值设置回字典中
if (PyDict_SetItemString(local_dict, key, value) != 0) {
// 如果设置失败,触发致命错误
Py_FatalError(
"F2PySwapThreadLocalCallbackPtr: PyDict_SetItemString failed");
}
// 释放 PyLong 对象的引用
Py_DECREF(value);
// 返回先前的值 prev
return prev;
}
// 获取线程本地回调指针
void *
F2PyGetThreadLocalCallbackPtr(char *key)
{
PyObject *local_dict, *value;
void *prev;
// 获取当前线程状态的字典
local_dict = PyThreadState_GetDict();
if (local_dict == NULL) {
// 如果获取失败,触发致命错误
Py_FatalError(
"F2PyGetThreadLocalCallbackPtr: PyThreadState_GetDict failed");
}
// 获取字典中 key 对应的值
value = PyDict_GetItemString(local_dict, key);
if (value != NULL) {
// 如果找到值,将其转换为 void 指针类型
prev = PyLong_AsVoidPtr(value);
if (PyErr_Occurred()) {
// 如果转换出错,触发致命错误
Py_FatalError(
"F2PyGetThreadLocalCallbackPtr: PyLong_AsVoidPtr failed");
}
}
else {
prev = NULL;
}
// 返回先前的值 prev
return prev;
}
// 根据给定的类型编号和元素大小,创建一个 PyArray_Descr 对象
static PyArray_Descr *
get_descr_from_type_and_elsize(const int type_num, const int elsize) {
// 根据类型编号创建 PyArray_Descr 对象
PyArray_Descr * descr = PyArray_DescrFromType(type_num);
if (type_num == NPY_STRING) {
// 对于字符串类型,PyArray_DescrFromType 返回的描述符 elsize 为 0
// 需要用 PyArray_DESCR_REPLACE 更新描述符并设置正确的 elsize
PyArray_DESCR_REPLACE(descr);
if (descr == NULL) {
return NULL;
}
PyDataType_SET_ELSIZE(descr, elsize);
}
return descr;
}
// 初始化指针变量,确保在后续使用中能正常赋值
PyFortranObject *fp = NULL;
// Python 对象指针,用于暂存临时变量
PyObject *v = NULL;
// 如果初始化函数不为空,则调用该函数进行 F90 模块对象的初始化
if (init != NULL) { /* Initialize F90 module objects */
(*(init))();
}
// 分配内存并创建 PyFortranObject 对象实例
fp = PyObject_New(PyFortranObject, &PyFortran_Type);
// 如果分配失败,则返回空指针
if (fp == NULL) {
return NULL;
}
// 创建一个新的空字典对象,并将其赋值给 fp 对象的 dict 属性
if ((fp->dict = PyDict_New()) == NULL) {
Py_DECREF(fp);
return NULL;
}
// 初始化 fp 对象的长度属性为 0
fp->len = 0;
// 遍历 defs 数组,计算其中非空项的数量并赋值给 fp 对象的 len 属性
while (defs[fp->len].name != NULL) {
fp->len++;
}
// 如果 defs 数组长度为 0,则跳转到错误处理部分
if (fp->len == 0) {
goto fail;
}
// 将 defs 数组赋值给 fp 对象的 defs 属性
fp->defs = defs;
// 遍历 fp 对象的 defs 数组
for (i = 0; i < fp->len; i++) {
// 如果当前项的 rank 属性为 -1,表示是 Fortran 过程
if (fp->defs[i].rank == -1) { /* Is Fortran routine */
// 创建一个新的 Python 对象作为属性,并赋值给 v
v = PyFortranObject_NewAsAttr(&(fp->defs[i]));
// 如果创建失败,则跳转到错误处理部分
if (v == NULL) {
goto fail;
}
// 将 v 对象以字符串形式存入 fp 对象的 dict 属性
PyDict_SetItemString(fp->dict, fp->defs[i].name, v);
// 减少 v 对象的引用计数
Py_XDECREF(v);
}
// 如果当前项的 data 属性不为空,表示是 Fortran 变量或数组(非 allocatable)
else if ((fp->defs[i].data) != NULL) { /* Is Fortran variable or array (not allocatable) */
// 根据类型和元素大小获取 PyArray_Descr 对象
PyArray_Descr *descr = get_descr_from_type_and_elsize(fp->defs[i].type,
fp->defs[i].elsize);
// 如果获取失败,则跳转到错误处理部分
if (descr == NULL) {
goto fail;
}
// 根据描述符创建一个新的数组对象,并赋值给 v
v = PyArray_NewFromDescr(&PyArray_Type, descr, fp->defs[i].rank,
fp->defs[i].dims.d, NULL, fp->defs[i].data,
NPY_ARRAY_FARRAY, NULL);
// 如果创建失败,则减少描述符的引用计数,并跳转到错误处理部分
if (v == NULL) {
Py_DECREF(descr);
goto fail;
}
// 将 v 对象以字符串形式存入 fp 对象的 dict 属性
PyDict_SetItemString(fp->dict, fp->defs[i].name, v);
// 减少 v 对象的引用计数
Py_XDECREF(v);
}
}
// 返回 fp 对象的 PyObject 指针类型
return (PyObject *)fp;
fail:
// 释放 fp 指针指向的对象,并返回 NULL,用于错误处理
Py_XDECREF(fp);
return NULL;
}
PyObject *
PyFortranObject_NewAsAttr(FortranDataDef *defs)
{ /* used for calling F90 module routines */
// 初始化 fp 为 NULL
PyFortranObject *fp = NULL;
// 创建一个新的 PyFortranObject 对象,并将其类型设置为 PyFortran_Type
fp = PyObject_New(PyFortranObject, &PyFortran_Type);
// 如果创建对象失败,则调用失败处理块
if (fp == NULL)
return NULL;
// 创建一个新的字典对象并将其赋值给 fp->dict
if ((fp->dict = PyDict_New()) == NULL) {
PyObject_Del(fp);
// 如果创建字典失败,则调用失败处理块
return NULL;
}
// 设置 fp->len 为 1
fp->len = 1;
// 将 defs 赋值给 fp->defs
fp->defs = defs;
// 根据 defs->rank 的值设置 "__name__" 键在 fp->dict 中的值
if (defs->rank == -1) {
PyDict_SetItemString(fp->dict, "__name__", PyUnicode_FromFormat("function %s", defs->name));
} else if (defs->rank == 0) {
PyDict_SetItemString(fp->dict, "__name__", PyUnicode_FromFormat("scalar %s", defs->name));
} else {
PyDict_SetItemString(fp->dict, "__name__", PyUnicode_FromFormat("array %s", defs->name));
}
// 返回创建的 PyFortranObject 对象
return (PyObject *)fp;
}
/* Fortran methods */
static void
fortran_dealloc(PyFortranObject *fp)
{
// 释放 fp->dict 指向的对象
Py_XDECREF(fp->dict);
// 释放 fp 指向的对象
PyObject_Del(fp);
}
/* Returns number of bytes consumed from buf, or -1 on error. */
static Py_ssize_t
format_def(char *buf, Py_ssize_t size, FortranDataDef def)
{
char *p = buf;
int i;
npy_intp n;
// 将数组格式的描述写入 buf 中,返回写入的字节数或者 -1 表示出错
n = PyOS_snprintf(p, size, "array(%" NPY_INTP_FMT, def.dims.d[0]);
if (n < 0 || n >= size) {
return -1;
}
p += n;
size -= n;
// 循环将数组维度信息写入 buf 中
for (i = 1; i < def.rank; i++) {
n = PyOS_snprintf(p, size, ",%" NPY_INTP_FMT, def.dims.d[i]);
if (n < 0 || n >= size) {
return -1;
}
p += n;
size -= n;
}
// 检查 buf 是否还有足够空间写入字符 ')'
if (size <= 0) {
return -1;
}
*p++ = ')';
size--;
// 如果 def.data 为 NULL,则在 buf 中添加字符串 ", not allocated"
if (def.data == NULL) {
static const char notalloc[] = ", not allocated";
if ((size_t)size < sizeof(notalloc)) {
return -1;
}
memcpy(p, notalloc, sizeof(notalloc));
p += sizeof(notalloc);
size -= sizeof(notalloc);
}
// 返回 buf 中的有效字节数
return p - buf;
}
static PyObject *
fortran_doc(FortranDataDef def)
{
char *buf, *p;
PyObject *s = NULL;
Py_ssize_t n, origsize, size = 100;
// 根据 def.doc 的大小调整 size
if (def.doc != NULL) {
size += strlen(def.doc);
}
origsize = size;
// 分配内存给 buf
buf = p = (char *)PyMem_Malloc(size);
if (buf == NULL) {
return PyErr_NoMemory();
}
// 根据 def.rank 的值生成文档信息
if (def.rank == -1) {
if (def.doc) {
// 将 def.doc 的内容复制到 buf 中
n = strlen(def.doc);
if (n > size) {
goto fail;
}
memcpy(p, def.doc, n);
p += n;
size -= n;
}
else {
// 如果没有 def.doc,则生成默认的描述信息
n = PyOS_snprintf(p, size, "%s - no docs available", def.name);
if (n < 0 || n >= size) {
goto fail;
}
p += n;
size -= n;
}
}
else {
// 根据数据类型定义创建一个 PyArray_Descr 结构体指针 d
PyArray_Descr *d = PyArray_DescrFromType(def.type);
// 使用 PyArray_Descr 结构体中的数据类型字符和定义的名称格式化输出到 p 所指向的 buf 中
n = PyOS_snprintf(p, size, "%s : '%c'-", def.name, d->type);
// 释放 PyArray_Descr 结构体指针 d
Py_DECREF(d);
// 如果格式化失败或者输出的字符数超过了 size,跳转到失败处理
if (n < 0 || n >= size) {
goto fail;
}
// 更新 p 的位置到新的可写入位置
p += n;
// 更新 size 的可用大小
size -= n;
// 如果 def.data 为 NULL,则调用 format_def 函数格式化输出到 p 所指向的 buf 中
if (def.data == NULL) {
n = format_def(p, size, def);
if (n < 0) {
goto fail;
}
p += n;
size -= n;
}
// 如果 def.rank 大于 0,则调用 format_def 函数格式化输出到 p 所指向的 buf 中
else if (def.rank > 0) {
n = format_def(p, size, def);
if (n < 0) {
goto fail;
}
p += n;
size -= n;
}
// 否则,输出字符串 "scalar" 到 p 所指向的 buf 中
else {
n = strlen("scalar");
// 如果 size 小于需要写入的字符串长度,跳转到失败处理
if (size < n) {
goto fail;
}
// 将字符串 "scalar" 复制到 p 所指向的 buf 中
memcpy(p, "scalar", n);
// 更新 p 的位置到新的可写入位置
p += n;
// 更新 size 的可用大小
size -= n;
}
}
// 如果 size 小于等于 1,跳转到失败处理
if (size <= 1) {
goto fail;
}
// 在 p 所指向的 buf 末尾写入换行符 '\n'
*p++ = '\n';
// 更新 size 的可用大小
size--;
/* p 现在指向 buf 中字符串的末尾的下一个位置 */
// 使用 buf 和 p 指向的字符串长度创建一个 Python Unicode 对象 s
s = PyUnicode_FromStringAndSize(buf, p - buf);
// 释放动态分配的 buf 内存
PyMem_Free(buf);
// 返回创建的 Python Unicode 对象 s
return s;
fail:
/* 打印错误消息到标准错误流,指出文档字符串长度超出允许的大小 */
fprintf(stderr,
"fortranobject.c: fortran_doc: len(p)=%zd>%zd=size:"
" too long docstring required, increase size\n",
p - buf, origsize);
/* 释放之前分配的内存 */
PyMem_Free(buf);
/* 返回空指针,表示函数执行失败 */
return NULL;
}
static FortranDataDef *save_def; /* 保存可分配数组的指针 */
static void
set_data(char *d, npy_intp *f)
{ /* 来自Fortran的回调函数 */
/* 如果 f 不为零,说明在Fortran中 d 已经被分配了内存 */
if (*f) /* In fortran f=allocated(d) */
save_def->data = d;
else
save_def->data = NULL;
/* printf("set_data: d=%p,f=%d\n",d,*f); */
}
static PyObject *
fortran_getattr(PyFortranObject *fp, char *name)
{
int i, j, k, flag;
/* 检查对象字典是否为空 */
if (fp->dict != NULL) {
/* 尝试从对象字典中获取名为 'name' 的项 */
PyObject *v = _PyDict_GetItemStringWithError(fp->dict, name);
/* 如果获取失败并且发生了错误,则返回空指针 */
if (v == NULL && PyErr_Occurred()) {
return NULL;
}
/* 如果获取成功,则增加引用计数并返回该项 */
else if (v != NULL) {
Py_INCREF(v);
return v;
}
}
/* 在对象的定义列表中查找与 'name' 匹配的项 */
for (i = 0, j = 1; i < fp->len && (j = strcmp(name, fp->defs[i].name));
i++)
;
/* 如果找到了匹配项 */
if (j == 0)
/* 检查定义是否为F90可分配数组 */
if (fp->defs[i].rank != -1) { /* F90 allocatable array */
/* 如果回调函数不为空,则进行数组维度重置 */
if (fp->defs[i].func == NULL)
return NULL;
/* 将数组维度设置为-1 */
for (k = 0; k < fp->defs[i].rank; ++k) fp->defs[i].dims.d[k] = -1;
/* 保存当前定义以便后续使用 */
save_def = &fp->defs[i];
/* 调用回调函数设置数据 */
(*(fp->defs[i].func))(&fp->defs[i].rank, fp->defs[i].dims.d,
set_data, &flag);
/* 根据返回的标志检查数组状态 */
if (flag == 2)
k = fp->defs[i].rank + 1;
else
k = fp->defs[i].rank;
/* 如果数据不为空,则创建新的PyArray对象 */
if (fp->defs[i].data != NULL) { /* array is allocated */
PyObject *v = PyArray_New(
&PyArray_Type, k, fp->defs[i].dims.d, fp->defs[i].type,
NULL, fp->defs[i].data, 0, NPY_ARRAY_FARRAY, NULL);
/* 如果创建失败,则返回空指针 */
if (v == NULL)
return NULL;
/* 增加新对象的引用计数并返回 */
/* Py_INCREF(v); */
return v;
}
else { /* 如果数据为空,则返回Py_None */
Py_RETURN_NONE;
}
}
/* 如果 'name' 是特殊属性 '__dict__',返回对象字典 */
if (strcmp(name, "__dict__") == 0) {
Py_INCREF(fp->dict);
return fp->dict;
}
/* 如果 'name' 是特殊属性 '__doc__' */
if (strcmp(name, "__doc__") == 0) {
/* 创建一个空的PyUnicode对象 */
PyObject *s = PyUnicode_FromString(""), *s2, *s3;
/* 遍历对象定义列表并获取其文档字符串 */
for (i = 0; i < fp->len; i++) {
s2 = fortran_doc(fp->defs[i]);
s3 = PyUnicode_Concat(s, s2);
Py_DECREF(s2);
Py_DECREF(s);
s = s3;
}
/* 将生成的文档字符串添加到对象字典中 */
if (PyDict_SetItemString(fp->dict, name, s))
return NULL;
/* 返回文档字符串对象 */
return s;
}
/* 如果 'name' 是特殊属性 '_cpointer',且对象只有一个定义 */
if ((strcmp(name, "_cpointer") == 0) && (fp->len == 1)) {
/* 创建一个F2PyCapsule对象 */
PyObject *cobj =
F2PyCapsule_FromVoidPtr((void *)(fp->defs[0].data), NULL);
/* 将F2PyCapsule对象添加到对象字典中 */
if (PyDict_SetItemString(fp->dict, name, cobj))
return NULL;
/* 返回F2PyCapsule对象 */
return cobj;
}
/* 如果 'name' 不是以上任何特殊属性,则使用通用的属性获取方法 */
PyObject *str, *ret;
/* 从 'name' 创建一个PyUnicode对象 */
str = PyUnicode_FromString(name);
/* 调用通用的属性获取方法获取属性值 */
ret = PyObject_GenericGetAttr((PyObject *)fp, str);
/* 释放PyUnicode对象 */
Py_DECREF(str);
/* 返回获取的属性值 */
return ret;
}
static int
fortran_setattr(PyFortranObject *fp, char *name, PyObject *v)
{
int i, j, flag;
PyArrayObject *arr = NULL;
// 遍历 fp 对象的定义列表,查找属性名为 name 的定义
for (i = 0, j = 1; i < fp->len && (j = strcmp(name, fp->defs[i].name)); i++)
;
// 如果找到属性名为 name 的定义
if (j == 0) {
// 如果属性是一个可分配的数组
if (fp->defs[i].rank == -1) {
// 抛出异常,不能覆盖 Fortran 程序
PyErr_SetString(PyExc_AttributeError, "over-writing fortran routine");
return -1; // 返回错误标志
}
// 如果属性有关联的函数
if (fp->defs[i].func != NULL) { /* is allocatable array */
npy_intp dims[F2PY_MAX_DIMS];
int k;
save_def = &fp->defs[i];
// 如果新值 v 不是 Py_None,设置新值(如果需要重新分配,请参阅生成的 f2py 代码以获取更多细节)
if (v != Py_None) {
for (k = 0; k < fp->defs[i].rank; k++) dims[k] = -1;
// 从 Python 对象 v 创建 NumPy 数组 arr
if ((arr = array_from_pyobj(fp->defs[i].type, dims,
fp->defs[i].rank, F2PY_INTENT_IN,
v)) == NULL)
return -1;
// 调用属性关联的函数来设置数据
(*(fp->defs[i].func))(&fp->defs[i].rank, PyArray_DIMS(arr),
set_data, &flag);
}
else { // 如果 v 是 Py_None,进行释放操作
for (k = 0; k < fp->defs[i].rank; k++) dims[k] = 0;
// 调用属性关联的函数来释放数据
(*(fp->defs[i].func))(&fp->defs[i].rank, dims, set_data,
&flag);
for (k = 0; k < fp->defs[i].rank; k++) dims[k] = -1;
}
// 将维度信息复制回属性定义中
memcpy(fp->defs[i].dims.d, dims,
fp->defs[i].rank * sizeof(npy_intp));
}
else { // 如果属性不是可分配的数组
// 从 Python 对象 v 创建 NumPy 数组 arr
if ((arr = array_from_pyobj(fp->defs[i].type, fp->defs[i].dims.d,
fp->defs[i].rank, F2PY_INTENT_IN,
v)) == NULL)
return -1;
}
// 如果属性有关联的数据
if (fp->defs[i].data != NULL) {
// 计算 Fortran 数组的总大小
npy_intp s = PyArray_MultiplyList(fp->defs[i].dims.d,
PyArray_NDIM(arr));
if (s == -1)
s = PyArray_MultiplyList(PyArray_DIMS(arr), PyArray_NDIM(arr));
// 将 Python 对象的数据复制到 Fortran 数组中
if (s < 0 || (memcpy(fp->defs[i].data, PyArray_DATA(arr),
s * PyArray_ITEMSIZE(arr))) == NULL) {
if ((PyObject *)arr != v) {
Py_DECREF(arr);
}
return -1;
}
if ((PyObject *)arr != v) {
Py_DECREF(arr);
}
}
else
return (fp->defs[i].func == NULL ? -1 : 0);
return 0; // 操作成功
}
// 如果找不到属性名为 name 的定义,并且 fp 对象的字典为空,则创建一个新的字典
if (fp->dict == NULL) {
fp->dict = PyDict_New();
if (fp->dict == NULL)
return -1;
}
if (v == NULL) {
int rv = PyDict_DelItemString(fp->dict, name);
if (rv < 0)
PyErr_SetString(PyExc_AttributeError,
"delete non-existing fortran attribute");
return rv;
}
else
return PyDict_SetItemString(fp->dict, name, v);
static PyObject *
fortran_call(PyFortranObject *fp, PyObject *arg, PyObject *kw)
{
int i = 0;
/* 检查调用的 Fortran 对象是否为 Fortran 程序 */
if (fp->defs[i].rank == -1) { /* is Fortran routine */
if (fp->defs[i].func == NULL) {
PyErr_Format(PyExc_RuntimeError, "no function to call");
return NULL;
}
else if (fp->defs[i].data == NULL)
/* 调用没有数据的 Fortran 子例程 */
return (*((fortranfunc)(fp->defs[i].func)))((PyObject *)fp, arg,
kw, NULL);
else
/* 调用带有数据的 Fortran 子例程 */
return (*((fortranfunc)(fp->defs[i].func)))(
(PyObject *)fp, arg, kw, (void *)fp->defs[i].data);
}
/* 如果不是 Fortran 对象,返回类型错误 */
PyErr_Format(PyExc_TypeError, "this fortran object is not callable");
return NULL;
}
static PyObject *
fortran_repr(PyFortranObject *fp)
{
PyObject *name = NULL, *repr = NULL;
// 获取对象的名称属性
name = PyObject_GetAttrString((PyObject *)fp, "__name__");
PyErr_Clear();
// 根据对象名称生成对象的字符串表示
if (name != NULL && PyUnicode_Check(name)) {
repr = PyUnicode_FromFormat("<fortran %U>", name);
}
else {
repr = PyUnicode_FromString("<fortran object>");
}
Py_XDECREF(name);
return repr;
}
PyTypeObject PyFortran_Type = {
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "fortran",
.tp_basicsize = sizeof(PyFortranObject),
.tp_dealloc = (destructor)fortran_dealloc,
.tp_getattr = (getattrfunc)fortran_getattr,
.tp_setattr = (setattrfunc)fortran_setattr,
.tp_repr = (reprfunc)fortran_repr,
.tp_call = (ternaryfunc)fortran_call,
};
/************************* f2py_report_atexit *******************************/
static int passed_time = 0;
static int passed_counter = 0;
static int passed_call_time = 0;
static struct timeb start_time;
static struct timeb stop_time;
static struct timeb start_call_time;
static struct timeb stop_call_time;
static int cb_passed_time = 0;
static int cb_passed_counter = 0;
static int cb_passed_call_time = 0;
static struct timeb cb_start_time;
static struct timeb cb_stop_time;
static struct timeb cb_start_call_time;
static struct timeb cb_stop_call_time;
extern void
f2py_start_clock(void)
{
// 开始计时
ftime(&start_time);
}
extern void
f2py_start_call_clock(void)
{
// 停止当前计时并开始调用计时
f2py_stop_clock();
ftime(&start_call_time);
}
extern void
f2py_stop_clock(void)
{
// 停止计时并计算经过的时间
ftime(&stop_time);
passed_time += 1000 * (stop_time.time - start_time.time);
passed_time += stop_time.millitm - start_time.millitm;
}
extern void
f2py_stop_call_clock(void)
{
// 停止调用计时并计算经过的时间和调用次数
ftime(&stop_call_time);
passed_call_time += 1000 * (stop_call_time.time - start_call_time.time);
passed_call_time += stop_call_time.millitm - start_call_time.millitm;
passed_counter += 1;
// 重新开始总计时
f2py_start_clock();
}
extern void
f2py_cb_start_clock(void)
{
// 开始回调计时
ftime(&cb_start_time);
}
extern void
static int f2py_report_on_exit_been_here = 0;
extern void
f2py_report_on_exit(int exit_flag, void *name)
{
// 如果已经在此函数中打印过一次报告,则只输出名称并返回
if (f2py_report_on_exit_been_here) {
fprintf(stderr, " %s\n", (char *)name);
return;
}
// 标记已经在此函数中打印过一次报告
f2py_report_on_exit_been_here = 1;
// 输出性能报告表头
fprintf(stderr, " /-----------------------\\\n");
fprintf(stderr, " < F2PY performance report >\n");
fprintf(stderr, " \\-----------------------/\n");
// 输出总体调用时间报告
fprintf(stderr, "Overall time spent in ...\n");
fprintf(stderr, "(a) wrapped (Fortran/C) functions : %8d msec\n",
passed_call_time);
fprintf(stderr, "(b) f2py interface, %6d calls : %8d msec\n",
passed_counter, passed_time);
fprintf(stderr, "(c) call-back (Python) functions : %8d msec\n",
cb_passed_call_time);
fprintf(stderr, "(d) f2py call-back interface, %6d calls : %8d msec\n",
cb_passed_counter, cb_passed_time);
// 输出实际耗时较长的 wrapped 函数的时间
fprintf(stderr,
"(e) wrapped (Fortran/C) functions (actual) : %8d msec\n\n",
passed_call_time - cb_passed_call_time - cb_passed_time);
// 输出退出时的提示信息和模块名称
fprintf(stderr,
"Use -DF2PY_REPORT_ATEXIT_DISABLE to disable this message.\n");
fprintf(stderr, "Exit status: %d\n", exit_flag);
fprintf(stderr, "Modules : %s\n", (char *)name);
}
/*
* File: array_from_pyobj.c
*
* Description:
* ------------
* Provides array_from_pyobj function that returns a contiguous array
* object with the given dimensions and required storage order, either
* in row-major (C) or column-major (Fortran) order. The function
* array_from_pyobj is very flexible about its Python object argument
* that can be any number, list, tuple, or array.
*
* array_from_pyobj is used in f2py generated Python extension
* modules.
*
* Author: Pearu Peterson <pearu@cens.ioc.ee>
* Created: 13-16 January 2002
* $Id: fortranobject.c,v 1.52 2005/07/11 07:44:20 pearu Exp $
*/
// 静态函数声明:检查并修正数组维度
static int check_and_fix_dimensions(const PyArrayObject* arr,
const int rank,
npy_intp *dims,
const char *errmess);
// 查找第一个负维度的索引
static int
find_first_negative_dimension(const int rank, const npy_intp *dims)
{
int i;
for (i = 0; i < rank; ++i) {
if (dims[i] < 0) {
return i;
}
}
return -1;
}
// 输出数组维度信息
void
dump_dims(int rank, npy_intp const *dims)
{
int i;
printf("[");
for (i = 0; i < rank; ++i) {
printf("%3" NPY_INTP_FMT, dims[i]);
}
printf("]\n");
}
// 输出数组属性信息
void
dump_attrs(const PyArrayObject *obj)
{
const PyArrayObject_fields *arr = (const PyArrayObject_fields *)obj;
int rank = PyArray_NDIM(arr);
npy_intp size = PyArray_Size((PyObject *)arr);
printf("\trank = %d, flags = %d, size = %" NPY_INTP_FMT "\n", rank,
arr->flags, size);
printf("\tstrides = ");
dump_dims(rank, arr->strides);
printf("\tdimensions = ");
dump_dims(rank, arr->dimensions);
}
// 定义宏:交换两个数组对象的数据及属性
{ \
t c; \
c = (a); \
(a) = (b); \
(b) = c; \
}
// 交换两个数组对象的数据及属性
static int
swap_arrays(PyArrayObject *obj1, PyArrayObject *obj2)
{
PyArrayObject_fields *arr1 = (PyArrayObject_fields *)obj1,
*arr2 = (PyArrayObject_fields *)obj2;
SWAPTYPE(arr1->data, arr2->data, char *);
SWAPTYPE(arr1->nd, arr2->nd, int);
SWAPTYPE(arr1->dimensions, arr2->dimensions, npy_intp *);
SWAPTYPE(arr1->strides, arr2->strides, npy_intp *);
SWAPTYPE(arr1->base, arr2->base, PyObject *);
SWAPTYPE(arr1->descr, arr2->descr, PyArray_Descr *);
SWAPTYPE(arr1->flags, arr2->flags, int);
/* SWAPTYPE(arr1->weakreflist,arr2->weakreflist,PyObject*); */
return 0;
}
// 定义宏:判断数组对象是否与指定数据类型兼容
((PyArray_ISINTEGER(arr) && PyTypeNum_ISINTEGER(type_num)) || \
(PyArray_ISFLOAT(arr) && PyTypeNum_ISFLOAT(type_num)) || \
(PyArray_ISCOMPLEX(arr) && PyTypeNum_ISCOMPLEX(type_num)) || \
(PyArray_ISBOOL(arr) && PyTypeNum_ISBOOL(type_num)) || \
(PyArray_ISSTRING(arr) && PyTypeNum_ISSTRING(type_num)))
// 获取对象的元素大小
static int
get_elsize(PyObject *obj) {
/*
/*
get_elsize 函数根据输入的 Python 对象确定数组元素的大小。如果成功,则返回元素大小,否则返回 -1。
支持的输入类型包括:numpy.ndarray, bytes, str, tuple, list.
*/
if (PyArray_Check(obj)) {
// 如果输入对象是 numpy 数组,则返回数组元素的大小
return PyArray_ITEMSIZE((PyArrayObject *)obj);
} else if (PyBytes_Check(obj)) {
// 如果输入对象是字节对象(bytes),则返回其大小
return PyBytes_GET_SIZE(obj);
} else if (PyUnicode_Check(obj)) {
// 如果输入对象是 Unicode 对象(str),则返回其长度
return PyUnicode_GET_LENGTH(obj);
} else if (PySequence_Check(obj)) {
// 如果输入对象是序列(tuple 或 list)
PyObject* fast = PySequence_Fast(obj, "f2py:fortranobject.c:get_elsize");
if (fast != NULL) {
// 获取序列的长度
Py_ssize_t i, n = PySequence_Fast_GET_SIZE(fast);
int sz, elsize = 0;
// 遍历序列中的每个元素
for (i=0; i<n; i++) {
// 递归调用 get_elsize 函数,获取每个元素的大小
sz = get_elsize(PySequence_Fast_GET_ITEM(fast, i) /* borrowed */);
// 更新 elsize 为最大的元素大小
if (sz > elsize) {
elsize = sz;
}
}
Py_DECREF(fast);
return elsize;
}
}
// 如果无法确定输入对象的类型或者其他情况,返回 -1
return -1;
/*
*/
extern PyArrayObject *
ndarray_from_pyobj(const int type_num,
const int elsize_,
npy_intp *dims,
const int rank,
const int intent,
PyObject *obj,
const char *errmess) {
/*
* 从 Python 对象中创建一个具有指定元素类型和形状的数组,并考虑数组的使用意图。
*
* - 元素类型由 type_num 和 elsize 定义
* - 形状由 dims 和 rank 定义
*
* ndarray_from_pyobj 用于将 Python 对象参数转换为具有给定类型和形状的 numpy ndarray,
* 以便将数据传递给与 Fortran 或 C 函数交互的场合。
*
* 如果 errmess 不为 NULL,则包含在此函数内部触发异常的错误消息前缀。
*
* 负的 elsize 值表示 elsize 在运行时从 Python 对象中确定。
*
* 字符串类型 (type_num == NPY_STRING) 没有固定的元素大小,默认情况下类型对象将其设置为 0。
* 因此,对于字符串类型,必须使用 elsize 参数。对于其他类型,elsize 值被忽略。
*
* NumPy 将固定宽度字符串的类型定义为 dtype('S<width>')。此外,还有 dtype('c'),显示为 dtype('S1')
* (它们具有相同的 type_num 值),但实际上不同(.char 属性为 'S' 或 'c')。
*
* 在 Fortran 中,字符数组和字符串是不同的概念。Fortran 类型、NumPy dtypes 和 type_num-elsize 对之间的关系定义如下:
*
* character*5 foo | dtype('S5') | elsize=5, shape=()
* character(5) foo | dtype('S1') | elsize=1, shape=(5)
* character*5 foo(n) | dtype('S5') | elsize=5, shape=(n,)
* character(5) foo(n) | dtype('S1') | elsize=1, shape=(5, n)
* character*(*) foo | dtype('S') | elsize=-1, shape=()
*
* 引用计数注意事项
* -----------------
*
* 如果调用者将数组返回给 Python,则必须使用 Py_BuildValue("N",arr)。否则,如果 obj!=arr,则调用者必须调用 Py_DECREF(arr)。
*
* 使用意图(intent)(缓存、输出等)的注意事项
* ------------------------------------------
*
* 当返回 intent(cache) 数组时,不要期望数据是正确的。
*
*/
char mess[F2PY_MESSAGE_BUFFER_SIZE]; // 用于存储错误消息的缓冲区
PyArrayObject *arr = NULL; // 初始化一个空的 PyArrayObject 对象
int elsize = (elsize_ < 0 ? get_elsize(obj) : elsize_); // 确定元素大小,如果 elsize_ 小于 0,则从 obj 中获取
// 如果 elsize 仍然小于 0,表示无法确定元素大小
if (elsize < 0) {
// 如果有错误消息,将其复制到 mess 中
if (errmess != NULL) {
strcpy(mess, errmess);
}
// 将错误消息格式化添加到 mess 中
sprintf(mess + strlen(mess),
" -- failed to determine element size from %s",
Py_TYPE(obj)->tp_name);
// 设置 Python 异常并返回空指针
PyErr_SetString(PyExc_SystemError, mess);
return NULL;
}
PyArray_Descr * descr = get_descr_from_type_and_elsize(type_num, elsize); // 根据数据类型和元素大小获取描述符,返回新引用
if (descr == NULL) {
return NULL;
}
elsize = PyDataType_ELSIZE(descr); // 获取描述符中的元素大小
if ((intent & F2PY_INTENT_HIDE)
|| ((intent & F2PY_INTENT_CACHE) && (obj == Py_None))
|| ((intent & F2PY_OPTIONAL) && (obj == Py_None))
) {
/* intent(cache), optional, intent(hide) */
int ineg = find_first_negative_dimension(rank, dims); // 查找维度数组中第一个负数的索引
if (ineg >= 0) {
int i;
strcpy(mess, "failed to create intent(cache|hide)|optional array"
"-- must have defined dimensions but got (");
for(i = 0; i < rank; ++i)
sprintf(mess + strlen(mess), "%" NPY_INTP_FMT ",", dims[i]); // 格式化拼接维度信息到错误消息中
strcat(mess, ")");
PyErr_SetString(PyExc_ValueError, mess); // 设置值错误异常并传入错误消息
Py_DECREF(descr); // 减少描述符的引用计数
return NULL;
}
arr = (PyArrayObject *) \
PyArray_NewFromDescr(&PyArray_Type, descr, rank, dims,
NULL, NULL, !(intent & F2PY_INTENT_C), NULL); // 根据描述符创建新的数组对象
if (arr == NULL) {
Py_DECREF(descr); // 创建失败时释放描述符的引用
return NULL;
}
if (PyArray_ITEMSIZE(arr) != elsize) {
strcpy(mess, "failed to create intent(cache|hide)|optional array");
sprintf(mess+strlen(mess)," -- expected elsize=%d got %" NPY_INTP_FMT, elsize, (npy_intp)PyArray_ITEMSIZE(arr)); // 格式化拼接预期和实际的元素大小到错误消息中
PyErr_SetString(PyExc_ValueError,mess); // 设置值错误异常并传入错误消息
Py_DECREF(arr); // 减少数组对象的引用计数
return NULL;
}
if (!(intent & F2PY_INTENT_CACHE)) {
PyArray_FILLWBYTE(arr, 0); // 如果没有缓存意图,则用零填充数组
}
return arr; // 返回创建的数组对象
}
if ((intent & F2PY_INTENT_INOUT) || (intent & F2PY_INTENT_INPLACE) ||
(intent & F2PY_INTENT_CACHE)) {
PyErr_Format(PyExc_TypeError,
"failed to initialize intent(inout|inplace|cache) "
"array, input '%s' object is not an array",
Py_TYPE(obj)->tp_name); // 格式化错误消息,指示输入对象不是数组
Py_DECREF(descr); // 减少描述符的引用计数
return NULL;
}
{
// 执行F2PY_REPORT_ON_ARRAY_COPY_FROMANY宏,可能生成有关数组复制的报告
F2PY_REPORT_ON_ARRAY_COPY_FROMANY;
// 将给定的Python对象转换为NumPy数组对象
arr = (PyArrayObject *)PyArray_FromAny(
obj, descr, 0, 0,
// 根据intent标志选择数组存储方式:C顺序或Fortran顺序,并强制类型转换
((intent & F2PY_INTENT_C) ? NPY_ARRAY_CARRAY
: NPY_ARRAY_FARRAY) |
NPY_ARRAY_FORCECAST,
NULL);
// 警告:在NPY_STRING的情况下,PyArray_FromAny可能会重置descr->elsize,
// 例如dtype('S0')可能变成dtype('S1')。
if (arr == NULL) {
// 如果转换失败,释放描述符对象并返回NULL
Py_DECREF(descr);
return NULL;
}
// 如果数组类型不是NPY_STRING且数组项大小不等于期望的elsize
if (type_num != NPY_STRING && PyArray_ITEMSIZE(arr) != elsize) {
// 这是内部的健全性检查:在函数开头已将elsize设置为descr->elsize。
// 设置错误信息说明预期的elsize和实际获取的数组项大小不匹配
strcpy(mess, "failed to initialize intent(in) array");
sprintf(mess + strlen(mess),
" -- expected elsize=%d got %" NPY_INTP_FMT, elsize,
(npy_intp)PyArray_ITEMSIZE(arr));
// 设置异常,并释放数组对象后返回NULL
PyErr_SetString(PyExc_ValueError, mess);
Py_DECREF(arr);
return NULL;
}
// 检查并修正数组的维度,如果出错则释放数组对象并返回NULL
if (check_and_fix_dimensions(arr, rank, dims, errmess)) {
Py_DECREF(arr);
return NULL;
}
// 如果以上检查都通过,返回转换后的NumPy数组对象
return arr;
}
}
extern PyArrayObject *
array_from_pyobj(const int type_num,
npy_intp *dims,
const int rank,
const int intent,
PyObject *obj) {
/*
Same as ndarray_from_pyobj but with elsize determined from type,
if possible. Provided for backward compatibility.
*/
// 根据给定的类型编号获取对应的数组描述符
PyArray_Descr* descr = PyArray_DescrFromType(type_num);
// 获取描述符的元素大小
int elsize = PyDataType_ELSIZE(descr);
// 减少描述符的引用计数,防止内存泄漏
Py_DECREF(descr);
// 调用ndarray_from_pyobj函数创建并返回NumPy数组对象
return ndarray_from_pyobj(type_num, elsize, dims, rank, intent, obj, NULL);
}
/*****************************************/
/* Helper functions for array_from_pyobj */
/*****************************************/
static int
check_and_fix_dimensions(const PyArrayObject* arr, const int rank,
npy_intp *dims, const char *errmess)
{
/*
* This function fills in blanks (that are -1's) in dims list using
* the dimensions from arr. It also checks that non-blank dims will
* match with the corresponding values in arr dimensions.
*
* Returns 0 if the function is successful.
*
* If an error condition is detected, an exception is set and 1 is
* returned.
*/
// 定义错误消息的字符数组
char mess[F2PY_MESSAGE_BUFFER_SIZE];
// 计算arr的总元素数
const npy_intp arr_size =
(PyArray_NDIM(arr)) ? PyArray_Size((PyObject *)arr) : 1;
#ifdef DEBUG_COPY_ND_ARRAY
// 调试模式下打印数组的属性
dump_attrs(arr);
printf("check_and_fix_dimensions:init: dims=");
// 调试模式下打印维度信息
dump_dims(rank, dims);
#endif
# 如果要求的维度rank大于数组arr的维度数,进行以下处理:[1,2] -> [[1],[2]]; 1 -> [[1]]
# 这段代码用于调整数组的维度匹配要求的rank
if (rank > PyArray_NDIM(arr)) { /* [1,2] -> [[1],[2]]; 1 -> [[1]] */
npy_intp new_size = 1; // 初始化新数组的总大小为1
int free_axe = -1; // 自由轴的索引,初始化为-1表示无自由轴
int i;
npy_intp d;
// 填充dims中为-1或0的维度,并检查维度是否符合要求,计算新的总大小
for (i = 0; i < PyArray_NDIM(arr); ++i) {
d = PyArray_DIM(arr, i); // 获取数组arr在第i维的大小
if (dims[i] >= 0) { // 如果dims中第i维要求大于等于0
if (d > 1 && dims[i] != d) { // 如果数组arr在第i维的大小大于1且与要求的dims[i]不符
PyErr_Format(
PyExc_ValueError,
"%d-th dimension must be fixed to %" NPY_INTP_FMT
" but got %" NPY_INTP_FMT "\n",
i, dims[i], d);
return 1; // 报错并返回1,表示处理失败
}
if (!dims[i])
dims[i] = 1; // 如果dims[i]为0,则设置为1
}
else {
dims[i] = d ? d : 1; // 如果dims[i]小于0,则设为数组arr在第i维的大小,如果为0则设为1
}
new_size *= dims[i]; // 计算新数组的总大小
}
// 对于超出数组arr维度数的维度rank,进行额外处理
for (i = PyArray_NDIM(arr); i < rank; ++i)
if (dims[i] > 1) { // 如果dims中第i维要求大于1
PyErr_Format(PyExc_ValueError,
"%d-th dimension must be %" NPY_INTP_FMT
" but got 0 (not defined).\n",
i, dims[i]);
return 1; // 报错并返回1,表示处理失败
}
else if (free_axe < 0)
free_axe = i; // 记录第一个自由轴的索引
else
dims[i] = 1; // 其余维度设为1
// 如果存在自由轴free_axe
if (free_axe >= 0) {
dims[free_axe] = arr_size / new_size; // 计算自由轴的大小
new_size *= dims[free_axe]; // 更新新数组的总大小
}
// 如果新的总大小与原数组的大小不匹配,报错
if (new_size != arr_size) {
PyErr_Format(PyExc_ValueError,
"unexpected array size: new_size=%" NPY_INTP_FMT
", got array with arr_size=%" NPY_INTP_FMT
" (maybe too many free indices)\n",
new_size, arr_size);
return 1; // 报错并返回1,表示处理失败
}
}
# 如果排名(维度数)等于数组的维度数
else if (rank == PyArray_NDIM(arr)) {
# 计算新的数组大小为1
npy_intp new_size = 1;
int i;
npy_intp d;
# 遍历数组的每一个维度
for (i = 0; i < rank; ++i) {
# 获取数组在第i维度上的大小
d = PyArray_DIM(arr, i);
# 如果给定的维度值大于等于0
if (dims[i] >= 0) {
# 如果数组的维度大于1且不等于给定的维度值
if (d > 1 && d != dims[i]) {
# 如果错误消息不为空,则复制到mess中
if (errmess != NULL) {
strcpy(mess, errmess);
}
# 将错误消息格式化加入到mess中
sprintf(mess + strlen(mess),
" -- %d-th dimension must be fixed to %"
NPY_INTP_FMT " but got %" NPY_INTP_FMT,
i, dims[i], d);
# 设置Python异常并返回1表示出错
PyErr_SetString(PyExc_ValueError, mess);
return 1;
}
# 如果给定的维度值为0,则将其设为1
if (!dims[i])
dims[i] = 1;
}
else
# 否则,将数组当前维度大小赋值给给定的维度值
dims[i] = d;
# 计算新的数组大小
new_size *= dims[i];
}
# 如果新的数组大小与原数组大小不一致
if (new_size != arr_size) {
# 抛出格式化的异常,指出意外的数组大小
PyErr_Format(PyExc_ValueError,
"unexpected array size: new_size=%" NPY_INTP_FMT
", got array with arr_size=%" NPY_INTP_FMT "\n",
new_size, arr_size);
# 返回1表示出错
return 1;
}
}
}
#ifdef DEBUG_COPY_ND_ARRAY
// 如果定义了 DEBUG_COPY_ND_ARRAY 宏,则输出调试信息
printf("check_and_fix_dimensions:end: dims=");
// 打印调试信息,显示维度信息
dump_dims(rank, dims);
#endif
// 返回操作成功的标志
return 0;
}
/* End of file: array_from_pyobj.c */
/************************* copy_ND_array *******************************/
extern int
copy_ND_array(const PyArrayObject *arr, PyArrayObject *out)
{
// 报告在从 arr 复制数组到 out 时的状态
F2PY_REPORT_ON_ARRAY_COPY_FROMARR;
// 调用 NumPy 提供的函数将 arr 复制到 out 中
return PyArray_CopyInto(out, (PyArrayObject *)arr);
}
/********************* Various utility functions ***********************/
extern int
f2py_describe(PyObject *obj, char *buf) {
/*
Write the description of a Python object to buf. The caller must
provide buffer with size sufficient to write the description.
Return 1 on success.
*/
// 本地缓冲区,用于存储描述信息
char localbuf[F2PY_MESSAGE_BUFFER_SIZE];
// 根据不同类型的 Python 对象生成描述信息
if (PyBytes_Check(obj)) {
sprintf(localbuf, "%d-%s", (npy_int)PyBytes_GET_SIZE(obj), Py_TYPE(obj)->tp_name);
} else if (PyUnicode_Check(obj)) {
sprintf(localbuf, "%d-%s", (npy_int)PyUnicode_GET_LENGTH(obj), Py_TYPE(obj)->tp_name);
} else if (PyArray_CheckScalar(obj)) {
PyArrayObject* arr = (PyArrayObject*)obj;
sprintf(localbuf, "%c%" NPY_INTP_FMT "-%s-scalar", PyArray_DESCR(arr)->kind, PyArray_ITEMSIZE(arr), Py_TYPE(obj)->tp_name);
} else if (PyArray_Check(obj)) {
int i;
PyArrayObject* arr = (PyArrayObject*)obj;
strcpy(localbuf, "(");
for (i=0; i<PyArray_NDIM(arr); i++) {
if (i) {
strcat(localbuf, " ");
}
sprintf(localbuf + strlen(localbuf), "%" NPY_INTP_FMT ",", PyArray_DIM(arr, i));
}
sprintf(localbuf + strlen(localbuf), ")-%c%" NPY_INTP_FMT "-%s", PyArray_DESCR(arr)->kind, PyArray_ITEMSIZE(arr), Py_TYPE(obj)->tp_name);
} else if (PySequence_Check(obj)) {
sprintf(localbuf, "%d-%s", (npy_int)PySequence_Length(obj), Py_TYPE(obj)->tp_name);
} else {
sprintf(localbuf, "%s instance", Py_TYPE(obj)->tp_name);
}
// 将本地缓冲区的内容复制到目标缓冲区 buf 中
strcpy(buf, localbuf);
// 返回成功标志
return 1;
}
extern npy_intp
f2py_size_impl(PyArrayObject* var, ...)
{
// 初始大小为 0
npy_intp sz = 0;
npy_intp dim;
npy_intp rank;
va_list argp;
va_start(argp, var);
// 获取可变参数中的第一个参数
dim = va_arg(argp, npy_int);
if (dim==-1)
{
// 如果 dim 为 -1,则返回数组的总元素个数
sz = PyArray_SIZE(var);
}
else
{
// 否则,获取数组的维度数
rank = PyArray_NDIM(var);
// 如果 dim 在有效范围内,则返回对应维度的大小
if (dim>=1 && dim<=rank)
sz = PyArray_DIM(var, dim-1);
else
// 否则输出错误信息并返回 0
fprintf(stderr, "f2py_size: 2nd argument value=%" NPY_INTP_FMT
" fails to satisfy 1<=value<=%" NPY_INTP_FMT
". Result will be 0.\n", dim, rank);
}
va_end(argp);
// 返回计算出的大小
return sz;
}
/*********************************************/
/* Compatibility functions for Python >= 3.0 */
/*********************************************/
PyObject *
F2PyCapsule_FromVoidPtr(void *ptr, void (*dtor)(PyObject *))
{
// 创建一个 Python Capsule 对象
PyObject *ret = PyCapsule_New(ptr, NULL, dtor);
// 如果创建失败,清空错误信息
if (ret == NULL) {
PyErr_Clear();
}
// 返回创建的 Python 对象
return ret;
}
void *
F2PyCapsule_AsVoidPtr(PyObject *obj)
{
# 使用 PyCapsule_GetPointer 函数从 Python Capsule 对象中获取指针
void *ret = PyCapsule_GetPointer(obj, NULL);
# 检查获取的指针是否为空
if (ret == NULL) {
# 如果为空指针,则清除当前的 Python 异常状态
PyErr_Clear();
}
# 返回获取到的指针
return ret;
}
int
F2PyCapsule_Check(PyObject *ptr)
{
// 检查给定指针是否是一个 PyCapsule 对象
return PyCapsule_CheckExact(ptr);
}
#ifdef __cplusplus
}
#endif
/************************* EOF fortranobject.c *******************************/
.\numpy\numpy\f2py\src\fortranobject.h
extern "C" {
// clang-format off
extern void f2py_start_clock(void);
extern void f2py_stop_clock(void);
extern void f2py_start_call_clock(void);
extern void f2py_stop_call_clock(void);
extern void f2py_cb_start_clock(void);
extern void f2py_cb_stop_clock(void);
extern void f2py_cb_start_call_clock(void);
extern void f2py_cb_stop_call_clock(void);
extern void f2py_report_on_exit(int, void *);
// clang-format on
/* Fortran object interface */
/*
123456789-123456789-123456789-123456789-123456789-123456789-123456789-12
PyFortranObject represents various Fortran objects:
Fortran (module) routines, COMMON blocks, module data.
Author: Pearu Peterson <pearu@cens.ioc.ee>
*/
typedef void (*f2py_set_data_func)(char *, npy_intp *);
typedef void (*f2py_void_func)(void);
typedef void (*f2py_init_func)(int *, npy_intp *, f2py_set_data_func, int *);
/*typedef void* (*f2py_c_func)(void*,...);*/
typedef void *(*f2pycfunc)(void);
typedef struct {
char *name; /* attribute (array||routine) name */
int rank; /* array rank, 0 for scalar, max is F2PY_MAX_DIMS,
|| rank=-1 for Fortran routine */
struct {
npy_intp d[F2PY_MAX_DIMS];
} dims; /* dimensions of the array, || not used */
int type; /* PyArray_<type> || not used */
int elsize; /* Element size || not used */
char *data; /* pointer to array || Fortran routine */
f2py_init_func func; /* initialization function for
allocatable arrays:
func(&rank,dims,set_ptr_func,name,len(name))
|| C/API wrapper for Fortran routine */
char *doc; /* documentation string; only recommended
for routines. */
} FortranDataDef;
typedef struct {
PyObject_HEAD
int len; /* Number of attributes */
FortranDataDef *defs; /* An array of FortranDataDef's */
PyObject *dict; /* Fortran object attribute dictionary */
} PyFortranObject;
#define PyFortran_Check(op) (Py_TYPE(op) == &PyFortran_Type)
#define PyFortran_Check1(op) (0 == strcmp(Py_TYPE(op)->tp_name, "fortran"))
extern PyTypeObject PyFortran_Type;
extern int
F2PyDict_SetItemString(PyObject *dict, char *name, PyObject *obj);
extern PyObject *
PyFortranObject_New(FortranDataDef *defs, f2py_void_func init);
extern PyObject *
#endif // Py_FORTRANOBJECT_H
/* 定义一个函数原型,用于创建一个 PyFortranObject 作为属性 */
PyFortranObject_NewAsAttr(FortranDataDef *defs);
/*
创建一个 Python Capsule 对象,将一个 void 指针封装起来,并指定一个析构函数。
这个 Capsule 对象可以用来在 Python 和 C 之间传递指针。
*/
PyObject *
F2PyCapsule_FromVoidPtr(void *ptr, void (*dtor)(PyObject *));
/*
从 Python Capsule 对象中获取 void 指针。
这个函数用于从 Python 中获取在 Capsule 中封装的原始指针。
*/
void *
F2PyCapsule_AsVoidPtr(PyObject *obj);
/*
检查一个对象是否是 F2Py Capsule 类型。
这个宏用于确定一个对象是否是有效的 Capsule 对象。
*/
int
F2PyCapsule_Check(PyObject *ptr);
/*
在多线程环境中,用于设置线程本地的回调指针。
*/
extern void *
F2PySwapThreadLocalCallbackPtr(char *key, void *ptr);
/*
在多线程环境中,用于获取线程本地的回调指针。
*/
extern void *
F2PyGetThreadLocalCallbackPtr(char *key);
/* 定义一个宏,用于检查一个数组是否是 C 连续存储的 */
#define ISCONTIGUOUS(m) (PyArray_FLAGS(m) & NPY_ARRAY_C_CONTIGUOUS)
/* 定义一系列常量,用于描述参数的意图 */
#define F2PY_INTENT_IN 1
#define F2PY_INTENT_INOUT 2
#define F2PY_INTENT_OUT 4
#define F2PY_INTENT_HIDE 8
#define F2PY_INTENT_CACHE 16
#define F2PY_INTENT_COPY 32
#define F2PY_INTENT_C 64
#define F2PY_OPTIONAL 128
#define F2PY_INTENT_INPLACE 256
#define F2PY_INTENT_ALIGNED4 512
#define F2PY_INTENT_ALIGNED8 1024
#define F2PY_INTENT_ALIGNED16 2048
/*
定义一系列宏,用于检查数组的对齐要求
*/
#define F2PY_ALIGN4(intent) (intent & F2PY_INTENT_ALIGNED4)
#define F2PY_ALIGN8(intent) (intent & F2PY_INTENT_ALIGNED8)
#define F2PY_ALIGN16(intent) (intent & F2PY_INTENT_ALIGNED16)
/*
获取指定意图下的数组对齐大小
*/
#define F2PY_GET_ALIGNMENT(intent) \
(F2PY_ALIGN4(intent) \
? 4 \
: (F2PY_ALIGN8(intent) ? 8 : (F2PY_ALIGN16(intent) ? 16 : 1)))
/*
检查数组是否满足指定的对齐要求
*/
#define F2PY_CHECK_ALIGNMENT(arr, intent) \
ARRAY_ISALIGNED(arr, F2PY_GET_ALIGNMENT(intent))
/*
检查数组是否兼容字符类型
*/
#define F2PY_ARRAY_IS_CHARACTER_COMPATIBLE(arr) ((PyArray_DESCR(arr)->type_num == NPY_STRING && PyArray_ITEMSIZE(arr) >= 1) \
|| PyArray_DESCR(arr)->type_num == NPY_UINT8)
/*
检查数组是否是 Unicode 类型
*/
#define F2PY_IS_UNICODE_ARRAY(arr) (PyArray_DESCR(arr)->type_num == NPY_UNICODE)
/*
定义一个函数原型,用于从 Python 对象创建一个多维数组
*/
extern PyArrayObject *
ndarray_from_pyobj(const int type_num, const int elsize_, npy_intp *dims,
const int rank, const int intent, PyObject *obj,
const char *errmess);
/*
定义一个函数原型,用于从 Python 对象创建一个一维数组
*/
extern PyArrayObject *
array_from_pyobj(const int type_num, npy_intp *dims, const int rank,
const int intent, PyObject *obj);
/*
定义一个函数,用于复制一个多维数组
*/
extern int
copy_ND_array(const PyArrayObject *in, PyArrayObject *out);
#ifdef DEBUG_COPY_ND_ARRAY
/*
如果定义了 DEBUG_COPY_ND_ARRAY,定义一个函数原型,用于打印数组的属性
*/
extern void
dump_attrs(const PyArrayObject *arr);
#endif
/*
定义一个函数原型,用于描述一个 Python 对象
*/
extern int f2py_describe(PyObject *obj, char *buf);
/*
以下是一系列宏和函数,用于在签名文件表达式中使用的实用工具。
可以参考 signature-file.rst 获取更多文档信息。
*/
/*
宏,用于获取数组元素的大小
*/
#define f2py_itemsize(var) (PyArray_ITEMSIZE(capi_ ## var ## _as_array))
/*
宏,用于获取数组的大小
*/
#define f2py_size(var, ...) f2py_size_impl((PyArrayObject *)(capi_ ## var ## _as_array), ## __VA_ARGS__, -1)
/*
宏,用于获取数组的秩(维度数量)
*/
#define f2py_rank(var) var ## _Rank
/*
宏,用于获取数组的指定维度的大小
*/
#define f2py_shape(var,dim) var ## _Dims[dim]
/*
宏,用于获取数组的第一维度大小
*/
#define f2py_len(var) f2py_shape(var,0)
/*
宏,用于获取数组的逆序排列的维度大小
*/
#define f2py_fshape(var,dim) f2py_shape(var,rank(var)-dim-1)
/*
宏,用于获取适用于字符串长度的数组大小
*/
#define f2py_flen(var) f2py_fshape(var,0)
/*
宏,用于获取对象的字符串长度
*/
#define f2py_slen(var) capi_ ## var ## _len
/*
定义一个函数原型,用于获取数组的大小
*/
extern npy_intp f2py_size_impl(PyArrayObject* var, ...);