##// END OF EJS Templates
remove nbformat...
Min RK -
Show More
@@ -1,521 +1,519 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """IPython Test Suite Runner.
2 """IPython Test Suite Runner.
3
3
4 This module provides a main entry point to a user script to test IPython
4 This module provides a main entry point to a user script to test IPython
5 itself from the command line. There are two ways of running this script:
5 itself from the command line. There are two ways of running this script:
6
6
7 1. With the syntax `iptest all`. This runs our entire test suite by
7 1. With the syntax `iptest all`. This runs our entire test suite by
8 calling this script (with different arguments) recursively. This
8 calling this script (with different arguments) recursively. This
9 causes modules and package to be tested in different processes, using nose
9 causes modules and package to be tested in different processes, using nose
10 or trial where appropriate.
10 or trial where appropriate.
11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
12 the script simply calls nose, but with special command line flags and
12 the script simply calls nose, but with special command line flags and
13 plugins loaded.
13 plugins loaded.
14
14
15 """
15 """
16
16
17 # Copyright (c) IPython Development Team.
17 # Copyright (c) IPython Development Team.
18 # Distributed under the terms of the Modified BSD License.
18 # Distributed under the terms of the Modified BSD License.
19
19
20 from __future__ import print_function
20 from __future__ import print_function
21
21
22 import glob
22 import glob
23 from io import BytesIO
23 from io import BytesIO
24 import os
24 import os
25 import os.path as path
25 import os.path as path
26 import sys
26 import sys
27 from threading import Thread, Lock, Event
27 from threading import Thread, Lock, Event
28 import warnings
28 import warnings
29
29
30 import nose.plugins.builtin
30 import nose.plugins.builtin
31 from nose.plugins.xunit import Xunit
31 from nose.plugins.xunit import Xunit
32 from nose import SkipTest
32 from nose import SkipTest
33 from nose.core import TestProgram
33 from nose.core import TestProgram
34 from nose.plugins import Plugin
34 from nose.plugins import Plugin
35 from nose.util import safe_str
35 from nose.util import safe_str
36
36
37 from IPython.utils.process import is_cmd_found
37 from IPython.utils.process import is_cmd_found
38 from IPython.utils.py3compat import bytes_to_str
38 from IPython.utils.py3compat import bytes_to_str
39 from IPython.utils.importstring import import_item
39 from IPython.utils.importstring import import_item
40 from IPython.testing.plugin.ipdoctest import IPythonDoctest
40 from IPython.testing.plugin.ipdoctest import IPythonDoctest
41 from IPython.external.decorators import KnownFailure, knownfailureif
41 from IPython.external.decorators import KnownFailure, knownfailureif
42
42
43 pjoin = path.join
43 pjoin = path.join
44
44
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46 # Warnings control
46 # Warnings control
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48
48
49 # Twisted generates annoying warnings with Python 2.6, as will do other code
49 # Twisted generates annoying warnings with Python 2.6, as will do other code
50 # that imports 'sets' as of today
50 # that imports 'sets' as of today
51 warnings.filterwarnings('ignore', 'the sets module is deprecated',
51 warnings.filterwarnings('ignore', 'the sets module is deprecated',
52 DeprecationWarning )
52 DeprecationWarning )
53
53
54 # This one also comes from Twisted
54 # This one also comes from Twisted
55 warnings.filterwarnings('ignore', 'the sha module is deprecated',
55 warnings.filterwarnings('ignore', 'the sha module is deprecated',
56 DeprecationWarning)
56 DeprecationWarning)
57
57
58 # Wx on Fedora11 spits these out
58 # Wx on Fedora11 spits these out
59 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
59 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
60 UserWarning)
60 UserWarning)
61
61
62 # ------------------------------------------------------------------------------
62 # ------------------------------------------------------------------------------
63 # Monkeypatch Xunit to count known failures as skipped.
63 # Monkeypatch Xunit to count known failures as skipped.
64 # ------------------------------------------------------------------------------
64 # ------------------------------------------------------------------------------
65 def monkeypatch_xunit():
65 def monkeypatch_xunit():
66 try:
66 try:
67 knownfailureif(True)(lambda: None)()
67 knownfailureif(True)(lambda: None)()
68 except Exception as e:
68 except Exception as e:
69 KnownFailureTest = type(e)
69 KnownFailureTest = type(e)
70
70
71 def addError(self, test, err, capt=None):
71 def addError(self, test, err, capt=None):
72 if issubclass(err[0], KnownFailureTest):
72 if issubclass(err[0], KnownFailureTest):
73 err = (SkipTest,) + err[1:]
73 err = (SkipTest,) + err[1:]
74 return self.orig_addError(test, err, capt)
74 return self.orig_addError(test, err, capt)
75
75
76 Xunit.orig_addError = Xunit.addError
76 Xunit.orig_addError = Xunit.addError
77 Xunit.addError = addError
77 Xunit.addError = addError
78
78
79 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
80 # Check which dependencies are installed and greater than minimum version.
80 # Check which dependencies are installed and greater than minimum version.
81 #-----------------------------------------------------------------------------
81 #-----------------------------------------------------------------------------
82 def extract_version(mod):
82 def extract_version(mod):
83 return mod.__version__
83 return mod.__version__
84
84
85 def test_for(item, min_version=None, callback=extract_version):
85 def test_for(item, min_version=None, callback=extract_version):
86 """Test to see if item is importable, and optionally check against a minimum
86 """Test to see if item is importable, and optionally check against a minimum
87 version.
87 version.
88
88
89 If min_version is given, the default behavior is to check against the
89 If min_version is given, the default behavior is to check against the
90 `__version__` attribute of the item, but specifying `callback` allows you to
90 `__version__` attribute of the item, but specifying `callback` allows you to
91 extract the value you are interested in. e.g::
91 extract the value you are interested in. e.g::
92
92
93 In [1]: import sys
93 In [1]: import sys
94
94
95 In [2]: from IPython.testing.iptest import test_for
95 In [2]: from IPython.testing.iptest import test_for
96
96
97 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
97 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
98 Out[3]: True
98 Out[3]: True
99
99
100 """
100 """
101 try:
101 try:
102 check = import_item(item)
102 check = import_item(item)
103 except (ImportError, RuntimeError):
103 except (ImportError, RuntimeError):
104 # GTK reports Runtime error if it can't be initialized even if it's
104 # GTK reports Runtime error if it can't be initialized even if it's
105 # importable.
105 # importable.
106 return False
106 return False
107 else:
107 else:
108 if min_version:
108 if min_version:
109 if callback:
109 if callback:
110 # extra processing step to get version to compare
110 # extra processing step to get version to compare
111 check = callback(check)
111 check = callback(check)
112
112
113 return check >= min_version
113 return check >= min_version
114 else:
114 else:
115 return True
115 return True
116
116
117 # Global dict where we can store information on what we have and what we don't
117 # Global dict where we can store information on what we have and what we don't
118 # have available at test run time
118 # have available at test run time
119 have = {}
119 have = {}
120
120
121 have['curses'] = test_for('_curses')
121 have['curses'] = test_for('_curses')
122 have['matplotlib'] = test_for('matplotlib')
122 have['matplotlib'] = test_for('matplotlib')
123 have['numpy'] = test_for('numpy')
123 have['numpy'] = test_for('numpy')
124 have['pexpect'] = test_for('pexpect')
124 have['pexpect'] = test_for('pexpect')
125 have['pymongo'] = test_for('pymongo')
125 have['pymongo'] = test_for('pymongo')
126 have['pygments'] = test_for('pygments')
126 have['pygments'] = test_for('pygments')
127 have['qt'] = test_for('IPython.external.qt')
127 have['qt'] = test_for('IPython.external.qt')
128 have['sqlite3'] = test_for('sqlite3')
128 have['sqlite3'] = test_for('sqlite3')
129 have['tornado'] = test_for('tornado.version_info', (4,0), callback=None)
129 have['tornado'] = test_for('tornado.version_info', (4,0), callback=None)
130 have['jinja2'] = test_for('jinja2')
130 have['jinja2'] = test_for('jinja2')
131 have['mistune'] = test_for('mistune')
131 have['mistune'] = test_for('mistune')
132 have['requests'] = test_for('requests')
132 have['requests'] = test_for('requests')
133 have['sphinx'] = test_for('sphinx')
133 have['sphinx'] = test_for('sphinx')
134 have['jsonschema'] = test_for('jsonschema')
134 have['jsonschema'] = test_for('jsonschema')
135 have['terminado'] = test_for('terminado')
135 have['terminado'] = test_for('terminado')
136 have['casperjs'] = is_cmd_found('casperjs')
136 have['casperjs'] = is_cmd_found('casperjs')
137 have['phantomjs'] = is_cmd_found('phantomjs')
137 have['phantomjs'] = is_cmd_found('phantomjs')
138 have['slimerjs'] = is_cmd_found('slimerjs')
138 have['slimerjs'] = is_cmd_found('slimerjs')
139
139
140 min_zmq = (13,)
140 min_zmq = (13,)
141
141
142 have['zmq'] = test_for('zmq.pyzmq_version_info', min_zmq, callback=lambda x: x())
142 have['zmq'] = test_for('zmq.pyzmq_version_info', min_zmq, callback=lambda x: x())
143
143
144 #-----------------------------------------------------------------------------
144 #-----------------------------------------------------------------------------
145 # Test suite definitions
145 # Test suite definitions
146 #-----------------------------------------------------------------------------
146 #-----------------------------------------------------------------------------
147
147
148 test_group_names = ['parallel', 'kernel', 'kernel.inprocess', 'config', 'core',
148 test_group_names = ['parallel', 'kernel', 'kernel.inprocess', 'config', 'core',
149 'extensions', 'lib', 'terminal', 'testing', 'utils',
149 'extensions', 'lib', 'terminal', 'testing', 'utils',
150 'nbformat', 'qt', 'html', 'nbconvert'
150 'qt', 'html', 'nbconvert'
151 ]
151 ]
152
152
153 class TestSection(object):
153 class TestSection(object):
154 def __init__(self, name, includes):
154 def __init__(self, name, includes):
155 self.name = name
155 self.name = name
156 self.includes = includes
156 self.includes = includes
157 self.excludes = []
157 self.excludes = []
158 self.dependencies = []
158 self.dependencies = []
159 self.enabled = True
159 self.enabled = True
160
160
161 def exclude(self, module):
161 def exclude(self, module):
162 if not module.startswith('IPython'):
162 if not module.startswith('IPython'):
163 module = self.includes[0] + "." + module
163 module = self.includes[0] + "." + module
164 self.excludes.append(module.replace('.', os.sep))
164 self.excludes.append(module.replace('.', os.sep))
165
165
166 def requires(self, *packages):
166 def requires(self, *packages):
167 self.dependencies.extend(packages)
167 self.dependencies.extend(packages)
168
168
169 @property
169 @property
170 def will_run(self):
170 def will_run(self):
171 return self.enabled and all(have[p] for p in self.dependencies)
171 return self.enabled and all(have[p] for p in self.dependencies)
172
172
173 shims = {
173 shims = {
174 'parallel': 'ipython_parallel',
174 'parallel': 'ipython_parallel',
175 'kernel': 'ipython_kernel',
175 'kernel': 'ipython_kernel',
176 'kernel.inprocess': 'ipython_kernel.inprocess',
176 'kernel.inprocess': 'ipython_kernel.inprocess',
177 'config': 'traitlets',
177 'config': 'traitlets',
178 }
178 }
179
179
180 # Name -> (include, exclude, dependencies_met)
180 # Name -> (include, exclude, dependencies_met)
181 test_sections = {n:TestSection(n, [shims.get(n, 'IPython.%s' % n)]) for n in test_group_names}
181 test_sections = {n:TestSection(n, [shims.get(n, 'IPython.%s' % n)]) for n in test_group_names}
182
182
183
183
184 # Exclusions and dependencies
184 # Exclusions and dependencies
185 # ---------------------------
185 # ---------------------------
186
186
187 # core:
187 # core:
188 sec = test_sections['core']
188 sec = test_sections['core']
189 if not have['sqlite3']:
189 if not have['sqlite3']:
190 sec.exclude('tests.test_history')
190 sec.exclude('tests.test_history')
191 sec.exclude('history')
191 sec.exclude('history')
192 if not have['matplotlib']:
192 if not have['matplotlib']:
193 sec.exclude('pylabtools'),
193 sec.exclude('pylabtools'),
194 sec.exclude('tests.test_pylabtools')
194 sec.exclude('tests.test_pylabtools')
195
195
196 # lib:
196 # lib:
197 sec = test_sections['lib']
197 sec = test_sections['lib']
198 if not have['zmq']:
198 if not have['zmq']:
199 sec.exclude('kernel')
199 sec.exclude('kernel')
200 # We do this unconditionally, so that the test suite doesn't import
200 # We do this unconditionally, so that the test suite doesn't import
201 # gtk, changing the default encoding and masking some unicode bugs.
201 # gtk, changing the default encoding and masking some unicode bugs.
202 sec.exclude('inputhookgtk')
202 sec.exclude('inputhookgtk')
203 # We also do this unconditionally, because wx can interfere with Unix signals.
203 # We also do this unconditionally, because wx can interfere with Unix signals.
204 # There are currently no tests for it anyway.
204 # There are currently no tests for it anyway.
205 sec.exclude('inputhookwx')
205 sec.exclude('inputhookwx')
206 # Testing inputhook will need a lot of thought, to figure out
206 # Testing inputhook will need a lot of thought, to figure out
207 # how to have tests that don't lock up with the gui event
207 # how to have tests that don't lock up with the gui event
208 # loops in the picture
208 # loops in the picture
209 sec.exclude('inputhook')
209 sec.exclude('inputhook')
210
210
211 # testing:
211 # testing:
212 sec = test_sections['testing']
212 sec = test_sections['testing']
213 # These have to be skipped on win32 because they use echo, rm, cd, etc.
213 # These have to be skipped on win32 because they use echo, rm, cd, etc.
214 # See ticket https://github.com/ipython/ipython/issues/87
214 # See ticket https://github.com/ipython/ipython/issues/87
215 if sys.platform == 'win32':
215 if sys.platform == 'win32':
216 sec.exclude('plugin.test_exampleip')
216 sec.exclude('plugin.test_exampleip')
217 sec.exclude('plugin.dtexample')
217 sec.exclude('plugin.dtexample')
218
218
219 # terminal:
219 # terminal:
220 if (not have['pexpect']) or (not have['zmq']):
220 if (not have['pexpect']) or (not have['zmq']):
221 test_sections['terminal'].exclude('console')
221 test_sections['terminal'].exclude('console')
222
222
223 # parallel
223 # parallel
224 sec = test_sections['parallel']
224 sec = test_sections['parallel']
225 sec.requires('zmq')
225 sec.requires('zmq')
226 if not have['pymongo']:
226 if not have['pymongo']:
227 sec.exclude('controller.mongodb')
227 sec.exclude('controller.mongodb')
228 sec.exclude('tests.test_mongodb')
228 sec.exclude('tests.test_mongodb')
229
229
230 # kernel:
230 # kernel:
231 sec = test_sections['kernel']
231 sec = test_sections['kernel']
232 sec.requires('zmq')
232 sec.requires('zmq')
233 # The in-process kernel tests are done in a separate section
233 # The in-process kernel tests are done in a separate section
234 sec.exclude('inprocess')
234 sec.exclude('inprocess')
235 # importing gtk sets the default encoding, which we want to avoid
235 # importing gtk sets the default encoding, which we want to avoid
236 sec.exclude('gui.gtkembed')
236 sec.exclude('gui.gtkembed')
237 sec.exclude('gui.gtk3embed')
237 sec.exclude('gui.gtk3embed')
238 if not have['matplotlib']:
238 if not have['matplotlib']:
239 sec.exclude('pylab')
239 sec.exclude('pylab')
240
240
241 # kernel.inprocess:
241 # kernel.inprocess:
242 test_sections['kernel.inprocess'].requires('zmq')
242 test_sections['kernel.inprocess'].requires('zmq')
243
243
244 # extensions:
244 # extensions:
245 sec = test_sections['extensions']
245 sec = test_sections['extensions']
246 # This is deprecated in favour of rpy2
246 # This is deprecated in favour of rpy2
247 sec.exclude('rmagic')
247 sec.exclude('rmagic')
248 # autoreload does some strange stuff, so move it to its own test section
248 # autoreload does some strange stuff, so move it to its own test section
249 sec.exclude('autoreload')
249 sec.exclude('autoreload')
250 sec.exclude('tests.test_autoreload')
250 sec.exclude('tests.test_autoreload')
251 test_sections['autoreload'] = TestSection('autoreload',
251 test_sections['autoreload'] = TestSection('autoreload',
252 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
252 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
253 test_group_names.append('autoreload')
253 test_group_names.append('autoreload')
254
254
255 # qt:
255 # qt:
256 test_sections['qt'].requires('zmq', 'qt', 'pygments')
256 test_sections['qt'].requires('zmq', 'qt', 'pygments')
257
257
258 # html:
258 # html:
259 sec = test_sections['html']
259 sec = test_sections['html']
260 sec.requires('zmq', 'tornado', 'requests', 'sqlite3', 'jsonschema')
260 sec.requires('zmq', 'tornado', 'requests', 'sqlite3', 'jsonschema')
261 # The notebook 'static' directory contains JS, css and other
261 # The notebook 'static' directory contains JS, css and other
262 # files for web serving. Occasionally projects may put a .py
262 # files for web serving. Occasionally projects may put a .py
263 # file in there (MathJax ships a conf.py), so we might as
263 # file in there (MathJax ships a conf.py), so we might as
264 # well play it safe and skip the whole thing.
264 # well play it safe and skip the whole thing.
265 sec.exclude('static')
265 sec.exclude('static')
266 sec.exclude('tasks')
266 sec.exclude('tasks')
267 if not have['jinja2']:
267 if not have['jinja2']:
268 sec.exclude('notebookapp')
268 sec.exclude('notebookapp')
269 if not have['pygments'] or not have['jinja2']:
269 if not have['pygments'] or not have['jinja2']:
270 sec.exclude('nbconvert')
270 sec.exclude('nbconvert')
271 if not have['terminado']:
271 if not have['terminado']:
272 sec.exclude('terminal')
272 sec.exclude('terminal')
273
273
274 # nbconvert:
274 # nbconvert:
275 sec = test_sections['nbconvert']
275 sec = test_sections['nbconvert']
276 sec.requires('pygments', 'jinja2', 'jsonschema', 'mistune')
276 sec.requires('pygments', 'jinja2', 'jsonschema', 'mistune')
277 # Exclude nbconvert directories containing config files used to test.
277 # Exclude nbconvert directories containing config files used to test.
278 # Executing the config files with iptest would cause an exception.
278 # Executing the config files with iptest would cause an exception.
279 sec.exclude('tests.files')
279 sec.exclude('tests.files')
280 sec.exclude('exporters.tests.files')
280 sec.exclude('exporters.tests.files')
281 if not have['tornado']:
281 if not have['tornado']:
282 sec.exclude('nbconvert.post_processors.serve')
282 sec.exclude('nbconvert.post_processors.serve')
283 sec.exclude('nbconvert.post_processors.tests.test_serve')
283 sec.exclude('nbconvert.post_processors.tests.test_serve')
284
284
285 # nbformat:
286 test_sections['nbformat'].requires('jsonschema')
287
285
288 #-----------------------------------------------------------------------------
286 #-----------------------------------------------------------------------------
289 # Functions and classes
287 # Functions and classes
290 #-----------------------------------------------------------------------------
288 #-----------------------------------------------------------------------------
291
289
292 def check_exclusions_exist():
290 def check_exclusions_exist():
293 from IPython.utils.path import get_ipython_package_dir
291 from IPython.utils.path import get_ipython_package_dir
294 from IPython.utils.warn import warn
292 from IPython.utils.warn import warn
295 parent = os.path.dirname(get_ipython_package_dir())
293 parent = os.path.dirname(get_ipython_package_dir())
296 for sec in test_sections:
294 for sec in test_sections:
297 for pattern in sec.exclusions:
295 for pattern in sec.exclusions:
298 fullpath = pjoin(parent, pattern)
296 fullpath = pjoin(parent, pattern)
299 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
297 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
300 warn("Excluding nonexistent file: %r" % pattern)
298 warn("Excluding nonexistent file: %r" % pattern)
301
299
302
300
303 class ExclusionPlugin(Plugin):
301 class ExclusionPlugin(Plugin):
304 """A nose plugin to effect our exclusions of files and directories.
302 """A nose plugin to effect our exclusions of files and directories.
305 """
303 """
306 name = 'exclusions'
304 name = 'exclusions'
307 score = 3000 # Should come before any other plugins
305 score = 3000 # Should come before any other plugins
308
306
309 def __init__(self, exclude_patterns=None):
307 def __init__(self, exclude_patterns=None):
310 """
308 """
311 Parameters
309 Parameters
312 ----------
310 ----------
313
311
314 exclude_patterns : sequence of strings, optional
312 exclude_patterns : sequence of strings, optional
315 Filenames containing these patterns (as raw strings, not as regular
313 Filenames containing these patterns (as raw strings, not as regular
316 expressions) are excluded from the tests.
314 expressions) are excluded from the tests.
317 """
315 """
318 self.exclude_patterns = exclude_patterns or []
316 self.exclude_patterns = exclude_patterns or []
319 super(ExclusionPlugin, self).__init__()
317 super(ExclusionPlugin, self).__init__()
320
318
321 def options(self, parser, env=os.environ):
319 def options(self, parser, env=os.environ):
322 Plugin.options(self, parser, env)
320 Plugin.options(self, parser, env)
323
321
324 def configure(self, options, config):
322 def configure(self, options, config):
325 Plugin.configure(self, options, config)
323 Plugin.configure(self, options, config)
326 # Override nose trying to disable plugin.
324 # Override nose trying to disable plugin.
327 self.enabled = True
325 self.enabled = True
328
326
329 def wantFile(self, filename):
327 def wantFile(self, filename):
330 """Return whether the given filename should be scanned for tests.
328 """Return whether the given filename should be scanned for tests.
331 """
329 """
332 if any(pat in filename for pat in self.exclude_patterns):
330 if any(pat in filename for pat in self.exclude_patterns):
333 return False
331 return False
334 return None
332 return None
335
333
336 def wantDirectory(self, directory):
334 def wantDirectory(self, directory):
337 """Return whether the given directory should be scanned for tests.
335 """Return whether the given directory should be scanned for tests.
338 """
336 """
339 if any(pat in directory for pat in self.exclude_patterns):
337 if any(pat in directory for pat in self.exclude_patterns):
340 return False
338 return False
341 return None
339 return None
342
340
343
341
344 class StreamCapturer(Thread):
342 class StreamCapturer(Thread):
345 daemon = True # Don't hang if main thread crashes
343 daemon = True # Don't hang if main thread crashes
346 started = False
344 started = False
347 def __init__(self, echo=False):
345 def __init__(self, echo=False):
348 super(StreamCapturer, self).__init__()
346 super(StreamCapturer, self).__init__()
349 self.echo = echo
347 self.echo = echo
350 self.streams = []
348 self.streams = []
351 self.buffer = BytesIO()
349 self.buffer = BytesIO()
352 self.readfd, self.writefd = os.pipe()
350 self.readfd, self.writefd = os.pipe()
353 self.buffer_lock = Lock()
351 self.buffer_lock = Lock()
354 self.stop = Event()
352 self.stop = Event()
355
353
356 def run(self):
354 def run(self):
357 self.started = True
355 self.started = True
358
356
359 while not self.stop.is_set():
357 while not self.stop.is_set():
360 chunk = os.read(self.readfd, 1024)
358 chunk = os.read(self.readfd, 1024)
361
359
362 with self.buffer_lock:
360 with self.buffer_lock:
363 self.buffer.write(chunk)
361 self.buffer.write(chunk)
364 if self.echo:
362 if self.echo:
365 sys.stdout.write(bytes_to_str(chunk))
363 sys.stdout.write(bytes_to_str(chunk))
366
364
367 os.close(self.readfd)
365 os.close(self.readfd)
368 os.close(self.writefd)
366 os.close(self.writefd)
369
367
370 def reset_buffer(self):
368 def reset_buffer(self):
371 with self.buffer_lock:
369 with self.buffer_lock:
372 self.buffer.truncate(0)
370 self.buffer.truncate(0)
373 self.buffer.seek(0)
371 self.buffer.seek(0)
374
372
375 def get_buffer(self):
373 def get_buffer(self):
376 with self.buffer_lock:
374 with self.buffer_lock:
377 return self.buffer.getvalue()
375 return self.buffer.getvalue()
378
376
379 def ensure_started(self):
377 def ensure_started(self):
380 if not self.started:
378 if not self.started:
381 self.start()
379 self.start()
382
380
383 def halt(self):
381 def halt(self):
384 """Safely stop the thread."""
382 """Safely stop the thread."""
385 if not self.started:
383 if not self.started:
386 return
384 return
387
385
388 self.stop.set()
386 self.stop.set()
389 os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
387 os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
390 self.join()
388 self.join()
391
389
392 class SubprocessStreamCapturePlugin(Plugin):
390 class SubprocessStreamCapturePlugin(Plugin):
393 name='subprocstreams'
391 name='subprocstreams'
394 def __init__(self):
392 def __init__(self):
395 Plugin.__init__(self)
393 Plugin.__init__(self)
396 self.stream_capturer = StreamCapturer()
394 self.stream_capturer = StreamCapturer()
397 self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
395 self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
398 # This is ugly, but distant parts of the test machinery need to be able
396 # This is ugly, but distant parts of the test machinery need to be able
399 # to redirect streams, so we make the object globally accessible.
397 # to redirect streams, so we make the object globally accessible.
400 nose.iptest_stdstreams_fileno = self.get_write_fileno
398 nose.iptest_stdstreams_fileno = self.get_write_fileno
401
399
402 def get_write_fileno(self):
400 def get_write_fileno(self):
403 if self.destination == 'capture':
401 if self.destination == 'capture':
404 self.stream_capturer.ensure_started()
402 self.stream_capturer.ensure_started()
405 return self.stream_capturer.writefd
403 return self.stream_capturer.writefd
406 elif self.destination == 'discard':
404 elif self.destination == 'discard':
407 return os.open(os.devnull, os.O_WRONLY)
405 return os.open(os.devnull, os.O_WRONLY)
408 else:
406 else:
409 return sys.__stdout__.fileno()
407 return sys.__stdout__.fileno()
410
408
411 def configure(self, options, config):
409 def configure(self, options, config):
412 Plugin.configure(self, options, config)
410 Plugin.configure(self, options, config)
413 # Override nose trying to disable plugin.
411 # Override nose trying to disable plugin.
414 if self.destination == 'capture':
412 if self.destination == 'capture':
415 self.enabled = True
413 self.enabled = True
416
414
417 def startTest(self, test):
415 def startTest(self, test):
418 # Reset log capture
416 # Reset log capture
419 self.stream_capturer.reset_buffer()
417 self.stream_capturer.reset_buffer()
420
418
421 def formatFailure(self, test, err):
419 def formatFailure(self, test, err):
422 # Show output
420 # Show output
423 ec, ev, tb = err
421 ec, ev, tb = err
424 captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
422 captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
425 if captured.strip():
423 if captured.strip():
426 ev = safe_str(ev)
424 ev = safe_str(ev)
427 out = [ev, '>> begin captured subprocess output <<',
425 out = [ev, '>> begin captured subprocess output <<',
428 captured,
426 captured,
429 '>> end captured subprocess output <<']
427 '>> end captured subprocess output <<']
430 return ec, '\n'.join(out), tb
428 return ec, '\n'.join(out), tb
431
429
432 return err
430 return err
433
431
434 formatError = formatFailure
432 formatError = formatFailure
435
433
436 def finalize(self, result):
434 def finalize(self, result):
437 self.stream_capturer.halt()
435 self.stream_capturer.halt()
438
436
439
437
440 def run_iptest():
438 def run_iptest():
441 """Run the IPython test suite using nose.
439 """Run the IPython test suite using nose.
442
440
443 This function is called when this script is **not** called with the form
441 This function is called when this script is **not** called with the form
444 `iptest all`. It simply calls nose with appropriate command line flags
442 `iptest all`. It simply calls nose with appropriate command line flags
445 and accepts all of the standard nose arguments.
443 and accepts all of the standard nose arguments.
446 """
444 """
447 # Apply our monkeypatch to Xunit
445 # Apply our monkeypatch to Xunit
448 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
446 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
449 monkeypatch_xunit()
447 monkeypatch_xunit()
450
448
451 warnings.filterwarnings('ignore',
449 warnings.filterwarnings('ignore',
452 'This will be removed soon. Use IPython.testing.util instead')
450 'This will be removed soon. Use IPython.testing.util instead')
453
451
454 arg1 = sys.argv[1]
452 arg1 = sys.argv[1]
455 if arg1 in test_sections:
453 if arg1 in test_sections:
456 section = test_sections[arg1]
454 section = test_sections[arg1]
457 sys.argv[1:2] = section.includes
455 sys.argv[1:2] = section.includes
458 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
456 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
459 section = test_sections[arg1[8:]]
457 section = test_sections[arg1[8:]]
460 sys.argv[1:2] = section.includes
458 sys.argv[1:2] = section.includes
461 else:
459 else:
462 section = TestSection(arg1, includes=[arg1])
460 section = TestSection(arg1, includes=[arg1])
463
461
464
462
465 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
463 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
466 # We add --exe because of setuptools' imbecility (it
464 # We add --exe because of setuptools' imbecility (it
467 # blindly does chmod +x on ALL files). Nose does the
465 # blindly does chmod +x on ALL files). Nose does the
468 # right thing and it tries to avoid executables,
466 # right thing and it tries to avoid executables,
469 # setuptools unfortunately forces our hand here. This
467 # setuptools unfortunately forces our hand here. This
470 # has been discussed on the distutils list and the
468 # has been discussed on the distutils list and the
471 # setuptools devs refuse to fix this problem!
469 # setuptools devs refuse to fix this problem!
472 '--exe',
470 '--exe',
473 ]
471 ]
474 if '-a' not in argv and '-A' not in argv:
472 if '-a' not in argv and '-A' not in argv:
475 argv = argv + ['-a', '!crash']
473 argv = argv + ['-a', '!crash']
476
474
477 if nose.__version__ >= '0.11':
475 if nose.__version__ >= '0.11':
478 # I don't fully understand why we need this one, but depending on what
476 # I don't fully understand why we need this one, but depending on what
479 # directory the test suite is run from, if we don't give it, 0 tests
477 # directory the test suite is run from, if we don't give it, 0 tests
480 # get run. Specifically, if the test suite is run from the source dir
478 # get run. Specifically, if the test suite is run from the source dir
481 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
479 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
482 # even if the same call done in this directory works fine). It appears
480 # even if the same call done in this directory works fine). It appears
483 # that if the requested package is in the current dir, nose bails early
481 # that if the requested package is in the current dir, nose bails early
484 # by default. Since it's otherwise harmless, leave it in by default
482 # by default. Since it's otherwise harmless, leave it in by default
485 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
483 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
486 argv.append('--traverse-namespace')
484 argv.append('--traverse-namespace')
487
485
488 plugins = [ ExclusionPlugin(section.excludes), KnownFailure(),
486 plugins = [ ExclusionPlugin(section.excludes), KnownFailure(),
489 SubprocessStreamCapturePlugin() ]
487 SubprocessStreamCapturePlugin() ]
490
488
491 # we still have some vestigial doctests in core
489 # we still have some vestigial doctests in core
492 if (section.name.startswith(('core', 'IPython.core'))):
490 if (section.name.startswith(('core', 'IPython.core'))):
493 plugins.append(IPythonDoctest())
491 plugins.append(IPythonDoctest())
494 argv.extend([
492 argv.extend([
495 '--with-ipdoctest',
493 '--with-ipdoctest',
496 '--ipdoctest-tests',
494 '--ipdoctest-tests',
497 '--ipdoctest-extension=txt',
495 '--ipdoctest-extension=txt',
498 ])
496 ])
499
497
500
498
501 # Use working directory set by parent process (see iptestcontroller)
499 # Use working directory set by parent process (see iptestcontroller)
502 if 'IPTEST_WORKING_DIR' in os.environ:
500 if 'IPTEST_WORKING_DIR' in os.environ:
503 os.chdir(os.environ['IPTEST_WORKING_DIR'])
501 os.chdir(os.environ['IPTEST_WORKING_DIR'])
504
502
505 # We need a global ipython running in this process, but the special
503 # We need a global ipython running in this process, but the special
506 # in-process group spawns its own IPython kernels, so for *that* group we
504 # in-process group spawns its own IPython kernels, so for *that* group we
507 # must avoid also opening the global one (otherwise there's a conflict of
505 # must avoid also opening the global one (otherwise there's a conflict of
508 # singletons). Ultimately the solution to this problem is to refactor our
506 # singletons). Ultimately the solution to this problem is to refactor our
509 # assumptions about what needs to be a singleton and what doesn't (app
507 # assumptions about what needs to be a singleton and what doesn't (app
510 # objects should, individual shells shouldn't). But for now, this
508 # objects should, individual shells shouldn't). But for now, this
511 # workaround allows the test suite for the inprocess module to complete.
509 # workaround allows the test suite for the inprocess module to complete.
512 if 'kernel.inprocess' not in section.name:
510 if 'kernel.inprocess' not in section.name:
513 from IPython.testing import globalipapp
511 from IPython.testing import globalipapp
514 globalipapp.start_ipython()
512 globalipapp.start_ipython()
515
513
516 # Now nose can run
514 # Now nose can run
517 TestProgram(argv=argv, addplugins=plugins)
515 TestProgram(argv=argv, addplugins=plugins)
518
516
519 if __name__ == '__main__':
517 if __name__ == '__main__':
520 run_iptest()
518 run_iptest()
521
519
@@ -1,361 +1,361 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
2 # -*- coding: utf-8 -*-
3 """Setup script for IPython.
3 """Setup script for IPython.
4
4
5 Under Posix environments it works like a typical setup.py script.
5 Under Posix environments it works like a typical setup.py script.
6 Under Windows, the command sdist is not supported, since IPython
6 Under Windows, the command sdist is not supported, since IPython
7 requires utilities which are not available under Windows."""
7 requires utilities which are not available under Windows."""
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (c) 2008-2011, IPython Development Team.
10 # Copyright (c) 2008-2011, IPython Development Team.
11 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
11 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
12 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
12 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
13 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
13 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
14 #
14 #
15 # Distributed under the terms of the Modified BSD License.
15 # Distributed under the terms of the Modified BSD License.
16 #
16 #
17 # The full license is in the file COPYING.rst, distributed with this software.
17 # The full license is in the file COPYING.rst, distributed with this software.
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Minimal Python version sanity check
21 # Minimal Python version sanity check
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 from __future__ import print_function
23 from __future__ import print_function
24
24
25 import sys
25 import sys
26
26
27 # This check is also made in IPython/__init__, don't forget to update both when
27 # This check is also made in IPython/__init__, don't forget to update both when
28 # changing Python version requirements.
28 # changing Python version requirements.
29 v = sys.version_info
29 v = sys.version_info
30 if v[:2] < (2,7) or (v[0] >= 3 and v[:2] < (3,3)):
30 if v[:2] < (2,7) or (v[0] >= 3 and v[:2] < (3,3)):
31 error = "ERROR: IPython requires Python version 2.7 or 3.3 or above."
31 error = "ERROR: IPython requires Python version 2.7 or 3.3 or above."
32 print(error, file=sys.stderr)
32 print(error, file=sys.stderr)
33 sys.exit(1)
33 sys.exit(1)
34
34
35 PY3 = (sys.version_info[0] >= 3)
35 PY3 = (sys.version_info[0] >= 3)
36
36
37 # At least we're on the python version we need, move on.
37 # At least we're on the python version we need, move on.
38
38
39 #-------------------------------------------------------------------------------
39 #-------------------------------------------------------------------------------
40 # Imports
40 # Imports
41 #-------------------------------------------------------------------------------
41 #-------------------------------------------------------------------------------
42
42
43 # Stdlib imports
43 # Stdlib imports
44 import os
44 import os
45 import shutil
45 import shutil
46
46
47 from glob import glob
47 from glob import glob
48
48
49 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
49 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
50 # update it when the contents of directories change.
50 # update it when the contents of directories change.
51 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
51 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
52
52
53 from distutils.core import setup
53 from distutils.core import setup
54
54
55 # Our own imports
55 # Our own imports
56 from setupbase import target_update
56 from setupbase import target_update
57
57
58 from setupbase import (
58 from setupbase import (
59 setup_args,
59 setup_args,
60 find_packages,
60 find_packages,
61 find_package_data,
61 find_package_data,
62 check_package_data_first,
62 check_package_data_first,
63 find_entry_points,
63 find_entry_points,
64 build_scripts_entrypt,
64 build_scripts_entrypt,
65 find_data_files,
65 find_data_files,
66 check_for_readline,
66 check_for_readline,
67 git_prebuild,
67 git_prebuild,
68 check_submodule_status,
68 check_submodule_status,
69 update_submodules,
69 update_submodules,
70 require_submodules,
70 require_submodules,
71 UpdateSubmodules,
71 UpdateSubmodules,
72 get_bdist_wheel,
72 get_bdist_wheel,
73 CompileCSS,
73 CompileCSS,
74 JavascriptVersion,
74 JavascriptVersion,
75 css_js_prerelease,
75 css_js_prerelease,
76 install_symlinked,
76 install_symlinked,
77 install_lib_symlink,
77 install_lib_symlink,
78 install_scripts_for_symlink,
78 install_scripts_for_symlink,
79 unsymlink,
79 unsymlink,
80 )
80 )
81
81
82 isfile = os.path.isfile
82 isfile = os.path.isfile
83 pjoin = os.path.join
83 pjoin = os.path.join
84
84
85 #-------------------------------------------------------------------------------
85 #-------------------------------------------------------------------------------
86 # Handle OS specific things
86 # Handle OS specific things
87 #-------------------------------------------------------------------------------
87 #-------------------------------------------------------------------------------
88
88
89 if os.name in ('nt','dos'):
89 if os.name in ('nt','dos'):
90 os_name = 'windows'
90 os_name = 'windows'
91 else:
91 else:
92 os_name = os.name
92 os_name = os.name
93
93
94 # Under Windows, 'sdist' has not been supported. Now that the docs build with
94 # Under Windows, 'sdist' has not been supported. Now that the docs build with
95 # Sphinx it might work, but let's not turn it on until someone confirms that it
95 # Sphinx it might work, but let's not turn it on until someone confirms that it
96 # actually works.
96 # actually works.
97 if os_name == 'windows' and 'sdist' in sys.argv:
97 if os_name == 'windows' and 'sdist' in sys.argv:
98 print('The sdist command is not available under Windows. Exiting.')
98 print('The sdist command is not available under Windows. Exiting.')
99 sys.exit(1)
99 sys.exit(1)
100
100
101 #-------------------------------------------------------------------------------
101 #-------------------------------------------------------------------------------
102 # Make sure we aren't trying to run without submodules
102 # Make sure we aren't trying to run without submodules
103 #-------------------------------------------------------------------------------
103 #-------------------------------------------------------------------------------
104 here = os.path.abspath(os.path.dirname(__file__))
104 here = os.path.abspath(os.path.dirname(__file__))
105
105
106 def require_clean_submodules():
106 def require_clean_submodules():
107 """Check on git submodules before distutils can do anything
107 """Check on git submodules before distutils can do anything
108
108
109 Since distutils cannot be trusted to update the tree
109 Since distutils cannot be trusted to update the tree
110 after everything has been set in motion,
110 after everything has been set in motion,
111 this is not a distutils command.
111 this is not a distutils command.
112 """
112 """
113 # PACKAGERS: Add a return here to skip checks for git submodules
113 # PACKAGERS: Add a return here to skip checks for git submodules
114
114
115 # don't do anything if nothing is actually supposed to happen
115 # don't do anything if nothing is actually supposed to happen
116 for do_nothing in ('-h', '--help', '--help-commands', 'clean', 'submodule'):
116 for do_nothing in ('-h', '--help', '--help-commands', 'clean', 'submodule'):
117 if do_nothing in sys.argv:
117 if do_nothing in sys.argv:
118 return
118 return
119
119
120 status = check_submodule_status(here)
120 status = check_submodule_status(here)
121
121
122 if status == "missing":
122 if status == "missing":
123 print("checking out submodules for the first time")
123 print("checking out submodules for the first time")
124 update_submodules(here)
124 update_submodules(here)
125 elif status == "unclean":
125 elif status == "unclean":
126 print('\n'.join([
126 print('\n'.join([
127 "Cannot build / install IPython with unclean submodules",
127 "Cannot build / install IPython with unclean submodules",
128 "Please update submodules with",
128 "Please update submodules with",
129 " python setup.py submodule",
129 " python setup.py submodule",
130 "or",
130 "or",
131 " git submodule update",
131 " git submodule update",
132 "or commit any submodule changes you have made."
132 "or commit any submodule changes you have made."
133 ]))
133 ]))
134 sys.exit(1)
134 sys.exit(1)
135
135
136 require_clean_submodules()
136 require_clean_submodules()
137
137
138 #-------------------------------------------------------------------------------
138 #-------------------------------------------------------------------------------
139 # Things related to the IPython documentation
139 # Things related to the IPython documentation
140 #-------------------------------------------------------------------------------
140 #-------------------------------------------------------------------------------
141
141
142 # update the manuals when building a source dist
142 # update the manuals when building a source dist
143 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
143 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
144
144
145 # List of things to be updated. Each entry is a triplet of args for
145 # List of things to be updated. Each entry is a triplet of args for
146 # target_update()
146 # target_update()
147 to_update = [
147 to_update = [
148 # FIXME - Disabled for now: we need to redo an automatic way
148 # FIXME - Disabled for now: we need to redo an automatic way
149 # of generating the magic info inside the rst.
149 # of generating the magic info inside the rst.
150 #('docs/magic.tex',
150 #('docs/magic.tex',
151 #['IPython/Magic.py'],
151 #['IPython/Magic.py'],
152 #"cd doc && ./update_magic.sh" ),
152 #"cd doc && ./update_magic.sh" ),
153
153
154 ('docs/man/ipcluster.1.gz',
154 ('docs/man/ipcluster.1.gz',
155 ['docs/man/ipcluster.1'],
155 ['docs/man/ipcluster.1'],
156 'cd docs/man && gzip -9c ipcluster.1 > ipcluster.1.gz'),
156 'cd docs/man && gzip -9c ipcluster.1 > ipcluster.1.gz'),
157
157
158 ('docs/man/ipcontroller.1.gz',
158 ('docs/man/ipcontroller.1.gz',
159 ['docs/man/ipcontroller.1'],
159 ['docs/man/ipcontroller.1'],
160 'cd docs/man && gzip -9c ipcontroller.1 > ipcontroller.1.gz'),
160 'cd docs/man && gzip -9c ipcontroller.1 > ipcontroller.1.gz'),
161
161
162 ('docs/man/ipengine.1.gz',
162 ('docs/man/ipengine.1.gz',
163 ['docs/man/ipengine.1'],
163 ['docs/man/ipengine.1'],
164 'cd docs/man && gzip -9c ipengine.1 > ipengine.1.gz'),
164 'cd docs/man && gzip -9c ipengine.1 > ipengine.1.gz'),
165
165
166 ('docs/man/ipython.1.gz',
166 ('docs/man/ipython.1.gz',
167 ['docs/man/ipython.1'],
167 ['docs/man/ipython.1'],
168 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
168 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
169
169
170 ]
170 ]
171
171
172
172
173 [ target_update(*t) for t in to_update ]
173 [ target_update(*t) for t in to_update ]
174
174
175 #---------------------------------------------------------------------------
175 #---------------------------------------------------------------------------
176 # Find all the packages, package data, and data_files
176 # Find all the packages, package data, and data_files
177 #---------------------------------------------------------------------------
177 #---------------------------------------------------------------------------
178
178
179 packages = find_packages()
179 packages = find_packages()
180 package_data = find_package_data()
180 package_data = find_package_data()
181
181
182 data_files = find_data_files()
182 data_files = find_data_files()
183
183
184 setup_args['packages'] = packages
184 setup_args['packages'] = packages
185 setup_args['package_data'] = package_data
185 setup_args['package_data'] = package_data
186 setup_args['data_files'] = data_files
186 setup_args['data_files'] = data_files
187
187
188 #---------------------------------------------------------------------------
188 #---------------------------------------------------------------------------
189 # custom distutils commands
189 # custom distutils commands
190 #---------------------------------------------------------------------------
190 #---------------------------------------------------------------------------
191 # imports here, so they are after setuptools import if there was one
191 # imports here, so they are after setuptools import if there was one
192 from distutils.command.sdist import sdist
192 from distutils.command.sdist import sdist
193 from distutils.command.upload import upload
193 from distutils.command.upload import upload
194
194
195 class UploadWindowsInstallers(upload):
195 class UploadWindowsInstallers(upload):
196
196
197 description = "Upload Windows installers to PyPI (only used from tools/release_windows.py)"
197 description = "Upload Windows installers to PyPI (only used from tools/release_windows.py)"
198 user_options = upload.user_options + [
198 user_options = upload.user_options + [
199 ('files=', 'f', 'exe file (or glob) to upload')
199 ('files=', 'f', 'exe file (or glob) to upload')
200 ]
200 ]
201 def initialize_options(self):
201 def initialize_options(self):
202 upload.initialize_options(self)
202 upload.initialize_options(self)
203 meta = self.distribution.metadata
203 meta = self.distribution.metadata
204 base = '{name}-{version}'.format(
204 base = '{name}-{version}'.format(
205 name=meta.get_name(),
205 name=meta.get_name(),
206 version=meta.get_version()
206 version=meta.get_version()
207 )
207 )
208 self.files = os.path.join('dist', '%s.*.exe' % base)
208 self.files = os.path.join('dist', '%s.*.exe' % base)
209
209
210 def run(self):
210 def run(self):
211 for dist_file in glob(self.files):
211 for dist_file in glob(self.files):
212 self.upload_file('bdist_wininst', 'any', dist_file)
212 self.upload_file('bdist_wininst', 'any', dist_file)
213
213
214 setup_args['cmdclass'] = {
214 setup_args['cmdclass'] = {
215 'build_py': css_js_prerelease(
215 'build_py': css_js_prerelease(
216 check_package_data_first(git_prebuild('IPython'))),
216 check_package_data_first(git_prebuild('IPython'))),
217 'sdist' : css_js_prerelease(git_prebuild('IPython', sdist)),
217 'sdist' : css_js_prerelease(git_prebuild('IPython', sdist)),
218 'upload_wininst' : UploadWindowsInstallers,
218 'upload_wininst' : UploadWindowsInstallers,
219 'submodule' : UpdateSubmodules,
219 'submodule' : UpdateSubmodules,
220 'css' : CompileCSS,
220 'css' : CompileCSS,
221 'symlink': install_symlinked,
221 'symlink': install_symlinked,
222 'install_lib_symlink': install_lib_symlink,
222 'install_lib_symlink': install_lib_symlink,
223 'install_scripts_sym': install_scripts_for_symlink,
223 'install_scripts_sym': install_scripts_for_symlink,
224 'unsymlink': unsymlink,
224 'unsymlink': unsymlink,
225 'jsversion' : JavascriptVersion,
225 'jsversion' : JavascriptVersion,
226 }
226 }
227
227
228 ### Temporarily disable install while it's broken during the big split
228 ### Temporarily disable install while it's broken during the big split
229 from textwrap import dedent
229 from textwrap import dedent
230 from distutils.command.install import install
230 from distutils.command.install import install
231
231
232 class DisabledInstall(install):
232 class DisabledInstall(install):
233 def run(self):
233 def run(self):
234 msg = dedent("""
234 msg = dedent("""
235 While we are in the midst of The Big Split,
235 While we are in the midst of The Big Split,
236 IPython cannot be installed from master.
236 IPython cannot be installed from master.
237 You can use `pip install -e .` for an editable install,
237 You can use `pip install -e .` for an editable install,
238 which still works.
238 which still works.
239 """)
239 """)
240 print(msg, file=sys.stderr)
240 print(msg, file=sys.stderr)
241 raise SystemExit(1)
241 raise SystemExit(1)
242
242
243 setup_args['cmdclass']['install'] = DisabledInstall
243 setup_args['cmdclass']['install'] = DisabledInstall
244
244
245
245
246 #---------------------------------------------------------------------------
246 #---------------------------------------------------------------------------
247 # Handle scripts, dependencies, and setuptools specific things
247 # Handle scripts, dependencies, and setuptools specific things
248 #---------------------------------------------------------------------------
248 #---------------------------------------------------------------------------
249
249
250 # For some commands, use setuptools. Note that we do NOT list install here!
250 # For some commands, use setuptools. Note that we do NOT list install here!
251 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
251 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
252 needs_setuptools = set(('develop', 'release', 'bdist_egg', 'bdist_rpm',
252 needs_setuptools = set(('develop', 'release', 'bdist_egg', 'bdist_rpm',
253 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
253 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
254 'egg_info', 'easy_install', 'upload', 'install_egg_info',
254 'egg_info', 'easy_install', 'upload', 'install_egg_info',
255 ))
255 ))
256
256
257 if len(needs_setuptools.intersection(sys.argv)) > 0:
257 if len(needs_setuptools.intersection(sys.argv)) > 0:
258 import setuptools
258 import setuptools
259
259
260 # This dict is used for passing extra arguments that are setuptools
260 # This dict is used for passing extra arguments that are setuptools
261 # specific to setup
261 # specific to setup
262 setuptools_extra_args = {}
262 setuptools_extra_args = {}
263
263
264 # setuptools requirements
264 # setuptools requirements
265
265
266 pyzmq = 'pyzmq>=13'
266 pyzmq = 'pyzmq>=13'
267
267
268 extras_require = dict(
268 extras_require = dict(
269 parallel = [pyzmq],
269 parallel = [pyzmq],
270 qtconsole = [pyzmq, 'pygments'],
270 qtconsole = [pyzmq, 'pygments'],
271 doc = ['Sphinx>=1.1', 'numpydoc'],
271 doc = ['Sphinx>=1.1', 'numpydoc'],
272 test = ['nose>=0.10.1', 'requests'],
272 test = ['nose>=0.10.1', 'requests'],
273 terminal = [],
273 terminal = [],
274 nbformat = ['jsonschema>=2.0'],
274 nbformat = ['jupyter_nbformat'],
275 notebook = ['tornado>=4.0', pyzmq, 'jinja2', 'pygments', 'mistune>=0.5'],
275 notebook = ['tornado>=4.0', pyzmq, 'jinja2', 'pygments', 'mistune>=0.5'],
276 nbconvert = ['pygments', 'jinja2', 'mistune>=0.3.1']
276 nbconvert = ['pygments', 'jinja2', 'mistune>=0.3.1']
277 )
277 )
278
278
279 if not sys.platform.startswith('win'):
279 if not sys.platform.startswith('win'):
280 extras_require['notebook'].append('terminado>=0.3.3')
280 extras_require['notebook'].append('terminado>=0.3.3')
281
281
282 if sys.version_info < (3, 3):
282 if sys.version_info < (3, 3):
283 extras_require['test'].append('mock')
283 extras_require['test'].append('mock')
284
284
285 extras_require['notebook'].extend(extras_require['nbformat'])
285 extras_require['notebook'].extend(extras_require['nbformat'])
286 extras_require['nbconvert'].extend(extras_require['nbformat'])
286 extras_require['nbconvert'].extend(extras_require['nbformat'])
287
287
288 install_requires = [
288 install_requires = [
289 'decorator',
289 'decorator',
290 'pickleshare',
290 'pickleshare',
291 'simplegeneric>0.8',
291 'simplegeneric>0.8',
292 'traitlets',
292 'traitlets',
293 ]
293 ]
294
294
295 # add platform-specific dependencies
295 # add platform-specific dependencies
296 if sys.platform == 'darwin':
296 if sys.platform == 'darwin':
297 install_requires.append('appnope')
297 install_requires.append('appnope')
298 if 'bdist_wheel' in sys.argv[1:] or not check_for_readline():
298 if 'bdist_wheel' in sys.argv[1:] or not check_for_readline():
299 install_requires.append('gnureadline')
299 install_requires.append('gnureadline')
300
300
301 if sys.platform.startswith('win'):
301 if sys.platform.startswith('win'):
302 extras_require['terminal'].append('pyreadline>=2.0')
302 extras_require['terminal'].append('pyreadline>=2.0')
303 else:
303 else:
304 install_requires.append('pexpect')
304 install_requires.append('pexpect')
305
305
306 everything = set()
306 everything = set()
307 for deps in extras_require.values():
307 for deps in extras_require.values():
308 everything.update(deps)
308 everything.update(deps)
309 extras_require['all'] = everything
309 extras_require['all'] = everything
310
310
311 if 'setuptools' in sys.modules:
311 if 'setuptools' in sys.modules:
312 # setup.py develop should check for submodules
312 # setup.py develop should check for submodules
313 from setuptools.command.develop import develop
313 from setuptools.command.develop import develop
314 setup_args['cmdclass']['develop'] = require_submodules(develop)
314 setup_args['cmdclass']['develop'] = require_submodules(develop)
315 setup_args['cmdclass']['bdist_wheel'] = css_js_prerelease(get_bdist_wheel())
315 setup_args['cmdclass']['bdist_wheel'] = css_js_prerelease(get_bdist_wheel())
316
316
317 setuptools_extra_args['zip_safe'] = False
317 setuptools_extra_args['zip_safe'] = False
318 setuptools_extra_args['entry_points'] = {
318 setuptools_extra_args['entry_points'] = {
319 'console_scripts': find_entry_points(),
319 'console_scripts': find_entry_points(),
320 'pygments.lexers': [
320 'pygments.lexers': [
321 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
321 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
322 'ipython = IPython.lib.lexers:IPythonLexer',
322 'ipython = IPython.lib.lexers:IPythonLexer',
323 'ipython3 = IPython.lib.lexers:IPython3Lexer',
323 'ipython3 = IPython.lib.lexers:IPython3Lexer',
324 ],
324 ],
325 }
325 }
326 setup_args['extras_require'] = extras_require
326 setup_args['extras_require'] = extras_require
327 requires = setup_args['install_requires'] = install_requires
327 requires = setup_args['install_requires'] = install_requires
328
328
329 # Script to be run by the windows binary installer after the default setup
329 # Script to be run by the windows binary installer after the default setup
330 # routine, to add shortcuts and similar windows-only things. Windows
330 # routine, to add shortcuts and similar windows-only things. Windows
331 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
331 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
332 # doesn't find them.
332 # doesn't find them.
333 if 'bdist_wininst' in sys.argv:
333 if 'bdist_wininst' in sys.argv:
334 if len(sys.argv) > 2 and \
334 if len(sys.argv) > 2 and \
335 ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
335 ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
336 print("ERROR: bdist_wininst must be run alone. Exiting.", file=sys.stderr)
336 print("ERROR: bdist_wininst must be run alone. Exiting.", file=sys.stderr)
337 sys.exit(1)
337 sys.exit(1)
338 setup_args['data_files'].append(
338 setup_args['data_files'].append(
339 ['Scripts', ('scripts/ipython.ico', 'scripts/ipython_nb.ico')])
339 ['Scripts', ('scripts/ipython.ico', 'scripts/ipython_nb.ico')])
340 setup_args['scripts'] = [pjoin('scripts','ipython_win_post_install.py')]
340 setup_args['scripts'] = [pjoin('scripts','ipython_win_post_install.py')]
341 setup_args['options'] = {"bdist_wininst":
341 setup_args['options'] = {"bdist_wininst":
342 {"install_script":
342 {"install_script":
343 "ipython_win_post_install.py"}}
343 "ipython_win_post_install.py"}}
344
344
345 else:
345 else:
346 # scripts has to be a non-empty list, or install_scripts isn't called
346 # scripts has to be a non-empty list, or install_scripts isn't called
347 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
347 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
348
348
349 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
349 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
350
350
351 #---------------------------------------------------------------------------
351 #---------------------------------------------------------------------------
352 # Do the actual setup now
352 # Do the actual setup now
353 #---------------------------------------------------------------------------
353 #---------------------------------------------------------------------------
354
354
355 setup_args.update(setuptools_extra_args)
355 setup_args.update(setuptools_extra_args)
356
356
357 def main():
357 def main():
358 setup(**setup_args)
358 setup(**setup_args)
359
359
360 if __name__ == '__main__':
360 if __name__ == '__main__':
361 main()
361 main()
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now