test_serverextensions.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. import imp
  2. import os
  3. import sys
  4. from unittest import TestCase
  5. try:
  6. from unittest.mock import patch
  7. except ImportError:
  8. from mock import patch # py2
  9. from ipython_genutils.tempdir import TemporaryDirectory
  10. from ipython_genutils import py3compat
  11. from notebook.config_manager import BaseJSONConfigManager
  12. from traitlets.tests.utils import check_help_all_output
  13. from jupyter_core import paths
  14. from notebook.serverextensions import toggle_serverextension_python
  15. from notebook import nbextensions, serverextensions, extensions
  16. from notebook.notebookapp import NotebookApp
  17. from notebook.nbextensions import _get_config_dir
  18. if sys.version_info > (3,):
  19. from types import SimpleNamespace
  20. else:
  21. class SimpleNamespace(object):
  22. pass
  23. from collections import OrderedDict
  24. def test_help_output():
  25. check_help_all_output('notebook.serverextensions')
  26. check_help_all_output('notebook.serverextensions', ['enable'])
  27. check_help_all_output('notebook.serverextensions', ['disable'])
  28. check_help_all_output('notebook.serverextensions', ['install'])
  29. check_help_all_output('notebook.serverextensions', ['uninstall'])
  30. outer_file = __file__
  31. class MockExtensionModule(object):
  32. __file__ = outer_file
  33. @staticmethod
  34. def _jupyter_server_extension_paths():
  35. return [{
  36. 'module': '_mockdestination/index'
  37. }]
  38. loaded = False
  39. def load_jupyter_server_extension(self, app):
  40. self.loaded = True
  41. class MockEnvTestCase(TestCase):
  42. def tempdir(self):
  43. td = TemporaryDirectory()
  44. self.tempdirs.append(td)
  45. return py3compat.cast_unicode(td.name)
  46. def setUp(self):
  47. self.tempdirs = []
  48. self._mock_extensions = []
  49. self.test_dir = self.tempdir()
  50. self.data_dir = os.path.join(self.test_dir, 'data')
  51. self.config_dir = os.path.join(self.test_dir, 'config')
  52. self.system_data_dir = os.path.join(self.test_dir, 'system_data')
  53. self.system_config_dir = os.path.join(self.test_dir, 'system_config')
  54. self.system_path = [self.system_data_dir]
  55. self.system_config_path = [self.system_config_dir]
  56. self.patches = []
  57. p = patch.dict('os.environ', {
  58. 'JUPYTER_CONFIG_DIR': self.config_dir,
  59. 'JUPYTER_DATA_DIR': self.data_dir,
  60. })
  61. self.patches.append(p)
  62. for mod in (paths, nbextensions):
  63. p = patch.object(mod,
  64. 'SYSTEM_JUPYTER_PATH', self.system_path)
  65. self.patches.append(p)
  66. p = patch.object(mod,
  67. 'ENV_JUPYTER_PATH', [])
  68. self.patches.append(p)
  69. for mod in (paths, extensions):
  70. p = patch.object(mod,
  71. 'SYSTEM_CONFIG_PATH', self.system_config_path)
  72. self.patches.append(p)
  73. p = patch.object(mod,
  74. 'ENV_CONFIG_PATH', [])
  75. self.patches.append(p)
  76. for p in self.patches:
  77. p.start()
  78. self.addCleanup(p.stop)
  79. # verify our patches
  80. self.assertEqual(paths.jupyter_config_path(), [self.config_dir] + self.system_config_path)
  81. self.assertEqual(extensions._get_config_dir(user=False), self.system_config_dir)
  82. self.assertEqual(paths.jupyter_path(), [self.data_dir] + self.system_path)
  83. def tearDown(self):
  84. for modulename in self._mock_extensions:
  85. sys.modules.pop(modulename)
  86. def _inject_mock_extension(self, modulename='mockextension'):
  87. sys.modules[modulename] = ext = MockExtensionModule()
  88. self._mock_extensions.append(modulename)
  89. return ext
  90. class TestInstallServerExtension(MockEnvTestCase):
  91. def _get_config(self, user=True):
  92. cm = BaseJSONConfigManager(config_dir=_get_config_dir(user))
  93. data = cm.get("jupyter_notebook_config")
  94. return data.get("NotebookApp", {}).get("nbserver_extensions", {})
  95. def test_enable(self):
  96. self._inject_mock_extension()
  97. toggle_serverextension_python('mockextension', True)
  98. config = self._get_config()
  99. assert config['mockextension']
  100. def test_disable(self):
  101. self._inject_mock_extension()
  102. toggle_serverextension_python('mockextension', True)
  103. toggle_serverextension_python('mockextension', False)
  104. config = self._get_config()
  105. assert not config['mockextension']
  106. def test_merge_config(self):
  107. # enabled at sys level
  108. mock_sys = self._inject_mock_extension('mockext_sys')
  109. # enabled at sys, disabled at user
  110. mock_both = self._inject_mock_extension('mockext_both')
  111. # enabled at user
  112. mock_user = self._inject_mock_extension('mockext_user')
  113. # enabled at Python
  114. mock_py = self._inject_mock_extension('mockext_py')
  115. toggle_serverextension_python('mockext_sys', enabled=True, user=False)
  116. toggle_serverextension_python('mockext_user', enabled=True, user=True)
  117. toggle_serverextension_python('mockext_both', enabled=True, user=False)
  118. toggle_serverextension_python('mockext_both', enabled=False, user=True)
  119. app = NotebookApp(nbserver_extensions={'mockext_py': True})
  120. app.init_server_extensions()
  121. assert mock_user.loaded
  122. assert mock_sys.loaded
  123. assert mock_py.loaded
  124. assert not mock_both.loaded
  125. class TestOrderedServerExtension(MockEnvTestCase):
  126. """
  127. Test that Server Extensions are loaded _in order_
  128. """
  129. def setUp(self):
  130. super(TestOrderedServerExtension, self).setUp()
  131. mockextension1 = SimpleNamespace()
  132. mockextension2 = SimpleNamespace()
  133. def load_jupyter_server_extension(obj):
  134. obj.mockI = True
  135. obj.mock_shared = 'I'
  136. mockextension1.load_jupyter_server_extension = load_jupyter_server_extension
  137. def load_jupyter_server_extension(obj):
  138. obj.mockII = True
  139. obj.mock_shared = 'II'
  140. mockextension2.load_jupyter_server_extension = load_jupyter_server_extension
  141. sys.modules['mockextension2'] = mockextension2
  142. sys.modules['mockextension1'] = mockextension1
  143. def tearDown(self):
  144. super(TestOrderedServerExtension, self).tearDown()
  145. del sys.modules['mockextension2']
  146. del sys.modules['mockextension1']
  147. def test_load_ordered(self):
  148. app = NotebookApp()
  149. app.nbserver_extensions = OrderedDict([('mockextension2',True),('mockextension1',True)])
  150. app.init_server_extensions()
  151. assert app.mockII is True, "Mock II should have been loaded"
  152. assert app.mockI is True, "Mock I should have been loaded"
  153. assert app.mock_shared == 'II', "Mock II should be loaded after Mock I"