NumPy-源码解析-九十三-

71 阅读1小时+

NumPy 源码解析(九十三)

.\numpy\pavement.py

r"""
This paver file is intended to help with the release process as much as
possible. It relies on virtualenv to generate 'bootstrap' environments as
independent from the user system as possible (e.g. to make sure the sphinx doc
is built against the built numpy, not an installed one).

Building changelog + notes
==========================

Assumes you have git and the binaries/tarballs in installers/::

    paver write_release
    paver write_note

This automatically put the checksum into README.rst, and writes the Changelog.

TODO
====
    - the script is messy, lots of global variables
    - make it more easily customizable (through command line args)
    - missing targets: install & test, sdist test, debian packaging
    - fix bdist_mpkg: we build the same source twice -> how to make sure we use
      the same underlying python for egg install in venv and for bdist_mpkg
"""
import os
import sys
import shutil
import hashlib
import textwrap

# The paver package needs to be installed to run tasks
import paver
from paver.easy import Bunch, options, task, sh

#-----------------------------------
# Things to be changed for a release
#-----------------------------------

# Path to the release notes
RELEASE_NOTES = 'doc/source/release/2.1.0-notes.rst'


#-------------------------------------------------------
# Hardcoded build/install dirs, virtualenv options, etc.
#-------------------------------------------------------

# Where to put the release installers
options(installers=Bunch(releasedir="release",
                         installersdir=os.path.join("release", "installers")),)


#-------------
# README stuff
#-------------

def _compute_hash(idirs, hashfunc):
    """Hash files using given hashfunc.

    Parameters
    ----------
    idirs : directory path
        Directory containing files to be hashed.
    hashfunc : hash function
        Function to be used to hash the files.

    """
    # List files in the specified directory
    released = paver.path.path(idirs).listdir()
    checksums = []
    # Iterate through each file path
    for fpath in sorted(released):
        # Open each file in binary mode
        with open(fpath, 'rb') as fin:
            # Compute the hash using the provided hash function
            fhash = hashfunc(fin.read())
            # Append checksum and file name to the list
            checksums.append(
                '%s  %s' % (fhash.hexdigest(), os.path.basename(fpath)))
    return checksums


def compute_md5(idirs):
    """Compute md5 hash of files in idirs.

    Parameters
    ----------
    idirs : directory path
        Directory containing files to be hashed.

    """
    # Delegate hash computation to _compute_hash using hashlib.md5
    return _compute_hash(idirs, hashlib.md5)


def compute_sha256(idirs):
    """Compute sha256 hash of files in idirs.

    Parameters
    ----------
    idirs : directory path
        Directory containing files to be hashed.

    """
    # Delegate hash computation to _compute_hash using hashlib.sha256
    # sha256 is preferred for better checksums
    return _compute_hash(idirs, hashlib.sha256)


def write_release_task(options, filename='README'):
    """Append hashes of release files to release notes.

    Parameters
    ----------
    options : paver.easy.Bunch
        Options object containing release configurations.
    filename : str, optional
        Name of the file to append hashes to (default is 'README').

    """
    This appends file hashes to the release notes and creates
    four README files of the result in various formats:

    - README.rst
    - README.rst.gpg
    - README.md
    - README.md.gpg

    The md file are created using `pandoc` so that the links are
    properly updated. The gpg files are kept separate, so that
    the unsigned files may be edited before signing if needed.

    Parameters
    ----------
    options :
        Set by ``task`` decorator.
        由 ``task`` 装饰器设置的选项。
    filename : str
        Filename of the modified notes. The file is written
        in the release directory.
        修改后的笔记文件名。文件将被写入发布目录。

    """
    idirs = options.installers.installersdir
    notes = paver.path.path(RELEASE_NOTES)
    rst_readme = paver.path.path(filename + '.rst')
    md_readme = paver.path.path(filename + '.md')

    # append hashes
    with open(rst_readme, 'w') as freadme:
        with open(notes) as fnotes:
            freadme.write(fnotes.read())

        # Write MD5 hashes to README.rst
        freadme.writelines(textwrap.dedent(
            """
            Checksums
            =========

            MD5
            ---
            ::

            """))
        freadme.writelines([f'    {c}\n' for c in compute_md5(idirs)])

        # Write SHA256 hashes to README.rst
        freadme.writelines(textwrap.dedent(
            """
            SHA256
            ------
            ::

            """))
        freadme.writelines([f'    {c}\n' for c in compute_sha256(idirs)])

    # generate md file using pandoc before signing
    sh(f"pandoc -s -o {md_readme} {rst_readme}")

    # Sign files
    if hasattr(options, 'gpg_key'):
        # Generate a clear-signed, armored ASCII file with default or specified GPG key
        cmd = f'gpg --clearsign --armor --default_key {options.gpg_key}'
    else:
        # Generate a clear-signed, armored ASCII file
        cmd = 'gpg --clearsign --armor'

    # Sign README.rst and README.md using GPG
    sh(cmd + f' --output {rst_readme}.gpg {rst_readme}')
    sh(cmd + f' --output {md_readme}.gpg {md_readme}')
# 使用 @task 装饰器声明一个任务函数,用于生成发布说明文件。
@task
# 定义函数 write_release,用于生成 README 文件。
def write_release(options):
    """Write the README files.

    Two README files are generated from the release notes, one in ``rst``
    markup for the general release, the other in ``md`` markup for the github
    release notes.

    Parameters
    ----------
    options :
        Set by ``task`` decorator.

    """
    # 获取发布目录路径
    rdir = options.installers.releasedir
    # 调用 write_release_task 函数,生成 README 文件,文件路径为发布目录下的 README
    write_release_task(options, os.path.join(rdir, 'README'))

.\numpy\tools\changelog.py

#!/usr/bin/env python3
"""
Script to generate contributor and pull request lists

This script generates contributor and pull request lists for release
changelogs using Github v3 protocol. Use requires an authentication token in
order to have sufficient bandwidth, you can get one following the directions at
`<https://help.github.com/articles/creating-an-access-token-for-command-line-use/>_
Don't add any scope, as the default is read access to public information. The
token may be stored in an environment variable as you only get one chance to
see it.

Usage::

    $ ./tools/announce.py <token> <revision range>

The output is utf8 rst.

Dependencies
------------

- gitpython
- pygithub
- git >= 2.29.0

Some code was copied from scipy `tools/gh_list.py` and `tools/authors.py`.

Examples
--------

From the bash command line with $GITHUB token::

    $ ./tools/announce $GITHUB v1.13.0..v1.14.0 > 1.14.0-changelog.rst

"""

import os
import sys
import re
from git import Repo
from github import Github

# Initialize the GitPython Repo object for the current repository directory
this_repo = Repo(os.path.join(os.path.dirname(__file__), ".."))

# Message template for authors in the release
author_msg =\
"""
A total of %d people contributed to this release.  People with a "+" by their
names contributed a patch for the first time.
"""

# Message template for pull requests in the release
pull_request_msg =\
"""
A total of %d pull requests were merged for this release.
"""


def get_authors(revision_range):
    # Split the revision range into last and current release versions
    lst_release, cur_release = [r.strip() for r in revision_range.split('..')]
    
    # Regular expression pattern to extract authors' names from shortlog output
    authors_pat = r'^.*\t(.*)$'

    # Define git shortlog groupings for current and previous releases
    grp1 = '--group=author'
    grp2 = '--group=trailer:co-authored-by'
    
    # Get shortlog output for current release and previous release
    cur = this_repo.git.shortlog('-s', grp1, grp2, revision_range)
    pre = this_repo.git.shortlog('-s', grp1, grp2, lst_release)
    
    # Extract authors' names using the defined pattern
    authors_cur = set(re.findall(authors_pat, cur, re.M))
    authors_pre = set(re.findall(authors_pat, pre, re.M))

    # Remove specific bot contributors from the sets
    authors_cur.discard('Homu')
    authors_pre.discard('Homu')
    authors_cur.discard('dependabot-preview')
    authors_pre.discard('dependabot-preview')

    # Identify new authors by comparing current and previous sets
    authors_new = [s + ' +' for s in authors_cur - authors_pre]
    authors_old = [s for s in authors_cur & authors_pre]
    
    # Combine new and old authors, then sort alphabetically
    authors = authors_new + authors_old
    authors.sort()
    
    return authors


def get_pull_requests(repo, revision_range):
    prnums = []

    # Extract pull request numbers from regular merges
    merges = this_repo.git.log('--oneline', '--merges', revision_range)
    issues = re.findall(r"Merge pull request \#(\d*)", merges)
    prnums.extend(int(s) for s in issues)

    # Extract pull request numbers from Homu merges (Auto merges)
    issues = re.findall(r"Auto merge of \#(\d*)", merges)
    prnums.extend(int(s) for s in issues)

    # Extract pull request numbers from fast forward squash-merges
    commits = this_repo.git.log('--oneline', '--no-merges', '--first-parent', revision_range)
    issues = re.findall(r'^.*\((\#|gh-|gh-\#)(\d+)\)$', commits, re.M)
    prnums.extend(int(s[1]) for s in issues)
    # 对 Pull Request 编号列表进行排序
    prnums.sort()
    # 使用列表推导式获取指定仓库中每个编号对应的 Pull Request 对象
    prs = [repo.get_pull(n) for n in prnums]
    # 返回获取到的 Pull Request 对象列表
    return prs
# 主函数,用于生成指定版本范围内的作者和Pull请求列表文档
def main(token, revision_range):
    # 将输入的版本范围按照 '..' 分割并去除首尾空格,得到最旧版本和当前版本
    lst_release, cur_release = [r.strip() for r in revision_range.split('..')]

    # 使用提供的 GitHub 访问令牌创建 GitHub 对象
    github = Github(token)
    # 获取指定仓库(numpy/numpy)的 GitHub 仓库对象
    github_repo = github.get_repo('numpy/numpy')

    # 获取版本范围内的作者列表
    authors = get_authors(revision_range)
    # 设置标题
    heading = "Contributors"
    # 输出标题和分隔线
    print()
    print(heading)
    print("="*len(heading))
    # 输出作者数量信息
    print(author_msg % len(authors))

    # 遍历并输出每位作者的名称
    for s in authors:
        print('* ' + s)

    # 获取版本范围内合并的 Pull 请求列表
    pull_requests = get_pull_requests(github_repo, revision_range)
    # 设置标题
    heading = "Pull requests merged"
    # 设置格式化输出模板
    pull_msg = "* `#{0} <{1}>`__: {2}"

    # 输出标题和分隔线
    print()
    print(heading)
    print("="*len(heading))
    # 输出 Pull 请求数量信息
    print(pull_request_msg % len(pull_requests))

    # 遍历并输出每个 Pull 请求的编号、链接和标题
    for pull in pull_requests:
        title = re.sub(r"\s+", " ", pull.title.strip())
        # 如果标题超过60个字符,则截断并添加省略号
        if len(title) > 60:
            remainder = re.sub(r"\s.*$", "...", title[60:])
            if len(remainder) > 20:
                remainder = title[:80] + "..."
            else:
                title = title[:60] + remainder
        # 输出格式化后的 Pull 请求信息
        print(pull_msg.format(pull.number, pull.html_url, title))


if __name__ == "__main__":
    from argparse import ArgumentParser

    # 创建参数解析器
    parser = ArgumentParser(description="Generate author/pr lists for release")
    # 添加必需的参数:GitHub 访问令牌和版本范围
    parser.add_argument('token', help='github access token')
    parser.add_argument('revision_range', help='<revision>..<revision>')
    # 解析命令行参数
    args = parser.parse_args()
    # 调用主函数,传入 GitHub 访问令牌和版本范围参数
    main(args.token, args.revision_range)

.\numpy\tools\check_installed_files.py

"""
Check if all the test and .pyi files are installed after building.

Examples::

    $ python check_installed_files.py install_dirname

        install_dirname:
            the relative path to the directory where NumPy is installed after
            building and running `meson install`.

Notes
=====

The script will stop on encountering the first missing file in the install dir,
it will not give a full listing. This should be okay, because the script is
meant for use in CI so it's not like many files will be missing at once.

"""

import os                   # 导入操作系统接口模块
import glob                 # 导入文件通配符模块
import sys                  # 导入系统相关模块
import json                 # 导入 JSON 解析模块


CUR_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__)))  # 获取当前脚本所在目录的绝对路径
ROOT_DIR = os.path.dirname(CUR_DIR)  # 获取当前脚本所在目录的父目录
NUMPY_DIR = os.path.join(ROOT_DIR, 'numpy')  # 构建 NumPy 目录路径


# Files whose installation path will be different from original one
changed_installed_path = {
    #'numpy/_build_utils/some_file.py': 'numpy/lib/some_file.py'
}


def main(install_dir, tests_check):
    INSTALLED_DIR = os.path.join(ROOT_DIR, install_dir)  # 构建安装目录的绝对路径
    if not os.path.exists(INSTALLED_DIR):  # 如果安装目录不存在,则抛出异常
        raise ValueError(
            f"Provided install dir {INSTALLED_DIR} does not exist"
        )

    numpy_test_files = get_files(NUMPY_DIR, kind='test')  # 获取 NumPy 测试文件列表
    installed_test_files = get_files(INSTALLED_DIR, kind='test')  # 获取安装目录下的测试文件列表

    if tests_check == "--no-tests":  # 如果传入参数指定不检查测试文件
        if len(installed_test_files) > 0:  # 如果安装目录下存在测试文件,则抛出异常
            raise Exception("Test files aren't expected to be installed in %s"
                            ", found %s" % (INSTALLED_DIR, installed_test_files))
        print("----------- No test files were installed --------------")
    else:
        # Check test files detected in repo are installed
        for test_file in numpy_test_files.keys():
            if test_file not in installed_test_files.keys():  # 如果有测试文件未安装,则抛出异常
                raise Exception(
                    "%s is not installed" % numpy_test_files[test_file]
                )

        print("----------- All the test files were installed --------------")

    numpy_pyi_files = get_files(NUMPY_DIR, kind='stub')  # 获取 NumPy .pyi 文件列表
    installed_pyi_files = get_files(INSTALLED_DIR, kind='stub')  # 获取安装目录下的 .pyi 文件列表

    # Check *.pyi files detected in repo are installed
    for pyi_file in numpy_pyi_files.keys():
        if pyi_file not in installed_pyi_files.keys():  # 如果有 .pyi 文件未安装,则根据情况继续检查或抛出异常
            if (tests_check == "--no-tests" and
                    "tests" in numpy_pyi_files[pyi_file]):
                continue
            raise Exception("%s is not installed" % numpy_pyi_files[pyi_file])

    print("----------- All the necessary .pyi files "
          "were installed --------------")


def get_files(dir_to_check, kind='test'):
    files = dict()
    patterns = {
        'test': f'{dir_to_check}/**/test_*.py',  # 搜索测试文件的通配符模式
        'stub': f'{dir_to_check}/**/*.pyi',     # 搜索 .pyi 文件的通配符模式
    }
    for path in glob.glob(patterns[kind], recursive=True):  # 使用通配符模式搜索文件并遍历结果
        relpath = os.path.relpath(path, dir_to_check)  # 获取相对路径
        files[relpath] = path  # 将文件相对路径和绝对路径添加到字典中

    if sys.version_info >= (3, 12):  # 如果 Python 版本大于等于 3.12
        files = {
            k: v for k, v in files.items() if not k.startswith('distutils')  # 过滤掉 distutils 开头的键
        }

    return files  # 返回文件字典
    # 在文件字典中过滤掉来自于名为 'pythoncapi-compat' 的子模块的 Python 文件
    files = {
        k: v for k, v in files.items() if 'pythoncapi-compat' not in k
    }

    # 返回过滤后的文件字典
    return files
if __name__ == '__main__':
    # 检查是否在主程序中执行
    if len(sys.argv) < 2:
        # 如果输入参数少于2个,抛出数值错误异常
        raise ValueError("Incorrect number of input arguments, need "
                         "check_installation.py relpath/to/installed/numpy")

    # 获取安装目录
    install_dir = sys.argv[1]
    # 初始化测试检查字符串
    tests_check = ""
    # 如果输入参数大于等于3个,将第三个参数赋值给tests_check
    if len(sys.argv) >= 3:
        tests_check = sys.argv[2]
    # 调用主函数,传入安装目录和测试检查字符串
    main(install_dir, tests_check)

    # 初始化一个空集合用来存储所有标签
    all_tags = set()

    # 打开并读取intro-install_plan.json文件
    with open(os.path.join('build', 'meson-info',
                           'intro-install_plan.json'), 'r') as f:
        # 解析JSON文件内容并赋值给targets变量
        targets = json.load(f)

    # 遍历targets字典的键
    for key in targets.keys():
        # 遍历每个键对应的值(字典)
        for values in list(targets[key].values()):
            # 如果值字典中的'tag'键对应的值不在all_tags集合中
            if not values['tag'] in all_tags:
                # 将'tag'键对应的值添加到all_tags集合中
                all_tags.add(values['tag'])

    # 检查all_tags集合是否包含预期的标签集合
    if all_tags != set(['runtime', 'python-runtime', 'devel', 'tests']):
        # 如果不符合预期,抛出断言错误,显示实际找到的标签集合
        raise AssertionError(f"Found unexpected install tag: {all_tags}")

.\numpy\tools\check_openblas_version.py

"""
usage: check_openblas_version.py <min_version>

Check the blas version is blas from scipy-openblas and is higher than
min_version
example: check_openblas_version.py 0.3.26
"""

# 导入必要的库
import numpy
import pprint
import sys

# 从命令行参数获取所需的最低版本号
version = sys.argv[1]

# 获取 numpy 的构建依赖配置信息
deps = numpy.show_config('dicts')['Build Dependencies']

# 确保依赖中包含名为 "blas" 的信息
assert "blas" in deps

# 输出提示信息
print("Build Dependencies: blas")

# 打印 blas 的详细信息
pprint.pprint(deps["blas"])

# 确保 blas 的版本号高于等于指定的最低版本号
assert deps["blas"]["version"].split(".") >= version.split(".")

# 确保 blas 的名称为 "scipy-openblas"
assert deps["blas"]["name"] == "scipy-openblas"

.\numpy\tools\ci\push_docs_to_repo.py

#!/usr/bin/env python3

import argparse  # 导入用于解析命令行参数的模块
import subprocess  # 导入用于执行外部命令的模块
import tempfile  # 导入用于创建临时文件和目录的模块
import os  # 导入操作系统相关功能的模块
import sys  # 导入系统相关功能的模块
import shutil  # 导入高级文件操作模块,用于文件复制和删除


parser = argparse.ArgumentParser(
    description='Upload files to a remote repo, replacing existing content'
)
parser.add_argument('dir', help='directory of which content will be uploaded')  # 解析要上传内容的目录参数
parser.add_argument('remote', help='remote to which content will be pushed')  # 解析远程仓库的参数
parser.add_argument('--message', default='Commit bot upload',
                    help='commit message to use')  # 解析提交信息的参数,默认为"Commit bot upload"
parser.add_argument('--committer', default='numpy-commit-bot',
                    help='Name of the git committer')  # 解析提交者姓名的参数,默认为"numpy-commit-bot"
parser.add_argument('--email', default='numpy-commit-bot@nomail',
                    help='Email of the git committer')  # 解析提交者邮箱的参数,默认为"numpy-commit-bot@nomail"
parser.add_argument('--count', default=1, type=int,
                    help="minimum number of expected files, defaults to 1")  # 解析期望上传的文件最小数量,默认为1

parser.add_argument(
    '--force', action='store_true',
    help='hereby acknowledge that remote repo content will be overwritten'
)  # 解析是否强制覆盖远程仓库内容的参数
args = parser.parse_args()
args.dir = os.path.abspath(args.dir)  # 获取绝对路径以确保目录存在

if not os.path.exists(args.dir):  # 检查目录是否存在
    print('Content directory does not exist')  # 输出提示信息
    sys.exit(1)  # 如果目录不存在,退出程序并返回错误码1

count = len([name for name in os.listdir(args.dir) if os.path.isfile(os.path.join(args.dir, name))])  # 统计目录下的文件数量

if count < args.count:  # 检查文件数量是否达到预期值
    print(f"Expected {args.count} top-directory files to upload, got {count}")  # 输出文件数量不符的提示信息
    sys.exit(1)  # 如果文件数量不符,退出程序并返回错误码1

def run(cmd, stdout=True):  # 定义运行外部命令的函数
    pipe = None if stdout else subprocess.DEVNULL  # 根据stdout参数确定是否需要输出命令结果
    try:
        subprocess.check_call(cmd, stdout=pipe, stderr=pipe)  # 执行命令
    except subprocess.CalledProcessError:
        print("\n! Error executing: `%s;` aborting" % ' '.join(cmd))  # 输出命令执行错误的信息
        sys.exit(1)  # 如果命令执行错误,退出程序并返回错误码1

workdir = tempfile.mkdtemp()  # 创建临时工作目录
os.chdir(workdir)  # 切换工作目录到临时目录

run(['git', 'init'])  # 初始化 Git 仓库
# 确保工作分支命名为 "main"
# (在旧版本的 Git 上,`--initial-branch=main` 可能会失败):
run(['git', 'checkout', '-b', 'main'])  # 创建并切换到名为 "main" 的分支
run(['git', 'remote', 'add', 'origin',  args.remote])  # 添加远程仓库地址
run(['git', 'config', '--local', 'user.name', args.committer])  # 设置本地 Git 用户名
run(['git', 'config', '--local', 'user.email', args.email])  # 设置本地 Git 用户邮箱

print('- committing new content: "%s"' % args.message)  # 输出正在提交新内容的信息
run(['cp', '-R', os.path.join(args.dir, '.'), '.'])  # 复制要上传的内容到当前工作目录
run(['git', 'add', '.'], stdout=False)  # 添加所有修改到 Git 暂存区
run(['git', 'commit', '--allow-empty', '-m', args.message], stdout=False)  # 提交更改到 Git 仓库

print('- uploading as %s <%s>' % (args.committer, args.email))  # 输出正在以提交者身份上传的信息
if args.force:
    run(['git', 'push', 'origin', 'main', '--force'])  # 强制推送更改到远程仓库
else:
    print('\n!! No `--force` argument specified; aborting')  # 输出没有指定 `--force` 参数的警告信息
    print('!! Before enabling that flag, make sure you know what it does\n')  # 输出在启用该标志之前,请确保了解其功能的建议
    sys.exit(1)  # 如果没有指定 `--force` 参数,退出程序并返回错误码1

shutil.rmtree(workdir)  # 删除临时工作目录

.\numpy\tools\ci\test_all_newsfragments_used.py

#!/usr/bin/env python3
# 导入系统相关的库
import sys
# 导入 TOML 格式解析库
import toml
# 导入操作系统相关功能的库
import os

# 主函数定义
def main():
    # 从 pyproject.toml 文件中加载配置路径
    path = toml.load("pyproject.toml")["tool"]["towncrier"]["directory"]

    # 获取指定路径下的所有文件和目录列表
    fragments = os.listdir(path)
    # 移除特定文件名以保证正确性
    fragments.remove("README.rst")
    fragments.remove("template.rst")

    # 如果仍有未找到的文件
    if fragments:
        # 打印未被 towncrier 找到的文件列表
        print("The following files were not found by towncrier:")
        print("    " + "\n    ".join(fragments))
        # 以错误状态退出程序
        sys.exit(1)

# 如果当前脚本作为主程序运行
if __name__ == "__main__":
    # 调用主函数
    main()

.\numpy\tools\commitstats.py

# 使用命令行执行 SVN log 命令,获取最近 2300 条提交日志,并将输出保存到 output.txt 文件中
command = 'svn log -l 2300 > output.txt'

# 导入正则表达式模块
import re
# 导入 NumPy 库,用于处理数组和数据
import numpy as np
# 导入操作系统接口模块
import os

# 定义正则表达式,匹配 SVN 日志中的提交者名字
names = re.compile(r'r\d+\s\|\s(.*)\s\|\s200')

# 定义函数,从指定文件中获取提交者名字出现的次数,并以列表形式返回
def get_count(filename, repo):
    # 读取指定文件的内容
    mystr = open(filename).read()
    # 使用正则表达式查找符合条件的提交者名字
    result = names.findall(mystr)
    # 使用 NumPy 的 unique 函数获取唯一的提交者名字
    u = np.unique(result)
    # 组装每个提交者名字、出现次数和仓库名为元组,并放入列表中
    count = [(x, result.count(x), repo) for x in u]
    return count

# 切换当前工作目录至上级目录
os.chdir('..')
# 使用操作系统接口执行 SVN log 命令,输出结果保存到 output.txt 文件
os.system(command)

# 获取 NumPy 仓库中提交者名字的出现次数统计
count = get_count('output.txt', 'NumPy')

# 切换当前工作目录至 scipy 目录
os.chdir('../scipy')
# 再次执行 SVN log 命令,更新 output.txt 文件
os.system(command)

# 将 SciPy 仓库中提交者名字的出现次数统计加入到 count 列表中
count.extend(get_count('output.txt', 'SciPy'))

# 切换当前工作目录至 scikits 目录
os.chdir('../scikits')
# 再次执行 SVN log 命令,更新 output.txt 文件
os.system(command)

# 将 SciKits 仓库中提交者名字的出现次数统计加入到 count 列表中
count.extend(get_count('output.txt', 'SciKits'))

# 对 count 列表进行排序,按提交者名字的字母顺序排序
count.sort()

# 输出标题,表示 SciPy 和 NumPy 仓库的统计结果
print("** SciPy and NumPy **")
print("=====================")

# 遍历 count 列表,输出每个元组的内容,包括提交者名字、出现次数和仓库名
for val in count:
    print(val)

.\numpy\tools\c_coverage\c_coverage_report.py

#!/usr/bin/env python3
"""
A script to create C code-coverage reports based on the output of
valgrind's callgrind tool.

"""
import os  # 导入操作系统相关的功能
import re  # 导入正则表达式模块
import sys  # 导入系统相关的功能
from xml.sax.saxutils import quoteattr, escape  # 导入 XML 相关工具

try:
    import pygments  # 尝试导入 Pygments
    if tuple([int(x) for x in pygments.__version__.split('.')]) < (0, 11):
        raise ImportError()
    from pygments import highlight  # 导入代码高亮函数
    from pygments.lexers import CLexer  # 导入 C 语言的代码词法分析器
    from pygments.formatters import HtmlFormatter  # 导入生成 HTML 的格式化器
    has_pygments = True  # 记录是否成功导入 Pygments
except ImportError:
    print("This script requires pygments 0.11 or greater to generate HTML")  # 提示需要 Pygments 0.11 或更高版本
    has_pygments = False  # 记录未成功导入 Pygments


class FunctionHtmlFormatter(HtmlFormatter):
    """Custom HTML formatter to insert extra information with the lines."""
    def __init__(self, lines, **kwargs):
        HtmlFormatter.__init__(self, **kwargs)  # 调用父类的初始化方法
        self.lines = lines  # 初始化行信息

    def wrap(self, source, outfile):
        for i, (c, t) in enumerate(HtmlFormatter.wrap(self, source, outfile)):
            as_functions = self.lines.get(i-1, None)  # 获取当前行的附加函数信息
            if as_functions is not None:
                yield 0, ('<div title=%s style="background: #ccffcc">[%2d]' %
                          (quoteattr('as ' + ', '.join(as_functions)),
                           len(as_functions)))  # 在 HTML 中插入附加函数信息的块
            else:
                yield 0, '    '  # 如果没有附加函数信息,则空行
            yield c, t
            if as_functions is not None:
                yield 0, '</div>'  # 结束附加函数信息的 HTML 块


class SourceFile:
    def __init__(self, path):
        self.path = path  # 初始化文件路径
        self.lines = {}  # 初始化行号和附加函数信息的字典

    def mark_line(self, lineno, as_func=None):
        line = self.lines.setdefault(lineno, set())  # 设置行号对应的附加函数信息
        if as_func is not None:
            as_func = as_func.split("'", 1)[0]  # 提取函数名称
            line.add(as_func)  # 添加函数名称到行信息中

    def write_text(self, fd):
        source = open(self.path, "r")  # 打开文件以读取文本
        for i, line in enumerate(source):
            if i + 1 in self.lines:
                fd.write("> ")  # 如果行号在附加函数信息中,则写入标记
            else:
                fd.write("! ")  # 否则写入另一种标记
            fd.write(line)  # 写入源代码行
        source.close()  # 关闭文件

    def write_html(self, fd):
        source = open(self.path, 'r')  # 打开文件以读取 HTML
        code = source.read()  # 读取文件内容
        lexer = CLexer()  # 创建 C 语言代码词法分析器
        formatter = FunctionHtmlFormatter(
            self.lines,
            full=True,
            linenos='inline')  # 使用自定义 HTML 格式化器生成 HTML
        fd.write(highlight(code, lexer, formatter))  # 使用 Pygments 高亮代码
        source.close()  # 关闭文件


class SourceFiles:
    def __init__(self):
        self.files = {}  # 初始化文件字典
        self.prefix = None  # 初始化文件路径前缀为 None

    def get_file(self, path):
        if path not in self.files:
            self.files[path] = SourceFile(path)  # 如果文件不在字典中,则创建新的 SourceFile 对象
            if self.prefix is None:
                self.prefix = path  # 设置文件路径前缀
            else:
                self.prefix = os.path.commonprefix([self.prefix, path])  # 更新文件路径前缀
        return self.files[path]  # 返回文件对象

    def clean_path(self, path):
        path = path[len(self.prefix):]  # 移除路径前缀
        return re.sub(r"[^A-Za-z0-9\.]", '_', path)  # 将非字母数字字符替换为下划线
    # 将数据写入文本文件的方法,接受一个根目录参数 root
    def write_text(self, root):
        # 遍历 self.files 中的路径和源数据
        for path, source in self.files.items():
            # 打开目标文件,使用写模式 'w',路径为 root 下的清理后的路径
            fd = open(os.path.join(root, self.clean_path(path)), "w")
            # 将源数据写入文件
            source.write_text(fd)
            # 关闭文件描述符
            fd.close()

    # 将数据写入 HTML 文件的方法,接受一个根目录参数 root
    def write_html(self, root):
        # 遍历 self.files 中的路径和源数据
        for path, source in self.files.items():
            # 打开目标文件,使用写模式 'w',路径为 root 下的清理后的路径后加上 ".html"
            fd = open(os.path.join(root, self.clean_path(path) + ".html"), "w")
            # 将源数据以 HTML 格式写入文件
            source.write_html(fd)
            # 关闭文件描述符
            fd.close()

        # 打开目标文件 'index.html',使用写模式 'w'
        fd = open(os.path.join(root, 'index.html'), 'w')
        # 写入 HTML 开始标签
        fd.write("<html>")
        # 对文件路径按字母顺序排序
        paths = sorted(self.files.keys())
        # 遍历排序后的文件路径列表
        for path in paths:
            # 写入 HTML 标签,包含链接到各文件的内容
            fd.write('<p><a href="%s.html">%s</a></p>' %
                     (self.clean_path(path), escape(path[len(self.prefix):])))
        # 写入 HTML 结束标签
        fd.write("</html>")
        # 关闭文件描述符
        fd.close()
# 定义一个函数用于收集统计信息,处理压缩的 callgrind 文件
def collect_stats(files, fd, pattern):
    # 定义两个正则表达式模式,用于匹配不同的行格式
    line_regexs = [
        re.compile(r"(?P<lineno>[0-9]+)(\s[0-9]+)+"),  # 匹配形如 "123 456 789"
        re.compile(r"((jump)|(jcnd))=([0-9]+)\s(?P<lineno>[0-9]+)")  # 匹配形如 "jump=123 456"
    ]

    current_file = None  # 当前处理的文件对象
    current_function = None  # 当前处理的函数名
    for i, line in enumerate(fd):
        if re.match("f[lie]=.+", line):  # 如果行以 "f="、"l=" 或 "ie=" 开头
            path = line.split('=', 2)[1].strip()  # 提取路径信息
            if os.path.exists(path) and re.search(pattern, path):  # 如果路径存在且符合指定模式
                current_file = files.get_file(path)  # 获取当前文件对象
            else:
                current_file = None  # 否则置空当前文件对象
        elif re.match("fn=.+", line):  # 如果行以 "fn=" 开头
            current_function = line.split('=', 2)[1].strip()  # 提取函数名信息
        elif current_file is not None:  # 如果当前文件对象不为空
            for regex in line_regexs:  # 遍历每个正则表达式模式
                match = regex.match(line)  # 尝试匹配当前行
                if match:  # 如果匹配成功
                    lineno = int(match.group('lineno'))  # 提取行号
                    current_file.mark_line(lineno, current_function)  # 标记该行号属于当前函数

if __name__ == '__main__':
    import argparse

    parser = argparse.ArgumentParser(description=__doc__)  # 创建命令行参数解析器
    parser.add_argument(
        'callgrind_file', nargs='+',
        help='One or more callgrind files')  # 接收一个或多个 callgrind 文件作为参数
    parser.add_argument(
        '-d', '--directory', default='coverage',
        help='Destination directory for output (default: %(default)s)')  # 指定输出目录,默认为 'coverage'
    parser.add_argument(
        '-p', '--pattern', default='numpy',
        help='Regex pattern to match against source file paths '
             '(default: %(default)s)')  # 指定用于匹配源文件路径的正则表达式模式,默认为 'numpy'
    parser.add_argument(
        '-f', '--format', action='append', default=[],
        choices=['text', 'html'],
        help="Output format(s) to generate. "
             "If option not provided, both will be generated.")  # 指定生成的输出格式,可选 'text' 或 'html',支持多次指定
    args = parser.parse_args()  # 解析命令行参数

    files = SourceFiles()  # 创建 SourceFiles 对象,用于管理源文件信息
    for log_file in args.callgrind_file:  # 遍历每个输入的 callgrind 文件
        log_fd = open(log_file, 'r')  # 打开文件,准备读取
        collect_stats(files, log_fd, args.pattern)  # 收集统计信息
        log_fd.close()  # 关闭文件

    if not os.path.exists(args.directory):  # 如果指定的输出目录不存在
        os.makedirs(args.directory)  # 创建该目录

    if args.format == []:  # 如果未指定输出格式
        formats = ['text', 'html']  # 默认生成 'text' 和 'html'
    else:
        formats = args.format  # 使用用户指定的输出格式
    if 'text' in formats:  # 如果需要生成文本格式
        files.write_text(args.directory)  # 生成文本输出到指定目录
    if 'html' in formats:  # 如果需要生成 HTML 格式
        if not has_pygments:  # 检查是否具备生成 HTML 所需的 Pygments 库
            print("Pygments 0.11 or later is required to generate HTML")  # 提示需要 Pygments 版本 0.11 或更高
            sys.exit(1)  # 退出程序,返回错误状态码
        files.write_html(args.directory)  # 生成 HTML 输出到指定目录

.\numpy\tools\download-wheels.py

#!/usr/bin/env python3
"""
Script to download NumPy wheels from the Anaconda staging area.

Usage::

    $ ./tools/download-wheels.py <version> -w <optional-wheelhouse>

The default wheelhouse is ``release/installers``.

Dependencies
------------

- beautifulsoup4
- urllib3

Examples
--------

While in the repository root::

    $ python tools/download-wheels.py 1.19.0
    $ python tools/download-wheels.py 1.19.0 -w ~/wheelhouse

"""
import os
import re
import shutil
import argparse

import urllib3
from bs4 import BeautifulSoup

__version__ = "0.1"

# Edit these for other projects.
STAGING_URL = "https://anaconda.org/multibuild-wheels-staging/numpy"
PREFIX = "numpy"

# Name endings of the files to download.
WHL = r"-.*\.whl$"
ZIP = r"\.zip$"
GZIP = r"\.tar\.gz$"
SUFFIX = rf"({WHL}|{GZIP}|{ZIP})"


def get_wheel_names(version):
    """ Get wheel names from Anaconda HTML directory.

    This looks in the Anaconda multibuild-wheels-staging page and
    parses the HTML to get all the wheel names for a release version.

    Parameters
    ----------
    version : str
        The release version. For instance, "1.18.3".

    """
    # Create an HTTP connection pool manager with certificate validation
    http = urllib3.PoolManager(cert_reqs="CERT_REQUIRED")
    # Regular expression pattern to match wheel file names for the given version
    tmpl = re.compile(rf"^.*{PREFIX}-{version}{SUFFIX}")
    # URL to the directory containing wheel files for the specified version
    index_url = f"{STAGING_URL}/files"
    # Perform a GET request to retrieve the HTML content of the directory
    index_html = http.request("GET", index_url)
    # Parse the HTML using BeautifulSoup for easier manipulation
    soup = BeautifulSoup(index_html.data, "html.parser")
    # Return all elements in the parsed HTML that match the wheel file name pattern
    return soup.find_all(string=tmpl)


def download_wheels(version, wheelhouse):
    """Download release wheels.

    The release wheels for the given NumPy version are downloaded
    into the given directory.

    Parameters
    ----------
    version : str
        The release version. For instance, "1.18.3".
    wheelhouse : str
        Directory in which to download the wheels.

    """
    # Create an HTTP connection pool manager with certificate validation
    http = urllib3.PoolManager(cert_reqs="CERT_REQUIRED")
    # Get the list of wheel names for the specified NumPy version
    wheel_names = get_wheel_names(version)

    # Iterate through each wheel name and download it
    for i, wheel_name in enumerate(wheel_names):
        # Construct the URL to download the wheel file
        wheel_url = f"{STAGING_URL}/{version}/download/{wheel_name}"
        # Construct the local path where the wheel file will be saved
        wheel_path = os.path.join(wheelhouse, wheel_name)
        # Open a file in binary write mode to save the downloaded content
        with open(wheel_path, "wb") as f:
            # Perform a GET request to download the wheel file
            with http.request("GET", wheel_url, preload_content=False) as r:
                # Print the progress of each download
                print(f"{i + 1:<4}{wheel_name}")
                # Copy the downloaded content to the local file
                shutil.copyfileobj(r, f)
    # Print the total number of files downloaded
    print(f"\nTotal files downloaded: {len(wheel_names)}")


if __name__ == "__main__":
    # Initialize argument parser for command line arguments
    parser = argparse.ArgumentParser()
    # Positional argument: NumPy version to download
    parser.add_argument(
        "version",
        help="NumPy version to download.")
    # Optional argument: directory where downloaded wheels will be stored
    parser.add_argument(
        "-w", "--wheelhouse",
        default=os.path.join(os.getcwd(), "release", "installers"),
        help="Directory in which to store downloaded wheels\n"
             "[defaults to <cwd>/release/installers]")

    # Parse command line arguments
    args = parser.parse_args()

    # Expand user directory in case of '~' in the path
    wheelhouse = os.path.expanduser(args.wheelhouse)
    # 如果指定的 wheelhouse 目录不存在,则抛出运行时错误
    if not os.path.isdir(wheelhouse):
        # 使用 f-string 格式化错误信息,提示指定的 wheelhouse 目录不存在
        raise RuntimeError(
            f"{wheelhouse} wheelhouse directory is not present."
            " Perhaps you need to use the '-w' flag to specify one.")
    
    # 调用函数下载指定版本的 wheels 到指定的 wheelhouse 目录
    download_wheels(args.version, wheelhouse)

.\numpy\tools\find_deprecated_escaped_characters.py

#!/usr/bin/env python3
r"""
Look for escape sequences deprecated in Python 3.6.

Python 3.6 deprecates a number of non-escape sequences starting with '\' that
were accepted before. For instance, '\(' was previously accepted but must now
be written as '\\(' or r'\('.

"""

# 主函数,用于查找已弃用的转义序列
def main(root):
    """Find deprecated escape sequences.

    Checks for deprecated escape sequences in ``*.py files``. If `root` is a
    file, that file is checked, if `root` is a directory all ``*.py`` files
    found in a recursive descent are checked.

    If a deprecated escape sequence is found, the file and line where found is
    printed. Note that for multiline strings the line where the string ends is
    printed and the error(s) are somewhere in the body of the string.

    Parameters
    ----------
    root : str
        File or directory to check.
    Returns
    -------
    None

    """
    import ast  # 导入抽象语法树模块
    import tokenize  # 导入 tokenize 模块
    import warnings  # 导入警告模块
    from pathlib import Path  # 从 pathlib 模块导入 Path 类

    count = 0  # 初始化计数器为 0
    base = Path(root)  # 将输入的根路径转换为 Path 对象
    paths = base.rglob("*.py") if base.is_dir() else [base]  # 获取所有以 .py 结尾的文件路径列表
    for path in paths:
        # 使用 tokenize 打开文件,自动检测编码
        with tokenize.open(str(path)) as f:
            # 使用警告模块捕获警告
            with warnings.catch_warnings(record=True) as w:
                warnings.simplefilter('always')  # 设置警告过滤器
                tree = ast.parse(f.read())  # 解析文件内容为抽象语法树
            if w:
                print("file: ", str(path))  # 打印文件路径
                for e in w:
                    print('line: ', e.lineno, ': ', e.message)  # 打印警告所在行及消息
                print()
                count += len(w)  # 计算警告数量
    print("Errors Found", count)  # 打印总共找到的错误数量


if __name__ == "__main__":
    from argparse import ArgumentParser  # 从 argparse 模块导入 ArgumentParser 类

    parser = ArgumentParser(description="Find deprecated escaped characters")
    parser.add_argument('root', help='directory or file to be checked')  # 添加命令行参数 root
    args = parser.parse_args()  # 解析命令行参数
    main(args.root)  # 调用主函数进行检查

.\numpy\tools\functions_missing_types.py

#!/usr/bin/env python
"""Find the functions in a module missing type annotations.

To use it run

./functions_missing_types.py <module>

and it will print out a list of functions in the module that don't
have types.

"""
import argparse  # 导入用于解析命令行参数的模块
import ast  # 导入用于抽象语法树操作的模块
import importlib  # 导入用于动态导入模块的模块
import os  # 导入与操作系统相关的功能

NUMPY_ROOT = os.path.dirname(os.path.join(
    os.path.abspath(__file__), "..",
))

# Technically "public" functions (they don't start with an underscore)
# that we don't want to include.
EXCLUDE_LIST = {
    "numpy": {
        # Stdlib modules in the namespace by accident
        "absolute_import",
        "division",
        "print_function",
        "warnings",
        "sys",
        "os",
        "math",
        # Accidentally public, deprecated, or shouldn't be used
        "Tester",
        "_core",
        "get_array_wrap",
        "int_asbuffer",
        "numarray",
        "oldnumeric",
        "safe_eval",
        "test",
        "typeDict",
        # Builtins
        "bool",
        "complex",
        "float",
        "int",
        "long",
        "object",
        "str",
        "unicode",
        # More standard names should be preferred
        "alltrue",  # all
        "sometrue",  # any
    }
}


class FindAttributes(ast.NodeVisitor):
    """Find top-level attributes/functions/classes in stubs files.

    Do this by walking the stubs ast. See e.g.

    https://greentreesnakes.readthedocs.io/en/latest/index.html

    for more information on working with Python's ast.

    """

    def __init__(self):
        self.attributes = set()

    def visit_FunctionDef(self, node):
        if node.name == "__getattr__":
            # Not really a module member.
            return
        self.attributes.add(node.name)
        # Do not call self.generic_visit; we are only interested in
        # top-level functions.
        return

    def visit_ClassDef(self, node):
        if not node.name.startswith("_"):
            self.attributes.add(node.name)
        return

    def visit_AnnAssign(self, node):
        self.attributes.add(node.target.id)


def find_missing(module_name):
    module_path = os.path.join(
        NUMPY_ROOT,
        module_name.replace(".", os.sep),
        "__init__.pyi",
    )

    module = importlib.import_module(module_name)
    module_attributes = {
        attribute for attribute in dir(module) if not attribute.startswith("_")
    }

    if os.path.isfile(module_path):
        with open(module_path) as f:
            tree = ast.parse(f.read())
        ast_visitor = FindAttributes()
        ast_visitor.visit(tree)
        stubs_attributes = ast_visitor.attributes
    else:
        # No stubs for this module yet.
        stubs_attributes = set()

    exclude_list = EXCLUDE_LIST.get(module_name, set())

    missing = module_attributes - stubs_attributes - exclude_list
    print("\n".join(sorted(missing)))


def main():
    parser = argparse.ArgumentParser()  # 创建命令行参数解析器对象
    parser.add_argument("module")  # 添加一个位置参数 'module'
    args = parser.parse_args()  # 解析命令行参数
    # 调用函数 `find_missing`,并传递参数 `args.module`
    find_missing(args.module)
# 如果当前脚本作为主程序执行(而不是作为模块被导入),则执行 main() 函数
if __name__ == "__main__":
    main()

.\numpy\tools\linter.py

import os
import sys
import subprocess
from argparse import ArgumentParser
from git import Repo, exc

CONFIG = os.path.join(
         os.path.abspath(os.path.dirname(__file__)),
         'lint_diff.ini',
)
# 配置文件的路径,用于指定 pycodestyle 的配置

# NOTE: The `diff` and `exclude` options of pycodestyle seem to be
# incompatible, so instead just exclude the necessary files when
# computing the diff itself.
EXCLUDE = (
    "numpy/typing/tests/data/",
    "numpy/typing/_char_codes.py",
    "numpy/__config__.py",
    "numpy/f2py",
)
# 需要在差异计算时排除的文件列表

class DiffLinter:
    def __init__(self, branch):
        self.branch = branch
        self.repo = Repo('.')
        self.head = self.repo.head.commit

    def get_branch_diff(self, uncommitted = False):
        """
            Determine the first common ancestor commit.
            Find diff between branch and FCA commit.
            Note: if `uncommitted` is set, check only
                  uncommitted changes
        """
        try:
            commit = self.repo.merge_base(self.branch, self.head)[0]
        except exc.GitCommandError:
            print(f"Branch with name `{self.branch}` does not exist")
            sys.exit(1)
        
        # 构建排除文件列表,以便在差异计算时使用
        exclude = [f':(exclude){i}' for i in EXCLUDE]
        if uncommitted:
            # 如果只检查未提交的更改,则使用当前头部与排除文件列表来计算差异
            diff = self.repo.git.diff(
                self.head, '--unified=0', '***.py', *exclude
            )
        else:
            # 否则,使用合并基础与当前头部以及排除文件列表来计算差异
            diff = self.repo.git.diff(
                commit, self.head, '--unified=0', '***.py', *exclude
            )
        return diff

    def run_pycodestyle(self, diff):
        """
            Original Author: Josh Wilson (@person142)
            Source:
              https://github.com/scipy/scipy/blob/main/tools/lint_diff.py
            Run pycodestyle on the given diff.
        """
        # 运行 pycodestyle 来检查给定的差异内容
        res = subprocess.run(
            ['pycodestyle', '--diff', '--config', CONFIG],
            input=diff,
            stdout=subprocess.PIPE,
            encoding='utf-8',
        )
        return res.returncode, res.stdout

    def run_lint(self, uncommitted):
        # 获取差异内容
        diff = self.get_branch_diff(uncommitted)
        # 运行 pycodestyle 来检查差异
        retcode, errors = self.run_pycodestyle(diff)

        # 如果有错误则打印出来
        errors and print(errors)

        # 根据返回码退出程序
        sys.exit(retcode)


if __name__ == '__main__':
    parser = ArgumentParser()
    parser.add_argument("--branch", type=str, default='main',
                        help="The branch to diff against")
    parser.add_argument("--uncommitted", action='store_true',
                        help="Check only uncommitted changes")
    args = parser.parse_args()

    # 创建 DiffLinter 实例并运行 lint 检查
    DiffLinter(args.branch).run_lint(args.uncommitted)

.\numpy\tools\refguide_check.py

"""
refguide_check.py [OPTIONS] [-- ARGS]

- Check for a NumPy submodule whether the objects in its __all__ dict
  correspond to the objects included in the reference guide.
- Check docstring examples
- Check example blocks in RST files

Example of usage::

    $ python tools/refguide_check.py

Note that this is a helper script to be able to check if things are missing;
the output of this script does need to be checked manually.  In some cases
objects are left out of the refguide for a good reason (it's an alias of
another function, or deprecated, or ...)

Another use of this helper script is to check validity of code samples
in docstrings::

    $ python tools/refguide_check.py --doctests ma

or in RST-based documentations::

    $ python tools/refguide_check.py --rst doc/source

"""

import copy  # 导入copy模块,用于复制对象
import doctest  # 导入doctest模块,用于执行文档字符串中的测试
import inspect  # 导入inspect模块,用于检查对象的属性和方法
import io  # 导入io模块,用于处理流
import os  # 导入os模块,用于与操作系统交互
import re  # 导入re模块,用于正则表达式操作
import shutil  # 导入shutil模块,用于文件和目录操作
import sys  # 导入sys模块,用于系统相关的参数和功能
import tempfile  # 导入tempfile模块,用于创建临时文件和目录
import warnings  # 导入warnings模块,用于警告控制

import docutils.core  # 导入docutils.core模块,用于处理reStructuredText文档
from argparse import ArgumentParser  # 导入ArgumentParser类,用于命令行参数解析
from contextlib import contextmanager, redirect_stderr  # 导入上下文管理器和错误重定向函数
from doctest import NORMALIZE_WHITESPACE, ELLIPSIS, IGNORE_EXCEPTION_DETAIL  # 导入doctest中的常量

from docutils.parsers.rst import directives  # 导入reStructuredText解析器中的指令处理器

import sphinx  # 导入sphinx模块,用于文档生成工具
import numpy as np  # 导入numpy模块,并使用np作为别名

sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'doc', 'sphinxext'))
# 将sphinxext目录添加到系统路径,以便导入自定义Sphinx扩展

from numpydoc.docscrape_sphinx import get_doc_object  # 导入获取文档对象的函数

SKIPBLOCK = doctest.register_optionflag('SKIPBLOCK')
# 注册一个名为SKIPBLOCK的自定义选项标志,用于doctest模块的扩展功能

# 启用特定的Sphinx指令
from sphinx.directives.other import SeeAlso, Only  # 导入Sphinx其他指令
directives.register_directive('seealso', SeeAlso)  # 注册seealso指令
directives.register_directive('only', Only)  # 注册only指令

BASE_MODULE = "numpy"  # 定义基础模块名称为numpy

PUBLIC_SUBMODULES = [
    "f2py",
    "linalg",
    "lib",
    "lib.format",
    "lib.mixins",
    "lib.recfunctions",
    "lib.scimath",
    "lib.stride_tricks",
    "lib.npyio",
    "lib.introspect",
    "lib.array_utils",
    "fft",
    "char",
    "rec",
    "ma",
    "ma.extras",
    "ma.mrecords",
    "polynomial",
    "polynomial.chebyshev",
    "polynomial.hermite",
    "polynomial.hermite_e",
    "polynomial.laguerre",
    "polynomial.legendre",
    "polynomial.polynomial",
    "matrixlib",
    "random",
    "strings",
    "testing",
]
# 定义公共子模块列表,包含NumPy模块中的各个子模块名称

# 这些模块的文档包含在父模块中
OTHER_MODULE_DOCS = {
    'fftpack.convolve': 'fftpack',
    'io.wavfile': 'io',
    'io.arff': 'io',
}
# 指定这些模块的文档内容包含在父模块中,以字典形式存储

# 这些名称已知会导致doctest失败,故意保留这种状态
# 例如,有时可以接受伪代码等情况
#
# 可选地,通过将字典值设置为方法名称的集合来跳过方法的子集
DOCTEST_SKIPDICT = {
    # NumPy文档字符串中从SciPy导入内容的情况:
    'numpy.lib.vectorize': None,
    'numpy.random.standard_gamma': None,
    'numpy.random.gamma': None,
    'numpy.random.vonmises': None,
    'numpy.random.power': None,
    'numpy.random.zipf': None,
    # NumPy文档字符串中从其他第三方库导入内容的情况:
    'numpy._core.from_dlpack': None,
}
# 定义一个字典,包含已知会导致doctest失败的函数或方法名称
    # 禁用对特定类的导入,因为在 doctest 中与远程或本地文件 IO 会出现问题:
    'numpy.lib.npyio.DataSource': None,
    'numpy.lib.Repository': None,
# 跳过非 numpy RST 文件和历史发布说明
# 任何单目录完全匹配将跳过该目录及其所有子目录。
# 任何完全匹配(如'doc/release')将扫描子目录但跳过匹配目录中的文件。
# 任何文件名将跳过该文件。
RST_SKIPLIST = [
    'scipy-sphinx-theme',
    'sphinxext',
    'neps',
    'changelog',
    'doc/release',
    'doc/source/release',
    'doc/release/upcoming_changes',
    'c-info.ufunc-tutorial.rst',
    'c-info.python-as-glue.rst',
    'f2py.getting-started.rst',
    'f2py-examples.rst',
    'arrays.nditer.cython.rst',
    'how-to-verify-bug.rst',
    # 参见 PR 17222,这些应该修复
    'basics.dispatch.rst',
    'basics.subclassing.rst',
    'basics.interoperability.rst',
    'misc.rst',
    'TESTS.rst'
]

# 这些名称不需要在所有 REFERENCE GUIDE 中必须出现,尽管在 autosummary:: 列表中
REFGUIDE_ALL_SKIPLIST = [
    r'scipy\.sparse\.linalg',
    r'scipy\.spatial\.distance',
    r'scipy\.linalg\.blas\.[sdczi].*',
    r'scipy\.linalg\.lapack\.[sdczi].*',
]

# 这些名称不需要在 autosummary:: 列表中出现,尽管在 ALL 中
REFGUIDE_AUTOSUMMARY_SKIPLIST = [
    # 注意: NumPy 是否应该在 autosummary 列表和 __all__ 之间有更好的匹配?暂时,TR 没有被说服这是一个优先事项 - 专注于执行/修正文档字符串
    r'numpy\.*',
]
# 在 scipy.signal 命名空间中废弃的窗口函数
for name in ('barthann', 'bartlett', 'blackmanharris', 'blackman', 'bohman',
             'boxcar', 'chebwin', 'cosine', 'exponential', 'flattop',
             'gaussian', 'general_gaussian', 'hamming', 'hann', 'hanning',
             'kaiser', 'nuttall', 'parzen', 'slepian', 'triang', 'tukey'):
    REFGUIDE_AUTOSUMMARY_SKIPLIST.append(r'scipy\.signal\.' + name)

# 是否拥有 matplotlib,默认为 False
HAVE_MATPLOTLIB = False
    """
    The `names_dict` is updated by reference and accessible in calling method

    Parameters
    ----------
    module : ModuleType
        The module, whose docstrings is to be searched
    names_dict : dict
        Dictionary which contains module name as key and a set of found
        function names and directives as value

    Returns
    -------
    None
    """

    # 定义用于匹配文档字符串中函数和指令名称的正则表达式模式列表
    patterns = [
        r"^\s\s\s([a-z_0-9A-Z]+)(\s+-+.*)?$",  # 匹配函数名或指令名格式
        r"^\.\. (?:data|function)::\s*([a-z_0-9A-Z]+)\s*$"  # 匹配数据或函数指令格式
    ]

    # 如果模块名为'scipy.constants',添加特定于该模块的正则表达式模式
    if module.__name__ == 'scipy.constants':
        patterns += ["^``([a-z_0-9A-Z]+)``"]

    # 编译所有正则表达式模式
    patterns = [re.compile(pattern) for pattern in patterns]

    # 获取模块的名称
    module_name = module.__name__

    # 遍历模块的文档字符串的每一行
    for line in module.__doc__.splitlines():
        # 检查是否匹配模块名称的注释行,并更新模块名称
        res = re.search(r"^\s*\.\. (?:currentmodule|module):: ([a-z0-9A-Z_.]+)\s*$", line)
        if res:
            module_name = res.group(1)
            continue

        # 遍历所有正则表达式模式,匹配当前行
        for pattern in patterns:
            res = re.match(pattern, line)
            if res is not None:
                # 提取匹配的函数或指令名称
                name = res.group(1)
                # 构建模块名和函数/指令名的完整条目
                entry = '.'.join([module_name, name])
                # 将函数或指令名称添加到names_dict中对应模块名的集合中
                names_dict.setdefault(module_name, set()).add(name)
                break
# 返回一个经过处理后的 __all__ 字典的副本,移除了无关的条目
def get_all_dict(module):
    if hasattr(module, "__all__"):
        # 如果模块有 __all__ 属性,则深拷贝它的值
        all_dict = copy.deepcopy(module.__all__)
    else:
        # 否则,深拷贝模块的所有属性名,并移除以 "_" 开头的属性名
        all_dict = copy.deepcopy(dir(module))
        all_dict = [name for name in all_dict
                    if not name.startswith("_")]

    # 移除特定的字符串 'absolute_import', 'division', 'print_function'
    for name in ['absolute_import', 'division', 'print_function']:
        try:
            all_dict.remove(name)
        except ValueError:
            pass

    # 如果剩余的 all_dict 为空列表,则表示可能是一个纯文档模块,将 '__doc__' 加入其中
    if not all_dict:
        all_dict.append('__doc__')

    # 进一步过滤掉模块属性,只保留可调用的函数或类名
    all_dict = [name for name in all_dict
                if not inspect.ismodule(getattr(module, name, None))]

    deprecated = []
    not_deprecated = []

    # 对每个属性进行分类,将已弃用的和未弃用的分别加入不同的列表中
    for name in all_dict:
        f = getattr(module, name, None)
        if callable(f) and is_deprecated(f):
            deprecated.append(name)
        else:
            not_deprecated.append(name)

    # 计算其他未归类的模块属性
    others = set(dir(module)).difference(set(deprecated)).difference(set(not_deprecated))

    return not_deprecated, deprecated, others


# 比较 all_dict 中的属性和其他属性,返回三个集合:仅存在于 all_dict 中的,仅存在于 names 中的,以及在 names 中但在 others 中缺失的
def compare(all_dict, others, names, module_name):
    only_all = set()

    # 遍历 all_dict 中的属性,将不在 names 中的加入 only_all 集合
    for name in all_dict:
        if name not in names:
            for pat in REFGUIDE_AUTOSUMMARY_SKIPLIST:
                if re.match(pat, module_name + '.' + name):
                    break
            else:
                only_all.add(name)

    only_ref = set()
    missing = set()

    # 遍历 names 中的属性,将不在 all_dict 中的加入 only_ref 集合,将不在 others 中的加入 missing 集合
    for name in names:
        if name not in all_dict:
            for pat in REFGUIDE_ALL_SKIPLIST:
                if re.match(pat, module_name + '.' + name):
                    if name not in others:
                        missing.add(name)
                    break
            else:
                only_ref.add(name)

    return only_all, only_ref, missing


# 检查模块 f 是否已弃用
def is_deprecated(f):
    # 省略了返回值说明,这是一个检查模块 f 是否已弃用的函数
    # 使用 `warnings.catch_warnings` 捕获警告信息,记录在变量 `w` 中
    with warnings.catch_warnings(record=True) as w:
        # 设置警告过滤器,使得 DeprecationWarning 被视为错误
        warnings.simplefilter("error")
        try:
            # 调用函数 `f`,传入一个不正确的关键字参数字典
            f(**{"not a kwarg": None})
        except DeprecationWarning:
            # 如果捕获到 DeprecationWarning,表示函数使用了不推荐使用的特性,返回 True
            return True
        except Exception:
            # 捕获所有其他异常,不做处理
            pass
        # 如果没有捕获到 DeprecationWarning,则返回 False
        return False
# 检查 `all_dict` 是否与 `names` 在 `module_name` 中一致,确保没有废弃或多余的对象。
# 返回一个列表,每个元素为 (name, success_flag, output),表示检查结果。

def check_items(all_dict, names, deprecated, others, module_name, dots=True):
    """
    Check that `all_dict` is consistent with the `names` in `module_name`
    For instance, that there are no deprecated or extra objects.

    Parameters
    ----------
    all_dict : list
        待检查的对象列表

    names : set
        参考指南中的对象名称集合

    deprecated : list
        废弃的对象列表

    others : list
        其他对象列表

    module_name : ModuleType
        模块名称

    dots : bool
        是否打印每次检查的点符号

    Returns
    -------
    list
        返回 [(name, success_flag, output)...] 的列表
    """

    # 计算 `all_dict` 和 `names` 的长度
    num_all = len(all_dict)
    num_ref = len(names)

    # 初始化输出字符串
    output = ""

    # 添加非废弃对象数量到输出
    output += "Non-deprecated objects in __all__: %i\n" % num_all
    # 添加参考指南中对象数量到输出
    output += "Objects in refguide: %i\n\n" % num_ref

    # 比较 `all_dict`、`others` 和 `names`,找出只在其中一个中存在的对象
    only_all, only_ref, missing = compare(all_dict, others, names, module_name)
    
    # 找出在参考指南中但已被废弃的对象
    dep_in_ref = only_ref.intersection(deprecated)
    # 从只在参考指南中的对象中去除已废弃的对象
    only_ref = only_ref.difference(deprecated)

    # 如果存在已废弃的对象,将它们添加到输出中
    if len(dep_in_ref) > 0:
        output += "Deprecated objects in refguide::\n\n"
        for name in sorted(deprecated):
            output += "    " + name + "\n"

    # 如果没有任何不一致,返回成功标志和输出
    if len(only_all) == len(only_ref) == len(missing) == 0:
        if dots:
            output_dot('.')
        return [(None, True, output)]
    else:
        # 如果存在在 `all_dict` 中但不在参考指南中的对象,将它们添加到输出中
        if len(only_all) > 0:
            output += "ERROR: objects in %s.__all__ but not in refguide::\n\n" % module_name
            for name in sorted(only_all):
                output += "    " + name + "\n"

            output += "\nThis issue can be fixed by adding these objects to\n"
            output += "the function listing in __init__.py for this module\n"

        # 如果存在在参考指南中但不在 `all_dict` 中的对象,将它们添加到输出中
        if len(only_ref) > 0:
            output += "ERROR: objects in refguide but not in %s.__all__::\n\n" % module_name
            for name in sorted(only_ref):
                output += "    " + name + "\n"

            output += "\nThis issue should likely be fixed by removing these objects\n"
            output += "from the function listing in __init__.py for this module\n"
            output += "or adding them to __all__.\n"

        # 如果存在缺失的对象,将它们添加到输出中
        if len(missing) > 0:
            output += "ERROR: missing objects::\n\n"
            for name in sorted(missing):
                output += "    " + name + "\n"

        # 如果需要打印点符号,打印 'F' 表示失败
        if dots:
            output_dot('F')
        return [(None, False, output)]


def validate_rst_syntax(text, name, dots=True):
    """
    Validates the doc string in a snippet of documentation
    `text` from file `name`

    Parameters
    ----------
    text : str
        待验证的文档字符串内容

    name : str
        文档所属文件名

    dots : bool
        是否打印每次检查的点符号

    Returns
    -------
    (bool, str)
        返回元组,第一个元素表示验证结果,第二个元素是相关输出信息
    """

    # 如果文档字符串为空,返回验证失败和相关错误信息
    if text is None:
        if dots:
            output_dot('E')
        return False, "ERROR: %s: no documentation" % (name,)
    # 定义一个包含可忽略项的集合,这些项在处理文档时可以忽略
    ok_unknown_items = set([
        'mod', 'doc', 'currentmodule', 'autosummary', 'data', 'attr',
        'obj', 'versionadded', 'versionchanged', 'module', 'class',
        'ref', 'func', 'toctree', 'moduleauthor', 'term', 'c:member',
        'sectionauthor', 'codeauthor', 'eq', 'doi', 'DOI', 'arXiv', 'arxiv'
    ])

    # 创建一个字符串流用于捕获错误信息
    error_stream = io.StringIO()

    # 定义一个函数 resolve,用于返回给定名称的 URL,通常返回一个假的 URL
    def resolve(name, is_label=False):
        return ("http://foo", name)

    # 定义一个特定的令牌字符串
    token = '<RST-VALIDATE-SYNTAX-CHECK>'

    # 使用 docutils 库的 publish_doctree 函数处理文本,返回文档树
    docutils.core.publish_doctree(
        text, token,
        settings_overrides = dict(halt_level=5,
                                  traceback=True,
                                  default_reference_context='title-reference',
                                  default_role='emphasis',
                                  link_base='',
                                  resolve_name=resolve,
                                  stylesheet_path='',
                                  raw_enabled=0,
                                  file_insertion_enabled=0,
                                  warning_stream=error_stream))

    # 从错误流中获取错误信息字符串
    error_msg = error_stream.getvalue()

    # 根据特定令牌分割错误信息,得到错误列表
    errors = error_msg.split(token)

    # 初始化成功标志为 True,输出字符串为空
    success = True
    output = ""

    # 遍历错误列表,处理每个错误信息
    for error in errors:
        lines = error.splitlines()
        if not lines:
            continue

        # 使用正则表达式匹配并检查是否是未知的解释文本角色或指令类型错误
        m = re.match(r'.*Unknown (?:interpreted text role|directive type) "(.*)".*$', lines[0])
        if m:
            # 如果匹配到未知项并且在可忽略项集合中,则跳过此错误
            if m.group(1) in ok_unknown_items:
                continue

        # 使用正则表达式匹配是否是数学指令错误,忽略“label”选项错误
        m = re.match(r'.*Error in "math" directive:.*unknown option: "label"', " ".join(lines), re.S)
        if m:
            continue

        # 将错误信息格式化为输出字符串,并设置成功标志为 False
        output += name + lines[0] + "::\n    " + "\n    ".join(lines[1:]).rstrip() + "\n"
        success = False

    # 如果有任何错误,则在输出字符串中添加分隔线和原始文本内容
    if not success:
        output += "    " + "-"*72 + "\n"
        for lineno, line in enumerate(text.splitlines()):
            output += "    %-4d    %s\n" % (lineno+1, line)
        output += "    " + "-"*72 + "\n\n"

    # 如果 dots 变量存在,则输出对应的点号(成功)或字母 'F'(失败)
    if dots:
        output_dot('.' if success else 'F')

    # 返回处理结果的成功标志和生成的输出字符串
    return success, output
# 输出一个消息到指定流,默认为标准错误流
def output_dot(msg='.', stream=sys.stderr):
    stream.write(msg)  # 将消息写入指定流
    stream.flush()  # 立即刷新流,确保消息被输出


# 检查模块的 reStructuredText 格式化情况
def check_rest(module, names, dots=True):
    """
    Check reStructuredText formatting of docstrings

    Parameters
    ----------
    module : ModuleType
        要检查的模块对象

    names : set
        包含要检查的名称的集合

    Returns
    -------
    result : list
        包含元组 (module_name, success_flag, output) 的列表
    """

    try:
        skip_types = (dict, str, unicode, float, int)  # 尝试定义要跳过的对象类型
    except NameError:
        # Python 3 中,unicode 类型不再存在
        skip_types = (dict, str, float, int)  # 定义要跳过的对象类型

    results = []  # 初始化结果列表

    # 如果模块名称不在预定义的 OTHER_MODULE_DOCS 中,则进行检查
    if module.__name__[6:] not in OTHER_MODULE_DOCS:
        # 调用 validate_rst_syntax 函数,验证模块的文档字符串语法
        results += [(module.__name__,) +
                    validate_rst_syntax(inspect.getdoc(module),
                                        module.__name__, dots=dots)]

    for name in names:
        full_name = module.__name__ + '.' + name
        obj = getattr(module, name, None)

        if obj is None:
            # 如果对象不存在,则记录结果
            results.append((full_name, False, "%s has no docstring" % (full_name,)))
            continue
        elif isinstance(obj, skip_types):
            # 如果对象的类型在跳过类型中,则跳过此次循环
            continue

        if inspect.ismodule(obj):
            text = inspect.getdoc(obj)  # 获取模块的文档字符串
        else:
            try:
                text = str(get_doc_object(obj))  # 获取对象的文档字符串并转换为字符串类型
            except Exception:
                import traceback
                # 如果获取文档字符串时出现异常,则记录错误信息
                results.append((full_name, False,
                                "Error in docstring format!\n" +
                                traceback.format_exc()))
                continue

        # 检查文档字符串中是否包含不可打印字符
        m = re.search("([\x00-\x09\x0b-\x1f])", text)
        if m:
            # 如果文档字符串包含不可打印字符,则记录警告消息
            msg = ("Docstring contains a non-printable character %r! "
                   "Maybe forgot r\"\"\"?" % (m.group(1),))
            results.append((full_name, False, msg))
            continue

        try:
            src_file = short_path(inspect.getsourcefile(obj))  # 获取对象源文件的简短路径
        except TypeError:
            src_file = None

        if src_file:
            file_full_name = src_file + ':' + full_name  # 构建带文件信息的完整名称
        else:
            file_full_name = full_name

        # 调用 validate_rst_syntax 函数,验证文本的 reStructuredText 语法
        results.append((full_name,) + validate_rst_syntax(text, file_full_name, dots=dots))

    return results  # 返回检查结果列表


### Doctest helpers ####

# 在运行例子时使用的命名空间
DEFAULT_NAMESPACE = {'np': np}

# 在检查中使用的命名空间
CHECK_NAMESPACE = {
      'np': np,
      'numpy': np,
      'assert_allclose': np.testing.assert_allclose,
      'assert_equal': np.testing.assert_equal,
      # 识别 numpy 的表现形式
      'array': np.array,
      'matrix': np.matrix,
      'int64': np.int64,
      'uint64': np.uint64,
      'int8': np.int8,
      'int32': np.int32,
      'float32': np.float32,
      'float64': np.float64,
      'dtype': np.dtype,
      'nan': np.nan,
      'inf': np.inf,
      'StringIO': io.StringIO,
}


class DTRunner(doctest.DocTestRunner):
    """
    The doctest runner
    """
    DIVIDER = "\n"  # 定义用于分隔输出的分隔符
    # 初始化函数,用于设置测试项的名称、检查器、详细模式和选项标志
    def __init__(self, item_name, checker=None, verbose=None, optionflags=0):
        # 设置实例的测试项名称
        self._item_name = item_name
        # 调用父类 DocTestRunner 的初始化方法,设置检查器、详细模式和选项标志
        doctest.DocTestRunner.__init__(self, checker=checker, verbose=verbose,
                                       optionflags=optionflags)

    # 报告测试项名称的输出,可选择在输出前添加换行符
    def _report_item_name(self, out, new_line=False):
        # 如果存在测试项名称并需要添加新行,则在输出中添加换行符
        if self._item_name is not None:
            if new_line:
                out("\n")
            # 将测试项名称设为 None,确保只输出一次
            self._item_name = None

    # 报告测试开始的方法,设置检查器的源码,并调用父类的 report_start 方法
    def report_start(self, out, test, example):
        # 设置检查器的源码为示例的源码
        self._checker._source = example.source
        # 调用父类 DocTestRunner 的 report_start 方法进行报告
        return doctest.DocTestRunner.report_start(self, out, test, example)

    # 报告测试成功的方法,根据详细模式决定是否输出测试项名称,并调用父类的 report_success 方法
    def report_success(self, out, test, example, got):
        # 如果设置了详细模式,则输出测试项名称(新行形式)
        if self._verbose:
            self._report_item_name(out, new_line=True)
        # 调用父类 DocTestRunner 的 report_success 方法进行报告
        return doctest.DocTestRunner.report_success(self, out, test, example, got)

    # 报告意外异常的方法,输出测试项名称,并调用父类的 report_unexpected_exception 方法
    def report_unexpected_exception(self, out, test, example, exc_info):
        # 输出测试项名称
        self._report_item_name(out)
        # 调用父类 DocTestRunner 的 report_unexpected_exception 方法进行报告
        return doctest.DocTestRunner.report_unexpected_exception(
            self, out, test, example, exc_info)

    # 报告测试失败的方法,输出测试项名称,并调用父类的 report_failure 方法
    def report_failure(self, out, test, example, got):
        # 输出测试项名称
        self._report_item_name(out)
        # 调用父类 DocTestRunner 的 report_failure 方法进行报告
        return doctest.DocTestRunner.report_failure(self, out, test,
                                                    example, got)
class Checker(doctest.OutputChecker):
    """
    自定义的输出检查器,继承自 doctest.OutputChecker 类。
    """

    # 正则表达式模式,用于匹配对象地址字符串
    obj_pattern = re.compile('at 0x[0-9a-fA-F]+>')

    # 创建一个默认的 doctest.OutputChecker 实例
    vanilla = doctest.OutputChecker()

    # 随机标记集合,用于匹配可能含有随机内容的注释或字符串
    rndm_markers = {'# random', '# Random', '#random', '#Random', "# may vary",
                    "# uninitialized", "#uninitialized", "# uninit"}

    # 停用词集合,包含一些常见的用于绘图和数据显示的方法或属性名
    stopwords = {'plt.', '.hist', '.show', '.ylim', '.subplot(',
                 'set_title', 'imshow', 'plt.show', '.axis(', '.plot(',
                 '.bar(', '.title', '.ylabel', '.xlabel', 'set_ylim', 'set_xlim',
                 '# reformatted', '.set_xlabel(', '.set_ylabel(', '.set_zlabel(',
                 '.set(xlim=', '.set(ylim=', '.set(xlabel=', '.set(ylabel='}

    def __init__(self, parse_namedtuples=True, ns=None, atol=1e-8, rtol=1e-2):
        """
        初始化方法,用于设置对象的各种属性。

        参数:
        - parse_namedtuples: 是否解析命名元组,默认为 True
        - ns: 命名空间,用于检查,如果为 None 则使用 CHECK_NAMESPACE
        - atol: 绝对误差容限,默认为 1e-8
        - rtol: 相对误差容限,默认为 1e-2
        """
        self.parse_namedtuples = parse_namedtuples
        self.atol, self.rtol = atol, rtol
        if ns is None:
            self.ns = CHECK_NAMESPACE
        else:
            self.ns = ns
    # 如果期望值和实际值相等,则返回True,表示通过检查
    def check_output(self, want, got, optionflags):
        if want == got:
            return True

        # 在源文本中跳过停用词
        if any(word in self._source for word in self.stopwords):
            return True

        # 跳过随机标记
        if any(word in want for word in self.rndm_markers):
            return True

        # 跳过函数/对象地址
        if self.obj_pattern.search(got):
            return True

        # 忽略注释(例如 signal.freqresp)
        if want.lstrip().startswith("#"):
            return True

        # 尝试使用标准的 doctest
        try:
            if self.vanilla.check_output(want, got, optionflags):
                return True
        except Exception:
            pass

        # 尝试将字符串转换为对象
        try:
            a_want = eval(want, dict(self.ns))
            a_got = eval(got, dict(self.ns))
        except Exception:
            # 可能是打印 numpy 数组的情况
            s_want = want.strip()
            s_got = got.strip()
            cond = (s_want.startswith("[") and s_want.endswith("]") and
                    s_got.startswith("[") and s_got.endswith("]"))
            if cond:
                # 重新插入逗号并重试比较
                s_want = ", ".join(s_want[1:-1].split())
                s_got = ", ".join(s_got[1:-1].split())
                return self.check_output(s_want, s_got, optionflags)

            if not self.parse_namedtuples:
                return False
            # 假设 "want" 是一个元组,"got" 是类似 MoodResult(statistic=10, pvalue=0.1) 的情况。
            # 将后者转换为元组 (10, 0.1),然后进行比较。
            try:
                num = len(a_want)
                regex = (r'[\w\d_]+\(' +
                         ', '.join([r'[\w\d_]+=(.+)']*num) +
                         r'\)')
                grp = re.findall(regex, got.replace('\n', ' '))
                if len(grp) > 1:  # 目前只支持一个匹配
                    return False
                # 再次折叠成一个元组
                got_again = '(' + ', '.join(grp[0]) + ')'
                return self.check_output(want, got_again, optionflags)
            except Exception:
                return False

        # 如果上述尝试失败,则尝试使用 numpy 进行比较
        try:
            return self._do_check(a_want, a_got)
        except Exception:
            # 异构元组,例如 (1, np.array([1., 2.]))
            try:
                return all(self._do_check(w, g) for w, g in zip(a_want, a_got))
            except (TypeError, ValueError):
                return False
    # 定义一个方法 `_do_check`,用于比较两个值 `want` 和 `got` 是否相等或者在数值上接近
    def _do_check(self, want, got):
        # 尝试执行以下操作,确保正确处理所有类似于 numpy 对象、字符串和异构元组
        try:
            # 如果 want 等于 got,返回 True
            if want == got:
                return True
        # 捕获所有异常,不做任何处理,继续执行
        except Exception:
            pass
        # 使用 NumPy 的 allclose 方法比较 want 和 got 是否在给定的绝对误差和相对误差范围内数值接近
        return np.allclose(want, got, atol=self.atol, rtol=self.rtol)
# 运行修改后的 doctest 测试集合 `tests`

def _run_doctests(tests, full_name, verbose, doctest_warnings):
    """
    Run modified doctests for the set of `tests`.

    Parameters
    ----------
    tests : list
        包含测试用例的列表

    full_name : str
        完整名称字符串

    verbose : bool
        是否输出详细信息

    doctest_warnings : bool
        是否输出 doctest 的警告信息

    Returns
    -------
    tuple(bool, list)
        返回一个元组,包含成功标志和输出信息列表
    """
    flags = NORMALIZE_WHITESPACE | ELLIPSIS
    # 创建 DTRunner 实例,设置检查器和选项标志
    runner = DTRunner(full_name, checker=Checker(), optionflags=flags,
                      verbose=verbose)

    output = io.StringIO(newline='')
    success = True

    # 将 stderr 重定向到 stdout 或 output
    tmp_stderr = sys.stdout if doctest_warnings else output

    @contextmanager
    def temp_cwd():
        cwd = os.getcwd()
        tmpdir = tempfile.mkdtemp()
        try:
            os.chdir(tmpdir)
            yield tmpdir
        finally:
            os.chdir(cwd)
            shutil.rmtree(tmpdir)

    # 运行测试,并尝试恢复全局状态
    cwd = os.getcwd()
    with np.errstate(), np.printoptions(), temp_cwd() as tmpdir, \
            redirect_stderr(tmp_stderr):
        # 尝试确保随机种子不可重现
        np.random.seed(None)

        ns = {}
        for t in tests:
            # 更新测试的全局命名空间,以避免变量在测试块之间丢失
            t.globs.update(ns)
            # 将文件名转换为相对于当前工作目录的短路径
            t.filename = short_path(t.filename, cwd)
            # 处理测试选项
            if any([SKIPBLOCK in ex.options for ex in t.examples]):
                continue
            # 运行测试,并将结果写入输出
            fails, successes = runner.run(t, out=output.write, clear_globs=False)
            if fails > 0:
                success = False
            ns = t.globs

    # 将输出指针移到开头并读取输出内容
    output.seek(0)
    return success, output.read()


def check_doctests(module, verbose, ns=None,
                   dots=True, doctest_warnings=False):
    """
    Check code in docstrings of the module's public symbols.

    Parameters
    ----------
    module : ModuleType
        要检查的模块对象

    verbose : bool
        是否输出详细信息

    ns : dict
        模块的命名空间

    dots : bool

    doctest_warnings : bool

    Returns
    -------
    results : list
        返回结果列表 [(item_name, success_flag, output), ...]
    """
    if ns is None:
        ns = dict(DEFAULT_NAMESPACE)

    # 遍历非过时的模块项
    results = []
    # 遍历给定模块的所有函数名
    for name in get_all_dict(module)[0]:
        # 构建完整的函数名,包括模块名前缀
        full_name = module.__name__ + '.' + name

        # 检查是否需要跳过该函数的测试,根据全名在跳过字典中查找
        if full_name in DOCTEST_SKIPDICT:
            # 如果在跳过字典中找到该函数名,则获取需要跳过的测试方法列表
            skip_methods = DOCTEST_SKIPDICT[full_name]
            # 如果跳过方法列表为 None,则跳过当前函数的测试
            if skip_methods is None:
                continue
        else:
            # 如果未在跳过字典中找到该函数名,则设置跳过方法列表为 None
            skip_methods = None

        try:
            # 尝试获取当前函数的对象
            obj = getattr(module, name)
        except AttributeError:
            # 如果获取对象失败,记录缺失的项并继续下一个函数的处理
            import traceback
            results.append((full_name, False,
                            "Missing item!\n" +
                            traceback.format_exc()))
            continue

        # 创建一个 doctest 查找器对象
        finder = doctest.DocTestFinder()
        try:
            # 使用查找器找到当前函数的 doctest 测试集合
            tests = finder.find(obj, name, globs=dict(ns))
        except Exception:
            # 如果获取 doctest 失败,记录失败信息并继续下一个函数的处理
            import traceback
            results.append((full_name, False,
                            "Failed to get doctests!\n" +
                            traceback.format_exc()))
            continue

        # 如果需要跳过特定方法的测试,则过滤掉跳过列表中的测试方法
        if skip_methods is not None:
            tests = [i for i in tests if
                     i.name.partition(".")[2] not in skip_methods]

        # 运行当前函数的 doctest 测试,并获取测试结果和输出信息
        success, output = _run_doctests(tests, full_name, verbose,
                                        doctest_warnings)

        # 如果需要打印简化的测试结果(成功或失败)
        if dots:
            output_dot('.' if success else 'F')

        # 将当前函数的测试结果记录到结果列表中
        results.append((full_name, success, output))

        # 如果有 matplotlib 模块,关闭所有打开的图形窗口
        if HAVE_MATPLOTLIB:
            import matplotlib.pyplot as plt
            plt.close('all')

    # 返回所有函数测试的结果列表
    return results
# 检查指定文本文件中的文档测试代码。
def check_doctests_testfile(fname, verbose, ns=None,
                   dots=True, doctest_warnings=False):
    """
    Check code in a text file.

    Mimic `check_doctests` above, differing mostly in test discovery.
    (which is borrowed from stdlib's doctest.testfile here,
     https://github.com/python-git/python/blob/master/Lib/doctest.py)

    Parameters
    ----------
    fname : str
        File name
    verbose : bool
        是否输出详细信息
    ns : dict
        Name space,命名空间
    dots : bool
        是否显示点
    doctest_warnings : bool
        是否显示文档测试警告

    Returns
    -------
    list
        List of [(item_name, success_flag, output), ...]

    Notes
    -----

    refguide can be signalled to skip testing code by adding
    ``#doctest: +SKIP`` to the end of the line. If the output varies or is
    random, add ``# may vary`` or ``# random`` to the comment. for example

    >>> plt.plot(...)  # doctest: +SKIP
    >>> random.randint(0,10)
    5 # random

    We also try to weed out pseudocode:
    * We maintain a list of exceptions which signal pseudocode,
    * We split the text file into "blocks" of code separated by empty lines
      and/or intervening text.
    * If a block contains a marker, the whole block is then assumed to be
      pseudocode. It is then not being doctested.

    The rationale is that typically, the text looks like this:

    blah
    <BLANKLINE>
    >>> from numpy import some_module   # pseudocode!
    >>> func = some_module.some_function
    >>> func(42)                  # still pseudocode
    146
    <BLANKLINE>
    blah
    <BLANKLINE>
    >>> 2 + 3        # real code, doctest it
    5

    """
    # 如果命名空间为None,则使用默认的CHECK_NAMESPACE命名空间
    if ns is None:
        ns = CHECK_NAMESPACE
    # 存储测试结果的列表
    results = []

    # 获取文件名的基本名称和完整路径
    _, short_name = os.path.split(fname)
    # 如果文件名在DOCTEST_SKIPDICT字典中,直接返回空结果列表
    if short_name in DOCTEST_SKIPDICT:
        return results

    # 打开文件并读取文件内容到文本变量中
    full_name = fname
    with open(fname, encoding='utf-8') as f:
        text = f.read()

    # 用于标识伪代码块的集合
    PSEUDOCODE = set(['some_function', 'some_module', 'import example',
                      'ctypes.CDLL',     # likely need compiling, skip it
                      'integrate.nquad(func,'  # ctypes integrate tutotial
    ])

    # 将文本分割为多个代码块,并尝试检测和排除伪代码块
    parser = doctest.DocTestParser()
    # 存储有效部分的列表
    good_parts = []
    # 基础行号初始化为0
    base_line_no = 0
    # 按双换行符 '\n\n' 分割文本,处理每个部分
    for part in text.split('\n\n'):
        try:
            # 尝试从当前部分获取 doctest 测试
            tests = parser.get_doctest(part, ns, fname, fname, base_line_no)
        except ValueError as e:
            # 如果捕获到 ValueError 异常
            if e.args[0].startswith('line '):
                # 修正错误消息中的行号,因为 `parser.get_doctest` 不会在错误消息中增加 base_line_no
                parts = e.args[0].split()
                parts[1] = str(int(parts[1]) + base_line_no)
                e.args = (' '.join(parts),) + e.args[1:]
            # 重新抛出异常
            raise

        # 检查是否有伪代码关键字,如果有则跳过该部分
        if any(word in ex.source for word in PSEUDOCODE
                                 for ex in tests.examples):
            # 跳过该部分
            pass
        else:
            # 将看起来像是好的代码部分添加到 good_parts 中以供后续 doctest
            good_parts.append((part, base_line_no))

        # 更新 base_line_no,增加当前部分中换行符 '\n' 的数量再加 2
        base_line_no += part.count('\n') + 2

    # 重新组装好的部分并对其进行 doctest 测试
    tests = []
    for good_text, line_no in good_parts:
        tests.append(parser.get_doctest(good_text, ns, fname, fname, line_no))
    # 运行所有 doctest 测试并获取结果
    success, output = _run_doctests(tests, full_name, verbose,
                                    doctest_warnings)

    # 如果 dots 为真,则输出相应的点或失败的标志
    if dots:
        output_dot('.' if success else 'F')

    # 将当前模块的测试结果添加到 results 列表中
    results.append((full_name, success, output))

    # 如果有 matplotlib 库,关闭所有打开的图形窗口
    if HAVE_MATPLOTLIB:
        import matplotlib.pyplot as plt
        plt.close('all')

    # 返回最终的测试结果列表
    return results
# 定义生成器函数,用于遍历 `base_path` 及其子目录,跳过在 RST_SKIPLIST 中指定的文件或目录,并生成每个具有指定后缀的文件路径

def iter_included_files(base_path, verbose=0, suffixes=('.rst',)):
    """
    Generator function to walk `base_path` and its subdirectories, skipping
    files or directories in RST_SKIPLIST, and yield each file with a suffix in
    `suffixes`

    Parameters
    ----------
    base_path : str
        Base path of the directory to be processed
    verbose : int
        Verbosity level (default is 0)
    suffixes : tuple
        Tuple of suffixes to filter files (default is ('.rst',))

    Yields
    ------
    path : str
        Path of the directory and its subdirectories containing files with specified suffixes
    """
    
    # 如果 `base_path` 存在且是一个文件,则直接生成该文件路径
    if os.path.exists(base_path) and os.path.isfile(base_path):
        yield base_path
    
    # 遍历 `base_path` 及其子目录
    for dir_name, subdirs, files in os.walk(base_path, topdown=True):
        # 如果当前目录在 RST_SKIPLIST 中,则跳过其中的文件
        if dir_name in RST_SKIPLIST:
            if verbose > 0:
                sys.stderr.write('skipping files in %s' % dir_name)
            files = []  # 清空文件列表以跳过文件
        
        # 如果当前目录中存在在 RST_SKIPLIST 中的子目录,则移除以跳过这些子目录
        for p in RST_SKIPLIST:
            if p in subdirs:
                if verbose > 0:
                    sys.stderr.write('skipping %s and subdirs' % p)
                subdirs.remove(p)
        
        # 遍历当前目录中的文件
        for f in files:
            # 如果文件具有指定后缀且不在 RST_SKIPLIST 中,则生成文件的完整路径
            if (os.path.splitext(f)[1] in suffixes and
                    f not in RST_SKIPLIST):
                yield os.path.join(dir_name, f)


def check_documentation(base_path, results, args, dots):
    """
    Check examples in any *.rst located inside `base_path`.
    Add the output to `results`.

    See Also
    --------
    check_doctests_testfile
    """
    
    # 遍历 `base_path` 中包含的所有 `.rst` 文件,检查其中的示例文档
    for filename in iter_included_files(base_path, args.verbose):
        if dots:
            sys.stderr.write(filename + ' ')
            sys.stderr.flush()

        # 检查给定文件中的 doctest 测试,并将结果添加到 `results` 列表中
        tut_results = check_doctests_testfile(
            filename,
            (args.verbose >= 2), dots=dots,
            doctest_warnings=args.doctest_warnings)

        # 创建一个空的“模块”用于报告结果时需要
        def scratch():
            pass
        scratch.__name__ = filename
        results.append((scratch, tut_results))
        if dots:
            sys.stderr.write('\n')
            sys.stderr.flush()


def init_matplotlib():
    """
    Check feasibility of matplotlib initialization.
    """
    
    # 尝试导入 matplotlib 并设置使用 Agg 后端,标记是否成功导入 matplotlib
    global HAVE_MATPLOTLIB

    try:
        import matplotlib
        matplotlib.use('Agg')
        HAVE_MATPLOTLIB = True
    except ImportError:
        HAVE_MATPLOTLIB = False


def main(argv):
    """
    Validates the docstrings of all the pre decided set of
    modules for errors and docstring standards.
    """
    
    # 解析命令行参数,验证预定义模块的文档字符串的正确性和标准
    parser = ArgumentParser(usage=__doc__.lstrip())
    parser.add_argument("module_names", metavar="SUBMODULES", default=[],
                        nargs='*', help="Submodules to check (default: all public)")
    parser.add_argument("--doctests", action="store_true",
                        help="Run also doctests on ")
    parser.add_argument("-v", "--verbose", action="count", default=0)
    parser.add_argument("--doctest-warnings", action="store_true",
                        help="Enforce warning checking for doctests")
    # 添加一个命令行参数 --rst,它可以是一个可选的位置参数,如果没有提供默认为None,常量为'doc',用来指定运行 *rst 文件中的示例,这些文件在指定的目录中递归查找,默认为'doc'
    parser.add_argument("--rst", nargs='?', const='doc', default=None,
                        help=("Run also examples from *rst files "
                              "discovered walking the directory(s) specified, "
                              "defaults to 'doc'"))
    
    # 解析命令行参数
    args = parser.parse_args(argv)

    # 初始化空列表,用于存放模块对象
    modules = []
    # 初始化空字典,用于存放模块名称映射
    names_dict = {}

    # 如果未提供模块名称参数,则使用预定义的公共子模块列表和基础模块
    if not args.module_names:
        args.module_names = list(PUBLIC_SUBMODULES) + [BASE_MODULE]

    # 设置环境变量,用于启用 SciPy 的 PIL 图像查看器
    os.environ['SCIPY_PIL_IMAGE_VIEWER'] = 'true'

    # 复制模块名称列表,以便动态修改
    module_names = list(args.module_names)
    # 循环处理模块名称列表中的每一个名称
    for name in module_names:
        # 如果模块名称在 OTHER_MODULE_DOCS 中有映射,则使用映射后的名称替换原名称,并确保新名称未在列表中
        if name in OTHER_MODULE_DOCS:
            name = OTHER_MODULE_DOCS[name]
            if name not in module_names:
                module_names.append(name)

    # 初始化布尔变量 dots 和 success,以及空列表 results 和 errormsgs
    dots = True
    success = True
    results = []
    errormsgs = []

    # 如果指定运行 doctests 或者 rst 文件,则初始化 matplotlib
    if args.doctests or args.rst:
        init_matplotlib()

    # 遍历模块名称列表中的每一个子模块名称
    for submodule_name in module_names:
        # 设置模块名称的前缀为 BASE_MODULE + '.'
        prefix = BASE_MODULE + '.'
        # 如果子模块名称不以前缀开头且不等于 BASE_MODULE,则加上前缀
        if not (
            submodule_name.startswith(prefix) or
            submodule_name == BASE_MODULE
        ):
            module_name = prefix + submodule_name
        else:
            module_name = submodule_name
        
        # 动态导入模块
        __import__(module_name)
        module = sys.modules[module_name]

        # 如果子模块名称不在 OTHER_MODULE_DOCS 中,调用 find_names 函数找到模块中的名称,并更新到 names_dict 中
        if submodule_name not in OTHER_MODULE_DOCS:
            find_names(module, names_dict)

        # 如果子模块名称在命令行参数中指定的模块名称中,则将模块对象添加到 modules 列表中
        if submodule_name in args.module_names:
            modules.append(module)

    # 如果指定运行 doctests 或者不运行 rst 文件,则打印运行检查的模块数量信息
    if args.doctests or not args.rst:
        print("Running checks for %d modules:" % (len(modules),))
        # 遍历 modules 列表中的每一个模块对象
        for module in modules:
            # 如果 dots 为 True,则将模块名称打印到标准错误
            if dots:
                sys.stderr.write(module.__name__ + ' ')
                sys.stderr.flush()

            # 获取模块中的所有符号字典、过时的名称集合和其他名称集合
            all_dict, deprecated, others = get_all_dict(module)
            # 获取模块名称对应的名称集合
            names = names_dict.get(module.__name__, set())

            # 初始化模块检查结果列表
            mod_results = []
            # 检查模块中的所有项,并将结果添加到 mod_results 中
            mod_results += check_items(all_dict, names, deprecated, others,
                                       module.__name__)
            # 检查模块中未包含的名称,并将结果添加到 mod_results 中
            mod_results += check_rest(module, set(names).difference(deprecated),
                                      dots=dots)
            # 如果指定运行 doctests,则运行模块中的 doctests,并将结果添加到 mod_results 中
            if args.doctests:
                mod_results += check_doctests(module, (args.verbose >= 2), dots=dots,
                                              doctest_warnings=args.doctest_warnings)

            # 断言 mod_results 中的每个元素都是元组类型
            for v in mod_results:
                assert isinstance(v, tuple), v

            # 将模块及其检查结果作为元组添加到 results 列表中
            results.append((module, mod_results))

            # 如果 dots 为 True,则换行打印标准错误
            if dots:
                sys.stderr.write('\n')
                sys.stderr.flush()
    # 如果传入了 rst 参数,则进行以下操作
    if args.rst:
        # 获取当前脚本文件所在目录的绝对路径,并拼接上一级目录作为基础目录
        base_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..')
        # 构建 rst 文件相对于基础目录的相对路径
        rst_path = os.path.relpath(os.path.join(base_dir, args.rst))
        # 如果构建的 rst 文件路径存在,则输出检查信息
        if os.path.exists(rst_path):
            print('\nChecking files in %s:' % rst_path)
            # 调用函数检查文档,并传递相应参数
            check_documentation(rst_path, results, args, dots)
        else:
            # 如果构建的 rst 文件路径不存在,则输出错误信息并记录错误消息
            sys.stderr.write(f'\ninvalid --rst argument "{args.rst}"')
            errormsgs.append('invalid directory argument to --rst')
        # 如果 dots 参数为真,则输出换行到标准错误输出
        if dots:
            sys.stderr.write("\n")
            sys.stderr.flush()

    # 报告检查结果
    for module, mod_results in results:
        # 检查当前模块的所有结果是否成功
        success = all(x[1] for x in mod_results)
        # 如果有任何一个检查不成功,则记录错误消息
        if not success:
            errormsgs.append(f'failed checking {module.__name__}')

        # 如果所有检查成功,并且 verbose 等于 0,则继续下一个模块
        if success and args.verbose == 0:
            continue

        # 输出模块名称
        print("")
        print("=" * len(module.__name__))
        print(module.__name__)
        print("=" * len(module.__name__))
        print("")

        # 遍历当前模块的检查结果
        for name, success, output in mod_results:
            # 如果名称为 None,且检查不成功或 verbose 大于等于 1,则输出相关信息
            if name is None:
                if not success or args.verbose >= 1:
                    print(output.strip())
                    print("")
            # 如果名称不为 None,且检查不成功或 verbose 大于等于 2,则输出相关信息
            elif not success or (args.verbose >= 2 and output.strip()):
                print(name)
                print("-" * len(name))
                print("")
                print(output.strip())
                print("")

    # 如果错误消息列表长度为 0,则输出所有检查通过的消息,并退出程序返回状态 0
    if len(errormsgs) == 0:
        print("\nOK: all checks passed!")
        sys.exit(0)
    # 否则输出错误消息列表,并退出程序返回状态 1
    else:
        print('\nERROR: ', '\n        '.join(errormsgs))
        sys.exit(1)
# 如果当前模块是直接被执行的主程序
if __name__ == '__main__':
    # 调用 main 函数,并传递命令行参数列表作为参数
    main(argv=sys.argv[1:])

.\numpy\tools\swig\test\Array1.cxx

// 引入自定义头文件 Array1.h
#include "Array1.h"
// 引入标准输入输出流库
#include <iostream>
// 引入字符串流库
#include <sstream>

// Array1 类的构造函数,支持默认、长度、数组输入
Array1::Array1(int length, long* data) :
  _ownData(false), _length(0), _buffer(0)
{
  // 调整数组大小并分配内存
  resize(length, data);
}

// Array1 类的拷贝构造函数
Array1::Array1(const Array1 & source) :
  _length(source._length)
{
  // 分配内存并复制源对象的数据
  allocateMemory();
  *this = source;
}

// Array1 类的析构函数,释放内存
Array1::~Array1()
{
  // 释放对象所持有的内存
  deallocateMemory();
}

// Array1 类的赋值运算符重载
Array1 & Array1::operator=(const Array1 & source)
{
  // 比较长度并选择较小的长度进行赋值
  int len = _length < source._length ? _length : source._length;
  for (int i=0;  i < len; ++i)
  {
    (*this)[i] = source[i];
  }
  return *this;
}

// Array1 类的相等比较运算符重载
bool Array1::operator==(const Array1 & other) const
{
  // 比较数组长度及每个元素是否相等
  if (_length != other._length) return false;
  for (int i=0; i < _length; ++i)
  {
    if ((*this)[i] != other[i]) return false;
  }
  return true;
}

// 获取数组长度的访问器
int Array1::length() const
{
  return _length;
}

// 调整数组大小的方法
void Array1::resize(int length, long* data)
{
  // 检查长度是否小于零,抛出异常
  if (length < 0) throw std::invalid_argument("Array1 length less than 0");
  // 若长度与当前长度相同,则直接返回
  if (length == _length) return;
  // 释放当前内存,并重新分配新的内存
  deallocateMemory();
  _length = length;
  // 如果没有指定数据,则分配新内存
  if (!data)
  {
    allocateMemory();
  }
  else
  {
    // 否则使用提供的数据并设置标志
    _ownData = false;
    _buffer  = data;
  }
}

// 设置数组元素的访问器
long & Array1::operator[](int i)
{
  // 检查索引是否越界,抛出异常
  if (i < 0 || i >= _length) throw std::out_of_range("Array1 index out of range");
  return _buffer[i];
}

// 获取数组元素的访问器(常量版本)
const long & Array1::operator[](int i) const
{
  // 检查索引是否越界,抛出异常
  if (i < 0 || i >= _length) throw std::out_of_range("Array1 index out of range");
  return _buffer[i];
}

// 以字符串形式输出数组
std::string Array1::asString() const
{
  // 使用字符串流生成数组的字符串表示
  std::stringstream result;
  result << "[";
  for (int i=0; i < _length; ++i)
  {
    result << " " << _buffer[i];
    if (i < _length-1) result << ",";
  }
  result << " ]";
  return result.str();
}

// 获取数组视图的方法
void Array1::view(long** data, int* length) const
{
  // 返回数组缓冲区及其长度
  *data   = _buffer;
  *length = _length;
}

// Array1 类的私有方法,分配内存
void Array1::allocateMemory()
{
  // 如果长度为零,则设置标志并置空缓冲区
  if (_length == 0)
  {
    _ownData = false;
    _buffer  = 0;
  }
  else
  {
    // 否则分配新内存,并设置标志
    _ownData = true;
    _buffer = new long[_length];
  }
}

// Array1 类的私有方法,释放内存
void Array1::deallocateMemory()
{
  // 如果对象持有数据并且缓冲区不为空,则释放内存
  if (_ownData && _length && _buffer)
  {
    delete [] _buffer;
  }
  // 重置标志及长度,置空缓冲区
  _ownData = false;
  _length  = 0;
  _buffer  = 0;
}

.\numpy\tools\swig\test\Array1.h

#ifndef ARRAY1_H
#define ARRAY1_H

#include <stdexcept>
#include <string>

class Array1
{
public:
  // Default/length/array constructor
  // 默认构造函数,可以指定长度和数据
  Array1(int length = 0, long* data = 0);

  // Copy constructor
  // 拷贝构造函数
  Array1(const Array1 & source);

  // Destructor
  // 析构函数
  ~Array1();

  // Assignment operator
  // 赋值运算符重载
  Array1 & operator=(const Array1 & source);

  // Equals operator
  // 等号运算符重载,用于比较两个对象是否相等
  bool operator==(const Array1 & other) const;

  // Length accessor
  // 获取数组长度
  int length() const;

  // Resize array
  // 调整数组大小
  void resize(int length, long* data = 0);

  // Set item accessor
  // 设置数组元素
  long & operator[](int i);

  // Get item accessor
  // 获取数组元素(常量版本)
  const long & operator[](int i) const;

  // String output
  // 返回数组的字符串表示
  std::string asString() const;

  // Get view
  // 获取数组数据和长度的视图
  void view(long** data, int* length) const;

private:
  // Members
  // 成员变量
  bool _ownData;  // 是否拥有数据
  int _length;    // 数组长度
  long * _buffer; // 数据缓冲区

  // Methods
  // 私有方法
  void allocateMemory();    // 分配内存
  void deallocateMemory();  // 释放内存
};

#endif

.\numpy\tools\swig\test\Array2.cxx

// 包含 Array2 类的声明文件
#include "Array2.h"
// 包含用于字符串流操作的头文件
#include <sstream>

// 默认构造函数
Array2::Array2() :
  _ownData(false), _nrows(0), _ncols(), _buffer(0), _rows(0)
{ }

// 大小和数组构造函数
Array2::Array2(int nrows, int ncols, long* data) :
  _ownData(false), _nrows(0), _ncols(), _buffer(0), _rows(0)
{
  // 调整数组大小,并使用传入的数据进行初始化
  resize(nrows, ncols, data);
}

// 复制构造函数
Array2::Array2(const Array2 & source) :
  _nrows(source._nrows), _ncols(source._ncols)
{
  // 分配内存并将数据复制到当前对象
  _ownData = true;
  allocateMemory();
  *this = source;
}

// 析构函数
Array2::~Array2()
{
  // 释放对象占用的内存
  deallocateMemory();
}

// 赋值运算符重载
Array2 & Array2::operator=(const Array2 & source)
{
  // 按最小行列数复制数据到当前对象
  int nrows = _nrows < source._nrows ? _nrows : source._nrows;
  int ncols = _ncols < source._ncols ? _ncols : source._ncols;
  for (int i=0; i < nrows; ++i)
  {
    for (int j=0; j < ncols; ++j)
    {
      (*this)[i][j] = source[i][j];
    }
  }
  return *this;
}

// 等于运算符重载
bool Array2::operator==(const Array2 & other) const
{
  // 检查是否行数、列数及所有元素相同
  if (_nrows != other._nrows) return false;
  if (_ncols != other._ncols) return false;
  for (int i=0; i < _nrows; ++i)
  {
    for (int j=0; j < _ncols; ++j)
    {
      if ((*this)[i][j] != other[i][j]) return false;
    }
  }
  return true;
}

// 获取行数
int Array2::nrows() const
{
  return _nrows;
}

// 获取列数
int Array2::ncols() const
{
  return _ncols;
}

// 调整数组大小
void Array2::resize(int nrows, int ncols, long* data)
{
  // 检查行数和列数是否合法,如果相同则直接返回,否则重新分配内存
  if (nrows < 0) throw std::invalid_argument("Array2 nrows less than 0");
  if (ncols < 0) throw std::invalid_argument("Array2 ncols less than 0");
  if (nrows == _nrows && ncols == _ncols) return;
  deallocateMemory();
  _nrows = nrows;
  _ncols = ncols;
  if (!data)
  {
    allocateMemory();
  }
  else
  {
    _ownData = false;
    _buffer  = data;
    allocateRows();
  }
}

// 仅调整数组大小(重载版本)
void Array2::resize(int nrows, int ncols)
{
  resize(nrows, ncols, nullptr);
}

// 设置元素访问器
Array1 & Array2::operator[](int i)
{
  // 检查行索引是否在合法范围内,然后返回对应行对象
  if (i < 0 || i >= _nrows) throw std::out_of_range("Array2 row index out of range");
  return _rows[i];
}

// 获取元素访问器
const Array1 & Array2::operator[](int i) const
{
  // 检查行索引是否在合法范围内,然后返回对应行对象(常量版本)
  if (i < 0 || i >= _nrows) throw std::out_of_range("Array2 row index out of range");
  return _rows[i];
}

// 返回数组的字符串表示
std::string Array2::asString() const
{
  std::stringstream result;
  result << "[ ";
  for (int i=0; i < _nrows; ++i)
  {
    if (i > 0) result << "  ";
    result << (*this)[i].asString();
    if (i < _nrows-1) result << "," << std::endl;
  }
  result << " ]" << std::endl;
  return result.str();
}

// 获取视图
void Array2::view(int* nrows, int* ncols, long** data) const
{
  // 返回当前数组的行数、列数及数据指针
  *nrows = _nrows;
  *ncols = _ncols;
  *data  = _buffer;
}

// 私有方法:分配内存
void Array2::allocateMemory()
{
  // 如果行列数为零,则将数据标记为非拥有,并清空缓冲区及行对象
  if (_nrows * _ncols == 0)
  {
    _ownData = false;
    _buffer  = 0;
    _rows    = 0;
  }
  else
  {
    // 否则分配内存,并标记为拥有数据,然后分配每一行的内存
    _ownData = true;
    _buffer = new long[_nrows*_ncols];
    allocateRows();
  }
}

// 私有方法:分配每一行的内存
void Array2::allocateRows()
{
  _rows = new Array1[_nrows];
  for (int i=0; i < _nrows; ++i)
  {
    _rows[i].resize(_ncols, &_buffer[i*_ncols]);



// 调整第 i 行的大小,确保其有 _ncols 列,使用 _buffer 中的数据作为初始内容
_rows[i].resize(_ncols, &_buffer[i*_ncols]);


这行代码的作用是调整 `_rows` 中第 `i` 行的大小,确保它包含 `_ncols` 列,并使用 `_buffer` 中第 `i*_ncols` 列开始的数据作为初始内容。`.resize()` 方法用于调整容器大小,并可以指定初始值。
}

void Array2::deallocateMemory()
{
  // 检查是否需要释放内存:确保_ownData为true,且_nrows和_ncols大于0,_buffer非空
  if (_ownData && _nrows*_ncols && _buffer)
  {
    // 删除_rows数组,释放行指针内存
    delete [] _rows;
    // 删除_buffer数组,释放数据内存
    delete [] _buffer;
  }
  // 重置所有成员变量,表示内存已被释放
  _ownData = false;
  _nrows   = 0;
  _ncols   = 0;
  _buffer  = 0;
  _rows    = 0;
}

.\numpy\tools\swig\test\Array2.h

#ifndef ARRAY2_H
#define ARRAY2_H

#include "Array1.h"              // 包含 Array1 类的头文件
#include <stdexcept>             // 包含异常处理的标准库
#include <string>                // 包含处理字符串的标准库

class Array2
{
public:

  // 默认构造函数
  Array2();

  // 带大小和数组数据的构造函数
  Array2(int nrows, int ncols, long* data=0);

  // 复制构造函数
  Array2(const Array2 & source);

  // 析构函数
  ~Array2();

  // 赋值运算符重载
  Array2 & operator=(const Array2 & source);

  // 等于运算符重载
  bool operator==(const Array2 & other) const;

  // 获取行数和列数
  int nrows() const;
  int ncols() const;

  // 调整数组大小
  void resize(int nrows, int ncols, long* data);
  void resize(int nrows, int ncols);
  
  // 设置元素访问器
  Array1 & operator[](int i);

  // 获取元素访问器
  const Array1 & operator[](int i) const;

  // 输出为字符串
  std::string asString() const;

  // 获取视图
  void view(int* nrows, int* ncols, long** data) const;

private:
  // 成员变量
  bool _ownData;        // 指示是否拥有数据
  int _nrows;           // 行数
  int _ncols;           // 列数
  long * _buffer;       // 缓冲区指针
  Array1 * _rows;       // 行数组指针

  // 私有方法
  void allocateMemory();     // 分配内存的方法
  void allocateRows();       // 分配行的方法
  void deallocateMemory();   // 释放内存的方法
};

#endif

.\numpy\tools\swig\test\ArrayZ.cxx

// 包含自定义头文件 ArrayZ.h 和标准输入输出流头文件
#include "ArrayZ.h"
#include <iostream>
#include <sstream>

// 默认/长度/数组构造函数
ArrayZ::ArrayZ(int length, std::complex<double>* data) :
  _ownData(false), _length(0), _buffer(0)
{
  // 调整数组大小并分配内存
  resize(length, data);
}

// 拷贝构造函数
ArrayZ::ArrayZ(const ArrayZ & source) :
  _length(source._length)
{
  // 分配内存并复制源对象数据
  allocateMemory();
  *this = source;
}

// 析构函数
ArrayZ::~ArrayZ()
{
  // 释放对象的内存
  deallocateMemory();
}

// 赋值运算符重载
ArrayZ & ArrayZ::operator=(const ArrayZ & source)
{
  // 比较并复制长度较小的数据
  int len = _length < source._length ? _length : source._length;
  for (int i=0;  i < len; ++i)
  {
    (*this)[i] = source[i];
  }
  return *this;
}

// 等号运算符重载
bool ArrayZ::operator==(const ArrayZ & other) const
{
  // 比较数组长度及每个元素是否相等
  if (_length != other._length) return false;
  for (int i=0; i < _length; ++i)
  {
    if ((*this)[i] != other[i]) return false;
  }
  return true;
}

// 返回数组长度的访问器
int ArrayZ::length() const
{
  return _length;
}

// 调整数组大小
void ArrayZ::resize(int length, std::complex<double>* data)
{
  // 检查长度是否合法
  if (length < 0) throw std::invalid_argument("ArrayZ length less than 0");
  // 若长度不变则直接返回
  if (length == _length) return;
  // 释放当前内存并重新分配
  deallocateMemory();
  _length = length;
  if (!data)
  {
    // 分配新的内存
    allocateMemory();
  }
  else
  {
    // 使用传入的数据作为数组缓冲区
    _ownData = false;
    _buffer  = data;
  }
}

// 设置元素访问器
std::complex<double> & ArrayZ::operator[](int i)
{
  // 检查索引是否有效
  if (i < 0 || i >= _length) throw std::out_of_range("ArrayZ index out of range");
  return _buffer[i];
}

// 获取元素访问器(常量版本)
const std::complex<double> & ArrayZ::operator[](int i) const
{
  // 检查索引是否有效
  if (i < 0 || i >= _length) throw std::out_of_range("ArrayZ index out of range");
  return _buffer[i];
}

// 生成数组的字符串表示
std::string ArrayZ::asString() const
{
  std::stringstream result;
  result << "[";
  for (int i=0; i < _length; ++i)
  {
    result << " " << _buffer[i];
    if (i < _length-1) result << ",";
  }
  result << " ]";
  return result.str();
}

// 获取数组视图
void ArrayZ::view(std::complex<double>** data, int* length) const
{
  // 返回数组缓冲区及其长度
  *data   = _buffer;
  *length = _length;
}

// 私有方法:分配内存
void ArrayZ::allocateMemory()
{
  if (_length == 0)
  {
    _ownData = false;
    _buffer  = 0;
  }
  else
  {
    // 分配新的数组内存
    _ownData = true;
    _buffer = new std::complex<double>[_length];
  }
}

// 私有方法:释放内存
void ArrayZ::deallocateMemory()
{
  // 如果拥有数据并且数组长度大于0,则释放内存
  if (_ownData && _length && _buffer)
  {
    delete [] _buffer;
  }
  _ownData = false;
  _length  = 0;
  _buffer  = 0;
}

.\numpy\tools\swig\test\ArrayZ.h

#ifndef ARRAYZ_H
#define ARRAYZ_H

#include <stdexcept>
#include <string>
#include <complex>

class ArrayZ
{
public:

  // 默认/长度/数组构造函数
  ArrayZ(int length = 0, std::complex<double>* data = 0);

  // 复制构造函数
  ArrayZ(const ArrayZ & source);

  // 析构函数
  ~ArrayZ();

  // 赋值运算符重载
  ArrayZ & operator=(const ArrayZ & source);

  // 等于运算符重载
  bool operator==(const ArrayZ & other) const;

  // 长度访问器
  int length() const;

  // 调整数组大小
  void resize(int length, std::complex<double>* data = 0);

  // 设置元素访问器
  std::complex<double> & operator[](int i);

  // 获取元素访问器
  const std::complex<double> & operator[](int i) const;

  // 字符串输出
  std::string asString() const;

  // 获取视图
  void view(std::complex<double>** data, int* length) const;

private:
  // 成员变量
  bool _ownData;                         // 是否拥有数据的标志
  int _length;                           // 数组长度
  std::complex<double> * _buffer;        // 数据缓冲区指针

  // 私有方法
  void allocateMemory();                 // 分配内存方法
  void deallocateMemory();               // 释放内存方法
};

#endif

.\numpy\tools\swig\test\Farray.cxx

// 包含自定义头文件 "Farray.h" 和标准头文件 <sstream>
#include "Farray.h"
#include <sstream>

// Farray 类的 Size 构造函数
Farray::Farray(int nrows, int ncols) :
  _nrows(nrows), _ncols(ncols), _buffer(0)
{
  // 分配内存空间
  allocateMemory();
}

// Farray 类的复制构造函数
Farray::Farray(const Farray & source) :
  _nrows(source._nrows), _ncols(source._ncols)
{
  // 分配内存空间
  allocateMemory();
  // 使用赋值运算符进行复制
  *this = source;
}

// Farray 类的析构函数
Farray::~Farray()
{
  // 释放动态分配的内存
  delete [] _buffer;
}

// Farray 类的赋值运算符重载
Farray & Farray::operator=(const Farray & source)
{
  // 确定有效的行和列数
  int nrows = _nrows < source._nrows ? _nrows : source._nrows;
  int ncols = _ncols < source._ncols ? _ncols : source._ncols;
  // 逐个元素进行赋值
  for (int i=0; i < nrows; ++i)
  {
    for (int j=0; j < ncols; ++j)
    {
      (*this)(i,j) = source(i,j);
    }
  }
  return *this;
}

// Farray 类的相等运算符重载
bool Farray::operator==(const Farray & other) const
{
  // 检查行数和列数是否相等
  if (_nrows != other._nrows) return false;
  if (_ncols != other._ncols) return false;
  // 逐个元素比较
  for (int i=0; i < _nrows; ++i)
  {
    for (int j=0; j < _ncols; ++j)
    {
      if ((*this)(i,j) != other(i,j)) return false;
    }
  }
  return true;
}

// 获取行数的访问器
int Farray::nrows() const
{
  return _nrows;
}

// 获取列数的访问器
int Farray::ncols() const
{
  return _ncols;
}

// 设置元素的访问器
long & Farray::operator()(int i, int j)
{
  // 检查行和列的索引是否有效
  if (i < 0 || i >= _nrows) throw std::out_of_range("Farray row index out of range");
  if (j < 0 || j >= _ncols) throw std::out_of_range("Farray col index out of range");
  return _buffer[offset(i,j)];
}

// 获取元素的访问器
const long & Farray::operator()(int i, int j) const
{
  // 检查行和列的索引是否有效
  if (i < 0 || i >= _nrows) throw std::out_of_range("Farray row index out of range");
  if (j < 0 || j >= _ncols) throw std::out_of_range("Farray col index out of range");
  return _buffer[offset(i,j)];
}

// 以字符串形式输出 Farray 对象
std::string Farray::asString() const
{
  std::stringstream result;
  result << "[ ";
  // 遍历数组元素并格式化输出
  for (int i=0; i < _nrows; ++i)
  {
    if (i > 0) result << "  ";
    result << "[";
    for (int j=0; j < _ncols; ++j)
    {
      result << " " << (*this)(i,j);
      if (j < _ncols-1) result << ",";
    }
    result << " ]";
    if (i < _nrows-1) result << "," << std::endl;
  }
  result << " ]" << std::endl;
  return result.str();
}

// 获取视图的访问器
void Farray::view(int* nrows, int* ncols, long** data) const
{
  // 返回行数、列数和数据指针的引用
  *nrows = _nrows;
  *ncols = _ncols;
  *data  = _buffer;
}

// 私有方法:分配内存空间
void Farray::allocateMemory()
{
  // 检查行数和列数是否有效
  if (_nrows <= 0) throw std::invalid_argument("Farray nrows <= 0");
  if (_ncols <= 0) throw std::invalid_argument("Farray ncols <= 0");
  // 分配内存空间
  _buffer = new long[_nrows*_ncols];
}

// 内联方法:计算元素在数组中的偏移量
inline int Farray::offset(int i, int j) const
{
  return i + j * _nrows;
}

.\numpy\tools\swig\test\Farray.h

#ifndef FARRAY_H
#define FARRAY_H

#include <stdexcept>
#include <string>

class Farray
{
public:
  // Size constructor
  Farray(int nrows, int ncols);

  // Copy constructor
  Farray(const Farray & source);

  // Destructor
  ~Farray();

  // Assignment operator
  Farray & operator=(const Farray & source);

  // Equals operator
  bool operator==(const Farray & other) const;

  // Length accessors
  int nrows() const;
  int ncols() const;

  // Set item accessor
  long & operator()(int i, int j);

  // Get item accessor
  const long & operator()(int i, int j) const;

  // String output
  std::string asString() const;

  // Get view
  void view(int* nrows, int* ncols, long** data) const;

private:
  // Members
  int _nrows;       // 行数
  int _ncols;       // 列数
  long * _buffer;   // 数据存储缓冲区

  // Default constructor: not implemented
  Farray();

  // Methods
  void allocateMemory();   // 分配内存方法
  int  offset(int i, int j) const;   // 计算索引偏移量的方法
};

#endif


这些注释提供了每个类成员和方法的简要解释,帮助理解其用途和功能。

.\numpy\tools\swig\test\Flat.cxx

#include <stdlib.h>
#include <math.h>
#include <iostream>
#include "Flat.h"

// 宏定义 TEST_FUNCS(TYPE, SNAME) 定义了一组函数,用于处理给定类型 TYPE 的 1D 数组,
// 函数名称采用 SNAMEProcess(TYPE * array, int size) 的格式。
// 这些函数用于测试 numpy 接口,处理:
// 
//  * 具有固定元素数量的多维度原地数组
//
#define TEST_FUNCS(TYPE, SNAME) \
\
// 实现处理函数 SNAME ## Process,用于处理 TYPE 类型的数组,大小为 size
void SNAME ## Process(TYPE * array, int size) {          \
  // 遍历数组,对每个元素执行加一操作
  for (int i=0; i<size; ++i) array[i] += 1;              \
}

// 以下展开宏 TEST_FUNCS(TYPE, SNAME) 以生成具体的处理函数

// 生成 signed char 类型的处理函数 scharProcess
TEST_FUNCS(signed char       , schar    )
// 生成 unsigned char 类型的处理函数 ucharProcess
TEST_FUNCS(unsigned char     , uchar    )
// 生成 short 类型的处理函数 shortProcess
TEST_FUNCS(short             , short    )
// 生成 unsigned short 类型的处理函数 ushortProcess
TEST_FUNCS(unsigned short    , ushort   )
// 生成 int 类型的处理函数 intProcess
TEST_FUNCS(int               , int      )
// 生成 unsigned int 类型的处理函数 uintProcess
TEST_FUNCS(unsigned int      , uint     )
// 生成 long 类型的处理函数 longProcess
TEST_FUNCS(long              , long     )
// 生成 unsigned long 类型的处理函数 ulongProcess
TEST_FUNCS(unsigned long     , ulong    )
// 生成 long long 类型的处理函数 longLongProcess
TEST_FUNCS(long long         , longLong )
// 生成 unsigned long long 类型的处理函数 ulongLongProcess
TEST_FUNCS(unsigned long long, ulongLong)
// 生成 float 类型的处理函数 floatProcess
TEST_FUNCS(float             , float    )
// 生成 double 类型的处理函数 doubleProcess
TEST_FUNCS(double            , double   )

.\numpy\tools\swig\test\Flat.h

#ifndef FLAT_H
#define FLAT_H

// 宏定义开始:定义了一组函数原型,用于处理不同类型数组
//
//     void SNAMEProcess(TYPE * array,  int size);
//
// 对于任意指定的类型 TYPE(例如:signed char, unsigned int, long long 等)和简短名称 SNAME
// (例如:schar, uint, longLong 等)。该宏会根据给定的 TYPE/SNAME 组合进行扩展,生成用于测试
// numpy 接口的函数。
//
#define TEST_FUNC_PROTOS(TYPE, SNAME) \
\
void SNAME ## Process(TYPE * array, int size); \

// 以下是针对不同类型和简短名称的宏扩展示例:
// 扩展为 signed char 对应的处理函数原型
TEST_FUNC_PROTOS(signed char       , schar    )
// 扩展为 unsigned char 对应的处理函数原型
TEST_FUNC_PROTOS(unsigned char     , uchar    )
// 扩展为 short 对应的处理函数原型
TEST_FUNC_PROTOS(short             , short    )
// 扩展为 unsigned short 对应的处理函数原型
TEST_FUNC_PROTOS(unsigned short    , ushort   )
// 扩展为 int 对应的处理函数原型
TEST_FUNC_PROTOS(int               , int      )
// 扩展为 unsigned int 对应的处理函数原型
TEST_FUNC_PROTOS(unsigned int      , uint     )
// 扩展为 long 对应的处理函数原型
TEST_FUNC_PROTOS(long              , long     )
// 扩展为 unsigned long 对应的处理函数原型
TEST_FUNC_PROTOS(unsigned long     , ulong    )
// 扩展为 long long 对应的处理函数原型
TEST_FUNC_PROTOS(long long         , longLong )
// 扩展为 unsigned long long 对应的处理函数原型
TEST_FUNC_PROTOS(unsigned long long, ulongLong)
// 扩展为 float 对应的处理函数原型
TEST_FUNC_PROTOS(float             , float    )
// 扩展为 double 对应的处理函数原型
TEST_FUNC_PROTOS(double            , double   )

#endif

.\numpy\tools\swig\test\Fortran.cxx

#include <stdlib.h>
#include <math.h>
#include <iostream>
#include "Fortran.h"

// 宏定义:TEST_FUNCS(TYPE, SNAME)
// 生成一个函数,用于获取矩阵中第二个元素的值,根据传入的类型和名称生成函数
#define TEST_FUNCS(TYPE, SNAME) \
\
// 函数定义:TYPE SNAME ## SecondElement(TYPE * matrix, int rows, int cols)
// 返回矩阵中第二个元素的值,根据传入的类型和名称生成函数
TYPE SNAME ## SecondElement(TYPE * matrix, int rows, int cols) {      \
  // 从矩阵中取出第二个元素的值
  TYPE result = matrix[1];                                \
  // 返回获取到的值
  return result;                                          \
}                                                         \

// 生成各种类型的 SecondElement 函数
TEST_FUNCS(signed char       , schar    )
TEST_FUNCS(unsigned char     , uchar    )
TEST_FUNCS(short             , short    )
TEST_FUNCS(unsigned short    , ushort   )
TEST_FUNCS(int               , int      )
TEST_FUNCS(unsigned int      , uint     )
TEST_FUNCS(long              , long     )
TEST_FUNCS(unsigned long     , ulong    )
TEST_FUNCS(long long         , longLong )
TEST_FUNCS(unsigned long long, ulongLong)
TEST_FUNCS(float             , float    )
TEST_FUNCS(double            , double   )


这段代码定义了一个宏 `TEST_FUNCS`,用于生成多个函数,这些函数根据不同的数据类型和名称,从传入的数组中获取第二个元素的值,并返回。

.\numpy\tools\swig\test\Fortran.h

#ifndef FORTRAN_H
#define FORTRAN_H

// 定义一个宏 TEST_FUNC_PROTOS,用于生成函数原型声明
#define TEST_FUNC_PROTOS(TYPE, SNAME) \
\
// 声明一个函数原型,函数名由 SNAME 和 "SecondElement" 组成,参数为指向 TYPE 类型数组的指针、行数和列数
TYPE SNAME ## SecondElement(TYPE * matrix, int rows, int cols); \

// 使用宏 TEST_FUNC_PROTOS 分别生成以下类型的函数原型声明
// 以下各行对应的注释格式相同,仅注明了宏的作用和生成的函数原型的具体参数
TEST_FUNC_PROTOS(signed char       , schar    )
TEST_FUNC_PROTOS(unsigned char     , uchar    )
TEST_FUNC_PROTOS(short             , short    )
TEST_FUNC_PROTOS(unsigned short    , ushort   )
TEST_FUNC_PROTOS(int               , int      )
TEST_FUNC_PROTOS(unsigned int      , uint     )
TEST_FUNC_PROTOS(long              , long     )
TEST_FUNC_PROTOS(unsigned long     , ulong    )
TEST_FUNC_PROTOS(long long         , longLong )
TEST_FUNC_PROTOS(unsigned long long, ulongLong)
TEST_FUNC_PROTOS(float             , float    )
TEST_FUNC_PROTOS(double            , double   )

#endif

.\numpy\tools\swig\test\Matrix.cxx

// 引入标准库头文件
#include <stdlib.h>
#include <math.h>
// 引入输入输出流库头文件
#include <iostream>
// 引入自定义矩阵头文件
#include "Matrix.h"

// 下面的宏定义了一组针对 2D 数组的函数,这些函数接受特定类型 TYPE 的参数,并使用简称 SNAME
// 宏展开如下,对于给定的类型 TYPE 和简称 SNAME:
//
//     TYPE SNAME ## Det(TYPE matrix[2][2]) { ... }
//     TYPE SNAME ## Max(TYPE * matrix, int rows, int cols) { ... }
//     TYPE SNAME ## Min(int rows, int cols, TYPE * matrix) { ... }
//     void SNAME ## Scale(TYPE array[3][3], TYPE val) { ... }
//
// 这些函数分别用于处理:
//  * 2x2 矩阵的行列式计算
//  * 任意行列数的二维数组中的最大值计算
//  * 任意行列数的二维数组中的最小值计算
//  * 3x3 矩阵的每个元素与给定值的乘积计算
#define TEST_FUNCS(TYPE, SNAME) \
\
TYPE SNAME ## Det(TYPE matrix[2][2]) {                          \
  return matrix[0][0]*matrix[1][1] - matrix[0][1]*matrix[1][0]; \
}                                                               \
\
TYPE SNAME ## Max(TYPE * matrix, int rows, int cols) {      \
  int i, j, index;                                        \
  TYPE result = matrix[0];                                \
  // 遍历二维数组找出最大值
  for (j=0; j<cols; ++j) {                                \
    for (i=0; i<rows; ++i) {                              \
      index = j*rows + i;                                 \
      if (matrix[index] > result) result = matrix[index]; \
    }                                                     \
  }                                                       \
  return result;                                          \
}                                                         \
\
TYPE SNAME ## Min(int rows, int cols, TYPE * matrix) {    \
  int i, j, index;                                        \
  TYPE result = matrix[0];                                \
  // 遍历二维数组找出最小值
  for (j=0; j<cols; ++j) {                                \
    for (i=0; i<rows; ++i) {                              \
      index = j*rows + i;                                 \
      if (matrix[index] < result) result = matrix[index]; \
    }                                                     \
  }                                                       \
  return result;                                          \
}                                                         \
\
void SNAME ## Scale(TYPE array[3][3], TYPE val) { \
  // 遍历 3x3 矩阵,每个元素乘以给定的值
  for (int i=0; i<3; ++i)                         \
    for (int j=0; j<3; ++j)                       \
      array[i][j] *= val;                         \
}                                                 \
\
# 定义函数 SNAME ## Floor,用于将数组中小于给定值 floor 的元素替换为 floor
void SNAME ## Floor(TYPE * array, int rows, int cols, TYPE floor) { \
  # 定义变量 i、j 和数组索引 index,循环遍历数组
  int i, j, index;                                                  \
  # 遍历列
  for (j=0; j<cols; ++j) {                                          \
    # 遍历行
    for (i=0; i<rows; ++i) {                                        \
      # 计算当前元素在一维数组中的索引
      index = j*rows + i;                                           \
      # 如果数组中的元素小于 floor,则将其赋值为 floor
      if (array[index] < floor) array[index] = floor;               \
    }                                                               \
  }                                                                 \
}                                                                   \
\
# 定义函数 SNAME ## Ceil,用于将数组中大于给定值 ceil 的元素替换为 ceil
void SNAME ## Ceil(int rows, int cols, TYPE * array, TYPE ceil) { \
  # 定义变量 i、j 和数组索引 index,循环遍历数组
  int i, j, index;                                                \
  # 遍历列
  for (j=0; j<cols; ++j) {                                        \
    # 遍历行
    for (i=0; i<rows; ++i) {                                      \
      # 计算当前元素在一维数组中的索引
      index = j*rows + i;                                         \
      # 如果数组中的元素大于 ceil,则将其赋值为 ceil
      if (array[index] > ceil) array[index] = ceil;               \
    }                                                             \
  }                                                               \
}                                  \
\
# 定义函数 SNAME ## LUSplit,将3x3矩阵分解为下三角矩阵和上三角矩阵
void SNAME ## LUSplit(TYPE matrix[3][3], TYPE lower[3][3], TYPE upper[3][3]) { \
  # 循环遍历矩阵的行和列
  for (int i=0; i<3; ++i) {                               \
    for (int j=0; j<3; ++j) {                               \
      # 如果行数大于等于列数,则当前位置属于下三角矩阵
      if (i >= j) {                                    \
    # 将原始矩阵中的元素复制到下三角矩阵中
    lower[i][j] = matrix[i][j];                           \
    # 上三角矩阵对应位置置零
    upper[i][j] = 0;                                \
      } else {                                        \
    # 下三角矩阵对应位置置零
    lower[i][j] = 0;                                \
    # 将原始矩阵中的元素复制到上三角矩阵中
    upper[i][j] = matrix[i][j];                           \
      }                                            \
    }                                            \
  }                                            \
}

# 定义测试宏 TEST_FUNCS,用于生成一系列函数的测试函数
TEST_FUNCS(signed char       , schar    )
TEST_FUNCS(unsigned char     , uchar    )
TEST_FUNCS(short             , short    )
TEST_FUNCS(unsigned short    , ushort   )
TEST_FUNCS(int               , int      )
TEST_FUNCS(unsigned int      , uint     )
TEST_FUNCS(long              , long     )
TEST_FUNCS(unsigned long     , ulong    )
TEST_FUNCS(long long         , longLong )
TEST_FUNCS(unsigned long long, ulongLong)
TEST_FUNCS(float             , float    )
TEST_FUNCS(double            , double   )

.\numpy\tools\swig\test\Matrix.h

// 如果未定义过 MATRIX_H,则开始定义 MATRIX_H
#ifndef MATRIX_H
#define MATRIX_H

// 以下宏定义了一组函数原型,这些函数用于处理二维数组,其形式为
//
//     TYPE SNAMEDet(    TYPE matrix[2][2]);
//     TYPE SNAMEMax(    TYPE * matrix, int rows, int cols);
//     TYPE SNAMEMin(    int rows, int cols, TYPE * matrix);
//     void SNAMEScale(  TYPE array[3][3], TYPE val);
//     void SNAMEFloor(  TYPE * array, int rows, int cols, TYPE floor);
//     void SNAMECeil(   int rows, int cols, TYPE * array, TYPE ceil );
//     void SNAMELUSplit(TYPE matrix[3][3], TYPE lower[3][3], TYPE upper[3][3]);
//
// 对于任何指定的类型 TYPE(例如:short、unsigned int、long long 等),以及给定的短名称 SNAME(例如:schar、uint、longLong 等)。
// 然后,根据给定的 TYPE/SNAME 对展开宏。生成的函数用于测试 numpy 接口,分别用于:
//
//  * 二维输入数组,硬编码长度
//  * 二维输入数组
//  * 二维输入数组,数据在最后
//  * 二维原位数组,硬编码长度
//  * 二维原位数组
//  * 二维原位数组,数据在最后
//  * 二维输出参数数组,硬编码长度
//
#define TEST_FUNC_PROTOS(TYPE, SNAME) \
\
TYPE SNAME ## Det(    TYPE matrix[2][2]); \
TYPE SNAME ## Max(    TYPE * matrix, int rows, int cols); \
TYPE SNAME ## Min(    int rows, int cols, TYPE * matrix); \
void SNAME ## Scale(  TYPE array[3][3], TYPE val); \
void SNAME ## Floor(  TYPE * array, int rows, int cols, TYPE floor); \
void SNAME ## Ceil(   int rows, int cols, TYPE * array, TYPE ceil ); \
void SNAME ## LUSplit(TYPE matrix[3][3], TYPE lower[3][3], TYPE upper[3][3]);

// 使用宏 TEST_FUNC_PROTOS 展开各种类型和对应的短名称,定义相关的函数原型
TEST_FUNC_PROTOS(signed char       , schar    )
TEST_FUNC_PROTOS(unsigned char     , uchar    )
TEST_FUNC_PROTOS(short             , short    )
TEST_FUNC_PROTOS(unsigned short    , ushort   )
TEST_FUNC_PROTOS(int               , int      )
TEST_FUNC_PROTOS(unsigned int      , uint     )
TEST_FUNC_PROTOS(long              , long     )
TEST_FUNC_PROTOS(unsigned long     , ulong    )
TEST_FUNC_PROTOS(long long         , longLong )
TEST_FUNC_PROTOS(unsigned long long, ulongLong)
TEST_FUNC_PROTOS(float             , float    )
TEST_FUNC_PROTOS(double            , double   )

// 结束宏定义部分
#endif

.\numpy\tools\swig\test\setup.py

#!/usr/bin/env python3
# System imports
# 导入 Extension 和 setup 函数
from distutils.core import Extension, setup

# Third-party modules - we depend on numpy for everything
# 导入 numpy 库
import numpy

# Obtain the numpy include directory.
# 获取 numpy 的头文件目录
numpy_include = numpy.get_include()

# Array extension module
# 定义名为 _Array 的扩展模块,包含多个源文件
_Array = Extension("_Array",
                   ["Array_wrap.cxx",
                    "Array1.cxx",
                    "Array2.cxx",
                    "ArrayZ.cxx"],
                   include_dirs=[numpy_include],
                   )

# Farray extension module
# 定义名为 _Farray 的扩展模块,包含多个源文件
_Farray = Extension("_Farray",
                    ["Farray_wrap.cxx",
                     "Farray.cxx"],
                    include_dirs=[numpy_include],
                    )

# _Vector extension module
# 定义名为 _Vector 的扩展模块,包含多个源文件
_Vector = Extension("_Vector",
                    ["Vector_wrap.cxx",
                     "Vector.cxx"],
                    include_dirs=[numpy_include],
                    )

# _Matrix extension module
# 定义名为 _Matrix 的扩展模块,包含多个源文件
_Matrix = Extension("_Matrix",
                    ["Matrix_wrap.cxx",
                     "Matrix.cxx"],
                    include_dirs=[numpy_include],
                    )

# _Tensor extension module
# 定义名为 _Tensor 的扩展模块,包含多个源文件
_Tensor = Extension("_Tensor",
                    ["Tensor_wrap.cxx",
                     "Tensor.cxx"],
                    include_dirs=[numpy_include],
                    )

# _Fortran extension module
# 定义名为 _Fortran 的扩展模块,包含多个源文件
_Fortran = Extension("_Fortran",
                    ["Fortran_wrap.cxx",
                     "Fortran.cxx"],
                    include_dirs=[numpy_include],
                    )

# _Flat extension module
# 定义名为 _Flat 的扩展模块,包含多个源文件
_Flat = Extension("_Flat",
                    ["Flat_wrap.cxx",
                     "Flat.cxx"],
                    include_dirs=[numpy_include],
                    )

# NumyTypemapTests setup
# 设置 NumyTypemapTests 包的信息和模块
setup(name="NumpyTypemapTests",
      description="Functions that work on arrays",
      author="Bill Spotz",
      py_modules=["Array", "Farray", "Vector", "Matrix", "Tensor",
                  "Fortran", "Flat"],
      ext_modules=[_Array, _Farray, _Vector, _Matrix, _Tensor,
                   _Fortran, _Flat]
      )

.\numpy\tools\swig\test\SuperTensor.cxx

// 包含标准库头文件
#include <stdlib.h>
#include <math.h>
// 包含输入输出流库的头文件
#include <iostream>
// 包含自定义头文件 "SuperTensor.h"
#include "SuperTensor.h"

// 宏定义,生成一组函数,用于处理特定类型的四维数组
#define TEST_FUNCS(TYPE, SNAME) \
\
// 计算四维数组的范数,返回结果
TYPE SNAME ## Norm(TYPE supertensor[2][2][2][2]) {
  // 定义并初始化结果变量
  double result = 0;
  // 循环遍历四维数组的所有元素
  for (int l=0; l<2; ++l)
    for (int k=0; k<2; ++k)
      for (int j=0; j<2; ++j)
        for (int i=0; i<2; ++i)
          // 计算每个元素的平方,并累加到结果中
          result += supertensor[l][k][j][i] * supertensor[l][k][j][i];
  // 对结果进行平方根运算并返回,转换为指定类型
  return (TYPE)sqrt(result/16);
}

// 查找四维数组中的最大值,并返回
TYPE SNAME ## Max(TYPE * supertensor, int cubes, int slices, int rows, int cols) {
  // 定义变量保存结果,初始为数组第一个元素
  TYPE result = supertensor[0];
  // 多层循环遍历四维数组的每个元素
  for (int l=0; l<cubes; ++l) {
    for (int k=0; k<slices; ++k) {
      for (int j=0; j<rows; ++j) {
        for (int i=0; i<cols; ++i) {
          // 计算当前元素在一维数组中的索引
          int index = l*slices*rows*cols + k*rows*cols + j*cols + i;
          // 如果当前元素大于保存的最大值,则更新最大值
          if (supertensor[index] > result) result = supertensor[index];
        }
      }
    }
  }
  // 返回找到的最大值
  return result;
}

// 查找四维数组中的最小值,并返回
TYPE SNAME ## Min(int cubes, int slices, int rows, int cols, TYPE * supertensor) {
  // 定义变量保存结果,初始为数组第一个元素
  TYPE result = supertensor[0];
  // 多层循环遍历四维数组的每个元素
  for (int l=0; l<cubes; ++l) {
    for (int k=0; k<slices; ++k) {
      for (int j=0; j<rows; ++j) {
        for (int i=0; i<cols; ++i) {
          // 计算当前元素在一维数组中的索引
          int index = l*slices*rows*cols + k*rows*cols + j*cols + i;
          // 如果当前元素小于保存的最小值,则更新最小值
          if (supertensor[index] < result) result = supertensor[index];
        }
      }
    }
  }
  // 返回找到的最小值
  return result;
}

// 将四维数组中的每个元素乘以自身
void SNAME ## Scale(TYPE supertensor[3][3][3][3]) {
  // 多层循环遍历四维数组的每个元素
  for (int l=0; l<3; ++l)
    for (int k=0; k<3; ++k)
      for (int j=0; j<3; ++j)
        for (int i=0; i<3; ++i)
          // 将当前元素乘以自身
          supertensor[l][k][j][i] *= supertensor[l][k][j][i];
}

// 将四维数组中的每个元素向下取整到指定值
void SNAME ## Floor(TYPE * array, int cubes, int slices, int rows, int cols, TYPE floor) {
  // 多层循环遍历四维数组的每个元素
  for (int l=0; l<cubes; ++l) {
    for (int k=0; k<slices; ++k) {
      for (int j=0; j<rows; ++j) {
        for (int i=0; i<cols; ++i) {
          // 计算当前元素在一维数组中的索引
          int index = l*slices*rows*cols + k*rows*cols + j*cols + i;
          // 如果当前元素小于指定的 floor 值,则将其设为 floor
          if (array[index] < floor) array[index] = floor;
        }
      }
    }
  }
}

// 将四维数组中的每个元素向上取整到指定值
void SNAME ## Ceil(int slices, int cubes, int slices, int rows, int cols, TYPE * array, TYPE ceil) {
  // 多层循环遍历四维数组的每个元素
  for (int l=0; l<cubes; ++l) {
    for (int k=0; k<slices; ++k) {
      for (int j=0; j<rows; ++j) {
        for (int i=0; i<cols; ++i) {
          // 计算当前元素在一维数组中的索引
          int index = l*slices*rows*cols + k*rows*cols + j*cols + i;
          // 如果当前元素大于指定的 ceil 值,则将其设为 ceil
          if (array[index] > ceil) array[index] = ceil;
        }
      }
    }
  }
}

// 将输入的四维数组分解为下三角矩阵和上三角矩阵
void SNAME ## LUSplit(TYPE in[2][2][2][2], TYPE lower[2][2][2][2], TYPE upper[2][2][2][2]) {
  // 多层循环遍历四维数组的每个元素
  for (int l=0; l<2; ++l)
    for (int k=0; k<2; ++k)
      for (int j=0; j<2; ++j)
        for (int i=0; i<2; ++i) {
          // 将输入数组的元素分别存入下三角和上三角矩阵
          if (j > i)
            lower[l][k][j][i] = 0;
          else if (j < i)
            upper[l][k][j][i] = 0;
          else {
            lower[l][k][j][i] = in[l][k][j][i];
            upper[l][k][j][i] = in[l][k][j][i];
          }
        }
}
    for (k=0; k<slices; ++k) {                    \
      // 遍历超级张量的第一维(切片维度)
      for (j=0; j<rows; ++j) {                    \
        // 在当前切片中,遍历第二维(行维度)
        for (i=0; i<cols; ++i) {                    \
          // 在当前行中,遍历第三维(列维度)
          // 计算超级张量中元素的索引
          index = l*slices*rows*cols + k*rows*cols + j*cols + i;            \
          // 检查超级张量中的当前元素是否小于结果值,更新结果值
          if (supertensor[index] < result) result = supertensor[index];    \
        }                       \
      }                                \
    }                                \
  }                                \
  // 返回最终的结果值
  return result;                        \
}                                \
\
void SNAME ## Scale(TYPE array[3][3][3][3], TYPE val) { \
  // 多维数组的缩放函数,将每个元素乘以指定的值
  for (int l=0; l<3; ++l)                 \
    for (int k=0; k<3; ++k)                 \
      for (int j=0; j<3; ++j)                 \
        for (int i=0; i<3; ++i)                 \
          array[l][k][j][i] *= val;                 \
}                             \
\
void SNAME ## Floor(TYPE * array, int cubes, int slices, int rows, int cols, TYPE floor) { \
  // 对一维数组中的元素执行下界截断操作,将小于给定下界的元素设为下界值
  int i, j, k, l, index;                                 \
  for (l=0; l<cubes; ++l) {                 \
    for (k=0; k<slices; ++k) {                             \
      for (j=0; j<rows; ++j) {                             \
        for (i=0; i<cols; ++i) {                             \
          index = l*slices*rows*cols + k*rows*cols + j*cols + i;            \
          if (array[index] < floor) array[index] = floor;                 \
        }                                         \
      }                                         \
    }                                         \
  }                                         \
}                                         \
\
void SNAME ## Ceil(int cubes, int slices, int rows, int cols, TYPE * array, TYPE ceil) { \
  // 对一维数组中的元素执行上界截断操作,将大于给定上界的元素设为上界值
  int i, j, k, l, index;                               \
  for (l=0; l<cubes; ++l) {                           \
    for (k=0; k<slices; ++k) {                           \
      for (j=0; j<rows; ++j) {                           \
        for (i=0; i<cols; ++i) {                           \
          index = l*slices*rows*cols + k*rows*cols + j*cols + i;            \
          if (array[index] > ceil) array[index] = ceil;               \
        }                                   \
      }                                       \
    }                                       \
  }                                       \
}                                       \
\
void SNAME ## LUSplit(TYPE supertensor[2][2][2][2], TYPE lower[2][2][2][2], \
              TYPE upper[2][2][2][2]) {             \
  // 将四维超张量分解为下三角矩阵和上三角矩阵
  int sum;                             \
  for (int l=0; l<2; ++l) {                     \
    for (int k=0; k<2; ++k) {                     \
      for (int j=0; j<2; ++j) {                     \
        for (int i=0; i<2; ++i) {                     \
          sum = i + j + k + l;                     \
          if (sum < 2) {                         \
            lower[l][k][j][i] = supertensor[l][k][j][i];             \
            upper[l][k][j][i] = 0;                     \
          } else {                         \
            upper[l][k][j][i] = supertensor[l][k][j][i];             \
            lower[l][k][j][i] = 0;                     \
          }                             \
        }                           \
      }                                 \
    }                                 \
  }                                 \
}

TEST_FUNCS(signed char       , schar    )
TEST_FUNCS(unsigned char     , uchar    )
# 定义一个宏 TEST_FUNCS,用于生成测试函数和它们的类型名
TEST_FUNCS(short             , short    )
# 生成测试函数及其类型名,类型名为 short
TEST_FUNCS(unsigned short    , ushort   )
# 生成测试函数及其类型名,类型名为 unsigned short
TEST_FUNCS(int               , int      )
# 生成测试函数及其类型名,类型名为 int
TEST_FUNCS(unsigned int      , uint     )
# 生成测试函数及其类型名,类型名为 unsigned int
TEST_FUNCS(long              , long     )
# 生成测试函数及其类型名,类型名为 long
TEST_FUNCS(unsigned long     , ulong    )
# 生成测试函数及其类型名,类型名为 unsigned long
TEST_FUNCS(long long         , longLong )
# 生成测试函数及其类型名,类型名为 long long
TEST_FUNCS(unsigned long long, ulongLong)
# 生成测试函数及其类型名,类型名为 unsigned long long
TEST_FUNCS(float             , float    )
# 生成测试函数及其类型名,类型名为 float
TEST_FUNCS(double            , double   )
# 生成测试函数及其类型名,类型名为 double

.\numpy\tools\swig\test\SuperTensor.h

// 定义条件编译宏,防止头文件重复包含
#ifndef SUPERTENSOR_H
#define SUPERTENSOR_H

// 宏定义说明:
// 下面的宏定义了一系列函数原型,用于处理4维数组,形式如下:
//
//     TYPE SNAMENorm(TYPE supertensor[2][2][2][2]);
//     TYPE SNAMEMax(TYPE *supertensor, int cubes, int slices, int rows, int cols);
//     TYPE SNAMEMin(int cubes, int slices, int rows, int cols, TYPE *supertensor);
//     void SNAMEScale(TYPE array[3][3][3][3], TYPE val);
//     void SNAMEFloor(TYPE *array, int cubes, int slices, int rows, int cols, TYPE floor);
//     void SNAMECeil(int cubes, int slices, int rows, int cols, TYPE *array, TYPE ceil);
//     void SNAMELUSplit(TYPE in[3][3][3][3], TYPE lower[3][3][3][3], TYPE upper[3][3][3][3]);
//
// 其中TYPE可以是任意指定的类型(如:short, unsigned int, long long等),SNAME为类型的简称(如:short, uint, longLong等)。
// 这些宏根据给定的TYPE/SNAME对扩展为特定的函数原型。这些函数用于测试numpy接口,分别用于:
//
//  * 处理4维输入数组,长度硬编码
//  * 处理4维输入数组
//  * 处理4维输入数组,数据最后
//  * 处理4维原地数组,长度硬编码
//  * 处理4维原地数组
//  * 处理4维原地数组,数据最后
//  * 处理4维输出数组,长度硬编码
//
#define TEST_FUNC_PROTOS(TYPE, SNAME) \
\
TYPE SNAME##Norm(TYPE supertensor[2][2][2][2]); \
TYPE SNAME##Max(TYPE *supertensor, int cubes, int slices, int rows, int cols); \
TYPE SNAME##Min(int cubes, int slices, int rows, int cols, TYPE *supertensor); \
void SNAME##Scale(TYPE array[3][3][3][3], TYPE val); \
void SNAME##Floor(TYPE *array, int cubes, int slices, int rows, int cols, TYPE floor); \
void SNAME##Ceil(int cubes, int slices, int rows, int cols, TYPE *array, TYPE ceil); \
void SNAME##LUSplit(TYPE supertensor[2][2][2][2], TYPE lower[2][2][2][2], TYPE upper[2][2][2][2]);

// 对各种类型的TYPE/SNAME进行宏扩展,生成相应的函数原型
TEST_FUNC_PROTOS(signed char, schar)
TEST_FUNC_PROTOS(unsigned char, uchar)
TEST_FUNC_PROTOS(short, short)
TEST_FUNC_PROTOS(unsigned short, ushort)
TEST_FUNC_PROTOS(int, int)
TEST_FUNC_PROTOS(unsigned int, uint)
TEST_FUNC_PROTOS(long, long)
TEST_FUNC_PROTOS(unsigned long, ulong)
TEST_FUNC_PROTOS(long long, longLong)
TEST_FUNC_PROTOS(unsigned long long, ulongLong)
TEST_FUNC_PROTOS(float, float)
TEST_FUNC_PROTOS(double, double)

// 结束条件编译宏的定义
#endif

.\numpy\tools\swig\test\Tensor.cxx

// 包含标准库头文件 <stdlib.h> 和 <math.h>
#include <stdlib.h>
#include <math.h>
// 包含输入输出流库头文件 <iostream>
#include <iostream>
// 包含自定义头文件 "Tensor.h"
#include "Tensor.h"

// 定义宏 TEST_FUNCS(TYPE, SNAME),生成一系列函数用于处理三维数组
#define TEST_FUNCS(TYPE, SNAME) \
\
// 计算三维数组的范数,返回类型为 TYPE
TYPE SNAME ## Norm(TYPE tensor[2][2][2]) {         
  // 初始化结果变量为 0
  double result = 0;                     
  // 遍历三维数组的每个元素并计算平方和
  for (int k=0; k<2; ++k)                 
    for (int j=0; j<2; ++j)                 
      for (int i=0; i<2; ++i)                 
        result += tensor[k][j][i] * tensor[k][j][i]; 
  // 返回平方和的平方根除以 8 的结果,强制转换为指定类型 TYPE
  return (TYPE)sqrt(result/8);                 
}                             

// 计算三维数组的最大值,返回类型为 TYPE
TYPE SNAME ## Max(TYPE * tensor, int slices, int rows, int cols) { 
  // 声明变量 i, j, k, index
  int i, j, k, index;                        
  // 初始化结果变量为第一个元素的值
  TYPE result = tensor[0];                    
  // 嵌套循环遍历三维数组的每个元素
  for (k=0; k<slices; ++k) {                    
    for (j=0; j<rows; ++j) {                    
      for (i=0; i<cols; ++i) {                    
        // 计算当前元素在一维数组中的索引
        index = k*rows*cols + j*cols + i;            
        // 如果当前元素大于结果变量,更新结果变量的值
        if (tensor[index] > result) result = tensor[index];    
      }                                
    }                                
  }                                
  // 返回最大值
  return result;                        
}                                

// 计算三维数组的最小值,返回类型为 TYPE
TYPE SNAME ## Min(int slices, int rows, int cols, TYPE * tensor) {    
  // 声明变量 i, j, k, index
  int i, j, k, index;                        
  // 初始化结果变量为第一个元素的值
  TYPE result = tensor[0];                    
  // 嵌套循环遍历三维数组的每个元素
  for (k=0; k<slices; ++k) {                    
    for (j=0; j<rows; ++j) {                    
      for (i=0; i<cols; ++i) {                    
        // 计算当前元素在一维数组中的索引
        index = k*rows*cols + j*cols + i;            
        // 如果当前元素小于结果变量,更新结果变量的值
        if (tensor[index] < result) result = tensor[index];    
      }                                
    }                                
  }                                
  // 返回最小值
  return result;                        
}                                
void scharScale(signed char array[3][3][3], signed char val) { \
  // 遍历三维数组,对每个元素乘以给定的值
  for (int k=0; k<3; ++k)                 \
    for (int j=0; j<3; ++j)                 \
      for (int i=0; i<3; ++i)                 \
        array[k][j][i] *= val;                 \
}                             \
\
void scharFloor(signed char * array, int slices, int rows, int cols, signed char floor) { \
  int i, j, k, index;                                 \
  // 遍历一维数组,将小于指定下限的元素设为下限值
  for (k=0; k<slices; ++k) {                             \
    for (j=0; j<rows; ++j) {                             \
      for (i=0; i<cols; ++i) {                             \
        index = k*rows*cols + j*cols + i;                     \
        if (array[index] < floor) array[index] = floor;                 \
      }                                         \
    }                                         \
  }                                         \
}                                         \
\
void scharCeil(int slices, int rows, int cols, signed char * array, signed char ceil) { \
  int i, j, k, index;                               \
  // 遍历一维数组,将大于指定上限的元素设为上限值
  for (k=0; k<slices; ++k) {                           \
    for (j=0; j<rows; ++j) {                           \
      for (i=0; i<cols; ++i) {                           \
        index = k*rows*cols + j*cols + i;                           \
        if (array[index] > ceil) array[index] = ceil;               \
      }                                       \
    }                                       \
  }                                       \
}                                       \
\
void scharLUSplit(signed char tensor[2][2][2], signed char lower[2][2][2], \
              signed char upper[2][2][2]) {             \
  int sum;                             \
  // 遍历三维数组,根据索引和小于2的条件分割为上下三角部分
  for (int k=0; k<2; ++k) {                     \
    for (int j=0; j<2; ++j) {                     \
      for (int i=0; i<2; ++i) {                     \
        sum = i + j + k;                     \
        if (sum < 2) {                         \
          lower[k][j][i] = tensor[k][j][i];             \
          upper[k][j][i] = 0;                     \
        } else {                         \
          upper[k][j][i] = tensor[k][j][i];             \
          lower[k][j][i] = 0;                     \
        }                             \
      }                                 \
    }                                 \
  }                                 \
}

.\numpy\tools\swig\test\Tensor.h

// 如果未定义 TENSOR_H,则进入条件编译,避免重复包含
#ifndef TENSOR_H
// 定义 TENSOR_H,确保此头文件只被包含一次
#define TENSOR_H

// 下面的宏定义了一系列与 3D 数组相关的函数原型,这些函数根据指定的类型 TYPE(例如:short、unsigned int、long long 等)
// 和简称 SNAME(例如:short、uint、longLong 等)来生成
// 这些函数用于测试与 numpy 接口相关的功能,涵盖了不同数据类型和操作方式的测试需求,具体包括:
//
// TYPE SNAME ## Norm(   TYPE tensor[2][2][2]);              // 对 3D 输入数组进行范数计算
// TYPE SNAME ## Max(    TYPE * tensor, int slices, int rows, int cols);  // 计算 3D 输入数组的最大值
// TYPE SNAME ## Min(    int slices, int rows, int cols, TYPE * tensor);   // 计算 3D 输入数组的最小值
// void SNAME ## Scale(  TYPE array[3][3][3], TYPE val);     // 对 3D 原位数组进行按值缩放
// void SNAME ## Floor(  TYPE * array, int slices, int rows, int cols, TYPE floor);  // 对 3D 原位数组进行下取整
// void SNAME ## Ceil(   int slices, int rows, int cols, TYPE * array, TYPE ceil );  // 对 3D 原位数组进行上取整
// void SNAME ## LUSplit(TYPE tensor[2][2][2], TYPE lower[2][2][2], TYPE upper[2][2][2]);  // 对 3D 输出数组进行 LU 分解

#define TEST_FUNC_PROTOS(TYPE, SNAME) \
\
TYPE SNAME ## Norm(   TYPE tensor[2][2][2]); \
TYPE SNAME ## Max(    TYPE * tensor, int slices, int rows, int cols); \
TYPE SNAME ## Min(    int slices, int rows, int cols, TYPE * tensor); \
void SNAME ## Scale(  TYPE array[3][3][3], TYPE val); \
void SNAME ## Floor(  TYPE * array, int slices, int rows, int cols, TYPE floor); \
void SNAME ## Ceil(   int slices, int rows, int cols, TYPE * array, TYPE ceil ); \
void SNAME ## LUSplit(TYPE tensor[2][2][2], TYPE lower[2][2][2], TYPE upper[2][2][2]);

// 根据各种类型和简称,展开测试函数的宏定义
TEST_FUNC_PROTOS(signed char       , schar    )
TEST_FUNC_PROTOS(unsigned char     , uchar    )
TEST_FUNC_PROTOS(short             , short    )
TEST_FUNC_PROTOS(unsigned short    , ushort   )
TEST_FUNC_PROTOS(int               , int      )
TEST_FUNC_PROTOS(unsigned int      , uint     )
TEST_FUNC_PROTOS(long              , long     )
TEST_FUNC_PROTOS(unsigned long     , ulong    )
TEST_FUNC_PROTOS(long long         , longLong )
TEST_FUNC_PROTOS(unsigned long long, ulongLong)
TEST_FUNC_PROTOS(float             , float    )
TEST_FUNC_PROTOS(double            , double   )

// 结束条件编译指令
#endif

.\numpy\tools\swig\test\testArray.py

#!/usr/bin/env python3
# System imports
import sys  # 导入系统模块sys,用于访问系统相关功能
import unittest  # 导入unittest模块,用于编写和运行单元测试

# Import NumPy
import numpy as np  # 导入NumPy库,用于处理数值数组和数据

major, minor = [ int(d) for d in np.__version__.split(".")[:2] ]
if major == 0:
    BadListError = TypeError  # 如果NumPy的主版本号为0,定义BadListError为TypeError
else:
    BadListError = ValueError  # 否则定义BadListError为ValueError

import Array  # 导入自定义的Array模块

######################################################################

class Array1TestCase(unittest.TestCase):
    """定义Array1类的单元测试用例"""

    def setUp(self):
        """每个测试方法执行前的初始化操作"""
        self.length = 5  # 设置数组长度为5
        self.array1 = Array.Array1(self.length)  # 创建Array1对象

    def testConstructor0(self):
        """测试Array1的默认构造函数"""
        a = Array.Array1()  # 调用默认构造函数创建Array1对象a
        self.assertTrue(isinstance(a, Array.Array1))  # 断言a是Array1的实例
        self.assertTrue(len(a) == 0)  # 断言a的长度为0

    def testConstructor1(self):
        """测试Array1的长度构造函数"""
        self.assertTrue(isinstance(self.array1, Array.Array1))  # 断言self.array1是Array1的实例

    def testConstructor2(self):
        """测试Array1的数组构造函数"""
        na = np.arange(self.length)  # 创建一个NumPy数组na,包含从0到self.length-1的整数
        aa = Array.Array1(na)  # 使用数组na创建Array1对象aa
        self.assertTrue(isinstance(aa, Array.Array1))  # 断言aa是Array1的实例

    def testConstructor3(self):
        """测试Array1的拷贝构造函数"""
        for i in range(self.array1.length()): self.array1[i] = i  # 设置self.array1的元素为索引值
        arrayCopy = Array.Array1(self.array1)  # 使用self.array1创建Array1对象arrayCopy
        self.assertTrue(arrayCopy == self.array1)  # 断言arrayCopy等于self.array1

    def testConstructorBad(self):
        """测试Array1的长度构造函数,负值情况"""
        self.assertRaises(ValueError, Array.Array1, -4)  # 断言创建Array1对象时传入负数会引发ValueError异常

    def testLength(self):
        """测试Array1的length方法"""
        self.assertTrue(self.array1.length() == self.length)  # 断言self.array1的长度为预期长度

    def testLen(self):
        """测试Array1的__len__方法"""
        self.assertTrue(len(self.array1) == self.length)  # 断言调用len函数返回self.array1的长度

    def testResize0(self):
        """测试Array1的resize方法,长度参数"""
        newLen = 2 * self.length  # 设置新的长度为当前长度的两倍
        self.array1.resize(newLen)  # 调整self.array1的长度为newLen
        self.assertTrue(len(self.array1) == newLen)  # 断言self.array1的长度为newLen

    def testResize1(self):
        """测试Array1的resize方法,数组参数"""
        a = np.zeros((2*self.length,), dtype='l')  # 创建一个NumPy数组a,包含长度为2*self.length的零数组
        self.array1.resize(a)  # 使用数组a调整self.array1的长度
        self.assertTrue(len(self.array1) == a.size)  # 断言self.array1的长度等于数组a的大小

    def testResizeBad(self):
        """测试Array1的resize方法,负值长度"""
        self.assertRaises(ValueError, self.array1.resize, -5)  # 断言调用resize方法时传入负数会引发ValueError异常

    def testSetGet(self):
        """测试Array1的__setitem__和__getitem__方法"""
        n = self.length  # 设置n为数组的长度
        for i in range(n):
            self.array1[i] = i*i  # 设置索引i处的元素为i的平方
        for i in range(n):
            self.assertTrue(self.array1[i] == i*i)  # 断言索引i处的元素等于i的平方

    def testSetBad1(self):
        """测试Array1的__setitem__方法,负索引"""
        self.assertRaises(IndexError, self.array1.__setitem__, -1, 0)  # 断言设置负索引会引发IndexError异常

    def testSetBad2(self):
        """测试Array1的__setitem__方法,超出范围的索引"""
        self.assertRaises(IndexError, self.array1.__setitem__, self.length+1, 0)  # 断言设置超出范围的索引会引发IndexError异常

    def testGetBad1(self):
        """测试Array1的__getitem__方法,负索引"""
        self.assertRaises(IndexError, self.array1.__getitem__, -1)  # 断言访问负索引会引发IndexError异常
    # 测试 Array1 的 __getitem__ 方法,测试索引超出范围的情况
    def testGetBad2(self):
        "Test Array1 __getitem__ method, out-of-range index"
        # 断言是否抛出 IndexError 异常,访问超出范围的索引 self.length+1
        self.assertRaises(IndexError, self.array1.__getitem__, self.length+1)

    # 测试 Array1 的 asString 方法
    def testAsString(self):
        "Test Array1 asString method"
        # 设置 Array1 的元素值为索引加一
        for i in range(self.array1.length()): self.array1[i] = i+1
        # 断言 Array1 转换为字符串后是否等于预期的字符串 "[ 1, 2, 3, 4, 5 ]"
        self.assertTrue(self.array1.asString() == "[ 1, 2, 3, 4, 5 ]")

    # 测试 Array1 的 __str__ 方法
    def testStr(self):
        "Test Array1 __str__ method"
        # 设置 Array1 的元素值为索引减二
        for i in range(self.array1.length()): self.array1[i] = i-2
        # 断言 Array1 转换为字符串后是否等于预期的字符串 "[ -2, -1, 0, 1, 2 ]"
        self.assertTrue(str(self.array1) == "[ -2, -1, 0, 1, 2 ]")

    # 测试 Array1 的 view 方法
    def testView(self):
        "Test Array1 view method"
        # 设置 Array1 的元素值为索引加一
        for i in range(self.array1.length()): self.array1[i] = i+1
        # 调用 Array1 的 view 方法
        a = self.array1.view()
        # 断言返回的视图 a 是否为 numpy.ndarray 类型
        self.assertTrue(isinstance(a, np.ndarray))
        # 断言视图 a 的长度是否与 Array1 的长度相等
        self.assertTrue(len(a) == self.length)
        # 断言视图 a 的所有元素是否与预期的数组 [1, 2, 3, 4, 5] 相等
        self.assertTrue((a == [1, 2, 3, 4, 5]).all())
######################################################################

class Array2TestCase(unittest.TestCase):

    def setUp(self):
        # 设置测试用例中使用的行数和列数
        self.nrows = 5
        self.ncols = 4
        # 创建一个 Array2 类的实例对象
        self.array2 = Array.Array2(self.nrows, self.ncols)

    def testConstructor0(self):
        "Test Array2 default constructor"
        # 测试默认构造函数是否返回了 Array2 类的实例
        a = Array.Array2()
        self.assertTrue(isinstance(a, Array.Array2))
        # 测试默认构造函数返回的对象是否长度为 0
        self.assertTrue(len(a) == 0)

    def testConstructor1(self):
        "Test Array2 nrows, ncols constructor"
        # 测试指定行数和列数的构造函数是否返回了 Array2 类的实例
        self.assertTrue(isinstance(self.array2, Array.Array2))

    def testConstructor2(self):
        "Test Array2 array constructor"
        # 使用给定的 numpy 数组创建 Array2 类的实例
        na = np.zeros((3, 4), dtype="l")
        aa = Array.Array2(na)
        self.assertTrue(isinstance(aa, Array.Array2))

    def testConstructor3(self):
        "Test Array2 copy constructor"
        # 测试复制构造函数是否能够正确复制 Array2 类的实例
        for i in range(self.nrows):
            for j in range(self.ncols):
                self.array2[i][j] = i * j
        # 使用复制构造函数创建一个新的 Array2 实例
        arrayCopy = Array.Array2(self.array2)
        self.assertTrue(arrayCopy == self.array2)

    def testConstructorBad1(self):
        "Test Array2 nrows, ncols constructor, negative nrows"
        # 测试当指定的行数为负数时是否会引发 ValueError 异常
        self.assertRaises(ValueError, Array.Array2, -4, 4)

    def testConstructorBad2(self):
        "Test Array2 nrows, ncols constructor, negative ncols"
        # 测试当指定的列数为负数时是否会引发 ValueError 异常
        self.assertRaises(ValueError, Array.Array2, 4, -4)

    def testNrows(self):
        "Test Array2 nrows method"
        # 测试 nrows 方法是否返回正确的行数
        self.assertTrue(self.array2.nrows() == self.nrows)

    def testNcols(self):
        "Test Array2 ncols method"
        # 测试 ncols 方法是否返回正确的列数
        self.assertTrue(self.array2.ncols() == self.ncols)

    def testLen(self):
        "Test Array2 __len__ method"
        # 测试 __len__ 方法是否返回正确的数组长度
        self.assertTrue(len(self.array2) == self.nrows*self.ncols)

    def testResize0(self):
        "Test Array2 resize method, size"
        # 测试 resize 方法是否能够正确调整数组大小(指定行数和列数)
        newRows = 2 * self.nrows
        newCols = 2 * self.ncols
        self.array2.resize(newRows, newCols)
        self.assertTrue(len(self.array2) == newRows * newCols)

    def testResize1(self):
        "Test Array2 resize method, array"
        # 测试 resize 方法是否能够正确调整数组大小(使用给定的 numpy 数组)
        a = np.zeros((2*self.nrows, 2*self.ncols), dtype='l')
        self.array2.resize(a)
        self.assertTrue(len(self.array2) == a.size)

    def testResizeBad1(self):
        "Test Array2 resize method, negative nrows"
        # 测试当调整行数为负数时,是否会引发 ValueError 异常
        self.assertRaises(ValueError, self.array2.resize, -5, 5)

    def testResizeBad2(self):
        "Test Array2 resize method, negative ncols"
        # 测试当调整列数为负数时,是否会引发 ValueError 异常
        self.assertRaises(ValueError, self.array2.resize, 5, -5)

    def testSetGet1(self):
        "Test Array2 __setitem__, __getitem__ methods"
        m = self.nrows
        n = self.ncols
        array1 = []
        a = np.arange(n, dtype="l")
        for i in range(m):
            # 创建一个 Array1 类的实例列表
            array1.append(Array.Array1(i * a))
        for i in range(m):
            # 测试 __setitem__ 方法是否能够正确设置元素
            self.array2[i] = array1[i]
        for i in range(m):
            # 测试 __getitem__ 方法是否能够正确获取元素
            self.assertTrue(self.array2[i] == array1[i])
    # 测试 Array2 类的链式 __setitem__ 和 __getitem__ 方法
    def testSetGet2(self):
        "Test Array2 chained __setitem__, __getitem__ methods"
        m = self.nrows  # 获取行数
        n = self.ncols  # 获取列数
        for i in range(m):  # 遍历行
            for j in range(n):  # 遍历列
                self.array2[i][j] = i * j  # 设置数组中索引 (i, j) 处的值为 i * j
        for i in range(m):  # 再次遍历行
            for j in range(n):  # 再次遍历列
                self.assertTrue(self.array2[i][j] == i * j)  # 断言数组中索引 (i, j) 处的值等于 i * j

    # 测试 Array2 类的 __setitem__ 方法,使用负索引
    def testSetBad1(self):
        "Test Array2 __setitem__ method, negative index"
        a = Array.Array1(self.ncols)  # 创建一个 Array1 类型的对象 a
        self.assertRaises(IndexError, self.array2.__setitem__, -1, a)  # 断言调用 __setitem__ 方法时使用负索引会抛出 IndexError 异常

    # 测试 Array2 类的 __setitem__ 方法,使用超出范围的索引
    def testSetBad2(self):
        "Test Array2 __setitem__ method, out-of-range index"
        a = Array.Array1(self.ncols)  # 创建一个 Array1 类型的对象 a
        self.assertRaises(IndexError, self.array2.__setitem__, self.nrows + 1, a)  # 断言调用 __setitem__ 方法时使用超出范围的索引会抛出 IndexError 异常

    # 测试 Array2 类的 __getitem__ 方法,使用负索引
    def testGetBad1(self):
        "Test Array2 __getitem__ method, negative index"
        self.assertRaises(IndexError, self.array2.__getitem__, -1)  # 断言调用 __getitem__ 方法时使用负索引会抛出 IndexError 异常

    # 测试 Array2 类的 __getitem__ 方法,使用超出范围的索引
    def testGetBad2(self):
        "Test Array2 __getitem__ method, out-of-range index"
        self.assertRaises(IndexError, self.array2.__getitem__, self.nrows + 1)  # 断言调用 __getitem__ 方法时使用超出范围的索引会抛出 IndexError 异常

    # 测试 Array2 类的 asString 方法
    def testAsString(self):
        "Test Array2 asString method"
        result = """\
# 定义一个二维列表,表示一个包含整数的二维数组
[ [ 0, 1, 2, 3 ],
  [ 1, 2, 3, 4 ],
  [ 2, 3, 4, 5 ],
  [ 3, 4, 5, 6 ],
  [ 4, 5, 6, 7 ] ]



        # 遍历二维数组的行
        for i in range(self.nrows):
            # 遍历二维数组的列
            for j in range(self.ncols):
                # 为每个元素赋值为行索引和列索引的和
                self.array2[i][j] = i+j
        # 使用断言验证数组转换为字符串后是否与指定的结果字符串相等
        self.assertTrue(self.array2.asString() == result)



    def testStr(self):
        "Test Array2 __str__ method"
        # 定义预期的结果字符串,表示包含整数的二维数组
        result = """\
[ [ 0, -1, -2, -3 ],
  [ 1, 0, -1, -2 ],
  [ 2, 1, 0, -1 ],
  [ 3, 2, 1, 0 ],
  [ 4, 3, 2, 1 ] ]
"""
        # 遍历二维数组的行
        for i in range(self.nrows):
            # 遍历二维数组的列
            for j in range(self.ncols):
                # 为每个元素赋值为行索引减去列索引
                self.array2[i][j] = i-j
        # 使用断言验证数组转换为字符串后是否与指定的结果字符串相等
        self.assertTrue(str(self.array2) == result)



    def testView(self):
        "Test Array2 view method"
        # 调用数组的视图方法
        a = self.array2.view()
        # 使用断言验证返回的视图对象是否是 NumPy 数组
        self.assertTrue(isinstance(a, np.ndarray))
        # 使用断言验证返回的视图数组的长度是否与指定的行数相等
        self.assertTrue(len(a) == self.nrows)



######################################################################

class ArrayZTestCase(unittest.TestCase):

    def setUp(self):
        # 初始化测试用例中数组的长度
        self.length = 5
        # 创建一个长度为 self.length 的 ArrayZ 对象
        self.array3 = Array.ArrayZ(self.length)


(以下测试函数可以根据需要进行类似的注释,但已超出了示例的范围,这里仅展示示例代码部分的注释。)
    def testSetBad1(self):
        "Test ArrayZ __setitem__ method, negative index"
        # 断言会抛出 IndexError 异常,因为索引为负数
        self.assertRaises(IndexError, self.array3.__setitem__, -1, 0)

    def testSetBad2(self):
        "Test ArrayZ __setitem__ method, out-of-range index"
        # 断言会抛出 IndexError 异常,因为索引超出范围
        self.assertRaises(IndexError, self.array3.__setitem__, self.length+1, 0)

    def testGetBad1(self):
        "Test ArrayZ __getitem__ method, negative index"
        # 断言会抛出 IndexError 异常,因为索引为负数
        self.assertRaises(IndexError, self.array3.__getitem__, -1)

    def testGetBad2(self):
        "Test ArrayZ __getitem__ method, out-of-range index"
        # 断言会抛出 IndexError 异常,因为索引超出范围
        self.assertRaises(IndexError, self.array3.__getitem__, self.length+1)

    def testAsString(self):
        "Test ArrayZ asString method"
        # 为 ArrayZ 对象的元素赋值为复数
        for i in range(self.array3.length()): self.array3[i] = complex(i+1,-i-1)
        # 断言 ArrayZ 对象的字符串表示与预期的字符串相等
        self.assertTrue(self.array3.asString() == "[ (1,-1), (2,-2), (3,-3), (4,-4), (5,-5) ]")

    def testStr(self):
        "Test ArrayZ __str__ method"
        # 为 ArrayZ 对象的元素赋值为复数
        for i in range(self.array3.length()): self.array3[i] = complex(i-2,(i-2)*2)
        # 断言 ArrayZ 对象的字符串表示与预期的字符串相等
        self.assertTrue(str(self.array3) == "[ (-2,-4), (-1,-2), (0,0), (1,2), (2,4) ]")

    def testView(self):
        "Test ArrayZ view method"
        # 为 ArrayZ 对象的元素赋值为复数
        for i in range(self.array3.length()): self.array3[i] = complex(i+1,i+2)
        # 获取 ArrayZ 对象的视图
        a = self.array3.view()
        # 断言视图对象是 numpy.ndarray 类型
        self.assertTrue(isinstance(a, np.ndarray))
        # 断言视图对象的长度与 ArrayZ 对象的长度相等
        self.assertTrue(len(a) == self.length)
        # 断言视图对象的所有元素与预期的复数数组相等
        self.assertTrue((a == [1+2j, 2+3j, 3+4j, 4+5j, 5+6j]).all())
######################################################################

if __name__ == "__main__":
    # 如果当前脚本被直接执行而非被导入,则执行以下代码块

    # 构建测试套件
    suite = unittest.TestSuite()
    # 将 Array1TestCase 的测试添加到测试套件中
    suite.addTest(unittest.makeSuite(Array1TestCase))
    # 将 Array2TestCase 的测试添加到测试套件中
    suite.addTest(unittest.makeSuite(Array2TestCase))
    # 将 ArrayZTestCase 的测试添加到测试套件中
    suite.addTest(unittest.makeSuite(ArrayZTestCase))

    # 执行测试套件
    print("Testing Classes of Module Array")
    # 打印 NumPy 版本信息
    print("NumPy version", np.__version__)
    print()
    # 运行测试套件,并返回测试结果
    result = unittest.TextTestRunner(verbosity=2).run(suite)
    # 根据测试结果中是否存在错误或失败,决定退出状态
    sys.exit(bool(result.errors + result.failures))