test_inlinecb.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. # -*- test-case-name: twisted.internet.test.test_inlinecb -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. Tests for L{twisted.internet.defer.inlineCallbacks}.
  6. Some tests for inlineCallbacks are defined in L{twisted.test.test_defgen} as
  7. well.
  8. """
  9. from __future__ import division, absolute_import
  10. import sys
  11. from twisted.trial.unittest import TestCase
  12. from twisted.internet.defer import Deferred, returnValue, inlineCallbacks
  13. class StopIterationReturnTests(TestCase):
  14. """
  15. On Python 3.3 and newer generator functions may use the C{return} statement
  16. with a value, which is attached to the L{StopIteration} exception that is
  17. raised.
  18. L{inlineCallbacks} will use this value when it fires the C{callback}.
  19. """
  20. def test_returnWithValue(self):
  21. """
  22. If the C{return} statement has a value it is propagated back to the
  23. L{Deferred} that the C{inlineCallbacks} function returned.
  24. """
  25. environ = {"inlineCallbacks": inlineCallbacks}
  26. exec("""
  27. @inlineCallbacks
  28. def f(d):
  29. yield d
  30. return 14
  31. """, environ)
  32. d1 = Deferred()
  33. d2 = environ["f"](d1)
  34. d1.callback(None)
  35. self.assertEqual(self.successResultOf(d2), 14)
  36. if sys.version_info < (3, 3):
  37. StopIterationReturnTests.skip = "Test requires Python 3.3 or greater"
  38. class NonLocalExitTests(TestCase):
  39. """
  40. It's possible for L{returnValue} to be (accidentally) invoked at a stack
  41. level below the L{inlineCallbacks}-decorated function which it is exiting.
  42. If this happens, L{returnValue} should report useful errors.
  43. If L{returnValue} is invoked from a function not decorated by
  44. L{inlineCallbacks}, it will emit a warning if it causes an
  45. L{inlineCallbacks} function further up the stack to exit.
  46. """
  47. def mistakenMethod(self):
  48. """
  49. This method mistakenly invokes L{returnValue}, despite the fact that it
  50. is not decorated with L{inlineCallbacks}.
  51. """
  52. returnValue(1)
  53. def assertMistakenMethodWarning(self, resultList):
  54. """
  55. Flush the current warnings and assert that we have been told that
  56. C{mistakenMethod} was invoked, and that the result from the Deferred
  57. that was fired (appended to the given list) is C{mistakenMethod}'s
  58. result. The warning should indicate that an inlineCallbacks function
  59. called 'inline' was made to exit.
  60. """
  61. self.assertEqual(resultList, [1])
  62. warnings = self.flushWarnings(offendingFunctions=[self.mistakenMethod])
  63. self.assertEqual(len(warnings), 1)
  64. self.assertEqual(warnings[0]['category'], DeprecationWarning)
  65. self.assertEqual(
  66. warnings[0]['message'],
  67. "returnValue() in 'mistakenMethod' causing 'inline' to exit: "
  68. "returnValue should only be invoked by functions decorated with "
  69. "inlineCallbacks")
  70. def test_returnValueNonLocalWarning(self):
  71. """
  72. L{returnValue} will emit a non-local exit warning in the simplest case,
  73. where the offending function is invoked immediately.
  74. """
  75. @inlineCallbacks
  76. def inline():
  77. self.mistakenMethod()
  78. returnValue(2)
  79. yield 0
  80. d = inline()
  81. results = []
  82. d.addCallback(results.append)
  83. self.assertMistakenMethodWarning(results)
  84. def test_returnValueNonLocalDeferred(self):
  85. """
  86. L{returnValue} will emit a non-local warning in the case where the
  87. L{inlineCallbacks}-decorated function has already yielded a Deferred
  88. and therefore moved its generator function along.
  89. """
  90. cause = Deferred()
  91. @inlineCallbacks
  92. def inline():
  93. yield cause
  94. self.mistakenMethod()
  95. returnValue(2)
  96. effect = inline()
  97. results = []
  98. effect.addCallback(results.append)
  99. self.assertEqual(results, [])
  100. cause.callback(1)
  101. self.assertMistakenMethodWarning(results)