##// END OF EJS Templates
Fix imports in test suite.
Fernando Perez -
Show More
@@ -1,603 +1,610 b''
1 1 # -*- coding: utf-8 -*-
2 2 """IPython Test Suite Runner.
3 3
4 4 This module provides a main entry point to a user script to test IPython
5 5 itself from the command line. There are two ways of running this script:
6 6
7 7 1. With the syntax `iptest all`. This runs our entire test suite by
8 8 calling this script (with different arguments) recursively. This
9 9 causes modules and package to be tested in different processes, using nose
10 10 or trial where appropriate.
11 11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
12 12 the script simply calls nose, but with special command line flags and
13 13 plugins loaded.
14 14
15 15 """
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Copyright (C) 2009-2011 The IPython Development Team
19 19 #
20 20 # Distributed under the terms of the BSD License. The full license is in
21 21 # the file COPYING, distributed as part of this software.
22 22 #-----------------------------------------------------------------------------
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Imports
26 26 #-----------------------------------------------------------------------------
27 27 from __future__ import print_function
28 28
29 29 # Stdlib
30 30 import glob
31 31 import os
32 32 import os.path as path
33 33 import signal
34 34 import sys
35 35 import subprocess
36 36 import tempfile
37 37 import time
38 38 import warnings
39 39
40 40 # Note: monkeypatch!
41 41 # We need to monkeypatch a small problem in nose itself first, before importing
42 42 # it for actual use. This should get into nose upstream, but its release cycle
43 43 # is slow and we need it for our parametric tests to work correctly.
44 44 from IPython.testing import nosepatch
45 45
46 46 # Monkeypatch extra assert methods into nose.tools if they're not already there.
47 47 # This can be dropped once we no longer test on Python 2.6
48 48 from IPython.testing import nose_assert_methods
49 49
50 50 # Now, proceed to import nose itself
51 51 import nose.plugins.builtin
52 52 from nose.plugins.xunit import Xunit
53 53 from nose import SkipTest
54 54 from nose.core import TestProgram
55 55
56 56 # Our own imports
57 57 from IPython.utils import py3compat
58 58 from IPython.utils.importstring import import_item
59 59 from IPython.utils.path import get_ipython_module_path, get_ipython_package_dir
60 60 from IPython.utils.process import find_cmd, pycmd2argv
61 61 from IPython.utils.sysinfo import sys_info
62 62 from IPython.utils.tempdir import TemporaryDirectory
63 63 from IPython.utils.warn import warn
64 64
65 65 from IPython.testing import globalipapp
66 66 from IPython.testing.plugin.ipdoctest import IPythonDoctest
67 67 from IPython.external.decorators import KnownFailure, knownfailureif
68 68
69 69 pjoin = path.join
70 70
71 71
72 72 #-----------------------------------------------------------------------------
73 73 # Globals
74 74 #-----------------------------------------------------------------------------
75 75
76 76
77 77 #-----------------------------------------------------------------------------
78 78 # Warnings control
79 79 #-----------------------------------------------------------------------------
80 80
81 81 # Twisted generates annoying warnings with Python 2.6, as will do other code
82 82 # that imports 'sets' as of today
83 83 warnings.filterwarnings('ignore', 'the sets module is deprecated',
84 84 DeprecationWarning )
85 85
86 86 # This one also comes from Twisted
87 87 warnings.filterwarnings('ignore', 'the sha module is deprecated',
88 88 DeprecationWarning)
89 89
90 90 # Wx on Fedora11 spits these out
91 91 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
92 92 UserWarning)
93 93
94 94 # ------------------------------------------------------------------------------
95 95 # Monkeypatch Xunit to count known failures as skipped.
96 96 # ------------------------------------------------------------------------------
97 97 def monkeypatch_xunit():
98 98 try:
99 99 knownfailureif(True)(lambda: None)()
100 100 except Exception as e:
101 101 KnownFailureTest = type(e)
102 102
103 103 def addError(self, test, err, capt=None):
104 104 if issubclass(err[0], KnownFailureTest):
105 105 err = (SkipTest,) + err[1:]
106 106 return self.orig_addError(test, err, capt)
107 107
108 108 Xunit.orig_addError = Xunit.addError
109 109 Xunit.addError = addError
110 110
111 111 #-----------------------------------------------------------------------------
112 112 # Logic for skipping doctests
113 113 #-----------------------------------------------------------------------------
114 114 def extract_version(mod):
115 115 return mod.__version__
116 116
117 117 def test_for(item, min_version=None, callback=extract_version):
118 118 """Test to see if item is importable, and optionally check against a minimum
119 119 version.
120 120
121 121 If min_version is given, the default behavior is to check against the
122 122 `__version__` attribute of the item, but specifying `callback` allows you to
123 123 extract the value you are interested in. e.g::
124 124
125 125 In [1]: import sys
126 126
127 127 In [2]: from IPython.testing.iptest import test_for
128 128
129 129 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
130 130 Out[3]: True
131 131
132 132 """
133 133 try:
134 134 check = import_item(item)
135 135 except (ImportError, RuntimeError):
136 136 # GTK reports Runtime error if it can't be initialized even if it's
137 137 # importable.
138 138 return False
139 139 else:
140 140 if min_version:
141 141 if callback:
142 142 # extra processing step to get version to compare
143 143 check = callback(check)
144 144
145 145 return check >= min_version
146 146 else:
147 147 return True
148 148
149 149 # Global dict where we can store information on what we have and what we don't
150 150 # have available at test run time
151 151 have = {}
152 152
153 153 have['curses'] = test_for('_curses')
154 154 have['matplotlib'] = test_for('matplotlib')
155 155 have['numpy'] = test_for('numpy')
156 156 have['pexpect'] = test_for('IPython.external.pexpect')
157 157 have['pymongo'] = test_for('pymongo')
158 158 have['pygments'] = test_for('pygments')
159 159 have['qt'] = test_for('IPython.external.qt')
160 160 have['rpy2'] = test_for('rpy2')
161 161 have['sqlite3'] = test_for('sqlite3')
162 162 have['cython'] = test_for('Cython')
163 163 have['oct2py'] = test_for('oct2py')
164 164 have['tornado'] = test_for('tornado.version_info', (2,1,0), callback=None)
165 165 have['jinja2'] = test_for('jinja2')
166 166 have['wx'] = test_for('wx')
167 167 have['wx.aui'] = test_for('wx.aui')
168 168 have['azure'] = test_for('azure')
169 169
170 170 min_zmq = (2,1,11)
171 171
172 172 have['zmq'] = test_for('zmq.pyzmq_version_info', min_zmq, callback=lambda x: x())
173 173
174 174 #-----------------------------------------------------------------------------
175 175 # Functions and classes
176 176 #-----------------------------------------------------------------------------
177 177
178 178 def report():
179 179 """Return a string with a summary report of test-related variables."""
180 180
181 181 out = [ sys_info(), '\n']
182 182
183 183 avail = []
184 184 not_avail = []
185 185
186 186 for k, is_avail in have.items():
187 187 if is_avail:
188 188 avail.append(k)
189 189 else:
190 190 not_avail.append(k)
191 191
192 192 if avail:
193 193 out.append('\nTools and libraries available at test time:\n')
194 194 avail.sort()
195 195 out.append(' ' + ' '.join(avail)+'\n')
196 196
197 197 if not_avail:
198 198 out.append('\nTools and libraries NOT available at test time:\n')
199 199 not_avail.sort()
200 200 out.append(' ' + ' '.join(not_avail)+'\n')
201 201
202 202 return ''.join(out)
203 203
204 204
205 205 def make_exclude():
206 206 """Make patterns of modules and packages to exclude from testing.
207 207
208 208 For the IPythonDoctest plugin, we need to exclude certain patterns that
209 209 cause testing problems. We should strive to minimize the number of
210 210 skipped modules, since this means untested code.
211 211
212 212 These modules and packages will NOT get scanned by nose at all for tests.
213 213 """
214 214 # Simple utility to make IPython paths more readably, we need a lot of
215 215 # these below
216 216 ipjoin = lambda *paths: pjoin('IPython', *paths)
217 217
218 218 exclusions = [ipjoin('external'),
219 219 ipjoin('quarantine'),
220 220 ipjoin('deathrow'),
221 221 # This guy is probably attic material
222 222 ipjoin('testing', 'mkdoctests'),
223 223 # Testing inputhook will need a lot of thought, to figure out
224 224 # how to have tests that don't lock up with the gui event
225 225 # loops in the picture
226 226 ipjoin('lib', 'inputhook'),
227 227 # Config files aren't really importable stand-alone
228 228 ipjoin('config', 'profile'),
229 229 # The notebook 'static' directory contains JS, css and other
230 230 # files for web serving. Occasionally projects may put a .py
231 231 # file in there (MathJax ships a conf.py), so we might as
232 232 # well play it safe and skip the whole thing.
233 ipjoin('frontend', 'html', 'notebook', 'static'),
234 ipjoin('frontend', 'html', 'notebook', 'fabfile'),
233 ipjoin('html', 'notebook', 'static'),
234 ipjoin('html', 'notebook', 'fabfile'),
235 235 ]
236 236 if not have['sqlite3']:
237 237 exclusions.append(ipjoin('core', 'tests', 'test_history'))
238 238 exclusions.append(ipjoin('core', 'history'))
239 239 if not have['wx']:
240 240 exclusions.append(ipjoin('lib', 'inputhookwx'))
241 241
242 242 if 'IPython.kernel.inprocess' not in sys.argv:
243 243 exclusions.append(ipjoin('kernel', 'inprocess'))
244 244
245 245 # FIXME: temporarily disable autoreload tests, as they can produce
246 246 # spurious failures in subsequent tests (cythonmagic).
247 247 exclusions.append(ipjoin('extensions', 'autoreload'))
248 248 exclusions.append(ipjoin('extensions', 'tests', 'test_autoreload'))
249 249
250 250 # We do this unconditionally, so that the test suite doesn't import
251 251 # gtk, changing the default encoding and masking some unicode bugs.
252 252 exclusions.append(ipjoin('lib', 'inputhookgtk'))
253 253 exclusions.append(ipjoin('kernel', 'zmq', 'gui', 'gtkembed'))
254 254
255 255 # These have to be skipped on win32 because the use echo, rm, cd, etc.
256 256 # See ticket https://github.com/ipython/ipython/issues/87
257 257 if sys.platform == 'win32':
258 258 exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip'))
259 259 exclusions.append(ipjoin('testing', 'plugin', 'dtexample'))
260 260
261 261 if not have['pexpect']:
262 262 exclusions.extend([ipjoin('lib', 'irunner'),
263 263 ipjoin('lib', 'tests', 'test_irunner'),
264 ipjoin('frontend', 'terminal', 'console'),
264 ipjoin('terminal', 'console'),
265 265 ])
266 266
267 267 if not have['zmq']:
268 268 exclusions.append(ipjoin('kernel'))
269 exclusions.append(ipjoin('frontend', 'qt'))
270 exclusions.append(ipjoin('frontend', 'html'))
271 exclusions.append(ipjoin('frontend', 'consoleapp.py'))
272 exclusions.append(ipjoin('frontend', 'terminal', 'console'))
269 exclusions.append(ipjoin('qt'))
270 exclusions.append(ipjoin('html'))
271 exclusions.append(ipjoin('consoleapp.py'))
272 exclusions.append(ipjoin('terminal', 'console'))
273 273 exclusions.append(ipjoin('parallel'))
274 274 elif not have['qt'] or not have['pygments']:
275 exclusions.append(ipjoin('frontend', 'qt'))
275 exclusions.append(ipjoin('qt'))
276 276
277 277 if not have['pymongo']:
278 278 exclusions.append(ipjoin('parallel', 'controller', 'mongodb'))
279 279 exclusions.append(ipjoin('parallel', 'tests', 'test_mongodb'))
280 280
281 281 if not have['matplotlib']:
282 282 exclusions.extend([ipjoin('core', 'pylabtools'),
283 283 ipjoin('core', 'tests', 'test_pylabtools'),
284 284 ipjoin('kernel', 'zmq', 'pylab'),
285 285 ])
286 286
287 287 if not have['cython']:
288 288 exclusions.extend([ipjoin('extensions', 'cythonmagic')])
289 289 exclusions.extend([ipjoin('extensions', 'tests', 'test_cythonmagic')])
290 290
291 291 if not have['oct2py']:
292 292 exclusions.extend([ipjoin('extensions', 'octavemagic')])
293 293 exclusions.extend([ipjoin('extensions', 'tests', 'test_octavemagic')])
294 294
295 295 if not have['tornado']:
296 exclusions.append(ipjoin('frontend', 'html'))
296 exclusions.append(ipjoin('html'))
297 297
298 298 if not have['jinja2']:
299 exclusions.append(ipjoin('frontend', 'html', 'notebook', 'notebookapp'))
299 exclusions.append(ipjoin('html', 'notebook', 'notebookapp'))
300 300
301 301 if not have['rpy2'] or not have['numpy']:
302 302 exclusions.append(ipjoin('extensions', 'rmagic'))
303 303 exclusions.append(ipjoin('extensions', 'tests', 'test_rmagic'))
304 304
305 305 if not have['azure']:
306 exclusions.append(ipjoin('frontend', 'html', 'notebook', 'services', 'notebooks', 'azurenbmanager'))
306 exclusions.append(ipjoin('html', 'notebook', 'services', 'notebooks', 'azurenbmanager'))
307 307
308 308 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
309 309 if sys.platform == 'win32':
310 310 exclusions = [s.replace('\\','\\\\') for s in exclusions]
311 311
312 312 # check for any exclusions that don't seem to exist:
313 313 parent, _ = os.path.split(get_ipython_package_dir())
314 314 for exclusion in exclusions:
315 315 if exclusion.endswith(('deathrow', 'quarantine')):
316 316 # ignore deathrow/quarantine, which exist in dev, but not install
317 317 continue
318 318 fullpath = pjoin(parent, exclusion)
319 319 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
320 320 warn("Excluding nonexistent file: %r" % exclusion)
321 321
322 322 return exclusions
323 323
324 324
325 325 class IPTester(object):
326 326 """Call that calls iptest or trial in a subprocess.
327 327 """
328 328 #: string, name of test runner that will be called
329 329 runner = None
330 330 #: list, parameters for test runner
331 331 params = None
332 332 #: list, arguments of system call to be made to call test runner
333 333 call_args = None
334 334 #: list, subprocesses we start (for cleanup)
335 335 processes = None
336 336 #: str, coverage xml output file
337 337 coverage_xml = None
338 338
339 339 def __init__(self, runner='iptest', params=None):
340 340 """Create new test runner."""
341 341 p = os.path
342 342 if runner == 'iptest':
343 343 iptest_app = get_ipython_module_path('IPython.testing.iptest')
344 344 self.runner = pycmd2argv(iptest_app) + sys.argv[1:]
345 345 else:
346 346 raise Exception('Not a valid test runner: %s' % repr(runner))
347 347 if params is None:
348 348 params = []
349 349 if isinstance(params, str):
350 350 params = [params]
351 351 self.params = params
352 352
353 353 # Assemble call
354 354 self.call_args = self.runner+self.params
355 355
356 356 # Find the section we're testing (IPython.foo)
357 357 for sect in self.params:
358 358 if sect.startswith('IPython'): break
359 359 else:
360 360 raise ValueError("Section not found", self.params)
361 361
362 362 if '--with-xunit' in self.call_args:
363 363
364 364 self.call_args.append('--xunit-file')
365 365 # FIXME: when Windows uses subprocess.call, these extra quotes are unnecessary:
366 366 xunit_file = path.abspath(sect+'.xunit.xml')
367 367 if sys.platform == 'win32':
368 368 xunit_file = '"%s"' % xunit_file
369 369 self.call_args.append(xunit_file)
370 370
371 371 if '--with-xml-coverage' in self.call_args:
372 372 self.coverage_xml = path.abspath(sect+".coverage.xml")
373 373 self.call_args.remove('--with-xml-coverage')
374 374 self.call_args = ["coverage", "run", "--source="+sect] + self.call_args[1:]
375 375
376 376 # Store anything we start to clean up on deletion
377 377 self.processes = []
378 378
379 379 def _run_cmd(self):
380 380 with TemporaryDirectory() as IPYTHONDIR:
381 381 env = os.environ.copy()
382 382 env['IPYTHONDIR'] = IPYTHONDIR
383 383 # print >> sys.stderr, '*** CMD:', ' '.join(self.call_args) # dbg
384 384 subp = subprocess.Popen(self.call_args, env=env)
385 385 self.processes.append(subp)
386 386 # If this fails, the process will be left in self.processes and
387 387 # cleaned up later, but if the wait call succeeds, then we can
388 388 # clear the stored process.
389 389 retcode = subp.wait()
390 390 self.processes.pop()
391 391 return retcode
392 392
393 393 def run(self):
394 394 """Run the stored commands"""
395 395 try:
396 396 retcode = self._run_cmd()
397 397 except KeyboardInterrupt:
398 398 return -signal.SIGINT
399 399 except:
400 400 import traceback
401 401 traceback.print_exc()
402 402 return 1 # signal failure
403 403
404 404 if self.coverage_xml:
405 405 subprocess.call(["coverage", "xml", "-o", self.coverage_xml])
406 406 return retcode
407 407
408 408 def __del__(self):
409 409 """Cleanup on exit by killing any leftover processes."""
410 410 for subp in self.processes:
411 411 if subp.poll() is not None:
412 412 continue # process is already dead
413 413
414 414 try:
415 415 print('Cleaning up stale PID: %d' % subp.pid)
416 416 subp.kill()
417 417 except: # (OSError, WindowsError) ?
418 418 # This is just a best effort, if we fail or the process was
419 419 # really gone, ignore it.
420 420 pass
421 421 else:
422 422 for i in range(10):
423 423 if subp.poll() is None:
424 424 time.sleep(0.1)
425 425 else:
426 426 break
427 427
428 428 if subp.poll() is None:
429 429 # The process did not die...
430 430 print('... failed. Manual cleanup may be required.')
431 431
432
432 433 def make_runners(inc_slow=False):
433 434 """Define the top-level packages that need to be tested.
434 435 """
435 436
436 437 # Packages to be tested via nose, that only depend on the stdlib
437 nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'lib',
438 nose_pkg_names = ['config', 'core', 'extensions', 'lib', 'terminal',
438 439 'testing', 'utils', 'nbformat' ]
439 440
441 if have['qt']:
442 nose_pkg_names.append('qt')
443
444 if have['tornado']:
445 nose_pkg_names.append('html')
446
440 447 if have['zmq']:
441 448 nose_pkg_names.append('kernel')
442 449 nose_pkg_names.append('kernel.inprocess')
443 450 if inc_slow:
444 451 nose_pkg_names.append('parallel')
445 452
446 453 # For debugging this code, only load quick stuff
447 454 #nose_pkg_names = ['core', 'extensions'] # dbg
448 455
449 456 # Make fully qualified package names prepending 'IPython.' to our name lists
450 457 nose_packages = ['IPython.%s' % m for m in nose_pkg_names ]
451 458
452 459 # Make runners
453 460 runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ]
454 461
455 462 return runners
456 463
457 464
458 465 def run_iptest():
459 466 """Run the IPython test suite using nose.
460 467
461 468 This function is called when this script is **not** called with the form
462 469 `iptest all`. It simply calls nose with appropriate command line flags
463 470 and accepts all of the standard nose arguments.
464 471 """
465 472 # Apply our monkeypatch to Xunit
466 473 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
467 474 monkeypatch_xunit()
468 475
469 476 warnings.filterwarnings('ignore',
470 477 'This will be removed soon. Use IPython.testing.util instead')
471 478
472 479 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
473 480
474 481 '--with-ipdoctest',
475 482 '--ipdoctest-tests','--ipdoctest-extension=txt',
476 483
477 484 # We add --exe because of setuptools' imbecility (it
478 485 # blindly does chmod +x on ALL files). Nose does the
479 486 # right thing and it tries to avoid executables,
480 487 # setuptools unfortunately forces our hand here. This
481 488 # has been discussed on the distutils list and the
482 489 # setuptools devs refuse to fix this problem!
483 490 '--exe',
484 491 ]
485 492 if '-a' not in argv and '-A' not in argv:
486 493 argv = argv + ['-a', '!crash']
487 494
488 495 if nose.__version__ >= '0.11':
489 496 # I don't fully understand why we need this one, but depending on what
490 497 # directory the test suite is run from, if we don't give it, 0 tests
491 498 # get run. Specifically, if the test suite is run from the source dir
492 499 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
493 500 # even if the same call done in this directory works fine). It appears
494 501 # that if the requested package is in the current dir, nose bails early
495 502 # by default. Since it's otherwise harmless, leave it in by default
496 503 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
497 504 argv.append('--traverse-namespace')
498 505
499 506 # use our plugin for doctesting. It will remove the standard doctest plugin
500 507 # if it finds it enabled
501 508 plugins = [IPythonDoctest(make_exclude()), KnownFailure()]
502 509
503 510 # We need a global ipython running in this process, but the special
504 511 # in-process group spawns its own IPython kernels, so for *that* group we
505 512 # must avoid also opening the global one (otherwise there's a conflict of
506 513 # singletons). Ultimately the solution to this problem is to refactor our
507 514 # assumptions about what needs to be a singleton and what doesn't (app
508 515 # objects should, individual shells shouldn't). But for now, this
509 516 # workaround allows the test suite for the inprocess module to complete.
510 517 if not 'IPython.kernel.inprocess' in sys.argv:
511 518 globalipapp.start_ipython()
512 519
513 520 # Now nose can run
514 521 TestProgram(argv=argv, addplugins=plugins)
515 522
516 523
517 524 def run_iptestall(inc_slow=False):
518 525 """Run the entire IPython test suite by calling nose and trial.
519 526
520 527 This function constructs :class:`IPTester` instances for all IPython
521 528 modules and package and then runs each of them. This causes the modules
522 529 and packages of IPython to be tested each in their own subprocess using
523 530 nose.
524 531
525 532 Parameters
526 533 ----------
527 534
528 535 inc_slow : bool, optional
529 536 Include slow tests, like IPython.parallel. By default, these tests aren't
530 537 run.
531 538 """
532 539
533 540 runners = make_runners(inc_slow=inc_slow)
534 541
535 542 # Run the test runners in a temporary dir so we can nuke it when finished
536 543 # to clean up any junk files left over by accident. This also makes it
537 544 # robust against being run in non-writeable directories by mistake, as the
538 545 # temp dir will always be user-writeable.
539 546 curdir = os.getcwdu()
540 547 testdir = tempfile.gettempdir()
541 548 os.chdir(testdir)
542 549
543 550 # Run all test runners, tracking execution time
544 551 failed = []
545 552 t_start = time.time()
546 553 try:
547 554 for (name, runner) in runners:
548 555 print('*'*70)
549 556 print('IPython test group:',name)
550 557 res = runner.run()
551 558 if res:
552 559 failed.append( (name, runner) )
553 560 if res == -signal.SIGINT:
554 561 print("Interrupted")
555 562 break
556 563 finally:
557 564 os.chdir(curdir)
558 565 t_end = time.time()
559 566 t_tests = t_end - t_start
560 567 nrunners = len(runners)
561 568 nfail = len(failed)
562 569 # summarize results
563 570 print()
564 571 print('*'*70)
565 572 print('Test suite completed for system with the following information:')
566 573 print(report())
567 574 print('Ran %s test groups in %.3fs' % (nrunners, t_tests))
568 575 print()
569 576 print('Status:')
570 577 if not failed:
571 578 print('OK')
572 579 else:
573 580 # If anything went wrong, point out what command to rerun manually to
574 581 # see the actual errors and individual summary
575 582 print('ERROR - %s out of %s test groups failed.' % (nfail, nrunners))
576 583 for name, failed_runner in failed:
577 584 print('-'*40)
578 585 print('Runner failed:',name)
579 586 print('You may wish to rerun this one individually, with:')
580 587 failed_call_args = [py3compat.cast_unicode(x) for x in failed_runner.call_args]
581 588 print(u' '.join(failed_call_args))
582 589 print()
583 590 # Ensure that our exit code indicates failure
584 591 sys.exit(1)
585 592
586 593
587 594 def main():
588 595 for arg in sys.argv[1:]:
589 596 if arg.startswith('IPython'):
590 597 # This is in-process
591 598 run_iptest()
592 599 else:
593 600 if "--all" in sys.argv:
594 601 sys.argv.remove("--all")
595 602 inc_slow = True
596 603 else:
597 604 inc_slow = False
598 605 # This starts subprocesses
599 606 run_iptestall(inc_slow=inc_slow)
600 607
601 608
602 609 if __name__ == '__main__':
603 610 main()
General Comments 0
You need to be logged in to leave comments. Login now