微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

用mypy键入元类工厂

如何解决用mypy键入元类工厂

上下文:

我为fibex files写了一个代码生成器。

  • Fibex几乎假定人们使用ISO-C语言编写代码。它根据您可以调用方法,可以订阅的事件,可以获取/设置/订阅的字段来定义汽车中每个ECU(电子控制单元)提供的服务。在“基本数据类型”方面还定义了很多数据类型。

  • 使用这些存根的测试人员以python编写

Say Fibex使用基本类型“ 64位浮点IEEE标准”定义了Float64类型,并提供了一些其他信息。目标是生成一个继承自c_double的Float64类,而且还转发基础类型中的所有dunder方法

例如,c_double和Float64均未定义__imul____add__。因此,这些将不起作用:

x = c_double(1) + c_double(2)
x *= c_double(2)
x = Float64(1) + c_double(2)
x *= Float64 (2)

代码

这里是元类,它将在生成代码中复制为'basic_types_py',它主要来自dunder转发和一些健全性检查:

""" Metaclass for generated integral types wrappers from fibex.
    Values are 'generated as hardcoded' so testers can't mess up with them.

    This class takes care of the coherence of these values with one another at class creation time
"""

from ctypes import sizeof,c_double

binary_op = [
    '__add__','__sub__','__mul__','__matmul__','__truediv__','__floordiv__','__mod__','__divmod__','__pow__','__lshift__','__rshift__','__and__','__xor__','__or__','__radd__','__rsub__','__rmul__','__rmatmul__','__rtruediv__','__rfloordiv__','__rmod__','__rdivmod__','__rpow__','__rlshift__','__rrshift__','__rand__','__rxor__','__ror__','__iadd__','__isub__','__imul__','__imatmul__','__itruediv__','__ifloordiv__','__imod__','__ipow__','__ilshift__','__irshift__','__iand__','__ixor__','__ior__',]

comp_op = ['__gt__','__ge__','__lt__','__le__','__eq__','__ne__']

unary_op = [
    '__neg__','__pos__','__abs__','__invert__','__index__','__round__','__trunc__','__floor__','__ceil__'
]


def limits(c_int_type,bit_size: int):
    """ Compute Limits (min,max) for c_types,for given bit_size"""
    assert (bit_size > 0 and hasattr(c_int_type(0),'value') and isinstance(c_int_type(0).value,int) and bit_size > 0)
    signed = c_int_type(-1).value < c_int_type(0).value
    if bit_size == 0:
        bit_size = sizeof(c_int_type) * 8
    signed_limit = 2 ** (bit_size - 1)
    return (-signed_limit,signed_limit - 1) if signed else (0,2 * signed_limit - 1)


def make_Meta(_type,is_integer=True):
    class MetaNum(type(_type)):
        def __new__(cls,name,bases,dct):
            type_attr: list[str] = type(_type(0).value).__dict__.keys()

            class __forward__:
                def __init__(self,attribute_name):
                    self.attr_name = attribute_name

                def __call__(self,mode='n'):
                    t = type(_type(0).value)

                    def op(this,other):
                        if hasattr(other,'value'):
                            return type(this)(t.__dict__[self.attr_name](this.value,t(other.value)))
                        else:
                            return type(this)(t.__dict__[self.attr_name](this.value,t(other)))

                    def c_op(this,'value'):
                            return t.__dict__[self.attr_name](this.value,t(other.value))
                        else:
                            return t.__dict__[self.attr_name](this.value,t(other))

                    def i_op(this,'value'):
                            self.value = t(t.__dict__[self.attr_name](this.value,t(other.value)))
                        else:
                            self.value = t(t.__dict__[self.attr_name](this.value,t(other)))
                        return self

                    def u_op(this):
                        return type(this)(t.__dict__[self.attr_name](this.value))

                    if mode == 'c':
                        return c_op
                    if mode == 'i':
                        return i_op
                    if mode == 'u':
                        return u_op
                    return op

            for attr_name in type_attr:
                if attr_name not in dct.keys():
                    if attr_name in binary_op:
                        if attr_name.startswith('__i'):
                            dct[attr_name] = __forward__(attr_name)('i')
                        else:
                            dct[attr_name] = __forward__(attr_name)('n')
                    if attr_name in comp_op:
                        dct[attr_name] = __forward__(attr_name)('c')
                    if attr_name in unary_op:
                        dct[attr_name] = __forward__(attr_name)('u')

            def __repr__(self):
                return str(bases[0].__name__) + '(' + str(self.value) + ')[' + name + ']'

            dct['__repr__'] = __repr__

            return super().__new__(cls,dct)

    class MetaInt(MetaNum):
        def __new__(cls,dct):
            bit_size = dct['__getattr__'](None,'bit_size')
            byte_size = dct['__getattr__'](None,'byte_size')
            signed = dct['__getattr__'](None,'signed')
            _min = dct['__getattr__'](None,'min')
            _max = dct['__getattr__'](None,'max')

            assert (1 <= bit_size <= byte_size * 8)
            assert (byte_size == sizeof(bases[0]))
            assert (signed == (bases[0](-1).value < bases[0](0).value))
            assert (limits(bases[0],bit_size=bit_size) == (_min,_max))

            return super().__new__(cls,dct)

    class MetaFloat(MetaNum):
        def __new__(cls,'byte_size')

            assert (bit_size == byte_size * 8)
            assert (bit_size in [32,64])
            assert (byte_size in [4,8])

            return super().__new__(cls,dct)

    if is_integer:
        return MetaInt
    else:
        return MetaFloat

以下是使用它生成代码的示例:

# Example Generated
class Float64(c_double,Metaclass=make_Meta(c_double,False)):

    __FIBEX_ID = '_594753200'

    def __getattr__(self,name):
        if name == 'bit_size':
            return 64
        elif name == 'byte_size':
            return 8
        return super().__getattribute__(name)

    def __setattr__(self,key,value):
        if key == 'value':
            if isinstance(value,(c_double,Float64)):
                value = value.value
        super().__setattr__(key,float(value))

    def __init__(self,value):
        self.value = value
        super().__init__(value)

这是测试人员 可能的使用方式:

print(Float64(1) + Float64(2))
print(Float64(3) ** Float64(2))
print(c_double(3) ** Float64(2))
print(3 ** Float64(2))
print(c_double(3.0) ** Float64(2))
print(3.0 ** Float64(2))
print(c_double(3) ** Float64(2.0))
print(3 ** Float64(2.0))
print(c_double(3.0) ** Float64(2.0))
print(3.0 ** Float64(2.0))

print(abs(Float64(-2.0)))
print(round(Float64(-2.1)))

print(Float64(1) > 0.5)
print(Float64(1) > Float64(0.5))
print(Float64(1) > c_double(0.5))
print(Float64(1) < 0.5)



print(Float64(1) < Float64(0.5))
print(Float64(1) < c_double(0.5))

x = Float64(1)
x += 3.1
print(x)

问题

如何用mypy键入? Iso Auditor甚至对我们测试代码的方式也很挑剔,因此要求我尽可能键入hint。我正在运行以下命令行:

mypy --strict --warn-unreachable --strict-optional --no-implicit-optional --warn-redundant-casts --warn-unused-ignores --warn-return-any --warn-unused-configs --no-incremental fibex2python

对于正常代码来说,这很少有问题,但是在这里

例如,我如何键入采用以下格式的函数的返回类型 类型T,并返回一个内部定义的继承自类型T的类?

对不起,这个问题很琐碎,我是C / C ++开发人员,python对我来说比较新。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。