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

时间:2013-03-08 11:41:10

标签: python metaclass bytecode-manipulation

这里是龙。你已被警告过了。

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

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

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

>>> TypeError: can't set attributes of built-in/extension type 'type'

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

2 个答案:

答案 0 :(得分:9)

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

__build_class__

这仅限于自定义类 ,但它确实为您提供了一个全能的钩子。

对于3.1之前的Python版本,您可以忘记挂钩创建类。如果没有定义元类,则C build_class function直接使用C类型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 值,它永远不会从type()模块中查找它,因此您无法覆盖它。

答案 1 :(得分:2)

我喜欢你的主意,但我认为你的目标略有偏差。如果代码调用库函数而不是类,该怎么办?你的假类型()永远不会被调用,你永远不会被告知你没有模仿那个库函数。 Django和任何真正的代码库都有很多实用功能。

我建议你以Python补丁的形式编写所需的解释器级支持。或者你可能会发现在PyPy的代码库中添加这样的钩子更容易,这是用Python本身编写的,而不是弄乱Python的C源。

我刚刚意识到Python解释器包含一套全面的工具,可以让任何一段Python代码逐步执行任何其他代码,检查它对每个函数调用的作用,甚至每个函数调用。如果需要,正在执行Python行。

sys.setprofile应该足以满足您的需求。有了它,你可以安装一个钩子(一个回调),它将被通知目标程序正在进行的每个函数调用。您无法使用它来更改目标程序的行为,但您可以收集有关它的统计信息,包括“模拟覆盖率”指标。

Python关于Profilers的文档介绍了许多基于sys.setprofile构建的模块。您可以研究他们的来源,看看如何有效地使用它。

如果结果不够,仍然有sys.settrace,这是一种严厉的方法,允许您逐步执行目标程序的每一行,检查其变量并修改其执行。标准模块bdb.py构建在sys.settrace之上,并实现了一组标准的调试工具(断点,步入,跳过等)。它由pdb.py使用,它是命令行调试器,以及其他图形调试器。

有了这两个钩子,你应该没事。