如何保留python 2.7中通过yield生成的函数的方法属性



我一直在做很多搜索,但我认为我并没有真正找到我一直在寻找的东西。我会尽力解释我想做什么,希望有一个简单的解决方案,我会很高兴学到一些新的东西。

这最终是我想要完成的:使用nosetests,使用属性选择器插件装饰一些测试用例,然后在命令行调用期间使用-a开关执行符合标准的测试用例。然后将执行的测试的属性值存储在外部位置。我使用的命令行调用如下:

nosetests testpath -a attribute='someValue'

我还创建了一个自定义的nosetest插件,它存储测试用例的attributse,并将它们写入外部位置。这个想法是,我可以选择一批测试,通过存储这些测试的属性,我可以稍后对这些结果进行过滤,以用于报告目的。我正在访问我的插件中的方法属性,方法是用类似以下的代码覆盖"wantMethod"方法:

def set_attribs(self, method, attribute):
if hasattr(method, attribute):
if not self.method_attributes.has_key(method.__name__):
self.method_attributes[method.__name__] = {}
self.method_attributes[method.__name__][attribute] = getattr(method, attribute)
def wantMethod(self, method):
self.set_attribs(method, "attribute1")
self.set_attribs(method, "attribute2")
pass

我在几乎所有的测试中都使用了这个方法,除了一个测试使用"yield"关键字的情况。所发生的情况是,生成的方法执行得很好,但随后生成的每个函数的方法属性都为空。

下面是我努力实现的目标的例子。下面的测试检索了一个值列表,对于每个值,都会产生另一个函数的结果:

@attr(attribute1='someValue', attribute2='anotherValue')
def sample_test_generator(self):
for (key, value) in _input_dictionary.items()
f = partial(self._do_test, key, value)
f.attribute1='someValue'
yield (lambda x: f(), key)
def _do_test(self, input1, input2):
# Some code

根据我所读到的内容,我认为我理解,当调用yield时,它会创建一个新的可调用函数,然后执行该函数。我一直试图找出如何从sample_test_generator方法中保留属性值,但我没有成功。我想我可以创建一个分部方法,然后将属性添加到该方法中,但没有成功。测试执行时没有任何错误,只是从我的插件的角度来看,方法属性不存在,所以它们不会被记录下来。

我意识到这是一个非常复杂的问题,但我想确保我试图实现的目标的背景是清楚的。我一直在努力寻找可以帮助我处理这个特殊案件的信息,但我觉得我现在遇到了一个绊脚石,所以我真的想向专家们寻求一些建议。

谢谢。

**更新**

在阅读了反馈并玩了更多之后,看起来如果我修改lambda表达式,它就会实现我想要的。事实上,我甚至不需要创建部分函数:

def sample_test_generator(self):
for (key, value) in _input_dictionary.items()
yield (lambda: self._do_test)

这种方法唯一的缺点是测试名称不会更改。随着我玩得越来越多,在nosetests中,当使用测试生成器时,它实际上会根据包含的关键字更改结果中的测试名称。当我使用带参数的lambda表达式时,也发生了同样的事情。

例如:

  1. 使用带参数的lamdba表达式:yield(lambda x:self.do_test,"value1")

在nosetests插件中,当您访问测试用例名称时,它将显示为"sample_test_generator(value1)

  1. 使用不带参数的lambda表达式:产量(lambda:self.do_test)

本例中的测试用例名称为"sample_test_generator"。在我上面的例子中,如果字典中有多个值,那么yield调用将发生多次。但是,测试名称将始终保持为"sample_test_generator"。这并不像我获得唯一的测试名称,但却根本无法存储属性值那样糟糕。我会继续玩下去,但感谢到目前为止的反馈!

编辑

我忘了回来提供我最终是如何做到这一点的最后更新,起初我有点困惑,在我仔细看了一遍之后,我发现这与如何识别测试有关:

我最初的实现假设每个被选中执行的测试都要经过插件基类的"wantMethod"调用。当使用"yield"生成测试时,情况并非如此,因为此时测试方法已经通过了"wantMethod"调用。

然而,一旦通过"yeild"调用生成测试用例,它就会通过插件基类的"startTest"调用,这就是我最终能够成功存储属性的地方。

因此,在一个坚果壳中,我的测试执行顺序看起来是这样的:

nose->wantMethod(method_name)->yield->startTest(yield_test_name)

在我对startTest方法的覆盖中,我有以下内容:

def startTest(self, test):
# If a test is spawned by using the 'yield' keyword, the test names would be the parent test name, appended by the '(' character
# example:  If the parent test is "smoke_test", the generated test from yield would be "smoke_test('input')
parent_test_name = test_name.split('(')[0]
if self.method_attributes.has_key(test_name):
self._test_attrib = self.method_attributes[test_name]
elif self.method_attributes.has_key(parent_test_name):
self._test_attrib = self.method_attributes[parent_test_name]
else:
self._test_attrib = None

有了这个实现,再加上我对wantMethod的覆盖,父测试用例派生的每个测试也继承了父方法的属性,这正是我所需要的。

再次感谢所有发送回复的人。这是一次很好的学习经历。

这能解决你的名字问题吗?

def _actual_test(x, y):
assert x == y
def test_yield():
_actual_test.description = "test_yield_%s_%s" % (5, 5)
yield _actual_test, 5, 5
_actual_test.description = "test_yield_%s_%s" % (4, 8)  # fail
yield _actual_test, 4, 8
_actual_test.description = "test_yield_%s_%s" % (2, 2)
yield _actual_test, 2, 2

重命名也在@attr中幸存。

这行得通吗?

@attr(attribute1='someValue', attribute2='anotherValue')
def sample_test_generator(self):
def get_f(f, key):
return lambda x: f(), key
for (key, value) in _input_dictionary.items()
f = partial(self._do_test, key, value)
f.attribute1='someValue'
yield get_f(f, key)
def _do_test(self, input1, input2):
# Some code

问题是在创建lambda之后局部变量会发生变化。

最新更新