NumPy-源码解析-十-

47 阅读1小时+

NumPy 源码解析(十)

.\numpy\numpy\f2py\symbolic.py

"""
Fortran/C symbolic expressions

References:
- J3/21-007: Draft Fortran 202x. https://j3-fortran.org/doc/year/21/21-007.pdf

Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
Copyright 2011 -- present NumPy Developers.
Permission to use, modify, and distribute this software is given under the
terms of the NumPy License.

NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
"""

# To analyze Fortran expressions to solve dimensions specifications,
# for instances, we implement a minimal symbolic engine for parsing
# expressions into a tree of expression instances. As a first
# instance, we care only about arithmetic expressions involving
# integers and operations like addition (+), subtraction (-),
# multiplication (*), division (Fortran / is Python //, Fortran // is
# concatenate), and exponentiation (**).  In addition, .pyf files may
# contain C expressions that support here is implemented as well.
#
# TODO: support logical constants (Op.BOOLEAN)
# TODO: support logical operators (.AND., ...)
# TODO: support defined operators (.MYOP., ...)
#

__all__ = ['Expr']

import re
import warnings
from enum import Enum
from math import gcd

class Language(Enum):
    """
    Used as Expr.tostring language argument.
    """
    Python = 0
    Fortran = 1
    C = 2

class Op(Enum):
    """
    Used as Expr op attribute.
    """
    INTEGER = 10
    REAL = 12
    COMPLEX = 15
    STRING = 20
    ARRAY = 30
    SYMBOL = 40
    TERNARY = 100
    APPLY = 200
    INDEXING = 210
    CONCAT = 220
    RELATIONAL = 300
    TERMS = 1000
    FACTORS = 2000
    REF = 3000
    DEREF = 3001

class RelOp(Enum):
    """
    Used in Op.RELATIONAL expression to specify the function part.
    """
    EQ = 1
    NE = 2
    LT = 3
    LE = 4
    GT = 5
    GE = 6

    @classmethod
    def fromstring(cls, s, language=Language.C):
        """
        Convert a string representation of a relational operator to its corresponding RelOp value.
        """
        if language is Language.Fortran:
            return {'.eq.': RelOp.EQ, '.ne.': RelOp.NE,
                    '.lt.': RelOp.LT, '.le.': RelOp.LE,
                    '.gt.': RelOp.GT, '.ge.': RelOp.GE}[s.lower()]
        return {'==': RelOp.EQ, '!=': RelOp.NE, '<': RelOp.LT,
                '<=': RelOp.LE, '>': RelOp.GT, '>=': RelOp.GE}[s]

    def tostring(self, language=Language.C):
        """
        Convert the RelOp value to its string representation based on the specified language.
        """
        if language is Language.Fortran:
            return {RelOp.EQ: '.eq.', RelOp.NE: '.ne.',
                    RelOp.LT: '.lt.', RelOp.LE: '.le.',
                    RelOp.GT: '.gt.', RelOp.GE: '.ge.'}[self]
        return {RelOp.EQ: '==', RelOp.NE: '!=',
                RelOp.LT: '<', RelOp.LE: '<=',
                RelOp.GT: '>', RelOp.GE: '>='}[self]

class ArithOp(Enum):
    """
    Used in Op.APPLY expression to specify the function part.
    """
    POS = 1
    NEG = 2
    ADD = 3
    SUB = 4
    MUL = 5
    DIV = 6
    POW = 7

class OpError(Exception):
    """
    Exception raised for errors in Op operations.
    """
    pass

class Precedence(Enum):
    """
    Used as Expr.tostring precedence argument.
    """
    ATOM = 0
    POWER = 1
    UNARY = 2
    PRODUCT = 3
    SUM = 4
    LT = 6
"""
This code defines a set of classes and enums related to symbolic expressions in Fortran and C. Here's the commented version:


"""
Fortran/C symbolic expressions

References:
- J3/21-007: Draft Fortran 202x. https://j3-fortran.org/doc/year/21/21-007.pdf

Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
Copyright 2011 -- present NumPy Developers.
Permission to use, modify, and distribute this software is given under the
terms of the NumPy License.

NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
"""

# To analyze Fortran expressions to solve dimensions specifications,
# for instances, we implement a minimal symbolic engine for parsing
# expressions into a tree of expression instances. As a first
# instance, we care only about arithmetic expressions involving
# integers and operations like addition (+), subtraction (-),
# multiplication (*), division (Fortran / is Python //, Fortran // is
# concatenate), and exponentiation (**).  In addition, .pyf files may
# contain C expressions that support here is implemented as well.
#
# TODO: support logical constants (Op.BOOLEAN)
# TODO: support logical operators (.AND., ...)
# TODO: support defined operators (.MYOP., ...)
#

__all__ = ['Expr']

import re
import warnings
from enum import Enum
from math import gcd

class Language(Enum):
    """
    Enum to represent programming languages for symbolic expressions.
    """
    Python = 0
    Fortran = 1
    C = 2

class Op(Enum):
    """
    Enum to represent types of operations in symbolic expressions.
    """
    INTEGER = 10
    REAL = 12
    COMPLEX = 15
    STRING = 20
    ARRAY = 30
    SYMBOL = 40
    TERNARY = 100
    APPLY = 200
    INDEXING = 210
    CONCAT = 220
    RELATIONAL = 300
    TERMS = 1000
    FACTORS = 2000
    REF = 3000
    DEREF = 3001

class RelOp(Enum):
    """
    Enum to represent relational operators.
    """
    EQ = 1
    NE = 2
    LT = 3
    LE = 4
    GT = 5
    GE = 6

    @classmethod
    def fromstring(cls, s, language=Language.C):
        """
        Convert a string representation of a relational operator to its corresponding RelOp value.
        """
        if language is Language.Fortran:
            return {'.eq.': RelOp.EQ, '.ne.': RelOp.NE,
                    '.lt.': RelOp.LT, '.le.': RelOp.LE,
                    '.gt.': RelOp.GT, '.ge.': RelOp.GE}[s.lower()]
        return {'==': RelOp.EQ, '!=': RelOp.NE, '<': RelOp.LT,
                '<=': RelOp.LE, '>': RelOp.GT, '>=': RelOp.GE}[s]

    def tostring(self, language=Language.C):
        """
        Convert the RelOp value to its string representation based on the specified language.
        """
        if language is Language.Fortran:
            return {RelOp.EQ: '.eq.', RelOp.NE: '.ne.',
                    RelOp.LT: '.lt.', RelOp.LE: '.le.',
                    RelOp.GT: '.gt.', RelOp.GE: '.ge.'}[self]
        return {RelOp.EQ: '==', RelOp.NE: '!=',
                RelOp.LT: '<', RelOp.LE: '<=',
                RelOp.GT: '>', RelOp.GE: '>='}[self]

class ArithOp(Enum):
    """
    Enum to represent arithmetic operations.
    """
    POS = 1
    NEG = 2
    ADD = 3
    SUB = 4
    MUL = 5
    DIV = 6
    POW = 7

class OpError(Exception):
    """
    Exception raised for errors in Op operations.
    """
    pass

class Precedence(Enum):
    """
    Enum to represent precedence levels in expression parsing.
    """
    ATOM = 0
    POWER = 1
    UNARY = 2
    PRODUCT = 3
    SUM = 4
    LT = 6


This Python module defines enums and classes necessary for handling symbolic expressions in Fortran and C, including various types of operations, relational operators, arithmetic operations, and precedence levels. The code also includes utility methods for converting between enum values and their string representations, specifically tailored for different programming languages.
    # 定义常量 EQ,其值为 7,表示等于操作
    EQ = 7
    # 定义常量 LAND,其值为 11,表示逻辑与操作
    LAND = 11
    # 定义常量 LOR,其值为 12,表示逻辑或操作
    LOR = 12
    # 定义常量 TERNARY,其值为 13,表示三元操作符
    TERNARY = 13
    # 定义常量 ASSIGN,其值为 14,表示赋值操作
    ASSIGN = 14
    # 定义常量 TUPLE,其值为 15,表示元组操作
    TUPLE = 15
    # 定义常量 NONE,其值为 100,表示空值或未定义状态
    NONE = 100
# 定义一个包含单个整数类型 int 的元组
integer_types = (int,)
# 定义一个包含整数和浮点数类型 int, float 的元组
number_types = (int, float)


def _pairs_add(d, k, v):
    # 内部实用方法,用于更新字典 d 中键 k 对应的值 v
    c = d.get(k)
    if c is None:
        d[k] = v
    else:
        # 如果 c 不为 None,则将其增加 v,更新到字典中
        c = c + v
        # 如果更新后的值 c 不为 0,则更新字典中的键 k
        if c:
            d[k] = c
        else:
            # 如果 c 为 0,则从字典中删除键 k
            del d[k]


class ExprWarning(UserWarning):
    # 定义一个继承自 UserWarning 的 ExprWarning 类
    pass


def ewarn(message):
    # 发出一个 ExprWarning 警告,警告消息为 message
    warnings.warn(message, ExprWarning, stacklevel=2)


class Expr:
    """Represents a Fortran expression as a op-data pair.

    Expr instances are hashable and sortable.
    """
    
    @staticmethod
    def parse(s, language=Language.C):
        """Parse a Fortran expression to a Expr.

        解析 Fortran 表达式 s 到一个 Expr 对象。
        """
        return fromstring(s, language=language)
    # 初始化方法,接受操作符 op 和数据 data 作为参数
    def __init__(self, op, data):
        # 确保 op 是 Op 类的实例
        assert isinstance(op, Op)

        # 对不同的操作符进行不同的数据有效性检查
        if op is Op.INTEGER:
            # 如果操作符是 Op.INTEGER,则数据应为一个包含两个元素的元组,第一个是数值对象,第二个是种类值(默认为4)
            assert isinstance(data, tuple) and len(data) == 2
            assert isinstance(data[0], int)
            assert isinstance(data[1], (int, str)), data
        elif op is Op.REAL:
            # 如果操作符是 Op.REAL,则数据应为一个包含两个元素的元组,第一个是浮点数对象,第二个是种类值(默认为4)
            assert isinstance(data, tuple) and len(data) == 2
            assert isinstance(data[0], float)
            assert isinstance(data[1], (int, str)), data
        elif op is Op.COMPLEX:
            # 如果操作符是 Op.COMPLEX,则数据应为一个包含两个元素的元组,都是常量表达式
            assert isinstance(data, tuple) and len(data) == 2
        elif op is Op.STRING:
            # 如果操作符是 Op.STRING,则数据应为一个包含两个元素的元组,第一个是带引号的字符串,第二个是种类值(默认为1)
            assert isinstance(data, tuple) and len(data) == 2
            # 确保第一个元素是字符串且以双引号或单引号包围,或者是一对 '@@'
            assert (isinstance(data[0], str)
                    and data[0][::len(data[0])-1] in ('""', "''", '@@'))
            assert isinstance(data[1], (int, str)), data
        elif op is Op.SYMBOL:
            # 如果操作符是 Op.SYMBOL,则数据可以是任意可散列对象
            assert hash(data) is not None
        elif op in (Op.ARRAY, Op.CONCAT):
            # 如果操作符是 Op.ARRAY 或 Op.CONCAT,则数据应为一个表达式的元组
            assert isinstance(data, tuple)
            assert all(isinstance(item, Expr) for item in data), data
        elif op in (Op.TERMS, Op.FACTORS):
            # 如果操作符是 Op.TERMS 或 Op.FACTORS,则数据应为一个字典,其值为非零整数的 Python 字典
            assert isinstance(data, dict)
        elif op is Op.APPLY:
            # 如果操作符是 Op.APPLY,则数据应为一个包含三个元素的元组,第一个元素是函数对象,第二个元素是操作数元组,第三个元素是关键字操作数字典
            assert isinstance(data, tuple) and len(data) == 3
            # 确保函数对象可散列
            assert hash(data[0]) is not None
            assert isinstance(data[1], tuple)
            assert isinstance(data[2], dict)
        elif op is Op.INDEXING:
            # 如果操作符是 Op.INDEXING,则数据应为一个包含两个元素的元组,第一个元素是对象,第二个元素是索引
            assert isinstance(data, tuple) and len(data) == 2
            # 确保对象可散列
            assert hash(data[0]) is not None
        elif op is Op.TERNARY:
            # 如果操作符是 Op.TERNARY,则数据应为一个包含三个元素的元组,分别是条件表达式、表达式1和表达式2
            assert isinstance(data, tuple) and len(data) == 3
        elif op in (Op.REF, Op.DEREF):
            # 如果操作符是 Op.REF 或 Op.DEREF,则数据应为一个 Expr 实例
            assert isinstance(data, Expr)
        elif op is Op.RELATIONAL:
            # 如果操作符是 Op.RELATIONAL,则数据应为一个包含三个元素的元组,分别是关系运算符、左操作数和右操作数
            assert isinstance(data, tuple) and len(data) == 3
        else:
            # 如果操作符未知或缺少有效性检查,则引发 NotImplementedError 异常
            raise NotImplementedError(
                f'unknown op or missing sanity check: {op}')

        # 将操作符和数据存储在对象实例的属性中
        self.op = op
        self.data = data
    def __eq__(self, other):
        # 检查是否是同一类型的表达式,并比较操作符和数据是否相等
        return (isinstance(other, Expr)
                and self.op is other.op
                and self.data == other.data)

    def __hash__(self):
        # 根据表达式的操作符和数据生成哈希值
        if self.op in (Op.TERMS, Op.FACTORS):
            data = tuple(sorted(self.data.items()))
        elif self.op is Op.APPLY:
            data = self.data[:2] + tuple(sorted(self.data[2].items()))
        else:
            data = self.data
        return hash((self.op, data))

    def __lt__(self, other):
        # 比较表达式的大小
        if isinstance(other, Expr):
            if self.op is not other.op:
                return self.op.value < other.op.value
            if self.op in (Op.TERMS, Op.FACTORS):
                return (tuple(sorted(self.data.items()))
                        < tuple(sorted(other.data.items())))
            if self.op is Op.APPLY:
                if self.data[:2] != other.data[:2]:
                    return self.data[:2] < other.data[:2]
                return tuple(sorted(self.data[2].items())) < tuple(
                    sorted(other.data[2].items()))
            return self.data < other.data
        return NotImplemented

    def __le__(self, other):
        # 检查表达式是否小于或等于另一个表达式
        return self == other or self < other

    def __gt__(self, other):
        # 检查表达式是否大于另一个表达式
        return not (self <= other)

    def __ge__(self, other):
        # 检查表达式是否大于或等于另一个表达式
        return not (self < other)

    def __repr__(self):
        # 返回表达式的规范字符串表示
        return f'{type(self).__name__}({self.op}, {self.data!r})'

    def __str__(self):
        # 返回表达式的字符串表示
        return self.tostring()

    def __pos__(self):
        # 正号操作,返回自身
        return self

    def __neg__(self):
        # 负号操作,返回自身乘以-1
        return self * -1

    def __add__(self, other):
        # 加法操作
        other = as_expr(other)
        if isinstance(other, Expr):
            if self.op is other.op:
                if self.op in (Op.INTEGER, Op.REAL):
                    # 对整数或实数执行加法
                    return as_number(
                        self.data[0] + other.data[0],
                        max(self.data[1], other.data[1]))
                if self.op is Op.COMPLEX:
                    # 对复数执行加法
                    r1, i1 = self.data
                    r2, i2 = other.data
                    return as_complex(r1 + r2, i1 + i2)
                if self.op is Op.TERMS:
                    # 对项执行加法
                    r = Expr(self.op, dict(self.data))
                    for k, v in other.data.items():
                        _pairs_add(r.data, k, v)
                    return normalize(r)
            if self.op is Op.COMPLEX and other.op in (Op.INTEGER, Op.REAL):
                return self + as_complex(other)
            elif self.op in (Op.INTEGER, Op.REAL) and other.op is Op.COMPLEX:
                return as_complex(self) + other
            elif self.op is Op.REAL and other.op is Op.INTEGER:
                return self + as_real(other, kind=self.data[1])
            elif self.op is Op.INTEGER and other.op is Op.REAL:
                return as_real(self, kind=other.data[1]) + other
            return as_terms(self) + as_terms(other)
        return NotImplemented
    # 定义特殊方法 __radd__,实现右加运算
    def __radd__(self, other):
        # 如果 other 是数值类型,则将其转换为表达式后执行加法运算
        if isinstance(other, number_types):
            return as_number(other) + self
        # 如果 other 不是数值类型,则返回 NotImplemented,表示无法处理此操作
        return NotImplemented

    # 定义特殊方法 __sub__,实现减法运算
    def __sub__(self, other):
        # 返回 self 与 -other 的加法结果,实现减法运算
        return self + (-other)

    # 定义特殊方法 __rsub__,实现右减运算
    def __rsub__(self, other):
        # 如果 other 是数值类型,则将其转换为表达式后执行减法运算
        if isinstance(other, number_types):
            return as_number(other) - self
        # 如果 other 不是数值类型,则返回 NotImplemented,表示无法处理此操作
        return NotImplemented

    # 定义特殊方法 __mul__,实现乘法运算
    def __mul__(self, other):
        # 将 other 转换为表达式类型
        other = as_expr(other)
        # 如果 other 是表达式对象
        if isinstance(other, Expr):
            # 如果两个表达式的操作符相同
            if self.op is other.op:
                # 处理不同操作符的乘法运算
                if self.op in (Op.INTEGER, Op.REAL):
                    # 对整数和实数执行乘法运算
                    return as_number(self.data[0] * other.data[0],
                                     max(self.data[1], other.data[1]))
                elif self.op is Op.COMPLEX:
                    # 对复数执行乘法运算
                    r1, i1 = self.data
                    r2, i2 = other.data
                    return as_complex(r1 * r2 - i1 * i2, r1 * i2 + r2 * i1)

                # 对因子操作进行乘法运算
                if self.op is Op.FACTORS:
                    r = Expr(self.op, dict(self.data))
                    for k, v in other.data.items():
                        _pairs_add(r.data, k, v)
                    return normalize(r)
                elif self.op is Op.TERMS:
                    r = Expr(self.op, {})
                    for t1, c1 in self.data.items():
                        for t2, c2 in other.data.items():
                            _pairs_add(r.data, t1 * t2, c1 * c2)
                    return normalize(r)

            # 处理复数与整数/实数之间的乘法运算
            if self.op is Op.COMPLEX and other.op in (Op.INTEGER, Op.REAL):
                return self * as_complex(other)
            elif other.op is Op.COMPLEX and self.op in (Op.INTEGER, Op.REAL):
                return as_complex(self) * other
            elif self.op is Op.REAL and other.op is Op.INTEGER:
                return self * as_real(other, kind=self.data[1])
            elif self.op is Op.INTEGER and other.op is Op.REAL:
                return as_real(self, kind=other.data[1]) * other

            # 处理项操作之间的乘法运算
            if self.op is Op.TERMS:
                return self * as_terms(other)
            elif other.op is Op.TERMS:
                return as_terms(self) * other

            # 如果均不匹配上述条件,则将两个表达式都转换为因子操作进行乘法运算
            return as_factors(self) * as_factors(other)
        # 如果 other 不是表达式类型,则返回 NotImplemented,表示无法处理此操作
        return NotImplemented

    # 定义特殊方法 __rmul__,实现右乘运算
    def __rmul__(self, other):
        # 如果 other 是数值类型,则将其转换为表达式后执行乘法运算
        if isinstance(other, number_types):
            return as_number(other) * self
        # 如果 other 不是数值类型,则返回 NotImplemented,表示无法处理此操作
        return NotImplemented
    def __pow__(self, other):
        other = as_expr(other)  # 将参数转换为表达式对象
        if isinstance(other, Expr):  # 如果参数是表达式对象
            if other.op is Op.INTEGER:  # 如果表达式操作是整数
                exponent = other.data[0]  # 获取整数指数
                if exponent == 0:
                    return as_number(1)  # 返回 1,任何数的 0 次方为 1
                if exponent == 1:
                    return self  # 返回自身,任何数的 1 次方为其本身
                if exponent > 0:
                    if self.op is Op.FACTORS:  # 如果自身是因子类型的表达式对象
                        r = Expr(self.op, {})  # 创建一个新的因子类型表达式对象
                        for k, v in self.data.items():
                            r.data[k] = v * exponent  # 对每个因子进行指数运算
                        return normalize(r)  # 规范化并返回结果
                    return self * (self ** (exponent - 1))  # 递归计算正整数次方
                elif exponent != -1:
                    return (self ** (-exponent)) ** -1  # 处理负指数
                return Expr(Op.FACTORS, {self: exponent})  # 返回带有自身和指数的新因子表达式
            return as_apply(ArithOp.POW, self, other)  # 如果不是整数操作,则应用 POW 操作符
        return NotImplemented  # 如果参数不是表达式对象,则返回 Not Implemented

    def __truediv__(self, other):
        other = as_expr(other)  # 将参数转换为表达式对象
        if isinstance(other, Expr):  # 如果参数是表达式对象
            # Fortran 的 / 操作符与 Python 的 / 不同:
            # - 对于整数操作数,/ 是截断操作
            return normalize(as_apply(ArithOp.DIV, self, other))  # 应用 DIV 操作符并规范化结果
        return NotImplemented  # 如果参数不是表达式对象,则返回 Not Implemented

    def __rtruediv__(self, other):
        other = as_expr(other)  # 将参数转换为表达式对象
        if isinstance(other, Expr):  # 如果参数是表达式对象
            return other / self  # 执行反向真除操作
        return NotImplemented  # 如果参数不是表达式对象,则返回 Not Implemented

    def __floordiv__(self, other):
        other = as_expr(other)  # 将参数转换为表达式对象
        if isinstance(other, Expr):  # 如果参数是表达式对象
            # Fortran 的 // 操作符与 Python 的 // 不同:
            # - 对于字符串操作数,// 是连接操作
            return normalize(Expr(Op.CONCAT, (self, other)))  # 创建连接操作的表达式并规范化结果
        return NotImplemented  # 如果参数不是表达式对象,则返回 Not Implemented

    def __rfloordiv__(self, other):
        other = as_expr(other)  # 将参数转换为表达式对象
        if isinstance(other, Expr):  # 如果参数是表达式对象
            return other // self  # 执行反向地板除法操作
        return NotImplemented  # 如果参数不是表达式对象,则返回 Not Implemented

    def __call__(self, *args, **kwargs):
        # 在 Fortran 中,括号 () 用于函数调用和索引操作。
        #
        # TODO: 实现一个方法来决定何时 __call__ 应该返回一个索引表达式。
        return as_apply(self, *map(as_expr, args),
                        **dict((k, as_expr(v)) for k, v in kwargs.items()))

    def __getitem__(self, index):
        # 用于支持可能包含在 .pyf 文件中的 C 索引操作。
        index = as_expr(index)  # 将索引转换为表达式对象
        if not isinstance(index, tuple):
            index = index,  # 如果索引不是元组,则转换为单元素元组
        if len(index) > 1:
            ewarn(f'C-index should be a single expression but got `{index}`')  # 发出警告,C 索引应该是单一表达式
        return Expr(Op.INDEXING, (self,) + index)  # 创建索引操作的表达式并返回
    def traverse(self, visit, *args, **kwargs):
        """遍历表达式树并应用访问函数。

        访问函数应用于带有给定 args 和 kwargs 的表达式。

        如果访问函数返回非空表达式,则遍历调用返回该表达式;
        否则,返回一个新的经过标准化处理的表达式,其中包含遍历-访问子表达式。
        """
        # 调用访问函数,并根据返回结果判断是否直接返回
        result = visit(self, *args, **kwargs)
        if result is not None:
            return result

        # 根据表达式的操作类型进行不同的处理
        if self.op in (Op.INTEGER, Op.REAL, Op.STRING, Op.SYMBOL):
            return self
        elif self.op in (Op.COMPLEX, Op.ARRAY, Op.CONCAT, Op.TERNARY):
            # 对于复杂操作类型,递归遍历处理每个子项
            return normalize(Expr(self.op, tuple(
                item.traverse(visit, *args, **kwargs)
                for item in self.data)))
        elif self.op in (Op.TERMS, Op.FACTORS):
            # 对于项或因子操作类型,遍历处理字典中的每一项
            data = {}
            for k, v in self.data.items():
                k = k.traverse(visit, *args, **kwargs)
                v = (v.traverse(visit, *args, **kwargs)
                     if isinstance(v, Expr) else v)
                if k in data:
                    v = data[k] + v
                data[k] = v
            return normalize(Expr(self.op, data))
        elif self.op is Op.APPLY:
            # 对于函数应用操作类型,遍历处理对象、操作数和关键字操作数
            obj = self.data[0]
            func = (obj.traverse(visit, *args, **kwargs)
                    if isinstance(obj, Expr) else obj)
            operands = tuple(operand.traverse(visit, *args, **kwargs)
                             for operand in self.data[1])
            kwoperands = dict((k, v.traverse(visit, *args, **kwargs))
                              for k, v in self.data[2].items())
            return normalize(Expr(self.op, (func, operands, kwoperands)))
        elif self.op is Op.INDEXING:
            # 对于索引操作类型,遍历处理对象和索引
            obj = self.data[0]
            obj = (obj.traverse(visit, *args, **kwargs)
                   if isinstance(obj, Expr) else obj)
            indices = tuple(index.traverse(visit, *args, **kwargs)
                            for index in self.data[1:])
            return normalize(Expr(self.op, (obj,) + indices))
        elif self.op in (Op.REF, Op.DEREF):
            # 对于引用和解引用操作类型,遍历处理数据
            return normalize(Expr(self.op,
                                  self.data.traverse(visit, *args, **kwargs)))
        elif self.op is Op.RELATIONAL:
            # 对于关系操作类型,遍历处理左操作数和右操作数
            rop, left, right = self.data
            left = left.traverse(visit, *args, **kwargs)
            right = right.traverse(visit, *args, **kwargs)
            return normalize(Expr(self.op, (rop, left, right)))
        
        # 若操作类型未被处理,则抛出未实现错误
        raise NotImplementedError(f'traverse method for {self.op}')

    def contains(self, other):
        """检查 self 是否包含 other。"""
        found = []

        def visit(expr, found=found):
            if found:
                return expr
            elif expr == other:
                found.append(1)
                return expr

        self.traverse(visit)

        # 返回是否找到匹配项的布尔值
        return len(found) != 0
    def symbols(self):
        """Return a set of symbols contained in self.
        """
        found = set()  # 初始化一个空集合用于存放找到的符号

        def visit(expr, found=found):
            if expr.op is Op.SYMBOL:  # 如果表达式的操作是符号类型
                found.add(expr)  # 将符号添加到集合中

        self.traverse(visit)  # 调用对象的 traverse 方法,传入 visit 函数进行遍历

        return found  # 返回找到的符号集合

    def polynomial_atoms(self):
        """Return a set of expressions used as atoms in polynomial self.
        """
        found = set()  # 初始化一个空集合用于存放找到的原子表达式

        def visit(expr, found=found):
            if expr.op is Op.FACTORS:  # 如果表达式的操作是因子类型
                for b in expr.data:  # 遍历因子表达式列表
                    b.traverse(visit)  # 递归调用 traverse 方法继续遍历子表达式
                return expr  # 返回当前表达式对象
            if expr.op in (Op.TERMS, Op.COMPLEX):  # 如果操作是项或复合类型,则直接返回
                return
            if expr.op is Op.APPLY and isinstance(expr.data[0], ArithOp):  # 如果是应用操作且第一个数据项是算术操作
                if expr.data[0] is ArithOp.POW:  # 如果是乘方操作
                    expr.data[1][0].traverse(visit)  # 遍历乘方操作的指数部分
                    return expr  # 返回当前表达式对象
                return
            if expr.op in (Op.INTEGER, Op.REAL):  # 如果是整数或实数类型
                return expr  # 直接返回表达式对象

            found.add(expr)  # 将当前表达式对象添加到集合中

            if expr.op in (Op.INDEXING, Op.APPLY):  # 如果是索引或应用操作
                return expr  # 返回当前表达式对象

        self.traverse(visit)  # 调用对象的 traverse 方法,传入 visit 函数进行遍历

        return found  # 返回找到的原子表达式集合

    def linear_solve(self, symbol):
        """Return a, b such that a * symbol + b == self.

        If self is not linear with respect to symbol, raise RuntimeError.
        """
        b = self.substitute({symbol: as_number(0)})  # 将 symbol 替换为 0,得到常数项 b
        ax = self - b  # 计算 self 减去常数项 b,得到 a * symbol
        a = ax.substitute({symbol: as_number(1)})  # 将 symbol 替换为 1,得到系数 a

        zero, _ = as_numer_denom(a * symbol - ax)  # 计算 a * symbol - ax 的分子,并获取分母

        if zero != as_number(0):  # 如果分子不为零
            raise RuntimeError(f'not a {symbol}-linear equation:'
                               f' {a} * {symbol} + {b} == {self}')  # 抛出运行时错误,指示不是线性方程
        return a, b  # 返回计算得到的系数 a 和常数项 b
# 将给定对象标准化并应用基本的求值方法
def normalize(obj):
    # 如果对象不是表达式类型,则直接返回该对象
    if not isinstance(obj, Expr):
        return obj

    # 处理表达式类型为 TERMS 的情况
    if obj.op is Op.TERMS:
        d = {}
        # 遍历表达式数据中的每个项和系数
        for t, c in obj.data.items():
            # 如果系数为0,则跳过该项
            if c == 0:
                continue
            # 如果项的操作类型为 COMPLEX 且系数不为1,则更新项和系数
            if t.op is Op.COMPLEX and c != 1:
                t = t * c
                c = 1
            # 如果项的操作类型为 TERMS,则进一步处理其数据项
            if t.op is Op.TERMS:
                for t1, c1 in t.data.items():
                    _pairs_add(d, t1, c1 * c)
            else:
                _pairs_add(d, t, c)
        # 如果字典 d 为空,则返回数字0
        if len(d) == 0:
            # TODO: 确定正确的类型
            return as_number(0)
        # 如果字典 d 只包含一个项,则返回该项
        elif len(d) == 1:
            (t, c), = d.items()
            if c == 1:
                return t
        # 否则返回一个表达式对象,操作类型为 TERMS,数据为字典 d
        return Expr(Op.TERMS, d)

    # 处理表达式类型为 FACTORS 的情况
    if obj.op is Op.FACTORS:
        coeff = 1
        d = {}
        # 遍历表达式数据中的每个因子和指数
        for b, e in obj.data.items():
            # 如果指数为0,则跳过该因子
            if e == 0:
                continue
            # 如果因子的操作类型为 TERMS,且指数为正整数大于1,则展开整数幂
            if b.op is Op.TERMS and isinstance(e, integer_types) and e > 1:
                b = b * (b ** (e - 1))
                e = 1

            # 处理因子操作类型为 INTEGER 或 REAL 的情况
            if b.op in (Op.INTEGER, Op.REAL):
                if e == 1:
                    coeff *= b.data[0]
                elif e > 0:
                    coeff *= b.data[0] ** e
                else:
                    _pairs_add(d, b, e)
            # 处理因子操作类型为 FACTORS 的情况
            elif b.op is Op.FACTORS:
                if e > 0 and isinstance(e, integer_types):
                    for b1, e1 in b.data.items():
                        _pairs_add(d, b1, e1 * e)
                else:
                    _pairs_add(d, b, e)
            else:
                _pairs_add(d, b, e)
        
        # 如果字典 d 为空或者系数为0,则返回数字0
        if len(d) == 0 or coeff == 0:
            # TODO: 确定正确的类型
            assert isinstance(coeff, number_types)
            return as_number(coeff)
        # 如果字典 d 只包含一个项,则根据情况返回该项或者表达式对象
        elif len(d) == 1:
            (b, e), = d.items()
            if e == 1:
                t = b
            else:
                t = Expr(Op.FACTORS, d)
            if coeff == 1:
                return t
            return Expr(Op.TERMS, {t: coeff})
        # 根据系数是否为1返回表达式对象,操作类型为 FACTORS 或 TERMS
        elif coeff == 1:
            return Expr(Op.FACTORS, d)
        else:
            return Expr(Op.TERMS, {Expr(Op.FACTORS, d): coeff})
    # 如果对象的操作符是 Op.APPLY,且第一个数据元素是 ArithOp.DIV
    if obj.op is Op.APPLY and obj.data[0] is ArithOp.DIV:
        # 将被除数和除数分配给相应变量
        dividend, divisor = obj.data[1]
        # 将被除数和除数分别转换为项和系数
        t1, c1 = as_term_coeff(dividend)
        t2, c2 = as_term_coeff(divisor)
        
        # 如果系数 c1 和 c2 都是整数类型,则计算它们的最大公约数,并进行化简
        if isinstance(c1, integer_types) and isinstance(c2, integer_types):
            g = gcd(c1, c2)
            c1, c2 = c1//g, c2//g
        else:
            # 如果 c1 或 c2 不是整数类型,则进行浮点数的除法
            c1, c2 = c1/c2, 1
        
        # 如果 t1 的操作符是 Op.APPLY,且第一个数据元素是 ArithOp.DIV
        if t1.op is Op.APPLY and t1.data[0] is ArithOp.DIV:
            # 计算分子和分母
            numer = t1.data[1][0] * c1
            denom = t1.data[1][1] * t2 * c2
            # 返回应用 ArithOp.DIV 操作符的表达式
            return as_apply(ArithOp.DIV, numer, denom)
        
        # 如果 t2 的操作符是 Op.APPLY,且第一个数据元素是 ArithOp.DIV
        if t2.op is Op.APPLY and t2.data[0] is ArithOp.DIV:
            # 计算分子和分母
            numer = t2.data[1][1] * t1 * c1
            denom = t2.data[1][0] * c2
            # 返回应用 ArithOp.DIV 操作符的表达式
            return as_apply(ArithOp.DIV, numer, denom)
        
        # 将 t1 和 t2 分解为因子,并构建因子字典
        d = dict(as_factors(t1).data)
        for b, e in as_factors(t2).data.items():
            _pairs_add(d, b, -e)
        
        # 分别构建分子和分母的字典,正负指数对应分子和分母
        numer, denom = {}, {}
        for b, e in d.items():
            if e > 0:
                numer[b] = e
            else:
                denom[b] = -e
        
        # 将分子和分母字典构建为 Op.FACTORS 操作的表达式,然后进行归一化
        numer = normalize(Expr(Op.FACTORS, numer)) * c1
        denom = normalize(Expr(Op.FACTORS, denom)) * c2
        
        # 如果分母是整数或实数类型,并且值为 1,则返回分子
        if denom.op in (Op.INTEGER, Op.REAL) and denom.data[0] == 1:
            # TODO: denom kind not used
            return numer
        
        # 返回应用 ArithOp.DIV 操作符的表达式
        return as_apply(ArithOp.DIV, numer, denom)
    
    # 如果对象的操作符是 Op.CONCAT
    if obj.op is Op.CONCAT:
        # 初始化列表,包含第一个元素
        lst = [obj.data[0]]
        # 遍历剩余元素
        for s in obj.data[1:]:
            last = lst[-1]
            # 如果最后一个元素和当前元素都是字符串,且可以拼接
            if (
                    last.op is Op.STRING
                    and s.op is Op.STRING
                    and last.data[0][0] in '"\''
                    and s.data[0][0] == last.data[0][-1]
            ):
                # 创建新的字符串表达式,并替换最后一个元素
                new_last = as_string(last.data[0][:-1] + s.data[0][1:],
                                     max(last.data[1], s.data[1]))
                lst[-1] = new_last
            else:
                # 否则直接添加当前元素到列表中
                lst.append(s)
        
        # 如果列表只有一个元素,则返回该元素
        if len(lst) == 1:
            return lst[0]
        
        # 返回 Op.CONCAT 操作的表达式,包含所有拼接后的元素
        return Expr(Op.CONCAT, tuple(lst))
    
    # 如果对象的操作符是 Op.TERNARY
    if obj.op is Op.TERNARY:
        # 将条件、表达式1和表达式2进行归一化处理
        cond, expr1, expr2 = map(normalize, obj.data)
        # 如果条件是整数类型,则根据条件的值返回相应表达式
        if cond.op is Op.INTEGER:
            return expr1 if cond.data[0] else expr2
        # 否则返回 Op.TERNARY 操作的表达式,包含归一化后的条件和表达式
        return Expr(Op.TERNARY, (cond, expr1, expr2))
    
    # 默认情况下,直接返回对象本身
    return obj
# 将非 Expr 类型的对象转换为 Expr 对象。
def as_expr(obj):
    if isinstance(obj, complex):
        # 如果 obj 是复数类型,则调用 as_complex 转换为复数表达式
        return as_complex(obj.real, obj.imag)
    if isinstance(obj, number_types):
        # 如果 obj 是数字类型,则调用 as_number 转换为数字表达式
        return as_number(obj)
    if isinstance(obj, str):
        # 如果 obj 是字符串类型,则将其应用 repr 函数转换为带有引号的字符串表达式
        return as_string(repr(obj))
    if isinstance(obj, tuple):
        # 如果 obj 是元组类型,则逐个调用 as_expr 转换为表达式元组
        return tuple(map(as_expr, obj))
    # 对于其他类型的对象,直接返回该对象
    return obj


# 将对象转换为 SYMBOL 表达式(变量或未解析表达式)。
def as_symbol(obj):
    return Expr(Op.SYMBOL, obj)


# 将对象转换为 INTEGER 或 REAL 常量。
def as_number(obj, kind=4):
    if isinstance(obj, int):
        # 如果 obj 是整数类型,则返回 INTEGER 表达式
        return Expr(Op.INTEGER, (obj, kind))
    if isinstance(obj, float):
        # 如果 obj 是浮点数类型,则返回 REAL 表达式
        return Expr(Op.REAL, (obj, kind))
    if isinstance(obj, Expr):
        # 如果 obj 已经是表达式类型,则检查其类型,如果是 INTEGER 或 REAL 直接返回
        if obj.op in (Op.INTEGER, Op.REAL):
            return obj
    # 对于无法转换的情况,抛出 OpError 异常
    raise OpError(f'cannot convert {obj} to INTEGER or REAL constant')


# 将对象转换为 INTEGER 常量。
def as_integer(obj, kind=4):
    if isinstance(obj, int):
        return Expr(Op.INTEGER, (obj, kind))
    if isinstance(obj, Expr):
        if obj.op is Op.INTEGER:
            return obj
    raise OpError(f'cannot convert {obj} to INTEGER constant')


# 将对象转换为 REAL 常量。
def as_real(obj, kind=4):
    if isinstance(obj, int):
        # 如果 obj 是整数类型,则转换为 REAL 表达式
        return Expr(Op.REAL, (float(obj), kind))
    if isinstance(obj, float):
        # 如果 obj 是浮点数类型,则转换为 REAL 表达式
        return Expr(Op.REAL, (obj, kind))
    if isinstance(obj, Expr):
        # 如果 obj 是表达式类型,则根据其类型进行转换
        if obj.op is Op.REAL:
            return obj
        elif obj.op is Op.INTEGER:
            # 如果 obj 是 INTEGER 表达式,则将其转换为 REAL 表达式
            return Expr(Op.REAL, (float(obj.data[0]), kind))
    # 对于无法转换的情况,抛出 OpError 异常
    raise OpError(f'cannot convert {obj} to REAL constant')


# 将对象转换为 STRING 表达式(字符串字面常量)。
def as_string(obj, kind=1):
    return Expr(Op.STRING, (obj, kind))


# 将对象转换为 ARRAY 表达式(数组常量)。
def as_array(obj):
    if isinstance(obj, Expr):
        # 如果 obj 已经是表达式类型,则将其转换为元组
        obj = obj,
    return Expr(Op.ARRAY, obj)


# 将对象转换为 COMPLEX 表达式(复数字面常量)。
def as_complex(real, imag=0):
    return Expr(Op.COMPLEX, (as_expr(real), as_expr(imag)))


# 将对象转换为 APPLY 表达式(函数调用、构造函数等)。
def as_apply(func, *args, **kwargs):
    return Expr(Op.APPLY,
                (func, tuple(map(as_expr, args)),
                 dict((k, as_expr(v)) for k, v in kwargs.items())))


# 将对象转换为 TERNARY 表达式(三元表达式 cond?expr1:expr2)。
def as_ternary(cond, expr1, expr2):
    return Expr(Op.TERNARY, (cond, expr1, expr2))


# 将对象转换为引用表达式。
def as_ref(expr):
    return Expr(Op.REF, expr)


# 将对象转换为解引用表达式。
def as_deref(expr):
    return Expr(Op.DEREF, expr)


# 返回等于(==)关系表达式。
def as_eq(left, right):
    return Expr(Op.RELATIONAL, (RelOp.EQ, left, right))
# 返回一个不等于关系的表达式对象
def as_ne(left, right):
    return Expr(Op.RELATIONAL, (RelOp.NE, left, right))

# 返回一个小于关系的表达式对象
def as_lt(left, right):
    return Expr(Op.RELATIONAL, (RelOp.LT, left, right))

# 返回一个小于等于关系的表达式对象
def as_le(left, right):
    return Expr(Op.RELATIONAL, (RelOp.LE, left, right))

# 返回一个大于关系的表达式对象
def as_gt(left, right):
    return Expr(Op.RELATIONAL, (RelOp.GT, left, right))

# 返回一个大于等于关系的表达式对象
def as_ge(left, right):
    return Expr(Op.RELATIONAL, (RelOp.GE, left, right))

# 将给定的表达式对象转换为TERMS表达式对象
def as_terms(obj):
    """Return expression as TERMS expression.
    """
    if isinstance(obj, Expr):
        obj = normalize(obj)
        # 如果表达式已经是TERMS类型,则直接返回
        if obj.op is Op.TERMS:
            return obj
        # 如果表达式是INTEGER类型,则转换为TERMS类型
        if obj.op is Op.INTEGER:
            return Expr(Op.TERMS, {as_integer(1, obj.data[1]): obj.data[0]})
        # 如果表达式是REAL类型,则转换为TERMS类型
        if obj.op is Op.REAL:
            return Expr(Op.TERMS, {as_real(1, obj.data[1]): obj.data[0]})
        # 否则,默认将表达式转换为一个包含单个项的TERMS表达式
        return Expr(Op.TERMS, {obj: 1})
    # 如果不是Expr类型,则抛出异常
    raise OpError(f'cannot convert {type(obj)} to terms Expr')

# 将给定的表达式对象转换为FACTORS表达式对象
def as_factors(obj):
    """Return expression as FACTORS expression.
    """
    if isinstance(obj, Expr):
        obj = normalize(obj)
        # 如果表达式已经是FACTORS类型,则直接返回
        if obj.op is Op.FACTORS:
            return obj
        # 如果表达式是TERMS类型,且只包含一个项,则转换为FACTORS类型
        if obj.op is Op.TERMS:
            if len(obj.data) == 1:
                (term, coeff), = obj.data.items()
                if coeff == 1:
                    return Expr(Op.FACTORS, {term: 1})
                return Expr(Op.FACTORS, {term: 1, Expr.number(coeff): 1})
        # 如果表达式是APPLY类型,并且是除法操作,则转换为FACTORS类型
        if (obj.op is Op.APPLY
            and obj.data[0] is ArithOp.DIV
            and not obj.data[2]):
            return Expr(Op.FACTORS, {obj.data[1][0]: 1, obj.data[1][1]: -1})
        # 否则,默认将表达式转换为一个包含单个因子的FACTORS表达式
        return Expr(Op.FACTORS, {obj: 1})
    # 如果不是Expr类型,则抛出异常
    raise OpError(f'cannot convert {type(obj)} to terms Expr')

# 将给定的表达式对象转换为项-系数对形式
def as_term_coeff(obj):
    """Return expression as term-coefficient pair.
    """
    if isinstance(obj, Expr):
        obj = normalize(obj)
        # 如果表达式是INTEGER类型,则转换为项-系数对形式
        if obj.op is Op.INTEGER:
            return as_integer(1, obj.data[1]), obj.data[0]
        # 如果表达式是REAL类型,则转换为项-系数对形式
        if obj.op is Op.REAL:
            return as_real(1, obj.data[1]), obj.data[0]
        # 如果表达式是TERMS类型,且只包含一个项,则返回项和系数
        if obj.op is Op.TERMS:
            if len(obj.data) == 1:
                (term, coeff), = obj.data.items()
                return term, coeff
            # TODO: 找到系数的最大公约数
        # 如果表达式是APPLY类型,并且是除法操作,则转换为项-系数对形式
        if obj.op is Op.APPLY and obj.data[0] is ArithOp.DIV:
            t, c = as_term_coeff(obj.data[1][0])
            return as_apply(ArithOp.DIV, t, obj.data[1][1]), c
        # 否则,默认将表达式直接返回,并假设系数为1
        return obj, 1
    # 如果不是Expr类型,则抛出异常
    raise OpError(f'cannot convert {type(obj)} to term and coeff')

# 将给定的表达式对象转换为数值-分母对形式
def as_numer_denom(obj):
    """Return expression as numer-denom pair.
    """
    # 检查对象是否属于表达式类
    if isinstance(obj, Expr):
        # 对象规范化,确保其符合标准形式
        obj = normalize(obj)
        # 检查操作符是否属于以下类型之一:整数、实数、复数、符号、索引、三元操作符
        if obj.op in (Op.INTEGER, Op.REAL, Op.COMPLEX, Op.SYMBOL,
                      Op.INDEXING, Op.TERNARY):
            # 返回对象和数值 1
            return obj, as_number(1)
        # 如果操作符是应用操作
        elif obj.op is Op.APPLY:
            # 如果对象表示的是除法且第三个数据为空
            if obj.data[0] is ArithOp.DIV and not obj.data[2]:
                # 将操作数转换为分子和分母,然后返回其交叉乘积
                numers, denoms = map(as_numer_denom, obj.data[1])
                return numers[0] * denoms[1], numers[1] * denoms[0]
            # 返回对象和数值 1
            return obj, as_number(1)
        # 如果操作符是项操作
        elif obj.op is Op.TERMS:
            # 初始化数值列表
            numers, denoms = [], []
            # 遍历项数据中的每一项及其系数
            for term, coeff in obj.data.items():
                # 将每个项转换为分子和分母形式
                n, d = as_numer_denom(term)
                # 根据系数调整分子值,并添加到分子列表中
                n = n * coeff
                numers.append(n)
                # 添加分母到分母列表中
                denoms.append(d)
            # 初始化数值并计算总分子和总分母
            numer, denom = as_number(0), as_number(1)
            for i in range(len(numers)):
                n = numers[i]
                for j in range(len(numers)):
                    if i != j:
                        # 对非当前项的分母应用乘法
                        n *= denoms[j]
                # 添加到总分子中
                numer += n
                # 对所有分母应用乘法
                denom *= denoms[i]
            # 如果总分母为整数或实数且为负数,则调整总分子和总分母的符号
            if denom.op in (Op.INTEGER, Op.REAL) and denom.data[0] < 0:
                numer, denom = -numer, -denom
            # 返回最终计算结果的分子和分母
            return numer, denom
        # 如果操作符是因子操作
        elif obj.op is Op.FACTORS:
            # 初始化分子和分母为 1
            numer, denom = as_number(1), as_number(1)
            # 遍历因子数据中的每一对底数和指数
            for b, e in obj.data.items():
                # 将每个底数转换为分子和分母形式
                bnumer, bdenom = as_numer_denom(b)
                # 根据指数值调整分子和分母
                if e > 0:
                    numer *= bnumer ** e
                    denom *= bdenom ** e
                elif e < 0:
                    numer *= bdenom ** (-e)
                    denom *= bnumer ** (-e)
            # 返回最终计算结果的分子和分母
            return numer, denom
    # 如果对象类型无法转换为分子和分母,则引发操作错误异常
    raise OpError(f'cannot convert {type(obj)} to numer and denom')
def _counter():
    # Used internally to generate unique dummy symbols
    counter = 0
    while True:
        counter += 1
        yield counter

# Initialize a global counter generator
COUNTER = _counter()

def eliminate_quotes(s):
    """Replace quoted substrings of input string.

    Return a new string and a mapping of replacements.
    """
    d = {}

    def repl(m):
        kind, value = m.groups()[:2]
        if kind:
            # remove trailing underscore
            kind = kind[:-1]
        # Determine if the quote is single or double and create a unique key
        p = {"'": "SINGLE", '"': "DOUBLE"}[value[0]]
        k = f'{kind}@__f2py_QUOTES_{p}_{COUNTER.__next__()}@'
        d[k] = value
        return k

    # Replace quoted substrings in the input string 's' using the repl function
    new_s = re.sub(r'({kind}_|)({single_quoted}|{double_quoted})'.format(
        kind=r'\w[\w\d_]*',
        single_quoted=r"('([^'\\]|(\\.))*')",
        double_quoted=r'("([^"\\]|(\\.))*")'),
        repl, s)

    # Ensure no quotes remain in the new string
    assert '"' not in new_s
    assert "'" not in new_s

    return new_s, d

def insert_quotes(s, d):
    """Inverse of eliminate_quotes.
    """
    # Replace the unique keys back with their original quoted values in string 's'
    for k, v in d.items():
        kind = k[:k.find('@')]
        if kind:
            kind += '_'
        s = s.replace(k, kind + v)
    return s

def replace_parenthesis(s):
    """Replace substrings of input that are enclosed in parenthesis.

    Return a new string and a mapping of replacements.
    """
    left, right = None, None
    mn_i = len(s)

    # Iterate through possible parenthesis pairs to find the first occurrence
    for left_, right_ in (('(/', '/)'), '()', '{}', '[]'):
        i = s.find(left_)
        if i == -1:
            continue
        if i < mn_i:
            mn_i = i
            left, right = left_, right_

    # If no parenthesis pairs are found, return original string and empty dictionary
    if left is None:
        return s, {}

    i = mn_i
    j = s.find(right, i)

    # Ensure balanced parenthesis within the found pair
    while s.count(left, i + 1, j) != s.count(right, i + 1, j):
        j = s.find(right, j + 1)
        if j == -1:
            raise ValueError(f'Mismatch of {left+right} parenthesis in {s!r}')

    # Determine the type of parenthesis and create a unique key
    p = {'(': 'ROUND', '[': 'SQUARE', '{': 'CURLY', '(/': 'ROUNDDIV'}[left]
    k = f'@__f2py_PARENTHESIS_{p}_{COUNTER.__next__()}@'
    v = s[i+len(left):j]

    # Recursively replace parenthesis in the remainder of the string
    r, d = replace_parenthesis(s[j+len(right):])
    d[k] = v
    return s[:i] + k + r, d

def _get_parenthesis_kind(s):
    assert s.startswith('@__f2py_PARENTHESIS_'), s
    return s.split('_')[4]

def unreplace_parenthesis(s, d):
    """Inverse of replace_parenthesis.
    """
    # Replace unique keys with their respective parenthesis and enclosed values
    for k, v in d.items():
        p = _get_parenthesis_kind(k)
        left = dict(ROUND='(', SQUARE='[', CURLY='{', ROUNDDIV='(/')[p]
        right = dict(ROUND=')', SQUARE=']', CURLY='}', ROUNDDIV='/)')[p]
        s = s.replace(k, left + v + right)
    return s

def fromstring(s, language=Language.C):
    """Create an expression from a string.

    This is a "lazy" parser, that is, only arithmetic operations are
    resolved, non-arithmetic operations are treated as symbols.
    """
    # 使用指定的语言(language)创建一个 _FromStringWorker 实例,并解析字符串 s
    r = _FromStringWorker(language=language).parse(s)
    # 如果解析结果 r 是 Expr 类型的对象,则直接返回它
    if isinstance(r, Expr):
        return r
    # 如果解析结果 r 不是 Expr 类型的对象,则抛出 ValueError 异常,指示解析失败
    raise ValueError(f'failed to parse `{s}` to Expr instance: got `{r}`')
class _Pair:
    # Internal class to represent a pair of expressions

    def __init__(self, left, right):
        # Constructor to initialize a _Pair object with left and right expressions
        self.left = left
        self.right = right

    def substitute(self, symbols_map):
        # Method to substitute expressions with symbols from symbols_map
        left, right = self.left, self.right
        if isinstance(left, Expr):
            left = left.substitute(symbols_map)
        if isinstance(right, Expr):
            right = right.substitute(symbols_map)
        return _Pair(left, right)

    def __repr__(self):
        # Returns a string representation of the _Pair object
        return f'{type(self).__name__}({self.left}, {self.right})'


class _FromStringWorker:

    def __init__(self, language=Language.C):
        # Constructor to initialize _FromStringWorker object with optional language parameter
        self.original = None
        self.quotes_map = None
        self.language = language

    def finalize_string(self, s):
        # Method to finalize string by inserting quotes according to quotes_map
        return insert_quotes(s, self.quotes_map)

    def parse(self, inp):
        # Method to parse input string inp
        self.original = inp
        # Eliminate quotes from inp, store unquoted version in unquoted, quotes mapping in quotes_map
        unquoted, self.quotes_map = eliminate_quotes(inp)
        # Process the unquoted string and return the result
        return self.process(unquoted)

.\numpy\numpy\f2py\tests\src\array_from_pyobj\wrapmodule.c

/*
 * This file was auto-generated with f2py (version:2_1330) and hand edited by
 * Pearu for testing purposes.  Do not edit this file unless you know what you
 * are doing!!!
 */

#ifdef __cplusplus
extern "C" {
#endif

/*********************** See f2py2e/cfuncs.py: includes ***********************/

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "fortranobject.h"
#include <math.h>

// 定义包裹错误和包裹模块对象
static PyObject *wrap_error;
static PyObject *wrap_module;

/************************************ call ************************************/
// 函数签名文档字符串
static char doc_f2py_rout_wrap_call[] = "\
Function signature:\n\
  arr = call(type_num,dims,intent,obj)\n\
Required arguments:\n"
"  type_num : input int\n"
"  dims : input int-sequence\n"
"  intent : input int\n"
"  obj : input python object\n"
"Return objects:\n"
"  arr : array";
// 包裹函数,调用Fortran函数并返回结果
static PyObject *f2py_rout_wrap_call(PyObject *capi_self,
                                     PyObject *capi_args) {
  PyObject * volatile capi_buildvalue = NULL;
  int type_num = 0;
  int elsize = 0;
  npy_intp *dims = NULL;
  PyObject *dims_capi = Py_None;
  int rank = 0;
  int intent = 0;
  PyArrayObject *capi_arr_tmp = NULL;
  PyObject *arr_capi = Py_None;
  int i;

  // 解析参数元组
  if (!PyArg_ParseTuple(capi_args,"iiOiO|:wrap.call",\
                        &type_num,&elsize,&dims_capi,&intent,&arr_capi))
    return NULL;
  // 获取序列的长度作为数组的维度
  rank = PySequence_Length(dims_capi);
  dims = malloc(rank*sizeof(npy_intp));
  // 遍历序列,获取每个维度的值
  for (i=0;i<rank;++i) {
    PyObject *tmp;
    tmp = PySequence_GetItem(dims_capi, i);
    if (tmp == NULL) {
        goto fail;
    }
    dims[i] = (npy_intp)PyLong_AsLong(tmp);
    Py_DECREF(tmp);
    if (dims[i] == -1 && PyErr_Occurred()) {
        goto fail;
    }
  }
  // 调用ndarray_from_pyobj函数,将Python对象转换为NumPy数组对象
  capi_arr_tmp = ndarray_from_pyobj(type_num,elsize,dims,rank,intent|F2PY_INTENT_OUT,arr_capi,"wrap.call failed");
  if (capi_arr_tmp == NULL) {
    free(dims);
    return NULL;
  }
  // 构建返回值,包含NumPy数组对象
  capi_buildvalue = Py_BuildValue("N",capi_arr_tmp);
  free(dims);
  return capi_buildvalue;

fail:
  free(dims);
  return NULL;
}

/************************************ attrs ************************************/
// 函数签名文档字符串
static char doc_f2py_rout_wrap_attrs[] = "\
Function signature:\n\
  arr = array_attrs(arr)\n\
Required arguments:\n"
"  arr : input array object\n"
"Return objects:\n"
"  data : data address in hex\n"
"  nd : int\n"
"  dimensions : tuple\n"
"  strides : tuple\n"
"  base : python object\n"
"  (kind,type,type_num,elsize,alignment) : 4-tuple\n"
"  flags : int\n"
"  itemsize : int\n"
;
// 包裹函数,返回NumPy数组对象的属性信息
static PyObject *f2py_rout_wrap_attrs(PyObject *capi_self,
                                      PyObject *capi_args) {
  PyObject *arr_capi = Py_None;
  PyArrayObject *arr = NULL;
  PyObject *dimensions = NULL;
  PyObject *strides = NULL;
  char s[100];
  int i;
  memset(s,0,100);
  // 解析参数元组,验证输入参数是否正确
  if (!PyArg_ParseTuple(capi_args,"O!|:wrap.attrs",
                        &PyArray_Type,&arr_capi))
    return NULL;
  // 将Python对象转换为NumPy数组对象
  arr = (PyArrayObject *)arr_capi;
  // 将数据地址转换为十六进制字符串
  sprintf(s,"%p",PyArray_DATA(arr));
  // 创建维度元组和步长元组
  dimensions = PyTuple_New(PyArray_NDIM(arr));
  strides = PyTuple_New(PyArray_NDIM(arr));
  // 遍历数组的维度,获取每个维度和步长
  for (i=0;i<PyArray_NDIM(arr);++i) {
    # 设置元组中的第 i 个元素为 PyArray_DIM(arr, i) 返回的长整型对象
    PyTuple_SetItem(dimensions, i, PyLong_FromLong(PyArray_DIM(arr, i)));
    # 设置元组中的第 i 个元素为 PyArray_STRIDE(arr, i) 返回的长整型对象
    PyTuple_SetItem(strides, i, PyLong_FromLong(PyArray_STRIDE(arr, i)));
  }
  # 构建一个包含多个值的 Python 元组对象,并返回该元组对象
  return Py_BuildValue("siNNO(cciii)ii", s, PyArray_NDIM(arr),
                       dimensions, strides,
                       (PyArray_BASE(arr)==NULL ? Py_None : PyArray_BASE(arr)),
                       PyArray_DESCR(arr)->kind,
                       PyArray_DESCR(arr)->type,
                       PyArray_TYPE(arr),
                       PyArray_ITEMSIZE(arr),
                       PyArray_DESCR(arr)->alignment,
                       PyArray_FLAGS(arr),
                       PyArray_ITEMSIZE(arr));
}

static PyMethodDef f2py_module_methods[] = {
    // 定义 Python 模块中的方法列表,每个条目包含方法名、函数指针、参数类型和文档字符串
    {"call", f2py_rout_wrap_call, METH_VARARGS, doc_f2py_rout_wrap_call},
    {"array_attrs", f2py_rout_wrap_attrs, METH_VARARGS, doc_f2py_rout_wrap_attrs},
    {NULL, NULL}  // 方法列表结束标记
};

static struct PyModuleDef moduledef = {
    PyModuleDef_HEAD_INIT,  // 初始化 Python 模块定义结构
    "test_array_from_pyobj_ext",  // 模块名称
    NULL,  // 模块文档字符串(此处为NULL)
    -1,    // 模块状态(此处为-1)
    f2py_module_methods,  // 指向模块方法列表的指针
    NULL,  // 模块的全局状态
    NULL,  // 模块的方法定义结构
    NULL,  // 模块的每个方法的文档字符串
    NULL   // 模块的清理函数
};

PyMODINIT_FUNC PyInit_test_array_from_pyobj_ext(void) {
    PyObject *m, *d, *s;
    // 创建 Python 模块对象,并将其赋值给变量 m 和 wrap_module
    m = wrap_module = PyModule_Create(&moduledef);
    // 将 PyFortran_Type 类型设置为 PyType_Type 类型
    Py_SET_TYPE(&PyFortran_Type, &PyType_Type);
    // 导入 NumPy 库
    import_array();
    // 检查是否有异常发生,若有则致命错误
    if (PyErr_Occurred())
        Py_FatalError("can't initialize module wrap (failed to import numpy)");
    // 获取模块的字典对象
    d = PyModule_GetDict(m);
    // 创建包含模块说明的 Unicode 字符串对象,并设置为模块的 __doc__ 属性
    s = PyUnicode_FromString("This module 'wrap' is auto-generated with f2py (version:2_1330).\nFunctions:\n"
                             "  arr = call(type_num,dims,intent,obj)\n"
                             ".");
    PyDict_SetItemString(d, "__doc__", s);
    // 创建 wrap.error 异常并设置到模块的全局字典中
    wrap_error = PyErr_NewException("wrap.error", NULL, NULL);
    Py_DECREF(s);

    // 定义宏 ADDCONST,用于向模块的字典中添加常量
#define ADDCONST(NAME, CONST)              \
    s = PyLong_FromLong(CONST);             \
    PyDict_SetItemString(d, NAME, s);      \

    // 以下代码可能继续定义各种常量,并添加到模块字典中
  // 释放 Python 对象 s 的引用计数
  Py_DECREF(s)

  // 添加常量 "F2PY_INTENT_IN" 到模块中
  ADDCONST("F2PY_INTENT_IN", F2PY_INTENT_IN);
  // 添加常量 "F2PY_INTENT_INOUT" 到模块中
  ADDCONST("F2PY_INTENT_INOUT", F2PY_INTENT_INOUT);
  // 添加常量 "F2PY_INTENT_OUT" 到模块中
  ADDCONST("F2PY_INTENT_OUT", F2PY_INTENT_OUT);
  // 添加常量 "F2PY_INTENT_HIDE" 到模块中
  ADDCONST("F2PY_INTENT_HIDE", F2PY_INTENT_HIDE);
  // 添加常量 "F2PY_INTENT_CACHE" 到模块中
  ADDCONST("F2PY_INTENT_CACHE", F2PY_INTENT_CACHE);
  // 添加常量 "F2PY_INTENT_COPY" 到模块中
  ADDCONST("F2PY_INTENT_COPY", F2PY_INTENT_COPY);
  // 添加常量 "F2PY_INTENT_C" 到模块中
  ADDCONST("F2PY_INTENT_C", F2PY_INTENT_C);
  // 添加常量 "F2PY_OPTIONAL" 到模块中
  ADDCONST("F2PY_OPTIONAL", F2PY_OPTIONAL);
  // 添加常量 "F2PY_INTENT_INPLACE" 到模块中
  ADDCONST("F2PY_INTENT_INPLACE", F2PY_INTENT_INPLACE);
  // 添加常量 "NPY_BOOL" 到模块中
  ADDCONST("NPY_BOOL", NPY_BOOL);
  // 添加常量 "NPY_BYTE" 到模块中
  ADDCONST("NPY_BYTE", NPY_BYTE);
  // 添加常量 "NPY_UBYTE" 到模块中
  ADDCONST("NPY_UBYTE", NPY_UBYTE);
  // 添加常量 "NPY_SHORT" 到模块中
  ADDCONST("NPY_SHORT", NPY_SHORT);
  // 添加常量 "NPY_USHORT" 到模块中
  ADDCONST("NPY_USHORT", NPY_USHORT);
  // 添加常量 "NPY_INT" 到模块中
  ADDCONST("NPY_INT", NPY_INT);
  // 添加常量 "NPY_UINT" 到模块中
  ADDCONST("NPY_UINT", NPY_UINT);
  // 添加常量 "NPY_INTP" 到模块中
  ADDCONST("NPY_INTP", NPY_INTP);
  // 添加常量 "NPY_UINTP" 到模块中
  ADDCONST("NPY_UINTP", NPY_UINTP);
  // 添加常量 "NPY_LONG" 到模块中
  ADDCONST("NPY_LONG", NPY_LONG);
  // 添加常量 "NPY_ULONG" 到模块中
  ADDCONST("NPY_ULONG", NPY_ULONG);
  // 添加常量 "NPY_LONGLONG" 到模块中
  ADDCONST("NPY_LONGLONG", NPY_LONGLONG);
  // 添加常量 "NPY_ULONGLONG" 到模块中
  ADDCONST("NPY_ULONGLONG", NPY_ULONGLONG);
  // 添加常量 "NPY_FLOAT" 到模块中
  ADDCONST("NPY_FLOAT", NPY_FLOAT);
  // 添加常量 "NPY_DOUBLE" 到模块中
  ADDCONST("NPY_DOUBLE", NPY_DOUBLE);
  // 添加常量 "NPY_LONGDOUBLE" 到模块中
  ADDCONST("NPY_LONGDOUBLE", NPY_LONGDOUBLE);
  // 添加常量 "NPY_CFLOAT" 到模块中
  ADDCONST("NPY_CFLOAT", NPY_CFLOAT);
  // 添加常量 "NPY_CDOUBLE" 到模块中
  ADDCONST("NPY_CDOUBLE", NPY_CDOUBLE);
  // 添加常量 "NPY_CLONGDOUBLE" 到模块中
  ADDCONST("NPY_CLONGDOUBLE", NPY_CLONGDOUBLE);
  // 添加常量 "NPY_OBJECT" 到模块中
  ADDCONST("NPY_OBJECT", NPY_OBJECT);
  // 添加常量 "NPY_STRING" 到模块中
  ADDCONST("NPY_STRING", NPY_STRING);
  // 添加常量 "NPY_UNICODE" 到模块中
  ADDCONST("NPY_UNICODE", NPY_UNICODE);
  // 添加常量 "NPY_VOID" 到模块中
  ADDCONST("NPY_VOID", NPY_VOID);
  // 添加常量 "NPY_NTYPES_LEGACY" 到模块中
  ADDCONST("NPY_NTYPES_LEGACY", NPY_NTYPES_LEGACY);
  // 添加常量 "NPY_NOTYPE" 到模块中
  ADDCONST("NPY_NOTYPE", NPY_NOTYPE);
  // 添加常量 "NPY_USERDEF" 到模块中
  ADDCONST("NPY_USERDEF", NPY_USERDEF);

  // 添加常量 "CONTIGUOUS" 到模块中
  ADDCONST("CONTIGUOUS", NPY_ARRAY_C_CONTIGUOUS);
  // 添加常量 "FORTRAN" 到模块中
  ADDCONST("FORTRAN", NPY_ARRAY_F_CONTIGUOUS);
  // 添加常量 "OWNDATA" 到模块中
  ADDCONST("OWNDATA", NPY_ARRAY_OWNDATA);
  // 添加常量 "FORCECAST" 到模块中
  ADDCONST("FORCECAST", NPY_ARRAY_FORCECAST);
  // 添加常量 "ENSURECOPY" 到模块中
  ADDCONST("ENSURECOPY", NPY_ARRAY_ENSURECOPY);
  // 添加常量 "ENSUREARRAY" 到模块中
  ADDCONST("ENSUREARRAY", NPY_ARRAY_ENSUREARRAY);
  // 添加常量 "ALIGNED" 到模块中
  ADDCONST("ALIGNED", NPY_ARRAY_ALIGNED);
  // 添加常量 "WRITEABLE" 到模块中
  ADDCONST("WRITEABLE", NPY_ARRAY_WRITEABLE);
  // 添加常量 "WRITEBACKIFCOPY" 到模块中
  ADDCONST("WRITEBACKIFCOPY", NPY_ARRAY_WRITEBACKIFCOPY);

  // 添加常量 "BEHAVED" 到模块中
  ADDCONST("BEHAVED", NPY_ARRAY_BEHAVED);
  // 添加常量 "BEHAVED_NS" 到模块中
  ADDCONST("BEHAVED_NS", NPY_ARRAY_BEHAVED_NS);
  // 添加常量 "CARRAY" 到模块中
  ADDCONST("CARRAY", NPY_ARRAY_CARRAY);
  // 添加常量 "FARRAY" 到模块中
  ADDCONST("FARRAY", NPY_ARRAY_FARRAY);
  // 添加常量 "CARRAY_RO" 到模块中
  ADDCONST("CARRAY_RO", NPY_ARRAY_CARRAY_RO);
  // 添加常量 "FARRAY_RO" 到模块中
  ADDCONST("FARRAY_RO", NPY_ARRAY_FARRAY_RO);
  // 添加常量 "DEFAULT" 到模块中
  ADDCONST("DEFAULT", NPY_ARRAY_DEFAULT);
  // 添加常量 "UPDATE_ALL" 到模块中
  ADDCONST("UPDATE_ALL", NPY_ARRAY_UPDATE_ALL);
#undef ADDCONST(

取消定义一个名为ADDCONST的宏。在这里,可能是取消定义某个在此之前定义的宏。


  if (PyErr_Occurred())
    Py_FatalError("can't initialize module wrap");

如果Python中发生了错误,调用Py_FatalError函数,程序将无法初始化模块wrap。


#ifdef F2PY_REPORT_ATEXIT
  on_exit(f2py_report_on_exit,(void*)"array_from_pyobj.wrap.call");
#endif

如果已定义了宏F2PY_REPORT_ATEXIT,则在程序退出时调用on_exit函数,传递f2py_report_on_exit函数和字符串"array_from_pyobj.wrap.call"作为参数。


  return m;
}
#ifdef __cplusplus
}
#endif

返回模块对象m并结束当前函数。如果是C++环境,则关闭extern "C"的语言链接规范。

.\numpy\numpy\f2py\tests\test_abstract_interface.py

# 从 pathlib 模块导入 Path 类,用于处理文件路径
from pathlib import Path
# 导入 pytest 模块,用于编写和运行测试用例
import pytest
# 导入 textwrap 模块,用于处理和操作文本内容的格式
import textwrap
# 从当前包中导入 util 模块,用于测试中的辅助功能
from . import util
# 从 numpy.f2py 模块导入 crackfortran 函数,用于处理 Fortran 代码
from numpy.f2py import crackfortran
# 从 numpy.testing 模块导入 IS_WASM 常量,用于条件判断
from numpy.testing import IS_WASM

# 使用 pytest 的装饰器标记该测试类,当 IS_WASM 为 True 时跳过测试,给出理由
@pytest.mark.skipif(IS_WASM, reason="Cannot start subprocess")
# 标记该测试类为慢速测试
@pytest.mark.slow
class TestAbstractInterface(util.F2PyTest):
    # 定义测试用例的源文件路径列表
    sources = [util.getpath("tests", "src", "abstract_interface", "foo.f90")]

    # 定义需要跳过的测试方法列表
    skip = ["add1", "add2"]

    # 测试抽象接口的方法
    def test_abstract_interface(self):
        # 断言调用 self.module.ops_module.foo(3, 5) 返回结果为 (8, 13)
        assert self.module.ops_module.foo(3, 5) == (8, 13)

    # 测试解析抽象接口的方法
    def test_parse_abstract_interface(self):
        # 测试用例名称为 gh18403
        fpath = util.getpath("tests", "src", "abstract_interface", "gh18403_mod.f90")
        # 调用 crackfortran.crackfortran 函数处理指定路径的 Fortran 文件,返回模块对象列表
        mod = crackfortran.crackfortran([str(fpath)])
        # 断言 mod 列表长度为 1
        assert len(mod) == 1
        # 断言 mod[0]["body"] 列表的长度为 1
        assert len(mod[0]["body"]) == 1
        # 断言 mod[0]["body"][0]["block"] 的值为 "abstract interface"
        assert mod[0]["body"][0]["block"] == "abstract interface"

.\numpy\numpy\f2py\tests\test_array_from_pyobj.py

import os
import sys
import copy
import platform
import pytest
from pathlib import Path

import numpy as np

from numpy.testing import assert_, assert_equal
from numpy._core._type_aliases import c_names_dict as _c_names_dict
from . import util

wrap = None

# 扩展核心类型信息,添加 CHARACTER 类型以测试 dtype('c')
# c_names_dict 是一个字典,将字符类型映射到对应的 NumPy 数据类型
c_names_dict = dict(
    CHARACTER=np.dtype("c"),
    **_c_names_dict
)

# 获取测试目录路径
def get_testdir():
    # 使用当前文件的绝对路径获取其父目录,然后添加 "src/array_from_pyobj" 作为测试根目录的子目录
    testroot = Path(__file__).resolve().parent / "src"
    return testroot / "array_from_pyobj"

# 在模块设置阶段构建必需的测试扩展模块
def setup_module():
    """
    构建必需的测试扩展模块

    """
    global wrap

    if wrap is None:
        # 定义源文件路径列表,包括 "src/array_from_pyobj/wrapmodule.c"
        src = [
            get_testdir() / "wrapmodule.c",
        ]
        # 使用 util.build_meson 函数构建 Meson 构建系统的扩展模块
        wrap = util.build_meson(src, module_name="test_array_from_pyobj_ext")

# 根据数组的 flags 属性获取其信息
def flags_info(arr):
    # 调用 wrap.array_attrs(arr) 获取数组的属性信息中的第 6 项,即 flags
    flags = wrap.array_attrs(arr)[6]
    return flags2names(flags)

# 将 flags 转换为对应的标志名称列表
def flags2names(flags):
    info = []
    for flagname in [
            "CONTIGUOUS",
            "FORTRAN",
            "OWNDATA",
            "ENSURECOPY",
            "ENSUREARRAY",
            "ALIGNED",
            "NOTSWAPPED",
            "WRITEABLE",
            "WRITEBACKIFCOPY",
            "UPDATEIFCOPY",
            "BEHAVED",
            "BEHAVED_RO",
            "CARRAY",
            "FARRAY",
    ]:
        # 检查 flags 中是否存在指定的标志位,若存在则将标志名称加入 info 列表
        if abs(flags) & getattr(wrap, flagname, 0):
            info.append(flagname)
    return info

# 定义 Intent 类,用于管理参数的意图
class Intent:
    def __init__(self, intent_list=[]):
        # 将传入的 intent_list 复制到对象的 intent_list 属性中
        self.intent_list = intent_list[:]
        # 初始化 flags 为 0
        flags = 0
        # 遍历 intent_list 中的每一项
        for i in intent_list:
            # 根据每一项的名称构建相应的标志位并加入 flags
            if i == "optional":
                flags |= wrap.F2PY_OPTIONAL
            else:
                flags |= getattr(wrap, "F2PY_INTENT_" + i.upper())
        # 将构建好的 flags 赋值给对象的 flags 属性
        self.flags = flags

    # 允许通过属性访问 intent_list 的内容
    def __getattr__(self, name):
        name = name.lower()
        if name == "in_":
            name = "in"
        return self.__class__(self.intent_list + [name])

    # 返回对象的字符串表示,格式为 intent(列表内容)
    def __str__(self):
        return "intent(%s)" % (",".join(self.intent_list))

    # 返回对象的表示形式
    def __repr__(self):
        return "Intent(%r)" % (self.intent_list)

    # 检查当前意图是否包含指定的参数意图
    def is_intent(self, *names):
        for name in names:
            if name not in self.intent_list:
                return False
        return True

    # 检查当前意图是否严格包含指定的参数意图
    def is_intent_exact(self, *names):
        return len(self.intent_list) == len(names) and self.is_intent(*names)

# 定义预定义的数据类型名称列表
_type_names = [
    "BOOL",
    "BYTE",
    "UBYTE",
    "SHORT",
    "USHORT",
    "INT",
    "UINT",
    "LONG",
    "ULONG",
    "LONGLONG",
    "ULONGLONG",
    "FLOAT",
    "DOUBLE",
    "CFLOAT",
    "STRING1",
    "STRING5",
    "CHARACTER",
]

# 定义数据类型强制转换字典,将数据类型映射到允许的强制转换列表
_cast_dict = {
    "BOOL": ["BOOL"],
    "BYTE": _cast_dict["BOOL"] + ["BYTE"],
    "UBYTE": _cast_dict["BOOL"] + ["UBYTE"],
    "BYTE": ["BYTE"],
    "UBYTE": ["UBYTE"],
    "SHORT": _cast_dict["BYTE"] + ["UBYTE", "SHORT"],
    "USHORT": _cast_dict["UBYTE"] + ["BYTE", "USHORT"],
    "INT": _cast_dict["SHORT"] + ["USHORT", "INT"],
}
# 将 UINT 类型转换为 USHORT 类型,添加 SHORT 和 UINT 到 UINT 类型的转换字典中
_cast_dict["UINT"] = _cast_dict["USHORT"] + ["SHORT", "UINT"]

# 将 LONG 类型转换为 INT 类型,添加 LONG 到 LONG 类型的转换字典中
_cast_dict["LONG"] = _cast_dict["INT"] + ["LONG"]

# 将 ULONG 类型转换为 UINT 类型,添加 ULONG 到 ULONG 类型的转换字典中
_cast_dict["ULONG"] = _cast_dict["UINT"] + ["ULONG"]

# 将 LONGLONG 类型转换为 LONG 类型,添加 LONGLONG 到 LONGLONG 类型的转换字典中
_cast_dict["LONGLONG"] = _cast_dict["LONG"] + ["LONGLONG"]

# 将 ULONGLONG 类型转换为 ULONG 类型,添加 ULONGLONG 到 ULONGLONG 类型的转换字典中
_cast_dict["ULONGLONG"] = _cast_dict["ULONG"] + ["ULONGLONG"]

# 将 FLOAT 类型转换为 SHORT 类型,添加 USHORT、FLOAT 到 FLOAT 类型的转换字典中
_cast_dict["FLOAT"] = _cast_dict["SHORT"] + ["USHORT", "FLOAT"]

# 将 DOUBLE 类型转换为 INT 类型,添加 UINT、FLOAT、DOUBLE 到 DOUBLE 类型的转换字典中
_cast_dict["DOUBLE"] = _cast_dict["INT"] + ["UINT", "FLOAT", "DOUBLE"]

# 将 CFLOAT 类型转换为 FLOAT 类型,添加 CFLOAT 到 CFLOAT 类型的转换字典中
_cast_dict["CFLOAT"] = _cast_dict["FLOAT"] + ["CFLOAT"]

# 添加 STRING1 到 STRING1 类型的转换字典中
_cast_dict['STRING1'] = ['STRING1']

# 添加 STRING5 到 STRING5 类型的转换字典中
_cast_dict['STRING5'] = ['STRING5']

# 添加 CHARACTER 到 CHARACTER 类型的转换字典中
_cast_dict['CHARACTER'] = ['CHARACTER']

# 检查条件:32位系统的 malloc 通常不提供长双精度类型所需的16字节对齐,
# 这意味着无法满足输入意图,导致多个测试失败,因为对齐标志可能随机为真或假。
# 当 numpy 获得对齐分配器时,可以重新启用这些测试。
#
# 此外,在 macOS ARM64 上,LONGDOUBLE 是 DOUBLE 的别名。
if ((np.intp().dtype.itemsize != 4 or np.clongdouble().dtype.alignment <= 8)
        and sys.platform != "win32"
        and (platform.system(), platform.processor()) != ("Darwin", "arm")):
    # 扩展 _type_names 列表,添加 LONGDOUBLE、CDOUBLE、CLONGDOUBLE 类型
    _type_names.extend(["LONGDOUBLE", "CDOUBLE", "CLONGDOUBLE"])
    
    # 将 LONGDOUBLE 类型转换为 LONG 类型,并添加 ULONG、FLOAT、DOUBLE、LONGDOUBLE 到 LONGDOUBLE 类型的转换字典中
    _cast_dict["LONGDOUBLE"] = _cast_dict["LONG"] + [
        "ULONG",
        "FLOAT",
        "DOUBLE",
        "LONGDOUBLE",
    ]
    
    # 将 CLONGDOUBLE 类型转换为 LONGDOUBLE 类型,并添加 CFLOAT、CDOUBLE、CLONGDOUBLE 到 CLONGDOUBLE 类型的转换字典中
    _cast_dict["CLONGDOUBLE"] = _cast_dict["LONGDOUBLE"] + [
        "CFLOAT",
        "CDOUBLE",
        "CLONGDOUBLE",
    ]
    
    # 将 CDOUBLE 类型转换为 DOUBLE 类型,并添加 CFLOAT、CDOUBLE 到 CDOUBLE 类型的转换字典中
    _cast_dict["CDOUBLE"] = _cast_dict["DOUBLE"] + ["CFLOAT", "CDOUBLE"]


class Type:
    _type_cache = {}

    def __new__(cls, name):
        if isinstance(name, np.dtype):
            dtype0 = name
            name = None
            for n, i in c_names_dict.items():
                if not isinstance(i, type) and dtype0.type is i.type:
                    name = n
                    break
        obj = cls._type_cache.get(name.upper(), None)
        if obj is not None:
            return obj
        obj = object.__new__(cls)
        obj._init(name)
        cls._type_cache[name.upper()] = obj
        return obj

    def _init(self, name):
        self.NAME = name.upper()

        if self.NAME == 'CHARACTER':
            info = c_names_dict[self.NAME]
            self.type_num = getattr(wrap, 'NPY_STRING')
            self.elsize = 1
            self.dtype = np.dtype('c')
        elif self.NAME.startswith('STRING'):
            info = c_names_dict[self.NAME[:6]]
            self.type_num = getattr(wrap, 'NPY_STRING')
            self.elsize = int(self.NAME[6:] or 0)
            self.dtype = np.dtype(f'S{self.elsize}')
        else:
            info = c_names_dict[self.NAME]
            self.type_num = getattr(wrap, 'NPY_' + self.NAME)
            self.elsize = info.itemsize
            self.dtype = np.dtype(info.type)

        assert self.type_num == info.num
        self.type = info.type
        self.dtypechar = info.char
    # 定义对象的字符串表示形式,返回格式化后的字符串,包括类型名称和相关属性
    def __repr__(self):
        return (f"Type({self.NAME})|type_num={self.type_num},"
                f" dtype={self.dtype},"
                f" type={self.type}, elsize={self.elsize},"
                f" dtypechar={self.dtypechar}")

    # 将当前类型转换为其它类型的对象列表,使用_cast_dict中当前类型的映射
    def cast_types(self):
        return [self.__class__(_m) for _m in _cast_dict[self.NAME]]

    # 返回所有已定义类型的对象列表,每个对象表示一个类型
    def all_types(self):
        return [self.__class__(_m) for _m in _type_names]

    # 返回比当前类型对齐位数小的所有类型的对象列表
    def smaller_types(self):
        bits = c_names_dict[self.NAME].alignment  # 获取当前类型的对齐位数
        types = []
        for name in _type_names:
            if c_names_dict[name].alignment < bits:  # 检查每个类型的对齐位数是否小于当前类型
                types.append(Type(name))  # 如果是,则添加对应类型的对象到列表
        return types

    # 返回与当前类型对齐位数相同的所有类型的对象列表
    def equal_types(self):
        bits = c_names_dict[self.NAME].alignment  # 获取当前类型的对齐位数
        types = []
        for name in _type_names:
            if name == self.NAME:
                continue
            if c_names_dict[name].alignment == bits:  # 检查每个类型的对齐位数是否与当前类型相同
                types.append(Type(name))  # 如果是,则添加对应类型的对象到列表
        return types

    # 返回比当前类型对齐位数大的所有类型的对象列表
    def larger_types(self):
        bits = c_names_dict[self.NAME].alignment  # 获取当前类型的对齐位数
        types = []
        for name in _type_names:
            if c_names_dict[name].alignment > bits:  # 检查每个类型的对齐位数是否大于当前类型
                types.append(Type(name))  # 如果是,则添加对应类型的对象到列表
        return types
class Array:
    # 返回数组的字符串表示形式,包括类型、维度、意图和对象信息
    def __repr__(self):
        return (f'Array({self.type}, {self.dims}, {self.intent},'
                f' {self.obj})|arr={self.arr}')

    # 比较两个数组是否相等
    def arr_equal(self, arr1, arr2):
        # 检查数组形状是否相同
        if arr1.shape != arr2.shape:
            return False
        # 检查数组元素是否完全相同
        return (arr1 == arr2).all()

    # 返回数组的字符串表示形式
    def __str__(self):
        return str(self.arr)

    # 检查是否创建的数组与输入数组共享内存
    def has_shared_memory(self):
        # 检查对象是否与数组相同
        if self.obj is self.arr:
            return True
        # 如果对象不是 numpy 数组,返回 False
        if not isinstance(self.obj, np.ndarray):
            return False
        # 使用 wrap 模块获取对象的属性
        obj_attr = wrap.array_attrs(self.obj)
        # 比较对象属性的第一个元素与数组属性的第一个元素是否相同
        return obj_attr[0] == self.arr_attr[0]


class TestIntent:
    # 测试 intent 对象的输入输出字符串表示
    def test_in_out(self):
        assert str(intent.in_.out) == "intent(in,out)"
        # 检查是否 c 是 intent
        assert intent.in_.c.is_intent("c")
        # 检查是否 c 是确切的 intent
        assert not intent.in_.c.is_intent_exact("c")
        # 检查是否 c 是确切的 intent
        assert intent.in_.c.is_intent_exact("c", "in")
        # 检查是否 c 是确切的 intent
        assert intent.in_.c.is_intent_exact("in", "c")
        # 检查是否 c 是 intent
        assert not intent.in_.is_intent("c")


class TestSharedMemory:
    # 设置测试类型的 fixture
    @pytest.fixture(autouse=True, scope="class", params=_type_names)
    def setup_type(self, request):
        request.cls.type = Type(request.param)
        # 创建 Array 对象的 lambda 函数
        request.cls.array = lambda self, dims, intent, obj: Array(
            Type(request.param), dims, intent, obj)

    # 返回 num2seq 属性
    @property
    def num2seq(self):
        # 如果类型以 'STRING' 开头,返回字符串序列
        if self.type.NAME.startswith('STRING'):
            elsize = self.type.elsize
            return ['1' * elsize, '2' * elsize]
        # 否则返回整数序列
        return [1, 2]

    # 返回 num23seq 属性
    @property
    def num23seq(self):
        # 如果类型以 'STRING' 开头,返回二维字符串序列
        if self.type.NAME.startswith('STRING'):
            elsize = self.type.elsize
            return [['1' * elsize, '2' * elsize, '3' * elsize],
                    ['4' * elsize, '5' * elsize, '6' * elsize]]
        # 否则返回二维整数序列
        return [[1, 2, 3], [4, 5, 6]]

    # 测试从两个元素序列创建输入数组
    def test_in_from_2seq(self):
        # 使用 Array 创建数组对象 a
        a = self.array([2], intent.in_, self.num2seq)
        # 断言 a 没有共享内存
        assert not a.has_shared_memory()

    # 测试从两个转换类型创建输入数组
    def test_in_from_2casttype(self):
        # 遍历类型的转换类型
        for t in self.type.cast_types():
            # 使用 numpy 创建数组对象 obj
            obj = np.array(self.num2seq, dtype=t.dtype)
            # 使用 Array 创建数组对象 a
            a = self.array([len(self.num2seq)], intent.in_, obj)
            # 如果类型的字节大小与转换类型的字节大小相同,断言 a 共享内存
            if t.elsize == self.type.elsize:
                assert a.has_shared_memory(), repr((self.type.dtype, t.dtype))
            else:
                assert not a.has_shared_memory()

    # 测试是否可以传递 intent(in) 数组而不进行复制
    @pytest.mark.parametrize("write", ["w", "ro"])
    @pytest.mark.parametrize("order", ["C", "F"])
    @pytest.mark.parametrize("inp", ["2seq", "23seq"])
    def test_in_nocopy(self, write, order, inp):
        """Test if intent(in) array can be passed without copies"""
        # 获取相应的序列属性
        seq = getattr(self, "num" + inp)
        # 使用 numpy 创建数组对象 obj
        obj = np.array(seq, dtype=self.type.dtype, order=order)
        # 设置数组的写入标志
        obj.setflags(write=(write == 'w'))
        # 使用 Array 创建数组对象 a
        a = self.array(obj.shape,
                       ((order == 'C' and intent.in_.c) or intent.in_), obj)
        # 断言 a 共享内存
        assert a.has_shared_memory()
    # 定义测试函数,用于测试特定的输入输出意图
    def test_inout_2seq(self):
        # 使用self.num2seq生成NumPy数组,并指定数据类型
        obj = np.array(self.num2seq, dtype=self.type.dtype)
        # 创建一个具有意图输入输出的数组对象,并传入长度信息和数据对象
        a = self.array([len(self.num2seq)], intent.inout, obj)
        # 断言数组具有共享内存特性
        assert a.has_shared_memory()

        try:
            # 尝试创建一个意图为输入输出的数组对象,但传入了序列对象self.num2seq,预期会抛出TypeError异常
            a = self.array([2], intent.in_.inout, self.num2seq)
        except TypeError as msg:
            # 如果异常消息不是以指定的字符串开头,则抛出异常
            if not str(msg).startswith(
                    "failed to initialize intent(inout|inplace|cache) array"):
                raise
        else:
            # 如果没有抛出TypeError异常,则抛出SystemError异常
            raise SystemError("intent(inout) should have failed on sequence")

    # 定义测试函数,测试Fortran顺序的输入输出意图
    def test_f_inout_23seq(self):
        # 使用self.num23seq生成NumPy数组,指定数据类型和Fortran顺序
        obj = np.array(self.num23seq, dtype=self.type.dtype, order="F")
        # 创建一个具有意图输入输出的数组对象,并传入形状信息和数据对象
        shape = (len(self.num23seq), len(self.num23seq[0]))
        a = self.array(shape, intent.in_.inout, obj)
        # 断言数组具有共享内存特性
        assert a.has_shared_memory()

        # 使用self.num23seq生成NumPy数组,指定数据类型和C顺序
        obj = np.array(self.num23seq, dtype=self.type.dtype, order="C")
        # 尝试创建一个意图为输入输出的数组对象,但传入了错误的数组对象,预期会抛出ValueError异常
        try:
            a = self.array(shape, intent.in_.inout, obj)
        except ValueError as msg:
            # 如果异常消息不是以指定的字符串开头,则抛出异常
            if not str(msg).startswith(
                    "failed to initialize intent(inout) array"):
                raise
        else:
            # 如果没有抛出ValueError异常,则抛出SystemError异常
            raise SystemError(
                "intent(inout) should have failed on improper array")

    # 定义测试函数,测试复杂顺序的输入输出意图
    def test_c_inout_23seq(self):
        # 使用self.num23seq生成NumPy数组,并指定数据类型
        obj = np.array(self.num23seq, dtype=self.type.dtype)
        # 创建一个具有意图输入输出的数组对象,并传入形状信息和数据对象
        shape = (len(self.num23seq), len(self.num23seq[0]))
        a = self.array(shape, intent.in_.c.inout, obj)
        # 断言数组具有共享内存特性
        assert a.has_shared_memory()

    # 定义测试函数,测试从不同类型转换而来的输入复制意图
    def test_in_copy_from_2casttype(self):
        # 遍历类型转换后的数据类型列表
        for t in self.type.cast_types():
            # 使用self.num2seq生成NumPy数组,并指定数据类型为当前类型t的dtype
            obj = np.array(self.num2seq, dtype=t.dtype)
            # 创建一个具有意图输入复制的数组对象,并传入长度信息和数据对象
            a = self.array([len(self.num2seq)], intent.in_.copy, obj)
            # 断言数组不具有共享内存特性
            assert not a.has_shared_memory()

    # 定义测试函数,测试从序列对象转换而来的输入意图
    def test_c_in_from_23seq(self):
        # 创建一个具有意图输入的数组对象,并传入形状信息和数据对象self.num23seq
        a = self.array(
            [len(self.num23seq), len(self.num23seq[0])], intent.in_,
            self.num23seq)
        # 断言数组不具有共享内存特性
        assert not a.has_shared_memory()

    # 定义测试函数,测试从不同类型转换而来的输入意图
    def test_in_from_23casttype(self):
        # 遍历类型转换后的数据类型列表
        for t in self.type.cast_types():
            # 使用self.num23seq生成NumPy数组,并指定数据类型为当前类型t的dtype
            obj = np.array(self.num23seq, dtype=t.dtype)
            # 创建一个具有意图输入的数组对象,并传入形状信息和数据对象
            a = self.array(
                [len(self.num23seq), len(self.num23seq[0])], intent.in_, obj)
            # 断言数组不具有共享内存特性
            assert not a.has_shared_memory()

    # 定义测试函数,测试从不同类型转换而来的Fortran顺序输入意图
    def test_f_in_from_23casttype(self):
        # 遍历类型转换后的数据类型列表
        for t in self.type.cast_types():
            # 使用self.num23seq生成NumPy数组,并指定数据类型为当前类型t的dtype和Fortran顺序
            obj = np.array(self.num23seq, dtype=t.dtype, order="F")
            # 创建一个具有意图输入的数组对象,并传入形状信息和数据对象
            a = self.array(
                [len(self.num23seq), len(self.num23seq[0])], intent.in_, obj)
            # 如果当前类型t的元素大小等于self.type的元素大小,则断言数组具有共享内存特性,否则断言数组不具有共享内存特性
            if t.elsize == self.type.elsize:
                assert a.has_shared_memory()
            else:
                assert not a.has_shared_memory()
    # 对于类型系统中的每种类型进行测试
    def test_c_in_from_23casttype(self):
        for t in self.type.cast_types():
            # 使用给定的类型创建一个 numpy 数组对象
            obj = np.array(self.num23seq, dtype=t.dtype)
            # 创建一个数组对象,指定其意图为输入 (intent.in_.c),传入 numpy 数组对象
            a = self.array(
                [len(self.num23seq), len(self.num23seq[0])], intent.in_.c, obj)
            # 如果类型的字节大小与数组对象的字节大小相同,则断言数组具有共享内存
            if t.elsize == self.type.elsize:
                assert a.has_shared_memory()
            else:
                assert not a.has_shared_memory()

    # 对于类型系统中的每种类型进行测试,强制使用 Fortran 的顺序
    def test_f_copy_in_from_23casttype(self):
        for t in self.type.cast_types():
            # 使用给定的类型创建一个 numpy 数组对象,强制使用 Fortran 的顺序
            obj = np.array(self.num23seq, dtype=t.dtype, order="F")
            # 创建一个数组对象,指定其意图为输入并且要求复制 (intent.in_.copy),传入 numpy 数组对象
            a = self.array(
                [len(self.num23seq), len(self.num23seq[0])], intent.in_.copy,
                obj)
            # 断言数组不具有共享内存
            assert not a.has_shared_memory()

    # 对于类型系统中的每种类型进行测试,强制使用 C 的顺序并要求复制
    def test_c_copy_in_from_23casttype(self):
        for t in self.type.cast_types():
            # 使用给定的类型创建一个 numpy 数组对象
            obj = np.array(self.num23seq, dtype=t.dtype)
            # 创建一个数组对象,指定其意图为输入并且要求复制 (intent.in_.c.copy),传入 numpy 数组对象
            a = self.array(
                [len(self.num23seq), len(self.num23seq[0])], intent.in_.c.copy,
                obj)
            # 断言数组不具有共享内存
            assert not a.has_shared_memory()

    # 对于类型系统中的每种类型进行测试,检查缓存意图 (intent.in_.cache)
    def test_in_cache_from_2casttype(self):
        for t in self.type.all_types():
            # 如果类型的字节大小与系统的字节大小不同,则跳过该类型的测试
            if t.elsize != self.type.elsize:
                continue
            # 使用给定的类型创建一个 numpy 数组对象
            obj = np.array(self.num2seq, dtype=t.dtype)
            shape = (len(self.num2seq), )
            # 创建一个数组对象,指定其意图为输入并且要求缓存 (intent.in_.c.cache),传入 numpy 数组对象
            a = self.array(shape, intent.in_.c.cache, obj)
            # 断言数组具有共享内存
            assert a.has_shared_memory()

            # 创建一个数组对象,指定其意图为输入并且要求缓存 (intent.in_.cache),传入 numpy 数组对象
            a = self.array(shape, intent.in_.cache, obj)
            # 断言数组具有共享内存
            assert a.has_shared_memory()

            # 使用给定的类型创建一个 numpy 数组对象,强制使用 Fortran 的顺序
            obj = np.array(self.num2seq, dtype=t.dtype, order="F")
            # 创建一个数组对象,指定其意图为输入并且要求缓存 (intent.in_.c.cache),传入 numpy 数组对象
            a = self.array(shape, intent.in_.c.cache, obj)
            # 断言数组具有共享内存
            assert a.has_shared_memory()

            # 创建一个数组对象,指定其意图为输入并且要求缓存 (intent.in_.cache),传入 numpy 数组对象
            a = self.array(shape, intent.in_.cache, obj)
            # 断言数组具有共享内存,并输出该类型的数据类型信息
            assert a.has_shared_memory(), repr(t.dtype)

            # 尝试使用反转后的数组对象创建一个数组对象,指定其意图为输入并且要求缓存 (intent.in_.cache)
            try:
                a = self.array(shape, intent.in_.cache, obj[::-1])
            # 如果抛出 ValueError 异常且消息不以指定的前缀开头,则重新抛出异常
            except ValueError as msg:
                if not str(msg).startswith(
                        "failed to initialize intent(cache) array"):
                    raise
            # 否则,抛出 SystemError 异常,表明意图 (cache) 应该在多段数组上失败
            else:
                raise SystemError(
                    "intent(cache) should have failed on multisegmented array")
    # 对 self.type 中的每种类型进行迭代,self.type 是一个类型对象的集合
    def test_in_cache_from_2casttype_failure(self):
        for t in self.type.all_types():
            # 如果类型 t 的名称为 'STRING',跳过当前循环,不执行后续代码
            if t.NAME == 'STRING':
                # string elsize is 0, so skipping the test
                continue
            # 如果 t 的元素大小大于等于 self.type 的元素大小,跳过当前循环,不执行后续代码
            if t.elsize >= self.type.elsize:
                continue
            # 判断 t.dtype 是否为整数类型
            is_int = np.issubdtype(t.dtype, np.integer)
            # 如果是整数类型,并且将 self.num2seq[0] 转换为整数后大于 t.dtype 的最大值,跳过当前循环,不执行后续代码
            if is_int and int(self.num2seq[0]) > np.iinfo(t.dtype).max:
                # skip test if num2seq would trigger an overflow error
                continue
            # 将 self.num2seq 转换为类型为 t.dtype 的 NumPy 数组
            obj = np.array(self.num2seq, dtype=t.dtype)
            # 创建形状为 (len(self.num2seq), ) 的数组
            shape = (len(self.num2seq), )
            try:
                # 调用 self.array 方法,传入参数 shape, intent.in_.cache, obj,期望操作成功
                self.array(shape, intent.in_.cache, obj)  # Should succeed
            except ValueError as msg:
                # 如果捕获到 ValueError 异常,并且异常消息不以 "failed to initialize intent(cache) array" 开头,抛出异常
                if not str(msg).startswith(
                        "failed to initialize intent(cache) array"):
                    raise
            else:
                # 如果没有捕获到异常,抛出 SystemError 异常,说明 intent(cache) 应该在更小的数组上失败
                raise SystemError(
                    "intent(cache) should have failed on smaller array")

    # 测试 intent.cache.hide 模式
    def test_cache_hidden(self):
        # 测试形状为 (2, ) 的数组
        shape = (2, )
        # 调用 self.array 方法,使用 intent.cache.hide 模式创建数组 a,不传入对象
        a = self.array(shape, intent.cache.hide, None)
        # 断言数组 a 的形状为 shape
        assert a.arr.shape == shape

        # 测试形状为 (2, 3) 的数组
        shape = (2, 3)
        # 调用 self.array 方法,使用 intent.cache.hide 模式创建数组 a,不传入对象
        a = self.array(shape, intent.cache.hide, None)
        # 断言数组 a 的形状为 shape
        assert a.arr.shape == shape

        # 测试形状为 (-1, 3) 的数组
        shape = (-1, 3)
        try:
            # 尝试调用 self.array 方法,使用 intent.cache.hide 模式创建数组 a,不传入对象
            a = self.array(shape, intent.cache.hide, None)
        except ValueError as msg:
            # 如果捕获到 ValueError 异常,并且异常消息不以 "failed to create intent(cache|hide)|optional array" 开头,抛出异常
            if not str(msg).startswith(
                    "failed to create intent(cache|hide)|optional array"):
                raise
        else:
            # 如果没有捕获到异常,抛出 SystemError 异常,说明 intent(cache) 应该在未定义维度上失败
            raise SystemError(
                "intent(cache) should have failed on undefined dimensions")

    # 测试 intent.hide 模式
    def test_hidden(self):
        # 测试形状为 (2, ) 的数组
        shape = (2, )
        # 调用 self.array 方法,使用 intent.hide 模式创建数组 a,不传入对象
        a = self.array(shape, intent.hide, None)
        # 断言数组 a 的形状为 shape
        assert a.arr.shape == shape
        # 断言数组 a 的内容与形状为 shape、数据类型为 self.type.dtype 的零数组相等
        assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))

        # 测试形状为 (2, 3) 的数组
        shape = (2, 3)
        # 调用 self.array 方法,使用 intent.hide 模式创建数组 a,不传入对象
        a = self.array(shape, intent.hide, None)
        # 断言数组 a 的形状为 shape
        assert a.arr.shape == shape
        # 断言数组 a 的内容与形状为 shape、数据类型为 self.type.dtype 的零数组相等
        assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))
        # 断言数组 a 是按 Fortran 顺序存储而不是连续存储
        assert a.arr.flags["FORTRAN"] and not a.arr.flags["CONTIGUOUS"]

        # 测试形状为 (2, 3) 的数组
        shape = (2, 3)
        # 调用 self.array 方法,使用 intent.c.hide 模式创建数组 a,不传入对象
        a = self.array(shape, intent.c.hide, None)
        # 断言数组 a 的形状为 shape
        assert a.arr.shape == shape
        # 断言数组 a 的内容与形状为 shape、数据类型为 self.type.dtype 的零数组相等
        assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))
        # 断言数组 a 不是按 Fortran 顺序存储而是连续存储
        assert not a.arr.flags["FORTRAN"] and a.arr.flags["CONTIGUOUS"]

        # 测试形状为 (-1, 3) 的数组
        shape = (-1, 3)
        try:
            # 尝试调用 self.array 方法,使用 intent.hide 模式创建数组 a,不传入对象
            a = self.array(shape, intent.hide, None)
        except ValueError as msg:
            # 如果捕获到 ValueError 异常,并且异常消息不以 "failed to create intent(cache|hide)|optional array" 开头,抛出异常
            if not str(msg).startswith(
                    "failed to create intent(cache|hide)|optional array"):
                raise
        else:
            # 如果没有捕获到异常,抛出 SystemError 异常,说明 intent(hide) 应该在未定义维度上失败
            raise SystemError(
                "intent(hide) should have failed on undefined dimensions")
    # 测试函数,验证处理可选参数为 None 的情况
    def test_optional_none(self):
        shape = (2, )
        # 调用 array 方法创建一个数组对象,形状为 (2,),可选参数为 None
        a = self.array(shape, intent.optional, None)
        # 断言数组对象的形状与预期相同
        assert a.arr.shape == shape
        # 断言数组对象与用零填充后的数组在值上相等
        assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))

        shape = (2, 3)
        # 再次调用 array 方法创建一个数组对象,形状为 (2,3),可选参数为 None
        a = self.array(shape, intent.optional, None)
        # 断言数组对象的形状与预期相同
        assert a.arr.shape == shape
        # 断言数组对象与用零填充后的数组在值上相等
        assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))
        # 断言数组对象为 Fortran 风格,且不是连续的
        assert a.arr.flags["FORTRAN"] and not a.arr.flags["CONTIGUOUS"]

        shape = (2, 3)
        # 第三次调用 array 方法创建一个数组对象,形状为 (2,3),可选参数为 None
        a = self.array(shape, intent.c.optional, None)
        # 断言数组对象的形状与预期相同
        assert a.arr.shape == shape
        # 断言数组对象与用零填充后的数组在值上相等
        assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))
        # 断言数组对象不是 Fortran 风格,且是连续的
        assert not a.arr.flags["FORTRAN"] and a.arr.flags["CONTIGUOUS"]

    # 测试函数,验证处理从可迭代对象创建数组的情况
    def test_optional_from_2seq(self):
        obj = self.num2seq
        shape = (len(obj), )
        # 调用 array 方法创建一个数组对象,形状为 (len(obj),),可选参数为 obj
        a = self.array(shape, intent.optional, obj)
        # 断言数组对象的形状与预期相同
        assert a.arr.shape == shape
        # 断言数组对象没有共享内存
        assert not a.has_shared_memory()

    # 测试函数,验证处理从二维可迭代对象创建数组的情况
    def test_optional_from_23seq(self):
        obj = self.num23seq
        shape = (len(obj), len(obj[0]))
        # 调用 array 方法创建一个数组对象,形状为 (len(obj), len(obj[0])),可选参数为 obj
        a = self.array(shape, intent.optional, obj)
        # 断言数组对象的形状与预期相同
        assert a.arr.shape == shape
        # 断言数组对象没有共享内存
        assert not a.has_shared_memory()

        # 再次调用 array 方法创建一个数组对象,形状为 (len(obj), len(obj[0])),可选参数为 obj
        a = self.array(shape, intent.optional.c, obj)
        # 断言数组对象的形状与预期相同
        assert a.arr.shape == shape
        # 断言数组对象没有共享内存
        assert not a.has_shared_memory()

    # 测试函数,验证处理 inplace 操作的情况
    def test_inplace(self):
        obj = np.array(self.num23seq, dtype=self.type.dtype)
        # 断言对象不是 Fortran 风格且是连续的
        assert not obj.flags["FORTRAN"] and obj.flags["CONTIGUOUS"]
        shape = obj.shape
        # 调用 array 方法创建一个数组对象,形状与 obj 相同,操作类型为 inplace,对象为 obj
        a = self.array(shape, intent.inplace, obj)
        # 断言数组对象的某个元素与 obj 相同
        assert obj[1][2] == a.arr[1][2], repr((obj, a.arr))
        # 修改数组对象的某个元素
        a.arr[1][2] = 54
        # 断言 obj 和数组对象的相同元素值为 54
        assert obj[1][2] == a.arr[1][2] == np.array(54, dtype=self.type.dtype)
        # 断言数组对象与 obj 是同一个对象
        assert a.arr is obj
        # 断言 obj 是 Fortran 风格的(因为 inplace 操作)
        assert obj.flags["FORTRAN"]  # obj attributes are changed inplace!
        # 断言 obj 不是连续的
        assert not obj.flags["CONTIGUOUS"]

    # 测试函数,验证从类型转换后进行 inplace 操作的情况
    def test_inplace_from_casttype(self):
        for t in self.type.cast_types():
            if t is self.type:
                continue
            obj = np.array(self.num23seq, dtype=t.dtype)
            # 断言对象的数据类型为 t
            assert obj.dtype.type == t.type
            # 断言对象的数据类型不是 self.type.type
            assert obj.dtype.type is not self.type.type
            # 断言对象不是 Fortran 风格且是连续的
            assert not obj.flags["FORTRAN"] and obj.flags["CONTIGUOUS"]
            shape = obj.shape
            # 调用 array 方法创建一个数组对象,形状与 obj 相同,操作类型为 inplace,对象为 obj
            a = self.array(shape, intent.inplace, obj)
            # 断言数组对象的某个元素与 obj 相同
            assert obj[1][2] == a.arr[1][2], repr((obj, a.arr))
            # 修改数组对象的某个元素
            a.arr[1][2] = 54
            # 断言 obj 和数组对象的相同元素值为 54
            assert obj[1][2] == a.arr[1][2] == np.array(54, dtype=self.type.dtype)
            # 断言数组对象与 obj 是同一个对象
            assert a.arr is obj
            # 断言 obj 是 Fortran 风格的(因为 inplace 操作)
            assert obj.flags["FORTRAN"]  # obj attributes changed inplace!
            # 断言 obj 不是连续的
            assert not obj.flags["CONTIGUOUS"]
            # 断言 obj 的数据类型是 self.type.type(因为 inplace 操作)
            assert obj.dtype.type is self.type.type  # obj changed inplace!

.\numpy\numpy\f2py\tests\test_assumed_shape.py

# 导入必要的库和模块
import os  # 导入操作系统相关的功能
import pytest  # 导入 pytest 测试框架
import tempfile  # 导入临时文件相关的功能

# 从当前包中导入 util 模块
from . import util


# 定义一个测试类 TestAssumedShapeSumExample,继承自 util.F2PyTest
class TestAssumedShapeSumExample(util.F2PyTest):
    # 定义一个列表,包含多个源文件的路径
    sources = [
        util.getpath("tests", "src", "assumed_shape", "foo_free.f90"),
        util.getpath("tests", "src", "assumed_shape", "foo_use.f90"),
        util.getpath("tests", "src", "assumed_shape", "precision.f90"),
        util.getpath("tests", "src", "assumed_shape", "foo_mod.f90"),
        util.getpath("tests", "src", "assumed_shape", ".f2py_f2cmap"),
    ]

    # 标记此测试方法为慢速测试
    @pytest.mark.slow
    def test_all(self):
        # 调用 self.module 对象的 fsum 方法,传入参数 [1, 2]
        r = self.module.fsum([1, 2])
        # 断言返回结果 r 等于 3
        assert r == 3
        # 调用 self.module 对象的 sum 方法,传入参数 [1, 2]
        r = self.module.sum([1, 2])
        # 断言返回结果 r 等于 3
        assert r == 3
        # 调用 self.module 对象的 sum_with_use 方法,传入参数 [1, 2]
        r = self.module.sum_with_use([1, 2])
        # 断言返回结果 r 等于 3
        assert r == 3

        # 调用 self.module.mod 对象的 sum 方法,传入参数 [1, 2]
        r = self.module.mod.sum([1, 2])
        # 断言返回结果 r 等于 3
        assert r == 3
        # 调用 self.module.mod 对象的 fsum 方法,传入参数 [1, 2]
        r = self.module.mod.fsum([1, 2])
        # 断言返回结果 r 等于 3
        assert r == 3


# 定义一个测试类 TestF2cmapOption,继承自 TestAssumedShapeSumExample
class TestF2cmapOption(TestAssumedShapeSumExample):
    # 设置每个测试方法的初始化方法
    def setup_method(self):
        # 创建 self.sources 列表的副本,并移除最后一个元素,保存到 f2cmap_src 变量中
        self.sources = list(self.sources)
        f2cmap_src = self.sources.pop(-1)

        # 创建一个临时命名文件,不会自动删除
        self.f2cmap_file = tempfile.NamedTemporaryFile(delete=False)
        # 以二进制读取 f2cmap_src 文件内容,并写入临时文件中
        with open(f2cmap_src, "rb") as f:
            self.f2cmap_file.write(f.read())
        # 关闭临时文件
        self.f2cmap_file.close()

        # 将临时文件名添加到 self.sources 列表末尾
        self.sources.append(self.f2cmap_file.name)
        # 设置选项参数列表,包含 "--f2cmap" 和临时文件名
        self.options = ["--f2cmap", self.f2cmap_file.name]

        # 调用父类的初始化方法
        super().setup_method()

    # 设置每个测试方法的清理方法
    def teardown_method(self):
        # 删除临时文件
        os.unlink(self.f2cmap_file.name)

.\numpy\numpy\f2py\tests\test_block_docstring.py

# 导入系统模块 sys
import sys
# 导入 pytest 测试框架
import pytest
# 从当前包中导入 util 模块
from . import util

# 导入 numpy 的测试工具 IS_PYPY
from numpy.testing import IS_PYPY

# 使用 pytest 的标记 @pytest.mark.slow 标记测试类为慢速测试
@pytest.mark.slow
# 定义一个测试类 TestBlockDocString,继承自 util.F2PyTest
class TestBlockDocString(util.F2PyTest):
    # 定义测试类的属性 sources,包含一个文件路径列表
    sources = [util.getpath("tests", "src", "block_docstring", "foo.f")]

    # 使用 pytest 的标记 @pytest.mark.skipif 根据条件跳过测试
    @pytest.mark.skipif(sys.platform == "win32",
                        reason="Fails with MinGW64 Gfortran (Issue #9673)")
    # 使用 pytest 的标记 @pytest.mark.xfail 标记测试预期会失败
    @pytest.mark.xfail(IS_PYPY,
                       reason="PyPy cannot modify tp_doc after PyType_Ready")
    # 定义测试方法 test_block_docstring
    def test_block_docstring(self):
        # 期望的文档字符串内容
        expected = "bar : 'i'-array(2,3)\n"
        # 断言模块 self.module.block 的文档字符串与期望值相等
        assert self.module.block.__doc__ == expected

.\numpy\numpy\f2py\tests\test_callback.py

import math  # 导入 math 模块,提供数学函数支持
import textwrap  # 导入 textwrap 模块,提供文本包装和填充功能
import sys  # 导入 sys 模块,提供对解释器相关的操作访问
import pytest  # 导入 pytest 模块,用于编写和运行测试用例
import threading  # 导入 threading 模块,提供多线程支持
import traceback  # 导入 traceback 模块,用于提取和格式化异常的回溯信息
import time  # 导入 time 模块,提供时间相关的功能

import numpy as np  # 导入 NumPy 库,用于科学计算
from numpy.testing import IS_PYPY  # 导入 IS_PYPY 变量,用于检测是否在 PyPy 下运行
from . import util  # 从当前包中导入 util 模块

class TestF77Callback(util.F2PyTest):
    sources = [util.getpath("tests", "src", "callback", "foo.f")]

    @pytest.mark.parametrize("name", "t,t2".split(","))
    @pytest.mark.slow
    def test_all(self, name):
        self.check_function(name)

    @pytest.mark.xfail(IS_PYPY,
                       reason="PyPy cannot modify tp_doc after PyType_Ready")
    def test_docstring(self):
        expected = textwrap.dedent("""\
        a = t(fun,[fun_extra_args])

        Wrapper for ``t``.

        Parameters
        ----------
        fun : call-back function

        Other Parameters
        ----------------
        fun_extra_args : input tuple, optional
            Default: ()

        Returns
        -------
        a : int

        Notes
        -----
        Call-back functions::

            def fun(): return a
            Return objects:
                a : int
        """)
        assert self.module.t.__doc__ == expected

    def check_function(self, name):
        t = getattr(self.module, name)  # 获取 self.module 中名称为 name 的属性,并赋值给 t
        r = t(lambda: 4)  # 调用 t,传入匿名函数 lambda: 4 作为参数,并将结果赋值给 r
        assert r == 4  # 断言 r 的值为 4
        r = t(lambda a: 5, fun_extra_args=(6, ))  # 调用 t,传入带有 fun_extra_args 参数的 lambda 函数,并将结果赋值给 r
        assert r == 5  # 断言 r 的值为 5
        r = t(lambda a: a, fun_extra_args=(6, ))  # 调用 t,传入带有 fun_extra_args 参数的 lambda 函数,并将结果赋值给 r
        assert r == 6  # 断言 r 的值为 6
        r = t(lambda a: 5 + a, fun_extra_args=(7, ))  # 调用 t,传入带有 fun_extra_args 参数的 lambda 函数,并将结果赋值给 r
        assert r == 12  # 断言 r 的值为 12
        r = t(lambda a: math.degrees(a), fun_extra_args=(math.pi, ))  # 调用 t,传入带有 fun_extra_args 参数的 math.degrees 函数,并将结果赋值给 r
        assert r == 180  # 断言 r 的值为 180
        r = t(math.degrees, fun_extra_args=(math.pi, ))  # 调用 t,传入带有 fun_extra_args 参数的 math.degrees 函数,并将结果赋值给 r
        assert r == 180  # 断言 r 的值为 180

        r = t(self.module.func, fun_extra_args=(6, ))  # 调用 t,传入带有 fun_extra_args 参数的 self.module.func 函数,并将结果赋值给 r
        assert r == 17  # 断言 r 的值为 17
        r = t(self.module.func0)  # 调用 t,传入 self.module.func0 函数,并将结果赋值给 r
        assert r == 11  # 断言 r 的值为 11
        r = t(self.module.func0._cpointer)  # 调用 t,传入 self.module.func0._cpointer 函数,并将结果赋值给 r
        assert r == 11  # 断言 r 的值为 11

        class A:
            def __call__(self):
                return 7

            def mth(self):
                return 9

        a = A()
        r = t(a)  # 调用 t,传入实例 a,并将结果赋值给 r
        assert r == 7  # 断言 r 的值为 7
        r = t(a.mth)  # 调用 t,传入 a.mth 方法,并将结果赋值给 r
        assert r == 9  # 断言 r 的值为 9

    @pytest.mark.skipif(sys.platform == 'win32',
                        reason='Fails with MinGW64 Gfortran (Issue #9673)')
    def test_string_callback(self):
        def callback(code):
            if code == "r":
                return 0
            else:
                return 1

        f = getattr(self.module, "string_callback")  # 获取 self.module 中名为 "string_callback" 的属性,并赋值给 f
        r = f(callback)  # 调用 f,传入 callback 函数作为参数,并将结果赋值给 r
        assert r == 0  # 断言 r 的值为 0

    @pytest.mark.skipif(sys.platform == 'win32',
                        reason='Fails with MinGW64 Gfortran (Issue #9673)')
    def test_string_callback_array(self):
        # See gh-10027
        # 创建一个长度为1的零数组,元素类型为字节串,每个元素长度为8字节
        cu1 = np.zeros((1, ), "S8")
        # 创建一个长度为1的零数组,每个元素为单个字符,每个字符为1字节
        cu2 = np.zeros((1, 8), "c")
        # 创建一个包含一个空字符串的字节数组,每个元素长度为8字节
        cu3 = np.array([""], "S8")

        def callback(cu, lencu):
            # 检查数组形状是否为(lencu,),如果不是则返回1
            if cu.shape != (lencu,):
                return 1
            # 检查数组元素类型是否为字节串"S8",如果不是则返回2
            if cu.dtype != "S8":
                return 2
            # 检查数组所有元素是否都是空字节串,如果不是则返回3
            if not np.all(cu == b""):
                return 3
            # 满足所有条件则返回0
            return 0

        # 获取对象self.module中名为"string_callback_array"的函数对象
        f = getattr(self.module, "string_callback_array")
        # 对cu1, cu2, cu3中的每个数组执行回调函数f,并断言返回值为0
        for cu in [cu1, cu2, cu3]:
            res = f(callback, cu, cu.size)
            assert res == 0

    def test_threadsafety(self):
        # 如果回调处理不是线程安全的,则可能导致段错误

        errors = []

        def cb():
            # 在这里睡眠以增加另一个线程在同一时间调用它们回调函数的可能性
            time.sleep(1e-3)

            # 检查重入性
            r = self.module.t(lambda: 123)
            assert r == 123

            return 42

        def runner(name):
            try:
                for j in range(50):
                    # 调用self.module中名为"t"的函数,并传递cb作为回调函数
                    r = self.module.t(cb)
                    # 断言返回值为42
                    assert r == 42
                    # 检查函数name的有效性
                    self.check_function(name)
            except Exception:
                errors.append(traceback.format_exc())

        # 创建20个线程,每个线程调用runner函数,并传递不同的参数("t"或"t2")
        threads = [
            threading.Thread(target=runner, args=(arg, ))
            for arg in ("t", "t2") for n in range(20)
        ]

        # 启动所有线程
        for t in threads:
            t.start()

        # 等待所有线程结束
        for t in threads:
            t.join()

        # 如果有错误发生,将所有错误信息合并为一个字符串并抛出AssertionError
        errors = "\n\n".join(errors)
        if errors:
            raise AssertionError(errors)

    def test_hidden_callback(self):
        try:
            # 尝试调用self.module中的"hidden_callback"函数,期望抛出异常并检查异常消息
            self.module.hidden_callback(2)
        except Exception as msg:
            assert str(msg).startswith("Callback global_f not defined")

        try:
            # 尝试调用self.module中的"hidden_callback2"函数,期望抛出异常并检查异常消息
            self.module.hidden_callback2(2)
        except Exception as msg:
            assert str(msg).startswith("cb: Callback global_f not defined")

        # 设置self.module中的全局变量"global_f"为一个lambda函数,返回输入参数加1
        self.module.global_f = lambda x: x + 1
        # 调用self.module中的"hidden_callback"函数,预期返回3
        r = self.module.hidden_callback(2)
        assert r == 3

        # 更新self.module中的全局变量"global_f"为一个lambda函数,返回输入参数加2
        self.module.global_f = lambda x: x + 2
        # 再次调用self.module中的"hidden_callback"函数,预期返回4
        r = self.module.hidden_callback(2)
        assert r == 4

        # 删除self.module中的全局变量"global_f"
        del self.module.global_f
        # 尝试调用self.module中的"hidden_callback"函数,期望抛出异常并检查异常消息
        try:
            self.module.hidden_callback(2)
        except Exception as msg:
            assert str(msg).startswith("Callback global_f not defined")

        # 设置self.module中的全局变量"global_f"为一个lambda函数,默认参数为0,返回输入参数加3
        self.module.global_f = lambda x=0: x + 3
        # 再次调用self.module中的"hidden_callback"函数,预期返回5
        r = self.module.hidden_callback(2)
        assert r == 5

        # 重现gh18341的问题
        # 调用self.module中的"hidden_callback2"函数,预期返回3
        r = self.module.hidden_callback2(2)
        assert r == 3
# 定义一个名为 TestF77CallbackPythonTLS 的测试类,继承自 TestF77Callback 类
class TestF77CallbackPythonTLS(TestF77Callback):
    """
    Callback tests using Python thread-local storage instead of
    compiler-provided
    """
    
    # 设置类的选项属性为包含字符串 "-DF2PY_USE_PYTHON_TLS" 的列表
    options = ["-DF2PY_USE_PYTHON_TLS"]


# 定义一个名为 TestF90Callback 的测试类,继承自 util.F2PyTest 类
class TestF90Callback(util.F2PyTest):
    # 设置类的 sources 属性为包含指定源文件路径的列表
    sources = [util.getpath("tests", "src", "callback", "gh17797.f90")]

    # 标记为慢速测试
    @pytest.mark.slow
    # 定义名为 test_gh17797 的测试方法
    def test_gh17797(self):
        # 定义一个函数 incr,参数 x,返回值为 x + 123
        def incr(x):
            return x + 123
        
        # 创建一个包含 [1, 2, 3] 的 numpy 数组 y,数据类型为 np.int64
        y = np.array([1, 2, 3], dtype=np.int64)
        # 调用 self.module 的 gh17797 方法,参数为 incr 函数和数组 y,返回结果赋给 r
        r = self.module.gh17797(incr, y)
        # 断言 r 的值等于 123 + 1 + 2 + 3
        assert r == 123 + 1 + 2 + 3


# 定义一个名为 TestGH18335 的测试类,继承自 util.F2PyTest 类
class TestGH18335(util.F2PyTest):
    """
    The reproduction of the reported issue requires specific input that
    extensions may break the issue conditions, so the reproducer is
    implemented as a separate test class. Do not extend this test with
    other tests!
    """
    # 设置类的 sources 属性为包含指定源文件路径的列表
    sources = [util.getpath("tests", "src", "callback", "gh18335.f90")]

    # 标记为慢速测试
    @pytest.mark.slow
    # 定义名为 test_gh18335 的测试方法
    def test_gh18335(self):
        # 定义一个函数 foo,参数 x,将 x[0] 的值加一
        def foo(x):
            x[0] += 1
        
        # 调用 self.module 的 gh18335 方法,参数为 foo 函数,返回结果赋给 r
        r = self.module.gh18335(foo)
        # 断言 r 的值等于 123 + 1
        assert r == 123 + 1


# 定义一个名为 TestGH25211 的测试类,继承自 util.F2PyTest 类
class TestGH25211(util.F2PyTest):
    # 设置类的 sources 属性为包含指定源文件路径的列表
    sources = [util.getpath("tests", "src", "callback", "gh25211.f"),
               util.getpath("tests", "src", "callback", "gh25211.pyf")]
    # 设置模块名为 "callback2"
    module_name = "callback2"

    # 定义名为 test_gh25211 的测试方法
    def test_gh25211(self):
        # 定义一个函数 bar,参数 x,返回值为 x*x
        def bar(x):
            return x*x
        
        # 调用 self.module 的 foo 方法,参数为 bar 函数,返回结果赋给 res
        res = self.module.foo(bar)
        # 断言 res 的值等于 110
        assert res == 110

.\numpy\numpy\f2py\tests\test_character.py

# 导入 pytest 模块,用于测试
import pytest
# 导入 textwrap 模块,用于处理字符串的缩进和格式
import textwrap
# 导入 numpy.testing 模块中的断言函数,用于数组比较和异常断言
from numpy.testing import assert_array_equal, assert_equal, assert_raises
# 导入 numpy 模块,并使用 np 别名
import numpy as np
# 导入 numpy.f2py.tests 中的 util 模块
from numpy.f2py.tests import util

# 用 @pytest.mark.slow 装饰器标记这个测试类为慢速测试
@pytest.mark.slow
# 定义一个测试类 TestCharacterString,继承自 util.F2PyTest
class TestCharacterString(util.F2PyTest):
    # 设置文件后缀名为 '.f90'
    suffix = '.f90'
    # 定义文件名前缀为 'test_character_string'
    fprefix = 'test_character_string'
    # 定义长度列表,包含字符串长度 '1', '3', 'star'
    length_list = ['1', '3', 'star']

    # 初始化代码字符串为空
    code = ''
    # 遍历长度列表
    for length in length_list:
        # 设置文件后缀为当前长度
        fsuffix = length
        # 根据长度设置字符长度 clength,将 'star' 转换为 '(*)'
        clength = dict(star='(*)').get(length, length)

        # 构建 Fortran 子程序的代码段,使用 textwrap.dedent 去除缩进
        code += textwrap.dedent(f"""
        
        subroutine {fprefix}_input_{fsuffix}(c, o, n)
          character*{clength}, intent(in) :: c
          integer n
          !f2py integer, depend(c), intent(hide) :: n = slen(c)
          integer*1, dimension(n) :: o
          !f2py intent(out) o
          o = transfer(c, o)
        end subroutine {fprefix}_input_{fsuffix}

        subroutine {fprefix}_output_{fsuffix}(c, o, n)
          character*{clength}, intent(out) :: c
          integer n
          integer*1, dimension(n), intent(in) :: o
          !f2py integer, depend(o), intent(hide) :: n = len(o)
          c = transfer(o, c)
        end subroutine {fprefix}_output_{fsuffix}

        subroutine {fprefix}_array_input_{fsuffix}(c, o, m, n)
          integer m, i, n
          character*{clength}, intent(in), dimension(m) :: c
          !f2py integer, depend(c), intent(hide) :: m = len(c)
          !f2py integer, depend(c), intent(hide) :: n = f2py_itemsize(c)
          integer*1, dimension(m, n), intent(out) :: o
          do i=1,m
            o(i, :) = transfer(c(i), o(i, :))
          end do
        end subroutine {fprefix}_array_input_{fsuffix}

        subroutine {fprefix}_array_output_{fsuffix}(c, o, m, n)
          character*{clength}, intent(out), dimension(m) :: c
          integer n
          integer*1, dimension(m, n), intent(in) :: o
          !f2py character(f2py_len=n) :: c
          !f2py integer, depend(o), intent(hide) :: m = len(o)
          !f2py integer, depend(o), intent(hide) :: n = shape(o, 1)
          do i=1,m
            c(i) = transfer(o(i, :), c(i))
          end do
        end subroutine {fprefix}_array_output_{fsuffix}

        subroutine {fprefix}_2d_array_input_{fsuffix}(c, o, m1, m2, n)
          integer m1, m2, i, j, n
          character*{clength}, intent(in), dimension(m1, m2) :: c
          !f2py integer, depend(c), intent(hide) :: m1 = len(c)
          !f2py integer, depend(c), intent(hide) :: m2 = shape(c, 1)
          !f2py integer, depend(c), intent(hide) :: n = f2py_itemsize(c)
          integer*1, dimension(m1, m2, n), intent(out) :: o
          do i=1,m1
            do j=1,m2
              o(i, j, :) = transfer(c(i, j), o(i, j, :))
            end do
          end do
        end subroutine {fprefix}_2d_array_input_{fsuffix}
        """)

    # 使用 @pytest.mark.parametrize 装饰器将 length_list 的值作为参数传入测试方法
    @pytest.mark.parametrize("length", length_list)
    # 定义一个测试函数,用于测试输入处理函数,参数为长度
    def test_input(self, length):
        # 根据长度选择文件后缀名,如果找不到对应的长度,使用本身作为后缀
        fsuffix = {'(*)': 'star'}.get(length, length)
        # 根据测试对象模块和函数名构造函数对象
        f = getattr(self.module, self.fprefix + '_input_' + fsuffix)

        # 根据长度选择字符串,构造输入数据a
        a = {'1': 'a', '3': 'abc', 'star': 'abcde' * 3}[length]

        # 断言输入数据经处理后与预期的字节表示一致
        assert_array_equal(f(a), np.array(list(map(ord, a)), dtype='u1'))

    # 使用参数化测试装饰器,测试输出处理函数,参数为长度
    @pytest.mark.parametrize("length", length_list[:-1])
    def test_output(self, length):
        # 设置函数后缀为长度本身
        fsuffix = length
        # 根据测试对象模块和函数名构造函数对象
        f = getattr(self.module, self.fprefix + '_output_' + fsuffix)

        # 根据长度选择字符串,构造输入数据a
        a = {'1': 'a', '3': 'abc'}[length]

        # 断言经处理后输出与预期的字节编码一致
        assert_array_equal(f(np.array(list(map(ord, a)), dtype='u1')),
                           a.encode())

    # 使用参数化测试装饰器,测试数组输入处理函数,参数为长度
    @pytest.mark.parametrize("length", length_list)
    def test_array_input(self, length):
        # 设置函数后缀为长度本身
        fsuffix = length
        # 根据测试对象模块和函数名构造函数对象
        f = getattr(self.module, self.fprefix + '_array_input_' + fsuffix)

        # 根据长度选择字符串,构造输入数据a,以及对应的大写版本
        a = np.array([{'1': 'a', '3': 'abc', 'star': 'abcde' * 3}[length],
                      {'1': 'A', '3': 'ABC', 'star': 'ABCDE' * 3}[length],
                      ], dtype='S')

        # 构造预期的字节表示的二维数组
        expected = np.array([[c for c in s] for s in a], dtype='u1')
        # 断言处理后的输出与预期的二维数组一致
        assert_array_equal(f(a), expected)

    # 使用参数化测试装饰器,测试数组输出处理函数,参数为长度
    @pytest.mark.parametrize("length", length_list)
    def test_array_output(self, length):
        # 设置函数后缀为长度本身
        fsuffix = length
        # 根据测试对象模块和函数名构造函数对象
        f = getattr(self.module, self.fprefix + '_array_output_' + fsuffix)

        # 根据长度选择字符串,构造预期的输出数据a
        expected = np.array(
            [{'1': 'a', '3': 'abc', 'star': 'abcde' * 3}[length],
             {'1': 'A', '3': 'ABC', 'star': 'ABCDE' * 3}[length]], dtype='S')

        # 构造预期的字节表示的二维数组
        a = np.array([[c for c in s] for s in expected], dtype='u1')
        # 断言处理后的输出与预期的二维数组一致
        assert_array_equal(f(a), expected)

    # 使用参数化测试装饰器,测试二维数组输入处理函数,参数为长度
    @pytest.mark.parametrize("length", length_list)
    def test_2d_array_input(self, length):
        # 设置函数后缀为长度本身
        fsuffix = length
        # 根据测试对象模块和函数名构造函数对象
        f = getattr(self.module, self.fprefix + '_2d_array_input_' + fsuffix)

        # 根据长度选择字符串,构造输入数据a及其大写版本的二维数组
        a = np.array([[{'1': 'a', '3': 'abc', 'star': 'abcde' * 3}[length],
                       {'1': 'A', '3': 'ABC', 'star': 'ABCDE' * 3}[length]],
                      [{'1': 'f', '3': 'fgh', 'star': 'fghij' * 3}[length],
                       {'1': 'F', '3': 'FGH', 'star': 'FGHIJ' * 3}[length]]],
                     dtype='S')
        # 构造预期的字节表示的三维数组,按Fortran顺序排列
        expected = np.array([[[c for c in item] for item in row] for row in a],
                            dtype='u1', order='F')
        # 断言处理后的输出与预期的三维数组一致
        assert_array_equal(f(a), expected)
class TestCharacter(util.F2PyTest):
    # 定义一个测试类 TestCharacter,继承自 util.F2PyTest
    # 设置类变量 suffix 为 '.f90',用于表示文件后缀名
    suffix = '.f90'
    # 设置类变量 fprefix 为 'test_character',用于表示文件名前缀
    fprefix = 'test_character'
    code = textwrap.dedent(f"""
       subroutine {fprefix}_input(c, o)
          character, intent(in) :: c
          integer*1 o
          !f2py intent(out) o
          o = transfer(c, o)
       end subroutine {fprefix}_input

       subroutine {fprefix}_output(c, o)
          character :: c
          integer*1, intent(in) :: o
          !f2py intent(out) c
          c = transfer(o, c)
       end subroutine {fprefix}_output

       subroutine {fprefix}_input_output(c, o)
          character, intent(in) :: c
          character o
          !f2py intent(out) o
          o = c
       end subroutine {fprefix}_input_output

       subroutine {fprefix}_inout(c, n)
          character :: c, n
          !f2py intent(in) n
          !f2py intent(inout) c
          c = n
       end subroutine {fprefix}_inout

       function {fprefix}_return(o) result (c)
          character :: c
          character, intent(in) :: o
          c = transfer(o, c)
       end function {fprefix}_return

       subroutine {fprefix}_array_input(c, o)
          character, intent(in) :: c(3)
          integer*1 o(3)
          !f2py intent(out) o
          integer i
          do i=1,3
            o(i) = transfer(c(i), o(i))
          end do
       end subroutine {fprefix}_array_input

       subroutine {fprefix}_2d_array_input(c, o)
          character, intent(in) :: c(2, 3)
          integer*1 o(2, 3)
          !f2py intent(out) o
          integer i, j
          do i=1,2
            do j=1,3
              o(i, j) = transfer(c(i, j), o(i, j))
            end do
          end do
       end subroutine {fprefix}_2d_array_input

       subroutine {fprefix}_array_output(c, o)
          character :: c(3)
          integer*1, intent(in) :: o(3)
          !f2py intent(out) c
          do i=1,3
            c(i) = transfer(o(i), c(i))
          end do
       end subroutine {fprefix}_array_output

       subroutine {fprefix}_array_inout(c, n)
          character :: c(3), n(3)
          !f2py intent(in) n(3)
          !f2py intent(inout) c(3)
          do i=1,3
            c(i) = n(i)
          end do
       end subroutine {fprefix}_array_inout

       subroutine {fprefix}_2d_array_inout(c, n)
          character :: c(2, 3), n(2, 3)
          !f2py intent(in) n(2, 3)
          !f2py intent(inout) c(2, 3)
          integer i, j
          do i=1,2
            do j=1,3
              c(i, j) = n(i, j)
            end do
          end do
       end subroutine {fprefix}_2d_array_inout

       function {fprefix}_array_return(o) result (c)
          character, dimension(3) :: c
          character, intent(in) :: o(3)
          do i=1,3
            c(i) = o(i)
          end do
       end function {fprefix}_array_return

       function {fprefix}_optional(o) result (c)
          character, intent(in) :: o
          !f2py character o = "a"
          character :: c
          c = o
       end function {fprefix}_optional
    """)

    # 使用 textwrap.dedent 对字符串进行缩进处理,以避免多余的空格
    @pytest.mark.parametrize("dtype", ['c', 'S1'])
    # 使用 pytest.mark.parametrize 创建一个参数化测试,测试 dtype 参数为 'c' 和 'S1'
    # 定义一个测试函数,用于测试输入类型为dtype的情况
    def test_input(self, dtype):
        # 获取要测试的输入函数的引用
        f = getattr(self.module, self.fprefix + '_input')

        # 断言函数对于不同类型的输入返回的结果是否符合预期
        assert_equal(f(np.array('a', dtype=dtype)), ord('a'))
        assert_equal(f(np.array(b'a', dtype=dtype)), ord('a'))
        assert_equal(f(np.array(['a'], dtype=dtype)), ord('a'))
        assert_equal(f(np.array('abc', dtype=dtype)), ord('a'))
        assert_equal(f(np.array([['a']], dtype=dtype)), ord('a'))

    # 定义另一个测试函数,用于测试不同类型和格式的输入情况
    def test_input_varia(self):
        # 获取要测试的输入函数的引用
        f = getattr(self.module, self.fprefix + '_input')

        # 测试函数对于不同类型和格式的输入是否能正确返回预期的结果
        assert_equal(f('a'), ord('a'))
        assert_equal(f(b'a'), ord(b'a'))
        assert_equal(f(''), 0)
        assert_equal(f(b''), 0)
        assert_equal(f(b'\0'), 0)
        assert_equal(f('ab'), ord('a'))
        assert_equal(f(b'ab'), ord('a'))
        assert_equal(f(['a']), ord('a'))

        # 使用NumPy数组作为输入进行进一步测试
        assert_equal(f(np.array(b'a')), ord('a'))
        assert_equal(f(np.array([b'a'])), ord('a'))
        a = np.array('a')
        assert_equal(f(a), ord('a'))
        a = np.array(['a'])
        assert_equal(f(a), ord('a'))

        # 测试空列表作为输入时是否会引发预期的异常
        try:
            f([])
        except IndexError as msg:
            if not str(msg).endswith(' got 0-list'):
                raise
        else:
            raise SystemError(f'{f.__name__} should have failed on empty list')

        # 测试整数作为输入时是否会引发预期的异常
        try:
            f(97)
        except TypeError as msg:
            if not str(msg).endswith(' got int instance'):
                raise
        else:
            raise SystemError(f'{f.__name__} should have failed on int value')

    # 使用pytest的参数化功能定义测试数组输入的情况
    @pytest.mark.parametrize("dtype", ['c', 'S1', 'U1'])
    def test_array_input(self, dtype):
        # 获取要测试的数组输入函数的引用
        f = getattr(self.module, self.fprefix + '_array_input')

        # 断言函数对于数组输入返回的结果是否符合预期
        assert_array_equal(f(np.array(['a', 'b', 'c'], dtype=dtype)),
                           np.array(list(map(ord, 'abc')), dtype='i1'))
        assert_array_equal(f(np.array([b'a', b'b', b'c'], dtype=dtype)),
                           np.array(list(map(ord, 'abc')), dtype='i1'))

    # 定义另一个测试函数,用于测试不同类型和格式的数组输入情况
    def test_array_input_varia(self):
        # 获取要测试的数组输入函数的引用
        f = getattr(self.module, self.fprefix + '_array_input')

        # 断言函数对于不同类型和格式的数组输入是否能正确返回预期的结果
        assert_array_equal(f(['a', 'b', 'c']),
                           np.array(list(map(ord, 'abc')), dtype='i1'))
        assert_array_equal(f([b'a', b'b', b'c']),
                           np.array(list(map(ord, 'abc')), dtype='i1'))

        # 测试输入数组长度不为3时是否会引发预期的异常
        try:
            f(['a', 'b', 'c', 'd'])
        except ValueError as msg:
            if not str(msg).endswith(
                    'th dimension must be fixed to 3 but got 4'):
                raise
        else:
            raise SystemError(
                f'{f.__name__} should have failed on wrong input')

    # 使用pytest的参数化功能定义测试数组输入的dtype情况
    @pytest.mark.parametrize("dtype", ['c', 'S1', 'U1'])
    # 定义一个测试方法,用于测试接受二维数组输入的函数,参数为数据类型dtype
    def test_2d_array_input(self, dtype):
        # 获取被测试模块中的相应函数
        f = getattr(self.module, self.fprefix + '_2d_array_input')

        # 创建一个二维数组a,内容为 [['a', 'b', 'c'], ['d', 'e', 'f']],指定数据类型为dtype,列序为F(列主序)
        a = np.array([['a', 'b', 'c'],
                      ['d', 'e', 'f']], dtype=dtype, order='F')
        # 创建预期结果expected,将数组a视图转换为np.uint32(如果dtype为'U1')或np.uint8
        expected = a.view(np.uint32 if dtype == 'U1' else np.uint8)
        # 断言调用函数f并传入数组a的返回值与预期结果expected相等
        assert_array_equal(f(a), expected)

    # 定义一个测试方法,测试函数的输出功能
    def test_output(self):
        # 获取被测试模块中的相应函数
        f = getattr(self.module, self.fprefix + '_output')

        # 断言调用函数f并传入ord(b'a')的返回值与b'a'相等
        assert_equal(f(ord(b'a')), b'a')
        # 断言调用函数f并传入0的返回值与b'\0'相等
        assert_equal(f(0), b'\0')

    # 定义一个测试方法,测试函数的数组输出功能
    def test_array_output(self):
        # 获取被测试模块中的相应函数
        f = getattr(self.module, self.fprefix + '_array_output')

        # 断言调用函数f并传入map(ord, 'abc')的返回值的数组与np.array(list('abc'), dtype='S1')相等
        assert_array_equal(f(list(map(ord, 'abc'))),
                           np.array(list('abc'), dtype='S1'))

    # 定义一个测试方法,测试函数的输入输出功能
    def test_input_output(self):
        # 获取被测试模块中的相应函数
        f = getattr(self.module, self.fprefix + '_input_output')

        # 断言调用函数f并传入b'a'的返回值与b'a'相等
        assert_equal(f(b'a'), b'a')
        # 断言调用函数f并传入'a'的返回值与b'a'相等(自动转换为字节串)
        assert_equal(f('a'), b'a')
        # 断言调用函数f并传入''的返回值与b'\0'相等
        assert_equal(f(''), b'\0')

    # 使用pytest的参数化装饰器,定义一个测试方法,测试函数的输入输出功能,参数为dtype
    @pytest.mark.parametrize("dtype", ['c', 'S1'])
    def test_inout(self, dtype):
        # 获取被测试模块中的相应函数
        f = getattr(self.module, self.fprefix + '_inout')

        # 创建一个数组a,内容为['a', 'b', 'c'],指定数据类型为dtype
        a = np.array(list('abc'), dtype=dtype)
        # 调用函数f并传入a及字符串'A'
        f(a, 'A')
        # 断言数组a与预期结果np.array(list('Abc'), dtype=a.dtype)相等
        assert_array_equal(a, np.array(list('Abc'), dtype=a.dtype))
        
        # 对数组a的子数组进行操作
        f(a[1:], 'B')
        # 断言数组a与预期结果np.array(list('ABc'), dtype=a.dtype)相等
        assert_array_equal(a, np.array(list('ABc'), dtype=a.dtype))

        # 创建一个数组a,内容为['abc'],指定数据类型为dtype
        a = np.array(['abc'], dtype=dtype)
        # 调用函数f并传入a及字符串'A'
        f(a, 'A')
        # 断言数组a与预期结果np.array(['Abc'], dtype=a.dtype)相等
        assert_array_equal(a, np.array(['Abc'], dtype=a.dtype))

    # 定义一个测试方法,测试函数的多种输入输出情况
    def test_inout_varia(self):
        # 获取被测试模块中的相应函数
        f = getattr(self.module, self.fprefix + '_inout')
        
        # 创建一个数组a,内容为'abc',指定数据类型为'S3'
        a = np.array('abc', dtype='S3')
        # 调用函数f并传入a及字符串'A'
        f(a, 'A')
        # 断言数组a与预期结果np.array('Abc', dtype=a.dtype)相等
        assert_array_equal(a, np.array('Abc', dtype=a.dtype))

        # 创建一个数组a,内容为['abc'],指定数据类型为'S3'
        a = np.array(['abc'], dtype='S3')
        # 调用函数f并传入a及字符串'A'
        f(a, 'A')
        # 断言数组a与预期结果np.array(['Abc'], dtype=a.dtype)相等
        assert_array_equal(a, np.array(['Abc'], dtype=a.dtype))

        # 尝试使用字符串'abc'调用函数f,预期会引发ValueError异常,如果异常消息不以' got 3-str'结尾,则抛出SystemError异常
        try:
            f('abc', 'A')
        except ValueError as msg:
            if not str(msg).endswith(' got 3-str'):
                raise
        else:
            raise SystemError(f'{f.__name__} should have failed on str value')

    # 使用pytest的参数化装饰器,定义一个测试方法,测试函数的数组输入输出功能,参数为dtype
    @pytest.mark.parametrize("dtype", ['c', 'S1'])
    def test_array_inout(self, dtype):
        # 获取被测试模块中的相应函数
        f = getattr(self.module, self.fprefix + '_array_inout')
        
        # 创建一个数组n,内容为['A', 'B', 'C'],指定数据类型为dtype,列序为F(列主序)
        n = np.array(['A', 'B', 'C'], dtype=dtype, order='F')

        # 创建一个数组a,内容为['a', 'b', 'c'],指定数据类型为dtype,列序为F(列主序)
        a = np.array(['a', 'b', 'c'], dtype=dtype, order='F')
        # 调用函数f并传入a及数组n
        f(a, n)
        # 断言数组a与数组n相等
        assert_array_equal(a, n)

        # 创建一个数组a,内容为['a', 'b', 'c', 'd'],指定数据类型为dtype
        a = np.array(['a', 'b', 'c', 'd'], dtype=dtype)
        # 调用函数f并传入a的子数组及数组n
        f(a[1:], n)
        # 断言数组a与预期结果np.array(['a', 'A', 'B', 'C'], dtype=dtype)相等
        assert_array_equal(a, np.array(['a', 'A', 'B', 'C'], dtype=dtype))

        # 创建一个数组a,内容为[['a', 'b', 'c']],指定数据类型为dtype,列序为F(列主序)
        a = np.array([['a', 'b', 'c']], dtype=dtype, order='F')
        # 调用函数f并传入a及数组n
        f(a, n)
        # 断言数组a与预期结果np.array([['A', 'B', 'C']], dtype=dtype)相等
        assert_array_equal(a, np.array([['A', 'B', 'C']], dtype=dtype))

        # 创建一个数组a,内容为['a', 'b', 'c', 'd'],指定数据类型为dtype,列序为F(列主序)
        a = np.array(['a', 'b', 'c', 'd'], dtype=dtype, order='F')
        # 尝试使用数组a调用函数f,预期会引发ValueError异常,如果异常消息不以'th dimension must be fixed to 3 but got 4'结尾,则抛出SystemError异常
        try:
            f(a, n)
        except ValueError as msg:
            if not str(msg).endswith(
                    'th dimension must be fixed to 3 but got 4'):
                raise
        else:
            raise SystemError(
                f'{f.__name__} should have failed on wrong input')
    # 使用 pytest 的参数化装饰器,为每个测试用例提供不同的数据类型('c' 和 'S1')
    @pytest.mark.parametrize("dtype", ['c', 'S1'])
    # 定义一个测试方法,测试二维数组的输入输出
    def test_2d_array_inout(self, dtype):
        # 获取被测试模块中以指定前缀开头的 '_2d_array_inout' 函数
        f = getattr(self.module, self.fprefix + '_2d_array_inout')
        # 创建一个Fortran风格的二维数组n,数据类型为dtype,按列主序(F顺序)
        n = np.array([['A', 'B', 'C'],
                      ['D', 'E', 'F']],
                     dtype=dtype, order='F')
        # 创建另一个Fortran风格的二维数组a,数据类型为dtype,按列主序(F顺序)
        a = np.array([['a', 'b', 'c'],
                      ['d', 'e', 'f']],
                     dtype=dtype, order='F')
        # 调用被测试函数f,传入数组a和n作为参数
        f(a, n)
        # 断言数组a和n相等
        assert_array_equal(a, n)
    
    # 定义一个测试方法,测试返回结果为字节串的函数
    def test_return(self):
        # 获取被测试模块中以指定前缀开头的 '_return' 函数
        f = getattr(self.module, self.fprefix + '_return')
        # 断言调用f函数,传入字符串'a'返回字节串b'a'
        assert_equal(f('a'), b'a')
    
    # 使用 pytest 的跳过装饰器,注明测试方法跳过并提供跳过的原因
    @pytest.mark.skip('fortran function returning array segfaults')
    # 定义一个测试方法,测试返回数组的Fortran函数
    def test_array_return(self):
        # 获取被测试模块中以指定前缀开头的 '_array_return' 函数
        f = getattr(self.module, self.fprefix + '_array_return')
        # 创建一个数据类型为'S1'的字符数组a
        a = np.array(list('abc'), dtype='S1')
        # 断言调用f函数,传入数组a,返回的结果与a数组相等
        assert_array_equal(f(a), a)
    
    # 定义一个测试方法,测试带有可选参数的函数
    def test_optional(self):
        # 获取被测试模块中以指定前缀开头的 '_optional' 函数
        f = getattr(self.module, self.fprefix + '_optional')
        # 断言调用f函数,不传入参数时返回字节串b'a'
        assert_equal(f(), b"a")
        # 断言调用f函数,传入参数b'B'时返回字节串b'B'
        assert_equal(f(b'B'), b"B")
class TestMiscCharacter(util.F2PyTest):
    # 定义一个测试类,继承自util.F2PyTest,用于测试F2Py相关功能
    # options = ['--debug-capi', '--build-dir', '/tmp/test-build-f2py']
    # 设置选项(注释掉的部分,这里是可选的调试选项)
    suffix = '.f90'
    # 文件后缀名为.f90
    fprefix = 'test_misc_character'
    # 函数名前缀为test_misc_character

    code = textwrap.dedent(f"""
       subroutine {fprefix}_gh18684(x, y, m)
         character(len=5), dimension(m), intent(in) :: x
         character*5, dimension(m), intent(out) :: y
         integer i, m
         !f2py integer, intent(hide), depend(x) :: m = f2py_len(x)
         # 这是一个F2Py的特殊注释,指定了一个隐藏的整数参数m,依赖于x的长度
         do i=1,m
           y(i) = x(i)
         end do
       end subroutine {fprefix}_gh18684

       subroutine {fprefix}_gh6308(x, i)
         integer i
         !f2py check(i>=0 && i<12) i
         # 检查i的值是否在0到11之间
         character*5 name, x
         common name(12)
         name(i + 1) = x
         # 将x的值存入name数组的第i+1个位置
       end subroutine {fprefix}_gh6308

       subroutine {fprefix}_gh4519(x)
         character(len=*), intent(in) :: x(:)
         !f2py intent(out) x
         integer :: i
         ! Uncomment for debug printing:
         !do i=1, size(x)
         !   print*, "x(",i,")=", x(i)
         !end do
         # 子程序用于处理输入参数x,但在此没有具体实现,注释掉了用于调试的打印代码
       end subroutine {fprefix}_gh4519

       pure function {fprefix}_gh3425(x) result (y)
         character(len=*), intent(in) :: x
         character(len=len(x)) :: y
         integer :: i
         do i = 1, len(x)
           j = iachar(x(i:i))
           if (j>=iachar("a") .and. j<=iachar("z") ) then
             y(i:i) = achar(j-32)
           else
             y(i:i) = x(i:i)
           endif
         end do
         # 纯函数,将输入字符串x中的小写字母转换为大写,返回结果y
       end function {fprefix}_gh3425

       subroutine {fprefix}_character_bc_new(x, y, z)
         character, intent(in) :: x
         character, intent(out) :: y
         !f2py character, depend(x) :: y = x
         !f2py character, dimension((x=='a'?1:2)), depend(x), intent(out) :: z
         character, dimension(*) :: z
         !f2py character, optional, check(x == 'a' || x == 'b') :: x = 'a'
         !f2py callstatement (*f2py_func)(&x, &y, z)
         !f2py callprotoargument character*, character*, character*
         if (y.eq.x) then
           y = x
         else
           y = 'e'
         endif
         z(1) = 'c'
         # 子程序,根据输入的x,将y设为x或者'e',z数组的第一个元素设为'c'
       end subroutine {fprefix}_character_bc_new

       subroutine {fprefix}_character_bc_old(x, y, z)
         character, intent(in) :: x
         character, intent(out) :: y
         !f2py character, depend(x) :: y = x[0]
         !f2py character, dimension((*x=='a'?1:2)), depend(x), intent(out) :: z
         character, dimension(*) :: z
         !f2py character, optional, check(*x == 'a' || x[0] == 'b') :: x = 'a'
         !f2py callstatement (*f2py_func)(x, y, z)
         !f2py callprotoargument char*, char*, char*
         if (y.eq.x) then
           y = x
         else
           y = 'e'
         endif
         z(1) = 'c'
         # 子程序,根据输入的x,将y设为x或者'e',z数组的第一个元素设为'c'
       end subroutine {fprefix}_character_bc_old
    """)

    @pytest.mark.slow
    # 测试函数:test_gh18684
    def test_gh18684(self):
        # 测试字符长度为5的数组和字符长度为5的字符串的用法
        f = getattr(self.module, self.fprefix + '_gh18684')
        x = np.array(["abcde", "fghij"], dtype='S5')
        y = f(x)

        # 断言输出数组x和函数返回的数组y相等
        assert_array_equal(x, y)

    # 测试函数:test_gh6308
    def test_gh6308(self):
        # 测试共同块中的字符字符串数组
        f = getattr(self.module, self.fprefix + '_gh6308')

        # 断言模块中共同块的名称的数据类型为'S5'
        assert_equal(self.module._BLNK_.name.dtype, np.dtype('S5'))
        # 断言模块中共同块的名称长度为12
        assert_equal(len(self.module._BLNK_.name), 12)
        # 调用函数f,将字符串"abcde"放入共同块索引为0的位置
        f("abcde", 0)
        # 断言模块中共同块索引为0的名称为b"abcde"
        assert_equal(self.module._BLNK_.name[0], b"abcde")
        # 调用函数f,将字符串"12345"放入共同块索引为5的位置
        f("12345", 5)
        # 断言模块中共同块索引为5的名称为b"12345"
        assert_equal(self.module._BLNK_.name[5], b"12345")

    # 测试函数:test_gh4519
    def test_gh4519(self):
        # 测试假定长度字符串的数组
        f = getattr(self.module, self.fprefix + '_gh4519')

        # 遍历测试用例,每个测试用例包含输入x和期望输出expected
        for x, expected in [
                ('a', dict(shape=(), dtype=np.dtype('S1'))),
                ('text', dict(shape=(), dtype=np.dtype('S4'))),
                (np.array(['1', '2', '3'], dtype='S1'),
                 dict(shape=(3,), dtype=np.dtype('S1'))),
                (['1', '2', '34'],
                 dict(shape=(3,), dtype=np.dtype('S2'))),
                (['', ''], dict(shape=(2,), dtype=np.dtype('S1')))]:
            # 调用函数f,传入输入x,获取返回值r
            r = f(x)
            # 遍历期望输出字典expected中的键值对
            for k, v in expected.items():
                # 断言返回值r的属性k等于期望值v
                assert_equal(getattr(r, k), v)

    # 测试函数:test_gh3425
    def test_gh3425(self):
        # 测试返回假定长度字符串的副本
        f = getattr(self.module, self.fprefix + '_gh3425')
        # 函数f相当于将字节串转为大写字母

        # 断言函数f对'abC'的处理结果为b'ABC'
        assert_equal(f('abC'), b'ABC')
        # 断言函数f对空字符串的处理结果为b''
        assert_equal(f(''), b'')
        # 断言函数f对'abC12d'的处理结果为b'ABC12D'
        assert_equal(f('abC12d'), b'ABC12D')

    # 使用pytest的参数化装饰器,测试函数:test_character_bc
    @pytest.mark.parametrize("state", ['new', 'old'])
    def test_character_bc(self, state):
        # 获取函数f,函数名包含前缀和状态信息
        f = getattr(self.module, self.fprefix + '_character_bc_' + state)

        # 调用f(),返回c和a
        c, a = f()
        # 断言c为b'a',a的长度为1
        assert_equal(c, b'a')
        assert_equal(len(a), 1)

        # 再次调用f(b'b'),返回c和a
        c, a = f(b'b')
        # 断言c为b'b',a的长度为2
        assert_equal(c, b'b')
        assert_equal(len(a), 2)

        # 使用lambda表达式捕获异常,断言调用f(b'c')时抛出异常
        assert_raises(Exception, lambda: f(b'c'))
# 定义一个测试类 TestStringScalarArr,继承自 util.F2PyTest
class TestStringScalarArr(util.F2PyTest):
    # 设置测试类的源文件路径列表,包括 scalar_string.f90
    sources = [util.getpath("tests", "src", "string", "scalar_string.f90")]

    # 定义测试方法 test_char,用于测试字符类型操作
    def test_char(self):
        # 遍历 self.module.string_test.string 和 self.module.string_test.string77
        for out in (self.module.string_test.string,
                    self.module.string_test.string77):
            expected = ()  # 期望的形状为空元组
            assert out.shape == expected  # 断言输出的形状与期望的形状相同
            expected = '|S8'  # 期望的数据类型为字符串,长度为8
            assert out.dtype == expected  # 断言输出的数据类型与期望的数据类型相同

    # 定义测试方法 test_char_arr,用于测试字符数组类型操作
    def test_char_arr(self):
        # 遍历 self.module.string_test.strarr 和 self.module.string_test.strarr77
        for out in (self.module.string_test.strarr,
                    self.module.string_test.strarr77):
            expected = (5, 7)  # 期望的形状为 (5, 7)
            assert out.shape == expected  # 断言输出的形状与期望的形状相同
            expected = '|S12'  # 期望的数据类型为字符串,长度为12
            assert out.dtype == expected  # 断言输出的数据类型与期望的数据类型相同

# 定义一个测试类 TestStringAssumedLength,继承自 util.F2PyTest
class TestStringAssumedLength(util.F2PyTest):
    # 设置测试类的源文件路径列表,包括 gh24008.f
    sources = [util.getpath("tests", "src", "string", "gh24008.f")]

    # 定义测试方法 test_gh24008,用于测试 gh24008 功能
    def test_gh24008(self):
        # 调用 self.module.greet 方法,传入两个参数 "joe" 和 "bob"
        self.module.greet("joe", "bob")

# 使用 pytest.mark.slow 标记的测试类 TestStringOptionalInOut,继承自 util.F2PyTest
@pytest.mark.slow
class TestStringOptionalInOut(util.F2PyTest):
    # 设置测试类的源文件路径列表,包括 gh24662.f90
    sources = [util.getpath("tests", "src", "string", "gh24662.f90")]

    # 定义测试方法 test_gh24662,用于测试 gh24662 功能
    def test_gh24662(self):
        # 调用 self.module.string_inout_optional 方法,不传入任何参数
        self.module.string_inout_optional()
        # 创建一个长度为 32 的字符串数组 a,内容为 'hi'
        a = np.array('hi', dtype='S32')
        # 调用 self.module.string_inout_optional 方法,传入参数 a
        self.module.string_inout_optional(a)
        # 断言字符串 "output string" 在 a 的字节表示中
        assert "output string" in a.tobytes().decode()
        # 使用 pytest.raises 检查是否抛出异常
        with pytest.raises(Exception):
            aa = "Hi"
            self.module.string_inout_optional(aa)

# 使用 pytest.mark.slow 标记的测试类 TestNewCharHandling,继承自 util.F2PyTest
@pytest.mark.slow
class TestNewCharHandling(util.F2PyTest):
    # 设置测试类的源文件路径列表,包括 gh25286.pyf 和 gh25286.f90
    sources = [
        util.getpath("tests", "src", "string", "gh25286.pyf"),
        util.getpath("tests", "src", "string", "gh25286.f90")
    ]
    module_name = "_char_handling_test"  # 模块名为 _char_handling_test

    # 定义测试方法 test_gh25286,用于测试 gh25286 功能
    def test_gh25286(self):
        # 调用 self.module.charint 方法,传入字符 'T',并将返回值赋给 info
        info = self.module.charint('T')
        # 断言 info 的值为 2
        assert info == 2

# 使用 pytest.mark.slow 标记的测试类 TestBCCharHandling,继承自 util.F2PyTest
@pytest.mark.slow
class TestBCCharHandling(util.F2PyTest):
    # 设置测试类的源文件路径列表,包括 gh25286_bc.pyf 和 gh25286.f90
    sources = [
        util.getpath("tests", "src", "string", "gh25286_bc.pyf"),
        util.getpath("tests", "src", "string", "gh25286.f90")
    ]
    module_name = "_char_handling_test"  # 模块名为 _char_handling_test

    # 定义测试方法 test_gh25286,用于测试 gh25286 功能
    def test_gh25286(self):
        # 调用 self.module.charint 方法,传入字符 'T',并将返回值赋给 info
        info = self.module.charint('T')
        # 断言 info 的值为 2
        assert info == 2

.\numpy\numpy\f2py\tests\test_common.py

# 导入 pytest 模块,用于测试
import pytest
# 导入 numpy 模块并重命名为 np,用于数值计算和数组操作
import numpy as np
# 从当前包中导入 util 模块
from . import util

# 用 pytest 的标记将该类标记为慢速测试
@pytest.mark.slow
# 测试类 TestCommonBlock,继承自 util.F2PyTest 类
class TestCommonBlock(util.F2PyTest):
    # 指定源文件列表
    sources = [util.getpath("tests", "src", "common", "block.f")]

    # 定义测试方法 test_common_block
    def test_common_block(self):
        # 调用 self.module 的 initcb 方法进行初始化
        self.module.initcb()
        # 断言 self.module.block.long_bn 等于一个浮点数数组,数值为 1.0,数据类型为 np.float64
        assert self.module.block.long_bn == np.array(1.0, dtype=np.float64)
        # 断言 self.module.block.string_bn 等于一个字符串数组,内容为 "2",数据类型为 '|S1'
        assert self.module.block.string_bn == np.array("2", dtype="|S1")
        # 断言 self.module.block.ok 等于一个整数数组,数值为 3,数据类型为 np.int32
        assert self.module.block.ok == np.array(3, dtype=np.int32)


# 测试类 TestCommonWithUse,继承自 util.F2PyTest 类
class TestCommonWithUse(util.F2PyTest):
    # 指定源文件列表
    sources = [util.getpath("tests", "src", "common", "gh19161.f90")]

    # 定义测试方法 test_common_gh19161
    def test_common_gh19161(self):
        # 断言 self.module.data.x 等于 0
        assert self.module.data.x == 0

.\numpy\numpy\f2py\tests\test_crackfortran.py

import importlib  # 导入标准库 importlib,用于动态加载模块
import codecs  # 导入标准库 codecs,提供编码和解码的工具函数
import time  # 导入标准库 time,提供时间相关的函数
import unicodedata  # 导入标准库 unicodedata,用于对 Unicode 字符进行数据库查询
import pytest  # 导入第三方库 pytest,用于编写和运行测试用例
import numpy as np  # 导入第三方库 numpy,并将其命名为 np,用于科学计算
from numpy.f2py.crackfortran import markinnerspaces, nameargspattern  # 从 numpy.f2py.crackfortran 模块导入两个函数
from . import util  # 从当前包中导入 util 模块
from numpy.f2py import crackfortran  # 导入 numpy.f2py 模块中的 crackfortran 子模块
import textwrap  # 导入标准库 textwrap,用于简单的文本包装和填充
import contextlib  # 导入标准库 contextlib,用于创建和管理上下文对象
import io  # 导入标准库 io,提供了 Python 核心的基本 I/O 功能

class TestNoSpace(util.F2PyTest):
    # issue gh-15035: add handling for endsubroutine, endfunction with no space
    # between "end" and the block name
    sources = [util.getpath("tests", "src", "crackfortran", "gh15035.f")]

    def test_module(self):
        k = np.array([1, 2, 3], dtype=np.float64)  # 创建一个 numpy 数组 k,元素为 1, 2, 3,数据类型为 np.float64
        w = np.array([1, 2, 3], dtype=np.float64)  # 创建一个 numpy 数组 w,元素为 1, 2, 3,数据类型为 np.float64
        self.module.subb(k)  # 调用 self.module 对象的 subb 方法,传入参数 k
        assert np.allclose(k, w + 1)  # 使用 numpy 的 allclose 函数断言 k 是否与 w+1 全部近似相等
        self.module.subc([w, k])  # 调用 self.module 对象的 subc 方法,传入参数列表 [w, k]
        assert np.allclose(k, w + 1)  # 再次断言 k 是否与 w+1 全部近似相等
        assert self.module.t0("23") == b"2"  # 断言 self.module 对象的 t0 方法返回的结果是否等于 b"2"

class TestPublicPrivate:
    def test_defaultPrivate(self):
        fpath = util.getpath("tests", "src", "crackfortran", "privatemod.f90")  # 获取指定文件的路径
        mod = crackfortran.crackfortran([str(fpath)])  # 使用 crackfortran 模块的 crackfortran 函数处理文件路径
        assert len(mod) == 1  # 断言 mod 的长度是否为 1
        mod = mod[0]  # 获取 mod 的第一个元素
        assert "private" in mod["vars"]["a"]["attrspec"]  # 断言 mod 中的变量 a 的属性包含 "private"
        assert "public" not in mod["vars"]["a"]["attrspec"]  # 断言 mod 中的变量 a 的属性不包含 "public"
        assert "private" in mod["vars"]["b"]["attrspec"]  # 断言 mod 中的变量 b 的属性包含 "private"
        assert "public" not in mod["vars"]["b"]["attrspec"]  # 断言 mod 中的变量 b 的属性不包含 "public"
        assert "private" not in mod["vars"]["seta"]["attrspec"]  # 断言 mod 中的变量 seta 的属性不包含 "private"
        assert "public" in mod["vars"]["seta"]["attrspec"]  # 断言 mod 中的变量 seta 的属性包含 "public"

    def test_defaultPublic(self, tmp_path):
        fpath = util.getpath("tests", "src", "crackfortran", "publicmod.f90")  # 获取指定文件的路径
        mod = crackfortran.crackfortran([str(fpath)])  # 使用 crackfortran 模块的 crackfortran 函数处理文件路径
        assert len(mod) == 1  # 断言 mod 的长度是否为 1
        mod = mod[0]  # 获取 mod 的第一个元素
        assert "private" in mod["vars"]["a"]["attrspec"]  # 断言 mod 中的变量 a 的属性包含 "private"
        assert "public" not in mod["vars"]["a"]["attrspec"]  # 断言 mod 中的变量 a 的属性不包含 "public"
        assert "private" not in mod["vars"]["seta"]["attrspec"]  # 断言 mod 中的变量 seta 的属性不包含 "private"
        assert "public" in mod["vars"]["seta"]["attrspec"]  # 断言 mod 中的变量 seta 的属性包含 "public"

    def test_access_type(self, tmp_path):
        fpath = util.getpath("tests", "src", "crackfortran", "accesstype.f90")  # 获取指定文件的路径
        mod = crackfortran.crackfortran([str(fpath)])  # 使用 crackfortran 模块的 crackfortran 函数处理文件路径
        assert len(mod) == 1  # 断言 mod 的长度是否为 1
        tt = mod[0]['vars']  # 获取 mod 的第一个元素中的 'vars' 键对应的值
        assert set(tt['a']['attrspec']) == {'private', 'bind(c)'}  # 断言变量 a 的属性集合是否为 {'private', 'bind(c)'}
        assert set(tt['b_']['attrspec']) == {'public', 'bind(c)'}  # 断言变量 b_ 的属性集合是否为 {'public', 'bind(c)'}
        assert set(tt['c']['attrspec']) == {'public'}  # 断言变量 c 的属性集合是否为 {'public'}

    def test_nowrap_private_proceedures(self, tmp_path):
        fpath = util.getpath("tests", "src", "crackfortran", "gh23879.f90")  # 获取指定文件的路径
        mod = crackfortran.crackfortran([str(fpath)])  # 使用 crackfortran 模块的 crackfortran 函数处理文件路径
        assert len(mod) == 1  # 断言 mod 的长度是否为 1
        pyf = crackfortran.crack2fortran(mod)  # 使用 crackfortran 模块的 crack2fortran 函数处理 mod
        assert 'bar' not in pyf  # 断言字符串 'bar' 不在 pyf 中

class TestModuleProcedure():
    # TestModuleProcedure 类的定义暂无代码
    # 测试模块运算符的功能
    def test_moduleOperators(self, tmp_path):
        # 获取指定路径下的 operators.f90 文件路径
        fpath = util.getpath("tests", "src", "crackfortran", "operators.f90")
        # 使用 crackfortran 模块解析该文件,返回模块列表
        mod = crackfortran.crackfortran([str(fpath)])
        # 断言模块列表的长度为1
        assert len(mod) == 1
        # 取出第一个模块
        mod = mod[0]
        # 断言该模块包含键名为 "body",并且其长度为9
        assert "body" in mod and len(mod["body"]) == 9
        # 断言模块的第二个元素的名称为 "operator(.item.)"
        assert mod["body"][1]["name"] == "operator(.item.)"
        # 断言第二个元素包含键名为 "implementedby"
        assert "implementedby" in mod["body"][1]
        # 断言第二个元素的 "implementedby" 值为 ["item_int", "item_real"]
        assert mod["body"][1]["implementedby"] == ["item_int", "item_real"]
        # 断言模块的第三个元素的名称为 "operator(==)"
        assert mod["body"][2]["name"] == "operator(==)"
        # 断言第三个元素包含键名为 "implementedby"
        assert "implementedby" in mod["body"][2]
        # 断言第三个元素的 "implementedby" 值为 ["items_are_equal"]
        assert mod["body"][2]["implementedby"] == ["items_are_equal"]
        # 断言模块的第四个元素的名称为 "assignment(=)"
        assert mod["body"][3]["name"] == "assignment(=)"
        # 断言第四个元素包含键名为 "implementedby"
        assert "implementedby" in mod["body"][3]
        # 断言第四个元素的 "implementedby" 值为 ["get_int", "get_real"]
    
    # 测试模块的非公有(private)和公有(public)属性设置
    def test_notPublicPrivate(self, tmp_path):
        # 获取指定路径下的 pubprivmod.f90 文件路径
        fpath = util.getpath("tests", "src", "crackfortran", "pubprivmod.f90")
        # 使用 crackfortran 模块解析该文件,返回模块列表
        mod = crackfortran.crackfortran([str(fpath)])
        # 断言模块列表的长度为1
        assert len(mod) == 1
        # 取出第一个模块
        mod = mod[0]
        # 断言模块变量 'a' 的 'attrspec' 属性为 ['private', ]
        assert mod['vars']['a']['attrspec'] == ['private', ]
        # 断言模块变量 'b' 的 'attrspec' 属性为 ['public', ]
        assert mod['vars']['b']['attrspec'] == ['public', ]
        # 断言模块变量 'seta' 的 'attrspec' 属性为 ['public', ]
        assert mod['vars']['seta']['attrspec'] == ['public', ]
class TestExternal(util.F2PyTest):
    # 问题编号 gh-17859: 添加对外部属性的支持
    sources = [util.getpath("tests", "src", "crackfortran", "gh17859.f")]

    def test_external_as_statement(self):
        # 定义一个简单的增加函数
        def incr(x):
            return x + 123

        # 调用被测试模块中的 external_as_statement 方法,并验证返回结果
        r = self.module.external_as_statement(incr)
        assert r == 123

    def test_external_as_attribute(self):
        # 定义一个简单的增加函数
        def incr(x):
            return x + 123

        # 调用被测试模块中的 external_as_attribute 方法,并验证返回结果
        r = self.module.external_as_attribute(incr)
        assert r == 123


class TestCrackFortran(util.F2PyTest):
    # 问题编号 gh-2848: 在 Fortran 子程序参数列表中的参数之间添加注释行
    sources = [util.getpath("tests", "src", "crackfortran", "gh2848.f90")]

    def test_gh2848(self):
        # 调用被测试模块中的 gh2848 方法,并验证返回结果
        r = self.module.gh2848(1, 2)
        assert r == (1, 2)


class TestMarkinnerspaces:
    # 问题编号 gh-14118: markinnerspaces 不处理多重引号

    def test_do_not_touch_normal_spaces(self):
        # 针对普通字符串,验证 markinnerspaces 函数不做修改
        test_list = ["a ", " a", "a b c", "'abcdefghij'"]
        for i in test_list:
            assert markinnerspaces(i) == i

    def test_one_relevant_space(self):
        # 针对带有一个有意义空格的字符串,验证 markinnerspaces 函数替换空格
        assert markinnerspaces("a 'b c' \\' \\'") == "a 'b@_@c' \\' \\'"
        assert markinnerspaces(r'a "b c" \" \"') == r'a "b@_@c" \" \"'

    def test_ignore_inner_quotes(self):
        # 针对带有内部引号的字符串,验证 markinnerspaces 函数只处理外部空格
        assert markinnerspaces("a 'b c\" \" d' e") == "a 'b@_@c\"@_@\"@_@d' e"
        assert markinnerspaces("a \"b c' ' d\" e") == "a \"b@_@c'@_@'@_@d\" e"

    def test_multiple_relevant_spaces(self):
        # 针对带有多个有意义空格的字符串,验证 markinnerspaces 函数替换空格
        assert markinnerspaces("a 'b c' 'd e'") == "a 'b@_@c' 'd@_@e'"
        assert markinnerspaces(r'a "b c" "d e"') == r'a "b@_@c" "d@_@e"'


class TestDimSpec(util.F2PyTest):
    """This test suite tests various expressions that are used as dimension
    specifications.

    There exists two usage cases where analyzing dimensions
    specifications are important.

    In the first case, the size of output arrays must be defined based
    on the inputs to a Fortran function. Because Fortran supports
    arbitrary bases for indexing, for instance, `arr(lower:upper)`,
    f2py has to evaluate an expression `upper - lower + 1` where
    `lower` and `upper` are arbitrary expressions of input parameters.
    The evaluation is performed in C, so f2py has to translate Fortran
    expressions to valid C expressions (an alternative approach is
    that a developer specifies the corresponding C expressions in a
    .pyf file).

    In the second case, when user provides an input array with a given
    size but some hidden parameters used in dimensions specifications
    need to be determined based on the input array size. This is a
    harder problem because f2py has to solve the inverse problem: find
    a parameter `p` such that `upper(p) - lower(p) + 1` equals to the
    size of input array. In the case when this equation cannot be
    solved (e.g. because the input array size is wrong), raise an
    error before calling the Fortran function (that otherwise would
    """
    # 定义文件名后缀为 ".f90"
    suffix = ".f90"

    # 定义代码模板,使用 textwrap.dedent 去除代码缩进
    code_template = textwrap.dedent("""
      function get_arr_size_{count}(a, n) result (length)
        integer, intent(in) :: n
        integer, dimension({dimspec}), intent(out) :: a
        integer length
        length = size(a)
      end function

      subroutine get_inv_arr_size_{count}(a, n)
        integer :: n
        ! the value of n is computed in f2py wrapper
        !f2py intent(out) n
        integer, dimension({dimspec}), intent(in) :: a
        if (a({first}).gt.0) then
          ! print*, "a=", a
        endif
      end subroutine
    """)

    # 线性维度规格列表
    linear_dimspecs = [
        "n", "2*n", "2:n", "n/2", "5 - n/2", "3*n:20", "n*(n+1):n*(n+5)",
        "2*n, n"
    ]
    
    # 非线性维度规格列表
    nonlinear_dimspecs = ["2*n:3*n*n+2*n"]
    
    # 所有维度规格列表,包括线性和非线性
    all_dimspecs = linear_dimspecs + nonlinear_dimspecs

    # 初始化代码字符串
    code = ""
    
    # 遍历所有维度规格
    for count, dimspec in enumerate(all_dimspecs):
        # 获取维度规格中的起始值列表
        lst = [(d.split(":")[0] if ":" in d else "1") for d in dimspec.split(',')]
        # 使用代码模板填充代码字符串
        code += code_template.format(
            count=count,
            dimspec=dimspec,
            first=", ".join(lst),
        )

    # 使用 pytest.mark.parametrize 标记测试参数化,参数为所有维度规格
    @pytest.mark.parametrize("dimspec", all_dimspecs)
    @pytest.mark.slow
    def test_array_size(self, dimspec):
        # 获取当前维度规格在列表中的索引
        count = self.all_dimspecs.index(dimspec)
        # 获取相应的 get_arr_size 函数
        get_arr_size = getattr(self.module, f"get_arr_size_{count}")

        # 遍历测试用例中的不同 n 值
        for n in [1, 2, 3, 4, 5]:
            # 调用 get_arr_size 函数获取返回值 sz 和数组 a
            sz, a = get_arr_size(n)
            # 断言数组 a 的大小与返回的 sz 相等
            assert a.size == sz

    # 使用 pytest.mark.parametrize 标记测试参数化,参数为所有维度规格
    @pytest.mark.parametrize("dimspec", all_dimspecs)
    def test_inv_array_size(self, dimspec):
        # 获取当前维度规格在列表中的索引
        count = self.all_dimspecs.index(dimspec)
        # 获取相应的 get_arr_size 和 get_inv_arr_size 函数
        get_arr_size = getattr(self.module, f"get_arr_size_{count}")
        get_inv_arr_size = getattr(self.module, f"get_inv_arr_size_{count}")

        # 遍历测试用例中的不同 n 值
        for n in [1, 2, 3, 4, 5]:
            # 调用 get_arr_size 函数获取返回值 sz 和数组 a
            sz, a = get_arr_size(n)
            
            # 如果当前维度规格在非线性维度规格列表中
            if dimspec in self.nonlinear_dimspecs:
                # 调用 get_inv_arr_size 函数,需要指定 n 作为输入
                n1 = get_inv_arr_size(a, n)
            else:
                # 否则,在线性依赖的情况下,n 可以从数组的形状中确定
                n1 = get_inv_arr_size(a)
            
            # 断言返回的 n1 值经过处理后得到的数组 sz1 与原始 sz 相等
            sz1, _ = get_arr_size(n1)
            assert sz == sz1, (n, n1, sz, sz1)
class TestModuleDeclaration:
    # 测试模块声明类
    def test_dependencies(self, tmp_path):
        # 获取测试文件路径
        fpath = util.getpath("tests", "src", "crackfortran", "foo_deps.f90")
        # 调用 crackfortran 函数处理文件路径并返回模块列表
        mod = crackfortran.crackfortran([str(fpath)])
        # 断言模块列表长度为1
        assert len(mod) == 1
        # 断言模块中第一个元素的变量 'abar' 的赋值为 "bar('abar')"
        assert mod[0]["vars"]["abar"]["="] == "bar('abar')"


class TestEval(util.F2PyTest):
    # 测试 _eval_scalar 函数
    def test_eval_scalar(self):
        eval_scalar = crackfortran._eval_scalar

        # 断言将字符串 '123' 传入 eval_scalar 函数返回 '123'
        assert eval_scalar('123', {}) == '123'
        # 断言将字符串 '12 + 3' 传入 eval_scalar 函数返回 '15'
        assert eval_scalar('12 + 3', {}) == '15'
        # 断言将字符串 'a + b' 和字典 {'a': 1, 'b': 2} 传入 eval_scalar 函数返回 '3'
        assert eval_scalar('a + b', dict(a=1, b=2)) == '3'
        # 断言将字符串 '"123"' 传入 eval_scalar 函数返回 "'123'"
        assert eval_scalar('"123"', {}) == "'123'"


class TestFortranReader(util.F2PyTest):
    # 测试 Fortran 读取器类
    @pytest.mark.parametrize("encoding",
                             ['ascii', 'utf-8', 'utf-16', 'utf-32'])
    def test_input_encoding(self, tmp_path, encoding):
        # 为了解决 gh-635 的问题,创建带有指定编码的临时文件
        f_path = tmp_path / f"input_with_{encoding}_encoding.f90"
        with f_path.open('w', encoding=encoding) as ff:
            # 向文件写入 Fortran 子例程定义
            ff.write("""
                     subroutine foo()
                     end subroutine foo
                     """)
        # 使用 crackfortran 函数处理文件路径并返回模块列表
        mod = crackfortran.crackfortran([str(f_path)])
        # 断言模块列表中第一个模块的名称为 'foo'
        assert mod[0]['name'] == 'foo'


@pytest.mark.slow
class TestUnicodeComment(util.F2PyTest):
    # 测试 Unicode 注释处理类
    sources = [util.getpath("tests", "src", "crackfortran", "unicode_comment.f90")]

    @pytest.mark.skipif(
        (importlib.util.find_spec("charset_normalizer") is None),
        reason="test requires charset_normalizer which is not installed",
    )
    def test_encoding_comment(self):
        # 调用模块中的 foo 方法,传入参数 3
        self.module.foo(3)


class TestNameArgsPatternBacktracking:
    # 测试名称参数模式回溯类
    @pytest.mark.parametrize(
        ['adversary'],
        [
            ('@)@bind@(@',),
            ('@)@bind                         @(@',),
            ('@)@bind foo bar baz@(@',)
        ]
    )
    # 定义一个测试函数,用于检测名为 `nameargspattern` 的正则表达式在处理 `adversary` 字符串时的反向递归问题
    def test_nameargspattern_backtracking(self, adversary):
        '''address ReDOS vulnerability:
        https://github.com/numpy/numpy/issues/23338'''
        
        # 每批次测试的次数
        trials_per_batch = 12
        # 每个正则表达式的批次数
        batches_per_regex = 4
        # 重复拷贝 `adversary` 字符串的次数范围
        start_reps, end_reps = 15, 25
        
        # 循环遍历重复次数范围内的每个值
        for ii in range(start_reps, end_reps):
            # 构造重复次数 `ii` 倍的 `adversary` 字符串
            repeated_adversary = adversary * ii
            
            # 在小批次中多次测试,以增加捕获不良正则表达式的机会
            for _ in range(batches_per_regex):
                times = []
                
                # 在每个批次中多次运行测试
                for _ in range(trials_per_batch):
                    # 记录测试开始时间
                    t0 = time.perf_counter()
                    # 在 `repeated_adversary` 中搜索 `nameargspattern` 正则表达式
                    mtch = nameargspattern.search(repeated_adversary)
                    # 计算测试所用时间并记录
                    times.append(time.perf_counter() - t0)
                
                # 断言:正则表达式的性能应该远快于每次搜索耗时超过 0.2 秒
                assert np.median(times) < 0.2
            
            # 断言:不应该找到匹配,即 `mtch` 应为 None
            assert not mtch
            
            # 构造一个含有 '@)@' 后缀的 `repeated_adversary` 字符串,检测是否可以通过旧版本的正则表达式
            good_version_of_adversary = repeated_adversary + '@)@'
            # 断言:应该能够找到匹配,即 `nameargspattern` 应该能够匹配 `good_version_of_adversary`
            assert nameargspattern.search(good_version_of_adversary)
class TestFunctionReturn(util.F2PyTest):
    sources = [util.getpath("tests", "src", "crackfortran", "gh23598.f90")]

    @pytest.mark.slow
    def test_function_rettype(self):
        # 标记此测试为缓慢执行,验证函数返回类型
        # gh-23598
        assert self.module.intproduct(3, 4) == 12


class TestFortranGroupCounters(util.F2PyTest):
    def test_end_if_comment(self):
        # gh-23533
        # 准备测试gh-23533文件的路径
        fpath = util.getpath("tests", "src", "crackfortran", "gh23533.f")
        try:
            # 尝试运行crackfortran.crackfortran来解析Fortran文件
            crackfortran.crackfortran([str(fpath)])
        except Exception as exc:
            # 如果抛出异常,则断言失败,显示异常信息
            assert False, f"'crackfortran.crackfortran' raised an exception {exc}"


class TestF77CommonBlockReader():
    def test_gh22648(self, tmp_path):
        # 准备测试gh-22648文件的路径
        fpath = util.getpath("tests", "src", "crackfortran", "gh22648.pyf")
        with contextlib.redirect_stdout(io.StringIO()) as stdout_f2py:
            # 运行crackfortran.crackfortran来解析Fortran文件,并重定向标准输出
            mod = crackfortran.crackfortran([str(fpath)])
        # 断言标准输出中不包含"Mismatch"
        assert "Mismatch" not in stdout_f2py.getvalue()

class TestParamEval():
    # issue gh-11612, array parameter parsing
    def test_param_eval_nested(self):
        # 准备测试参数解析,包含嵌套结构
        v = '(/3.14, 4./)'
        g_params = dict(kind=crackfortran._kind_func,
                selected_int_kind=crackfortran._selected_int_kind_func,
                selected_real_kind=crackfortran._selected_real_kind_func)
        params = {'dp': 8, 'intparamarray': {1: 3, 2: 5},
                  'nested': {1: 1, 2: 2, 3: 3}}
        dimspec = '(2)'
        # 调用crackfortran.param_eval进行参数解析,验证结果是否符合预期
        ret = crackfortran.param_eval(v, g_params, params, dimspec=dimspec)
        assert ret == {1: 3.14, 2: 4.0}

    def test_param_eval_nonstandard_range(self):
        # 准备测试非标准范围的参数解析
        v = '(/ 6, 3, 1 /)'
        g_params = dict(kind=crackfortran._kind_func,
                selected_int_kind=crackfortran._selected_int_kind_func,
                selected_real_kind=crackfortran._selected_real_kind_func)
        params = {}
        dimspec = '(-1:1)'
        # 调用crackfortran.param_eval进行参数解析,验证结果是否符合预期
        ret = crackfortran.param_eval(v, g_params, params, dimspec=dimspec)
        assert ret == {-1: 6, 0: 3, 1: 1}

    def test_param_eval_empty_range(self):
        # 准备测试空范围的参数解析
        v = '6'
        g_params = dict(kind=crackfortran._kind_func,
                selected_int_kind=crackfortran._selected_int_kind_func,
                selected_real_kind=crackfortran._selected_real_kind_func)
        params = {}
        dimspec = ''
        # 检查传递空范围参数时,是否抛出ValueError异常
        pytest.raises(ValueError, crackfortran.param_eval, v, g_params, params,
                      dimspec=dimspec)

    def test_param_eval_non_array_param(self):
        # 准备测试非数组参数的解析
        v = '3.14_dp'
        g_params = dict(kind=crackfortran._kind_func,
                selected_int_kind=crackfortran._selected_int_kind_func,
                selected_real_kind=crackfortran._selected_real_kind_func)
        params = {}
        # 调用crackfortran.param_eval进行参数解析,验证结果是否符合预期
        ret = crackfortran.param_eval(v, g_params, params, dimspec=None)
        assert ret == '3.14_dp'
    # 定义一个测试方法,用于测试参数评估是否具有过多维度
    def test_param_eval_too_many_dims(self):
        # 定义一个包含 Fortran 样式重塑函数的字符串参数
        v = 'reshape((/ (i, i=1, 250) /), (/5, 10, 5/))'
        # 创建一个全局参数字典,包括三个函数作为值
        g_params = dict(kind=crackfortran._kind_func,
                        selected_int_kind=crackfortran._selected_int_kind_func,
                        selected_real_kind=crackfortran._selected_real_kind_func)
        # 初始化一个空参数字典
        params = {}
        # 定义维度规范字符串
        dimspec = '(0:4, 3:12, 5)'
        # 使用 pytest 模块断言函数引发 ValueError 异常,调用 crackfortran.param_eval 函数进行测试
        pytest.raises(ValueError, crackfortran.param_eval, v, g_params, params,
                      dimspec=dimspec)

.\numpy\numpy\f2py\tests\test_data.py

# 导入必要的模块:os、pytest、numpy
import os
import pytest
import numpy as np

# 从当前目录下的util模块中导入F2PyTest类
from . import util

# 从numpy.f2py.crackfortran中导入crackfortran函数
from numpy.f2py.crackfortran import crackfortran

# TestData类,继承自util.F2PyTest类
class TestData(util.F2PyTest):
    
    # 源文件路径列表,包含要测试的Fortran源文件路径
    sources = [util.getpath("tests", "src", "crackfortran", "data_stmts.f90")]

    # 标记为slow的测试方法,用于测试data_stmts.f90中的变量
    @pytest.mark.slow
    def test_data_stmts(self):
        # 断言各变量的值是否符合预期
        assert self.module.cmplxdat.i == 2
        assert self.module.cmplxdat.j == 3
        assert self.module.cmplxdat.x == 1.5
        assert self.module.cmplxdat.y == 2.0
        assert self.module.cmplxdat.pi == 3.1415926535897932384626433832795028841971693993751058209749445923078164062
        assert self.module.cmplxdat.medium_ref_index == np.array(1.+0.j)
        assert np.all(self.module.cmplxdat.z == np.array([3.5, 7.0]))
        assert np.all(self.module.cmplxdat.my_array == np.array([1.+2.j, -3.+4.j]))
        assert np.all(self.module.cmplxdat.my_real_array == np.array([1., 2., 3.]))
        assert np.all(self.module.cmplxdat.ref_index_one == np.array([13.0 + 21.0j]))
        assert np.all(self.module.cmplxdat.ref_index_two == np.array([-30.0 + 43.0j]))

    # 测试crackfortran函数
    def test_crackedlines(self):
        # 调用crackfortran函数处理源文件列表,返回处理结果mod
        mod = crackfortran(self.sources)
        # 断言处理结果中的变量值是否符合预期
        assert mod[0]['vars']['x']['='] == '1.5'
        assert mod[0]['vars']['y']['='] == '2.0'
        assert mod[0]['vars']['pi']['='] == '3.1415926535897932384626433832795028841971693993751058209749445923078164062d0'
        assert mod[0]['vars']['my_real_array']['='] == '(/1.0d0, 2.0d0, 3.0d0/)'
        assert mod[0]['vars']['ref_index_one']['='] == '(13.0d0, 21.0d0)'
        assert mod[0]['vars']['ref_index_two']['='] == '(-30.0d0, 43.0d0)'
        assert mod[0]['vars']['my_array']['='] == '(/(1.0d0, 2.0d0), (-3.0d0, 4.0d0)/)'
        assert mod[0]['vars']['z']['='] == '(/3.5,  7.0/)'

# TestDataF77类,继承自util.F2PyTest类
class TestDataF77(util.F2PyTest):
    
    # 源文件路径列表,包含要测试的Fortran源文件路径
    sources = [util.getpath("tests", "src", "crackfortran", "data_common.f")]

    # 测试data_common.f中的变量
    # For gh-23276
    def test_data_stmts(self):
        # 断言self.module.mycom.mydata的值是否为0
        assert self.module.mycom.mydata == 0

    # 测试crackfortran函数
    def test_crackedlines(self):
        # 使用str转换源文件路径为字符串,调用crackfortran函数处理源文件,返回处理结果mod
        mod = crackfortran(str(self.sources[0]))
        # 打印处理结果mod中的变量信息
        print(mod[0]['vars'])
        # 断言处理结果中mydata变量的值是否为0
        assert mod[0]['vars']['mydata']['='] == '0'

# TestDataMultiplierF77类,继承自util.F2PyTest类
class TestDataMultiplierF77(util.F2PyTest):
    
    # 源文件路径列表,包含要测试的Fortran源文件路径
    sources = [util.getpath("tests", "src", "crackfortran", "data_multiplier.f")]

    # 测试data_multiplier.f中的变量
    # For gh-23276
    def test_data_stmts(self):
        # 断言self.module.mycom中的各变量值是否符合预期
        assert self.module.mycom.ivar1 == 3
        assert self.module.mycom.ivar2 == 3
        assert self.module.mycom.ivar3 == 2
        assert self.module.mycom.ivar4 == 2
        assert self.module.mycom.evar5 == 0

# TestDataWithCommentsF77类,继承自util.F2PyTest类
class TestDataWithCommentsF77(util.F2PyTest):
    
    # 源文件路径列表,包含要测试的Fortran源文件路径
    sources = [util.getpath("tests", "src", "crackfortran", "data_with_comments.f")]

    # 测试data_with_comments.f中的变量
    # For gh-23276
    def test_data_stmts(self):
        # 断言self.module.mycom.mytab的长度是否为3,以及其各元素的值是否符合预期
        assert len(self.module.mycom.mytab) == 3
        assert self.module.mycom.mytab[0] == 0
        assert self.module.mycom.mytab[1] == 4
        assert self.module.mycom.mytab[2] == 0

.\numpy\numpy\f2py\tests\test_docs.py

# 导入 pytest 库,用于运行测试
import pytest
# 导入 numpy 库并使用 np 别名
import numpy as np
# 导入 numpy.testing 模块中的 assert_array_equal 和 assert_equal 函数
from numpy.testing import assert_array_equal, assert_equal
# 导入当前目录下的 util 模块
from . import util
# 导入 pathlib 库中的 Path 类
from pathlib import Path

# 定义函数 get_docdir,用于获取文档目录路径
def get_docdir():
    # 解析当前文件的绝对路径并获取其父级目录列表
    parents = Path(__file__).resolve().parents
    try:
        # 假设 spin 用于运行测试,获取父级目录列表的第九个元素作为根目录
        nproot = parents[8]
    except IndexError:
        # 如果索引错误,说明无法找到合适的根目录,设定 docdir 为 None
        docdir = None
    else:
        # 合成文档目录路径
        docdir = nproot / "doc" / "source" / "f2py" / "code"
    # 如果 docdir 不为 None 且存在,则返回文档目录路径
    if docdir and docdir.is_dir():
        return docdir
    # 假设采用可编辑安装来运行测试,返回默认文档目录路径
    return parents[3] / "doc" / "source" / "f2py" / "code"

# 使用 pytest.mark.skipif 标记装饰器,条件是若文档目录不存在则跳过测试
pytestmark = pytest.mark.skipif(
    not get_docdir().is_dir(),
    reason=f"Could not find f2py documentation sources"
           f"({get_docdir()} does not exist)",
)

# 定义函数 _path,用于生成文档目录下的指定路径
def _path(*args):
    return get_docdir().joinpath(*args)

# 使用 pytest.mark.slow 标记装饰器,表明该测试类中的测试较慢
@pytest.mark.slow
# 定义测试类 TestDocAdvanced,继承自 util.F2PyTest 类
class TestDocAdvanced(util.F2PyTest):
    # 定义类属性 sources,包含了三个测试源文件的路径列表
    sources = [_path('asterisk1.f90'), _path('asterisk2.f90'),
               _path('ftype.f')]

    # 定义测试方法 test_asterisk1,验证模块中的 foo1 函数返回值
    def test_asterisk1(self):
        foo = getattr(self.module, 'foo1')
        assert_equal(foo(), b'123456789A12')

    # 定义测试方法 test_asterisk2,验证模块中的 foo2 函数不同参数的返回值
    def test_asterisk2(self):
        foo = getattr(self.module, 'foo2')
        assert_equal(foo(2), b'12')
        assert_equal(foo(12), b'123456789A12')
        assert_equal(foo(20), b'123456789A123456789B')

    # 定义测试方法 test_ftype,验证模块中的 foo 函数及其对数据的操作
    def test_ftype(self):
        ftype = self.module
        ftype.foo()
        assert_equal(ftype.data.a, 0)
        ftype.data.a = 3
        ftype.data.x = [1, 2, 3]
        assert_equal(ftype.data.a, 3)
        assert_array_equal(ftype.data.x,
                           np.array([1, 2, 3], dtype=np.float32))
        ftype.data.x[1] = 45
        assert_array_equal(ftype.data.x,
                           np.array([1, 45, 3], dtype=np.float32))

    # TODO: implement test methods for other example Fortran codes

.\numpy\numpy\f2py\tests\test_f2cmap.py

# 导入名为 util 的当前目录下的模块
from . import util
# 导入 NumPy 库,并将其命名为 np
import numpy as np

# 创建一个名为 TestF2Cmap 的类,继承自 util.F2PyTest 类
class TestF2Cmap(util.F2PyTest):
    # 定义一个名为 sources 的类变量,包含两个路径字符串的列表
    sources = [
        # 获取指定路径下的文件路径字符串,构成一个 Fortran 源文件的路径
        util.getpath("tests", "src", "f2cmap", "isoFortranEnvMap.f90"),
        # 获取指定路径下的文件路径字符串,构成一个 .f2py_f2cmap 文件的路径
        util.getpath("tests", "src", "f2cmap", ".f2py_f2cmap")
    ]

    # 定义一个名为 test_gh15095 的测试方法
    # 测试用例名称为 gh-15095
    def test_gh15095(self):
        # 创建一个包含三个元素的全为 1 的 NumPy 数组
        inp = np.ones(3)
        # 调用 self.module 的 func1 方法,输入为 inp 数组,返回值赋给 out
        out = self.module.func1(inp)
        # 预期输出为整数 3
        exp_out = 3
        # 断言 out 的值等于预期输出 exp_out
        assert out == exp_out

.\numpy\numpy\f2py\tests\test_f2py2e.py

# 导入必要的模块和库
import textwrap, re, sys, subprocess, shlex
from pathlib import Path
from collections import namedtuple
import platform

# 导入 pytest 测试框架
import pytest

# 导入本地的 util 模块
from . import util

# 导入 numpy 中的 f2py2e 模块的主函数
from numpy.f2py.f2py2e import main as f2pycli

#########################
# CLI utils and classes #
#########################

# 定义一个命名元组 PPaths,表示路径集合
PPaths = namedtuple("PPaths", "finp, f90inp, pyf, wrap77, wrap90, cmodf")


def get_io_paths(fname_inp, mname="untitled"):
    """获取输入文件的路径和生成文件的可能路径

    这个函数用于生成测试时需要的输入和输出文件路径。

    ..note::

         由于这并不实际运行 f2py,因此生成的文件并不一定存在,模块名通常也是错误的

    Parameters
    ----------
    fname_inp : str
                输入文件名
    mname : str, optional
                模块名,默认为 untitled

    Returns
    -------
    genp : NamedTuple PPaths
            可能的生成文件路径集合,不一定全部存在
    """
    # 将输入文件名转换为 Path 对象
    bpath = Path(fname_inp)
    # 返回可能的生成文件路径的命名元组
    return PPaths(
        finp=bpath.with_suffix(".f"),
        f90inp=bpath.with_suffix(".f90"),
        pyf=bpath.with_suffix(".pyf"),
        wrap77=bpath.with_name(f"{mname}-f2pywrappers.f"),
        wrap90=bpath.with_name(f"{mname}-f2pywrappers2.f90"),
        cmodf=bpath.with_name(f"{mname}module.c"),
    )


##############
# CLI Fixtures and Tests #
##############

# 定义 pytest 的 session 级别的 fixture:生成一个 hello_world_f90 的测试文件
@pytest.fixture(scope="session")
def hello_world_f90(tmpdir_factory):
    """生成一个用于测试的单个 f90 文件"""
    # 从 util 模块中读取文件内容
    fdat = util.getpath("tests", "src", "cli", "hiworld.f90").read_text()
    # 创建临时文件路径
    fn = tmpdir_factory.getbasetemp() / "hello.f90"
    # 将文件内容写入临时文件
    fn.write_text(fdat, encoding="ascii")
    # 返回临时文件路径
    return fn


# 定义 pytest 的 session 级别的 fixture:生成一个 gh23598_warn 的测试文件
@pytest.fixture(scope="session")
def gh23598_warn(tmpdir_factory):
    """用于测试 gh23598 中警告的 f90 文件"""
    # 从 util 模块中读取文件内容
    fdat = util.getpath("tests", "src", "crackfortran", "gh23598Warn.f90").read_text()
    # 创建临时文件路径
    fn = tmpdir_factory.getbasetemp() / "gh23598Warn.f90"
    # 将文件内容写入临时文件
    fn.write_text(fdat, encoding="ascii")
    # 返回临时文件路径
    return fn


# 定义 pytest 的 session 级别的 fixture:生成一个 gh22819_cli 的测试文件
@pytest.fixture(scope="session")
def gh22819_cli(tmpdir_factory):
    """用于测试 ghff819 中不允许的 CLI 参数的 f90 文件"""
    # 从 util 模块中读取文件内容
    fdat = util.getpath("tests", "src", "cli", "gh_22819.pyf").read_text()
    # 创建临时文件路径
    fn = tmpdir_factory.getbasetemp() / "gh_22819.pyf"
    # 将文件内容写入临时文件
    fn.write_text(fdat, encoding="ascii")
    # 返回临时文件路径
    return fn


# 定义 pytest 的 session 级别的 fixture:生成一个 hello_world_f77 的测试文件
@pytest.fixture(scope="session")
def hello_world_f77(tmpdir_factory):
    """生成一个用于测试的单个 f77 文件"""
    # 从 util 模块中读取文件内容
    fdat = util.getpath("tests", "src", "cli", "hi77.f").read_text()
    # 创建临时文件路径
    fn = tmpdir_factory.getbasetemp() / "hello.f"
    # 将文件内容写入临时文件
    fn.write_text(fdat, encoding="ascii")
    # 返回临时文件路径
    return fn


# 定义 pytest 的 session 级别的 fixture:生成一个 retreal_f77 的测试文件
@pytest.fixture(scope="session")
def retreal_f77(tmpdir_factory):
    """生成一个用于测试的单个 f77 文件"""
    # 从 util 模块中读取文件内容
    fdat = util.getpath("tests", "src", "return_real", "foo77.f").read_text()
    # 创建临时文件路径
    fn = tmpdir_factory.getbasetemp() / "foo.f"
    # 将文件内容写入临时文件
    fn.write_text(fdat, encoding="ascii")
    # 返回临时文件路径
    return fn
# 定义一个 pytest 的 session 级别的 fixture,用于生成单个的 f90 文件用于测试
@pytest.fixture(scope="session")
def f2cmap_f90(tmpdir_factory):
    # 从指定路径读取 f90 文件的内容作为字符串
    fdat = util.getpath("tests", "src", "f2cmap", "isoFortranEnvMap.f90").read_text()
    # 读取另一个路径下的文件内容作为字符串
    f2cmap = util.getpath("tests", "src", "f2cmap", ".f2py_f2cmap").read_text()
    # 获取临时目录并创建一个新的 f90 文件,写入之前读取的 fdat 内容
    fn = tmpdir_factory.getbasetemp() / "f2cmap.f90"
    # 创建一个名为 mapfile 的文件,并将 f2cmap 的内容写入其中
    fmap = tmpdir_factory.getbasetemp() / "mapfile"
    fn.write_text(fdat, encoding="ascii")
    fmap.write_text(f2cmap, encoding="ascii")
    # 返回生成的 f90 文件的路径
    return fn


# 测试函数,检查模块名称是否正确处理
def test_gh22819_cli(capfd, gh22819_cli, monkeypatch):
    """Check that module names are handled correctly
    gh-22819
    Essentially, the -m name cannot be used to import the module, so the module
    named in the .pyf needs to be used instead

    CLI :: -m and a .pyf file
    """
    # 获取路径对象
    ipath = Path(gh22819_cli)
    # 设置模拟的命令行参数
    monkeypatch.setattr(sys, "argv", f"f2py -m blah {ipath}".split())
    # 在指定路径下执行 f2pycli 命令,并验证生成的文件是否符合预期
    with util.switchdir(ipath.parent):
        f2pycli()
        # 获取生成文件的名称列表
        gen_paths = [item.name for item in ipath.parent.rglob("*") if item.is_file()]
        # 断言生成的特定文件不存在
        assert "blahmodule.c" not in gen_paths
        assert "blah-f2pywrappers.f" not in gen_paths
        # 断言生成的特定文件存在
        assert "test_22819-f2pywrappers.f" in gen_paths
        assert "test_22819module.c" in gen_paths
        assert "Ignoring blah"


# 测试函数,检查是否只允许一个 .pyf 文件存在
def test_gh22819_many_pyf(capfd, gh22819_cli, monkeypatch):
    """Only one .pyf file allowed
    gh-22819
    CLI :: .pyf files
    """
    # 获取路径对象
    ipath = Path(gh22819_cli)
    # 设置模拟的命令行参数
    monkeypatch.setattr(sys, "argv", f"f2py -m blah {ipath} hello.pyf".split())
    # 在指定路径下执行 f2pycli 命令,预期会引发 ValueError 异常
    with util.switchdir(ipath.parent):
        with pytest.raises(ValueError, match="Only one .pyf file per call"):
            f2pycli()


# 测试函数,检查是否生成警告信息
def test_gh23598_warn(capfd, gh23598_warn, monkeypatch):
    # 获取输出路径对象
    foutl = get_io_paths(gh23598_warn, mname="test")
    ipath = foutl.f90inp
    # 设置模拟的命令行参数
    monkeypatch.setattr(
        sys, "argv",
        f'f2py {ipath} -m test'.split())

    with util.switchdir(ipath.parent):
        f2pycli()  # 生成文件
        # 读取生成的包装文件内容
        wrapper = foutl.wrap90.read_text()
        # 断言特定字符串不在包装文件中
        assert "intproductf2pywrap, intpr" not in wrapper


# 测试函数,检查是否生成签名文件
def test_gen_pyf(capfd, hello_world_f90, monkeypatch):
    """Ensures that a signature file is generated via the CLI
    CLI :: -h
    """
    # 获取路径对象
    ipath = Path(hello_world_f90)
    # 定义签名文件的输出路径
    opath = Path(hello_world_f90).stem + ".pyf"
    # 设置模拟的命令行参数
    monkeypatch.setattr(sys, "argv", f'f2py -h {opath} {ipath}'.split())

    with util.switchdir(ipath.parent):
        f2pycli()  # 生成包装文件
        out, _ = capfd.readouterr()
        # 断言输出包含特定提示信息
        assert "Saving signatures to file" in out
        # 断言签名文件是否存在
        assert Path(f'{opath}').exists()


# 测试函数,检查是否可以将签名文件输出到 stdout
def test_gen_pyf_stdout(capfd, hello_world_f90, monkeypatch):
    """Ensures that a signature file can be dumped to stdout
    CLI :: -h
    """
    # 获取路径对象
    ipath = Path(hello_world_f90)
    # 设置模拟的命令行参数
    monkeypatch.setattr(sys, "argv", f'f2py -h stdout {ipath}'.split())
    # 使用 `util.switchdir(ipath.parent)` 进入 `ipath` 的父目录环境,执行以下代码块
    with util.switchdir(ipath.parent):
        # 调用 `f2pycli()` 函数,通常用于与Fortran程序交互的命令行接口
        f2pycli()
        # 读取并捕获标准输出和错误输出
        out, _ = capfd.readouterr()
        # 断言标准输出中包含特定文本 "Saving signatures to file"
        assert "Saving signatures to file" in out
        # 断言标准输出中包含特定文本 "function hi() ! in "
        assert "function hi() ! in " in out
# 确保CLI在不覆盖签名文件的情况下拒绝操作
def test_gen_pyf_no_overwrite(capfd, hello_world_f90, monkeypatch):
    """Ensures that the CLI refuses to overwrite signature files
    CLI :: -h without --overwrite-signature
    """
    # 获取指定Fortran文件的路径对象
    ipath = Path(hello_world_f90)
    # 通过monkeypatch设置模拟的命令行参数
    monkeypatch.setattr(sys, "argv", f'f2py -h faker.pyf {ipath}'.split())

    # 在指定路径下进行上下文切换
    with util.switchdir(ipath.parent):
        # 创建名为"faker.pyf"的文件,内容为"Fake news",ASCII编码
        Path("faker.pyf").write_text("Fake news", encoding="ascii")
        # 确保引发SystemExit异常
        with pytest.raises(SystemExit):
            # 调用f2pycli()函数,预期拒绝覆盖操作
            f2pycli()
            # 读取并捕获标准输出和标准错误
            _, err = capfd.readouterr()
            # 断言标准错误中包含特定提示信息
            assert "Use --overwrite-signature to overwrite" in err


@pytest.mark.skipif((platform.system() != 'Linux') or (sys.version_info <= (3, 12)),
                    reason='Compiler and 3.12 required')
def test_untitled_cli(capfd, hello_world_f90, monkeypatch):
    """Check that modules are named correctly

    CLI :: defaults
    """
    # 获取指定Fortran文件的路径对象
    ipath = Path(hello_world_f90)
    # 通过monkeypatch设置模拟的命令行参数
    monkeypatch.setattr(sys, "argv", f"f2py --backend meson -c {ipath}".split())
    # 在指定路径下进行上下文切换
    with util.switchdir(ipath.parent):
        # 调用f2pycli()函数,检查默认情况下模块命名是否正确
        f2pycli()
        # 读取并捕获标准输出和标准错误
        out, _ = capfd.readouterr()
        # 断言标准输出中包含"untitledmodule.c"
        assert "untitledmodule.c" in out


@pytest.mark.skipif((platform.system() != 'Linux') or (sys.version_info <= (3, 12)), reason='Compiler and 3.12 required')
def test_no_py312_distutils_fcompiler(capfd, hello_world_f90, monkeypatch):
    """Check that no distutils imports are performed on 3.12
    CLI :: --fcompiler --help-link --backend distutils
    """
    # 设置模块名
    MNAME = "hi"
    # 获取输入和输出文件路径对象
    foutl = get_io_paths(hello_world_f90, mname=MNAME)
    ipath = foutl.f90inp
    # 通过monkeypatch设置模拟的命令行参数
    monkeypatch.setattr(
        sys, "argv", f"f2py {ipath} -c --fcompiler=gfortran -m {MNAME}".split()
    )
    # 在指定路径下进行上下文切换
    with util.switchdir(ipath.parent):
        # 调用f2pycli()函数,检查是否使用了不兼容meson的--fcompiler选项
        f2pycli()
        # 读取并捕获标准输出和标准错误
        out, _ = capfd.readouterr()
        # 断言标准输出中包含特定提示信息
        assert "--fcompiler cannot be used with meson" in out
    # 通过monkeypatch设置模拟的命令行参数
    monkeypatch.setattr(
        sys, "argv", f"f2py --help-link".split()
    )
    # 在指定路径下进行上下文切换
    with util.switchdir(ipath.parent):
        # 调用f2pycli()函数,检查是否正确处理了--help-link选项
        f2pycli()
        # 读取并捕获标准输出和标准错误
        out, _ = capfd.readouterr()
        # 断言标准输出中包含特定提示信息
        assert "Use --dep for meson builds" in out
    # 设置新的模块名
    MNAME = "hi2" # 需要一个不同的模块名来进行新的-c操作
    # 通过monkeypatch设置模拟的命令行参数
    monkeypatch.setattr(
        sys, "argv", f"f2py {ipath} -c -m {MNAME} --backend distutils".split()
    )
    # 在指定路径下进行上下文切换
    with util.switchdir(ipath.parent):
        # 调用f2pycli()函数,检查是否正确处理了不兼容Python>=3.12的--backend distutils选项
        f2pycli()
        # 读取并捕获标准输出和标准错误
        out, _ = capfd.readouterr()
        # 断言标准输出中包含特定提示信息
        assert "Cannot use distutils backend with Python>=3.12" in out


@pytest.mark.xfail
def test_f2py_skip(capfd, retreal_f77, monkeypatch):
    """Tests that functions can be skipped
    CLI :: skip:
    """
    # 获取输入文件路径对象和要跳过的函数名称
    foutl = get_io_paths(retreal_f77, mname="test")
    ipath = foutl.finp
    toskip = "t0 t4 t8 sd s8 s4"
    remaining = "td s0"
    # 通过monkeypatch设置模拟的命令行参数
    monkeypatch.setattr(
        sys, "argv",
        f'f2py {ipath} -m test skip: {toskip}'.split())
    # 在指定路径中切换工作目录并执行 f2pycli 命令
    with util.switchdir(ipath.parent):
        # 调用名为 f2pycli 的函数,执行相关操作
        f2pycli()
        # 从捕获的标准输出和标准错误中读取输出和错误信息
        out, err = capfd.readouterr()
        # 遍历需要跳过的任务列表,检查错误信息中是否包含特定字符串
        for skey in toskip.split():
            assert (
                f'buildmodule: Could not found the body of interfaced routine "{skey}". Skipping.'
                in err)
        # 遍历剩余任务列表,检查输出信息中是否包含特定字符串
        for rkey in remaining.split():
            assert f'Constructing wrapper function "{rkey}"' in out
def test_f2py_only(capfd, retreal_f77, monkeypatch):
    """Test that functions can be kept by only:
    CLI :: only:
    """
    # 获取测试所需的输入输出路径对象
    foutl = get_io_paths(retreal_f77, mname="test")
    # 获取输入路径
    ipath = foutl.finp
    # 需要跳过的例外例程
    toskip = "t0 t4 t8 sd s8 s4"
    # 需要保留的例程
    tokeep = "td s0"
    # 使用 monkeypatch 设置模拟的命令行参数
    monkeypatch.setattr(
        sys, "argv",
        f'f2py {ipath} -m test only: {tokeep}'.split())

    # 在指定的目录中执行函数 f2pycli
    with util.switchdir(ipath.parent):
        f2pycli()
        # 读取标准输出和标准错误
        out, err = capfd.readouterr()
        # 检查需要跳过的例外例程是否在错误信息中
        for skey in toskip.split():
            assert (
                f'buildmodule: Could not find the body of interfaced routine "{skey}". Skipping.'
                in err)
        # 检查需要保留的例程是否在输出信息中
        for rkey in tokeep.split():
            assert f'Constructing wrapper function "{rkey}"' in out


def test_file_processing_switch(capfd, hello_world_f90, retreal_f77,
                                monkeypatch):
    """Tests that it is possible to return to file processing mode
    CLI :: :
    BUG: numpy-gh #20520
    """
    # 获取测试所需的输入输出路径对象
    foutl = get_io_paths(retreal_f77, mname="test")
    # 获取输入路径
    ipath = foutl.finp
    # 需要跳过的例外例程
    toskip = "t0 t4 t8 sd s8 s4"
    # 获取另一个输入路径
    ipath2 = Path(hello_world_f90)
    # 需要保留的例程,以及其中的一个例程在 ipath2 中
    tokeep = "td s0 hi"  # hi is in ipath2
    # 模块名
    mname = "blah"
    # 使用 monkeypatch 设置模拟的命令行参数
    monkeypatch.setattr(
        sys,
        "argv",
        f'f2py {ipath} -m {mname} only: {tokeep} : {ipath2}'.split(),
    )

    # 在指定的目录中执行函数 f2pycli
    with util.switchdir(ipath.parent):
        f2pycli()
        # 读取标准输出和标准错误
        out, err = capfd.readouterr()
        # 检查需要跳过的例外例程是否在错误信息中
        for skey in toskip.split():
            assert (
                f'buildmodule: Could not find the body of interfaced routine "{skey}". Skipping.'
                in err)
        # 检查需要保留的例程是否在输出信息中
        for rkey in tokeep.split():
            assert f'Constructing wrapper function "{rkey}"' in out


def test_mod_gen_f77(capfd, hello_world_f90, monkeypatch):
    """Checks the generation of files based on a module name
    CLI :: -m
    """
    # 模块名
    MNAME = "hi"
    # 获取测试所需的输入输出路径对象
    foutl = get_io_paths(hello_world_f90, mname=MNAME)
    # 获取输入路径
    ipath = foutl.f90inp
    # 使用 monkeypatch 设置模拟的命令行参数
    monkeypatch.setattr(sys, "argv", f'f2py {ipath} -m {MNAME}'.split())
    # 在指定的目录中执行函数 f2pycli
    with util.switchdir(ipath.parent):
        f2pycli()

    # 检查是否生成了 C 模块文件
    assert Path.exists(foutl.cmodf)
    # 检查文件是否包含函数,以便检查 F77 封装器
    assert Path.exists(foutl.wrap77)


def test_mod_gen_gh25263(capfd, hello_world_f77, monkeypatch):
    """Check that pyf files are correctly generated with module structure
    CLI :: -m <name> -h pyf_file
    BUG: numpy-gh #20520
    """
    # 模块名
    MNAME = "hi"
    # 获取测试所需的输入输出路径对象
    foutl = get_io_paths(hello_world_f77, mname=MNAME)
    # 获取输入路径
    ipath = foutl.finp
    # 使用 monkeypatch 设置模拟的命令行参数
    monkeypatch.setattr(sys, "argv", f'f2py {ipath} -m {MNAME} -h hi.pyf'.split())
    # 在指定的目录中执行函数 f2pycli
    with util.switchdir(ipath.parent):
        f2pycli()
        # 打开生成的 hi.pyf 文件,读取内容进行检查
        with Path('hi.pyf').open() as hipyf:
            pyfdat = hipyf.read()
            # 断言生成的 pyf 文件包含指定的模块名信息
            assert "python module hi" in pyfdat


def test_lower_cmod(capfd, hello_world_f77, monkeypatch):
    """Lowers cases by flag or when -h is present

    CLI :: --[no-]lower
    """
    # 获取测试所需的输入输出路径对象
    foutl = get_io_paths(hello_world_f77, mname="test")
    # 获取输入路径
    ipath = foutl.finp
    # 编译正则表达式,匹配形如 "HI()" 的字符串
    capshi = re.compile(r"HI\(\)")
    # 编译正则表达式,匹配形如 "hi()" 的字符串
    capslo = re.compile(r"hi\(\)")
    
    # Case I: 当传递了 --lower 参数
    # 使用 monkeypatch 修改 sys.argv,模拟命令行参数为 'f2py <ipath> -m test --lower'
    monkeypatch.setattr(sys, "argv", f'f2py {ipath} -m test --lower'.split())
    # 在 ipath.parent 目录下执行代码块
    with util.switchdir(ipath.parent):
        # 调用 f2pycli 函数
        f2pycli()
        # 读取并捕获输出
        out, _ = capfd.readouterr()
        # 断言输出中应存在形如 "hi()" 的字符串
        assert capslo.search(out) is not None
        # 断言输出中不应存在形如 "HI()" 的字符串
        assert capshi.search(out) is None
    
    # Case II: 当传递了 --no-lower 参数
    # 使用 monkeypatch 修改 sys.argv,模拟命令行参数为 'f2py <ipath> -m test --no-lower'
    monkeypatch.setattr(sys, "argv", f'f2py {ipath} -m test --no-lower'.split())
    # 在 ipath.parent 目录下执行代码块
    with util.switchdir(ipath.parent):
        # 调用 f2pycli 函数
        f2pycli()
        # 读取并捕获输出
        out, _ = capfd.readouterr()
        # 断言输出中不应存在形如 "hi()" 的字符串
        assert capslo.search(out) is None
        # 断言输出中应存在形如 "HI()" 的字符串
        assert capshi.search(out) is not None
def test_lower_sig(capfd, hello_world_f77, monkeypatch):
    """Lowers cases in signature files by flag or when -h is present

    CLI :: --[no-]lower -h
    """
    # 获取输入输出路径对象
    foutl = get_io_paths(hello_world_f77, mname="test")
    # 获取输入路径
    ipath = foutl.finp

    # Signature files
    # 定义正则表达式,用于匹配特定字符串
    capshi = re.compile(r"Block: HI")
    capslo = re.compile(r"Block: hi")

    # Case I: --lower is implied by -h
    # TODO: Clean up to prevent passing --overwrite-signature
    # 修改系统参数,模拟命令行输入
    monkeypatch.setattr(
        sys,
        "argv",
        f'f2py {ipath} -h {foutl.pyf} -m test --overwrite-signature'.split(),
    )

    # 在指定目录下执行命令行工具
    with util.switchdir(ipath.parent):
        f2pycli()  # 调用f2pycli函数
        out, _ = capfd.readouterr()  # 读取标准输出和标准错误
        assert capslo.search(out) is not None  # 断言输出中存在"Block: hi"
        assert capshi.search(out) is None  # 断言输出中不存在"Block: HI"

    # Case II: --no-lower overrides -h
    # 修改系统参数,模拟命令行输入
    monkeypatch.setattr(
        sys,
        "argv",
        f'f2py {ipath} -h {foutl.pyf} -m test --overwrite-signature --no-lower'
        .split(),
    )

    # 在指定目录下执行命令行工具
    with util.switchdir(ipath.parent):
        f2pycli()  # 调用f2pycli函数
        out, _ = capfd.readouterr()  # 读取标准输出和标准错误
        assert capslo.search(out) is None  # 断言输出中不存在"Block: hi"
        assert capshi.search(out) is not None  # 断言输出中存在"Block: HI"


def test_build_dir(capfd, hello_world_f90, monkeypatch):
    """Ensures that the build directory can be specified

    CLI :: --build-dir
    """
    # 获取输入路径对象
    ipath = Path(hello_world_f90)
    mname = "blah"
    odir = "tttmp"

    # 修改系统参数,模拟命令行输入
    monkeypatch.setattr(sys, "argv",
                        f'f2py -m {mname} {ipath} --build-dir {odir}'.split())

    # 在指定目录下执行命令行工具
    with util.switchdir(ipath.parent):
        f2pycli()  # 调用f2pycli函数
        out, _ = capfd.readouterr()  # 读取标准输出和标准错误
        assert f"Wrote C/API module \"{mname}\"" in out  # 断言输出中包含特定信息


def test_overwrite(capfd, hello_world_f90, monkeypatch):
    """Ensures that the build directory can be specified

    CLI :: --overwrite-signature
    """
    # 获取输入路径对象
    ipath = Path(hello_world_f90)

    # 修改系统参数,模拟命令行输入
    monkeypatch.setattr(
        sys, "argv",
        f'f2py -h faker.pyf {ipath} --overwrite-signature'.split())

    # 在指定目录下创建文件并写入内容
    with util.switchdir(ipath.parent):
        Path("faker.pyf").write_text("Fake news", encoding="ascii")
        f2pycli()  # 调用f2pycli函数
        out, _ = capfd.readouterr()  # 读取标准输出和标准错误
        assert "Saving signatures to file" in out  # 断言输出中包含特定信息


def test_latexdoc(capfd, hello_world_f90, monkeypatch):
    """Ensures that TeX documentation is written out

    CLI :: --latex-doc
    """
    # 获取输入路径对象
    ipath = Path(hello_world_f90)
    mname = "blah"

    # 修改系统参数,模拟命令行输入
    monkeypatch.setattr(sys, "argv",
                        f'f2py -m {mname} {ipath} --latex-doc'.split())

    # 在指定目录下执行命令行工具
    with util.switchdir(ipath.parent):
        f2pycli()  # 调用f2pycli函数
        out, _ = capfd.readouterr()  # 读取标准输出和标准错误
        assert "Documentation is saved to file" in out  # 断言输出中包含特定信息
        with Path(f"{mname}module.tex").open() as otex:
            assert "\\documentclass" in otex.read()  # 断言生成的TeX文件中包含特定内容


def test_nolatexdoc(capfd, hello_world_f90, monkeypatch):
    """Ensures that TeX documentation is written out

    CLI :: --no-latex-doc
    """
    # 获取输入路径对象
    ipath = Path(hello_world_f90)
    mname = "blah"
    # 使用 monkeypatch 模块设置 sys.argv,模拟命令行参数以调用 f2py 工具
    monkeypatch.setattr(sys, "argv",
                        f'f2py -m {mname} {ipath} --no-latex-doc'.split())
    
    # 在指定的路径 ipath 的父目录下执行代码块
    with util.switchdir(ipath.parent):
        # 调用 f2pycli 函数,执行 f2py 工具的命令
        f2pycli()
        # 读取并捕获标准输出和标准错误输出
        out, _ = capfd.readouterr()
        # 断言确保输出中不包含特定的文本 "Documentation is saved to file"
        assert "Documentation is saved to file" not in out
def test_shortlatex(capfd, hello_world_f90, monkeypatch):
    """Ensures that truncated documentation is written out

    TODO: Test to ensure this has no effect without --latex-doc
    CLI :: --latex-doc --short-latex
    """
    # 将文件路径转换为 Path 对象
    ipath = Path(hello_world_f90)
    # 定义模块名
    mname = "blah"
    # 使用 monkeypatch 设置 sys.argv 以模拟命令行参数
    monkeypatch.setattr(
        sys,
        "argv",
        f'f2py -m {mname} {ipath} --latex-doc --short-latex'.split(),
    )

    # 切换到指定路径的上级目录
    with util.switchdir(ipath.parent):
        # 调用 f2pycli 函数
        f2pycli()
        # 读取 stdout 和 stderr
        out, _ = capfd.readouterr()
        # 断言输出中包含特定信息
        assert "Documentation is saved to file" in out
        # 打开生成的 LaTeX 文件,断言文件内容不包含 LaTeX 文档标识
        with Path(f"./{mname}module.tex").open() as otex:
            assert "\\documentclass" not in otex.read()


def test_restdoc(capfd, hello_world_f90, monkeypatch):
    """Ensures that RsT documentation is written out

    CLI :: --rest-doc
    """
    ipath = Path(hello_world_f90)
    mname = "blah"
    # 使用 monkeypatch 设置 sys.argv 以模拟命令行参数
    monkeypatch.setattr(sys, "argv",
                        f'f2py -m {mname} {ipath} --rest-doc'.split())

    # 切换到指定路径的上级目录
    with util.switchdir(ipath.parent):
        # 调用 f2pycli 函数
        f2pycli()
        # 读取 stdout 和 stderr
        out, _ = capfd.readouterr()
        # 断言输出中包含特定信息
        assert "ReST Documentation is saved to file" in out
        # 打开生成的 ReST 文件,断言文件内容包含 ReST 标识
        with Path(f"./{mname}module.rest").open() as orst:
            assert r".. -*- rest -*-" in orst.read()


def test_norestexdoc(capfd, hello_world_f90, monkeypatch):
    """Ensures that TeX documentation is written out

    CLI :: --no-rest-doc
    """
    ipath = Path(hello_world_f90)
    mname = "blah"
    # 使用 monkeypatch 设置 sys.argv 以模拟命令行参数
    monkeypatch.setattr(sys, "argv",
                        f'f2py -m {mname} {ipath} --no-rest-doc'.split())

    # 切换到指定路径的上级目录
    with util.switchdir(ipath.parent):
        # 调用 f2pycli 函数
        f2pycli()
        # 读取 stdout 和 stderr
        out, _ = capfd.readouterr()
        # 断言输出中不包含特定信息
        assert "ReST Documentation is saved to file" not in out


def test_debugcapi(capfd, hello_world_f90, monkeypatch):
    """Ensures that debugging wrappers are written

    CLI :: --debug-capi
    """
    ipath = Path(hello_world_f90)
    mname = "blah"
    # 使用 monkeypatch 设置 sys.argv 以模拟命令行参数
    monkeypatch.setattr(sys, "argv",
                        f'f2py -m {mname} {ipath} --debug-capi'.split())

    # 切换到指定路径的上级目录
    with util.switchdir(ipath.parent):
        # 调用 f2pycli 函数
        f2pycli()
        # 打开生成的 C 模块文件,断言文件内容包含调试相关标识
        with Path(f"./{mname}module.c").open() as ocmod:
            assert r"#define DEBUGCFUNCS" in ocmod.read()


@pytest.mark.skip(reason="Consistently fails on CI; noisy so skip not xfail.")
def test_debugcapi_bld(hello_world_f90, monkeypatch):
    """Ensures that debugging wrappers work

    CLI :: --debug-capi -c
    """
    ipath = Path(hello_world_f90)
    mname = "blah"
    # 使用 monkeypatch 设置 sys.argv 以模拟命令行参数
    monkeypatch.setattr(sys, "argv",
                        f'f2py -m {mname} {ipath} -c --debug-capi'.split())

    # 切换到指定路径的上级目录
    with util.switchdir(ipath.parent):
        # 调用 f2pycli 函数
        f2pycli()
        # 构建 Python 命令来运行生成的模块,断言输出符合预期
        cmd_run = shlex.split("python3 -c \"import blah; blah.hi()\"")
        rout = subprocess.run(cmd_run, capture_output=True, encoding='UTF-8')
        eout = ' Hello World\n'
        eerr = textwrap.dedent("""\
debug-capi:Python C/API function blah.hi()
debug-capi:float hi=:output,hidden,scalar
debug-capi:hi=0
debug-capi:Fortran subroutine `f2pywraphi(&hi)'
# 设置一个 debug-capi 标签的日志条目,表明当前 hi 值为 0
debug-capi:hi=0
# 标记 debug-capi,指示正在构建返回值
debug-capi:Building return value.
# 记录 Python C/API 函数 blah.hi 的成功执行
debug-capi:Python C/API function blah.hi: successful.
# 释放内存的 debug-capi 标签日志条目
debug-capi:Freeing memory.
        """)
# 断言标准输出 rout.stdout 等于期望输出 eout
        assert rout.stdout == eout
# 断言标准错误 rout.stderr 等于期望错误输出 eerr
        assert rout.stderr == eerr


def test_wrapfunc_def(capfd, hello_world_f90, monkeypatch):
    """Ensures that fortran subroutine wrappers for F77 are included by default

    CLI :: --[no]-wrap-functions
    """
    # 隐式设置
    ipath = Path(hello_world_f90)
    mname = "blah"
    monkeypatch.setattr(sys, "argv", f'f2py -m {mname} {ipath}'.split())

    with util.switchdir(ipath.parent):
        f2pycli()
    out, _ = capfd.readouterr()
    assert r"Fortran 77 wrappers are saved to" in out

    # 显式设置
    monkeypatch.setattr(sys, "argv",
                        f'f2py -m {mname} {ipath} --wrap-functions'.split())

    with util.switchdir(ipath.parent):
        f2pycli()
        out, _ = capfd.readouterr()
        assert r"Fortran 77 wrappers are saved to" in out


def test_nowrapfunc(capfd, hello_world_f90, monkeypatch):
    """Ensures that fortran subroutine wrappers for F77 can be disabled

    CLI :: --no-wrap-functions
    """
    ipath = Path(hello_world_f90)
    mname = "blah"
    monkeypatch.setattr(sys, "argv",
                        f'f2py -m {mname} {ipath} --no-wrap-functions'.split())

    with util.switchdir(ipath.parent):
        f2pycli()
        out, _ = capfd.readouterr()
        assert r"Fortran 77 wrappers are saved to" not in out


def test_inclheader(capfd, hello_world_f90, monkeypatch):
    """Add to the include directories

    CLI :: -include
    TODO: Document this in the help string
    """
    ipath = Path(hello_world_f90)
    mname = "blah"
    monkeypatch.setattr(
        sys,
        "argv",
        f'f2py -m {mname} {ipath} -include<stdbool.h> -include<stdio.h> '.
        split(),
    )

    with util.switchdir(ipath.parent):
        f2pycli()
        with Path(f"./{mname}module.c").open() as ocmod:
            ocmr = ocmod.read()
            assert "#include <stdbool.h>" in ocmr
            assert "#include <stdio.h>" in ocmr


def test_inclpath():
    """Add to the include directories

    CLI :: --include-paths
    """
    # TODO: populate
    pass


def test_hlink():
    """Add to the include directories

    CLI :: --help-link
    """
    # TODO: populate
    pass


def test_f2cmap(capfd, f2cmap_f90, monkeypatch):
    """Check that Fortran-to-Python KIND specs can be passed

    CLI :: --f2cmap
    """
    ipath = Path(f2cmap_f90)
    monkeypatch.setattr(sys, "argv", f'f2py -m blah {ipath} --f2cmap mapfile'.split())
    # 切换工作目录到输入路径的父目录
    with util.switchdir(ipath.parent):
        # 调用名为 f2pycli 的函数
        f2pycli()
        # 读取并捕获标准输出和错误输出
        out, _ = capfd.readouterr()
        # 断言输出中包含特定的字符串
        assert "Reading f2cmap from 'mapfile' ..." in out
        assert "Mapping \"real(kind=real32)\" to \"float\"" in out
        assert "Mapping \"real(kind=real64)\" to \"double\"" in out
        assert "Mapping \"integer(kind=int64)\" to \"long_long\"" in out
        assert "Successfully applied user defined f2cmap changes" in out
# 函数定义:测试函数,用于验证是否成功减少输出的详细程度
def test_quiet(capfd, hello_world_f90, monkeypatch):
    """Reduce verbosity

    CLI :: --quiet
    """
    # 获取文件路径对象
    ipath = Path(hello_world_f90)
    # 设置模拟的命令行参数,将输出设置为静默模式
    monkeypatch.setattr(sys, "argv", f'f2py -m blah {ipath} --quiet'.split())

    # 在文件路径的父目录中执行测试
    with util.switchdir(ipath.parent):
        # 调用 f2pycli 函数
        f2pycli()
        # 读取并捕获标准输出和标准错误
        out, _ = capfd.readouterr()
        # 断言标准输出为空
        assert len(out) == 0


# 函数定义:测试函数,用于验证是否成功增加输出的详细程度
def test_verbose(capfd, hello_world_f90, monkeypatch):
    """Increase verbosity

    CLI :: --verbose
    """
    # 获取文件路径对象
    ipath = Path(hello_world_f90)
    # 设置模拟的命令行参数,将输出设置为详细模式
    monkeypatch.setattr(sys, "argv", f'f2py -m blah {ipath} --verbose'.split())

    # 在文件路径的父目录中执行测试
    with util.switchdir(ipath.parent):
        # 调用 f2pycli 函数
        f2pycli()
        # 读取并捕获标准输出和标准错误
        out, _ = capfd.readouterr()
        # 断言标准输出中包含指定字符串
        assert "analyzeline" in out


# 函数定义:测试函数,用于验证版本输出
def test_version(capfd, monkeypatch):
    """Ensure version

    CLI :: -v
    """
    # 设置模拟的命令行参数,获取版本信息
    monkeypatch.setattr(sys, "argv", 'f2py -v'.split())
    # 期望抛出 SystemExit 异常
    with pytest.raises(SystemExit):
        # 调用 f2pycli 函数
        f2pycli()
        # 读取并捕获标准输出和标准错误
        out, _ = capfd.readouterr()
        # 导入 NumPy 库,断言其版本号与输出的版本号一致
        import numpy as np
        assert np.__version__ == out.strip()


# 函数定义:跳过的测试函数,因为在持续集成中经常失败,不进行预期失败标记
@pytest.mark.skip(reason="Consistently fails on CI; noisy so skip not xfail.")
def test_npdistop(hello_world_f90, monkeypatch):
    """
    CLI :: -c
    """
    # 获取文件路径对象
    ipath = Path(hello_world_f90)
    # 设置模拟的命令行参数,编译为 C 语言模块
    monkeypatch.setattr(sys, "argv", f'f2py -m blah {ipath} -c'.split())

    # 在文件路径的父目录中执行测试
    with util.switchdir(ipath.parent):
        # 调用 f2pycli 函数
        f2pycli()
        # 构建命令行,运行 Python 代码以调用编译的模块
        cmd_run = shlex.split("python -c \"import blah; blah.hi()\"")
        # 运行命令,捕获输出
        rout = subprocess.run(cmd_run, capture_output=True, encoding='UTF-8')
        # 预期的输出
        eout = ' Hello World\n'
        # 断言运行结果的标准输出与预期输出一致
        assert rout.stdout == eout


# 函数定义:测试函数,用于验证 Numpy distutils 的编译器参数 --fcompiler
def test_npd_fcompiler():
    """
    CLI :: -c --fcompiler
    """
    # TODO: populate
    pass


# 函数定义:测试函数,用于验证 Numpy distutils 的编译器参数 --compiler
def test_npd_compiler():
    """
    CLI :: -c --compiler
    """
    # TODO: populate
    pass


# 函数定义:测试函数,用于验证 Numpy distutils 的帮助编译器参数 --help-fcompiler
def test_npd_help_fcompiler():
    """
    CLI :: -c --help-fcompiler
    """
    # TODO: populate
    pass


# 函数定义:测试函数,用于验证 Numpy distutils 的 Fortran 77 执行器参数 --f77exec
def test_npd_f77exec():
    """
    CLI :: -c --f77exec
    """
    # TODO: populate
    pass


# 函数定义:测试函数,用于验证 Numpy distutils 的 Fortran 90 执行器参数 --f90exec
def test_npd_f90exec():
    """
    CLI :: -c --f90exec
    """
    # TODO: populate
    pass


# 函数定义:测试函数,用于验证 Numpy distutils 的 Fortran 77 编译标志参数 --f77flags
def test_npd_f77flags():
    """
    CLI :: -c --f77flags
    """
    # TODO: populate
    pass


# 函数定义:测试函数,用于验证 Numpy distutils 的 Fortran 90 编译标志参数 --f90flags
def test_npd_f90flags():
    """
    CLI :: -c --f90flags
    """
    # TODO: populate
    pass


# 函数定义:测试函数,用于验证 Numpy distutils 的优化参数 --opt
def test_npd_opt():
    """
    CLI :: -c --opt
    """
    # TODO: populate
    pass


# 函数定义:测试函数,用于验证 Numpy distutils 的架构参数 --arch
def test_npd_arch():
    """
    CLI :: -c --arch
    """
    # TODO: populate
    pass


# 函数定义:测试函数,用于验证 Numpy distutils 的禁用优化参数 --noopt
def test_npd_noopt():
    """
    CLI :: -c --noopt
    """
    # TODO: populate
    pass


# 函数定义:测试函数,用于验证 Numpy distutils 的禁用架构参数 --noarch
def test_npd_noarch():
    """
    CLI :: -c --noarch
    """
    # TODO: populate
    pass


# 函数定义:测试函数,用于验证 Numpy distutils 的调试参数 --debug
def test_npd_debug():
    """
    CLI :: -c --debug
    """
    # TODO: populate
    pass


# 函数定义:测试函数,用于验证 Numpy distutils 的链接自动化参数 --link-<resource>
def test_npd_link_auto():
    """
    CLI :: -c --link-<resource>
    """
    # TODO: populate
    pass


# 函数定义:测试函数,用于验证 Numpy distutils 的库参数 --lib
def test_npd_lib():
    """
    CLI :: -c -L/path/to/lib/ -l<libname>
    """
    # 命令行接口(CLI)的说明,用法示例为 `-c -L/path/to/lib/ -l<libname>`
    # 在这里添加更多详细信息或操作说明
    pass
# 定义测试函数 `test_npd_define`,用于测试命令行接口中的 `-D<define>` 参数
def test_npd_define():
    """
    CLI :: -D<define>
    """
    # TODO: populate
    pass


# 定义测试函数 `test_npd_undefine`,用于测试命令行接口中的 `-U<name>` 参数
def test_npd_undefine():
    """
    CLI :: -U<name>
    """
    # TODO: populate
    pass


# 定义测试函数 `test_npd_incl`,用于测试命令行接口中的 `-I/path/to/include/` 参数
def test_npd_incl():
    """
    CLI :: -I/path/to/include/
    """
    # TODO: populate
    pass


# 定义测试函数 `test_npd_linker`,用于测试命令行接口中的 `<filename>.o <filename>.so <filename>.a` 参数
def test_npd_linker():
    """
    CLI :: <filename>.o <filename>.so <filename>.a
    """
    # TODO: populate
    pass