test_nooldstyle.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Tests for L{twisted.python._oldstyle._oldStyle}.
  5. """
  6. from __future__ import absolute_import, division
  7. import types
  8. import inspect
  9. from twisted.python.reflect import namedAny, fullyQualifiedName
  10. from twisted.python.modules import getModule
  11. from twisted.python.compat import _PY3, _shouldEnableNewStyle
  12. from twisted.trial import unittest
  13. from twisted.python import _oldstyle
  14. _skip = None
  15. if _PY3:
  16. _skip = "Not relevant on Python 3."
  17. elif not _shouldEnableNewStyle():
  18. _skip = "Not running with TWISTED_NEWSTYLE=1"
  19. forbiddenModules = [
  20. "twisted._threads",
  21. "twisted.application",
  22. "twisted.internet",
  23. "twisted.logger",
  24. "twisted.plugins",
  25. "twisted.positioning",
  26. "twisted.protocols.haproxy",
  27. "twisted.python",
  28. "twisted.script",
  29. "twisted.tap",
  30. "twisted.trial",
  31. ]
  32. class SomeOldStyleClass:
  33. """
  34. I am a docstring!
  35. """
  36. bar = "baz"
  37. def func(self):
  38. """
  39. A function on an old style class.
  40. @return: "hi", for testing.
  41. """
  42. return "hi"
  43. class SomeNewStyleClass(object):
  44. """
  45. Some new style class!
  46. """
  47. class OldStyleDecoratorTests(unittest.TestCase):
  48. """
  49. Tests for L{_oldstyle._oldStyle}.
  50. """
  51. def test_makesNewStyle(self):
  52. """
  53. L{_oldstyle._oldStyle} wraps an old-style class and returns a new-style
  54. class that has the same functions, attributes, etc.
  55. """
  56. class SomeClassThatUsesOldStyle(SomeOldStyleClass):
  57. pass
  58. self.assertEqual(type(SomeClassThatUsesOldStyle), types.ClassType)
  59. updatedClass = _oldstyle._oldStyle(SomeClassThatUsesOldStyle)
  60. self.assertEqual(type(updatedClass), type)
  61. self.assertEqual(updatedClass.__bases__, (SomeOldStyleClass, object))
  62. self.assertEqual(updatedClass().func(), "hi")
  63. self.assertEqual(updatedClass().bar, "baz")
  64. def test_carriesAttributes(self):
  65. """
  66. The class returned by L{_oldstyle._oldStyle} has the same C{__name__},
  67. C{__module__}, and docstring (C{__doc__}) attributes as the original.
  68. """
  69. updatedClass = _oldstyle._oldStyle(SomeOldStyleClass)
  70. self.assertEqual(updatedClass.__name__, SomeOldStyleClass.__name__)
  71. self.assertEqual(updatedClass.__doc__, SomeOldStyleClass.__doc__)
  72. self.assertEqual(updatedClass.__module__, SomeOldStyleClass.__module__)
  73. def test_onlyOldStyleMayBeDecorated(self):
  74. """
  75. Using L{_oldstyle._oldStyle} on a new-style class on Python 2 will
  76. raise an exception.
  77. """
  78. with self.assertRaises(ValueError) as e:
  79. _oldstyle._oldStyle(SomeNewStyleClass)
  80. self.assertEqual(
  81. e.exception.args[0],
  82. ("twisted.python._oldstyle._oldStyle is being used to decorate a "
  83. "new-style class (twisted.test.test_nooldstyle.SomeNewStyleClass)"
  84. ". This should only be used to decorate old-style classes."))
  85. def test_noOpByDefault(self):
  86. """
  87. On Python 3 or on Py2 when C{TWISTED_NEWSTYLE} is not set,
  88. L{_oldStyle._oldStyle} is a no-op.
  89. """
  90. updatedClass = _oldstyle._oldStyle(SomeOldStyleClass)
  91. self.assertEqual(type(updatedClass), type(SomeOldStyleClass))
  92. self.assertIs(updatedClass, SomeOldStyleClass)
  93. if _PY3:
  94. test_onlyOldStyleMayBeDecorated.skip = "Only relevant on Py2."
  95. if _skip:
  96. test_makesNewStyle.skip = _skip
  97. test_carriesAttributes.skip = _skip
  98. else:
  99. test_noOpByDefault.skip = ("Only relevant when not running under "
  100. "TWISTED_NEWSTYLE=1")
  101. class NewStyleOnly(object):
  102. """
  103. A base testclass that takes a module and tests if the classes defined
  104. in it are old-style.
  105. CAVEATS: This is maybe slightly dumb. It doesn't look inside functions, for
  106. classes defined there, or nested classes.
  107. """
  108. skip = _skip
  109. def test_newStyleClassesOnly(self):
  110. """
  111. Test that C{self.module} has no old-style classes in it.
  112. """
  113. try:
  114. module = namedAny(self.module)
  115. except ImportError as e:
  116. raise unittest.SkipTest("Not importable: {}".format(e))
  117. oldStyleClasses = []
  118. for name, val in inspect.getmembers(module):
  119. if hasattr(val, "__module__") \
  120. and val.__module__ == self.module:
  121. if isinstance(val, types.ClassType):
  122. oldStyleClasses.append(fullyQualifiedName(val))
  123. if oldStyleClasses:
  124. self.todo = "Not all classes are made new-style yet. See #8243."
  125. for x in forbiddenModules:
  126. if self.module.startswith(x):
  127. delattr(self, "todo")
  128. raise unittest.FailTest(
  129. "Old-style classes in {module}: {val}".format(
  130. module=self.module,
  131. val=", ".join(oldStyleClasses)))
  132. def _buildTestClasses(_locals):
  133. """
  134. Build the test classes that use L{NewStyleOnly}, one class per module.
  135. @param _locals: The global C{locals()} dict.
  136. """
  137. for x in getModule("twisted").walkModules():
  138. ignoredModules = [
  139. "twisted.test.reflect_helper",
  140. "twisted.internet.test.process_",
  141. "twisted.test.process_"
  142. ]
  143. isIgnored = [x.name.startswith(ignored) for ignored in ignoredModules]
  144. if True in isIgnored:
  145. continue
  146. class Test(NewStyleOnly, unittest.TestCase):
  147. """
  148. @see: L{NewStyleOnly}
  149. """
  150. module = x.name
  151. acceptableName = x.name.replace(".", "_")
  152. Test.__name__ = acceptableName
  153. if hasattr(Test, "__qualname__"):
  154. Test.__qualname__ = acceptableName
  155. _locals.update({acceptableName: Test})
  156. _buildTestClasses(locals())