NumPy 源码解析(七)
.\numpy\numpy\distutils\tests\test_exec_command.py
import os
import pytest
import sys
from tempfile import TemporaryFile
from numpy.distutils import exec_command
from numpy.distutils.exec_command import get_pythonexe
from numpy.testing import tempdir, assert_, assert_warns, IS_WASM
from io import StringIO
class redirect_stdout:
"""上下文管理器,用于重定向stdout,用于exec_command测试。"""
def __init__(self, stdout=None):
self._stdout = stdout or sys.stdout
def __enter__(self):
self.old_stdout = sys.stdout
sys.stdout = self._stdout
def __exit__(self, exc_type, exc_value, traceback):
self._stdout.flush()
sys.stdout = self.old_stdout
self._stdout.close()
class redirect_stderr:
"""上下文管理器,用于重定向stderr,用于exec_command测试。"""
def __init__(self, stderr=None):
self._stderr = stderr or sys.stderr
def __enter__(self):
self.old_stderr = sys.stderr
sys.stderr = self._stderr
def __exit__(self, exc_type, exc_value, traceback):
self._stderr.flush()
sys.stderr = self.old_stderr
self._stderr.close()
class emulate_nonposix:
"""上下文管理器,用于模拟非posix系统的行为。"""
def __init__(self, osname='non-posix'):
self._new_name = osname
def __enter__(self):
self._old_name = os.name
os.name = self._new_name
def __exit__(self, exc_type, exc_value, traceback):
os.name = self._old_name
def test_exec_command_stdout():
with redirect_stdout(StringIO()):
with redirect_stderr(TemporaryFile()):
with assert_warns(DeprecationWarning):
exec_command.exec_command("cd '.'")
if os.name == 'posix':
with emulate_nonposix():
with redirect_stdout(StringIO()):
with redirect_stderr(TemporaryFile()):
with assert_warns(DeprecationWarning):
exec_command.exec_command("cd '.'")
def test_exec_command_stderr():
with redirect_stdout(TemporaryFile(mode='w+')):
with redirect_stderr(StringIO()):
with assert_warns(DeprecationWarning):
exec_command.exec_command("cd '.'")
if os.name == 'posix':
with emulate_nonposix():
with redirect_stdout(TemporaryFile()):
with redirect_stderr(StringIO()):
with assert_warns(DeprecationWarning):
exec_command.exec_command("cd '.'")
@pytest.mark.skipif(IS_WASM, reason="Cannot start subprocess")
class TestExecCommand:
def setup_method(self):
self.pyexe = get_pythonexe()
def check_nt(self, **kws):
s, o = exec_command.exec_command('cmd /C echo path=%path%')
assert_(s == 0)
assert_(o != '')
s, o = exec_command.exec_command(
'"%s" -c "import sys;sys.stderr.write(sys.platform)"' % self.pyexe)
assert_(s == 0)
assert_(o == 'win32')
def check_posix(self, **kws):
s, o = exec_command.exec_command("echo Hello", **kws)
assert_(s == 0)
assert_(o == 'Hello')
s, o = exec_command.exec_command('echo $AAA', **kws)
assert_(s == 0)
assert_(o == '')
s, o = exec_command.exec_command('echo "$AAA"', AAA='Tere', **kws)
assert_(s == 0)
assert_(o == 'Tere')
s, o = exec_command.exec_command('echo "$AAA"', **kws)
assert_(s == 0)
assert_(o == '')
if 'BBB' not in os.environ:
os.environ['BBB'] = 'Hi'
s, o = exec_command.exec_command('echo "$BBB"', **kws)
assert_(s == 0)
assert_(o == 'Hi')
s, o = exec_command.exec_command('echo "$BBB"', BBB='Hey', **kws)
assert_(s == 0)
assert_(o == 'Hey')
s, o = exec_command.exec_command('echo "$BBB"', **kws)
assert_(s == 0)
assert_(o == 'Hi')
del os.environ['BBB']
s, o = exec_command.exec_command('echo "$BBB"', **kws)
assert_(s == 0)
assert_(o == '')
s, o = exec_command.exec_command('this_is_not_a_command', **kws)
assert_(s != 0)
assert_(o != '')
s, o = exec_command.exec_command('echo path=$PATH', **kws)
assert_(s == 0)
assert_(o != '')
s, o = exec_command.exec_command(
'"%s" -c "import sys,os;sys.stderr.write(os.name)"' %
self.pyexe, **kws)
assert_(s == 0)
assert_(o == 'posix')
def check_basic(self, *kws):
s, o = exec_command.exec_command(
'"%s" -c "raise \'Ignore me.\'"' % self.pyexe, **kws)
assert_(s != 0)
assert_(o != '')
s, o = exec_command.exec_command(
'"%s" -c "import sys;sys.stderr.write(\'0\');'
'sys.stderr.write(\'1\');sys.stderr.write(\'2\')"' %
self.pyexe, **kws)
assert_(s == 0)
assert_(o == '012')
s, o = exec_command.exec_command(
'"%s" -c "import sys;sys.exit(15)"' % self.pyexe, **kws)
assert_(s == 15)
assert_(o == '')
s, o = exec_command.exec_command(
'"%s" -c "print(\'Heipa\'")' % self.pyexe, **kws)
assert_(s == 0)
assert_(o == 'Heipa')
def check_execute_in(self, **kws):
with tempdir() as tmpdir:
fn = "file"
tmpfile = os.path.join(tmpdir, fn)
with open(tmpfile, 'w') as f:
f.write('Hello')
s, o = exec_command.exec_command(
'"%s" -c "f = open(\'%s\', \'r\'); f.close()"' %
(self.pyexe, fn), **kws)
assert_(s != 0)
assert_(o != '')
s, o = exec_command.exec_command(
'"%s" -c "f = open(\'%s\', \'r\'); print(f.read()); '
'f.close()"' % (self.pyexe, fn), execute_in=tmpdir, **kws)
assert_(s == 0)
assert_(o == 'Hello')
def test_basic(self):
with redirect_stdout(StringIO()):
with redirect_stderr(StringIO()):
with assert_warns(DeprecationWarning):
if os.name == "posix":
self.check_posix(use_tee=0)
self.check_posix(use_tee=1)
elif os.name == "nt":
self.check_nt(use_tee=0)
self.check_nt(use_tee=1)
self.check_execute_in(use_tee=0)
self.check_execute_in(use_tee=1)
.\numpy\numpy\distutils\tests\test_fcompiler.py
from numpy.testing import assert_
import numpy.distutils.fcompiler
customizable_flags = [
('f77', 'F77FLAGS'),
('f90', 'F90FLAGS'),
('free', 'FREEFLAGS'),
('arch', 'FARCH'),
('debug', 'FDEBUG'),
('flags', 'FFLAGS'),
('linker_so', 'LDFLAGS'),
]
def test_fcompiler_flags(monkeypatch):
monkeypatch.setenv('NPY_DISTUTILS_APPEND_FLAGS', '0')
fc = numpy.distutils.fcompiler.new_fcompiler(compiler='none')
flag_vars = fc.flag_vars.clone(lambda *args, **kwargs: None)
for opt, envvar in customizable_flags:
new_flag = '-dummy-{}-flag'.format(opt)
prev_flags = getattr(flag_vars, opt)
monkeypatch.setenv(envvar, new_flag)
new_flags = getattr(flag_vars, opt)
monkeypatch.delenv(envvar)
assert_(new_flags == [new_flag])
monkeypatch.setenv('NPY_DISTUTILS_APPEND_FLAGS', '1')
for opt, envvar in customizable_flags:
new_flag = '-dummy-{}-flag'.format(opt)
prev_flags = getattr(flag_vars, opt)
monkeypatch.setenv(envvar, new_flag)
new_flags = getattr(flag_vars, opt)
monkeypatch.delenv(envvar)
if prev_flags is None:
assert_(new_flags == [new_flag])
else:
assert_(new_flags == prev_flags + [new_flag])
.\numpy\numpy\distutils\tests\test_fcompiler_gnu.py
from numpy.testing import assert_
import numpy.distutils.fcompiler
g77_version_strings = [
('GNU Fortran 0.5.25 20010319 (prerelease)', '0.5.25'),
('GNU Fortran (GCC 3.2) 3.2 20020814 (release)', '3.2'),
('GNU Fortran (GCC) 3.3.3 20040110 (prerelease) (Debian)', '3.3.3'),
('GNU Fortran (GCC) 3.3.3 (Debian 20040401)', '3.3.3'),
('GNU Fortran (GCC 3.2.2 20030222 (Red Hat Linux 3.2.2-5)) 3.2.2'
' 20030222 (Red Hat Linux 3.2.2-5)', '3.2.2'),
]
gfortran_version_strings = [
('GNU Fortran 95 (GCC 4.0.3 20051023 (prerelease) (Debian 4.0.2-3))',
'4.0.3'),
('GNU Fortran 95 (GCC) 4.1.0', '4.1.0'),
('GNU Fortran 95 (GCC) 4.2.0 20060218 (experimental)', '4.2.0'),
('GNU Fortran (GCC) 4.3.0 20070316 (experimental)', '4.3.0'),
('GNU Fortran (rubenvb-4.8.0) 4.8.0', '4.8.0'),
('4.8.0', '4.8.0'),
('4.0.3-7', '4.0.3'),
("gfortran: warning: couldn't understand kern.osversion '14.1.0\n4.9.1",
'4.9.1'),
("gfortran: warning: couldn't understand kern.osversion '14.1.0\n"
"gfortran: warning: yet another warning\n4.9.1",
'4.9.1'),
('GNU Fortran (crosstool-NG 8a21ab48) 7.2.0', '7.2.0')
]
class TestG77Versions:
def test_g77_version(self):
fc = numpy.distutils.fcompiler.new_fcompiler(compiler='gnu')
for vs, version in g77_version_strings:
v = fc.version_match(vs)
assert_(v == version, (vs, v))
def test_not_g77(self):
fc = numpy.distutils.fcompiler.new_fcompiler(compiler='gnu')
for vs, _ in gfortran_version_strings:
v = fc.version_match(vs)
assert_(v is None, (vs, v))
class TestGFortranVersions:
def test_gfortran_version(self):
fc = numpy.distutils.fcompiler.new_fcompiler(compiler='gnu95')
for vs, version in gfortran_version_strings:
v = fc.version_match(vs)
assert_(v == version, (vs, v))
def test_not_gfortran(self):
fc = numpy.distutils.fcompiler.new_fcompiler(compiler='gnu95')
for vs, _ in g77_version_strings:
v = fc.version_match(vs)
assert_(v is None, (vs, v))
.\numpy\numpy\distutils\tests\test_fcompiler_intel.py
import numpy.distutils.fcompiler
from numpy.testing import assert_
intel_32bit_version_strings = [
("Intel(R) Fortran Intel(R) 32-bit Compiler Professional for applications"
"running on Intel(R) 32, Version 11.1", '11.1'),
]
intel_64bit_version_strings = [
("Intel(R) Fortran IA-64 Compiler Professional for applications"
"running on IA-64, Version 11.0", '11.0'),
("Intel(R) Fortran Intel(R) 64 Compiler Professional for applications"
"running on Intel(R) 64, Version 11.1", '11.1')
]
class TestIntelFCompilerVersions:
def test_32bit_version(self):
fc = numpy.distutils.fcompiler.new_fcompiler(compiler='intel')
for vs, version in intel_32bit_version_strings:
v = fc.version_match(vs)
assert_(v == version)
class TestIntelEM64TFCompilerVersions:
def test_64bit_version(self):
fc = numpy.distutils.fcompiler.new_fcompiler(compiler='intelem')
for vs, version in intel_64bit_version_strings:
v = fc.version_match(vs)
assert_(v == version)
.\numpy\numpy\distutils\tests\test_fcompiler_nagfor.py
from numpy.testing import assert_
import numpy.distutils.fcompiler
nag_version_strings = [('nagfor', 'NAG Fortran Compiler Release '
'6.2(Chiyoda) Build 6200', '6.2'),
('nagfor', 'NAG Fortran Compiler Release '
'6.1(Tozai) Build 6136', '6.1'),
('nagfor', 'NAG Fortran Compiler Release '
'6.0(Hibiya) Build 1021', '6.0'),
('nagfor', 'NAG Fortran Compiler Release '
'5.3.2(971)', '5.3.2'),
('nag', 'NAGWare Fortran 95 compiler Release 5.1'
'(347,355-367,375,380-383,389,394,399,401-402,407,'
'431,435,437,446,459-460,463,472,494,496,503,508,'
'511,517,529,555,557,565)', '5.1')]
class TestNagFCompilerVersions:
def test_version_match(self):
for comp, vs, version in nag_version_strings:
fc = numpy.distutils.fcompiler.new_fcompiler(compiler=comp)
v = fc.version_match(vs)
assert_(v == version)
.\numpy\numpy\distutils\tests\test_from_template.py
from numpy.distutils.from_template import process_str
from numpy.testing import assert_equal
pyf_src = """
python module foo
<_rd=real,double precision>
interface
subroutine <s,d>foosub(tol)
<_rd>, intent(in,out) :: tol
end subroutine <s,d>foosub
end interface
end python module foo
"""
expected_pyf = """
python module foo
interface
subroutine sfoosub(tol)
real, intent(in,out) :: tol
end subroutine sfoosub
subroutine dfoosub(tol)
double precision, intent(in,out) :: tol
end subroutine dfoosub
end interface
end python module foo
"""
def normalize_whitespace(s):
"""
Remove leading and trailing whitespace, and convert internal
stretches of whitespace to a single space.
"""
return ' '.join(s.split())
def test_from_template():
"""Regression test for gh-10712."""
pyf = process_str(pyf_src)
normalized_pyf = normalize_whitespace(pyf)
normalized_expected_pyf = normalize_whitespace(expected_pyf)
assert_equal(normalized_pyf, normalized_expected_pyf)
.\numpy\numpy\distutils\tests\test_log.py
import io
import re
from contextlib import redirect_stdout
import pytest
from numpy.distutils import log
def setup_module():
f = io.StringIO()
with redirect_stdout(f):
log.set_verbosity(2, force=True)
def teardown_module():
log.set_verbosity(0, force=True)
r_ansi = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
@pytest.mark.parametrize("func_name", ["error", "warn", "info", "debug"])
def test_log_prefix(func_name):
func = getattr(log, func_name)
msg = f"{func_name} message"
f = io.StringIO()
with redirect_stdout(f):
func(msg)
out = f.getvalue()
assert out
clean_out = r_ansi.sub("", out)
line = next(line for line in clean_out.splitlines())
assert line == f"{func_name.upper()}: {msg}"
.\numpy\numpy\distutils\tests\test_mingw32ccompiler.py
import shutil`
import shutil
import subprocess
import sys
import pytest
from numpy.distutils import mingw32ccompiler
@pytest.mark.skipif(sys.platform != 'win32', reason='win32 only test')
def test_build_import():
'''Test the mingw32ccompiler.build_import_library, which builds a
`python.a` from the MSVC `python.lib`
'''
try:
out = subprocess.check_output(['nm.exe', '--help'])
except FileNotFoundError:
pytest.skip("'nm.exe' not on path, is mingw installed?")
supported = out[out.find(b'supported targets:'):]
if sys.maxsize < 2**32 when using 32-bit python. Supported "
"formats: '%s'" % supported)
elif b'pe-x86-64' not in supported:
raise ValueError("'nm.exe' found but it does not support 64-bit "
"dlls when using 64-bit python. Supported "
"formats: '%s'" % supported)
# 隐藏导入库以强制重新构建
has_import_lib, fullpath = mingw32ccompiler._check_for_import_lib()
if has_import_lib:
shutil.move(fullpath, fullpath + '.bak')
try:
# 真正执行函数测试
mingw32ccompiler.build_import_library()
finally:
# 恢复隐藏的导入库
if has_import_lib:
shutil.move(fullpath + '.bak', fullpath)
.\numpy\numpy\distutils\tests\test_misc_util.py
from os.path import join, sep, dirname
import pytest
from numpy.distutils.misc_util import (
appendpath, minrelpath, gpaths, get_shared_lib_extension, get_info
)
from numpy.testing import (
assert_, assert_equal, IS_EDITABLE
)
ajoin = lambda *paths: join(*((sep,)+paths))
class TestAppendpath:
def test_1(self):
assert_equal(appendpath('prefix', 'name'), join('prefix', 'name'))
assert_equal(appendpath('/prefix', 'name'), ajoin('prefix', 'name'))
assert_equal(appendpath('/prefix', '/name'), ajoin('prefix', 'name'))
assert_equal(appendpath('prefix', '/name'), join('prefix', 'name'))
def test_2(self):
assert_equal(appendpath('prefix/sub', 'name'),
join('prefix', 'sub', 'name'))
assert_equal(appendpath('prefix/sub', 'sup/name'),
join('prefix', 'sub', 'sup', 'name'))
assert_equal(appendpath('/prefix/sub', '/prefix/name'),
ajoin('prefix', 'sub', 'name'))
def test_3(self):
assert_equal(appendpath('/prefix/sub', '/prefix/sup/name'),
ajoin('prefix', 'sub', 'sup', 'name'))
assert_equal(appendpath('/prefix/sub/sub2', '/prefix/sup/sup2/name'),
ajoin('prefix', 'sub', 'sub2', 'sup', 'sup2', 'name'))
assert_equal(appendpath('/prefix/sub/sub2', '/prefix/sub/sup/name'),
ajoin('prefix', 'sub', 'sub2', 'sup', 'name'))
class TestMinrelpath:
def test_1(self):
n = lambda path: path.replace('/', sep)
assert_equal(minrelpath(n('aa/bb')), n('aa/bb'))
assert_equal(minrelpath('..'), '..')
assert_equal(minrelpath(n('aa/..')), '')
assert_equal(minrelpath(n('aa/../bb')), 'bb')
assert_equal(minrelpath(n('aa/bb/..')), 'aa')
assert_equal(minrelpath(n('aa/bb/../..')), '')
assert_equal(minrelpath(n('aa/bb/../cc/../dd')), n('aa/dd'))
assert_equal(minrelpath(n('.././..')), n('../..'))
assert_equal(minrelpath(n('aa/bb/.././../dd')), n('dd'))
class TestGpaths:
def test_gpaths(self):
local_path = minrelpath(join(dirname(__file__), '..'))
ls = gpaths('command/*.py', local_path)
assert_(join(local_path, 'command', 'build_src.py') in ls, repr(ls))
f = gpaths('system_info.py', local_path)
assert_(join(local_path, 'system_info.py') == f[0], repr(f))
class TestSharedExtension:
def test_get_shared_lib_extension(self):
import sys
ext = get_shared_lib_extension(is_python_ext=False)
if sys.platform.startswith('linux'):
assert_equal(ext, '.so')
elif sys.platform.startswith('gnukfreebsd'):
assert_equal(ext, '.so')
elif sys.platform.startswith('darwin'):
assert_equal(ext, '.dylib')
elif sys.platform.startswith('win'):
assert_equal(ext, '.dll')
assert_(get_shared_lib_extension(is_python_ext=True))
@pytest.mark.skipif(
IS_EDITABLE,
reason="`get_info` .ini lookup method incompatible with editable install"
)
def test_installed_npymath_ini():
info = get_info('npymath')
assert isinstance(info, dict)
assert "define_macros" in info
.\numpy\numpy\distutils\tests\test_npy_pkg_config.py
import os
from numpy.distutils.npy_pkg_config import read_config, parse_flags
from numpy.testing import temppath, assert_
simple = """\
[meta]
Name = foo
Description = foo lib
Version = 0.1
[default]
cflags = -I/usr/include
libs = -L/usr/lib
"""
simple_d = {'cflags': '-I/usr/include', 'libflags': '-L/usr/lib',
'version': '0.1', 'name': 'foo'}
simple_variable = """\
[meta]
Name = foo
Description = foo lib
Version = 0.1
[variables]
prefix = /foo/bar
libdir = ${prefix}/lib
includedir = ${prefix}/include
[default]
cflags = -I${includedir}
libs = -L${libdir}
"""
simple_variable_d = {'cflags': '-I/foo/bar/include', 'libflags': '-L/foo/bar/lib',
'version': '0.1', 'name': 'foo'}
class TestLibraryInfo:
def test_simple(self):
with temppath('foo.ini') as path:
with open(path, 'w') as f:
f.write(simple)
pkg = os.path.splitext(path)[0]
out = read_config(pkg)
assert_(out.cflags() == simple_d['cflags'])
assert_(out.libs() == simple_d['libflags'])
assert_(out.name == simple_d['name'])
assert_(out.version == simple_d['version'])
def test_simple_variable(self):
with temppath('foo.ini') as path:
with open(path, 'w') as f:
f.write(simple_variable)
pkg = os.path.splitext(path)[0]
out = read_config(pkg)
assert_(out.cflags() == simple_variable_d['cflags'])
assert_(out.libs() == simple_variable_d['libflags'])
assert_(out.name == simple_variable_d['name'])
assert_(out.version == simple_variable_d['version'])
out.vars['prefix'] = '/Users/david'
assert_(out.cflags() == '-I/Users/david/include')
class TestParseFlags:
def test_simple_cflags(self):
d = parse_flags("-I/usr/include")
assert_(d['include_dirs'] == ['/usr/include'])
d = parse_flags("-I/usr/include -DFOO")
assert_(d['include_dirs'] == ['/usr/include'])
assert_(d['macros'] == ['FOO'])
d = parse_flags("-I /usr/include -DFOO")
assert_(d['include_dirs'] == ['/usr/include'])
assert_(d['macros'] == ['FOO'])
def test_simple_lflags(self):
d = parse_flags("-L/usr/lib -lfoo -L/usr/lib -lbar")
assert_(d['library_dirs'] == ['/usr/lib', '/usr/lib'])
assert_(d['libraries'] == ['foo', 'bar'])
d = parse_flags("-L /usr/lib -lfoo -L/usr/lib -lbar")
assert_(d['library_dirs'] == ['/usr/lib', '/usr/lib'])
assert_(d['libraries'] == ['foo', 'bar'])
.\numpy\numpy\distutils\tests\test_shell_utils.py
import
.\numpy\numpy\distutils\tests\test_system_info.py
import os
import shutil
import pytest
from tempfile import mkstemp, mkdtemp
from subprocess import Popen, PIPE
import importlib.metadata
from distutils.errors import DistutilsError
from numpy.testing import assert_, assert_equal, assert_raises
from numpy.distutils import ccompiler, customized_ccompiler
from numpy.distutils.system_info import (
system_info, ConfigParser, mkl_info, AliasedOptionError
)
from numpy.distutils.system_info import default_lib_dirs, default_include_dirs
from numpy.distutils import _shell_utils
try:
if importlib.metadata.version('setuptools') >= '60':
pytest.skip("setuptools is too new", allow_module_level=True)
except importlib.metadata.PackageNotFoundError:
pass
def get_class(name, notfound_action=1):
"""
根据名称获取类对象
notfound_action:
0 - 什么都不做
1 - 显示警告消息
2 - 抛出错误
"""
cl = {'temp1': Temp1Info,
'temp2': Temp2Info,
'duplicate_options': DuplicateOptionInfo,
}.get(name.lower(), _system_info)
return cl()
simple_site = """
[ALL]
library_dirs = {dir1:s}{pathsep:s}{dir2:s}
libraries = {lib1:s},{lib2:s}
extra_compile_args = -I/fake/directory -I"/path with/spaces" -Os
runtime_library_dirs = {dir1:s}
[temp1]
library_dirs = {dir1:s}
libraries = {lib1:s}
runtime_library_dirs = {dir1:s}
[temp2]
library_dirs = {dir2:s}
libraries = {lib2:s}
extra_link_args = -Wl,-rpath={lib2_escaped:s}
rpath = {dir2:s}
[duplicate_options]
mylib_libs = {lib1:s}
libraries = {lib2:s}
"""
site_cfg = simple_site
fakelib_c_text = """
/* This file is generated from numpy/distutils/testing/test_system_info.py */
#include<stdio.h>
void foo(void) {
printf("Hello foo");
}
void bar(void) {
printf("Hello bar");
}
"""
def have_compiler():
""" 返回True如果存在可执行的编译器 """
compiler = customized_ccompiler()
try:
cmd = compiler.compiler
except AttributeError:
try:
if not compiler.initialized:
compiler.initialize()
except (DistutilsError, ValueError):
return False
cmd = [compiler.cc]
try:
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
p.stdout.close()
p.stderr.close()
p.wait()
except OSError:
return False
return True
HAVE_COMPILER = have_compiler()
class _system_info(system_info):
def __init__(self,
default_lib_dirs=default_lib_dirs,
default_include_dirs=default_include_dirs,
verbosity=1,
):
self.__class__.info = {}
self.local_prefixes = []
defaults = {'library_dirs': '',
'include_dirs': '',
'runtime_library_dirs': '',
'rpath': '',
'src_dirs': '',
'search_static_first': "0",
'extra_compile_args': '',
'extra_link_args': ''}
self.cp = ConfigParser(defaults)
def _check_libs(self, lib_dirs, libs, opt_libs, exts):
"""Override _check_libs to return with all dirs """
info = {'libraries': libs, 'library_dirs': lib_dirs}
return info
class Temp1Info(_system_info):
"""For testing purposes"""
section = 'temp1'
class Temp2Info(_system_info):
"""For testing purposes"""
section = 'temp2'
class DuplicateOptionInfo(_system_info):
"""For testing purposes"""
section = 'duplicate_options'
class TestSystemInfoReading:
def setup_method(self):
"""设置测试环境"""
self._dir1 = mkdtemp()
self._src1 = os.path.join(self._dir1, 'foo.c')
self._lib1 = os.path.join(self._dir1, 'libfoo.so')
self._dir2 = mkdtemp()
self._src2 = os.path.join(self._dir2, 'bar.c')
self._lib2 = os.path.join(self._dir2, 'libbar.so')
global simple_site, site_cfg
site_cfg = simple_site.format(**{
'dir1': self._dir1,
'lib1': self._lib1,
'dir2': self._dir2,
'lib2': self._lib2,
'pathsep': os.pathsep,
'lib2_escaped': _shell_utils.NativeParser.join([self._lib2])
})
fd, self._sitecfg = mkstemp()
os.close(fd)
with open(self._sitecfg, 'w') as fd:
fd.write(site_cfg)
with open(self._src1, 'w') as fd:
fd.write(fakelib_c_text)
with open(self._src2, 'w') as fd:
fd.write(fakelib_c_text)
def site_and_parse(c, site_cfg):
c.files = [site_cfg]
c.parse_config_files()
return c
self.c_default = site_and_parse(get_class('default'), self._sitecfg)
self.c_temp1 = site_and_parse(get_class('temp1'), self._sitecfg)
self.c_temp2 = site_and_parse(get_class('temp2'), self._sitecfg)
self.c_dup_options = site_and_parse(get_class('duplicate_options'), self._sitecfg)
def teardown_method(self):
try:
shutil.rmtree(self._dir1)
except Exception:
pass
try:
shutil.rmtree(self._dir2)
except Exception:
pass
try:
os.remove(self._sitecfg)
except Exception:
pass
def test_all(self):
tsi = self.c_default
assert_equal(tsi.get_lib_dirs(), [self._dir1, self._dir2])
assert_equal(tsi.get_libraries(), [self._lib1, self._lib2])
assert_equal(tsi.get_runtime_lib_dirs(), [self._dir1])
extra = tsi.calc_extra_info()
assert_equal(extra['extra_compile_args'], ['-I/fake/directory', '-I/path with/spaces', '-Os'])
def test_temp1(self):
tsi = self.c_temp1
assert_equal(tsi.get_lib_dirs(), [self._dir1])
assert_equal(tsi.get_libraries(), [self._lib1])
assert_equal(tsi.get_runtime_lib_dirs(), [self._dir1])
def test_temp2(self):
tsi = self.c_temp2
assert_equal(tsi.get_lib_dirs(), [self._dir2])
assert_equal(tsi.get_libraries(), [self._lib2])
assert_equal(tsi.get_runtime_lib_dirs(key='rpath'), [self._dir2])
extra = tsi.calc_extra_info()
assert_equal(extra['extra_link_args'], ['-Wl,-rpath=' + self._lib2])
def test_duplicate_options(self):
tsi = self.c_dup_options
assert_raises(AliasedOptionError, tsi.get_option_single, "mylib_libs", "libraries")
assert_equal(tsi.get_libs("mylib_libs", [self._lib1]), [self._lib1])
assert_equal(tsi.get_libs("libraries", [self._lib2]), [self._lib2])
@pytest.mark.skipif(not HAVE_COMPILER, reason="Missing compiler")
def test_compile1(self):
c = customized_ccompiler()
previousDir = os.getcwd()
try:
os.chdir(self._dir1)
c.compile([os.path.basename(self._src1)], output_dir=self._dir1)
assert_(os.path.isfile(self._src1.replace('.c', '.o')) or
os.path.isfile(self._src1.replace('.c', '.obj')))
finally:
os.chdir(previousDir)
@pytest.mark.skipif(not HAVE_COMPILER, reason="Missing compiler")
@pytest.mark.skipif('msvc' in repr(ccompiler.new_compiler()),
reason="Fails with MSVC compiler ")
def test_compile2(self):
tsi = self.c_temp2
c = customized_ccompiler()
extra_link_args = tsi.calc_extra_info()['extra_link_args']
previousDir = os.getcwd()
try:
os.chdir(self._dir2)
c.compile([os.path.basename(self._src2)], output_dir=self._dir2,
extra_postargs=extra_link_args)
assert_(os.path.isfile(self._src2.replace('.c', '.o')))
finally:
os.chdir(previousDir)
HAS_MKL = "mkl_rt" in mkl_info().calc_libraries_info().get("libraries", [])
@pytest.mark.xfail(HAS_MKL, reason=("`[DEFAULT]` override doesn't work if "
"numpy is built with MKL support"))
def test_overrides(self):
previousDir = os.getcwd()
cfg = os.path.join(self._dir1, 'site.cfg')
shutil.copy(self._sitecfg, cfg)
try:
os.chdir(self._dir1)
info = mkl_info()
lib_dirs = info.cp['ALL']['library_dirs'].split(os.pathsep)
assert info.get_lib_dirs() != lib_dirs
with open(cfg) as fid:
mkl = fid.read().replace('[ALL]', '[mkl]', 1)
with open(cfg, 'w') as fid:
fid.write(mkl)
info = mkl_info()
assert info.get_lib_dirs() == lib_dirs
with open(cfg) as fid:
dflt = fid.read().replace('[mkl]', '[DEFAULT]', 1)
with open(cfg, 'w') as fid:
fid.write(dflt)
info = mkl_info()
assert info.get_lib_dirs() == lib_dirs
finally:
os.chdir(previousDir)
def test_distutils_parse_env_order(monkeypatch):
from numpy.distutils.system_info import _parse_env_order
env = 'NPY_TESTS_DISTUTILS_PARSE_ENV_ORDER'
base_order = list('abcdef')
monkeypatch.setenv(env, 'b,i,e,f')
order, unknown = _parse_env_order(base_order, env)
assert len(order) == 3
assert order == list('bef')
assert len(unknown) == 1
monkeypatch.setenv(env, '')
order, unknown = _parse_env_order(base_order, env)
assert len(order) == 0
assert len(unknown) == 0
for prefix in '^!':
monkeypatch.setenv(env, f'{prefix}b,i,e')
order, unknown = _parse_env_order(base_order, env)
assert len(order) == 4
assert order == list('acdf')
assert len(unknown) == 1
with pytest.raises(ValueError):
monkeypatch.setenv(env, 'b,^e,i')
_parse_env_order(base_order, env)
with pytest.raises(ValueError):
monkeypatch.setenv(env, '!b,^e,i')
_parse_env_order(base_order, env)
.\numpy\numpy\distutils\tests\utilities.py
from numpy.testing import IS_WASM
import textwrap
import shutil
import tempfile
import os
import re
import subprocess
import sys
_compiler_status = None
def _get_compiler_status():
global _compiler_status
if _compiler_status is not None:
return _compiler_status
_compiler_status = (False, False, False)
if IS_WASM:
return _compiler_status
code = textwrap.dedent(
f"""\
import os
import sys
sys.path = {repr(sys.path)}
def configuration(parent_name='',top_path=None):
global config
from numpy.distutils.misc_util import Configuration
config = Configuration('', parent_name, top_path)
return config
from numpy.distutils.core import setup
setup(configuration=configuration)
config_cmd = config.get_config_cmd()
have_c = config_cmd.try_compile('void foo() {{}}')
print('COMPILERS:%%d,%%d,%%d' %% (have_c,
config.have_f77c(),
config.have_f90c()))
sys.exit(99)
"""
)
code = code % dict(syspath=repr(sys.path))
tmpdir = tempfile.mkdtemp()
try:
script = os.path.join(tmpdir, "setup.py")
with open(script, "w") as f:
f.write(code)
cmd = [sys.executable, "setup.py", "config"]
p = subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=tmpdir
)
out, err = p.communicate()
finally:
shutil.rmtree(tmpdir)
m = re.search(rb"COMPILERS:(\d+),(\d+),(\d+)", out)
if m:
_compiler_status = (
bool(int(m.group(1))),
bool(int(m.group(2))),
bool(int(m.group(3))),
)
return _compiler_status
def has_c_compiler():
return _get_compiler_status()[0]
def has_f77_compiler():
return _get_compiler_status()[1]
def has_f90_compiler():
return _get_compiler_status()[2]
.\numpy\numpy\distutils\tests\__init__.py
import os
import subprocess
def run_command(cmd):
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output, _ = proc.communicate()
return proc.returncode, output.decode('utf-8').strip()
SHELL = os.getenv('SHELL', '/bin/bash')
retcode, output = run_command(['echo', '$SHELL'])
print(f'Return Code: {retcode}, Output: {output}')
.\numpy\numpy\distutils\unixccompiler.py
"""
unixccompiler - can handle very long argument lists for ar.
"""
import os
import sys
import subprocess
import shlex
from distutils.errors import CompileError, DistutilsExecError, LibError
from distutils.unixccompiler import UnixCCompiler
from numpy.distutils.ccompiler import replace_method
from numpy.distutils.misc_util import _commandline_dep_string
from numpy.distutils import log
def UnixCCompiler__compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
"""Compile a single source files with a Unix-style compiler."""
ccomp = self.compiler_so
if ccomp[0] == 'aCC':
if '-Ae' in ccomp:
ccomp.remove('-Ae')
if '-Aa' in ccomp:
ccomp.remove('-Aa')
ccomp += ['-AA']
self.compiler_so = ccomp
if 'OPT' in os.environ:
from sysconfig import get_config_vars
opt = shlex.join(shlex.split(os.environ['OPT']))
gcv_opt = shlex.join(shlex.split(get_config_vars('OPT')[0]))
ccomp_s = shlex.join(self.compiler_so)
if opt not in ccomp_s:
ccomp_s = ccomp_s.replace(gcv_opt, opt)
self.compiler_so = shlex.split(ccomp_s)
llink_s = shlex.join(self.linker_so)
if opt not in llink_s:
self.linker_so = self.linker_so + shlex.split(opt)
display = '%s: %s' % (os.path.basename(self.compiler_so[0]), src)
if getattr(self, '_auto_depends', False):
deps = ['-MMD', '-MF', obj + '.d']
else:
deps = []
try:
self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + deps +
extra_postargs, display=display)
except DistutilsExecError as e:
msg = str(e)
raise CompileError(msg) from None
if deps:
if sys.platform == 'zos':
subprocess.check_output(['chtag', '-tc', 'IBM1047', obj + '.d'])
with open(obj + '.d', 'a') as f:
f.write(_commandline_dep_string(cc_args, extra_postargs, pp_opts))
replace_method(UnixCCompiler, '_compile', UnixCCompiler__compile)
def UnixCCompiler_create_static_lib(self, objects, output_libname,
output_dir=None, debug=0, target_lang=None):
"""
Build a static library in a separate sub-process.
Parameters
----------
objects : list
List of object files (.o) to be included in the library.
output_libname : str
Name of the output static library.
output_dir : str, optional
Directory where the library will be created.
debug : int, optional
Debug level (0 or 1).
target_lang : str, optional
Target language of the objects (default is None).
"""
objects : list or tuple of str
output_libname : str
output_dir : str, optional
debug : bool, optional
target_lang : str, optional
Returns
-------
None
"""
objects, output_dir = self._fix_object_args(objects, output_dir)
# 调用 _fix_object_args 方法修正 objects 和 output_dir 参数
output_filename = \
self.library_filename(output_libname, output_dir=output_dir)
# 使用 library_filename 方法生成输出的静态库文件名,包括输出目录
if self._need_link(objects, output_filename):
# 判断是否需要链接,即是否需要重新生成静态库
try:
# 尝试删除之前的 .a 文件,以便重新创建
# 在 macOS 上,ar 不支持更新 universal archives
os.unlink(output_filename)
except OSError:
pass
# 确保输出目录存在
self.mkpath(os.path.dirname(output_filename))
# 将对象文件添加到静态库中,每次最多添加 50 个对象文件
tmp_objects = objects + self.objects
while tmp_objects:
objects = tmp_objects[:50]
tmp_objects = tmp_objects[50:]
display = '%s: adding %d object files to %s' % (
os.path.basename(self.archiver[0]),
len(objects), output_filename)
# 调用 archiver 对象将对象文件添加到静态库中
self.spawn(self.archiver + [output_filename] + objects,
display=display)
# 某些 Unix 系统不再需要 ranlib,如 SunOS 4.x 可能是唯一仍需要的主要 Unix 系统
if self.ranlib:
display = '%s:@ %s' % (os.path.basename(self.ranlib[0]),
output_filename)
try:
# 调用 ranlib 命令为静态库添加索引
self.spawn(self.ranlib + [output_filename],
display=display)
except DistutilsExecError as e:
msg = str(e)
raise LibError(msg) from None
else:
# 如果静态库文件已经是最新的,跳过操作并记录调试信息
log.debug("skipping %s (up-to-date)", output_filename)
return
# 函数执行结束,返回 None
# 用新的方法替换给定类的现有方法
replace_method(UnixCCompiler, 'create_static_lib',
UnixCCompiler_create_static_lib)
.\numpy\numpy\distutils\_shell_utils.py
"""
Helper functions for interacting with the shell, and consuming shell-style
parameters provided in config files.
"""
import os
import shlex
import subprocess
__all__ = ['WindowsParser', 'PosixParser', 'NativeParser']
class CommandLineParser:
"""
An object that knows how to split and join command-line arguments.
It must be true that ``argv == split(join(argv))`` for all ``argv``.
The reverse neednt be true - `join(split(cmd))` may result in the addition
or removal of unnecessary escaping.
"""
@staticmethod
def join(argv):
""" Join a list of arguments into a command line string """
raise NotImplementedError
@staticmethod
def split(cmd):
""" Split a command line string into a list of arguments """
raise NotImplementedError
class WindowsParser:
"""
The parsing behavior used by `subprocess.call("string")` on Windows, which
matches the Microsoft C/C++ runtime.
Note that this is _not_ the behavior of cmd.
"""
@staticmethod
def join(argv):
return subprocess.list2cmdline(argv)
@staticmethod
def split(cmd):
import ctypes
try:
ctypes.windll
except AttributeError:
raise NotImplementedError
if not cmd:
return []
cmd = 'dummy ' + cmd
CommandLineToArgvW = ctypes.windll.shell32.CommandLineToArgvW
CommandLineToArgvW.restype = ctypes.POINTER(ctypes.c_wchar_p)
CommandLineToArgvW.argtypes = (ctypes.c_wchar_p, ctypes.POINTER(ctypes.c_int))
nargs = ctypes.c_int()
lpargs = CommandLineToArgvW(cmd, ctypes.byref(nargs))
args = [lpargs[i] for i in range(nargs.value)]
assert not ctypes.windll.kernel32.LocalFree(lpargs)
assert args[0] == "dummy"
return args[1:]
class PosixParser:
"""
The parsing behavior used by `subprocess.call("string", shell=True)` on Posix.
"""
@staticmethod
def join(argv):
return ' '.join(shlex.quote(arg) for arg in argv)
@staticmethod
def split(cmd):
return shlex.split(cmd, posix=True)
if os.name == 'nt':
NativeParser = WindowsParser
elif os.name == 'posix':
NativeParser = PosixParser
.\numpy\numpy\distutils\__init__.py
"""
An enhanced distutils, providing support for Fortran compilers, for BLAS,
LAPACK and other common libraries for numerical computing, and more.
Public submodules are::
misc_util
system_info
cpu_info
log
exec_command
For details, please see the *Packaging* and *NumPy Distutils User Guide*
sections of the NumPy Reference Guide.
For configuring the preference for and location of libraries like BLAS and
LAPACK, and for setting include paths and similar build options, please see
``site.cfg.example`` in the root of the NumPy repository or sdist.
"""
import warnings
from . import ccompiler
from . import unixccompiler
from .npy_pkg_config import *
warnings.warn("\n\n"
" `numpy.distutils` is deprecated since NumPy 1.23.0, as a result\n"
" of the deprecation of `distutils` itself. It will be removed for\n"
" Python >= 3.12. For older Python versions it will remain present.\n"
" It is recommended to use `setuptools < 60.0` for those Python versions.\n"
" For more details, see:\n"
" https://numpy.org/devdocs/reference/distutils_status_migration.html \n\n",
DeprecationWarning, stacklevel=2
)
del warnings
try:
from . import __config__
from numpy._pytesttester import PytestTester
test = PytestTester(__name__)
del PytestTester
except ImportError:
pass
def customized_fcompiler(plat=None, compiler=None):
from numpy.distutils.fcompiler import new_fcompiler
c = new_fcompiler(plat=plat, compiler=compiler)
c.customize()
return c
def customized_ccompiler(plat=None, compiler=None, verbose=1):
c = ccompiler.new_compiler(plat=plat, compiler=compiler, verbose=verbose)
c.customize('')
return c
.\numpy\numpy\distutils\__init__.pyi
from typing import Any
def __getattr__(name: str) -> Any: ...
.\numpy\numpy\doc\ufuncs.py
"""
===================
Universal Functions
===================
Ufuncs are, generally speaking, mathematical functions or operations that are
applied element-by-element to the contents of an array. That is, the result
in each output array element only depends on the value in the corresponding
input array (or arrays) and on no other array elements. NumPy comes with a
large suite of ufuncs, and scipy extends that suite substantially. The simplest
example is the addition operator: ::
>>> np.array([0,2,3,4]) + np.array([1,1,-1,2])
array([1, 3, 2, 6])
The ufunc module lists all the available ufuncs in numpy. Documentation on
the specific ufuncs may be found in those modules. This documentation is
intended to address the more general aspects of ufuncs common to most of
them. All of the ufuncs that make use of Python operators (e.g., +, -, etc.)
have equivalent functions defined (e.g. add() for +)
Type coercion
=============
What happens when a binary operator (e.g., +,-,\\*,/, etc) deals with arrays of
two different types? What is the type of the result? Typically, the result is
the higher of the two types. For example: ::
float32 + float64 -> float64
int8 + int32 -> int32
int16 + float32 -> float32
float32 + complex64 -> complex64
There are some less obvious cases generally involving mixes of types
(e.g. uints, ints and floats) where equal bit sizes for each are not
capable of saving all the information in a different type of equivalent
bit size. Some examples are int32 vs float32 or uint32 vs int32.
Generally, the result is the higher type of larger size than both
(if available). So: ::
int32 + float32 -> float64
uint32 + int32 -> int64
Finally, the type coercion behavior when expressions involve Python
scalars is different than that seen for arrays. Since Python has a
limited number of types, combining a Python int with a dtype=np.int8
array does not coerce to the higher type but instead, the type of the
array prevails. So the rules for Python scalars combined with arrays is
that the result will be that of the array equivalent the Python scalar
if the Python scalar is of a higher 'kind' than the array (e.g., float
vs. int), otherwise the resultant type will be that of the array.
For example: ::
Python int + int8 -> int8
Python float + int8 -> float64
ufunc methods
=============
Binary ufuncs support 4 methods.
**.reduce(arr)** applies the binary operator to elements of the array in
sequence. For example: ::
>>> np.add.reduce(np.arange(10)) # adds all elements of array
45
For multidimensional arrays, the first dimension is reduced by default: ::
>>> np.add.reduce(np.arange(10).reshape(2,5))
array([ 5, 7, 9, 11, 13])
The axis keyword can be used to specify different axes to reduce: ::
>>> np.add.reduce(np.arange(10).reshape(2,5),axis=1)
array([10, 35])
**.accumulate(arr)** applies the binary operator and generates an
equivalently shaped array that includes the accumulated amount for each
"""
>>> np.add.accumulate(np.arange(10))
>>> np.multiply.accumulate(np.arange(1,9))
>>> np.multiply.outer(np.arange(3),np.arange(4))
>>> x = np.arange(2)
>>> np.add(np.arange(2, dtype=float), np.arange(2, dtype=float), x, casting='unsafe')
.\numpy\numpy\dtypes.py
"""
This module is home to specific dtypes related functionality and their classes.
For more general information about dtypes, also see `numpy.dtype` and
:ref:`arrays.dtypes`.
Similar to the builtin ``types`` module, this submodule defines types (classes)
that are not widely used directly.
.. versionadded:: NumPy 1.25
The dtypes module is new in NumPy 1.25. Previously DType classes were
only accessible indirectly.
DType classes
-------------
The following are the classes of the corresponding NumPy dtype instances and
NumPy scalar types. The classes can be used in ``isinstance`` checks and can
also be instantiated or used directly. Direct use of these classes is not
typical, since their scalar counterparts (e.g. ``np.float64``) or strings
like ``"float64"`` can be used.
"""
__all__ = []
def _add_dtype_helper(DType, alias):
from numpy import dtypes
setattr(dtypes, DType.__name__, DType)
__all__.append(DType.__name__)
if alias:
alias = alias.removeprefix("numpy.dtypes.")
setattr(dtypes, alias, DType)
__all__.append(alias)
.\numpy\numpy\dtypes.pyi
import numpy as np
__all__: list[str]
BoolDType = np.dtype[np.bool]
Int8DType = np.dtype[np.int8]
UInt8DType = np.dtype[np.uint8]
Int16DType = np.dtype[np.int16]
UInt16DType = np.dtype[np.uint16]
Int32DType = np.dtype[np.int32]
UInt32DType = np.dtype[np.uint32]
Int64DType = np.dtype[np.int64]
UInt64DType = np.dtype[np.uint64]
ByteDType = np.dtype[np.byte]
UByteDType = np.dtype[np.ubyte]
ShortDType = np.dtype[np.short]
UShortDType = np.dtype[np.ushort]
IntDType = np.dtype[np.intc]
UIntDType = np.dtype[np.uintc]
LongDType = np.dtype[np.long]
ULongDType = np.dtype[np.ulong]
LongLongDType = np.dtype[np.longlong]
ULongLongDType = np.dtype[np.ulonglong]
Float16DType = np.dtype[np.float16]
Float32DType = np.dtype[np.float32]
Float64DType = np.dtype[np.float64]
LongDoubleDType = np.dtype[np.longdouble]
Complex64DType = np.dtype[np.complex64]
Complex128DType = np.dtype[np.complex128]
CLongDoubleDType = np.dtype[np.clongdouble]
ObjectDType = np.dtype[np.object_]
BytesDType = np.dtype[np.bytes_]
StrDType = np.dtype[np.str_]
VoidDType = np.dtype[np.void]
DateTime64DType = np.dtype[np.datetime64]
TimeDelta64DType = np.dtype[np.timedelta64]
.\numpy\numpy\exceptions.py
__all__ = [
"ComplexWarning", "VisibleDeprecationWarning", "ModuleDeprecationWarning",
"TooHardError", "AxisError", "DTypePromotionError"]
if '_is_loaded' in globals():
raise RuntimeError('Reloading numpy._globals is not allowed')
_is_loaded = True
class ComplexWarning(RuntimeWarning):
"""
The warning raised when casting a complex dtype to a real dtype.
As implemented, casting a complex number to a real discards its imaginary
part, but this behavior may not be what the user actually wants.
"""
pass
class ModuleDeprecationWarning(DeprecationWarning):
"""Module deprecation warning.
.. warning::
This warning should not be used, since nose testing is not relevant
anymore.
The nose tester turns ordinary Deprecation warnings into test failures.
That makes it hard to deprecate whole modules, because they get
imported by default. So this is a special Deprecation warning that the
nose tester will let pass without making tests fail.
"""
pass
class VisibleDeprecationWarning(UserWarning):
"""Visible deprecation warning.
By default, python will not show deprecation warnings, so this class
can be used when a very visible warning is helpful, for example because
the usage is most likely a user bug.
"""
pass
class RankWarning(RuntimeWarning):
"""Matrix rank warning.
Issued by polynomial functions when the design matrix is rank deficient.
"""
pass
class TooHardError(RuntimeError):
"""max_work was exceeded.
This is raised whenever the maximum number of candidate solutions
to consider specified by the ``max_work`` parameter is exceeded.
Assigning a finite number to max_work may have caused the operation
to fail.
"""
pass
class AxisError(ValueError, IndexError):
"""Axis supplied was invalid.
"""
pass
This is raised whenever an ``axis`` parameter is specified that is larger
than the number of array dimensions.
For compatibility with code written against older numpy versions, which
raised a mixture of :exc:`ValueError` and :exc:`IndexError` for this
situation, this exception subclasses both to ensure that
``except ValueError`` and ``except IndexError`` statements continue
to catch ``AxisError``.
.. versionadded:: 1.13
Parameters
----------
axis : int or str
The out of bounds axis or a custom exception message.
If an axis is provided, then `ndim` should be specified as well.
ndim : int, optional
The number of array dimensions.
msg_prefix : str, optional
A prefix for the exception message.
Attributes
----------
axis : int, optional
The out of bounds axis or ``None`` if a custom exception
message was provided. This should be the axis as passed by
the user, before any normalization to resolve negative indices.
.. versionadded:: 1.22
ndim : int, optional
The number of array dimensions or ``None`` if a custom exception
message was provided.
.. versionadded:: 1.22
Examples
--------
>>> array_1d = np.arange(10)
>>> np.cumsum(array_1d, axis=1)
Traceback (most recent call last):
...
numpy.exceptions.AxisError: axis 1 is out of bounds for array of dimension 1
Negative axes are preserved:
>>> np.cumsum(array_1d, axis=-2)
Traceback (most recent call last):
...
numpy.exceptions.AxisError: axis -2 is out of bounds for array of dimension 1
The class constructor generally takes the axis and arrays'
dimensionality as arguments:
>>> print(np.exceptions.AxisError(2, 1, msg_prefix='error'))
error: axis 2 is out of bounds for array of dimension 1
Alternatively, a custom exception message can be passed:
>>> print(np.exceptions.AxisError('Custom error message'))
Custom error message
"""
# Define __slots__ to restrict instance attributes for memory efficiency
__slots__ = ("axis", "ndim", "_msg")
def __init__(self, axis, ndim=None, msg_prefix=None):
# Handle different constructor forms based on arguments provided
if ndim is msg_prefix is None:
# single-argument form: directly set the error message
self._msg = axis
self.axis = None
self.ndim = None
else:
# full form: set specific attributes for axis and ndim
self._msg = msg_prefix
self.axis = axis
self.ndim = ndim
def __str__(self):
# Construct the error message based on the instance's attributes
axis = self.axis
ndim = self.ndim
if axis is ndim is None:
return self._msg
else:
msg = f"axis {axis} is out of bounds for array of dimension {ndim}"
if self._msg is not None:
msg = f"{self._msg}: {msg}"
return msg
class DTypePromotionError(TypeError):
"""Multiple DTypes could not be converted to a common one.
This exception derives from ``TypeError`` and is raised whenever dtypes
cannot be converted to a single common one. This can be because they
are of a different category/class or incompatible instances of the same
one (see Examples).
Notes
-----
Many functions will use promotion to find the correct result and
implementation. For these functions the error will typically be chained
with a more specific error indicating that no implementation was found
for the input dtypes.
Typically promotion should be considered "invalid" between the dtypes of
two arrays when `arr1 == arr2` can safely return all ``False`` because the
dtypes are fundamentally different.
Examples
--------
Datetimes and complex numbers are incompatible classes and cannot be
promoted:
>>> np.result_type(np.dtype("M8[s]"), np.complex128) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
DTypePromotionError: The DType <class 'numpy.dtype[datetime64]'> could not
be promoted by <class 'numpy.dtype[complex128]'>. This means that no common
DType exists for the given inputs. For example they cannot be stored in a
single array unless the dtype is `object`. The full list of DTypes is:
(<class 'numpy.dtype[datetime64]'>, <class 'numpy.dtype[complex128]'>)
For example for structured dtypes, the structure can mismatch and the
same ``DTypePromotionError`` is given when two structured dtypes with
a mismatch in their number of fields is given:
>>> dtype1 = np.dtype([("field1", np.float64), ("field2", np.int64)])
>>> dtype2 = np.dtype([("field1", np.float64)])
>>> np.promote_types(dtype1, dtype2) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
DTypePromotionError: field names `('field1', 'field2')` and `('field1',)`
mismatch.
"""
pass
.\numpy\numpy\exceptions.pyi
from typing import overload
__all__: list[str]
class ComplexWarning(RuntimeWarning): ...
class ModuleDeprecationWarning(DeprecationWarning): ...
class VisibleDeprecationWarning(UserWarning): ...
class RankWarning(RuntimeWarning): ...
class TooHardError(RuntimeError): ...
class DTypePromotionError(TypeError): ...
class AxisError(ValueError, IndexError):
axis: None | int
ndim: None | int
@overload
def __init__(self, axis: str, ndim: None = ..., msg_prefix: None = ...) -> None: ...
@overload
def __init__(self, axis: int, ndim: int, msg_prefix: None | str = ...) -> None: ...
def __str__(self) -> str: ...
.\numpy\numpy\f2py\auxfuncs.py
import pprint
import sys
import re
import types
from functools import reduce
from copy import deepcopy
from . import __version__
from . import cfuncs
__all__ = [
'applyrules', 'debugcapi', 'dictappend', 'errmess', 'gentitle',
'getargs2', 'getcallprotoargument', 'getcallstatement',
'getfortranname', 'getpymethoddef', 'getrestdoc', 'getusercode',
'getusercode1', 'getdimension', 'hasbody', 'hascallstatement', 'hascommon',
'hasexternals', 'hasinitvalue', 'hasnote', 'hasresultnote',
'isallocatable', 'isarray', 'isarrayofstrings',
'ischaracter', 'ischaracterarray', 'ischaracter_or_characterarray',
'iscomplex',
'iscomplexarray', 'iscomplexfunction', 'iscomplexfunction_warn',
'isdouble', 'isdummyroutine', 'isexternal', 'isfunction',
'isfunction_wrap', 'isint1', 'isint1array', 'isinteger', 'isintent_aux',
'isintent_c', 'isintent_callback', 'isintent_copy', 'isintent_dict',
'isintent_hide', 'isintent_in', 'isintent_inout', 'isintent_inplace',
'isintent_nothide', 'isintent_out', 'isintent_overwrite', 'islogical',
'islogicalfunction', 'islong_complex', 'islong_double',
'islong_doublefunction', 'islong_long', 'islong_longfunction',
'ismodule', 'ismoduleroutine', 'isoptional', 'isprivate', 'isrequired',
'isroutine', 'isscalar', 'issigned_long_longarray', 'isstring',
'isstringarray', 'isstring_or_stringarray', 'isstringfunction',
'issubroutine', 'get_f2py_modulename',
'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', 'show', 'stripcomma', 'throw_error', 'isattr_value',
'getuseblocks', 'process_f2cmap_dict'
]
f2py_version = __version__.version
errmess = sys.stderr.write
show = pprint.pprint
options = {}
debugoptions = []
wrapfuncs = 1
def outmess(t):
if options.get('verbose', 1):
sys.stdout.write(t)
def debugcapi(var):
return 'capi' in debugoptions
def _ischaracter(var):
return 'typespec' in var and var['typespec'] == 'character' and \
not isexternal(var)
def _isstring(var):
return 'typespec' in var and var['typespec'] == 'character' and \
not isexternal(var)
def ischaracter_or_characterarray(var):
return _ischaracter(var) and 'charselector' not in var
def ischaracter(var):
return ischaracter_or_characterarray(var) and not isarray(var)
def ischaracterarray(var):
return ischaracter_or_characterarray(var) and isarray(var)
return _ischaracter(var) and 'charselector' in var
def isstring(var):
return isstring_or_stringarray(var) and not isarray(var)
def isstringarray(var):
return isstring_or_stringarray(var) and isarray(var)
def isarrayofstrings(var):
return isstringarray(var) and var['dimension'][-1] == '(*)'
def isarray(var):
return 'dimension' in var and not isexternal(var)
def isscalar(var):
return not (isarray(var) or isstring(var) or isexternal(var))
def iscomplex(var):
return isscalar(var) and \
var.get('typespec') in ['complex', 'double complex']
def islogical(var):
return isscalar(var) and var.get('typespec') == 'logical'
def isinteger(var):
return isscalar(var) and var.get('typespec') == 'integer'
def isreal(var):
return isscalar(var) and var.get('typespec') == 'real'
def get_kind(var):
try:
return var['kindselector']['*']
except KeyError:
try:
return var['kindselector']['kind']
except KeyError:
pass
def isint1(var):
return var.get('typespec') == 'integer' \
and get_kind(var) == '1' and not isarray(var)
def islong_long(var):
if not isscalar(var):
return 0
if var.get('typespec') not in ['integer', 'logical']:
return 0
return get_kind(var) == '8'
def isunsigned_char(var):
if not isscalar(var):
return 0
if var.get('typespec') != 'integer':
return 0
return get_kind(var) == '-1'
def isunsigned_short(var):
if not isscalar(var):
return 0
if var.get('typespec') != 'integer':
return 0
return get_kind(var) == '-2'
def isunsigned(var):
if not isscalar(var):
return 0
if var.get('typespec') != 'integer':
return 0
return get_kind(var) == '-4'
def isunsigned_long_long(var):
if not isscalar(var):
return 0
if var.get('typespec') != 'integer':
return 0
return get_kind(var) == '-8'
def isdouble(var):
if not isscalar(var):
return 0
if not var.get('typespec') == 'real':
return 0
return get_kind(var) == '8'
def islong_double(var):
if not isscalar(var):
return 0
if not var.get('typespec') == 'real':
return 0
return get_kind(var) == '16'
def islong_complex(var):
if not iscomplex(var):
return 0
return get_kind(var) == '32'
def iscomplexarray(var):
return isarray(var) and \
var.get('typespec') in ['complex', 'double complex']
def isint1array(var):
return isarray(var) and var.get('typespec') == 'integer' \
and get_kind(var) == '1'
def isunsigned_chararray(var):
return isarray(var) and var.get('typespec') in ['integer', 'logical']\
and get_kind(var) == '-1'
def isunsigned_shortarray(var):
if not isarray(var):
return 0
if var.get('typespec') not in ['integer', 'logical']:
return 0
return get_kind(var) == '-2'
return isarray(var) and var.get('typespec') in ['integer', 'logical'] \
and get_kind(var) == '-2'
def isunsignedarray(var):
return isarray(var) and var.get('typespec') in ['integer', 'logical']\
and get_kind(var) == '-4'
def isunsigned_long_longarray(var):
return isarray(var) and var.get('typespec') in ['integer', 'logical']\
and get_kind(var) == '-8'
def issigned_chararray(var):
return isarray(var) and var.get('typespec') in ['integer', 'logical']\
and get_kind(var) == '1'
def issigned_shortarray(var):
return isarray(var) and var.get('typespec') in ['integer', 'logical']\
and get_kind(var) == '2'
def issigned_array(var):
return isarray(var) and var.get('typespec') in ['integer', 'logical']\
and get_kind(var) == '4'
def issigned_long_longarray(var):
return isarray(var) and var.get('typespec') in ['integer', 'logical']\
and get_kind(var) == '8'
def isallocatable(var):
return 'attrspec' in var and 'allocatable' in var['attrspec']
def ismutable(var):
return not ('dimension' not in var or isstring(var))
def ismoduleroutine(rout):
return 'modulename' in rout
def ismodule(rout):
return 'block' in rout and 'module' == rout['block']
def isfunction(rout):
return 'block' in rout and 'function' == rout['block']
def isfunction_wrap(rout):
if isintent_c(rout):
return 0
return wrapfuncs and isfunction(rout) and (not isexternal(rout))
def issubroutine(rout):
return 'block' in rout and 'subroutine' == rout['block']
def issubroutine_wrap(rout):
if isintent_c(rout):
return 0
return issubroutine(rout) and hasassumedshape(rout)
def isattr_value(var):
return 'value' in var.get('attrspec', [])
def hasassumedshape(rout):
if rout.get('hasassumedshape'):
return True
for a in rout['args']:
for d in rout['vars'].get(a, {}).get('dimension', []):
if d == ':':
rout['hasassumedshape'] = True
return True
return False
def requiresf90wrapper(rout):
return ismoduleroutine(rout) or hasassumedshape(rout)
def isroutine(rout):
return isfunction(rout) or issubroutine(rout)
def islogicalfunction(rout):
if not isfunction(rout):
return 0
if 'result' in rout:
a = rout['result']
else:
a = rout['name']
if a in rout['vars']:
return islogical(rout['vars'][a])
return 0
def islong_longfunction(rout):
if not isfunction(rout):
return 0
if 'result' in rout:
a = rout['result']
else:
a = rout['name']
if a in rout['vars']:
return islong_long(rout['vars'][a])
return 0
def islong_doublefunction(rout):
if not isfunction(rout):
return 0
if 'result' in rout:
a = rout['result']
else:
a = rout['name']
if a in rout['vars']:
return islong_double(rout['vars'][a])
return 0
def iscomplexfunction(rout):
if not isfunction(rout):
return 0
if 'result' in rout:
a = rout['result']
else:
a = rout['name']
if a in rout['vars']:
return iscomplex(rout['vars'][a])
return 0
def iscomplexfunction_warn(rout):
if iscomplexfunction(rout):
outmess("""\
**************************************************************
Warning: code with a function returning complex value
may not work correctly with your Fortran compiler.
When using GNU gcc/g77 compilers, codes should work
correctly for callbacks with:
f2py -c -DF2PY_CB_RETURNCOMPLEX
**************************************************************\n""")
return 1
return 0
def isstringfunction(rout):
if not isfunction(rout):
return 0
if 'result' in rout:
a = rout['result']
else:
a = rout['name']
if a in rout['vars']:
return isstring(rout['vars'][a])
return 0
def hasexternals(rout):
return 'externals' in rout and rout['externals']
def isthreadsafe(rout):
return 'f2pyenhancements' in rout and \
'threadsafe' in rout['f2pyenhancements']
def hasvariables(rout):
return 'vars' in rout and rout['vars']
def isoptional(var):
return ('attrspec' in var and 'optional' in var['attrspec'] and
'required' not in var['attrspec']) and isintent_nothide(var)
def isexternal(var):
return 'attrspec' in var and 'external' in var['attrspec']
def getdimension(var):
dimpattern = r"\((.*?)\)"
if 'attrspec' in var.keys():
if any('dimension' in s for s in var['attrspec']):
return [re.findall(dimpattern, v) for v in var['attrspec']][0]
def isrequired(var):
return not isoptional(var) and isintent_nothide(var)
def isintent_in(var):
if 'intent' not in var:
return 1
if 'hide' in var['intent']:
return 0
if 'inplace' in var['intent']:
return 0
if 'in' in var['intent']:
return 1
if 'out' in var['intent']:
return 0
if 'inout' in var['intent']:
return 0
if 'outin' in var['intent']:
return 0
return 1
def isintent_inout(var):
return ('intent' in var and ('inout' in var['intent'] or
'outin' in var['intent']) and 'in' not in var['intent'] and
'hide' not in var['intent'] and 'inplace' not in var['intent'])
def isintent_out(var):
return 'out' in var.get('intent', [])
def isintent_hide(var):
return ('intent' in var and ('hide' in var['intent'] or
('out' in var['intent'] and 'in' not in var['intent'] and
(not l_or(isintent_inout, isintent_inplace)(var)))))
def isintent_nothide(var):
return not isintent_hide(var)
def isintent_c(var):
return 'c' in var.get('intent', [])
def isintent_cache(var):
return 'cache' in var.get('intent', [])
def isintent_copy(var):
return 'copy' in var.get('intent', [])
def isintent_overwrite(var):
return 'overwrite' in var.get('intent', [])
def isintent_callback(var):
return 'callback' in var.get('intent', [])
def isintent_inplace(var):
return 'inplace' in var.get('intent', [])
def isintent_aux(var):
return 'aux' in var.get('intent', [])
def isintent_aligned4(var):
return 'aligned4' in var.get('intent', [])
def isintent_aligned8(var):
return 'aligned8' in var.get('intent', [])
def isintent_aligned16(var):
return 'aligned16' in var.get('intent', [])
isintent_dict = {
isintent_in: 'INTENT_IN',
isintent_inout: 'INTENT_INOUT',
isintent_out: 'INTENT_OUT',
isintent_hide: 'INTENT_HIDE',
isintent_cache: 'INTENT_CACHE',
isintent_c: 'INTENT_C',
isoptional: 'OPTIONAL',
isintent_inplace: 'INTENT_INPLACE',
isintent_aligned4: 'INTENT_ALIGNED4',
isintent_aligned8: 'INTENT_ALIGNED8',
isintent_aligned16: 'INTENT_ALIGNED16',
}
def isprivate(var):
return 'attrspec' in var and 'private' in var['attrspec']
def hasinitvalue(var):
return '=' in var
def hasinitvalueasstring(var):
if not hasinitvalue(var):
return 0
return var['='][0] in ['"', "'"]
def hasnote(var):
return 'note' in var
def hasresultnote(rout):
if not isfunction(rout):
return 0
if 'result' in rout:
a = rout['result']
else:
a = rout['name']
if a in rout['vars']:
return hasnote(rout['vars'][a])
return 0
def hascommon(rout):
return 'common' in rout
def containscommon(rout):
if hascommon(rout):
return 1
if hasbody(rout):
for b in rout['body']:
if containscommon(b):
return 1
return 0
def containsmodule(block):
if ismodule(block):
return 1
if not hasbody(block):
return 0
for b in block['body']:
if containsmodule(b):
return 1
return 0
def hasbody(rout):
return 'body' in rout
def hascallstatement(rout):
return getcallstatement(rout) is not None
def istrue(var):
return 1
def isfalse(var):
return 0
class F2PYError(Exception):
pass
class throw_error:
def __init__(self, mess):
self.mess = mess
def __call__(self, var):
mess = '\n\n var = %s\n Message: %s\n' % (var, self.mess)
raise F2PYError(mess)
def l_and(*f):
l1, l2 = 'lambda v', []
for i in range(len(f)):
l1 = '%s,f%d=f[%d]' % (l1, i, i)
l2.append('f%d(v)' % (i))
return eval('%s:%s' % (l1, ' and '.join(l2)))
def l_or(*f):
l1, l2 = 'lambda v', []
for i in range(len(f)):
l1 = '%s,f%d=f[%d]' % (l1, i, i)
l2.append('f%d(v)' % (i))
return eval('%s:%s' % (l1, ' or '.join(l2)))
def l_not(f):
return eval('lambda v,f=f:not f(v)')
def isdummyroutine(rout):
try:
return rout['f2pyenhancements']['fortranname'] == ''
except KeyError:
return 0
def getfortranname(rout):
try:
name = rout['f2pyenhancements']['fortranname']
if name == '':
raise KeyError
if not name:
errmess('Failed to use fortranname from %s\n' %
(rout['f2pyenhancements']))
raise KeyError
except KeyError:
name = rout['name']
return name
def getmultilineblock(rout, blockname, comment=1, counter=0):
try:
r = rout['f2pyenhancements'].get(blockname)
except KeyError:
return
if not r:
return
if counter > 0 and isinstance(r, str):
return
if isinstance(r, list):
if counter >= len(r):
return
r = r[counter]
if r[:3] == "'''":
if comment:
r = '\t/* start ' + blockname + \
' multiline (' + repr(counter) + ') */\n' + r[3:]
else:
r = r[3:]
if r[-3:] == "'''":
if comment:
r = r[:-3] + '\n\t/* end multiline (' + repr(counter) + ')*/'
else:
r = r[:-3]
else:
errmess("%s multiline block should end with `'''`: %s\n"
% (blockname, repr(r)))
return r
def getcallstatement(rout):
return getmultilineblock(rout, 'callstatement')
def getcallprotoargument(rout, cb_map={}):
r = getmultilineblock(rout, 'callprotoargument', comment=0)
if r:
return r
if hascallstatement(rout):
outmess(
'warning: callstatement is defined without callprotoargument\n')
return
from .capi_maps import getctype
arg_types, arg_types2 = [], []
if l_and(isstringfunction, l_not(isfunction_wrap))(rout):
arg_types.extend(['char*', 'size_t'])
for n in rout['args']:
var = rout['vars'][n]
if isintent_callback(var):
continue
if n in cb_map:
ctype = cb_map[n] + '_typedef'
else:
ctype = getctype(var)
if l_and(isintent_c, l_or(isscalar, iscomplex))(var):
pass
elif isstring(var):
pass
else:
if not isattr_value(var):
ctype = ctype + '*'
if ((isstring(var)
or isarrayofstrings(var)
or isstringarray(var))):
arg_types2.append('size_t')
arg_types.append(ctype)
proto_args = ','.join(arg_types + arg_types2)
if not proto_args:
proto_args = 'void'
return proto_args
def getusercode(rout):
return getmultilineblock(rout, 'usercode')
def getusercode1(rout):
return getmultilineblock(rout, 'usercode', counter=1)
def getpymethoddef(rout):
return getmultilineblock(rout, 'pymethoddef')
def getargs(rout):
sortargs, args = [], []
if 'args' in rout:
args = rout['args']
if 'sortvars' in rout:
for a in rout['sortvars']:
if a in args:
sortargs.append(a)
for a in args:
if a not in sortargs:
sortargs.append(a)
else:
sortargs = rout['args']
return args, sortargs
def getargs2(rout):
sortargs, args = [], rout.get('args', [])
auxvars = [a for a in rout['vars'].keys() if isintent_aux(rout['vars'][a])
and a not in args]
args = auxvars + args
if 'sortvars' in rout:
for a in rout['sortvars']:
if a in args:
sortargs.append(a)
for a in args:
if a not in sortargs:
sortargs.append(a)
else:
sortargs = auxvars + rout['args']
return args, sortargs
def getrestdoc(rout):
if 'f2pymultilines' not in rout:
return None
k = None
if rout['block'] == 'python module':
k = rout['block'], rout['name']
return rout['f2pymultilines'].get(k, None)
def gentitle(name):
ln = (80 - len(name) - 6) // 2
return '/*%s %s %s*/' % (ln * '*', name, ln * '*')
def flatlist(lst):
if isinstance(lst, list):
return reduce(lambda x, y, f=flatlist: x + f(y), lst, [])
return [lst]
def stripcomma(s):
if s and s[-1] == ',':
return s[:-1]
return s
def replace(str, d, defaultsep=''):
if isinstance(d, list):
return [replace(str, _m, defaultsep) for _m in d]
if isinstance(str, list):
return [replace(_m, d, defaultsep) for _m in str]
for k in 2 * list(d.keys()):
if k == 'separatorsfor':
continue
if 'separatorsfor' in d and k in d['separatorsfor']:
sep = d['separatorsfor'][k]
else:
sep = defaultsep
if isinstance(d[k], list):
str = str.replace('#%s#' % (k), sep.join(flatlist(d[k])))
else:
str = str.replace('#%s#' % (k), d[k])
return str
def dictappend(rd, ar):
if isinstance(ar, list):
for a in ar:
rd = dictappend(rd, a)
return rd
for k in ar.keys():
if k[0] == '_':
continue
if k in rd:
if isinstance(rd[k], str):
rd[k] = [rd[k]]
if isinstance(rd[k], list):
if isinstance(ar[k], list):
rd[k] = rd[k] + ar[k]
else:
rd[k].append(ar[k])
elif isinstance(rd[k], dict):
if isinstance(ar[k], dict):
if k == 'separatorsfor':
for k1 in ar[k].keys():
if k1 not in rd[k]:
rd[k][k1] = ar[k][k1]
else:
rd[k] = dictappend(rd[k], ar[k])
else:
rd[k] = ar[k]
return rd
def applyrules(rules, d, var={}):
ret = {}
if isinstance(rules, list):
for r in rules:
rr = applyrules(r, d, var)
ret = dictappend(ret, rr)
if '_break' in rr:
break
return ret
if '_check' in rules and (not rules['_check'](var)):
return ret
if 'need' in rules:
res = applyrules({'needs': rules['need']}, d, var)
if 'needs' in res:
cfuncs.append_needs(res['needs'])
return ret
for k in rules.keys():
if k == 'separatorsfor':
ret[k] = rules[k]
continue
if isinstance(rules[k], str):
ret[k] = replace(rules[k], d)
elif isinstance(rules[k], list):
ret[k] = []
for i in rules[k]:
ar = applyrules({k: i}, d, var)
if k in ar:
ret[k].append(ar[k])
elif k[0] == '_':
continue
elif isinstance(rules[k], dict):
ret[k] = []
for k1 in rules[k].keys():
if isinstance(k1, types.FunctionType) and k1(var):
if isinstance(rules[k][k1], list):
for i in rules[k][k1]:
if isinstance(i, dict):
res = applyrules({'supertext': i}, d, var)
if 'supertext' in res:
i = res['supertext']
else:
i = ''
ret[k].append(replace(i, d))
else:
i = rules[k][k1]
if isinstance(i, dict):
res = applyrules({'supertext': i}, d)
if 'supertext' in res:
i = res['supertext']
else:
i = ''
ret[k].append(replace(i, d))
else:
errmess('applyrules: ignoring rule %s.\n' % repr(rules[k]))
if isinstance(ret[k], list):
if len(ret[k]) == 1:
ret[k] = ret[k][0]
if ret[k] == []:
del ret[k]
return ret
_f2py_module_name_match = re.compile(r'\s*python\s*module\s*(?P<name>[\w_]+)',
re.I).match
_f2py_user_module_name_match = re.compile(r'\s*python\s*module\s*(?P<name>[\w_]*?'
r'__user__[\w_]*)', re.I).match
def get_f2py_modulename(source):
"""
从给定的源文件中获取F2PY模块的名称。
Parameters
----------
source : str
源文件的路径
Returns
-------
str or None
匹配到的F2PY模块的名称,如果未找到则返回None。
"""
name = None
with open(source) as f:
for line in f:
m = _f2py_module_name_match(line)
if m:
if _f2py_user_module_name_match(line):
continue
name = m.group('name')
break
return name
def getuseblocks(pymod):
"""
从Python模块的AST表示中提取所有的`use`块中的模块名。
Parameters
----------
pymod : dict
Python模块的AST表示,包含`body`字段用于表示模块的主体。
Returns
-------
list
所有有效的模块名列表,排除包含`__`的特殊模块名。
"""
all_uses = []
for inner in pymod['body']:
for modblock in inner['body']:
if modblock.get('use'):
all_uses.extend([x for x in modblock.get("use").keys() if "__" not in x])
return all_uses
def process_f2cmap_dict(f2cmap_all, new_map, c2py_map, verbose = False):
"""
更新Fortran到C类型映射字典,并返回成功映射的C类型列表。
此函数将新的映射字典集成到现有的Fortran到C类型映射字典中。确保所有键都是小写,并针对给定的C到Python映射字典验证新条目。重定义和无效条目将报告警告。
Parameters
----------
f2cmap_all : dict
将要更新的现有Fortran到C类型映射字典。它应该是一个字典,其中主键表示Fortran类型,嵌套字典映射Fortran类型说明符到相应的C类型。
new_map : dict
包含要添加到`f2cmap_all`中的新类型映射的字典。结构类似于`f2cmap_all`,主键表示Fortran类型,值是类型说明符及其C类型等价物的字典。
c2py_map : dict
用于验证`new_map`中的C类型的字典。它将C类型映射到相应的Python类型,用于确保`new_map`中指定的C类型是有效的。
verbose : boolean
一个标志,用于提供关于映射类型的信息。
Returns
-------
tuple of (dict, list)
更新后的Fortran到C类型映射字典和成功映射的C类型列表。
"""
f2cmap_mapped = []
new_map_lower = {}
for k, d1 in new_map.items():
d1_lower = {k1.lower(): v1 for k1, v1 in d1.items()}
new_map_lower[k.lower()] = d1_lower
for k, d1 in new_map_lower.items():
if k not in f2cmap_all:
f2cmap_all[k] = {}
for k1, v1 in d1.items():
if v1 in c2py_map:
if k1 in f2cmap_all[k]:
outmess(
"\tWarning: redefinition of {'%s':{'%s':'%s'->'%s'}}\n"
% (k, k1, f2cmap_all[k][k1], v1)
)
f2cmap_all[k][k1] = v1
if verbose:
outmess('\tMapping "%s(kind=%s)" to "%s"\n' % (k, k1, v1))
f2cmap_mapped.append(v1)
else:
if verbose:
errmess(
"\tIgnoring map {'%s':{'%s':'%s'}}: '%s' must be in %s\n"
% (k, k1, v1, v1, list(c2py_map.keys()))
)
return f2cmap_all, f2cmap_mapped
.\numpy\numpy\f2py\capi_maps.py
"""
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.
"""
from . import __version__
f2py_version = __version__.version
import copy
import re
import os
from .crackfortran import markoutercomma
from . import cb_rules
from ._isocbind import iso_c_binding_map, isoc_c2pycode_map, iso_c2py_map
from .auxfuncs import *
__all__ = [
'getctype', 'getstrlength', 'getarrdims', 'getpydocsign',
'getarrdocsign', 'getinit', 'sign2map', 'routsign2map', 'modsign2map',
'cb_sign2map', 'cb_routsign2map', 'common_sign2map', 'process_f2cmap_dict'
]
depargs = []
lcb_map = {}
lcb2_map = {}
c2py_map = {'double': 'float',
'float': 'float',
'long_double': 'float',
'char': 'int',
'signed_char': 'int',
'unsigned_char': 'int',
'short': 'int',
'unsigned_short': 'int',
'int': 'int',
'long': 'int',
'long_long': 'long',
'unsigned': 'int',
'complex_float': 'complex',
'complex_double': 'complex',
'complex_long_double': 'complex',
'string': 'string',
'character': 'bytes',
}
c2capi_map = {'double': 'NPY_DOUBLE',
'float': 'NPY_FLOAT',
'long_double': 'NPY_LONGDOUBLE',
'char': 'NPY_BYTE',
'unsigned_char': 'NPY_UBYTE',
'signed_char': 'NPY_BYTE',
'short': 'NPY_SHORT',
'unsigned_short': 'NPY_USHORT',
'int': 'NPY_INT',
'unsigned': 'NPY_UINT',
'long': 'NPY_LONG',
'unsigned_long': 'NPY_ULONG',
'long_long': 'NPY_LONGLONG',
'unsigned_long_long': 'NPY_ULONGLONG',
'complex_float': 'NPY_CFLOAT',
'complex_double': 'NPY_CDOUBLE',
'complex_long_double': 'NPY_CDOUBLE',
'string': 'NPY_STRING',
'character': 'NPY_STRING'}
c2pycode_map = {'double': 'd',
'float': 'f',
'long_double': 'g',
'char': 'b',
'unsigned_char': 'B',
'signed_char': 'b',
'short': 'h',
'unsigned_short': 'H',
'int': 'i',
'unsigned': 'I',
'long': 'l',
'unsigned_long': 'L',
'long_long': 'q',
'unsigned_long_long': 'Q',
'complex_float': 'F',
'complex_double': 'D',
'complex_long_double': 'G',
'string': 'S',
'character': 'c'}
c2buildvalue_map = {'double': 'd',
'float': 'f',
'char': 'b',
'signed_char': 'b',
'short': 'h',
'int': 'i',
'long': 'l',
'long_long': 'L',
'complex_float': 'N',
'complex_double': 'N',
'complex_long_double': 'N',
'string': 'y',
'character': 'c'}
f2cmap_all = {'real': {'': 'float', '4': 'float', '8': 'double',
'12': 'long_double', '16': 'long_double'},
'integer': {'': 'int', '1': 'signed_char', '2': 'short',
'4': 'int', '8': 'long_long',
'-1': 'unsigned_char', '-2': 'unsigned_short',
'-4': 'unsigned', '-8': 'unsigned_long_long'},
'complex': {'': 'complex_float', '8': 'complex_float',
'16': 'complex_double', '24': 'complex_long_double',
'32': 'complex_long_double'},
'complexkind': {'': 'complex_float', '4': 'complex_float',
'8': 'complex_double', '12': 'complex_long_double',
'16': 'complex_long_double'},
'logical': {'': 'int', '1': 'char', '2': 'short', '4': 'int',
'8': 'long_long'},
'double complex': {'': 'complex_double'},
'double precision': {'': 'double'},
'byte': {'': 'char'},
}
c2pycode_map.update(isoc_c2pycode_map)
c2py_map.update(iso_c2py_map)
f2cmap_all, _ = process_f2cmap_dict(f2cmap_all, iso_c_binding_map, c2py_map)
f2cmap_default = copy.deepcopy(f2cmap_all)
f2cmap_mapped = []
def load_f2cmap_file(f2cmap_file):
global f2cmap_all, f2cmap_mapped
f2cmap_all = copy.deepcopy(f2cmap_default)
if f2cmap_file is None:
f2cmap_file = '.f2py_f2cmap'
if not os.path.isfile(f2cmap_file):
return
try:
outmess('Reading f2cmap from {!r} ...\n'.format(f2cmap_file))
with open(f2cmap_file) as f:
d = eval(f.read().lower(), {}, {})
f2cmap_all, f2cmap_mapped = process_f2cmap_dict(f2cmap_all, d, c2py_map, True)
outmess('Successfully applied user defined f2cmap changes\n')
except Exception as msg:
errmess('Failed to apply user defined f2cmap changes: %s. Skipping.\n' % (msg))
cformat_map = {'double': '%g',
'float': '%g',
'long_double': '%Lg',
'char': '%d',
'signed_char': '%d',
'unsigned_char': '%hhu',
'short': '%hd',
'unsigned_short': '%hu',
'int': '%d',
'unsigned': '%u',
'long': '%ld',
'unsigned_long': '%lu',
'long_long': '%ld',
'complex_float': '(%g,%g)',
'complex_double': '(%g,%g)',
'complex_long_double': '(%Lg,%Lg)',
'string': '\\"%s\\"',
'character': "'%c'",
}
def getctype(var):
"""
根据变量类型信息确定其对应的 C 类型
参数:
var -- 包含类型信息的变量
返回:
ctype -- 变量的 C 类型
"""
ctype = 'void'
if isfunction(var):
if 'result' in var:
a = var['result']
else:
a = var['name']
if a in var['vars']:
return getctype(var['vars'][a])
else:
errmess('getctype: function %s has no return value?!\n' % a)
elif issubroutine(var):
return ctype
elif ischaracter_or_characterarray(var):
return 'character'
elif isstring_or_stringarray(var):
return 'string'
elif 'typespec' in var and var['typespec'].lower() in f2cmap_all:
typespec = var['typespec'].lower()
f2cmap = f2cmap_all[typespec]
ctype = f2cmap['']
if 'kindselector' in var:
if '*' in var['kindselector']:
try:
ctype = f2cmap[var['kindselector']['*']]
except KeyError:
errmess('getctype: "%s %s %s" not supported.\n' %
(var['typespec'], '*', var['kindselector']['*']))
elif 'kind' in var['kindselector']:
if typespec + 'kind' in f2cmap_all:
f2cmap = f2cmap_all[typespec + 'kind']
try:
ctype = f2cmap[var['kindselector']['kind']]
except KeyError:
if typespec in f2cmap_all:
f2cmap = f2cmap_all[typespec]
try:
ctype = f2cmap[str(var['kindselector']['kind'])]
except KeyError:
errmess('getctype: "%s(kind=%s)" is mapped to C "%s" (to override define dict(%s = dict(%s="<C typespec>")) in %s/.f2py_f2cmap file).\n'
% (typespec, var['kindselector']['kind'], ctype,
typespec, var['kindselector']['kind'], os.getcwd()))
else:
if not isexternal(var):
errmess('getctype: No C-type found in "%s", assuming void.\n' % var)
return ctype
def f2cexpr(expr):
"""
将 Fortran 表达式重写为 f2py 支持的 C 表达式
由于 f2py 缺乏合适的表达式解析器,该函数使用一种启发式方法,
假设 Fortran
# 替换表达式中的 Fortran `len` 函数为 `f2py_slen`,用于支持 Fortran 到 C/C++ 的映射
"""
arithmetic expressions are valid C arithmetic expressions when
mapping Fortran function calls to the corresponding C function/CPP
macros calls.
"""
expr = re.sub(r'\blen\b', 'f2py_slen', expr)
# 返回替换后的表达式
return expr
# 如果变量是字符串类型的函数
def getstrlength(var):
if isstringfunction(var): # 检查变量是否是字符串类型的函数
if 'result' in var: # 如果变量包含'result'字段
a = var['result'] # 将'result'字段赋给变量a
else:
a = var['name'] # 否则将'name'字段赋给变量a
if a in var['vars']: # 如果a存在于变量的'vars'字段中
return getstrlength(var['vars'][a]) # 递归调用getstrlength函数,传入a对应的变量
else:
errmess('getstrlength: function %s has no return value?!\n' % a) # 报告错误,函数没有返回值
if not isstring(var): # 如果变量不是字符串类型
errmess( # 报告错误,期望一个字符串的签名,但实际得到了其他类型
'getstrlength: expected a signature of a string but got: %s\n' % (repr(var)))
len = '1' # 将长度设置为默认值'1'
if 'charselector' in var: # 如果变量中包含'charselector'字段
a = var['charselector'] # 将'charselector'字段赋给变量a
if '*' in a: # 如果a中包含'*'
len = a['*'] # 将'*'对应的值赋给len
elif 'len' in a: # 否则如果a中包含'len'
len = f2cexpr(a['len']) # 将'len'对应的值转换成C表达式并赋给len
# 如果len符合预期的正则表达式条件,设置长度为'-1'
if re.match(r'\(\s*(\*|:)\s*\)', len) or re.match(r'(\*|:)', len):
if isintent_hide(var): # 如果意图是隐藏
errmess('getstrlength:intent(hide): expected a string with defined length but got: %s\n' % (
repr(var))) # 报告错误,期望一个具有定义长度的字符串,但实际得到其他类型
len = '-1' # 设置长度为'-1'
return len # 返回计算得到的长度
# 获取数组维度信息
def getarrdims(a, var, verbose=0):
ret = {} # 创建一个空字典作为返回结果
if isstring(var) and not isarray(var): # 如果变量是字符串并且不是数组
ret['size'] = getstrlength(var) # 获取字符串的长度信息
ret['rank'] = '0' # 数组的秩为0,表示不是数组
ret['dims'] = '' # 维度为空字符串,表示不是数组
elif isscalar(var): # 如果变量是标量(非数组,非字符串)
ret['size'] = '1' # 大小为1,表示单个元素
ret['rank'] = '0' # 数组的秩为0,表示不是数组
ret['dims'] = '' # 维度为空字符串,表示不是数组
# 如果变量是数组,执行以下操作
elif isarray(var):
# 复制数组的维度信息
dim = copy.copy(var['dimension'])
# 将维度转换为字符串,并用 '*' 连接起来,赋给返回字典中的 'size' 键
ret['size'] = '*'.join(dim)
try:
# 尝试使用 eval 函数计算 'size' 字符串表达式的值,并将结果转换为字符串
ret['size'] = repr(eval(ret['size']))
except Exception:
# 如果计算失败,则忽略异常
pass
# 将维度列表转换为以逗号分隔的字符串,赋给返回字典中的 'dims' 键
ret['dims'] = ','.join(dim)
# 计算维度列表的长度,并将结果转换为字符串,赋给返回字典中的 'rank' 键
ret['rank'] = repr(len(dim))
# 根据维度列表生成一个以 -1 填充的列表,并将结果转换为字符串后去掉首尾的方括号,赋给返回字典中的 'rank*[-1]' 键
ret['rank*[-1]'] = repr(len(dim) * [-1])[1:-1]
# 遍历维度列表,解决依赖关系
for i in range(len(dim)): # solve dim for dependencies
v = []
# 如果当前维度在依赖参数列表中,则将其添加到 v 中
if dim[i] in depargs:
v = [dim[i]]
else:
# 否则,遍历依赖参数列表,查找与当前维度匹配的参数,并将其添加到 v 中
for va in depargs:
if re.match(r'.*?\b%s\b.*' % va, dim[i]):
v.append(va)
# 遍历 v 中的每个参数
for va in v:
# 如果当前参数在依赖参数列表中的位置在 a 的后面,则将当前维度设置为 '*'
if depargs.index(va) > depargs.index(a):
dim[i] = '*'
break
# 初始化 'setdims' 和计数器 i
ret['setdims'], i = '', -1
# 遍历维度列表 dim
for d in dim:
i = i + 1
# 如果当前维度不在 ['*', ':', '(*)', '(:)'] 中
if d not in ['*', ':', '(*)', '(:)']:
# 将格式化后的字符串添加到 'setdims' 中,用于后续变量名的维度设置
ret['setdims'] = '%s#varname#_Dims[%d]=%s,' % (
ret['setdims'], i, d)
# 如果 'setdims' 不为空,则去掉末尾的逗号
if ret['setdims']:
ret['setdims'] = ret['setdims'][:-1]
# 初始化 'cbsetdims' 和计数器 i
ret['cbsetdims'], i = '', -1
# 遍历数组的维度信息列表
for d in var['dimension']:
i = i + 1
# 如果当前维度不在 ['*', ':', '(*)', '(:)'] 中
if d not in ['*', ':', '(*)', '(:)']:
# 将格式化后的字符串添加到 'cbsetdims' 中,用于回调函数中变量名的维度设置
ret['cbsetdims'] = '%s#varname#_Dims[%d]=%s,' % (
ret['cbsetdims'], i, d)
# 如果当前变量是输入参数,并且维度为 '*'
elif isintent_in(var):
# 输出警告信息,表示假定为定形数组,并用 0 替换当前维度
outmess('getarrdims:warning: assumed shape array, using 0 instead of %r\n'
% (d))
# 将格式化后的字符串添加到 'cbsetdims' 中,用于回调函数中变量名的维度设置
ret['cbsetdims'] = '%s#varname#_Dims[%d]=%s,' % (
ret['cbsetdims'], i, 0)
# 如果开启了详细输出
elif verbose:
# 输出错误信息,表示在回调函数中,数组参数 %s 必须有有界维度,但当前为 %s
errmess(
'getarrdims: If in call-back function: array argument %s must have bounded dimensions: got %s\n' % (repr(a), repr(d)))
# 如果 'cbsetdims' 不为空,则去掉末尾的逗号
if ret['cbsetdims']:
ret['cbsetdims'] = ret['cbsetdims'][:-1]
# 返回函数结果
return ret
def getpydocsign(a, var):
global lcb_map
# 如果变量是函数
if isfunction(var):
# 如果函数有'result'属性,将其赋值给af,否则将函数名赋值给af
if 'result' in var:
af = var['result']
else:
af = var['name']
# 如果函数名在var['vars']中
if af in var['vars']:
# 递归调用getpydocsign函数,处理函数返回值的情况
return getpydocsign(af, var['vars'][af])
else:
# 报错,函数%s没有返回值?!
errmess('getctype: function %s has no return value?!\\n' % af)
return '', ''
# 默认签名和输出签名初始值为a
sig, sigout = a, a
opt = ''
# 如果变量是输入类型
if isintent_in(var):
opt = 'input'
# 如果变量是输入输出类型
elif isintent_inout(var):
opt = 'in/output'
out_a = a
# 如果变量是输出类型
if isintent_out(var):
# 遍历var['intent'],查找以'out='开头的键
for k in var['intent']:
if k[:4] == 'out=':
out_a = k[4:] # 获取输出变量名
break
init = ''
ctype = getctype(var)
# 如果变量有初始值
if hasinitvalue(var):
# 调用getinit函数获取初始值和显示的初始值
init, showinit = getinit(a, var)
init = ', optional\\n Default: %s' % showinit
# 如果变量是标量类型
if isscalar(var):
if isintent_inout(var):
# 设置标量的签名
sig = '%s : %s rank-0 array(%s,\'%s\')%s' % (a, opt, c2py_map[ctype],
c2pycode_map[ctype], init)
else:
# 设置标量的签名
sig = '%s : %s %s%s' % (a, opt, c2py_map[ctype], init)
# 设置标量的输出签名
sigout = '%s : %s' % (out_a, c2py_map[ctype])
# 如果变量是字符串类型
elif isstring(var):
if isintent_inout(var):
# 设置字符串的签名
sig = '%s : %s rank-0 array(string(len=%s),\'c\')%s' % (
a, opt, getstrlength(var), init)
else:
# 设置字符串的签名
sig = '%s : %s string(len=%s)%s' % (
a, opt, getstrlength(var), init)
# 设置字符串的输出签名
sigout = '%s : string(len=%s)' % (out_a, getstrlength(var))
# 如果变量是数组类型
elif isarray(var):
dim = var['dimension']
rank = repr(len(dim))
# 设置数组的签名
sig = '%s : %s rank-%s array(\'%s\') with bounds (%s)%s' % (a, opt, rank,
c2pycode_map[
ctype],
','.join(dim), init)
# 如果输入变量名和输出变量名相同
if a == out_a:
# 设置数组的输出签名
sigout = '%s : rank-%s array(\'%s\') with bounds (%s)'\
% (a, rank, c2pycode_map[ctype], ','.join(dim))
else:
# 设置数组的输出签名,包括存储信息
sigout = '%s : rank-%s array(\'%s\') with bounds (%s) and %s storage'\
% (out_a, rank, c2pycode_map[ctype], ','.join(dim), a)
# 如果变量是外部变量类型
elif isexternal(var):
ua = ''
# 如果变量在lcb_map中,并且映射存在于lcb2_map中,并且lcb2_map[lcb_map[a]]中有'argname'键
if a in lcb_map and lcb_map[a] in lcb2_map and 'argname' in lcb2_map[lcb_map[a]]:
ua = lcb2_map[lcb_map[a]]['argname']
# 如果ua不等于a,设置ua为'=> %s'
if not ua == a:
ua = ' => %s' % ua
else:
ua = ''
# 设置外部变量的签名
sig = '%s : call-back function%s' % (a, ua)
sigout = sig
else:
# 报错,无法解析%s的文档签名
errmess(
'getpydocsign: Could not resolve docsignature for "%s".\\n' % a)
# 返回签名和输出签名
return sig, sigout
def getarrdocsign(a, var):
ctype = getctype(var)
# 如果变量 var 是字符串并且不是数组
if isstring(var) and (not isarray(var)):
# 根据变量 var 的长度获取字符串的长度信息,构造数组签名字符串
sig = '%s : rank-0 array(string(len=%s),\'c\')' % (a,
getstrlength(var))
# 如果变量 var 是标量
elif isscalar(var):
# 根据变量的类型获取对应的 Python 类型和代码映射,构造数组签名字符串
sig = '%s : rank-0 array(%s,\'%s\')' % (a, c2py_map[ctype],
c2pycode_map[ctype],)
# 如果变量 var 是数组
elif isarray(var):
# 获取变量 var 的维度信息
dim = var['dimension']
# 获取数组的维度数并转换为字符串形式
rank = repr(len(dim))
# 根据变量的类型获取对应的代码映射,构造数组签名字符串
sig = '%s : rank-%s array(\'%s\') with bounds (%s)' % (a, rank,
c2pycode_map[
ctype],
','.join(dim))
# 返回构造好的数组签名字符串
return sig
# 定义函数 getinit,接收两个参数 a 和 var
def getinit(a, var):
# 如果 var 是字符串类型
if isstring(var):
# 初始化 init 和 showinit 分别为空字符串和单引号空字符串
init, showinit = '""', "''"
else:
# 否则初始化 init 和 showinit 为空字符串
init, showinit = '', ''
# 如果 var 具有初始值
if hasinitvalue(var):
# 将 init 设置为 var['='],showinit 设置为 init
init = var['=']
showinit = init
# 如果 var 是复数或复数数组
if iscomplex(var) or iscomplexarray(var):
# 初始化一个空字典 ret
ret = {}
try:
# 尝试获取 var["="] 的值并处理
v = var["="]
# 如果值包含逗号
if ',' in v:
# 将去除首尾括号后的字符串通过 '@,@' 分隔,并存入 ret 字典
ret['init.r'], ret['init.i'] = markoutercomma(
v[1:-1]).split('@,@')
else:
# 否则将值解析为表达式,并分别获取实部和虚部
v = eval(v, {}, {})
ret['init.r'], ret['init.i'] = str(v.real), str(v.imag)
except Exception:
# 如果出错则抛出 ValueError 异常
raise ValueError(
'getinit: expected complex number `(r,i)\' but got `%s\' as initial value of %r.' % (init, a))
# 如果 var 是数组类型,则重新设置 init 为复数格式字符串
if isarray(var):
init = '(capi_c.r=%s,capi_c.i=%s,capi_c)' % (
ret['init.r'], ret['init.i'])
# 如果 var 是字符串类型
elif isstring(var):
# 如果 init 是空的,将其设置为双引号包裹的 var["="] 值的处理字符串,同时设置 showinit 为单引号包裹
if not init:
init, showinit = '""', "''"
if init[0] == "'":
init = '"%s"' % (init[1:-1].replace('"', '\\"'))
if init[0] == '"':
showinit = "'%s'" % (init[1:-1])
# 返回 init 和 showinit
return init, showinit
# 如果变量是外部回调函数
if isexternal(var):
# 将回调函数的名称映射到 ret 字典中的 'cbnamekey'
ret['cbnamekey'] = a
# 如果回调函数在 lcb_map 中存在
if a in lcb_map:
# 设置 ret 字典中的回调函数名称为 lcb_map 中对应的值
ret['cbname'] = lcb_map[a]
# 设置最大参数数目为 lcb_map 对应值在 lcb2_map 中的最大参数数目
ret['maxnofargs'] = lcb2_map[lcb_map[a]]['maxnofargs']
# 设置可选参数数目为 lcb_map 对应值在 lcb2_map 中的可选参数数目
ret['nofoptargs'] = lcb2_map[lcb_map[a]]['nofoptargs']
# 设置回调函数的文档字符串为 lcb_map 对应值在 lcb2_map 中的文档字符串
ret['cbdocstr'] = lcb2_map[lcb_map[a]]['docstr']
# 设置回调函数的 LaTeX 文档字符串为 lcb_map 对应值在 lcb2_map 中的 LaTeX 文档字符串
ret['cblatexdocstr'] = lcb2_map[lcb_map[a]]['latexdocstr']
else:
# 如果回调函数在 lcb_map 中不存在,设置 ret 字典中的回调函数名称为 a
ret['cbname'] = a
# 输出错误信息,说明外部回调函数 a 不在 lcb_map 中
errmess('sign2map: Confused: external %s is not in lcb_map%s.\n' % (
a, list(lcb_map.keys())))
# 如果变量是字符串类型
if isstring(var):
# 设置 ret 字典中的 'length' 键为字符串 var 的长度
ret['length'] = getstrlength(var)
# 如果变量是数组类型
if isarray(var):
# 将 ret 字典更新为包含数组维度信息的新字典
ret = dictappend(ret, getarrdims(a, var))
# 复制数组变量的维度到 dim 变量中
dim = copy.copy(var['dimension'])
# 如果 ret 字典中的 'ctype' 键存在于 c2capi_map 中
if ret['ctype'] in c2capi_map:
# 设置 ret 字典中的 'atype' 键为 c2capi_map 中 'ctype' 键对应的值
ret['atype'] = c2capi_map[ret['ctype']]
# 设置 ret 字典中的 'elsize' 键为变量的元素大小
ret['elsize'] = get_elsize(var)
# 调试信息
if debugcapi(var):
# 定义调试信息列表 il
il = [isintent_in, 'input', isintent_out, 'output',
isintent_inout, 'inoutput', isrequired, 'required',
isoptional, 'optional', isintent_hide, 'hidden',
iscomplex, 'complex scalar',
l_and(isscalar, l_not(iscomplex)), 'scalar',
isstring, 'string', isarray, 'array',
iscomplexarray, 'complex array', isstringarray, 'string array',
iscomplexfunction, 'complex function',
l_and(isfunction, l_not(iscomplexfunction)), 'function',
isexternal, 'callback',
isintent_callback, 'callback',
isintent_aux, 'auxiliary',
]
# 初始化结果列表 rl
rl = []
# 遍历 il 列表,每两个元素一组判断是否符合条件,并添加到 rl 中
for i in range(0, len(il), 2):
if il[i](var):
rl.append(il[i + 1])
# 如果变量是字符串类型,添加字符串长度信息到 rl
if isstring(var):
rl.append('slen(%s)=%s' % (a, ret['length']))
# 如果变量是数组类型,添加数组维度信息到 rl
if isarray(var):
ddim = ','.join(
map(lambda x, y: '%s|%s' % (x, y), var['dimension'], dim))
rl.append('dims(%s)' % ddim)
# 如果变量是外部回调函数,设置调试信息
if isexternal(var):
ret['vardebuginfo'] = 'debug-capi:%s=>%s:%s' % (
a, ret['cbname'], ','.join(rl))
else:
# 设置其他类型变量的调试信息
ret['vardebuginfo'] = 'debug-capi:%s %s=%s:%s' % (
ret['ctype'], a, ret['showinit'], ','.join(rl))
# 如果变量是标量,根据 'ctype' 设置调试显示值
if isscalar(var):
if ret['ctype'] in cformat_map:
ret['vardebugshowvalue'] = 'debug-capi:%s=%s' % (
a, cformat_map[ret['ctype']])
# 如果变量是字符串,设置调试显示值为字符串长度和值的格式化字符串
if isstring(var):
ret['vardebugshowvalue'] = 'debug-capi:slen(%s)=%s %s="%s"' % (
a, ret['length'], a, a)
# 如果变量是外部回调函数,设置调试显示值
if isexternal(var):
ret['vardebugshowvalue'] = 'debug-capi:%s=%p' % (a)
# 如果 ret 字典中的 'ctype' 键存在于 cformat_map 中
if ret['ctype'] in cformat_map:
# 设置 ret 字典中的 'varshowvalue' 键为格式化后的值的显示名称
ret['varshowvalue'] = '#name#:%s=%s' % (a, cformat_map[ret['ctype']])
# 设置 ret 字典中的 'showvalueformat' 键为格式化后的值的显示格式
ret['showvalueformat'] = '%s' % (cformat_map[ret['ctype']])
# 如果变量是字符串,设置 ret 字典中的 'varshowvalue' 键为字符串长度和字符串值的格式化字符串
if isstring(var):
ret['varshowvalue'] = '#name#:slen(%s)=%d %s="%s"' % (a, ret['length'], a, a)
# 获取变量的 Python 文档签名和输出签名,存储在 ret 字典中的 'pydocsign' 和 'pydocsignout' 键中
ret['pydocsign'], ret['pydocsignout'] = getpydocsign(a, var)
# 如果变量 var 中包含名为 'note' 的键
if hasnote(var):
# 将 'note' 键对应的值赋给返回字典 ret 的 'note' 键
ret['note'] = var['note']
# 返回处理后的字典 ret
return ret
# 将函数 routsign2map 定义为将给定的 rout 字典转换为一个映射字典的函数
def routsign2map(rout):
"""
name,NAME,begintitle,endtitle
rname,ctype,rformat
routdebugshowvalue
"""
# 声明 lcb_map 为全局变量
global lcb_map
# 从 rout 字典中获取 'name' 键对应的值,并赋给 name 变量
name = rout['name']
# 使用 getfortranname 函数获取 rout 对应的 Fortran 名称,并赋给 fname 变量
fname = getfortranname(rout)
# 创建一个新的字典 ret,包含一些与 name 和 fname 相关的派生值
ret = {'name': name,
'texname': name.replace('_', '\\_'), # 生成 LaTeX 格式的 name
'name_lower': name.lower(), # name 的小写形式
'NAME': name.upper(), # name 的大写形式
'begintitle': gentitle(name), # 生成 name 的标题形式
'endtitle': gentitle('end of %s' % name), # 生成 name 结尾的标题形式
'fortranname': fname, # Fortran 名称
'FORTRANNAME': fname.upper(), # Fortran 名称的大写形式
'callstatement': getcallstatement(rout) or '', # 获取调用语句,如果不存在则为空字符串
'usercode': getusercode(rout) or '', # 获取用户代码,如果不存在则为空字符串
'usercode1': getusercode1(rout) or '', # 获取用户代码1,如果不存在则为空字符串
}
# 根据 fname 是否包含下划线来确定 F_FUNC 的值
if '_' in fname:
ret['F_FUNC'] = 'F_FUNC_US'
else:
ret['F_FUNC'] = 'F_FUNC'
# 根据 name 是否包含下划线来确定 F_WRAPPEDFUNC 的值
if '_' in name:
ret['F_WRAPPEDFUNC'] = 'F_WRAPPEDFUNC_US'
else:
ret['F_WRAPPEDFUNC'] = 'F_WRAPPEDFUNC'
# 初始化空字典 lcb_map
lcb_map = {}
# 如果 rout 中包含 'use' 键
if 'use' in rout:
# 遍历 rout['use'] 中的每个键
for u in rout['use'].keys():
# 如果 u 在 cb_rules.cb_map 中
if u in cb_rules.cb_map:
# 遍历 cb_rules.cb_map[u] 中的每个元素 un
for un in cb_rules.cb_map[u]:
ln = un[0]
# 如果 rout['use'][u] 中包含 'map' 键
if 'map' in rout['use'][u]:
# 遍历 rout['use'][u]['map'] 中的每个键 k
for k in rout['use'][u]['map'].keys():
# 如果 rout['use'][u]['map'][k] 等于 un[0]
if rout['use'][u]['map'][k] == un[0]:
ln = k # 将 k 赋给 ln
break # 结束内部循环
lcb_map[ln] = un[1] # 将 un[1] 添加到 lcb_map 中
# 如果 rout 中没有 'use' 键,但有 'externals' 键且 rout['externals'] 不为空
elif 'externals' in rout and rout['externals']:
# 输出错误信息,指出函数 routsign2map 中出现了错误情况
errmess('routsign2map: Confused: function %s has externals %s but no "use" statement.\n' % (
ret['name'], repr(rout['externals'])))
# 获取调用协议参数,使用 rout 和 lcb_map,如果不存在则为空字符串
ret['callprotoargument'] = getcallprotoargument(rout, lcb_map) or ''
# 如果 rout 是一个函数对象
if isfunction(rout):
# 如果 rout 包含 'result' 键
if 'result' in rout:
# 将 rout['result'] 赋给变量 a
a = rout['result']
else:
# 否则将 rout['name'] 赋给变量 a
a = rout['name']
# 将变量 a 赋给返回字典 ret 的 'rname' 键
ret['rname'] = a
# 调用 getpydocsign 函数获取签名信息,将结果分别赋给 ret['pydocsign'] 和 ret['pydocsignout']
ret['pydocsign'], ret['pydocsignout'] = getpydocsign(a, rout)
# 调用 getctype 函数获取 rout['vars'][a] 的类型,将结果赋给 ret['ctype']
ret['ctype'] = getctype(rout['vars'][a])
# 如果 rout 包含结果的注释信息
if hasresultnote(rout):
# 将 rout['vars'][a]['note'] 赋给 ret['resultnote']
ret['resultnote'] = rout['vars'][a]['note']
# 将 rout['vars'][a]['note'] 设置为 ['See elsewhere.'],替换原有注释信息
rout['vars'][a]['note'] = ['See elsewhere.']
# 如果 ret['ctype'] 在 c2buildvalue_map 中有对应的值
if ret['ctype'] in c2buildvalue_map:
# 将 c2buildvalue_map[ret['ctype']] 的值赋给 ret['rformat']
ret['rformat'] = c2buildvalue_map[ret['ctype']]
else:
# 否则将 'O' 赋给 ret['rformat'],并记录错误消息
ret['rformat'] = 'O'
errmess('routsign2map: no c2buildvalue key for type %s\n' % (repr(ret['ctype'])))
# 如果启用了 debugcapi(rout) 调试模式
if debugcapi(rout):
# 如果 ret['ctype'] 在 cformat_map 中有对应的格式化方式
if ret['ctype'] in cformat_map:
# 设置 ret['routdebugshowvalue'] 为 'debug-capi:%s=%s' 的格式化字符串
ret['routdebugshowvalue'] = 'debug-capi:%s=%s' % (a, cformat_map[ret['ctype']])
# 如果 rout 是字符串函数
if isstringfunction(rout):
# 设置 ret['routdebugshowvalue'] 为 'debug-capi:slen(%s)=%%d %s=\\"%%s\\"' 的格式化字符串
ret['routdebugshowvalue'] = 'debug-capi:slen(%s)=%%d %s=\\"%%s\\"' % (a, a)
# 如果 rout 是字符串函数
if isstringfunction(rout):
# 获取 rout['vars'][a] 的字符串长度,将结果赋给 ret['rlength']
ret['rlength'] = getstrlength(rout['vars'][a])
# 如果字符串长度为 '-1',记录错误消息并设置 ret['rlength'] 为 '10'
if ret['rlength'] == '-1':
errmess('routsign2map: expected explicit specification of the length of the string returned by the fortran function %s; taking 10.\n' % (repr(rout['name'])))
ret['rlength'] = '10'
# 如果 rout 中有注释信息
if hasnote(rout):
# 将 rout['note'] 赋给 ret['note']
ret['note'] = rout['note']
# 将 rout['note'] 设置为 ['See elsewhere.'],替换原有注释信息
rout['note'] = ['See elsewhere.']
# 返回最终的结果字典 ret
return ret
def modsign2map(m):
"""
将模块的签名映射到特定格式的字典
"""
if ismodule(m):
# 如果 m 是模块,则设置特定的键值对
ret = {'f90modulename': m['name'],
'F90MODULENAME': m['name'].upper(),
'texf90modulename': m['name'].replace('_', '\\_')}
else:
# 如果 m 不是模块,则设置另一组特定的键值对
ret = {'modulename': m['name'],
'MODULENAME': m['name'].upper(),
'texmodulename': m['name'].replace('_', '\\_')}
# 获取模块的文档字符串列表,如果不存在则返回空列表
ret['restdoc'] = getrestdoc(m) or []
# 如果模块有注释信息,则添加到结果字典中
if hasnote(m):
ret['note'] = m['note']
# 获取模块的用户代码字符串,如果不存在则为空字符串
ret['usercode'] = getusercode(m) or ''
# 获取模块的另一部分用户代码字符串,如果不存在则为空字符串
ret['usercode1'] = getusercode1(m) or ''
# 如果模块有函数体,则获取第一个函数体的用户代码字符串,否则为空字符串
if m['body']:
ret['interface_usercode'] = getusercode(m['body'][0]) or ''
else:
ret['interface_usercode'] = ''
# 获取模块的 Python 方法定义字符串,如果不存在则为空字符串
ret['pymethoddef'] = getpymethoddef(m) or ''
# 如果模块中包含 'coutput' 键,则添加到结果字典中
if 'coutput' in m:
ret['coutput'] = m['coutput']
# 如果模块中包含 'f2py_wrapper_output' 键,则添加到结果字典中
if 'f2py_wrapper_output' in m:
ret['f2py_wrapper_output'] = m['f2py_wrapper_output']
# 返回整理后的结果字典
return ret
def cb_sign2map(a, var, index=None):
"""
将回调函数参数的签名映射到特定格式的字典
"""
ret = {'varname': a}
ret['varname_i'] = ret['varname']
# 获取变量的 C 类型字符串
ret['ctype'] = getctype(var)
# 如果 C 类型在 c2capi_map 中存在映射,则设置相关字段
if ret['ctype'] in c2capi_map:
ret['atype'] = c2capi_map[ret['ctype']]
ret['elsize'] = get_elsize(var)
# 如果 C 类型在 cformat_map 中存在格式化字符串,则设置显示值格式字段
if ret['ctype'] in cformat_map:
ret['showvalueformat'] = '%s' % (cformat_map[ret['ctype']])
# 如果变量是数组,则追加数组维度信息到结果字典中
if isarray(var):
ret = dictappend(ret, getarrdims(a, var))
# 获取变量的 Python 文档签名和输出签名
ret['pydocsign'], ret['pydocsignout'] = getpydocsign(a, var)
# 如果变量有注释信息,则添加到结果字典中,并将原变量的注释设置为 ['See elsewhere.']
if hasnote(var):
ret['note'] = var['note']
var['note'] = ['See elsewhere.']
# 返回整理后的结果字典
return ret
def cb_routsign2map(rout, um):
"""
将回调函数的签名映射到特定格式的字典
"""
ret = {'name': 'cb_%s_in_%s' % (rout['name'], um),
'returncptr': ''}
# 如果回调函数是意图回调函数,则设置相关字段
if isintent_callback(rout):
if '_' in rout['name']:
F_FUNC = 'F_FUNC_US'
else:
F_FUNC = 'F_FUNC'
# 设置回调函数名称和 static 属性
ret['callbackname'] = '%s(%s,%s)' \
% (F_FUNC,
rout['name'].lower(),
rout['name'].upper(),
)
ret['static'] = 'extern'
else:
# 否则,直接设置回调函数名称和 static 属性
ret['callbackname'] = ret['name']
ret['static'] = 'static'
# 设置回调函数的参数名称、标题等信息
ret['argname'] = rout['name']
ret['begintitle'] = gentitle(ret['name'])
ret['endtitle'] = gentitle('end of %s' % ret['name'])
# 获取回调函数的 C 类型字符串和返回类型
ret['ctype'] = getctype(rout)
ret['rctype'] = 'void'
# 如果返回类型是字符串,则设置返回类型为 void
if ret['ctype'] == 'string':
ret['rctype'] = 'void'
else:
ret['rctype'] = ret['ctype']
# 如果返回类型不是 void,则根据是否为复杂函数设置返回值指针的相关代码
if ret['rctype'] != 'void':
if iscomplexfunction(rout):
ret['returncptr'] = """
return_value=
"""
else:
ret['returncptr'] = 'return_value='
# 如果回调函数的 C 类型在 cformat_map 中存在格式化字符串,则设置显示值格式字段
if ret['ctype'] in cformat_map:
ret['showvalueformat'] = '%s' % (cformat_map[ret['ctype']])
# 如果回调函数是字符串函数,则获取其字符串长度信息
if isstringfunction(rout):
ret['strlength'] = getstrlength(rout)
# 返回整理后的结果字典
return ret
# 如果 rout 是一个函数
if isfunction(rout):
# 如果 rout 字典中包含 'result' 键
if 'result' in rout:
# 将 a 设为 rout['result'] 对应的值
a = rout['result']
else:
# 否则将 a 设为 rout['name'] 对应的值
a = rout['name']
# 如果 rout['vars'][a] 中有注释
if hasnote(rout['vars'][a]):
# 将 ret 字典中的 'note' 键设为 rout['vars'][a] 中 'note' 键对应的值
ret['note'] = rout['vars'][a]['note']
# 将 rout['vars'][a] 中 'note' 键对应的值设为 ['See elsewhere.']
rout['vars'][a]['note'] = ['See elsewhere.']
# 将 ret 字典中的 'rname' 键设为 a 的值
ret['rname'] = a
# 调用 getpydocsign 函数,将其返回值分别设为 ret 字典中的 'pydocsign' 和 'pydocsignout' 键的值
ret['pydocsign'], ret['pydocsignout'] = getpydocsign(a, rout)
# 如果 rout 是复杂函数
if iscomplexfunction(rout):
# 将 ret 字典中的 'rctype' 键设为空字符串
ret['rctype'] = ""
#ifdef F2PY_CB_RETURNCOMPLEX
#ctype#
#else
void
#endif
"""
else:
if hasnote(rout):
ret['note'] = rout['note']
rout['note'] = ['See elsewhere.']
nofargs = 0
nofoptargs = 0
if 'args' in rout and 'vars' in rout:
for a in rout['args']:
var = rout['vars'][a]
if l_or(isintent_in, isintent_inout)(var):
nofargs = nofargs + 1
if isoptional(var):
nofoptargs = nofoptargs + 1
ret['maxnofargs'] = repr(nofargs)
ret['nofoptargs'] = repr(nofoptargs)
if hasnote(rout) and isfunction(rout) and 'result' in rout:
ret['routnote'] = rout['note']
rout['note'] = ['See elsewhere.']
return ret
def common_sign2map(a, var):
ret = {'varname': a, 'ctype': getctype(var)}
if isstringarray(var):
ret['ctype'] = 'char'
if ret['ctype'] in c2capi_map:
ret['atype'] = c2capi_map[ret['ctype']]
ret['elsize'] = get_elsize(var)
if ret['ctype'] in cformat_map:
ret['showvalueformat'] = '%s' % (cformat_map[ret['ctype']])
if isarray(var):
ret = dictappend(ret, getarrdims(a, var))
elif isstring(var):
ret['size'] = getstrlength(var)
ret['rank'] = '1'
ret['pydocsign'], ret['pydocsignout'] = getpydocsign(a, var)
if hasnote(var):
ret['note'] = var['note']
var['note'] = ['See elsewhere.']
ret['arrdocstr'] = getarrdocsign(a, var)
return ret
.\numpy\numpy\f2py\cb_rules.py
"""
Build call-back mechanism 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.
"""
from . import __version__
from .auxfuncs import (
applyrules, debugcapi, dictappend, errmess, getargs, hasnote, isarray,
iscomplex, iscomplexarray, iscomplexfunction, isfunction, isintent_c,
isintent_hide, isintent_in, isintent_inout, isintent_nothide,
isintent_out, isoptional, isrequired, isscalar, isstring,
isstringfunction, issubroutine, l_and, l_not, l_or, outmess, replace,
stripcomma, throw_error
)
from . import cfuncs
f2py_version = __version__.version
cb_routine_rules = {
'cbtypedefs': 'typedef #rctype#(*#name#_typedef)(#optargs_td##args_td##strarglens_td##noargs#);',
'body': """
#begintitle#
typedef struct {
PyObject *capi;
PyTupleObject *args_capi;
int nofargs;
jmp_buf jmpbuf;
} #name#_t;
#if defined(F2PY_THREAD_LOCAL_DECL) && !defined(F2PY_USE_PYTHON_TLS)
static F2PY_THREAD_LOCAL_DECL #name#_t *_active_#name# = NULL;
static #name#_t *swap_active_#name#(#name#_t *ptr) {
#name#_t *prev = _active_#name#;
_active_#name# = ptr;
return prev;
}
static #name#_t *get_active_#name#(void) {
return _active_#name#;
}
#else
static #name#_t *swap_active_#name#(#name#_t *ptr) {
char *key = "__f2py_cb_#name#";
return (#name#_t *)F2PySwapThreadLocalCallbackPtr(key, ptr);
}
static #name#_t *get_active_#name#(void) {
char *key = "__f2py_cb_#name#";
return (#name#_t *)F2PyGetThreadLocalCallbackPtr(key);
}
#endif
/*typedef #rctype#(*#name#_typedef)(#optargs_td##args_td##strarglens_td##noargs#);*/
#static# #rctype# #callbackname# (#optargs##args##strarglens##noargs#) {
#name#_t cb_local = { NULL, NULL, 0 };
#name#_t *cb = NULL;
PyTupleObject *capi_arglist = NULL;
PyObject *capi_return = NULL;
PyObject *capi_tmp = NULL;
PyObject *capi_arglist_list = NULL;
int capi_j,capi_i = 0;
int capi_longjmp_ok = 1;
#decl#
#ifdef F2PY_REPORT_ATEXIT
f2py_cb_start_clock();
#endif
cb = get_active_#name#();
if (cb == NULL) {
capi_longjmp_ok = 0;
cb = &cb_local;
}
capi_arglist = cb->args_capi;
CFUNCSMESS(\"cb:Call-back function #name# (maxnofargs=#maxnofargs#(-#nofoptargs#))\\n\");
CFUNCSMESSPY(\"cb:#name#_capi=\",cb->capi);
if (cb->capi==NULL) {
capi_longjmp_ok = 0;
cb->capi = PyObject_GetAttrString(#modulename#_module,\"#argname#\");
CFUNCSMESSPY(\"cb:#name#_capi=\",cb->capi);
}
if (cb->capi==NULL) {
PyErr_SetString(#modulename#_error,\"cb: Callback #argname# not defined (as an argument or module #modulename# attribute).\\n\");
goto capi_fail;
}
if (F2PyCapsule_Check(cb->capi)) {
#name#_typedef #name#_cptr;
#name#_cptr = F2PyCapsule_AsVoidPtr(cb->capi);
#returncptr#(*#name#_cptr)(#optargs_nm##args_nm##strarglens_nm#);
#return#
}
if (capi_arglist==NULL) {
// 如果回调函数的参数列表为空指针,则执行以下操作
capi_longjmp_ok = 0;
// 从模块中获取名为 "#argname#_extra_args" 的属性
capi_tmp = PyObject_GetAttrString(#modulename#_module,\"#argname#_extra_args\");
// 如果成功获取到了属性
if (capi_tmp) {
// 将属性转换为元组对象
capi_arglist = (PyTupleObject *)PySequence_Tuple(capi_tmp);
// 释放临时对象
Py_DECREF(capi_tmp);
// 如果转换失败
if (capi_arglist==NULL) {
// 抛出异常,指示转换失败
PyErr_SetString(#modulename#_error,\"Failed to convert #modulename#.#argname#_extra_args to tuple.\\n\");
// 跳转到错误处理标签
goto capi_fail;
}
} else {
// 清除之前的异常状态
PyErr_Clear();
// 如果未获取到属性,则创建一个空的元组对象
capi_arglist = (PyTupleObject *)Py_BuildValue(\"()\");
}
}
// 如果参数列表仍为空
if (capi_arglist == NULL) {
// 抛出异常,指示回调函数的参数列表未设置
PyErr_SetString(#modulename#_error,\"Callback #argname# argument list is not set.\\n\");
// 跳转到错误处理标签
goto capi_fail;
}
#setdims#
#ifdef PYPY_VERSION
#define CAPI_ARGLIST_SETITEM(idx, value) PyList_SetItem((PyObject *)capi_arglist_list, idx, value)
// 如果是在 PyPy 环境下,将 capi_arglist 转换为 PyList 对象
capi_arglist_list = PySequence_List((PyObject *)capi_arglist);
// 如果转换失败,跳转到 capi_fail 标签处处理异常
if (capi_arglist_list == NULL) goto capi_fail;
#else
#define CAPI_ARGLIST_SETITEM(idx, value) PyTuple_SetItem((PyObject *)capi_arglist, idx, value)
#endif
#pyobjfrom#
#undef CAPI_ARGLIST_SETITEM
#ifdef PYPY_VERSION
// 在 PyPy 环境下,输出 capi_arglist_list 的内容到日志
CFUNCSMESSPY(\"cb:capi_arglist=\",capi_arglist_list);
#else
// 在非 PyPy 环境下,输出 capi_arglist 的内容到日志
CFUNCSMESSPY(\"cb:capi_arglist=\",capi_arglist);
#endif
// 输出调试信息到日志,指示正在调用 Python 函数
CFUNCSMESS(\"cb:Call-back calling Python function #argname#.\\n\");
#ifdef F2PY_REPORT_ATEXIT
// 如果定义了 F2PY_REPORT_ATEXIT 宏,则开始计时
f2py_cb_start_call_clock();
#endif
#ifdef PYPY_VERSION
// 在 PyPy 环境下,调用 Python 函数 cb->capi,传入 capi_arglist_list 作为参数
capi_return = PyObject_CallObject(cb->capi,(PyObject *)capi_arglist_list);
// 减少 capi_arglist_list 的引用计数
Py_DECREF(capi_arglist_list);
// 置空 capi_arglist_list 指针
capi_arglist_list = NULL;
#else
// 在非 PyPy 环境下,调用 Python 函数 cb->capi,传入 capi_arglist 作为参数
capi_return = PyObject_CallObject(cb->capi,(PyObject *)capi_arglist);
#endif
#ifdef F2PY_REPORT_ATEXIT
// 如果定义了 F2PY_REPORT_ATEXIT 宏,则停止计时
f2py_cb_stop_call_clock();
#endif
// 输出 capi_return 的内容到日志
CFUNCSMESSPY(\"cb:capi_return=\",capi_return);
// 如果 capi_return 为空指针,输出错误信息并跳转到 capi_fail 处
if (capi_return == NULL) {
fprintf(stderr,\"capi_return is NULL\\n\");
goto capi_fail;
}
// 如果 capi_return 为 Py_None,减少其引用计数,并重新创建一个空元组对象
if (capi_return == Py_None) {
Py_DECREF(capi_return);
capi_return = Py_BuildValue(\"()\");
}
// 如果 capi_return 不是元组对象,将其封装为一个元组对象
else if (!PyTuple_Check(capi_return)) {
capi_return = Py_BuildValue(\"(N)\",capi_return);
}
// 获取 capi_return 的元素个数
capi_j = PyTuple_Size(capi_return);
// 初始化 capi_i 为 0
capi_i = 0;
#frompyobj#
// 输出成功调用函数的信息到日志
CFUNCSMESS(\"cb:#name#:successful\\n\");
// 减少 capi_return 的引用计数
Py_DECREF(capi_return);
#ifdef F2PY_REPORT_ATEXIT
// 如果定义了 F2PY_REPORT_ATEXIT 宏,则停止计时
f2py_cb_stop_clock();
#endif
// 跳转到 capi_return_pt 处,返回执行点
goto capi_return_pt;
capi_fail:
// 输出调用失败的信息到 stderr
fprintf(stderr,\"Call-back #name# failed.\\n\");
// 减少 capi_return 和 capi_arglist_list 的引用计数
Py_XDECREF(capi_return);
Py_XDECREF(capi_arglist_list);
// 如果允许使用 longjmp,则跳转到 cb->jmpbuf 指定的位置
if (capi_longjmp_ok) {
longjmp(cb->jmpbuf,-1);
}
capi_return_pt:
// 返回空语句
;
#return#
}
#endtitle#
{ # Init
# 初始化字典,包含用于不同用途的分隔符和声明标记
'separatorsfor': {'decl': '\n',
'args': ',', 'optargs': '', 'pyobjfrom': '\n', 'freemem': '\n',
'args_td': ',', 'optargs_td': '',
'args_nm': ',', 'optargs_nm': '',
'frompyobj': '\n', 'setdims': '\n',
'docstrsigns': '\\n"\n"',
'latexdocstrsigns': '\n',
'latexdocstrreq': '\n', 'latexdocstropt': '\n',
'latexdocstrout': '\n', 'latexdocstrcbs': '\n',
},
# 声明标记和从Python对象转换的标记
'decl': '/*decl*/', 'pyobjfrom': '/*pyobjfrom*/', 'frompyobj': '/*frompyobj*/',
'args': [], 'optargs': '', 'return': '', 'strarglens': '', 'freemem': '/*freemem*/',
'args_td': [], 'optargs_td': '', 'strarglens_td': '',
'args_nm': [], 'optargs_nm': '', 'strarglens_nm': '',
'noargs': '',
'setdims': '/*setdims*/',
# 文档字符串相关标记和信息
'docstrsigns': '', 'latexdocstrsigns': '',
'docstrreq': ' Required arguments:',
'docstropt': ' Optional arguments:',
'docstrout': ' Return objects:',
'docstrcbs': ' Call-back functions:',
'docreturn': '', 'docsign': '', 'docsignopt': '',
'latexdocstrreq': '\\noindent Required arguments:',
'latexdocstropt': '\\noindent Optional arguments:',
'latexdocstrout': '\\noindent Return objects:',
'latexdocstrcbs': '\\noindent Call-back functions:',
'routnote': {hasnote: '--- #note#', l_not(hasnote): ''},
}, { # Function
# 函数声明
'decl': ' #ctype# return_value = 0;',
'frompyobj': [
{debugcapi: ' CFUNCSMESS("cb:Getting return_value->");'},
# 从Python对象转换为C对象时的调试信息
'''\
if (capi_j>capi_i) {
GETSCALARFROMPYTUPLE(capi_return,capi_i++,&return_value,#ctype#,
"#ctype#_from_pyobj failed in converting return_value of"
" call-back function #name# to C #ctype#\\n");
} else {
fprintf(stderr,"Warning: call-back function #name# did not provide"
" return value (index=%d, type=#ctype#)\\n",capi_i);
}''',
{debugcapi:
' fprintf(stderr,"#showvalueformat#.\\n",return_value);'}
],
# 从Python对象获取标量值的相关函数和调试信息
'need': ['#ctype#_from_pyobj', {debugcapi: 'CFUNCSMESS'}, 'GETSCALARFROMPYTUPLE'],
'return': ' return return_value;',
'_check': l_and(isfunction, l_not(isstringfunction), l_not(iscomplexfunction))
},
{ # String function
# 字符串函数相关信息
'pyobjfrom': {debugcapi: ' fprintf(stderr,"debug-capi:cb:#name#:%d:\\n",return_value_len);'},
'args': '#ctype# return_value,int return_value_len',
'args_nm': 'return_value,&return_value_len',
'args_td': '#ctype# ,int',
'frompyobj': [
{debugcapi: ' CFUNCSMESS("cb:Getting return_value->\\"");'},
"""\
if (capi_j>capi_i) {
GETSTRFROMPYTUPLE(capi_return,capi_i++,return_value,return_value_len);
} else {
fprintf(stderr,"Warning: call-back function #name# did not provide"
" return value (index=%d, type=#ctype#)\\n",capi_i);
}""",
{debugcapi:
' fprintf(stderr,"#showvalueformat#\\".\\n",return_value);'}
],
# 需要的依赖项列表
'need': ['#ctype#_from_pyobj', {debugcapi: 'CFUNCSMESS'},
'string.h', 'GETSTRFROMPYTUPLE'],
# 返回值表达式
'return': 'return;',
# 检查函数是否为字符串处理函数
'_check': isstringfunction
},
{ # 复杂函数
# 可选参数的默认值定义
'optargs': """
""",
'optargs_nm': """
return_value
""",
#ifndef F2PY_CB_RETURNCOMPLEX
return_value // 如果未定义 F2PY_CB_RETURNCOMPLEX,则直接使用 return_value
#endif
'optargs_td': """
""",
#ifndef F2PY_CB_RETURNCOMPLEX
#ctype# * // 如果未定义 F2PY_CB_RETURNCOMPLEX,则定义 #ctype# 类型的指针
#endif
'decl': """
""",
#ifdef F2PY_CB_RETURNCOMPLEX
#ctype# return_value = {0, 0}; // 如果定义了 F2PY_CB_RETURNCOMPLEX,则定义并初始化 return_value 为 {0, 0}
#endif
'frompyobj': [
{debugcapi: ' CFUNCSMESS("cb:Getting return_value->");'},
// 如果 debugcapi 被定义,则输出调试信息 "cb:Getting return_value->"
"""\
if (capi_j>capi_i) {
GETSCALARFROMPYTUPLE(capi_return,capi_i++,&return_value,
\"#ctype#_from_pyobj failed in converting return_value of call-back\"
\" function #name# to C #ctype#\\n\");
#else
GETSCALARFROMPYTUPLE(capi_return,capi_i++,return_value,#ctype#,
\"#ctype#_from_pyobj failed in converting return_value of call-back\"
\" function #name# to C #ctype#\\n\");
#endif
} else {
fprintf(stderr,
\"Warning: call-back function #name# did not provide\"
\" return value (index=%d, type=#ctype#)\\n\",capi_i);
}""",
// 根据条件检查获取来自 Python 元组的标量值,并根据定义的类型进行转换,如未提供则输出警告信息
{debugcapi: """\
#ifdef F2PY_CB_RETURNCOMPLEX
fprintf(stderr,\"#showvalueformat#.\\n\",(return_value).r,(return_value).i);
#else
fprintf(stderr,\"#showvalueformat#.\\n\",(*return_value).r,(*return_value).i);
#endif
"""}
],
// 如果定义了 F2PY_CB_RETURNCOMPLEX,则输出复杂数值的格式信息
fprintf(stderr,"#showvalueformat#.\\n",(return_value).r,(return_value).i);
// 否则输出普通数值的格式信息
fprintf(stderr,"#showvalueformat#.\\n",(*return_value).r,(*return_value).i);
'return': """
#ifdef F2PY_CB_RETURNCOMPLEX
return return_value;
#else
return;
#endif
""",
return return_value; // 如果定义了 F2PY_CB_RETURNCOMPLEX,则返回 return_value
return; // 否则返回空
'need': ['#ctype#_from_pyobj', {debugcapi: 'CFUNCSMESS'},
'string.h', 'GETSCALARFROMPYTUPLE', '#ctype#'],
// 需要包含的头文件和宏定义
CFUNCSMESS // 如果 debugcapi 被定义,则输出调试信息的宏
string.h // C 标准库中的字符串操作头文件
GETSCALARFROMPYTUPLE // 从 Python 元组中获取标量值的宏
'_check': iscomplexfunction
},
// 检查是否为复杂函数的标志
{'docstrout': ' #pydocsignout#',
'latexdocstrout': ['\\item[]{{}\\verb@#pydocsignout#@{}}',
{hasnote: '--- #note#'}],
// 输出文档字符串的格式化字符串
// 如果有注释,则添加注释内容
'docreturn': '#rname#,',
'_check': isfunction},
// 文档中的返回值说明
{'_check': issubroutine, 'return': 'return;'}
]
// 检查是否为子例程的标志
return; // 返回空
{
'args': {
l_and(isscalar, isintent_c): '#ctype# #varname_i#',
l_and(isscalar, l_not(isintent_c)): '#ctype# *#varname_i#_cb_capi',
isarray: '#ctype# *#varname_i#',
isstring: '#ctype# #varname_i#'
},
'args_nm': {
l_and(isscalar, isintent_c): '#varname_i#',
l_and(isscalar, l_not(isintent_c)): '#varname_i#_cb_capi',
isarray: '#varname_i#',
isstring: '#varname_i#'
},
'args_td': {
l_and(isscalar, isintent_c): '#ctype#',
l_and(isscalar, l_not(isintent_c)): '#ctype# *',
isarray: '#ctype# *',
isstring: '#ctype#'
},
'need': {l_or(isscalar, isarray, isstring): '#ctype#'},
'strarglens': {isstring: ',int #varname_i#_cb_len'},
'strarglens_td': {isstring: ',int'},
'strarglens_nm': {isstring: ',#varname_i#_cb_len'},
},
{
'decl': {l_not(isintent_c): ' #ctype# #varname_i#=(*#varname_i#_cb_capi);'},
'error': {l_and(isintent_c, isintent_out,
throw_error('intent(c,out) is forbidden for callback scalar arguments')):
''},
'frompyobj': [{debugcapi: ' CFUNCSMESS("cb:Getting #varname#->");'},
{isintent_out:
' if (capi_j>capi_i)\n GETSCALARFROMPYTUPLE(capi_return,capi_i++,#varname_i#_cb_capi,#ctype#,"#ctype#_from_pyobj failed in converting argument #varname# of call-back function #name# to C #ctype#\\n");'},
{l_and(debugcapi, l_and(l_not(iscomplex), isintent_c)):
' fprintf(stderr,"#showvalueformat#.\\n",#varname_i#);'},
{l_and(debugcapi, l_and(l_not(iscomplex), l_not( isintent_c))):
' fprintf(stderr,"#showvalueformat#.\\n",*#varname_i#_cb_capi);'},
{l_and(debugcapi, l_and(iscomplex, isintent_c)):
' fprintf(stderr,"#showvalueformat#.\\n",(#varname_i#).r,(#varname_i#).i);'},
{l_and(debugcapi, l_and(iscomplex, l_not( isintent_c))):
' fprintf(stderr,"#showvalueformat#.\\n",(*#varname_i#_cb_capi).r,(*#varname_i#_cb_capi).i);'},
],
'need': [{isintent_out: ['#ctype#_from_pyobj', 'GETSCALARFROMPYTUPLE']},
{debugcapi: 'CFUNCSMESS'}],
'_check': isscalar
}, {
'pyobjfrom': [{isintent_in: """\
if (cb->nofargs>capi_i)
if (CAPI_ARGLIST_SETITEM(capi_i++,pyobj_from_#ctype#1(#varname_i#)))
goto capi_fail;"""},
{isintent_inout: """\
注释:
{
# 定义函数参数格式,根据数据类型和意图生成参数字符串
'args': {
l_and(isscalar, isintent_c): '#ctype# #varname_i#',
l_and(isscalar, l_not(isintent_c)): '#ctype# *#varname_i#_cb_capi',
isarray: '#ctype# *#varname_i#',
isstring: '#ctype# #varname_i#'
},
# 定义函数参数名称格式,根据数据类型和意图生成参数名称字符串
'args_nm': {
l_and(isscalar, isintent_c): '#varname_i#',
l_and(isscalar, l_not(isintent_c)): '#varname_i#_cb_capi',
isarray: '#varname_i#',
isstring: '#varname_i#'
},
# 定义函数参数类型格式,根据数据类型和意图生成参数类型字符串
'args_td': {
l_and(isscalar, isintent_c): '#ctype#',
l_and(isscalar, l_not(isintent_c)): '#ctype# *',
isarray: '#ctype# *',
isstring: '#ctype#'
},
# 需要的数据类型,对于标量、数组或字符串类型都需要
'need': {l_or(isscalar, isarray, isstring): '#ctype#'},
# 在处理字符串参数时,未经测试多个参数情况下的参数长度
'strarglens': {isstring: ',int #varname_i#_cb_len'},
# 在处理字符串参数时,未经测试多个参数情况下的参数长度
'strarglens_td': {isstring: ',int'},
# 在处理字符串参数时,未经测试多个参数情况下的参数长度
'strarglens_nm': {isstring: ',#varname_i#_cb_len'},
},
{ # 标量
# 如果不是意图参数,生成标量变量声明
'decl': {l_not(isintent_c): ' #ctype# #varname_i#=(*#varname_i#_cb_capi);'},
# 如果参数是意图输出且禁止使用意图输出的回调标量参数,生成错误消息
'error': {l_and(isintent_c, isintent_out,
throw_error('intent(c,out) is forbidden for callback scalar arguments')):
''},
# 从Python对象中提取标量数据
'frompyobj': [
{debugcapi: ' CFUNCSMESS("cb:Getting #varname#->");'},
{isintent_out:
' if (capi_j>capi_i)\n GETSCALARFROMPYTUPLE(capi_return,capi_i++,#varname_i#_cb_capi,#ctype#,"#ctype#_from_pyobj failed in converting argument #varname# of call-back function #name# to C #ctype#\\n");'},
{l_and(debugcapi, l_and(l_not(iscomplex), isintent_c)):
' fprintf(stderr,"#showvalueformat#.\\n",#varname_i#);'},
{l_and(debugcapi, l_and(l_not(iscomplex), l_not( isintent_c))):
' fprintf(stderr,"#showvalueformat#.\\n",*#varname_i#_cb_capi);'},
{l_and(debugcapi, l_and(iscomplex, isintent_c)):
' fprintf(stderr,"#showvalueformat#.\\n",(#varname_i#).r,(#varname_i#).i);'},
{l_and(debugcapi, l_and(iscomplex, l_not( isintent_c))):
' fprintf(stderr,"#showvalueformat#.\\n",(*#varname_i#_cb_capi).r,(*#varname_i#_cb_capi).i);'},
],
# 需要的辅助函数和宏定义,包括输出参数和调试信息
'need': [
{isintent_out: ['#ctype#_from_pyobj', 'GETSCALARFROMPYTUPLE']},
{debugcapi: 'CFUNCSMESS'}
],
# 检查是否为标量变量
'_check': isscalar
}, {
# 从Python对象生成标量数据
'pyobjfrom': [
{isintent_in: """\
if (cb->nofargs>capi_i)
if (CAPI_ARGLIST_SETITEM(capi_i++,pyobj_from_
goto capi_fail;"""},
{isintent_inout: """\
if (cb->nofargs>capi_i)
if (CAPI_ARGLIST_SETITEM(capi_i++,pyarr_from_p_
goto capi_fail;
'need': [{isintent_in: 'pyobj_from_#ctype#1'},
{isintent_inout: 'pyarr_from_p_#ctype#1'},
{iscomplex: '#ctype#'}],
'frompyobj': [{debugcapi: ' CFUNCSMESS("cb:Getting #varname#->\\"");'},
""" if (capi_j>capi_i)
GETSTRFROMPYTUPLE(capi_return,capi_i++,#varname_i#,#varname_i#_cb_len);""",
{debugcapi:
' fprintf(stderr,"#showvalueformat#\\":%d:.\\n",#varname_i#,#varname_i#_cb_len);'},
],
'need': ['#ctype#', 'GETSTRFROMPYTUPLE',
{debugcapi: 'CFUNCSMESS'}, 'string.h'],
'pyobjfrom': [
{debugcapi:
(' fprintf(stderr,"debug-capi:cb:#varname#=#showvalueformat#:'
'%d:\\n",#varname_i#,#varname_i#_cb_len);')},
{isintent_in: """\
if (cb->nofargs>capi_i)
if (CAPI_ARGLIST_SETITEM(capi_i++,pyobj_from_#ctype#1size(#varname_i#,#varname_i#_cb_len)))
goto capi_fail;"""},
{isintent_inout: """\
if (cb->nofargs>capi_i) {
int #varname_i#_cb_dims[] = {#varname_i#_cb_len};
if (CAPI_ARGLIST_SETITEM(capi_i++,pyarr_from_p_#ctype#1(#varname_i#,#varname_i#_cb_dims)))
goto capi_fail;
}"""}],
'need': [{isintent_in: 'pyobj_from_#ctype#1size'},
{isintent_inout: 'pyarr_from_p_#ctype#1'}],
{
'decl': ' npy_intp #varname_i#_Dims[#rank#] = {#rank*[-1]#};',
'setdims': ' #cbsetdims#;',
'_check': isarray,
'_depend': ''
},
{
'pyobjfrom': [{debugcapi: ' fprintf(stderr,"debug-capi:cb:#varname#\\n");'},
{isintent_c: """\
if (cb->nofargs>capi_i) {
/* tmp_arr will be inserted to capi_arglist_list that will be
destroyed when leaving callback function wrapper together
with tmp_arr. */
PyArrayObject *tmp_arr = (PyArrayObject *)PyArray_New(&PyArray_Type,
#rank#,#varname_i#_Dims,#atype#,NULL,(char*)#varname_i#,#elsize#,
NPY_ARRAY_CARRAY,NULL);
# 定义一个多行字符串,包含多个代码块,每个代码块用于生成回调函数的特定部分
"""
""",
# 当回调函数中的参数数量大于当前处理的参数索引时执行以下代码块
l_not(isintent_c): """\
if (cb->nofargs>capi_i) {
/* tmp_arr 将被插入到 capi_arglist_list 中,在离开回调函数包装器时会一同被销毁。 */
PyArrayObject *tmp_arr = (PyArrayObject *)PyArray_New(&PyArray_Type,
NPY_ARRAY_FARRAY,NULL);
""",
# 如果条件不满足 isintent_c 的规则,则执行此代码块
},
# 当 isarray 为真且 isintent_nothide 为真且 isintent_in 或 isintent_inout 其中之一为真时执行以下代码块
"""
if (tmp_arr==NULL)
goto capi_fail;
if (CAPI_ARGLIST_SETITEM(capi_i++,(PyObject *)tmp_arr))
goto capi_fail;
}"""],
'_check': l_and(isarray, isintent_nothide, l_or(isintent_in, isintent_inout)),
'_optional': '',
}, {
'frompyobj': [{debugcapi: ' CFUNCSMESS("cb:Getting #varname#->");'},
# 如果 debugcapi 为真,则输出调试信息到标准错误流
""" if (capi_j>capi_i) {
PyArrayObject *rv_cb_arr = NULL;
if ((capi_tmp = PyTuple_GetItem(capi_return,capi_i++))==NULL) goto capi_fail;
rv_cb_arr = array_from_pyobj(
{isintent_c: '|F2PY_INTENT_C'},
""",capi_tmp);
if (rv_cb_arr == NULL) {
fprintf(stderr,\"rv_cb_arr is NULL\\n\");
goto capi_fail;
}
MEMCOPY(#varname_i#,PyArray_DATA(rv_cb_arr),PyArray_NBYTES(rv_cb_arr));
if (capi_tmp != (PyObject *)rv_cb_arr) {
Py_DECREF(rv_cb_arr);
}
}""",
{debugcapi: ' fprintf(stderr,"<-.\\n");'},
],
'need': ['MEMCOPY', {iscomplexarray: '#ctype#'}],
'_check': l_and(isarray, isintent_out)
}, {
'docreturn': '#varname#,',
'_check': isintent_out
}
]
cb_map = {}
def buildcallbacks(m):
cb_map[m['name']] = []
for bi in m['body']:
if bi['block'] == 'interface':
for b in bi['body']:
if b:
buildcallback(b, m['name'])
else:
errmess('warning: empty body for %s\n' % (m['name']))
def buildcallback(rout, um):
from . import capi_maps
outmess(' Constructing call-back function "cb_%s_in_%s"\n' %
(rout['name'], um))
args, depargs = getargs(rout)
capi_maps.depargs = depargs
var = rout['vars']
vrd = capi_maps.cb_routsign2map(rout, um)
rd = dictappend({}, vrd)
cb_map[um].append([rout['name'], rd['name']])
for r in cb_rout_rules:
if ('_check' in r and r['_check'](rout)) or ('_check' not in r):
ar = applyrules(r, vrd, rout)
rd = dictappend(rd, ar)
savevrd = {}
for i, a in enumerate(args):
vrd = capi_maps.cb_sign2map(a, var[a], index=i)
savevrd[a] = vrd
for r in cb_arg_rules:
if '_depend' in r:
continue
if '_optional' in r and isoptional(var[a]):
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 args:
vrd = savevrd[a]
for r in cb_arg_rules:
if '_depend' in r:
continue
if ('_optional' not in r) or ('_optional' in r and isrequired(var[a])):
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:
vrd = savevrd[a]
for r in cb_arg_rules:
if '_depend' not in r:
continue
if '_optional' 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 'args' in rd and 'optargs' in rd:
if isinstance(rd['optargs'], list):
rd['optargs'] = rd['optargs'] + [```
,
"""]
rd['optargs_nm'] = rd['optargs_nm'] + ["""
,
"""]
rd['optargs_td'] = rd['optargs_td'] + ["""
,
"""]
if isinstance(rd['docreturn'], list):
rd['docreturn'] = stripcomma(
replace('#docreturn#', {'docreturn': rd['docreturn']}))
# 如果 rd['docreturn'] 是列表类型,则将其用 stripcomma 函数处理,替换 '#docreturn#'
optargs = stripcomma(replace('#docsignopt#',
{'docsignopt': rd['docsignopt']}
))
if optargs == '':
rd['docsignature'] = stripcomma(
replace('#docsign#', {'docsign': rd['docsign']}))
# 如果 optargs 是空字符串,则使用 stripcomma 函数处理 rd['docsignature'],替换 '#docsign#'
else:
rd['docsignature'] = replace('#docsign#[#docsignopt#]',
{'docsign': rd['docsign'],
'docsignopt': optargs,
})
# 否则,使用 replace 函数替换 '#docsign#[#docsignopt#]' 的内容
rd['latexdocsignature'] = rd['docsignature'].replace('_', '\\_')
rd['latexdocsignature'] = rd['latexdocsignature'].replace(',', ', ')
rd['docstrsigns'] = []
rd['latexdocstrsigns'] = []
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}']
# 合并文档字符串相关的列表
if 'args' not in rd:
rd['args'] = ''
rd['args_td'] = ''
rd['args_nm'] = ''
# 如果 rd 中没有 'args' 键,则初始化为空字符串
if not (rd.get('args') or rd.get('optargs') or rd.get('strarglens')):
rd['noargs'] = 'void'
# 如果 rd 中 'args'、'optargs' 或 'strarglens' 为空,则设置 'noargs' 为 'void'
ar = applyrules(cb_routine_rules, rd)
cfuncs.callbacks[rd['name']] = ar['body']
if isinstance(ar['need'], str):
ar['need'] = [ar['need']]
# 如果 ar['need'] 是字符串类型,则转换为列表
if 'need' in rd:
for t in cfuncs.typedefs.keys():
if t in rd['need']:
ar['need'].append(t)
# 遍历 rd['need'],将其对应的键添加到 ar['need'] 中
cfuncs.typedefs_generated[rd['name'] + '_typedef'] = ar['cbtypedefs']
ar['need'].append(rd['name'] + '_typedef')
cfuncs.needs[rd['name']] = ar['need']
# 设置回调函数和相关的 typedef 和需要的键
capi_maps.lcb2_map[rd['name']] = {'maxnofargs': ar['maxnofargs'],
'nofoptargs': ar['nofoptargs'],
'docstr': ar['docstr'],
'latexdocstr': ar['latexdocstr'],
'argname': rd['argname']
}
outmess(' %s\n' % (ar['docstrshort']))
return
################## Build call-back function #############