/notebook/tests/launchnotebook.py

https://gitlab.com/yeah568/notebook · Python · 151 lines · 116 code · 19 blank · 16 comment · 14 complexity · 6d32bbccdf3dc05db3846e13ab8c6694 MD5 · raw file

  1. """Base class for notebook tests."""
  2. from __future__ import print_function
  3. import os
  4. import sys
  5. import time
  6. import requests
  7. from contextlib import contextmanager
  8. from threading import Thread, Event
  9. from unittest import TestCase
  10. pjoin = os.path.join
  11. try:
  12. from unittest.mock import patch
  13. except ImportError:
  14. from mock import patch #py2
  15. from tornado.ioloop import IOLoop
  16. import jupyter_core.paths
  17. from ..notebookapp import NotebookApp
  18. from ipython_genutils.tempdir import TemporaryDirectory
  19. MAX_WAITTIME = 30 # seconds to wait for notebook server to start
  20. POLL_INTERVAL = 0.1 # time between attempts
  21. # TimeoutError is a builtin on Python 3. This can be removed when we stop
  22. # supporting Python 2.
  23. class TimeoutError(Exception):
  24. pass
  25. class NotebookTestBase(TestCase):
  26. """A base class for tests that need a running notebook.
  27. This create some empty config and runtime directories
  28. and then starts the notebook server with them.
  29. """
  30. port = 12341
  31. config = None
  32. # run with a base URL that would be escaped,
  33. # to test that we don't double-escape URLs
  34. url_prefix = '/a%40b/'
  35. @classmethod
  36. def wait_until_alive(cls):
  37. """Wait for the server to be alive"""
  38. url = cls.base_url() + 'api/contents'
  39. for _ in range(int(MAX_WAITTIME/POLL_INTERVAL)):
  40. try:
  41. requests.get(url)
  42. except Exception as e:
  43. if not cls.notebook_thread.is_alive():
  44. raise RuntimeError("The notebook server failed to start")
  45. time.sleep(POLL_INTERVAL)
  46. else:
  47. return
  48. raise TimeoutError("The notebook server didn't start up correctly.")
  49. @classmethod
  50. def wait_until_dead(cls):
  51. """Wait for the server process to terminate after shutdown"""
  52. cls.notebook_thread.join(timeout=MAX_WAITTIME)
  53. if cls.notebook_thread.is_alive():
  54. raise TimeoutError("Undead notebook server")
  55. @classmethod
  56. def setup_class(cls):
  57. cls.home_dir = TemporaryDirectory()
  58. data_dir = TemporaryDirectory()
  59. cls.env_patch = patch.dict('os.environ', {
  60. 'HOME': cls.home_dir.name,
  61. 'PYTHONPATH': os.pathsep.join(sys.path),
  62. 'IPYTHONDIR': pjoin(cls.home_dir.name, '.ipython'),
  63. 'JUPYTER_DATA_DIR' : data_dir.name
  64. })
  65. cls.env_patch.start()
  66. cls.path_patch = patch.object(jupyter_core.paths, 'SYSTEM_JUPYTER_PATH', [])
  67. cls.path_patch.start()
  68. cls.config_dir = TemporaryDirectory()
  69. cls.data_dir = data_dir
  70. cls.runtime_dir = TemporaryDirectory()
  71. cls.notebook_dir = TemporaryDirectory()
  72. started = Event()
  73. def start_thread():
  74. app = cls.notebook = NotebookApp(
  75. port=cls.port,
  76. port_retries=0,
  77. open_browser=False,
  78. config_dir=cls.config_dir.name,
  79. data_dir=cls.data_dir.name,
  80. runtime_dir=cls.runtime_dir.name,
  81. notebook_dir=cls.notebook_dir.name,
  82. base_url=cls.url_prefix,
  83. config=cls.config,
  84. )
  85. # don't register signal handler during tests
  86. app.init_signal = lambda : None
  87. # clear log handlers and propagate to root for nose to capture it
  88. # needs to be redone after initialize, which reconfigures logging
  89. app.log.propagate = True
  90. app.log.handlers = []
  91. app.initialize(argv=[])
  92. app.log.propagate = True
  93. app.log.handlers = []
  94. loop = IOLoop.current()
  95. loop.add_callback(started.set)
  96. try:
  97. app.start()
  98. finally:
  99. # set the event, so failure to start doesn't cause a hang
  100. started.set()
  101. app.session_manager.close()
  102. cls.notebook_thread = Thread(target=start_thread)
  103. cls.notebook_thread.start()
  104. started.wait()
  105. cls.wait_until_alive()
  106. @classmethod
  107. def teardown_class(cls):
  108. cls.notebook.stop()
  109. cls.wait_until_dead()
  110. cls.home_dir.cleanup()
  111. cls.config_dir.cleanup()
  112. cls.data_dir.cleanup()
  113. cls.runtime_dir.cleanup()
  114. cls.notebook_dir.cleanup()
  115. cls.env_patch.stop()
  116. cls.path_patch.stop()
  117. @classmethod
  118. def base_url(cls):
  119. return 'http://localhost:%i%s' % (cls.port, cls.url_prefix)
  120. @contextmanager
  121. def assert_http_error(status, msg=None):
  122. try:
  123. yield
  124. except requests.HTTPError as e:
  125. real_status = e.response.status_code
  126. assert real_status == status, \
  127. "Expected status %d, got %d" % (status, real_status)
  128. if msg:
  129. assert msg in str(e), e
  130. else:
  131. assert False, "Expected HTTP error status"