[Twisted-Python] Problems with inlineCallback, Deferred, yield and Python 3 in buildbot
Craig Rodrigues
rodrigc at crodrigues.org
Thu Jan 26 02:41:51 MST 2017
On Tue, Jan 24, 2017 at 12:48 PM, Craig Rodrigues <rodrigc at crodrigues.org>
wrote:
>
>
> On Mon, Jan 23, 2017 at 5:10 PM, Jean-Paul Calderone <
> exarkun at twistedmatrix.com> wrote:
>
>> On Mon, Jan 23, 2017 at 7:08 PM, Craig Rodrigues <rodrigc at crodrigues.org>
>> wrote:
>>
>>>
>>>
>>>
>>> I did some more debugging. The callstack is quite deep. :/
>>> I used this command to trigger the error:
>>>
>>> trial buildbot.test.unit.test_process_buildrequestdistributor.Tes
>>> tMaybeStartBuilds.test_slow_db
>>>
>>> I found in this function in buildbot's BuildRequestsEndpoint.get()
>>> function ( https://github.com/buildbot/buildbot/blob/master/master/buil
>>> dbot/data/buildrequests.py#L146 ) there is this line:
>>>
>>>
>>>
>>> * defer.returnValue( [(yield self.db2data(br)) for br
>>> in buildrequests])*
>>> On Python 2, this line returns:
>>> an object list
>>> each list entry is a dictionary of name/value pairs that looks like:
>>>
>>> [{'buildrequestid': 10, 'complete': False, 'waited_for': False,
>>> 'claimed_at': None, 'results': -1, 'claimed': False, 'buildsetid': 11,
>>> 'complete_at': None, 'submitted_at': datetime.datetime(1970, 1, 2, 12, 6,
>>> 40, tzinfo=tzutc()), 'builderid': 77, 'claimed_by_masterid': None,
>>> 'priority': 0}, {'buildrequestid': 11, 'complete': False, 'waited_for':
>>> False, 'claimed_at': None, 'results': -1, 'claimed': False, 'buildsetid':
>>> 11, 'complete_at': None, 'submitted_at': datetime.datetime(1970, 1, 2, 13,
>>> 30, tzinfo=tzutc()), 'builderid': 77, 'claimed_by_masterid': None,
>>> 'priority': 0}]
>>>
>>> On Python 3, this returns:
>>> an object <generator object BuildRequestsEndpoint.get.<loc
>>> als>.<listcomp>
>>> of type <class 'generator'>
>>>
>>>
>> Yep.
>>
>> On Python 2, [(yield self.db2data(br)) for br in buildrequests] is a list
>> comprehension. It will have len(buildrequests) elements and each will be
>> the value sent back in to the generator via the yield expression.
>>
>> On Python 3, the same expression is a list of one element which is a
>> generator expression.
>>
>> This form is much clearer, I think, and behaves as intended on both
>> versions of Python:
>>
>> results = []
>> for br in buildrequests:
>> results.append((yield self.db2data(br)))
>> defer.returnValue(results)
>>
>>
>>
> Wow, thanks! That fixed it. I submitted those fixes upstream to buildbot.
>
> I'm not so familiar with generator expressions and Deferred's so have
> been playing
> around with things to try to understand.
>
> I went back to the problem code, and changed:
>
>
> * defer.returnValue( [(yield self.db2data(br)) for br in
> buildrequests])*
>
> to:
>
> * defer.returnValue(*
> * list([**(yield self.db2data(br)) for br in buildrequests])*
>
> and ran the code under Python 3 and got a return value of:
>
> [ Deferred ]
>
> instead of:
> [ {......} ]
>
> where the Deferred.results value was the dictionary.
>
> How does Python 2 directly go to returning the dictionary out of the
> Deferred,
> while Python 3 returns the Deferred inside the list? self.db2data()
> returns a Deferred.
>
> Thanks.
>
> --
> Craig
>
On the python-dev mailing list, Joe Jevnik gave a really excellent
explanation of what
is going on here, down to the Python bytecode level:
https://mail.python.org/pipermail/python-dev/2017-January/147244.html
--
Craig
-------------- next part --------------
An HTML attachment was scrubbed...
URL: </pipermail/twisted-python/attachments/20170126/1af9491c/attachment-0002.html>
More information about the Twisted-Python
mailing list