From 38d326e420c31ebfb58d3f5521206c9c59f4db50 2014-03-24 22:52:49 From: Min RK Date: 2014-03-24 22:52:49 Subject: [PATCH] Merge pull request #5339 from minrk/iptest-qt-js-wtf Don't use fork to start the notebook in js tests --- diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index ba1442f..45d2c78 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -268,7 +268,7 @@ test_sections['qt'].requires('zmq', 'qt', 'pygments') # html: sec = test_sections['html'] -sec.requires('zmq', 'tornado', 'requests') +sec.requires('zmq', 'tornado', 'requests', 'sqlite3') # The notebook 'static' directory contains JS, css and other # files for web serving. Occasionally projects may put a .py # file in there (MathJax ships a conf.py), so we might as diff --git a/IPython/testing/iptestcontroller.py b/IPython/testing/iptestcontroller.py index d9535b1..adc2ea1 100644 --- a/IPython/testing/iptestcontroller.py +++ b/IPython/testing/iptestcontroller.py @@ -19,8 +19,8 @@ test suite. from __future__ import print_function import argparse +import json import multiprocessing.pool -from multiprocessing import Process, Queue import os import shutil import signal @@ -28,7 +28,7 @@ import sys import subprocess import time -from .iptest import have, test_group_names as py_test_group_names, test_sections +from .iptest import have, test_group_names as py_test_group_names, test_sections, StreamCapturer from IPython.utils.path import compress_user from IPython.utils.py3compat import bytes_to_str from IPython.utils.sysinfo import get_sys_info @@ -220,48 +220,77 @@ class JSController(TestController): os.makedirs(os.path.join(self.nbdir.name, os.path.join(u'sub ∂ir2', u'sub ∂ir 1b'))) # start the ipython notebook, so we get the port number + self.server_port = 0 self._init_server() - self.cmd.append('--port=%s' % self.server_port) - + if self.server_port: + self.cmd.append("--port=%i" % self.server_port) + else: + # don't launch tests if the server didn't start + self.cmd = [sys.executable, '-c', 'raise SystemExit(1)'] + def print_extra_info(self): print("Running tests with notebook directory %r" % self.nbdir.name) @property def will_run(self): - return all(have[a] for a in ['zmq', 'tornado', 'jinja2', 'casperjs']) + return all(have[a] for a in ['zmq', 'tornado', 'jinja2', 'casperjs', 'sqlite3']) def _init_server(self): "Start the notebook server in a separate process" - self.queue = q = Queue() - self.server = Process(target=run_webapp, args=(q, self.ipydir.name, self.nbdir.name)) - self.server.start() - self.server_port = q.get() + self.server_command = command = [sys.executable, + '-m', 'IPython.html', + '--no-browser', + '--ipython-dir', self.ipydir.name, + '--notebook-dir', self.nbdir.name, + ] + # ipc doesn't work on Windows, and darwin has crazy-long temp paths, + # which run afoul of ipc's maximum path length. + if sys.platform.startswith('linux'): + command.append('--KernelManager.transport=ipc') + self.stream_capturer = c = StreamCapturer() + c.start() + self.server = subprocess.Popen(command, stdout=c.writefd, stderr=subprocess.STDOUT) + self.server_info_file = os.path.join(self.ipydir.name, + 'profile_default', 'security', 'nbserver-%i.json' % self.server.pid + ) + self._wait_for_server() + + def _wait_for_server(self): + """Wait 30 seconds for the notebook server to start""" + for i in range(300): + if self.server.poll() is not None: + return self._failed_to_start() + if os.path.exists(self.server_info_file): + self._load_server_info() + return + time.sleep(0.1) + print("Notebook server-info file never arrived: %s" % self.server_info_file, + file=sys.stderr + ) + + def _failed_to_start(self): + """Notebook server exited prematurely""" + captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace') + print("Notebook failed to start: ", file=sys.stderr) + print(self.server_command) + print(captured, file=sys.stderr) + + def _load_server_info(self): + """Notebook server started, load connection info from JSON""" + with open(self.server_info_file) as f: + info = json.load(f) + self.server_port = info['port'] def cleanup(self): - self.server.terminate() - self.server.join() + try: + self.server.terminate() + except OSError: + # already dead + pass + self.server.wait() + self.stream_capturer.halt() TestController.cleanup(self) -def run_webapp(q, ipydir, nbdir, loglevel=0): - """start the IPython Notebook, and pass port back to the queue""" - import os - import IPython.html.notebookapp as nbapp - import sys - sys.stderr = open(os.devnull, 'w') - server = nbapp.NotebookApp() - args = ['--no-browser'] - args.extend(['--ipython-dir', ipydir, - '--notebook-dir', nbdir, - '--log-level', str(loglevel), - ]) - # ipc doesn't work on Windows, and darwin has crazy-long temp paths, - # which run afoul of ipc's maximum path length. - if sys.platform.startswith('linux'): - args.append('--KernelManager.transport=ipc') - server.initialize(args) - # communicate the port number to the parent process - q.put(server.port) - server.start() def prepare_controllers(options): """Returns two lists of TestController instances, those to run, and those