##// END OF EJS Templates
remove qtconsole
Min RK -
Show More
@@ -1,494 +1,490 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')
128 have['sqlite3'] = test_for('sqlite3')
127 have['sqlite3'] = test_for('sqlite3')
129 have['tornado'] = test_for('tornado.version_info', (4,0), callback=None)
128 have['tornado'] = test_for('tornado.version_info', (4,0), callback=None)
130 have['jinja2'] = test_for('jinja2')
129 have['jinja2'] = test_for('jinja2')
131 have['mistune'] = test_for('mistune')
130 have['mistune'] = test_for('mistune')
132 have['requests'] = test_for('requests')
131 have['requests'] = test_for('requests')
133 have['sphinx'] = test_for('sphinx')
132 have['sphinx'] = test_for('sphinx')
134 have['jsonschema'] = test_for('jsonschema')
133 have['jsonschema'] = test_for('jsonschema')
135 have['terminado'] = test_for('terminado')
134 have['terminado'] = test_for('terminado')
136 have['casperjs'] = is_cmd_found('casperjs')
135 have['casperjs'] = is_cmd_found('casperjs')
137 have['phantomjs'] = is_cmd_found('phantomjs')
136 have['phantomjs'] = is_cmd_found('phantomjs')
138 have['slimerjs'] = is_cmd_found('slimerjs')
137 have['slimerjs'] = is_cmd_found('slimerjs')
139
138
140 min_zmq = (13,)
139 min_zmq = (13,)
141
140
142 have['zmq'] = test_for('zmq.pyzmq_version_info', min_zmq, callback=lambda x: x())
141 have['zmq'] = test_for('zmq.pyzmq_version_info', min_zmq, callback=lambda x: x())
143
142
144 #-----------------------------------------------------------------------------
143 #-----------------------------------------------------------------------------
145 # Test suite definitions
144 # Test suite definitions
146 #-----------------------------------------------------------------------------
145 #-----------------------------------------------------------------------------
147
146
148 test_group_names = ['core',
147 test_group_names = ['core',
149 'extensions', 'lib', 'terminal', 'testing', 'utils',
148 'extensions', 'lib', 'terminal', 'testing', 'utils',
150 'qt', 'html', 'nbconvert'
149 'html', 'nbconvert'
151 ]
150 ]
152
151
153 class TestSection(object):
152 class TestSection(object):
154 def __init__(self, name, includes):
153 def __init__(self, name, includes):
155 self.name = name
154 self.name = name
156 self.includes = includes
155 self.includes = includes
157 self.excludes = []
156 self.excludes = []
158 self.dependencies = []
157 self.dependencies = []
159 self.enabled = True
158 self.enabled = True
160
159
161 def exclude(self, module):
160 def exclude(self, module):
162 if not module.startswith('IPython'):
161 if not module.startswith('IPython'):
163 module = self.includes[0] + "." + module
162 module = self.includes[0] + "." + module
164 self.excludes.append(module.replace('.', os.sep))
163 self.excludes.append(module.replace('.', os.sep))
165
164
166 def requires(self, *packages):
165 def requires(self, *packages):
167 self.dependencies.extend(packages)
166 self.dependencies.extend(packages)
168
167
169 @property
168 @property
170 def will_run(self):
169 def will_run(self):
171 return self.enabled and all(have[p] for p in self.dependencies)
170 return self.enabled and all(have[p] for p in self.dependencies)
172
171
173 shims = {
172 shims = {
174 'html': 'jupyter_notebook',
173 'html': 'jupyter_notebook',
175 }
174 }
176
175
177 # Name -> (include, exclude, dependencies_met)
176 # Name -> (include, exclude, dependencies_met)
178 test_sections = {n:TestSection(n, [shims.get(n, 'IPython.%s' % n)]) for n in test_group_names}
177 test_sections = {n:TestSection(n, [shims.get(n, 'IPython.%s' % n)]) for n in test_group_names}
179
178
180
179
181 # Exclusions and dependencies
180 # Exclusions and dependencies
182 # ---------------------------
181 # ---------------------------
183
182
184 # core:
183 # core:
185 sec = test_sections['core']
184 sec = test_sections['core']
186 if not have['sqlite3']:
185 if not have['sqlite3']:
187 sec.exclude('tests.test_history')
186 sec.exclude('tests.test_history')
188 sec.exclude('history')
187 sec.exclude('history')
189 if not have['matplotlib']:
188 if not have['matplotlib']:
190 sec.exclude('pylabtools'),
189 sec.exclude('pylabtools'),
191 sec.exclude('tests.test_pylabtools')
190 sec.exclude('tests.test_pylabtools')
192
191
193 # lib:
192 # lib:
194 sec = test_sections['lib']
193 sec = test_sections['lib']
195 if not have['zmq']:
194 if not have['zmq']:
196 sec.exclude('kernel')
195 sec.exclude('kernel')
197 # We do this unconditionally, so that the test suite doesn't import
196 # We do this unconditionally, so that the test suite doesn't import
198 # gtk, changing the default encoding and masking some unicode bugs.
197 # gtk, changing the default encoding and masking some unicode bugs.
199 sec.exclude('inputhookgtk')
198 sec.exclude('inputhookgtk')
200 # We also do this unconditionally, because wx can interfere with Unix signals.
199 # We also do this unconditionally, because wx can interfere with Unix signals.
201 # There are currently no tests for it anyway.
200 # There are currently no tests for it anyway.
202 sec.exclude('inputhookwx')
201 sec.exclude('inputhookwx')
203 # Testing inputhook will need a lot of thought, to figure out
202 # Testing inputhook will need a lot of thought, to figure out
204 # how to have tests that don't lock up with the gui event
203 # how to have tests that don't lock up with the gui event
205 # loops in the picture
204 # loops in the picture
206 sec.exclude('inputhook')
205 sec.exclude('inputhook')
207
206
208 # testing:
207 # testing:
209 sec = test_sections['testing']
208 sec = test_sections['testing']
210 # These have to be skipped on win32 because they use echo, rm, cd, etc.
209 # These have to be skipped on win32 because they use echo, rm, cd, etc.
211 # See ticket https://github.com/ipython/ipython/issues/87
210 # See ticket https://github.com/ipython/ipython/issues/87
212 if sys.platform == 'win32':
211 if sys.platform == 'win32':
213 sec.exclude('plugin.test_exampleip')
212 sec.exclude('plugin.test_exampleip')
214 sec.exclude('plugin.dtexample')
213 sec.exclude('plugin.dtexample')
215
214
216 # don't run jupyter_console tests found via shim
215 # don't run jupyter_console tests found via shim
217 test_sections['terminal'].exclude('console')
216 test_sections['terminal'].exclude('console')
218
217
219 # extensions:
218 # extensions:
220 sec = test_sections['extensions']
219 sec = test_sections['extensions']
221 # This is deprecated in favour of rpy2
220 # This is deprecated in favour of rpy2
222 sec.exclude('rmagic')
221 sec.exclude('rmagic')
223 # autoreload does some strange stuff, so move it to its own test section
222 # autoreload does some strange stuff, so move it to its own test section
224 sec.exclude('autoreload')
223 sec.exclude('autoreload')
225 sec.exclude('tests.test_autoreload')
224 sec.exclude('tests.test_autoreload')
226 test_sections['autoreload'] = TestSection('autoreload',
225 test_sections['autoreload'] = TestSection('autoreload',
227 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
226 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
228 test_group_names.append('autoreload')
227 test_group_names.append('autoreload')
229
228
230 # qt:
231 test_sections['qt'].requires('zmq', 'qt', 'pygments')
232
233 # html:
229 # html:
234 sec = test_sections['html']
230 sec = test_sections['html']
235 sec.requires('zmq', 'tornado', 'requests', 'sqlite3', 'jsonschema')
231 sec.requires('zmq', 'tornado', 'requests', 'sqlite3', 'jsonschema')
236 # The notebook 'static' directory contains JS, css and other
232 # The notebook 'static' directory contains JS, css and other
237 # files for web serving. Occasionally projects may put a .py
233 # files for web serving. Occasionally projects may put a .py
238 # file in there (MathJax ships a conf.py), so we might as
234 # file in there (MathJax ships a conf.py), so we might as
239 # well play it safe and skip the whole thing.
235 # well play it safe and skip the whole thing.
240 sec.exclude('static')
236 sec.exclude('static')
241 sec.exclude('tasks')
237 sec.exclude('tasks')
242 if not have['jinja2']:
238 if not have['jinja2']:
243 sec.exclude('notebookapp')
239 sec.exclude('notebookapp')
244 if not have['pygments'] or not have['jinja2']:
240 if not have['pygments'] or not have['jinja2']:
245 sec.exclude('nbconvert')
241 sec.exclude('nbconvert')
246 if not have['terminado']:
242 if not have['terminado']:
247 sec.exclude('terminal')
243 sec.exclude('terminal')
248
244
249 # nbconvert:
245 # nbconvert:
250 sec = test_sections['nbconvert']
246 sec = test_sections['nbconvert']
251 sec.requires('pygments', 'jinja2', 'jsonschema', 'mistune')
247 sec.requires('pygments', 'jinja2', 'jsonschema', 'mistune')
252 # Exclude nbconvert directories containing config files used to test.
248 # Exclude nbconvert directories containing config files used to test.
253 # Executing the config files with iptest would cause an exception.
249 # Executing the config files with iptest would cause an exception.
254 sec.exclude('tests.files')
250 sec.exclude('tests.files')
255 sec.exclude('exporters.tests.files')
251 sec.exclude('exporters.tests.files')
256 if not have['tornado']:
252 if not have['tornado']:
257 sec.exclude('nbconvert.post_processors.serve')
253 sec.exclude('nbconvert.post_processors.serve')
258 sec.exclude('nbconvert.post_processors.tests.test_serve')
254 sec.exclude('nbconvert.post_processors.tests.test_serve')
259
255
260
256
261 #-----------------------------------------------------------------------------
257 #-----------------------------------------------------------------------------
262 # Functions and classes
258 # Functions and classes
263 #-----------------------------------------------------------------------------
259 #-----------------------------------------------------------------------------
264
260
265 def check_exclusions_exist():
261 def check_exclusions_exist():
266 from IPython.utils.path import get_ipython_package_dir
262 from IPython.utils.path import get_ipython_package_dir
267 from IPython.utils.warn import warn
263 from IPython.utils.warn import warn
268 parent = os.path.dirname(get_ipython_package_dir())
264 parent = os.path.dirname(get_ipython_package_dir())
269 for sec in test_sections:
265 for sec in test_sections:
270 for pattern in sec.exclusions:
266 for pattern in sec.exclusions:
271 fullpath = pjoin(parent, pattern)
267 fullpath = pjoin(parent, pattern)
272 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
268 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
273 warn("Excluding nonexistent file: %r" % pattern)
269 warn("Excluding nonexistent file: %r" % pattern)
274
270
275
271
276 class ExclusionPlugin(Plugin):
272 class ExclusionPlugin(Plugin):
277 """A nose plugin to effect our exclusions of files and directories.
273 """A nose plugin to effect our exclusions of files and directories.
278 """
274 """
279 name = 'exclusions'
275 name = 'exclusions'
280 score = 3000 # Should come before any other plugins
276 score = 3000 # Should come before any other plugins
281
277
282 def __init__(self, exclude_patterns=None):
278 def __init__(self, exclude_patterns=None):
283 """
279 """
284 Parameters
280 Parameters
285 ----------
281 ----------
286
282
287 exclude_patterns : sequence of strings, optional
283 exclude_patterns : sequence of strings, optional
288 Filenames containing these patterns (as raw strings, not as regular
284 Filenames containing these patterns (as raw strings, not as regular
289 expressions) are excluded from the tests.
285 expressions) are excluded from the tests.
290 """
286 """
291 self.exclude_patterns = exclude_patterns or []
287 self.exclude_patterns = exclude_patterns or []
292 super(ExclusionPlugin, self).__init__()
288 super(ExclusionPlugin, self).__init__()
293
289
294 def options(self, parser, env=os.environ):
290 def options(self, parser, env=os.environ):
295 Plugin.options(self, parser, env)
291 Plugin.options(self, parser, env)
296
292
297 def configure(self, options, config):
293 def configure(self, options, config):
298 Plugin.configure(self, options, config)
294 Plugin.configure(self, options, config)
299 # Override nose trying to disable plugin.
295 # Override nose trying to disable plugin.
300 self.enabled = True
296 self.enabled = True
301
297
302 def wantFile(self, filename):
298 def wantFile(self, filename):
303 """Return whether the given filename should be scanned for tests.
299 """Return whether the given filename should be scanned for tests.
304 """
300 """
305 if any(pat in filename for pat in self.exclude_patterns):
301 if any(pat in filename for pat in self.exclude_patterns):
306 return False
302 return False
307 return None
303 return None
308
304
309 def wantDirectory(self, directory):
305 def wantDirectory(self, directory):
310 """Return whether the given directory should be scanned for tests.
306 """Return whether the given directory should be scanned for tests.
311 """
307 """
312 if any(pat in directory for pat in self.exclude_patterns):
308 if any(pat in directory for pat in self.exclude_patterns):
313 return False
309 return False
314 return None
310 return None
315
311
316
312
317 class StreamCapturer(Thread):
313 class StreamCapturer(Thread):
318 daemon = True # Don't hang if main thread crashes
314 daemon = True # Don't hang if main thread crashes
319 started = False
315 started = False
320 def __init__(self, echo=False):
316 def __init__(self, echo=False):
321 super(StreamCapturer, self).__init__()
317 super(StreamCapturer, self).__init__()
322 self.echo = echo
318 self.echo = echo
323 self.streams = []
319 self.streams = []
324 self.buffer = BytesIO()
320 self.buffer = BytesIO()
325 self.readfd, self.writefd = os.pipe()
321 self.readfd, self.writefd = os.pipe()
326 self.buffer_lock = Lock()
322 self.buffer_lock = Lock()
327 self.stop = Event()
323 self.stop = Event()
328
324
329 def run(self):
325 def run(self):
330 self.started = True
326 self.started = True
331
327
332 while not self.stop.is_set():
328 while not self.stop.is_set():
333 chunk = os.read(self.readfd, 1024)
329 chunk = os.read(self.readfd, 1024)
334
330
335 with self.buffer_lock:
331 with self.buffer_lock:
336 self.buffer.write(chunk)
332 self.buffer.write(chunk)
337 if self.echo:
333 if self.echo:
338 sys.stdout.write(bytes_to_str(chunk))
334 sys.stdout.write(bytes_to_str(chunk))
339
335
340 os.close(self.readfd)
336 os.close(self.readfd)
341 os.close(self.writefd)
337 os.close(self.writefd)
342
338
343 def reset_buffer(self):
339 def reset_buffer(self):
344 with self.buffer_lock:
340 with self.buffer_lock:
345 self.buffer.truncate(0)
341 self.buffer.truncate(0)
346 self.buffer.seek(0)
342 self.buffer.seek(0)
347
343
348 def get_buffer(self):
344 def get_buffer(self):
349 with self.buffer_lock:
345 with self.buffer_lock:
350 return self.buffer.getvalue()
346 return self.buffer.getvalue()
351
347
352 def ensure_started(self):
348 def ensure_started(self):
353 if not self.started:
349 if not self.started:
354 self.start()
350 self.start()
355
351
356 def halt(self):
352 def halt(self):
357 """Safely stop the thread."""
353 """Safely stop the thread."""
358 if not self.started:
354 if not self.started:
359 return
355 return
360
356
361 self.stop.set()
357 self.stop.set()
362 os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
358 os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
363 self.join()
359 self.join()
364
360
365 class SubprocessStreamCapturePlugin(Plugin):
361 class SubprocessStreamCapturePlugin(Plugin):
366 name='subprocstreams'
362 name='subprocstreams'
367 def __init__(self):
363 def __init__(self):
368 Plugin.__init__(self)
364 Plugin.__init__(self)
369 self.stream_capturer = StreamCapturer()
365 self.stream_capturer = StreamCapturer()
370 self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
366 self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
371 # This is ugly, but distant parts of the test machinery need to be able
367 # This is ugly, but distant parts of the test machinery need to be able
372 # to redirect streams, so we make the object globally accessible.
368 # to redirect streams, so we make the object globally accessible.
373 nose.iptest_stdstreams_fileno = self.get_write_fileno
369 nose.iptest_stdstreams_fileno = self.get_write_fileno
374
370
375 def get_write_fileno(self):
371 def get_write_fileno(self):
376 if self.destination == 'capture':
372 if self.destination == 'capture':
377 self.stream_capturer.ensure_started()
373 self.stream_capturer.ensure_started()
378 return self.stream_capturer.writefd
374 return self.stream_capturer.writefd
379 elif self.destination == 'discard':
375 elif self.destination == 'discard':
380 return os.open(os.devnull, os.O_WRONLY)
376 return os.open(os.devnull, os.O_WRONLY)
381 else:
377 else:
382 return sys.__stdout__.fileno()
378 return sys.__stdout__.fileno()
383
379
384 def configure(self, options, config):
380 def configure(self, options, config):
385 Plugin.configure(self, options, config)
381 Plugin.configure(self, options, config)
386 # Override nose trying to disable plugin.
382 # Override nose trying to disable plugin.
387 if self.destination == 'capture':
383 if self.destination == 'capture':
388 self.enabled = True
384 self.enabled = True
389
385
390 def startTest(self, test):
386 def startTest(self, test):
391 # Reset log capture
387 # Reset log capture
392 self.stream_capturer.reset_buffer()
388 self.stream_capturer.reset_buffer()
393
389
394 def formatFailure(self, test, err):
390 def formatFailure(self, test, err):
395 # Show output
391 # Show output
396 ec, ev, tb = err
392 ec, ev, tb = err
397 captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
393 captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
398 if captured.strip():
394 if captured.strip():
399 ev = safe_str(ev)
395 ev = safe_str(ev)
400 out = [ev, '>> begin captured subprocess output <<',
396 out = [ev, '>> begin captured subprocess output <<',
401 captured,
397 captured,
402 '>> end captured subprocess output <<']
398 '>> end captured subprocess output <<']
403 return ec, '\n'.join(out), tb
399 return ec, '\n'.join(out), tb
404
400
405 return err
401 return err
406
402
407 formatError = formatFailure
403 formatError = formatFailure
408
404
409 def finalize(self, result):
405 def finalize(self, result):
410 self.stream_capturer.halt()
406 self.stream_capturer.halt()
411
407
412
408
413 def run_iptest():
409 def run_iptest():
414 """Run the IPython test suite using nose.
410 """Run the IPython test suite using nose.
415
411
416 This function is called when this script is **not** called with the form
412 This function is called when this script is **not** called with the form
417 `iptest all`. It simply calls nose with appropriate command line flags
413 `iptest all`. It simply calls nose with appropriate command line flags
418 and accepts all of the standard nose arguments.
414 and accepts all of the standard nose arguments.
419 """
415 """
420 # Apply our monkeypatch to Xunit
416 # Apply our monkeypatch to Xunit
421 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
417 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
422 monkeypatch_xunit()
418 monkeypatch_xunit()
423
419
424 warnings.filterwarnings('ignore',
420 warnings.filterwarnings('ignore',
425 'This will be removed soon. Use IPython.testing.util instead')
421 'This will be removed soon. Use IPython.testing.util instead')
426
422
427 arg1 = sys.argv[1]
423 arg1 = sys.argv[1]
428 if arg1 in test_sections:
424 if arg1 in test_sections:
429 section = test_sections[arg1]
425 section = test_sections[arg1]
430 sys.argv[1:2] = section.includes
426 sys.argv[1:2] = section.includes
431 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
427 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
432 section = test_sections[arg1[8:]]
428 section = test_sections[arg1[8:]]
433 sys.argv[1:2] = section.includes
429 sys.argv[1:2] = section.includes
434 else:
430 else:
435 section = TestSection(arg1, includes=[arg1])
431 section = TestSection(arg1, includes=[arg1])
436
432
437
433
438 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
434 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
439 # We add --exe because of setuptools' imbecility (it
435 # We add --exe because of setuptools' imbecility (it
440 # blindly does chmod +x on ALL files). Nose does the
436 # blindly does chmod +x on ALL files). Nose does the
441 # right thing and it tries to avoid executables,
437 # right thing and it tries to avoid executables,
442 # setuptools unfortunately forces our hand here. This
438 # setuptools unfortunately forces our hand here. This
443 # has been discussed on the distutils list and the
439 # has been discussed on the distutils list and the
444 # setuptools devs refuse to fix this problem!
440 # setuptools devs refuse to fix this problem!
445 '--exe',
441 '--exe',
446 ]
442 ]
447 if '-a' not in argv and '-A' not in argv:
443 if '-a' not in argv and '-A' not in argv:
448 argv = argv + ['-a', '!crash']
444 argv = argv + ['-a', '!crash']
449
445
450 if nose.__version__ >= '0.11':
446 if nose.__version__ >= '0.11':
451 # I don't fully understand why we need this one, but depending on what
447 # I don't fully understand why we need this one, but depending on what
452 # directory the test suite is run from, if we don't give it, 0 tests
448 # directory the test suite is run from, if we don't give it, 0 tests
453 # get run. Specifically, if the test suite is run from the source dir
449 # get run. Specifically, if the test suite is run from the source dir
454 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
450 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
455 # even if the same call done in this directory works fine). It appears
451 # even if the same call done in this directory works fine). It appears
456 # that if the requested package is in the current dir, nose bails early
452 # that if the requested package is in the current dir, nose bails early
457 # by default. Since it's otherwise harmless, leave it in by default
453 # by default. Since it's otherwise harmless, leave it in by default
458 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
454 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
459 argv.append('--traverse-namespace')
455 argv.append('--traverse-namespace')
460
456
461 plugins = [ ExclusionPlugin(section.excludes), KnownFailure(),
457 plugins = [ ExclusionPlugin(section.excludes), KnownFailure(),
462 SubprocessStreamCapturePlugin() ]
458 SubprocessStreamCapturePlugin() ]
463
459
464 # we still have some vestigial doctests in core
460 # we still have some vestigial doctests in core
465 if (section.name.startswith(('core', 'IPython.core'))):
461 if (section.name.startswith(('core', 'IPython.core'))):
466 plugins.append(IPythonDoctest())
462 plugins.append(IPythonDoctest())
467 argv.extend([
463 argv.extend([
468 '--with-ipdoctest',
464 '--with-ipdoctest',
469 '--ipdoctest-tests',
465 '--ipdoctest-tests',
470 '--ipdoctest-extension=txt',
466 '--ipdoctest-extension=txt',
471 ])
467 ])
472
468
473
469
474 # Use working directory set by parent process (see iptestcontroller)
470 # Use working directory set by parent process (see iptestcontroller)
475 if 'IPTEST_WORKING_DIR' in os.environ:
471 if 'IPTEST_WORKING_DIR' in os.environ:
476 os.chdir(os.environ['IPTEST_WORKING_DIR'])
472 os.chdir(os.environ['IPTEST_WORKING_DIR'])
477
473
478 # We need a global ipython running in this process, but the special
474 # We need a global ipython running in this process, but the special
479 # in-process group spawns its own IPython kernels, so for *that* group we
475 # in-process group spawns its own IPython kernels, so for *that* group we
480 # must avoid also opening the global one (otherwise there's a conflict of
476 # must avoid also opening the global one (otherwise there's a conflict of
481 # singletons). Ultimately the solution to this problem is to refactor our
477 # singletons). Ultimately the solution to this problem is to refactor our
482 # assumptions about what needs to be a singleton and what doesn't (app
478 # assumptions about what needs to be a singleton and what doesn't (app
483 # objects should, individual shells shouldn't). But for now, this
479 # objects should, individual shells shouldn't). But for now, this
484 # workaround allows the test suite for the inprocess module to complete.
480 # workaround allows the test suite for the inprocess module to complete.
485 if 'kernel.inprocess' not in section.name:
481 if 'kernel.inprocess' not in section.name:
486 from IPython.testing import globalipapp
482 from IPython.testing import globalipapp
487 globalipapp.start_ipython()
483 globalipapp.start_ipython()
488
484
489 # Now nose can run
485 # Now nose can run
490 TestProgram(argv=argv, addplugins=plugins)
486 TestProgram(argv=argv, addplugins=plugins)
491
487
492 if __name__ == '__main__':
488 if __name__ == '__main__':
493 run_iptest()
489 run_iptest()
494
490
@@ -1,38 +1,36 b''
1 include README.rst
1 include README.rst
2 include COPYING.rst
2 include COPYING.rst
3 include setupbase.py
3 include setupbase.py
4 include setupegg.py
4 include setupegg.py
5
5
6 graft setupext
6 graft setupext
7
7
8 graft scripts
8 graft scripts
9
9
10 # Load main dir but exclude things we don't want in the distro
10 # Load main dir but exclude things we don't want in the distro
11 graft IPython
11 graft IPython
12 prune IPython/html/static/mathjax
13
12
14 # Include some specific files and data resources we need
13 # Include some specific files and data resources we need
15 include IPython/.git_commit_info.ini
14 include IPython/.git_commit_info.ini
16 include IPython/qt/console/resources/icon/IPythonConsole.svg
17
15
18 # Documentation
16 # Documentation
19 graft docs
17 graft docs
20 exclude docs/\#*
18 exclude docs/\#*
21 exclude docs/man/*.1.gz
19 exclude docs/man/*.1.gz
22
20
23 # Examples
21 # Examples
24 graft examples
22 graft examples
25
23
26 # docs subdirs we want to skip
24 # docs subdirs we want to skip
27 prune docs/build
25 prune docs/build
28 prune docs/gh-pages
26 prune docs/gh-pages
29 prune docs/dist
27 prune docs/dist
30
28
31 # Patterns to exclude from any directory
29 # Patterns to exclude from any directory
32 global-exclude *~
30 global-exclude *~
33 global-exclude *.flc
31 global-exclude *.flc
34 global-exclude *.pyc
32 global-exclude *.pyc
35 global-exclude *.pyo
33 global-exclude *.pyo
36 global-exclude .dircopy.log
34 global-exclude .dircopy.log
37 global-exclude .git
35 global-exclude .git
38 global-exclude .ipynb_checkpoints
36 global-exclude .ipynb_checkpoints
@@ -1,140 +1,138 b''
1 #!python
1 #!python
2 """Distutils post installation script for Windows.
2 """Distutils post installation script for Windows.
3
3
4 http://docs.python.org/2/distutils/builtdist.html#the-postinstallation-script
4 http://docs.python.org/2/distutils/builtdist.html#the-postinstallation-script
5
5
6 """
6 """
7
7
8 from __future__ import print_function
8 from __future__ import print_function
9
9
10 import os
10 import os
11 import sys
11 import sys
12 import shutil
12 import shutil
13
13
14 try:
14 try:
15 import setuptools
15 import setuptools
16 have_setuptools = True
16 have_setuptools = True
17 except ImportError:
17 except ImportError:
18 have_setuptools = False
18 have_setuptools = False
19
19
20
20
21 pjoin = os.path.join
21 pjoin = os.path.join
22
22
23 # suffix for start menu folder names
23 # suffix for start menu folder names
24 pyver = "(Py%i.%i %i bit)" % (sys.version_info[0], sys.version_info[1],
24 pyver = "(Py%i.%i %i bit)" % (sys.version_info[0], sys.version_info[1],
25 (32, 64)[sys.maxsize > 2**32])
25 (32, 64)[sys.maxsize > 2**32])
26
26
27
27
28 def mkshortcut(target, description, linkdir, arguments="", iconpath='',
28 def mkshortcut(target, description, linkdir, arguments="", iconpath='',
29 workdir="%HOMEDRIVE%%HOMEPATH%", iconindex=0):
29 workdir="%HOMEDRIVE%%HOMEPATH%", iconindex=0):
30 """Make a shortcut if it doesn't exist and register its creation."""
30 """Make a shortcut if it doesn't exist and register its creation."""
31 filename = pjoin(linkdir, description + '.lnk')
31 filename = pjoin(linkdir, description + '.lnk')
32 description = "%s %s" % (description, pyver)
32 description = "%s %s" % (description, pyver)
33 create_shortcut(target, description, filename, arguments, workdir,
33 create_shortcut(target, description, filename, arguments, workdir,
34 iconpath, iconindex)
34 iconpath, iconindex)
35 file_created(filename)
35 file_created(filename)
36
36
37
37
38 def arguments(scriptsdir, script, scriptargs=''):
38 def arguments(scriptsdir, script, scriptargs=''):
39 """Return command line arguments to be passed to the python executable."""
39 """Return command line arguments to be passed to the python executable."""
40 cmdbase = suffix(pjoin(scriptsdir, script))
40 cmdbase = suffix(pjoin(scriptsdir, script))
41 if have_setuptools:
41 if have_setuptools:
42 cmdbase += '-script.py'
42 cmdbase += '-script.py'
43 return '"%s" %s' % (cmdbase, scriptargs)
43 return '"%s" %s' % (cmdbase, scriptargs)
44
44
45
45
46 def suffix(s):
46 def suffix(s):
47 """Add '3' suffix to programs for Python 3."""
47 """Add '3' suffix to programs for Python 3."""
48 if sys.version_info[0] == 3:
48 if sys.version_info[0] == 3:
49 s = s + '3'
49 s = s + '3'
50 return s
50 return s
51
51
52
52
53 def install():
53 def install():
54 """Routine to be run by the win32 installer with the -install switch."""
54 """Routine to be run by the win32 installer with the -install switch."""
55 # Get some system constants
55 # Get some system constants
56 python = pjoin(sys.prefix, 'python.exe')
56 python = pjoin(sys.prefix, 'python.exe')
57 pythonw = pjoin(sys.prefix, 'pythonw.exe')
57 pythonw = pjoin(sys.prefix, 'pythonw.exe')
58
58
59 if not have_setuptools:
59 if not have_setuptools:
60 # This currently doesn't work without setuptools,
60 # This currently doesn't work without setuptools,
61 # so don't bother making broken links
61 # so don't bother making broken links
62 print("Setuptools is required to"
62 print("Setuptools is required to"
63 " create Start Menu items.", file=sys.stderr)
63 " create Start Menu items.", file=sys.stderr)
64 print("Re-run this installer after installing"
64 print("Re-run this installer after installing"
65 " Setuptools to get Start Menu items.", file=sys.stderr)
65 " Setuptools to get Start Menu items.", file=sys.stderr)
66 return
66 return
67
67
68 # Lookup path to common startmenu ...
68 # Lookup path to common startmenu ...
69 ip_start_menu = pjoin(get_special_folder_path('CSIDL_COMMON_PROGRAMS'),
69 ip_start_menu = pjoin(get_special_folder_path('CSIDL_COMMON_PROGRAMS'),
70 'IPython %s' % pyver)
70 'IPython %s' % pyver)
71
71
72 # Create IPython entry ...
72 # Create IPython entry ...
73 if not os.path.isdir(ip_start_menu):
73 if not os.path.isdir(ip_start_menu):
74 os.mkdir(ip_start_menu)
74 os.mkdir(ip_start_menu)
75 directory_created(ip_start_menu)
75 directory_created(ip_start_menu)
76
76
77 # Create .py and .bat files to make things available from
77 # Create .py and .bat files to make things available from
78 # the Windows command line. Thanks to the Twisted project
78 # the Windows command line. Thanks to the Twisted project
79 # for this logic!
79 # for this logic!
80 programs = [
80 programs = [
81 'ipython',
81 'ipython',
82 'iptest',
82 'iptest',
83 ]
83 ]
84 programs = [suffix(p) for p in programs]
84 programs = [suffix(p) for p in programs]
85 scripts = pjoin(sys.prefix, 'scripts')
85 scripts = pjoin(sys.prefix, 'scripts')
86 if not have_setuptools:
86 if not have_setuptools:
87 # only create .bat files if we don't have setuptools
87 # only create .bat files if we don't have setuptools
88 for program in programs:
88 for program in programs:
89 raw = pjoin(scripts, program)
89 raw = pjoin(scripts, program)
90 bat = raw + '.bat'
90 bat = raw + '.bat'
91 py = raw + '.py'
91 py = raw + '.py'
92 # Create .py versions of the scripts
92 # Create .py versions of the scripts
93 shutil.copy(raw, py)
93 shutil.copy(raw, py)
94 # Create .bat files for each of the scripts
94 # Create .bat files for each of the scripts
95 bat_file = file(bat, 'w')
95 bat_file = file(bat, 'w')
96 bat_file.write("@%s %s %%*" % (python, py))
96 bat_file.write("@%s %s %%*" % (python, py))
97 bat_file.close()
97 bat_file.close()
98
98
99 # Create Start Menu shortcuts
99 # Create Start Menu shortcuts
100 iconpath = pjoin(scripts, 'ipython.ico')
100 iconpath = pjoin(scripts, 'ipython.ico')
101 mkshortcut(python, 'IPython', ip_start_menu,
101 mkshortcut(python, 'IPython', ip_start_menu,
102 arguments(scripts, 'ipython'), iconpath)
102 arguments(scripts, 'ipython'), iconpath)
103 mkshortcut(python, 'IPython (pylab mode)', ip_start_menu,
103 mkshortcut(python, 'IPython (pylab mode)', ip_start_menu,
104 arguments(scripts, 'ipython', '--pylab'), iconpath)
104 arguments(scripts, 'ipython', '--pylab'), iconpath)
105 mkshortcut(pythonw, 'IPython Qt Console', ip_start_menu,
106 arguments(scripts, 'ipython', 'qtconsole'), iconpath)
107
105
108 iconpath = pjoin(scripts, 'ipython_nb.ico')
106 iconpath = pjoin(scripts, 'ipython_nb.ico')
109 mkshortcut(python, 'IPython Notebook', ip_start_menu,
107 mkshortcut(python, 'IPython Notebook', ip_start_menu,
110 arguments(scripts, 'ipython', 'notebook'), iconpath)
108 arguments(scripts, 'ipython', 'notebook'), iconpath)
111
109
112 mkshortcut(pythonw, 'IPython Documentation', ip_start_menu,
110 mkshortcut(pythonw, 'IPython Documentation', ip_start_menu,
113 '-m webbrowser -t "http://ipython.org/documentation.html',
111 '-m webbrowser -t "http://ipython.org/documentation.html',
114 iconpath='url.dll')
112 iconpath='url.dll')
115
113
116 # Disable pysh Start item until the profile restores functionality
114 # Disable pysh Start item until the profile restores functionality
117 # Most of this code is in IPython/deathrow, and needs to be updated
115 # Most of this code is in IPython/deathrow, and needs to be updated
118 # to 0.11 APIs
116 # to 0.11 APIs
119 #mkshortcut(python, 'IPython%s (command prompt mode)', ip_start_menu,
117 #mkshortcut(python, 'IPython%s (command prompt mode)', ip_start_menu,
120 # arguments(scripts, 'ipython', 'profile=pysh --init'))
118 # arguments(scripts, 'ipython', 'profile=pysh --init'))
121
119
122
120
123 def remove():
121 def remove():
124 """Routine to be run by the win32 installer with the -remove switch."""
122 """Routine to be run by the win32 installer with the -remove switch."""
125 pass
123 pass
126
124
127
125
128 # main()
126 # main()
129 if len(sys.argv) > 1:
127 if len(sys.argv) > 1:
130 if sys.argv[1] == '-install':
128 if sys.argv[1] == '-install':
131 try:
129 try:
132 install()
130 install()
133 except OSError:
131 except OSError:
134 print("Failed to create Start Menu items, try running the"
132 print("Failed to create Start Menu items, try running the"
135 " installer as administrator.", file=sys.stderr)
133 " installer as administrator.", file=sys.stderr)
136 elif sys.argv[1] == '-remove':
134 elif sys.argv[1] == '-remove':
137 remove()
135 remove()
138 else:
136 else:
139 print("Script was called with option %s" % sys.argv[1],
137 print("Script was called with option %s" % sys.argv[1],
140 file=sys.stderr)
138 file=sys.stderr)
@@ -1,342 +1,342 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 ('docs/man/ipython.1.gz',
148 ('docs/man/ipython.1.gz',
149 ['docs/man/ipython.1'],
149 ['docs/man/ipython.1'],
150 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
150 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
151 ]
151 ]
152
152
153
153
154 [ target_update(*t) for t in to_update ]
154 [ target_update(*t) for t in to_update ]
155
155
156 #---------------------------------------------------------------------------
156 #---------------------------------------------------------------------------
157 # Find all the packages, package data, and data_files
157 # Find all the packages, package data, and data_files
158 #---------------------------------------------------------------------------
158 #---------------------------------------------------------------------------
159
159
160 packages = find_packages()
160 packages = find_packages()
161 package_data = find_package_data()
161 package_data = find_package_data()
162
162
163 data_files = find_data_files()
163 data_files = find_data_files()
164
164
165 setup_args['packages'] = packages
165 setup_args['packages'] = packages
166 setup_args['package_data'] = package_data
166 setup_args['package_data'] = package_data
167 setup_args['data_files'] = data_files
167 setup_args['data_files'] = data_files
168
168
169 #---------------------------------------------------------------------------
169 #---------------------------------------------------------------------------
170 # custom distutils commands
170 # custom distutils commands
171 #---------------------------------------------------------------------------
171 #---------------------------------------------------------------------------
172 # imports here, so they are after setuptools import if there was one
172 # imports here, so they are after setuptools import if there was one
173 from distutils.command.sdist import sdist
173 from distutils.command.sdist import sdist
174 from distutils.command.upload import upload
174 from distutils.command.upload import upload
175
175
176 class UploadWindowsInstallers(upload):
176 class UploadWindowsInstallers(upload):
177
177
178 description = "Upload Windows installers to PyPI (only used from tools/release_windows.py)"
178 description = "Upload Windows installers to PyPI (only used from tools/release_windows.py)"
179 user_options = upload.user_options + [
179 user_options = upload.user_options + [
180 ('files=', 'f', 'exe file (or glob) to upload')
180 ('files=', 'f', 'exe file (or glob) to upload')
181 ]
181 ]
182 def initialize_options(self):
182 def initialize_options(self):
183 upload.initialize_options(self)
183 upload.initialize_options(self)
184 meta = self.distribution.metadata
184 meta = self.distribution.metadata
185 base = '{name}-{version}'.format(
185 base = '{name}-{version}'.format(
186 name=meta.get_name(),
186 name=meta.get_name(),
187 version=meta.get_version()
187 version=meta.get_version()
188 )
188 )
189 self.files = os.path.join('dist', '%s.*.exe' % base)
189 self.files = os.path.join('dist', '%s.*.exe' % base)
190
190
191 def run(self):
191 def run(self):
192 for dist_file in glob(self.files):
192 for dist_file in glob(self.files):
193 self.upload_file('bdist_wininst', 'any', dist_file)
193 self.upload_file('bdist_wininst', 'any', dist_file)
194
194
195 setup_args['cmdclass'] = {
195 setup_args['cmdclass'] = {
196 'build_py': css_js_prerelease(
196 'build_py': css_js_prerelease(
197 check_package_data_first(git_prebuild('IPython'))),
197 check_package_data_first(git_prebuild('IPython'))),
198 'sdist' : css_js_prerelease(git_prebuild('IPython', sdist)),
198 'sdist' : css_js_prerelease(git_prebuild('IPython', sdist)),
199 'upload_wininst' : UploadWindowsInstallers,
199 'upload_wininst' : UploadWindowsInstallers,
200 'submodule' : UpdateSubmodules,
200 'submodule' : UpdateSubmodules,
201 'css' : CompileCSS,
201 'css' : CompileCSS,
202 'symlink': install_symlinked,
202 'symlink': install_symlinked,
203 'install_lib_symlink': install_lib_symlink,
203 'install_lib_symlink': install_lib_symlink,
204 'install_scripts_sym': install_scripts_for_symlink,
204 'install_scripts_sym': install_scripts_for_symlink,
205 'unsymlink': unsymlink,
205 'unsymlink': unsymlink,
206 'jsversion' : JavascriptVersion,
206 'jsversion' : JavascriptVersion,
207 }
207 }
208
208
209 ### Temporarily disable install while it's broken during the big split
209 ### Temporarily disable install while it's broken during the big split
210 from textwrap import dedent
210 from textwrap import dedent
211 from distutils.command.install import install
211 from distutils.command.install import install
212
212
213 class DisabledInstall(install):
213 class DisabledInstall(install):
214 def run(self):
214 def run(self):
215 msg = dedent("""
215 msg = dedent("""
216 While we are in the midst of The Big Split,
216 While we are in the midst of The Big Split,
217 IPython cannot be installed from master.
217 IPython cannot be installed from master.
218 You can use `pip install -e .` for an editable install,
218 You can use `pip install -e .` for an editable install,
219 which still works.
219 which still works.
220 """)
220 """)
221 print(msg, file=sys.stderr)
221 print(msg, file=sys.stderr)
222 raise SystemExit(1)
222 raise SystemExit(1)
223
223
224 setup_args['cmdclass']['install'] = DisabledInstall
224 setup_args['cmdclass']['install'] = DisabledInstall
225
225
226
226
227 #---------------------------------------------------------------------------
227 #---------------------------------------------------------------------------
228 # Handle scripts, dependencies, and setuptools specific things
228 # Handle scripts, dependencies, and setuptools specific things
229 #---------------------------------------------------------------------------
229 #---------------------------------------------------------------------------
230
230
231 # For some commands, use setuptools. Note that we do NOT list install here!
231 # For some commands, use setuptools. Note that we do NOT list install here!
232 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
232 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
233 needs_setuptools = set(('develop', 'release', 'bdist_egg', 'bdist_rpm',
233 needs_setuptools = set(('develop', 'release', 'bdist_egg', 'bdist_rpm',
234 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
234 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
235 'egg_info', 'easy_install', 'upload', 'install_egg_info',
235 'egg_info', 'easy_install', 'upload', 'install_egg_info',
236 ))
236 ))
237
237
238 if len(needs_setuptools.intersection(sys.argv)) > 0:
238 if len(needs_setuptools.intersection(sys.argv)) > 0:
239 import setuptools
239 import setuptools
240
240
241 # This dict is used for passing extra arguments that are setuptools
241 # This dict is used for passing extra arguments that are setuptools
242 # specific to setup
242 # specific to setup
243 setuptools_extra_args = {}
243 setuptools_extra_args = {}
244
244
245 # setuptools requirements
245 # setuptools requirements
246
246
247 pyzmq = 'pyzmq>=13'
247 pyzmq = 'pyzmq>=13'
248
248
249 extras_require = dict(
249 extras_require = dict(
250 parallel = ['ipython_parallel'],
250 parallel = ['ipython_parallel'],
251 qtconsole = [pyzmq, 'pygments'],
251 qtconsole = ['jupyter_qtconsole'],
252 doc = ['Sphinx>=1.1', 'numpydoc'],
252 doc = ['Sphinx>=1.1', 'numpydoc'],
253 test = ['nose>=0.10.1', 'requests'],
253 test = ['nose>=0.10.1', 'requests'],
254 terminal = [],
254 terminal = [],
255 nbformat = ['jupyter_nbformat'],
255 nbformat = ['jupyter_nbformat'],
256 notebook = ['tornado>=4.0', pyzmq, 'jinja2', 'pygments', 'mistune>=0.5'],
256 notebook = ['tornado>=4.0', pyzmq, 'jinja2', 'pygments', 'mistune>=0.5'],
257 nbconvert = ['pygments', 'jinja2', 'mistune>=0.3.1']
257 nbconvert = ['pygments', 'jinja2', 'mistune>=0.3.1']
258 )
258 )
259
259
260 if not sys.platform.startswith('win'):
260 if not sys.platform.startswith('win'):
261 extras_require['notebook'].append('terminado>=0.3.3')
261 extras_require['notebook'].append('terminado>=0.3.3')
262
262
263 if sys.version_info < (3, 3):
263 if sys.version_info < (3, 3):
264 extras_require['test'].append('mock')
264 extras_require['test'].append('mock')
265
265
266 extras_require['notebook'].extend(extras_require['nbformat'])
266 extras_require['notebook'].extend(extras_require['nbformat'])
267 extras_require['nbconvert'].extend(extras_require['nbformat'])
267 extras_require['nbconvert'].extend(extras_require['nbformat'])
268
268
269 install_requires = [
269 install_requires = [
270 'decorator',
270 'decorator',
271 'pickleshare',
271 'pickleshare',
272 'simplegeneric>0.8',
272 'simplegeneric>0.8',
273 'traitlets',
273 'traitlets',
274 ]
274 ]
275
275
276 # add platform-specific dependencies
276 # add platform-specific dependencies
277 if sys.platform == 'darwin':
277 if sys.platform == 'darwin':
278 install_requires.append('appnope')
278 install_requires.append('appnope')
279 if 'bdist_wheel' in sys.argv[1:] or not check_for_readline():
279 if 'bdist_wheel' in sys.argv[1:] or not check_for_readline():
280 install_requires.append('gnureadline')
280 install_requires.append('gnureadline')
281
281
282 if sys.platform.startswith('win'):
282 if sys.platform.startswith('win'):
283 extras_require['terminal'].append('pyreadline>=2.0')
283 extras_require['terminal'].append('pyreadline>=2.0')
284 else:
284 else:
285 install_requires.append('pexpect')
285 install_requires.append('pexpect')
286
286
287 everything = set()
287 everything = set()
288 for deps in extras_require.values():
288 for deps in extras_require.values():
289 everything.update(deps)
289 everything.update(deps)
290 extras_require['all'] = everything
290 extras_require['all'] = everything
291
291
292 if 'setuptools' in sys.modules:
292 if 'setuptools' in sys.modules:
293 # setup.py develop should check for submodules
293 # setup.py develop should check for submodules
294 from setuptools.command.develop import develop
294 from setuptools.command.develop import develop
295 setup_args['cmdclass']['develop'] = require_submodules(develop)
295 setup_args['cmdclass']['develop'] = require_submodules(develop)
296 setup_args['cmdclass']['bdist_wheel'] = css_js_prerelease(get_bdist_wheel())
296 setup_args['cmdclass']['bdist_wheel'] = css_js_prerelease(get_bdist_wheel())
297
297
298 setuptools_extra_args['zip_safe'] = False
298 setuptools_extra_args['zip_safe'] = False
299 setuptools_extra_args['entry_points'] = {
299 setuptools_extra_args['entry_points'] = {
300 'console_scripts': find_entry_points(),
300 'console_scripts': find_entry_points(),
301 'pygments.lexers': [
301 'pygments.lexers': [
302 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
302 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
303 'ipython = IPython.lib.lexers:IPythonLexer',
303 'ipython = IPython.lib.lexers:IPythonLexer',
304 'ipython3 = IPython.lib.lexers:IPython3Lexer',
304 'ipython3 = IPython.lib.lexers:IPython3Lexer',
305 ],
305 ],
306 }
306 }
307 setup_args['extras_require'] = extras_require
307 setup_args['extras_require'] = extras_require
308 requires = setup_args['install_requires'] = install_requires
308 requires = setup_args['install_requires'] = install_requires
309
309
310 # Script to be run by the windows binary installer after the default setup
310 # Script to be run by the windows binary installer after the default setup
311 # routine, to add shortcuts and similar windows-only things. Windows
311 # routine, to add shortcuts and similar windows-only things. Windows
312 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
312 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
313 # doesn't find them.
313 # doesn't find them.
314 if 'bdist_wininst' in sys.argv:
314 if 'bdist_wininst' in sys.argv:
315 if len(sys.argv) > 2 and \
315 if len(sys.argv) > 2 and \
316 ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
316 ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
317 print("ERROR: bdist_wininst must be run alone. Exiting.", file=sys.stderr)
317 print("ERROR: bdist_wininst must be run alone. Exiting.", file=sys.stderr)
318 sys.exit(1)
318 sys.exit(1)
319 setup_args['data_files'].append(
319 setup_args['data_files'].append(
320 ['Scripts', ('scripts/ipython.ico', 'scripts/ipython_nb.ico')])
320 ['Scripts', ('scripts/ipython.ico', 'scripts/ipython_nb.ico')])
321 setup_args['scripts'] = [pjoin('scripts','ipython_win_post_install.py')]
321 setup_args['scripts'] = [pjoin('scripts','ipython_win_post_install.py')]
322 setup_args['options'] = {"bdist_wininst":
322 setup_args['options'] = {"bdist_wininst":
323 {"install_script":
323 {"install_script":
324 "ipython_win_post_install.py"}}
324 "ipython_win_post_install.py"}}
325
325
326 else:
326 else:
327 # scripts has to be a non-empty list, or install_scripts isn't called
327 # scripts has to be a non-empty list, or install_scripts isn't called
328 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
328 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
329
329
330 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
330 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
331
331
332 #---------------------------------------------------------------------------
332 #---------------------------------------------------------------------------
333 # Do the actual setup now
333 # Do the actual setup now
334 #---------------------------------------------------------------------------
334 #---------------------------------------------------------------------------
335
335
336 setup_args.update(setuptools_extra_args)
336 setup_args.update(setuptools_extra_args)
337
337
338 def main():
338 def main():
339 setup(**setup_args)
339 setup(**setup_args)
340
340
341 if __name__ == '__main__':
341 if __name__ == '__main__':
342 main()
342 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
This diff has been collapsed as it changes many lines, (2167 lines changed) Show them Hide them
1 NO CONTENT: file was removed
NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (811 lines changed) Show them Hide them
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (594 lines changed) Show them Hide them
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
This diff has been collapsed as it changes many lines, (928 lines changed) Show them Hide them
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
This diff has been collapsed as it changes many lines, (569 lines changed) Show them Hide them
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