util_test.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. # coding: utf-8
  2. from io import StringIO
  3. import re
  4. import sys
  5. import datetime
  6. import unittest
  7. import tornado.escape
  8. from tornado.escape import utf8
  9. from tornado.util import (
  10. raise_exc_info,
  11. Configurable,
  12. exec_in,
  13. ArgReplacer,
  14. timedelta_to_seconds,
  15. import_object,
  16. re_unescape,
  17. is_finalizing,
  18. )
  19. import typing
  20. from typing import cast
  21. if typing.TYPE_CHECKING:
  22. from typing import Dict, Any # noqa: F401
  23. class RaiseExcInfoTest(unittest.TestCase):
  24. def test_two_arg_exception(self):
  25. # This test would fail on python 3 if raise_exc_info were simply
  26. # a three-argument raise statement, because TwoArgException
  27. # doesn't have a "copy constructor"
  28. class TwoArgException(Exception):
  29. def __init__(self, a, b):
  30. super(TwoArgException, self).__init__()
  31. self.a, self.b = a, b
  32. try:
  33. raise TwoArgException(1, 2)
  34. except TwoArgException:
  35. exc_info = sys.exc_info()
  36. try:
  37. raise_exc_info(exc_info)
  38. self.fail("didn't get expected exception")
  39. except TwoArgException as e:
  40. self.assertIs(e, exc_info[1])
  41. class TestConfigurable(Configurable):
  42. @classmethod
  43. def configurable_base(cls):
  44. return TestConfigurable
  45. @classmethod
  46. def configurable_default(cls):
  47. return TestConfig1
  48. class TestConfig1(TestConfigurable):
  49. def initialize(self, pos_arg=None, a=None):
  50. self.a = a
  51. self.pos_arg = pos_arg
  52. class TestConfig2(TestConfigurable):
  53. def initialize(self, pos_arg=None, b=None):
  54. self.b = b
  55. self.pos_arg = pos_arg
  56. class TestConfig3(TestConfigurable):
  57. # TestConfig3 is a configuration option that is itself configurable.
  58. @classmethod
  59. def configurable_base(cls):
  60. return TestConfig3
  61. @classmethod
  62. def configurable_default(cls):
  63. return TestConfig3A
  64. class TestConfig3A(TestConfig3):
  65. def initialize(self, a=None):
  66. self.a = a
  67. class TestConfig3B(TestConfig3):
  68. def initialize(self, b=None):
  69. self.b = b
  70. class ConfigurableTest(unittest.TestCase):
  71. def setUp(self):
  72. self.saved = TestConfigurable._save_configuration()
  73. self.saved3 = TestConfig3._save_configuration()
  74. def tearDown(self):
  75. TestConfigurable._restore_configuration(self.saved)
  76. TestConfig3._restore_configuration(self.saved3)
  77. def checkSubclasses(self):
  78. # no matter how the class is configured, it should always be
  79. # possible to instantiate the subclasses directly
  80. self.assertIsInstance(TestConfig1(), TestConfig1)
  81. self.assertIsInstance(TestConfig2(), TestConfig2)
  82. obj = TestConfig1(a=1)
  83. self.assertEqual(obj.a, 1)
  84. obj2 = TestConfig2(b=2)
  85. self.assertEqual(obj2.b, 2)
  86. def test_default(self):
  87. # In these tests we combine a typing.cast to satisfy mypy with
  88. # a runtime type-assertion. Without the cast, mypy would only
  89. # let us access attributes of the base class.
  90. obj = cast(TestConfig1, TestConfigurable())
  91. self.assertIsInstance(obj, TestConfig1)
  92. self.assertIs(obj.a, None)
  93. obj = cast(TestConfig1, TestConfigurable(a=1))
  94. self.assertIsInstance(obj, TestConfig1)
  95. self.assertEqual(obj.a, 1)
  96. self.checkSubclasses()
  97. def test_config_class(self):
  98. TestConfigurable.configure(TestConfig2)
  99. obj = cast(TestConfig2, TestConfigurable())
  100. self.assertIsInstance(obj, TestConfig2)
  101. self.assertIs(obj.b, None)
  102. obj = cast(TestConfig2, TestConfigurable(b=2))
  103. self.assertIsInstance(obj, TestConfig2)
  104. self.assertEqual(obj.b, 2)
  105. self.checkSubclasses()
  106. def test_config_str(self):
  107. TestConfigurable.configure("tornado.test.util_test.TestConfig2")
  108. obj = cast(TestConfig2, TestConfigurable())
  109. self.assertIsInstance(obj, TestConfig2)
  110. self.assertIs(obj.b, None)
  111. obj = cast(TestConfig2, TestConfigurable(b=2))
  112. self.assertIsInstance(obj, TestConfig2)
  113. self.assertEqual(obj.b, 2)
  114. self.checkSubclasses()
  115. def test_config_args(self):
  116. TestConfigurable.configure(None, a=3)
  117. obj = cast(TestConfig1, TestConfigurable())
  118. self.assertIsInstance(obj, TestConfig1)
  119. self.assertEqual(obj.a, 3)
  120. obj = cast(TestConfig1, TestConfigurable(42, a=4))
  121. self.assertIsInstance(obj, TestConfig1)
  122. self.assertEqual(obj.a, 4)
  123. self.assertEqual(obj.pos_arg, 42)
  124. self.checkSubclasses()
  125. # args bound in configure don't apply when using the subclass directly
  126. obj = TestConfig1()
  127. self.assertIs(obj.a, None)
  128. def test_config_class_args(self):
  129. TestConfigurable.configure(TestConfig2, b=5)
  130. obj = cast(TestConfig2, TestConfigurable())
  131. self.assertIsInstance(obj, TestConfig2)
  132. self.assertEqual(obj.b, 5)
  133. obj = cast(TestConfig2, TestConfigurable(42, b=6))
  134. self.assertIsInstance(obj, TestConfig2)
  135. self.assertEqual(obj.b, 6)
  136. self.assertEqual(obj.pos_arg, 42)
  137. self.checkSubclasses()
  138. # args bound in configure don't apply when using the subclass directly
  139. obj = TestConfig2()
  140. self.assertIs(obj.b, None)
  141. def test_config_multi_level(self):
  142. TestConfigurable.configure(TestConfig3, a=1)
  143. obj = cast(TestConfig3A, TestConfigurable())
  144. self.assertIsInstance(obj, TestConfig3A)
  145. self.assertEqual(obj.a, 1)
  146. TestConfigurable.configure(TestConfig3)
  147. TestConfig3.configure(TestConfig3B, b=2)
  148. obj2 = cast(TestConfig3B, TestConfigurable())
  149. self.assertIsInstance(obj2, TestConfig3B)
  150. self.assertEqual(obj2.b, 2)
  151. def test_config_inner_level(self):
  152. # The inner level can be used even when the outer level
  153. # doesn't point to it.
  154. obj = TestConfig3()
  155. self.assertIsInstance(obj, TestConfig3A)
  156. TestConfig3.configure(TestConfig3B)
  157. obj = TestConfig3()
  158. self.assertIsInstance(obj, TestConfig3B)
  159. # Configuring the base doesn't configure the inner.
  160. obj2 = TestConfigurable()
  161. self.assertIsInstance(obj2, TestConfig1)
  162. TestConfigurable.configure(TestConfig2)
  163. obj3 = TestConfigurable()
  164. self.assertIsInstance(obj3, TestConfig2)
  165. obj = TestConfig3()
  166. self.assertIsInstance(obj, TestConfig3B)
  167. class UnicodeLiteralTest(unittest.TestCase):
  168. def test_unicode_escapes(self):
  169. self.assertEqual(utf8(u"\u00e9"), b"\xc3\xa9")
  170. class ExecInTest(unittest.TestCase):
  171. # TODO(bdarnell): make a version of this test for one of the new
  172. # future imports available in python 3.
  173. @unittest.skip("no testable future imports")
  174. def test_no_inherit_future(self):
  175. # This file has from __future__ import print_function...
  176. f = StringIO()
  177. print("hello", file=f)
  178. # ...but the template doesn't
  179. exec_in('print >> f, "world"', dict(f=f))
  180. self.assertEqual(f.getvalue(), "hello\nworld\n")
  181. class ArgReplacerTest(unittest.TestCase):
  182. def setUp(self):
  183. def function(x, y, callback=None, z=None):
  184. pass
  185. self.replacer = ArgReplacer(function, "callback")
  186. def test_omitted(self):
  187. args = (1, 2)
  188. kwargs = dict() # type: Dict[str, Any]
  189. self.assertIs(self.replacer.get_old_value(args, kwargs), None)
  190. self.assertEqual(
  191. self.replacer.replace("new", args, kwargs),
  192. (None, (1, 2), dict(callback="new")),
  193. )
  194. def test_position(self):
  195. args = (1, 2, "old", 3)
  196. kwargs = dict() # type: Dict[str, Any]
  197. self.assertEqual(self.replacer.get_old_value(args, kwargs), "old")
  198. self.assertEqual(
  199. self.replacer.replace("new", args, kwargs),
  200. ("old", [1, 2, "new", 3], dict()),
  201. )
  202. def test_keyword(self):
  203. args = (1,)
  204. kwargs = dict(y=2, callback="old", z=3)
  205. self.assertEqual(self.replacer.get_old_value(args, kwargs), "old")
  206. self.assertEqual(
  207. self.replacer.replace("new", args, kwargs),
  208. ("old", (1,), dict(y=2, callback="new", z=3)),
  209. )
  210. class TimedeltaToSecondsTest(unittest.TestCase):
  211. def test_timedelta_to_seconds(self):
  212. time_delta = datetime.timedelta(hours=1)
  213. self.assertEqual(timedelta_to_seconds(time_delta), 3600.0)
  214. class ImportObjectTest(unittest.TestCase):
  215. def test_import_member(self):
  216. self.assertIs(import_object("tornado.escape.utf8"), utf8)
  217. def test_import_member_unicode(self):
  218. self.assertIs(import_object(u"tornado.escape.utf8"), utf8)
  219. def test_import_module(self):
  220. self.assertIs(import_object("tornado.escape"), tornado.escape)
  221. def test_import_module_unicode(self):
  222. # The internal implementation of __import__ differs depending on
  223. # whether the thing being imported is a module or not.
  224. # This variant requires a byte string in python 2.
  225. self.assertIs(import_object(u"tornado.escape"), tornado.escape)
  226. class ReUnescapeTest(unittest.TestCase):
  227. def test_re_unescape(self):
  228. test_strings = ("/favicon.ico", "index.html", "Hello, World!", "!$@#%;")
  229. for string in test_strings:
  230. self.assertEqual(string, re_unescape(re.escape(string)))
  231. def test_re_unescape_raises_error_on_invalid_input(self):
  232. with self.assertRaises(ValueError):
  233. re_unescape("\\d")
  234. with self.assertRaises(ValueError):
  235. re_unescape("\\b")
  236. with self.assertRaises(ValueError):
  237. re_unescape("\\Z")
  238. class IsFinalizingTest(unittest.TestCase):
  239. def test_basic(self):
  240. self.assertFalse(is_finalizing())