util.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import contextlib
  2. import os
  3. import platform
  4. import socket
  5. import sys
  6. import sysconfig
  7. import textwrap
  8. import typing
  9. import unittest
  10. import warnings
  11. from tornado.testing import bind_unused_port
  12. _TestCaseType = typing.TypeVar("_TestCaseType", bound=typing.Type[unittest.TestCase])
  13. skipIfNonUnix = unittest.skipIf(
  14. os.name != "posix" or sys.platform == "cygwin", "non-unix platform"
  15. )
  16. # Set the environment variable NO_NETWORK=1 to disable any tests that
  17. # depend on an external network.
  18. skipIfNoNetwork = unittest.skipIf("NO_NETWORK" in os.environ, "network access disabled")
  19. skipNotCPython = unittest.skipIf(
  20. # "CPython" here essentially refers to the traditional synchronous refcounting GC,
  21. # so we skip these tests in free-threading builds of cpython too.
  22. platform.python_implementation() != "CPython"
  23. or sysconfig.get_config_var("Py_GIL_DISABLED"),
  24. "Not CPython implementation",
  25. )
  26. def _detect_ipv6():
  27. if not socket.has_ipv6:
  28. # socket.has_ipv6 check reports whether ipv6 was present at compile
  29. # time. It's usually true even when ipv6 doesn't work for other reasons.
  30. return False
  31. sock = None
  32. try:
  33. sock = socket.socket(socket.AF_INET6)
  34. sock.bind(("::1", 0))
  35. except OSError:
  36. return False
  37. finally:
  38. if sock is not None:
  39. sock.close()
  40. return True
  41. skipIfNoIPv6 = unittest.skipIf(not _detect_ipv6(), "ipv6 support not present")
  42. def refusing_port():
  43. """Returns a local port number that will refuse all connections.
  44. Return value is (cleanup_func, port); the cleanup function
  45. must be called to free the port to be reused.
  46. """
  47. # On travis-ci port numbers are reassigned frequently. To avoid
  48. # collisions with other tests, we use an open client-side socket's
  49. # ephemeral port number to ensure that nothing can listen on that
  50. # port.
  51. server_socket, port = bind_unused_port()
  52. server_socket.setblocking(True)
  53. client_socket = socket.socket()
  54. client_socket.connect(("127.0.0.1", port))
  55. conn, client_addr = server_socket.accept()
  56. conn.close()
  57. server_socket.close()
  58. return (client_socket.close, client_addr[1])
  59. def exec_test(caller_globals, caller_locals, s):
  60. """Execute ``s`` in a given context and return the result namespace.
  61. Used to define functions for tests in particular python
  62. versions that would be syntax errors in older versions.
  63. """
  64. # Flatten the real global and local namespace into our fake
  65. # globals: it's all global from the perspective of code defined
  66. # in s.
  67. global_namespace = dict(caller_globals, **caller_locals) # type: ignore
  68. local_namespace = {} # type: typing.Dict[str, typing.Any]
  69. exec(textwrap.dedent(s), global_namespace, local_namespace)
  70. return local_namespace
  71. @contextlib.contextmanager
  72. def ignore_deprecation():
  73. """Context manager to ignore deprecation warnings."""
  74. with warnings.catch_warnings():
  75. warnings.simplefilter("ignore", DeprecationWarning)
  76. yield
  77. ABT_SKIP_MESSAGE = "abstract base class"
  78. def abstract_base_test(cls: _TestCaseType) -> _TestCaseType:
  79. """Decorator to mark a test class as an "abstract" base class.
  80. This is different from a regular abstract base class because
  81. we do not limit instantiation of the class. (If we did, it would
  82. interfere with test discovery). Instead, we prevent the tests from
  83. being run.
  84. Subclasses of an abstract base test are run as normal. There is
  85. no support for the ``@abstractmethod`` decorator so there is no runtime
  86. check that all such methods are implemented.
  87. Note that while it is semantically cleaner to modify the test loader
  88. to exclude abstract base tests, this is more complicated and would
  89. interfere with third-party test runners. This approach degrades
  90. gracefully to other tools such as editor-integrated testing.
  91. """
  92. # Type-checking fails due to https://github.com/python/mypy/issues/14458
  93. # @functools.wraps(cls)
  94. class AbstractBaseWrapper(cls): # type: ignore
  95. @classmethod
  96. def setUpClass(cls):
  97. if cls is AbstractBaseWrapper:
  98. raise unittest.SkipTest(ABT_SKIP_MESSAGE)
  99. super(AbstractBaseWrapper, cls).setUpClass()
  100. return AbstractBaseWrapper # type: ignore