在Python运行之前覆盖默认的type()元类

the*_*row 9 python metaclass bytecode-manipulation

这里是龙.你被警告过了.

我正在考虑创建一个新的库,试图帮助编写一个更好的测试套件.
为了做到这一点,其中一个功能是一个功能,它验证正在使用的任何不是测试运行器和被测系统的对象都有一个测试对象(模拟对象,存根,伪造或虚拟对象) ).如果测试人员想要活动对象并因此减少测试隔离,则必须明确指定.

我看到这样做的唯一方法是覆盖内置type()函数,它是默认的元类.
新的默认元类将检查测试双重注册表字典,以查看它是否已被测试双重替换或是否指定了活动对象.

当然,这不可能通过Python本身实现:

>>> TypeError: can't set attributes of built-in/extension type 'type'
Run Code Online (Sandbox Code Playgroud)

有没有办法在测试套件运行之前干预Python的元类查找(可能还有Python)?
也许使用字节码操作?但到底怎么样?

Mar*_*ers 9

以下是不可取的,你会遇到很多问题和角落实现你的想法,但是在Python 3.1及以后,你可以通过覆盖内置的钩子来挂钩自定义类创建过程__build_class__:

import builtins


_orig_build_class = builtins.__build_class__


class SomeMockingMeta(type):
    # whatever


def my_build_class(func, name, *bases, **kwargs):
    if not any(isinstance(b, type) for b in bases):
        # a 'regular' class, not a metaclass
        if 'metaclass' in kwargs:
            if not isinstance(kwargs['metaclass'], type):
                # the metaclass is a callable, but not a class
                orig_meta = kwargs.pop('metaclass')
                class HookedMeta(SomeMockingMeta):
                    def __new__(meta, name, bases, attrs):
                        return orig_meta(name, bases, attrs)
                kwargs['metaclass'] = HookedMeta
            else:
                # There already is a metaclass, insert ours and hope for the best
                class SubclassedMeta(SomeMockingMeta, kwargs['metaclass']):
                    pass
                kwargs['metaclass'] = SubclassedMeta
        else:
            kwargs['metaclass'] = SomeMockingMeta

    return _orig_build_class(func, name, *bases, **kwargs)


builtins.__build_class__ = my_build_class
Run Code Online (Sandbox Code Playgroud)

这仅限于自定义类,但不会给你一个全能的钩.

对于3.1之前的Python版本,您可以忘记挂钩类创建.如果没有定义元类,C build_class函数直接使用C类型type()值,它从不从__builtin__模块中查找,因此您无法覆盖它.

  • @the_drow:我的意思是当你走这条路时你会发现许多角落案例和其他问题,所以这将是一个实施挑战.我同意Ned的观点并认为嘲笑应该是明确的,而不是像你提议的那样隐含和自动化.但至少你现在知道枪柜的位置,我只是希望你不要射击自己的脚.:-) (2认同)