greenlet_yielddefer.py 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. # The contents of this file are subject to the Python Software Foundation
  2. # License Version 2.3 (the License). You may not copy or use this file, in
  3. # either source code or executable form, except in compliance with the License.
  4. # You may obtain a copy of the License at http://www.python.org/license.
  5. #
  6. # Software distributed under the License is distributed on an AS IS basis,
  7. # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  8. # for the specific language governing rights and limitations under the
  9. # License.
  10. #
  11. # by Greg Hazel
  12. import greenlet
  13. from BTL import defer
  14. class GreenletWithDeferred(greenlet.greenlet):
  15. __slots__ = ['root', 'yielded_once', 'finished']
  16. def __init__(self, root, df, _f, *a, **kw):
  17. self.root = root
  18. self.yielded_once = False
  19. self.finished = False
  20. greenlet.greenlet.__init__(self,
  21. lambda : self.body(df, _f, *a, **kw))
  22. def body(self, df, _f, *a, **kw):
  23. try:
  24. v = _f(*a, **kw)
  25. except:
  26. self.finished = True
  27. df.errback(defer.Failure())
  28. else:
  29. self.finished = True
  30. df.callback(v)
  31. return df
  32. def switch(self, *a):
  33. g = greenlet.getcurrent()
  34. if (isinstance(g, GreenletWithDeferred) and
  35. g.finished and g.parent == self):
  36. # control will return to the parent anyway, and switching to it
  37. # causes a memory leak (greenlets don't participate in gc).
  38. if a:
  39. return a[0]
  40. return
  41. return greenlet.greenlet.switch(self, *a)
  42. def launch_coroutine(_f, *a, **kw):
  43. parent = greenlet.getcurrent()
  44. if isinstance(parent, GreenletWithDeferred):
  45. parent = parent.root
  46. df = defer.Deferred()
  47. g = GreenletWithDeferred(parent, df, _f, *a, **kw)
  48. g.switch()
  49. return df
  50. def coroutine(_f):
  51. def replacement(*a, **kw):
  52. return launch_coroutine(_f, *a, **kw)
  53. return replacement
  54. def like_yield(df):
  55. assert isinstance(df, defer.Deferred)
  56. if not df.called or df.paused:
  57. g = greenlet.getcurrent()
  58. assert isinstance(g, GreenletWithDeferred)
  59. df.addBoth(g.switch)
  60. if not g.yielded_once:
  61. g.yielded_once = True
  62. g = g.parent
  63. else:
  64. g = g.root
  65. while not df.called or df.paused:
  66. g.switch()
  67. assert df.called and not df.paused
  68. return df.getResult()