Pythonic拦截函数调用的方法?

时间:2010-08-01 18:30:24

标签: python testing macros scope

为了测试查询环境的内容(例如os.getenvsys.version等),进行查询通常比实际伪造环境更方便。这是一个上下文管理器,它一次为一个os.getenv调用执行此操作:

from __future__ import with_statement
from contextlib import contextmanager
import os

@contextmanager
def fake_env(**fakes):
    '''fakes is a dict mapping variables to their values. In the
    fake_env context, os.getenv calls try to return out of the fakes
    dict whenever possible before querying the actual environment.
    '''

    global os
    original = os.getenv

    def dummy(var):
        try: return fakes[var]
        except KeyError: return original(var)

    os.getenv = dummy
    yield
    os.getenv = original

if __name__ == '__main__':

    print os.getenv('HOME') 
    with fake_env(HOME='here'):
        print os.getenv('HOME') 
    print os.getenv('HOME') 

但这仅适用于os.getenv,如果我允许具有多个参数的函数,语法会变得有点笨拙。我想在astcode / exec / eval之间我可以扩展它以使函数覆盖作为参数,但不是干净利落。此外,我将前往Greenspun的第十名。还有更好的方法吗?

2 个答案:

答案 0 :(得分:4)

您可以轻松地将os.getenv本身作为第一个参数传递,然后在上下文管理器中分析它比astcode等更简单等:

>>> os.getenv.__name__
'getenv'
>>> os.getenv.__module__
'os'

之后,为了合理的通用目的,您可以返回结果对象,或者从参数(可能是其元组)到结果的映射。 faker上下文管理器也可以选择接受用于伪造的callable。

例如,最简单:

import sys

def faker(original, fakefun):

    original = os.getenv
    themod = sys.modules[original.__module__]
    thename = original.__name__

    def dummy(*a, **k):
        try: return fakefun(*a, **k)
        except BaseException: return original(*a, **k)

    setattr(themod, thename, dummy)
    yield
    setattr(themod, thename, original)

您的具体示例可能会变为:

with faker(os.getenv, dict(HOME='here').__getitem__):
   ...

当然,如果需要更多复杂性,则可能需要更加复杂。你想传播某些异常,而不是向原始函数进行扩展,或者提供一些常见的情况,其中提供一个假冒的可调用程序是笨重的,等等。但是,没有理由这样一般的骗子需要比你的特定的更复杂

答案 1 :(得分:1)

为什么不编写自己的(假)sysos和& c。模块?

import fakeSys as sys