oeqa problems with >= Python3.8


Hulme, Richard <richard.hulme@...>
 

Hello,

I found an issue when upgrading a box running unit tests that bumped Python from 3.7 to 3.9.

The unit tests themselves are run OK but processing them causes KeyErrors to be raised:

======================================================================
ERROR: test_ping (oeqa.runtime.ping.PingTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File \"/tmp/moduletest/oeqa/oetest.py\", line 114, in tearDown
res = getResults()
File \"/tmp/moduletest/oeqa/utils/decorators.py\", line 43, in __init__
self.faillist = handleList(upperf.f_locals['result'].failures)
KeyError: 'result'

It seems that the problem is in https://git.yoctoproject.org/poky/tree/meta/lib/oeqa/utils/decorators.py?h=master#n25 where it looks for the last frame in the call stack from 'unittest.case' and then assumes that frame must have a local variable called 'result'

upperf = sys._current_frames()[ident]
while (upperf.f_globals['__name__'] != 'unittest.case'):
upperf = upperf.f_back
# deleted a few lines for clarity
self.faillist = handleList(upperf.f_locals['result'].failures)
self.errorlist = handleList(upperf.f_locals['result'].errors)
self.skiplist = handleList(upperf.f_locals['result'].skipped)

That frame will be the one belonging to unittest.case.run but since Python3.8 'run' no longer calls 'tearDown' directly so there is now an additional frame belonging to unittest.case (see https://github.com/python/cpython/commit/4dd3e3f9bbd320f0dd556688e04db0a6b55a7b52)

This change was introduced here https://github.com/python/cpython/commit/4dd3e3f9bbd320f0dd556688e04db0a6b55a7b52 and is tagged with everything from 3.8 beta1 onwards.

Changing the 'while' loop to look for a frame from "unittest.case" *and* that has a local variable called "result" seems to fix the problem:

while (upperf.f_globals['__name__'] != 'unittest.case') or ('result' not in upperf_flocals):

or

while not ((upperf.f_globals['__name__'] == 'unittest.case') and ('result' in upperf.f_locals)):

(depending on your preference for combining negative conditions).

Accessing a local variable in a base class method seems rather ugly and fragile to me but that is perhaps a different issue.

I haven't been able to find any references to anyone else coming across this problem before but Python3.8 hasn't only just been released so surely someone's been hit by this already?

Am I missing something?

Richard

Join {yocto@lists.yoctoproject.org to automatically receive all group messages.