##// END OF EJS Templates
remove pexpect from external...
MinRK -
Show More
@@ -1,78 +1,78 b''
1 1 """Tests for two-process terminal frontend
2 2
3 3 Currently only has the most simple test possible, starting a console and running
4 4 a single command.
5 5
6 6 Authors:
7 7
8 8 * Min RK
9 9 """
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 import sys
16 16
17 17 from nose import SkipTest
18 18
19 19 import IPython.testing.tools as tt
20 20 from IPython.testing import decorators as dec
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Tests
24 24 #-----------------------------------------------------------------------------
25 25
26 26 @dec.skip_win32
27 27 def test_console_starts():
28 28 """test that `ipython console` starts a terminal"""
29 29 p, pexpect, t = start_console()
30 30 p.sendline('5')
31 31 idx = p.expect([r'Out\[\d+\]: 5', pexpect.EOF], timeout=t)
32 32 idx = p.expect([r'In \[\d+\]', pexpect.EOF], timeout=t)
33 33 stop_console(p, pexpect, t)
34 34
35 35 def test_help_output():
36 36 """ipython console --help-all works"""
37 37 tt.help_all_output_test('console')
38 38
39 39
40 40 def test_display_text():
41 41 "Ensure display protocol plain/text key is supported"
42 42 # equivalent of:
43 43 #
44 44 # x = %lsmagic
45 45 # from IPython.display import display; display(x);
46 46 p, pexpect, t = start_console()
47 47 p.sendline('x = %lsmagic')
48 48 idx = p.expect([r'In \[\d+\]', pexpect.EOF], timeout=t)
49 49 p.sendline('from IPython.display import display; display(x);')
50 50 p.expect([r'Available line magics:', pexpect.EOF], timeout=t)
51 51 stop_console(p, pexpect, t)
52 52
53 53 def stop_console(p, pexpect, t):
54 54 "Stop a running `ipython console` running via pexpect"
55 55 # send ctrl-D;ctrl-D to exit
56 56 p.sendeof()
57 57 p.sendeof()
58 58 p.expect([pexpect.EOF, pexpect.TIMEOUT], timeout=t)
59 59 if p.isalive():
60 60 p.terminate()
61 61
62 62
63 63 def start_console():
64 64 "Start `ipython console` using pexpect"
65 from IPython.external import pexpect
65 import pexpect
66 66
67 67 args = ['-m', 'IPython', 'console', '--colors=NoColor']
68 68 cmd = sys.executable
69 69
70 70 try:
71 71 p = pexpect.spawn(cmd, args=args)
72 72 except IOError:
73 73 raise SkipTest("Couldn't find command %s" % cmd)
74 74
75 75 # timeout after one minute
76 76 t = 60
77 77 idx = p.expect([r'In \[\d+\]', pexpect.EOF], timeout=t)
78 78 return p, pexpect, t
@@ -1,125 +1,125 b''
1 1 """Test embedding of IPython"""
2 2
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (C) 2013 The IPython Development Team
5 5 #
6 6 # Distributed under the terms of the BSD License. The full license is in
7 7 # the file COPYING, distributed as part of this software.
8 8 #-----------------------------------------------------------------------------
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Imports
12 12 #-----------------------------------------------------------------------------
13 13
14 14 import os
15 15 import sys
16 16 import nose.tools as nt
17 17 from IPython.utils.process import process_handler
18 18 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
19 19 from IPython.testing.decorators import skip_win32
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Tests
23 23 #-----------------------------------------------------------------------------
24 24
25 25
26 26 _sample_embed = b"""
27 27 from __future__ import print_function
28 28 import IPython
29 29
30 30 a = 3
31 31 b = 14
32 32 print(a, '.', b)
33 33
34 34 IPython.embed()
35 35
36 36 print('bye!')
37 37 """
38 38
39 39 _exit = b"exit\r"
40 40
41 41 def test_ipython_embed():
42 42 """test that `IPython.embed()` works"""
43 43 with NamedFileInTemporaryDirectory('file_with_embed.py') as f:
44 44 f.write(_sample_embed)
45 45 f.flush()
46 46 f.close() # otherwise msft won't be able to read the file
47 47
48 48 # run `python file_with_embed.py`
49 49 cmd = [sys.executable, f.name]
50 50
51 51 out, p = process_handler(cmd, lambda p: (p.communicate(_exit), p))
52 52 std = out[0].decode('UTF-8')
53 53 nt.assert_equal(p.returncode, 0)
54 54 nt.assert_in('3 . 14', std)
55 55 if os.name != 'nt':
56 56 # TODO: Fix up our different stdout references, see issue gh-14
57 57 nt.assert_in('IPython', std)
58 58 nt.assert_in('bye!', std)
59 59
60 60 @skip_win32
61 61 def test_nest_embed():
62 62 """test that `IPython.embed()` is nestable"""
63 from IPython.external import pexpect
63 import pexpect
64 64 ipy_prompt = r']:' #ansi color codes give problems matching beyond this
65 65
66 66
67 67 child = pexpect.spawn('%s -m IPython'%(sys.executable, ))
68 68 child.expect(ipy_prompt)
69 69 child.sendline("from __future__ import print_function")
70 70 child.expect(ipy_prompt)
71 71 child.sendline("import IPython")
72 72 child.expect(ipy_prompt)
73 73 child.sendline("ip0 = get_ipython()")
74 74 #enter first nested embed
75 75 child.sendline("IPython.embed()")
76 76 #skip the banner until we get to a prompt
77 77 try:
78 78 prompted = -1
79 79 while prompted != 0:
80 80 prompted = child.expect([ipy_prompt, '\r\n'])
81 81 except pexpect.TIMEOUT as e:
82 82 print(e)
83 83 #child.interact()
84 84 child.sendline("embed1 = get_ipython()"); child.expect(ipy_prompt)
85 85 child.sendline("print('true' if embed1 is not ip0 else 'false')")
86 86 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
87 87 child.expect(ipy_prompt)
88 88 child.sendline("print('true' if IPython.get_ipython() is embed1 else 'false')")
89 89 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
90 90 child.expect(ipy_prompt)
91 91 #enter second nested embed
92 92 child.sendline("IPython.embed()")
93 93 #skip the banner until we get to a prompt
94 94 try:
95 95 prompted = -1
96 96 while prompted != 0:
97 97 prompted = child.expect([ipy_prompt, '\r\n'])
98 98 except pexpect.TIMEOUT as e:
99 99 print(e)
100 100 #child.interact()
101 101 child.sendline("embed2 = get_ipython()"); child.expect(ipy_prompt)
102 102 child.sendline("print('true' if embed2 is not embed1 else 'false')")
103 103 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
104 104 child.expect(ipy_prompt)
105 105 child.sendline("print('true' if embed2 is IPython.get_ipython() else 'false')")
106 106 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
107 107 child.expect(ipy_prompt)
108 108 child.sendline('exit')
109 109 #back at first embed
110 110 child.expect(ipy_prompt)
111 111 child.sendline("print('true' if get_ipython() is embed1 else 'false')")
112 112 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
113 113 child.expect(ipy_prompt)
114 114 child.sendline("print('true' if IPython.get_ipython() is embed1 else 'false')")
115 115 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
116 116 child.expect(ipy_prompt)
117 117 child.sendline('exit')
118 118 #back at launching scope
119 119 child.expect(ipy_prompt)
120 120 child.sendline("print('true' if get_ipython() is ip0 else 'false')")
121 121 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
122 122 child.expect(ipy_prompt)
123 123 child.sendline("print('true' if IPython.get_ipython() is ip0 else 'false')")
124 124 assert(child.expect(['true\r\n', 'false\r\n']) == 0)
125 125 child.expect(ipy_prompt)
@@ -1,519 +1,513 b''
1 1 # -*- coding: utf-8 -*-
2 2 """IPython Test Suite Runner.
3 3
4 4 This module provides a main entry point to a user script to test IPython
5 5 itself from the command line. There are two ways of running this script:
6 6
7 7 1. With the syntax `iptest all`. This runs our entire test suite by
8 8 calling this script (with different arguments) recursively. This
9 9 causes modules and package to be tested in different processes, using nose
10 10 or trial where appropriate.
11 11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
12 12 the script simply calls nose, but with special command line flags and
13 13 plugins loaded.
14 14
15 15 """
16 16
17 17 # Copyright (c) IPython Development Team.
18 18 # Distributed under the terms of the Modified BSD License.
19 19
20 20 from __future__ import print_function
21 21
22 22 import glob
23 23 from io import BytesIO
24 24 import os
25 25 import os.path as path
26 26 import sys
27 27 from threading import Thread, Lock, Event
28 28 import warnings
29 29
30 30 import nose.plugins.builtin
31 31 from nose.plugins.xunit import Xunit
32 32 from nose import SkipTest
33 33 from nose.core import TestProgram
34 34 from nose.plugins import Plugin
35 35 from nose.util import safe_str
36 36
37 37 from IPython.utils.process import is_cmd_found
38 38 from IPython.utils.py3compat import bytes_to_str
39 39 from IPython.utils.importstring import import_item
40 40 from IPython.testing.plugin.ipdoctest import IPythonDoctest
41 41 from IPython.external.decorators import KnownFailure, knownfailureif
42 42
43 43 pjoin = path.join
44 44
45
46 #-----------------------------------------------------------------------------
47 # Globals
48 #-----------------------------------------------------------------------------
49
50
51 45 #-----------------------------------------------------------------------------
52 46 # Warnings control
53 47 #-----------------------------------------------------------------------------
54 48
55 49 # Twisted generates annoying warnings with Python 2.6, as will do other code
56 50 # that imports 'sets' as of today
57 51 warnings.filterwarnings('ignore', 'the sets module is deprecated',
58 52 DeprecationWarning )
59 53
60 54 # This one also comes from Twisted
61 55 warnings.filterwarnings('ignore', 'the sha module is deprecated',
62 56 DeprecationWarning)
63 57
64 58 # Wx on Fedora11 spits these out
65 59 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
66 60 UserWarning)
67 61
68 62 # ------------------------------------------------------------------------------
69 63 # Monkeypatch Xunit to count known failures as skipped.
70 64 # ------------------------------------------------------------------------------
71 65 def monkeypatch_xunit():
72 66 try:
73 67 knownfailureif(True)(lambda: None)()
74 68 except Exception as e:
75 69 KnownFailureTest = type(e)
76 70
77 71 def addError(self, test, err, capt=None):
78 72 if issubclass(err[0], KnownFailureTest):
79 73 err = (SkipTest,) + err[1:]
80 74 return self.orig_addError(test, err, capt)
81 75
82 76 Xunit.orig_addError = Xunit.addError
83 77 Xunit.addError = addError
84 78
85 79 #-----------------------------------------------------------------------------
86 80 # Check which dependencies are installed and greater than minimum version.
87 81 #-----------------------------------------------------------------------------
88 82 def extract_version(mod):
89 83 return mod.__version__
90 84
91 85 def test_for(item, min_version=None, callback=extract_version):
92 86 """Test to see if item is importable, and optionally check against a minimum
93 87 version.
94 88
95 89 If min_version is given, the default behavior is to check against the
96 90 `__version__` attribute of the item, but specifying `callback` allows you to
97 91 extract the value you are interested in. e.g::
98 92
99 93 In [1]: import sys
100 94
101 95 In [2]: from IPython.testing.iptest import test_for
102 96
103 97 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
104 98 Out[3]: True
105 99
106 100 """
107 101 try:
108 102 check = import_item(item)
109 103 except (ImportError, RuntimeError):
110 104 # GTK reports Runtime error if it can't be initialized even if it's
111 105 # importable.
112 106 return False
113 107 else:
114 108 if min_version:
115 109 if callback:
116 110 # extra processing step to get version to compare
117 111 check = callback(check)
118 112
119 113 return check >= min_version
120 114 else:
121 115 return True
122 116
123 117 # Global dict where we can store information on what we have and what we don't
124 118 # have available at test run time
125 119 have = {}
126 120
127 121 have['curses'] = test_for('_curses')
128 122 have['matplotlib'] = test_for('matplotlib')
129 123 have['numpy'] = test_for('numpy')
130 have['pexpect'] = test_for('IPython.external.pexpect')
124 have['pexpect'] = test_for('pexpect')
131 125 have['pymongo'] = test_for('pymongo')
132 126 have['pygments'] = test_for('pygments')
133 127 have['qt'] = test_for('IPython.external.qt')
134 128 have['sqlite3'] = test_for('sqlite3')
135 129 have['tornado'] = test_for('tornado.version_info', (4,0), callback=None)
136 130 have['jinja2'] = test_for('jinja2')
137 131 have['mistune'] = test_for('mistune')
138 132 have['requests'] = test_for('requests')
139 133 have['sphinx'] = test_for('sphinx')
140 134 have['jsonschema'] = test_for('jsonschema')
141 135 have['terminado'] = test_for('terminado')
142 136 have['casperjs'] = is_cmd_found('casperjs')
143 137 have['phantomjs'] = is_cmd_found('phantomjs')
144 138 have['slimerjs'] = is_cmd_found('slimerjs')
145 139
146 140 min_zmq = (13,)
147 141
148 142 have['zmq'] = test_for('zmq.pyzmq_version_info', min_zmq, callback=lambda x: x())
149 143
150 144 #-----------------------------------------------------------------------------
151 145 # Test suite definitions
152 146 #-----------------------------------------------------------------------------
153 147
154 148 test_group_names = ['parallel', 'kernel', 'kernel.inprocess', 'config', 'core',
155 149 'extensions', 'lib', 'terminal', 'testing', 'utils',
156 150 'nbformat', 'qt', 'html', 'nbconvert'
157 151 ]
158 152
159 153 class TestSection(object):
160 154 def __init__(self, name, includes):
161 155 self.name = name
162 156 self.includes = includes
163 157 self.excludes = []
164 158 self.dependencies = []
165 159 self.enabled = True
166 160
167 161 def exclude(self, module):
168 162 if not module.startswith('IPython'):
169 163 module = self.includes[0] + "." + module
170 164 self.excludes.append(module.replace('.', os.sep))
171 165
172 166 def requires(self, *packages):
173 167 self.dependencies.extend(packages)
174 168
175 169 @property
176 170 def will_run(self):
177 171 return self.enabled and all(have[p] for p in self.dependencies)
178 172
179 173 # Name -> (include, exclude, dependencies_met)
180 174 test_sections = {n:TestSection(n, ['IPython.%s' % n]) for n in test_group_names}
181 175
182 176 # Exclusions and dependencies
183 177 # ---------------------------
184 178
185 179 # core:
186 180 sec = test_sections['core']
187 181 if not have['sqlite3']:
188 182 sec.exclude('tests.test_history')
189 183 sec.exclude('history')
190 184 if not have['matplotlib']:
191 185 sec.exclude('pylabtools'),
192 186 sec.exclude('tests.test_pylabtools')
193 187
194 188 # lib:
195 189 sec = test_sections['lib']
196 190 if not have['zmq']:
197 191 sec.exclude('kernel')
198 192 # We do this unconditionally, so that the test suite doesn't import
199 193 # gtk, changing the default encoding and masking some unicode bugs.
200 194 sec.exclude('inputhookgtk')
201 195 # We also do this unconditionally, because wx can interfere with Unix signals.
202 196 # There are currently no tests for it anyway.
203 197 sec.exclude('inputhookwx')
204 198 # Testing inputhook will need a lot of thought, to figure out
205 199 # how to have tests that don't lock up with the gui event
206 200 # loops in the picture
207 201 sec.exclude('inputhook')
208 202
209 203 # testing:
210 204 sec = test_sections['testing']
211 205 # These have to be skipped on win32 because they use echo, rm, cd, etc.
212 206 # See ticket https://github.com/ipython/ipython/issues/87
213 207 if sys.platform == 'win32':
214 208 sec.exclude('plugin.test_exampleip')
215 209 sec.exclude('plugin.dtexample')
216 210
217 211 # terminal:
218 212 if (not have['pexpect']) or (not have['zmq']):
219 213 test_sections['terminal'].exclude('console')
220 214
221 215 # parallel
222 216 sec = test_sections['parallel']
223 217 sec.requires('zmq')
224 218 if not have['pymongo']:
225 219 sec.exclude('controller.mongodb')
226 220 sec.exclude('tests.test_mongodb')
227 221
228 222 # kernel:
229 223 sec = test_sections['kernel']
230 224 sec.requires('zmq')
231 225 # The in-process kernel tests are done in a separate section
232 226 sec.exclude('inprocess')
233 227 # importing gtk sets the default encoding, which we want to avoid
234 228 sec.exclude('zmq.gui.gtkembed')
235 229 sec.exclude('zmq.gui.gtk3embed')
236 230 if not have['matplotlib']:
237 231 sec.exclude('zmq.pylab')
238 232
239 233 # kernel.inprocess:
240 234 test_sections['kernel.inprocess'].requires('zmq')
241 235
242 236 # extensions:
243 237 sec = test_sections['extensions']
244 238 # This is deprecated in favour of rpy2
245 239 sec.exclude('rmagic')
246 240 # autoreload does some strange stuff, so move it to its own test section
247 241 sec.exclude('autoreload')
248 242 sec.exclude('tests.test_autoreload')
249 243 test_sections['autoreload'] = TestSection('autoreload',
250 244 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
251 245 test_group_names.append('autoreload')
252 246
253 247 # qt:
254 248 test_sections['qt'].requires('zmq', 'qt', 'pygments')
255 249
256 250 # html:
257 251 sec = test_sections['html']
258 252 sec.requires('zmq', 'tornado', 'requests', 'sqlite3', 'jsonschema')
259 253 # The notebook 'static' directory contains JS, css and other
260 254 # files for web serving. Occasionally projects may put a .py
261 255 # file in there (MathJax ships a conf.py), so we might as
262 256 # well play it safe and skip the whole thing.
263 257 sec.exclude('static')
264 258 sec.exclude('tasks')
265 259 if not have['jinja2']:
266 260 sec.exclude('notebookapp')
267 261 if not have['pygments'] or not have['jinja2']:
268 262 sec.exclude('nbconvert')
269 263 if not have['terminado']:
270 264 sec.exclude('terminal')
271 265
272 266 # config:
273 267 # Config files aren't really importable stand-alone
274 268 test_sections['config'].exclude('profile')
275 269
276 270 # nbconvert:
277 271 sec = test_sections['nbconvert']
278 272 sec.requires('pygments', 'jinja2', 'jsonschema', 'mistune')
279 273 # Exclude nbconvert directories containing config files used to test.
280 274 # Executing the config files with iptest would cause an exception.
281 275 sec.exclude('tests.files')
282 276 sec.exclude('exporters.tests.files')
283 277 if not have['tornado']:
284 278 sec.exclude('nbconvert.post_processors.serve')
285 279 sec.exclude('nbconvert.post_processors.tests.test_serve')
286 280
287 281 # nbformat:
288 282 test_sections['nbformat'].requires('jsonschema')
289 283
290 284 #-----------------------------------------------------------------------------
291 285 # Functions and classes
292 286 #-----------------------------------------------------------------------------
293 287
294 288 def check_exclusions_exist():
295 289 from IPython.utils.path import get_ipython_package_dir
296 290 from IPython.utils.warn import warn
297 291 parent = os.path.dirname(get_ipython_package_dir())
298 292 for sec in test_sections:
299 293 for pattern in sec.exclusions:
300 294 fullpath = pjoin(parent, pattern)
301 295 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
302 296 warn("Excluding nonexistent file: %r" % pattern)
303 297
304 298
305 299 class ExclusionPlugin(Plugin):
306 300 """A nose plugin to effect our exclusions of files and directories.
307 301 """
308 302 name = 'exclusions'
309 303 score = 3000 # Should come before any other plugins
310 304
311 305 def __init__(self, exclude_patterns=None):
312 306 """
313 307 Parameters
314 308 ----------
315 309
316 310 exclude_patterns : sequence of strings, optional
317 311 Filenames containing these patterns (as raw strings, not as regular
318 312 expressions) are excluded from the tests.
319 313 """
320 314 self.exclude_patterns = exclude_patterns or []
321 315 super(ExclusionPlugin, self).__init__()
322 316
323 317 def options(self, parser, env=os.environ):
324 318 Plugin.options(self, parser, env)
325 319
326 320 def configure(self, options, config):
327 321 Plugin.configure(self, options, config)
328 322 # Override nose trying to disable plugin.
329 323 self.enabled = True
330 324
331 325 def wantFile(self, filename):
332 326 """Return whether the given filename should be scanned for tests.
333 327 """
334 328 if any(pat in filename for pat in self.exclude_patterns):
335 329 return False
336 330 return None
337 331
338 332 def wantDirectory(self, directory):
339 333 """Return whether the given directory should be scanned for tests.
340 334 """
341 335 if any(pat in directory for pat in self.exclude_patterns):
342 336 return False
343 337 return None
344 338
345 339
346 340 class StreamCapturer(Thread):
347 341 daemon = True # Don't hang if main thread crashes
348 342 started = False
349 343 def __init__(self, echo=False):
350 344 super(StreamCapturer, self).__init__()
351 345 self.echo = echo
352 346 self.streams = []
353 347 self.buffer = BytesIO()
354 348 self.readfd, self.writefd = os.pipe()
355 349 self.buffer_lock = Lock()
356 350 self.stop = Event()
357 351
358 352 def run(self):
359 353 self.started = True
360 354
361 355 while not self.stop.is_set():
362 356 chunk = os.read(self.readfd, 1024)
363 357
364 358 with self.buffer_lock:
365 359 self.buffer.write(chunk)
366 360 if self.echo:
367 361 sys.stdout.write(bytes_to_str(chunk))
368 362
369 363 os.close(self.readfd)
370 364 os.close(self.writefd)
371 365
372 366 def reset_buffer(self):
373 367 with self.buffer_lock:
374 368 self.buffer.truncate(0)
375 369 self.buffer.seek(0)
376 370
377 371 def get_buffer(self):
378 372 with self.buffer_lock:
379 373 return self.buffer.getvalue()
380 374
381 375 def ensure_started(self):
382 376 if not self.started:
383 377 self.start()
384 378
385 379 def halt(self):
386 380 """Safely stop the thread."""
387 381 if not self.started:
388 382 return
389 383
390 384 self.stop.set()
391 385 os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
392 386 self.join()
393 387
394 388 class SubprocessStreamCapturePlugin(Plugin):
395 389 name='subprocstreams'
396 390 def __init__(self):
397 391 Plugin.__init__(self)
398 392 self.stream_capturer = StreamCapturer()
399 393 self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
400 394 # This is ugly, but distant parts of the test machinery need to be able
401 395 # to redirect streams, so we make the object globally accessible.
402 396 nose.iptest_stdstreams_fileno = self.get_write_fileno
403 397
404 398 def get_write_fileno(self):
405 399 if self.destination == 'capture':
406 400 self.stream_capturer.ensure_started()
407 401 return self.stream_capturer.writefd
408 402 elif self.destination == 'discard':
409 403 return os.open(os.devnull, os.O_WRONLY)
410 404 else:
411 405 return sys.__stdout__.fileno()
412 406
413 407 def configure(self, options, config):
414 408 Plugin.configure(self, options, config)
415 409 # Override nose trying to disable plugin.
416 410 if self.destination == 'capture':
417 411 self.enabled = True
418 412
419 413 def startTest(self, test):
420 414 # Reset log capture
421 415 self.stream_capturer.reset_buffer()
422 416
423 417 def formatFailure(self, test, err):
424 418 # Show output
425 419 ec, ev, tb = err
426 420 captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
427 421 if captured.strip():
428 422 ev = safe_str(ev)
429 423 out = [ev, '>> begin captured subprocess output <<',
430 424 captured,
431 425 '>> end captured subprocess output <<']
432 426 return ec, '\n'.join(out), tb
433 427
434 428 return err
435 429
436 430 formatError = formatFailure
437 431
438 432 def finalize(self, result):
439 433 self.stream_capturer.halt()
440 434
441 435
442 436 def run_iptest():
443 437 """Run the IPython test suite using nose.
444 438
445 439 This function is called when this script is **not** called with the form
446 440 `iptest all`. It simply calls nose with appropriate command line flags
447 441 and accepts all of the standard nose arguments.
448 442 """
449 443 # Apply our monkeypatch to Xunit
450 444 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
451 445 monkeypatch_xunit()
452 446
453 447 warnings.filterwarnings('ignore',
454 448 'This will be removed soon. Use IPython.testing.util instead')
455 449
456 450 arg1 = sys.argv[1]
457 451 if arg1 in test_sections:
458 452 section = test_sections[arg1]
459 453 sys.argv[1:2] = section.includes
460 454 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
461 455 section = test_sections[arg1[8:]]
462 456 sys.argv[1:2] = section.includes
463 457 else:
464 458 section = TestSection(arg1, includes=[arg1])
465 459
466 460
467 461 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
468 462
469 463 '--with-ipdoctest',
470 464 '--ipdoctest-tests','--ipdoctest-extension=txt',
471 465
472 466 # We add --exe because of setuptools' imbecility (it
473 467 # blindly does chmod +x on ALL files). Nose does the
474 468 # right thing and it tries to avoid executables,
475 469 # setuptools unfortunately forces our hand here. This
476 470 # has been discussed on the distutils list and the
477 471 # setuptools devs refuse to fix this problem!
478 472 '--exe',
479 473 ]
480 474 if '-a' not in argv and '-A' not in argv:
481 475 argv = argv + ['-a', '!crash']
482 476
483 477 if nose.__version__ >= '0.11':
484 478 # I don't fully understand why we need this one, but depending on what
485 479 # directory the test suite is run from, if we don't give it, 0 tests
486 480 # get run. Specifically, if the test suite is run from the source dir
487 481 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
488 482 # even if the same call done in this directory works fine). It appears
489 483 # that if the requested package is in the current dir, nose bails early
490 484 # by default. Since it's otherwise harmless, leave it in by default
491 485 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
492 486 argv.append('--traverse-namespace')
493 487
494 488 # use our plugin for doctesting. It will remove the standard doctest plugin
495 489 # if it finds it enabled
496 490 plugins = [ExclusionPlugin(section.excludes), IPythonDoctest(), KnownFailure(),
497 491 SubprocessStreamCapturePlugin() ]
498 492
499 493 # Use working directory set by parent process (see iptestcontroller)
500 494 if 'IPTEST_WORKING_DIR' in os.environ:
501 495 os.chdir(os.environ['IPTEST_WORKING_DIR'])
502 496
503 497 # We need a global ipython running in this process, but the special
504 498 # in-process group spawns its own IPython kernels, so for *that* group we
505 499 # must avoid also opening the global one (otherwise there's a conflict of
506 500 # singletons). Ultimately the solution to this problem is to refactor our
507 501 # assumptions about what needs to be a singleton and what doesn't (app
508 502 # objects should, individual shells shouldn't). But for now, this
509 503 # workaround allows the test suite for the inprocess module to complete.
510 504 if 'kernel.inprocess' not in section.name:
511 505 from IPython.testing import globalipapp
512 506 globalipapp.start_ipython()
513 507
514 508 # Now nose can run
515 509 TestProgram(argv=argv, addplugins=plugins)
516 510
517 511 if __name__ == '__main__':
518 512 run_iptest()
519 513
@@ -1,225 +1,225 b''
1 1 """Posix-specific implementation of process utilities.
2 2
3 3 This file is only meant to be imported by process.py, not by end-users.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2010-2011 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 # Stdlib
19 19 import errno
20 20 import os
21 21 import subprocess as sp
22 22 import sys
23 23
24 from IPython.external import pexpect
24 import pexpect
25 25
26 26 # Our own
27 27 from ._process_common import getoutput, arg_split
28 28 from IPython.utils import py3compat
29 29 from IPython.utils.encoding import DEFAULT_ENCODING
30 30
31 31 #-----------------------------------------------------------------------------
32 32 # Function definitions
33 33 #-----------------------------------------------------------------------------
34 34
35 35 def _find_cmd(cmd):
36 36 """Find the full path to a command using which."""
37 37
38 38 path = sp.Popen(['/usr/bin/env', 'which', cmd],
39 39 stdout=sp.PIPE, stderr=sp.PIPE).communicate()[0]
40 40 return py3compat.bytes_to_str(path)
41 41
42 42
43 43 class ProcessHandler(object):
44 44 """Execute subprocesses under the control of pexpect.
45 45 """
46 46 # Timeout in seconds to wait on each reading of the subprocess' output.
47 47 # This should not be set too low to avoid cpu overusage from our side,
48 48 # since we read in a loop whose period is controlled by this timeout.
49 49 read_timeout = 0.05
50 50
51 51 # Timeout to give a process if we receive SIGINT, between sending the
52 52 # SIGINT to the process and forcefully terminating it.
53 53 terminate_timeout = 0.2
54 54
55 55 # File object where stdout and stderr of the subprocess will be written
56 56 logfile = None
57 57
58 58 # Shell to call for subprocesses to execute
59 59 _sh = None
60 60
61 61 @property
62 62 def sh(self):
63 63 if self._sh is None:
64 64 self._sh = pexpect.which('sh')
65 65 if self._sh is None:
66 66 raise OSError('"sh" shell not found')
67 67
68 68 return self._sh
69 69
70 70 def __init__(self, logfile=None, read_timeout=None, terminate_timeout=None):
71 71 """Arguments are used for pexpect calls."""
72 72 self.read_timeout = (ProcessHandler.read_timeout if read_timeout is
73 73 None else read_timeout)
74 74 self.terminate_timeout = (ProcessHandler.terminate_timeout if
75 75 terminate_timeout is None else
76 76 terminate_timeout)
77 77 self.logfile = sys.stdout if logfile is None else logfile
78 78
79 79 def getoutput(self, cmd):
80 80 """Run a command and return its stdout/stderr as a string.
81 81
82 82 Parameters
83 83 ----------
84 84 cmd : str
85 85 A command to be executed in the system shell.
86 86
87 87 Returns
88 88 -------
89 89 output : str
90 90 A string containing the combination of stdout and stderr from the
91 91 subprocess, in whatever order the subprocess originally wrote to its
92 92 file descriptors (so the order of the information in this string is the
93 93 correct order as would be seen if running the command in a terminal).
94 94 """
95 95 try:
96 96 return pexpect.run(self.sh, args=['-c', cmd]).replace('\r\n', '\n')
97 97 except KeyboardInterrupt:
98 98 print('^C', file=sys.stderr, end='')
99 99
100 100 def getoutput_pexpect(self, cmd):
101 101 """Run a command and return its stdout/stderr as a string.
102 102
103 103 Parameters
104 104 ----------
105 105 cmd : str
106 106 A command to be executed in the system shell.
107 107
108 108 Returns
109 109 -------
110 110 output : str
111 111 A string containing the combination of stdout and stderr from the
112 112 subprocess, in whatever order the subprocess originally wrote to its
113 113 file descriptors (so the order of the information in this string is the
114 114 correct order as would be seen if running the command in a terminal).
115 115 """
116 116 try:
117 117 return pexpect.run(self.sh, args=['-c', cmd]).replace('\r\n', '\n')
118 118 except KeyboardInterrupt:
119 119 print('^C', file=sys.stderr, end='')
120 120
121 121 def system(self, cmd):
122 122 """Execute a command in a subshell.
123 123
124 124 Parameters
125 125 ----------
126 126 cmd : str
127 127 A command to be executed in the system shell.
128 128
129 129 Returns
130 130 -------
131 131 int : child's exitstatus
132 132 """
133 133 # Get likely encoding for the output.
134 134 enc = DEFAULT_ENCODING
135 135
136 136 # Patterns to match on the output, for pexpect. We read input and
137 137 # allow either a short timeout or EOF
138 138 patterns = [pexpect.TIMEOUT, pexpect.EOF]
139 139 # the index of the EOF pattern in the list.
140 140 # even though we know it's 1, this call means we don't have to worry if
141 141 # we change the above list, and forget to change this value:
142 142 EOF_index = patterns.index(pexpect.EOF)
143 143 # The size of the output stored so far in the process output buffer.
144 144 # Since pexpect only appends to this buffer, each time we print we
145 145 # record how far we've printed, so that next time we only print *new*
146 146 # content from the buffer.
147 147 out_size = 0
148 148 try:
149 149 # Since we're not really searching the buffer for text patterns, we
150 150 # can set pexpect's search window to be tiny and it won't matter.
151 151 # We only search for the 'patterns' timeout or EOF, which aren't in
152 152 # the text itself.
153 153 #child = pexpect.spawn(pcmd, searchwindowsize=1)
154 154 if hasattr(pexpect, 'spawnb'):
155 155 child = pexpect.spawnb(self.sh, args=['-c', cmd]) # Pexpect-U
156 156 else:
157 157 child = pexpect.spawn(self.sh, args=['-c', cmd]) # Vanilla Pexpect
158 158 flush = sys.stdout.flush
159 159 while True:
160 160 # res is the index of the pattern that caused the match, so we
161 161 # know whether we've finished (if we matched EOF) or not
162 162 res_idx = child.expect_list(patterns, self.read_timeout)
163 163 print(child.before[out_size:].decode(enc, 'replace'), end='')
164 164 flush()
165 165 if res_idx==EOF_index:
166 166 break
167 167 # Update the pointer to what we've already printed
168 168 out_size = len(child.before)
169 169 except KeyboardInterrupt:
170 170 # We need to send ^C to the process. The ascii code for '^C' is 3
171 171 # (the character is known as ETX for 'End of Text', see
172 172 # curses.ascii.ETX).
173 173 child.sendline(chr(3))
174 174 # Read and print any more output the program might produce on its
175 175 # way out.
176 176 try:
177 177 out_size = len(child.before)
178 178 child.expect_list(patterns, self.terminate_timeout)
179 179 print(child.before[out_size:].decode(enc, 'replace'), end='')
180 180 sys.stdout.flush()
181 181 except KeyboardInterrupt:
182 182 # Impatient users tend to type it multiple times
183 183 pass
184 184 finally:
185 185 # Ensure the subprocess really is terminated
186 186 child.terminate(force=True)
187 187 # add isalive check, to ensure exitstatus is set:
188 188 child.isalive()
189 189
190 190 # We follow the subprocess pattern, returning either the exit status
191 191 # as a positive number, or the terminating signal as a negative
192 192 # number.
193 193 # on Linux, sh returns 128+n for signals terminating child processes on Linux
194 194 # on BSD (OS X), the signal code is set instead
195 195 if child.exitstatus is None:
196 196 # on WIFSIGNALED, pexpect sets signalstatus, leaving exitstatus=None
197 197 if child.signalstatus is None:
198 198 # this condition may never occur,
199 199 # but let's be certain we always return an integer.
200 200 return 0
201 201 return -child.signalstatus
202 202 if child.exitstatus > 128:
203 203 return -(child.exitstatus - 128)
204 204 return child.exitstatus
205 205
206 206
207 207 # Make system() with a functional interface for outside use. Note that we use
208 208 # getoutput() from the _common utils, which is built on top of popen(). Using
209 209 # pexpect to get subprocess output produces difficult to parse output, since
210 210 # programs think they are talking to a tty and produce highly formatted output
211 211 # (ls is a good example) that makes them hard.
212 212 system = ProcessHandler().system
213 213
214 214 def check_pid(pid):
215 215 try:
216 216 os.kill(pid, 0)
217 217 except OSError as err:
218 218 if err.errno == errno.ESRCH:
219 219 return False
220 220 elif err.errno == errno.EPERM:
221 221 # Don't have permission to signal the process - probably means it exists
222 222 return True
223 223 raise
224 224 else:
225 225 return True
@@ -1,346 +1,349 b''
1 1 #!/usr/bin/env python
2 2 # -*- coding: utf-8 -*-
3 3 """Setup script for IPython.
4 4
5 5 Under Posix environments it works like a typical setup.py script.
6 6 Under Windows, the command sdist is not supported, since IPython
7 7 requires utilities which are not available under Windows."""
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (c) 2008-2011, IPython Development Team.
11 11 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
12 12 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
13 13 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
14 14 #
15 15 # Distributed under the terms of the Modified BSD License.
16 16 #
17 17 # The full license is in the file COPYING.rst, distributed with this software.
18 18 #-----------------------------------------------------------------------------
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Minimal Python version sanity check
22 22 #-----------------------------------------------------------------------------
23 23 from __future__ import print_function
24 24
25 25 import sys
26 26
27 27 # This check is also made in IPython/__init__, don't forget to update both when
28 28 # changing Python version requirements.
29 29 v = sys.version_info
30 30 if v[:2] < (2,7) or (v[0] >= 3 and v[:2] < (3,3)):
31 31 error = "ERROR: IPython requires Python version 2.7 or 3.3 or above."
32 32 print(error, file=sys.stderr)
33 33 sys.exit(1)
34 34
35 35 PY3 = (sys.version_info[0] >= 3)
36 36
37 37 # At least we're on the python version we need, move on.
38 38
39 39 #-------------------------------------------------------------------------------
40 40 # Imports
41 41 #-------------------------------------------------------------------------------
42 42
43 43 # Stdlib imports
44 44 import os
45 45 import shutil
46 46
47 47 from glob import glob
48 48
49 49 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
50 50 # update it when the contents of directories change.
51 51 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
52 52
53 53 from distutils.core import setup
54 54
55 55 # Our own imports
56 56 from setupbase import target_update
57 57
58 58 from setupbase import (
59 59 setup_args,
60 60 find_packages,
61 61 find_package_data,
62 62 check_package_data_first,
63 63 find_entry_points,
64 64 build_scripts_entrypt,
65 65 find_data_files,
66 66 check_for_dependencies,
67 67 git_prebuild,
68 68 check_submodule_status,
69 69 update_submodules,
70 70 require_submodules,
71 71 UpdateSubmodules,
72 72 get_bdist_wheel,
73 73 CompileCSS,
74 74 JavascriptVersion,
75 75 css_js_prerelease,
76 76 install_symlinked,
77 77 install_lib_symlink,
78 78 install_scripts_for_symlink,
79 79 unsymlink,
80 80 )
81 81 from setupext import setupext
82 82
83 83 isfile = os.path.isfile
84 84 pjoin = os.path.join
85 85
86 86 #-------------------------------------------------------------------------------
87 87 # Handle OS specific things
88 88 #-------------------------------------------------------------------------------
89 89
90 90 if os.name in ('nt','dos'):
91 91 os_name = 'windows'
92 92 else:
93 93 os_name = os.name
94 94
95 95 # Under Windows, 'sdist' has not been supported. Now that the docs build with
96 96 # Sphinx it might work, but let's not turn it on until someone confirms that it
97 97 # actually works.
98 98 if os_name == 'windows' and 'sdist' in sys.argv:
99 99 print('The sdist command is not available under Windows. Exiting.')
100 100 sys.exit(1)
101 101
102 102 #-------------------------------------------------------------------------------
103 103 # Make sure we aren't trying to run without submodules
104 104 #-------------------------------------------------------------------------------
105 105 here = os.path.abspath(os.path.dirname(__file__))
106 106
107 107 def require_clean_submodules():
108 108 """Check on git submodules before distutils can do anything
109 109
110 110 Since distutils cannot be trusted to update the tree
111 111 after everything has been set in motion,
112 112 this is not a distutils command.
113 113 """
114 114 # PACKAGERS: Add a return here to skip checks for git submodules
115 115
116 116 # don't do anything if nothing is actually supposed to happen
117 117 for do_nothing in ('-h', '--help', '--help-commands', 'clean', 'submodule'):
118 118 if do_nothing in sys.argv:
119 119 return
120 120
121 121 status = check_submodule_status(here)
122 122
123 123 if status == "missing":
124 124 print("checking out submodules for the first time")
125 125 update_submodules(here)
126 126 elif status == "unclean":
127 127 print('\n'.join([
128 128 "Cannot build / install IPython with unclean submodules",
129 129 "Please update submodules with",
130 130 " python setup.py submodule",
131 131 "or",
132 132 " git submodule update",
133 133 "or commit any submodule changes you have made."
134 134 ]))
135 135 sys.exit(1)
136 136
137 137 require_clean_submodules()
138 138
139 139 #-------------------------------------------------------------------------------
140 140 # Things related to the IPython documentation
141 141 #-------------------------------------------------------------------------------
142 142
143 143 # update the manuals when building a source dist
144 144 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
145 145
146 146 # List of things to be updated. Each entry is a triplet of args for
147 147 # target_update()
148 148 to_update = [
149 149 # FIXME - Disabled for now: we need to redo an automatic way
150 150 # of generating the magic info inside the rst.
151 151 #('docs/magic.tex',
152 152 #['IPython/Magic.py'],
153 153 #"cd doc && ./update_magic.sh" ),
154 154
155 155 ('docs/man/ipcluster.1.gz',
156 156 ['docs/man/ipcluster.1'],
157 157 'cd docs/man && gzip -9c ipcluster.1 > ipcluster.1.gz'),
158 158
159 159 ('docs/man/ipcontroller.1.gz',
160 160 ['docs/man/ipcontroller.1'],
161 161 'cd docs/man && gzip -9c ipcontroller.1 > ipcontroller.1.gz'),
162 162
163 163 ('docs/man/ipengine.1.gz',
164 164 ['docs/man/ipengine.1'],
165 165 'cd docs/man && gzip -9c ipengine.1 > ipengine.1.gz'),
166 166
167 167 ('docs/man/ipython.1.gz',
168 168 ['docs/man/ipython.1'],
169 169 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
170 170
171 171 ]
172 172
173 173
174 174 [ target_update(*t) for t in to_update ]
175 175
176 176 #---------------------------------------------------------------------------
177 177 # Find all the packages, package data, and data_files
178 178 #---------------------------------------------------------------------------
179 179
180 180 packages = find_packages()
181 181 package_data = find_package_data()
182 182
183 183 data_files = find_data_files()
184 184
185 185 setup_args['packages'] = packages
186 186 setup_args['package_data'] = package_data
187 187 setup_args['data_files'] = data_files
188 188
189 189 #---------------------------------------------------------------------------
190 190 # custom distutils commands
191 191 #---------------------------------------------------------------------------
192 192 # imports here, so they are after setuptools import if there was one
193 193 from distutils.command.sdist import sdist
194 194 from distutils.command.upload import upload
195 195
196 196 class UploadWindowsInstallers(upload):
197 197
198 198 description = "Upload Windows installers to PyPI (only used from tools/release_windows.py)"
199 199 user_options = upload.user_options + [
200 200 ('files=', 'f', 'exe file (or glob) to upload')
201 201 ]
202 202 def initialize_options(self):
203 203 upload.initialize_options(self)
204 204 meta = self.distribution.metadata
205 205 base = '{name}-{version}'.format(
206 206 name=meta.get_name(),
207 207 version=meta.get_version()
208 208 )
209 209 self.files = os.path.join('dist', '%s.*.exe' % base)
210 210
211 211 def run(self):
212 212 for dist_file in glob(self.files):
213 213 self.upload_file('bdist_wininst', 'any', dist_file)
214 214
215 215 setup_args['cmdclass'] = {
216 216 'build_py': css_js_prerelease(
217 217 check_package_data_first(git_prebuild('IPython'))),
218 218 'sdist' : css_js_prerelease(git_prebuild('IPython', sdist)),
219 219 'upload_wininst' : UploadWindowsInstallers,
220 220 'submodule' : UpdateSubmodules,
221 221 'css' : CompileCSS,
222 222 'symlink': install_symlinked,
223 223 'install_lib_symlink': install_lib_symlink,
224 224 'install_scripts_sym': install_scripts_for_symlink,
225 225 'unsymlink': unsymlink,
226 226 'jsversion' : JavascriptVersion,
227 227 }
228 228
229 229 #---------------------------------------------------------------------------
230 230 # Handle scripts, dependencies, and setuptools specific things
231 231 #---------------------------------------------------------------------------
232 232
233 233 # For some commands, use setuptools. Note that we do NOT list install here!
234 234 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
235 235 needs_setuptools = set(('develop', 'release', 'bdist_egg', 'bdist_rpm',
236 236 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
237 237 'egg_info', 'easy_install', 'upload', 'install_egg_info',
238 238 ))
239 239
240 240 if len(needs_setuptools.intersection(sys.argv)) > 0:
241 241 import setuptools
242 242
243 243 # This dict is used for passing extra arguments that are setuptools
244 244 # specific to setup
245 245 setuptools_extra_args = {}
246 246
247 247 # setuptools requirements
248 248
249 249 pyzmq = 'pyzmq>=13'
250 250
251 251 extras_require = dict(
252 252 parallel = [pyzmq],
253 253 qtconsole = [pyzmq, 'pygments'],
254 254 doc = ['Sphinx>=1.1', 'numpydoc'],
255 255 test = ['nose>=0.10.1', 'requests'],
256 256 terminal = [],
257 257 nbformat = ['jsonschema>=2.0'],
258 258 notebook = ['tornado>=4.0', pyzmq, 'jinja2', 'pygments', 'mistune>=0.5'],
259 259 nbconvert = ['pygments', 'jinja2', 'mistune>=0.3.1']
260 260 )
261 261
262 262 if not sys.platform.startswith('win'):
263 263 extras_require['notebook'].append('terminado>=0.3.3')
264 264
265 265 if sys.version_info < (3, 3):
266 266 extras_require['test'].append('mock')
267 267
268 268 extras_require['notebook'].extend(extras_require['nbformat'])
269 269 extras_require['nbconvert'].extend(extras_require['nbformat'])
270 270
271 271 install_requires = [
272 272 'decorator',
273 273 'path.py', # required by pickleshare, remove when pickleshare is added here
274 274 ]
275 275
276 # add readline
276 # add platform-specific dependencies
277 277 if sys.platform == 'darwin':
278 278 install_requires.append('appnope')
279 279 if 'bdist_wheel' in sys.argv[1:] or not setupext.check_for_readline():
280 280 install_requires.append('gnureadline')
281 elif sys.platform.startswith('win'):
281
282 if sys.platform.startswith('win'):
282 283 extras_require['terminal'].append('pyreadline>=2.0')
284 else:
285 install_requires.append('pexpect')
283 286
284 287 everything = set()
285 288 for deps in extras_require.values():
286 289 everything.update(deps)
287 290 extras_require['all'] = everything
288 291
289 292 if 'setuptools' in sys.modules:
290 293 # setup.py develop should check for submodules
291 294 from setuptools.command.develop import develop
292 295 setup_args['cmdclass']['develop'] = require_submodules(develop)
293 296 setup_args['cmdclass']['bdist_wheel'] = css_js_prerelease(get_bdist_wheel())
294 297
295 298 setuptools_extra_args['zip_safe'] = False
296 299 setuptools_extra_args['entry_points'] = {
297 300 'console_scripts': find_entry_points(),
298 301 'pygments.lexers': [
299 302 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
300 303 'ipython = IPython.lib.lexers:IPythonLexer',
301 304 'ipython3 = IPython.lib.lexers:IPython3Lexer',
302 305 ],
303 306 }
304 307 setup_args['extras_require'] = extras_require
305 308 requires = setup_args['install_requires'] = install_requires
306 309
307 310 # Script to be run by the windows binary installer after the default setup
308 311 # routine, to add shortcuts and similar windows-only things. Windows
309 312 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
310 313 # doesn't find them.
311 314 if 'bdist_wininst' in sys.argv:
312 315 if len(sys.argv) > 2 and \
313 316 ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
314 317 print("ERROR: bdist_wininst must be run alone. Exiting.", file=sys.stderr)
315 318 sys.exit(1)
316 319 setup_args['data_files'].append(
317 320 ['Scripts', ('scripts/ipython.ico', 'scripts/ipython_nb.ico')])
318 321 setup_args['scripts'] = [pjoin('scripts','ipython_win_post_install.py')]
319 322 setup_args['options'] = {"bdist_wininst":
320 323 {"install_script":
321 324 "ipython_win_post_install.py"}}
322 325
323 326 else:
324 327 # If we are installing without setuptools, call this function which will
325 328 # check for dependencies an inform the user what is needed. This is
326 329 # just to make life easy for users.
327 330 for install_cmd in ('install', 'symlink'):
328 331 if install_cmd in sys.argv:
329 332 check_for_dependencies()
330 333 break
331 334 # scripts has to be a non-empty list, or install_scripts isn't called
332 335 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
333 336
334 337 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
335 338
336 339 #---------------------------------------------------------------------------
337 340 # Do the actual setup now
338 341 #---------------------------------------------------------------------------
339 342
340 343 setup_args.update(setuptools_extra_args)
341 344
342 345 def main():
343 346 setup(**setup_args)
344 347
345 348 if __name__ == '__main__':
346 349 main()
@@ -1,769 +1,770 b''
1 1 # encoding: utf-8
2 2 """
3 3 This module defines the things that are used in setup.py for building IPython
4 4
5 5 This includes:
6 6
7 7 * The basic arguments to setup
8 8 * Functions for finding things like packages, package data, etc.
9 9 * A function for checking dependencies.
10 10 """
11 11
12 12 # Copyright (c) IPython Development Team.
13 13 # Distributed under the terms of the Modified BSD License.
14 14
15 15 from __future__ import print_function
16 16
17 17 import errno
18 18 import os
19 19 import sys
20 20
21 21 from distutils import log
22 22 from distutils.command.build_py import build_py
23 23 from distutils.command.build_scripts import build_scripts
24 24 from distutils.command.install import install
25 25 from distutils.command.install_scripts import install_scripts
26 26 from distutils.cmd import Command
27 27 from distutils.errors import DistutilsExecError
28 28 from fnmatch import fnmatch
29 29 from glob import glob
30 30 from subprocess import Popen, PIPE
31 31
32 32 from setupext import install_data_ext
33 33
34 34 #-------------------------------------------------------------------------------
35 35 # Useful globals and utility functions
36 36 #-------------------------------------------------------------------------------
37 37
38 38 # A few handy globals
39 39 isfile = os.path.isfile
40 40 pjoin = os.path.join
41 41 repo_root = os.path.dirname(os.path.abspath(__file__))
42 42
43 43 def oscmd(s):
44 44 print(">", s)
45 45 os.system(s)
46 46
47 47 # Py3 compatibility hacks, without assuming IPython itself is installed with
48 48 # the full py3compat machinery.
49 49
50 50 try:
51 51 execfile
52 52 except NameError:
53 53 def execfile(fname, globs, locs=None):
54 54 locs = locs or globs
55 55 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
56 56
57 57 # A little utility we'll need below, since glob() does NOT allow you to do
58 58 # exclusion on multiple endings!
59 59 def file_doesnt_endwith(test,endings):
60 60 """Return true if test is a file and its name does NOT end with any
61 61 of the strings listed in endings."""
62 62 if not isfile(test):
63 63 return False
64 64 for e in endings:
65 65 if test.endswith(e):
66 66 return False
67 67 return True
68 68
69 69 #---------------------------------------------------------------------------
70 70 # Basic project information
71 71 #---------------------------------------------------------------------------
72 72
73 73 # release.py contains version, authors, license, url, keywords, etc.
74 74 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
75 75
76 76 # Create a dict with the basic information
77 77 # This dict is eventually passed to setup after additional keys are added.
78 78 setup_args = dict(
79 79 name = name,
80 80 version = version,
81 81 description = description,
82 82 long_description = long_description,
83 83 author = author,
84 84 author_email = author_email,
85 85 url = url,
86 86 download_url = download_url,
87 87 license = license,
88 88 platforms = platforms,
89 89 keywords = keywords,
90 90 classifiers = classifiers,
91 91 cmdclass = {'install_data': install_data_ext},
92 92 )
93 93
94 94
95 95 #---------------------------------------------------------------------------
96 96 # Find packages
97 97 #---------------------------------------------------------------------------
98 98
99 99 def find_packages():
100 100 """
101 101 Find all of IPython's packages.
102 102 """
103 103 excludes = ['deathrow', 'quarantine']
104 104 packages = []
105 105 for dir,subdirs,files in os.walk('IPython'):
106 106 package = dir.replace(os.path.sep, '.')
107 107 if any(package.startswith('IPython.'+exc) for exc in excludes):
108 108 # package is to be excluded (e.g. deathrow)
109 109 continue
110 110 if '__init__.py' not in files:
111 111 # not a package
112 112 continue
113 113 packages.append(package)
114 114 return packages
115 115
116 116 #---------------------------------------------------------------------------
117 117 # Find package data
118 118 #---------------------------------------------------------------------------
119 119
120 120 def find_package_data():
121 121 """
122 122 Find IPython's package_data.
123 123 """
124 124 # This is not enough for these things to appear in an sdist.
125 125 # We need to muck with the MANIFEST to get this to work
126 126
127 127 # exclude components and less from the walk;
128 128 # we will build the components separately
129 129 excludes = [
130 130 pjoin('static', 'components'),
131 131 pjoin('static', '*', 'less'),
132 132 ]
133 133
134 134 # walk notebook resources:
135 135 cwd = os.getcwd()
136 136 os.chdir(os.path.join('IPython', 'html'))
137 137 static_data = []
138 138 for parent, dirs, files in os.walk('static'):
139 139 if any(fnmatch(parent, pat) for pat in excludes):
140 140 # prevent descending into subdirs
141 141 dirs[:] = []
142 142 continue
143 143 for f in files:
144 144 static_data.append(pjoin(parent, f))
145 145
146 146 components = pjoin("static", "components")
147 147 # select the components we actually need to install
148 148 # (there are lots of resources we bundle for sdist-reasons that we don't actually use)
149 149 static_data.extend([
150 150 pjoin(components, "backbone", "backbone-min.js"),
151 151 pjoin(components, "bootstrap", "js", "bootstrap.min.js"),
152 152 pjoin(components, "bootstrap-tour", "build", "css", "bootstrap-tour.min.css"),
153 153 pjoin(components, "bootstrap-tour", "build", "js", "bootstrap-tour.min.js"),
154 154 pjoin(components, "es6-promise", "*.js"),
155 155 pjoin(components, "font-awesome", "fonts", "*.*"),
156 156 pjoin(components, "google-caja", "html-css-sanitizer-minified.js"),
157 157 pjoin(components, "jquery", "jquery.min.js"),
158 158 pjoin(components, "jquery-ui", "ui", "minified", "jquery-ui.min.js"),
159 159 pjoin(components, "jquery-ui", "themes", "smoothness", "jquery-ui.min.css"),
160 160 pjoin(components, "jquery-ui", "themes", "smoothness", "images", "*"),
161 161 pjoin(components, "marked", "lib", "marked.js"),
162 162 pjoin(components, "requirejs", "require.js"),
163 163 pjoin(components, "underscore", "underscore-min.js"),
164 164 pjoin(components, "moment", "moment.js"),
165 165 pjoin(components, "moment", "min", "moment.min.js"),
166 166 pjoin(components, "term.js", "src", "term.js"),
167 167 pjoin(components, "text-encoding", "lib", "encoding.js"),
168 168 ])
169 169
170 170 # Ship all of Codemirror's CSS and JS
171 171 for parent, dirs, files in os.walk(pjoin(components, 'codemirror')):
172 172 for f in files:
173 173 if f.endswith(('.js', '.css')):
174 174 static_data.append(pjoin(parent, f))
175 175
176 176 os.chdir(os.path.join('tests',))
177 177 js_tests = glob('*.js') + glob('*/*.js')
178 178
179 179 os.chdir(os.path.join(cwd, 'IPython', 'nbconvert'))
180 180 nbconvert_templates = [os.path.join(dirpath, '*.*')
181 181 for dirpath, _, _ in os.walk('templates')]
182 182
183 183 os.chdir(cwd)
184 184
185 185 package_data = {
186 186 'IPython.config.profile' : ['README*', '*/*.py'],
187 187 'IPython.core.tests' : ['*.png', '*.jpg'],
188 188 'IPython.lib.tests' : ['*.wav'],
189 189 'IPython.testing.plugin' : ['*.txt'],
190 190 'IPython.html' : ['templates/*'] + static_data,
191 191 'IPython.html.tests' : js_tests,
192 192 'IPython.qt.console' : ['resources/icon/*.svg'],
193 193 'IPython.nbconvert' : nbconvert_templates +
194 194 [
195 195 'tests/files/*.*',
196 196 'exporters/tests/files/*.*',
197 197 'preprocessors/tests/files/*.*',
198 198 ],
199 199 'IPython.nbconvert.filters' : ['marked.js'],
200 200 'IPython.nbformat' : [
201 201 'tests/*.ipynb',
202 202 'v3/nbformat.v3.schema.json',
203 203 'v4/nbformat.v4.schema.json',
204 204 ],
205 205 'IPython.kernel': ['resources/*.*'],
206 206 }
207 207
208 208 return package_data
209 209
210 210
211 211 def check_package_data(package_data):
212 212 """verify that package_data globs make sense"""
213 213 print("checking package data")
214 214 for pkg, data in package_data.items():
215 215 pkg_root = pjoin(*pkg.split('.'))
216 216 for d in data:
217 217 path = pjoin(pkg_root, d)
218 218 if '*' in path:
219 219 assert len(glob(path)) > 0, "No files match pattern %s" % path
220 220 else:
221 221 assert os.path.exists(path), "Missing package data: %s" % path
222 222
223 223
224 224 def check_package_data_first(command):
225 225 """decorator for checking package_data before running a given command
226 226
227 227 Probably only needs to wrap build_py
228 228 """
229 229 class DecoratedCommand(command):
230 230 def run(self):
231 231 check_package_data(self.package_data)
232 232 command.run(self)
233 233 return DecoratedCommand
234 234
235 235
236 236 #---------------------------------------------------------------------------
237 237 # Find data files
238 238 #---------------------------------------------------------------------------
239 239
240 240 def make_dir_struct(tag,base,out_base):
241 241 """Make the directory structure of all files below a starting dir.
242 242
243 243 This is just a convenience routine to help build a nested directory
244 244 hierarchy because distutils is too stupid to do this by itself.
245 245
246 246 XXX - this needs a proper docstring!
247 247 """
248 248
249 249 # we'll use these a lot below
250 250 lbase = len(base)
251 251 pathsep = os.path.sep
252 252 lpathsep = len(pathsep)
253 253
254 254 out = []
255 255 for (dirpath,dirnames,filenames) in os.walk(base):
256 256 # we need to strip out the dirpath from the base to map it to the
257 257 # output (installation) path. This requires possibly stripping the
258 258 # path separator, because otherwise pjoin will not work correctly
259 259 # (pjoin('foo/','/bar') returns '/bar').
260 260
261 261 dp_eff = dirpath[lbase:]
262 262 if dp_eff.startswith(pathsep):
263 263 dp_eff = dp_eff[lpathsep:]
264 264 # The output path must be anchored at the out_base marker
265 265 out_path = pjoin(out_base,dp_eff)
266 266 # Now we can generate the final filenames. Since os.walk only produces
267 267 # filenames, we must join back with the dirpath to get full valid file
268 268 # paths:
269 269 pfiles = [pjoin(dirpath,f) for f in filenames]
270 270 # Finally, generate the entry we need, which is a pari of (output
271 271 # path, files) for use as a data_files parameter in install_data.
272 272 out.append((out_path, pfiles))
273 273
274 274 return out
275 275
276 276
277 277 def find_data_files():
278 278 """
279 279 Find IPython's data_files.
280 280
281 281 Just man pages at this point.
282 282 """
283 283
284 284 manpagebase = pjoin('share', 'man', 'man1')
285 285
286 286 # Simple file lists can be made by hand
287 287 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
288 288 if not manpages:
289 289 # When running from a source tree, the manpages aren't gzipped
290 290 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
291 291
292 292 # And assemble the entire output list
293 293 data_files = [ (manpagebase, manpages) ]
294 294
295 295 return data_files
296 296
297 297
298 298 def make_man_update_target(manpage):
299 299 """Return a target_update-compliant tuple for the given manpage.
300 300
301 301 Parameters
302 302 ----------
303 303 manpage : string
304 304 Name of the manpage, must include the section number (trailing number).
305 305
306 306 Example
307 307 -------
308 308
309 309 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
310 310 ('docs/man/ipython.1.gz',
311 311 ['docs/man/ipython.1'],
312 312 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
313 313 """
314 314 man_dir = pjoin('docs', 'man')
315 315 manpage_gz = manpage + '.gz'
316 316 manpath = pjoin(man_dir, manpage)
317 317 manpath_gz = pjoin(man_dir, manpage_gz)
318 318 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
319 319 locals() )
320 320 return (manpath_gz, [manpath], gz_cmd)
321 321
322 322 # The two functions below are copied from IPython.utils.path, so we don't need
323 323 # to import IPython during setup, which fails on Python 3.
324 324
325 325 def target_outdated(target,deps):
326 326 """Determine whether a target is out of date.
327 327
328 328 target_outdated(target,deps) -> 1/0
329 329
330 330 deps: list of filenames which MUST exist.
331 331 target: single filename which may or may not exist.
332 332
333 333 If target doesn't exist or is older than any file listed in deps, return
334 334 true, otherwise return false.
335 335 """
336 336 try:
337 337 target_time = os.path.getmtime(target)
338 338 except os.error:
339 339 return 1
340 340 for dep in deps:
341 341 dep_time = os.path.getmtime(dep)
342 342 if dep_time > target_time:
343 343 #print "For target",target,"Dep failed:",dep # dbg
344 344 #print "times (dep,tar):",dep_time,target_time # dbg
345 345 return 1
346 346 return 0
347 347
348 348
349 349 def target_update(target,deps,cmd):
350 350 """Update a target with a given command given a list of dependencies.
351 351
352 352 target_update(target,deps,cmd) -> runs cmd if target is outdated.
353 353
354 354 This is just a wrapper around target_outdated() which calls the given
355 355 command if target is outdated."""
356 356
357 357 if target_outdated(target,deps):
358 358 os.system(cmd)
359 359
360 360 #---------------------------------------------------------------------------
361 361 # Find scripts
362 362 #---------------------------------------------------------------------------
363 363
364 364 def find_entry_points():
365 365 """Defines the command line entry points for IPython
366 366
367 367 This always uses setuptools-style entry points. When setuptools is not in
368 368 use, our own build_scripts_entrypt class below parses these and builds
369 369 command line scripts.
370 370
371 371 Each of our entry points gets both a plain name, e.g. ipython, and one
372 372 suffixed with the Python major version number, e.g. ipython3.
373 373 """
374 374 ep = [
375 375 'ipython%s = IPython:start_ipython',
376 376 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
377 377 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
378 378 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
379 379 'iptest%s = IPython.testing.iptestcontroller:main',
380 380 ]
381 381 suffix = str(sys.version_info[0])
382 382 return [e % '' for e in ep] + [e % suffix for e in ep]
383 383
384 384 script_src = """#!{executable}
385 385 # This script was automatically generated by setup.py
386 386 if __name__ == '__main__':
387 387 from {mod} import {func}
388 388 {func}()
389 389 """
390 390
391 391 class build_scripts_entrypt(build_scripts):
392 392 """Build the command line scripts
393 393
394 394 Parse setuptools style entry points and write simple scripts to run the
395 395 target functions.
396 396
397 397 On Windows, this also creates .cmd wrappers for the scripts so that you can
398 398 easily launch them from a command line.
399 399 """
400 400 def run(self):
401 401 self.mkpath(self.build_dir)
402 402 outfiles = []
403 403 for script in find_entry_points():
404 404 name, entrypt = script.split('=')
405 405 name = name.strip()
406 406 entrypt = entrypt.strip()
407 407 outfile = os.path.join(self.build_dir, name)
408 408 outfiles.append(outfile)
409 409 print('Writing script to', outfile)
410 410
411 411 mod, func = entrypt.split(':')
412 412 with open(outfile, 'w') as f:
413 413 f.write(script_src.format(executable=sys.executable,
414 414 mod=mod, func=func))
415 415
416 416 if sys.platform == 'win32':
417 417 # Write .cmd wrappers for Windows so 'ipython' etc. work at the
418 418 # command line
419 419 cmd_file = os.path.join(self.build_dir, name + '.cmd')
420 420 cmd = '@"{python}" "%~dp0\{script}" %*\r\n'.format(
421 421 python=sys.executable, script=name)
422 422 log.info("Writing %s wrapper script" % cmd_file)
423 423 with open(cmd_file, 'w') as f:
424 424 f.write(cmd)
425 425
426 426 return outfiles, outfiles
427 427
428 428 class install_lib_symlink(Command):
429 429 user_options = [
430 430 ('install-dir=', 'd', "directory to install to"),
431 431 ]
432 432
433 433 def initialize_options(self):
434 434 self.install_dir = None
435 435
436 436 def finalize_options(self):
437 437 self.set_undefined_options('symlink',
438 438 ('install_lib', 'install_dir'),
439 439 )
440 440
441 441 def run(self):
442 442 if sys.platform == 'win32':
443 443 raise Exception("This doesn't work on Windows.")
444 444 pkg = os.path.join(os.getcwd(), 'IPython')
445 445 dest = os.path.join(self.install_dir, 'IPython')
446 446 if os.path.islink(dest):
447 447 print('removing existing symlink at %s' % dest)
448 448 os.unlink(dest)
449 449 print('symlinking %s -> %s' % (pkg, dest))
450 450 os.symlink(pkg, dest)
451 451
452 452 class unsymlink(install):
453 453 def run(self):
454 454 dest = os.path.join(self.install_lib, 'IPython')
455 455 if os.path.islink(dest):
456 456 print('removing symlink at %s' % dest)
457 457 os.unlink(dest)
458 458 else:
459 459 print('No symlink exists at %s' % dest)
460 460
461 461 class install_symlinked(install):
462 462 def run(self):
463 463 if sys.platform == 'win32':
464 464 raise Exception("This doesn't work on Windows.")
465 465
466 466 # Run all sub-commands (at least those that need to be run)
467 467 for cmd_name in self.get_sub_commands():
468 468 self.run_command(cmd_name)
469 469
470 470 # 'sub_commands': a list of commands this command might have to run to
471 471 # get its work done. See cmd.py for more info.
472 472 sub_commands = [('install_lib_symlink', lambda self:True),
473 473 ('install_scripts_sym', lambda self:True),
474 474 ]
475 475
476 476 class install_scripts_for_symlink(install_scripts):
477 477 """Redefined to get options from 'symlink' instead of 'install'.
478 478
479 479 I love distutils almost as much as I love setuptools.
480 480 """
481 481 def finalize_options(self):
482 482 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
483 483 self.set_undefined_options('symlink',
484 484 ('install_scripts', 'install_dir'),
485 485 ('force', 'force'),
486 486 ('skip_build', 'skip_build'),
487 487 )
488 488
489 489 #---------------------------------------------------------------------------
490 490 # Verify all dependencies
491 491 #---------------------------------------------------------------------------
492 492
493 493 def check_for_dependencies():
494 494 """Check for IPython's dependencies.
495 495
496 496 This function should NOT be called if running under setuptools!
497 497 """
498 498 from setupext.setupext import (
499 499 print_line, print_raw, print_status,
500 500 check_for_sphinx, check_for_pygments,
501 501 check_for_nose, check_for_pexpect,
502 502 check_for_pyzmq, check_for_readline,
503 503 check_for_jinja2, check_for_tornado
504 504 )
505 505 print_line()
506 506 print_raw("BUILDING IPYTHON")
507 507 print_status('python', sys.version)
508 508 print_status('platform', sys.platform)
509 509 if sys.platform == 'win32':
510 510 print_status('Windows version', sys.getwindowsversion())
511 511
512 512 print_raw("")
513 513 print_raw("OPTIONAL DEPENDENCIES")
514 514
515 515 check_for_sphinx()
516 516 check_for_pygments()
517 517 check_for_nose()
518 518 if os.name == 'posix':
519 519 check_for_pexpect()
520 520 check_for_pyzmq()
521 521 check_for_tornado()
522 522 check_for_readline()
523 523 check_for_jinja2()
524 524
525 525 #---------------------------------------------------------------------------
526 526 # VCS related
527 527 #---------------------------------------------------------------------------
528 528
529 529 # utils.submodule has checks for submodule status
530 530 execfile(pjoin('IPython','utils','submodule.py'), globals())
531 531
532 532 class UpdateSubmodules(Command):
533 533 """Update git submodules
534 534
535 535 IPython's external javascript dependencies live in a separate repo.
536 536 """
537 537 description = "Update git submodules"
538 538 user_options = []
539 539
540 540 def initialize_options(self):
541 541 pass
542 542
543 543 def finalize_options(self):
544 544 pass
545 545
546 546 def run(self):
547 547 failure = False
548 548 try:
549 549 self.spawn('git submodule init'.split())
550 550 self.spawn('git submodule update --recursive'.split())
551 551 except Exception as e:
552 552 failure = e
553 553 print(e)
554 554
555 555 if not check_submodule_status(repo_root) == 'clean':
556 556 print("submodules could not be checked out")
557 557 sys.exit(1)
558 558
559 559
560 560 def git_prebuild(pkg_dir, build_cmd=build_py):
561 561 """Return extended build or sdist command class for recording commit
562 562
563 563 records git commit in IPython.utils._sysinfo.commit
564 564
565 565 for use in IPython.utils.sysinfo.sys_info() calls after installation.
566 566
567 567 Also ensures that submodules exist prior to running
568 568 """
569 569
570 570 class MyBuildPy(build_cmd):
571 571 ''' Subclass to write commit data into installation tree '''
572 572 def run(self):
573 573 build_cmd.run(self)
574 574 # this one will only fire for build commands
575 575 if hasattr(self, 'build_lib'):
576 576 self._record_commit(self.build_lib)
577 577
578 578 def make_release_tree(self, base_dir, files):
579 579 # this one will fire for sdist
580 580 build_cmd.make_release_tree(self, base_dir, files)
581 581 self._record_commit(base_dir)
582 582
583 583 def _record_commit(self, base_dir):
584 584 import subprocess
585 585 proc = subprocess.Popen('git rev-parse --short HEAD',
586 586 stdout=subprocess.PIPE,
587 587 stderr=subprocess.PIPE,
588 588 shell=True)
589 589 repo_commit, _ = proc.communicate()
590 590 repo_commit = repo_commit.strip().decode("ascii")
591 591
592 592 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
593 593 if os.path.isfile(out_pth) and not repo_commit:
594 594 # nothing to write, don't clobber
595 595 return
596 596
597 597 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
598 598
599 599 # remove to avoid overwriting original via hard link
600 600 try:
601 601 os.remove(out_pth)
602 602 except (IOError, OSError):
603 603 pass
604 604 with open(out_pth, 'w') as out_file:
605 605 out_file.writelines([
606 606 '# GENERATED BY setup.py\n',
607 607 'commit = u"%s"\n' % repo_commit,
608 608 ])
609 609 return require_submodules(MyBuildPy)
610 610
611 611
612 612 def require_submodules(command):
613 613 """decorator for instructing a command to check for submodules before running"""
614 614 class DecoratedCommand(command):
615 615 def run(self):
616 616 if not check_submodule_status(repo_root) == 'clean':
617 617 print("submodules missing! Run `setup.py submodule` and try again")
618 618 sys.exit(1)
619 619 command.run(self)
620 620 return DecoratedCommand
621 621
622 622 #---------------------------------------------------------------------------
623 623 # bdist related
624 624 #---------------------------------------------------------------------------
625 625
626 626 def get_bdist_wheel():
627 627 """Construct bdist_wheel command for building wheels
628 628
629 629 Constructs py2-none-any tag, instead of py2.7-none-any
630 630 """
631 631 class RequiresWheel(Command):
632 632 description = "Dummy command for missing bdist_wheel"
633 633 user_options = []
634 634
635 635 def initialize_options(self):
636 636 pass
637 637
638 638 def finalize_options(self):
639 639 pass
640 640
641 641 def run(self):
642 642 print("bdist_wheel requires the wheel package")
643 643 sys.exit(1)
644 644
645 645 if 'setuptools' not in sys.modules:
646 646 return RequiresWheel
647 647 else:
648 648 try:
649 649 from wheel.bdist_wheel import bdist_wheel, read_pkg_info, write_pkg_info
650 650 except ImportError:
651 651 return RequiresWheel
652 652
653 653 class bdist_wheel_tag(bdist_wheel):
654 654
655 655 def add_requirements(self, metadata_path):
656 656 """transform platform-dependent requirements"""
657 657 pkg_info = read_pkg_info(metadata_path)
658 658 # pkg_info is an email.Message object (?!)
659 659 # we have to remove the unconditional 'readline' and/or 'pyreadline' entries
660 660 # and transform them to conditionals
661 661 requires = pkg_info.get_all('Requires-Dist')
662 662 del pkg_info['Requires-Dist']
663 663 def _remove_startswith(lis, prefix):
664 664 """like list.remove, but with startswith instead of =="""
665 665 found = False
666 666 for idx, item in enumerate(lis):
667 667 if item.startswith(prefix):
668 668 found = True
669 669 break
670 670 if found:
671 671 lis.pop(idx)
672 672
673 for pkg in ("gnureadline", "pyreadline", "mock", "appnope", "terminado"):
673 for pkg in ("gnureadline", "pyreadline", "mock", "terminado", "appnope", "pexpect"):
674 674 _remove_startswith(requires, pkg)
675 675 requires.append("gnureadline; sys.platform == 'darwin' and platform.python_implementation == 'CPython'")
676 676 requires.append("terminado (>=0.3.3); extra == 'notebook' and sys.platform != 'win32'")
677 677 requires.append("terminado (>=0.3.3); extra == 'all' and sys.platform != 'win32'")
678 678 requires.append("pyreadline (>=2.0); extra == 'terminal' and sys.platform == 'win32' and platform.python_implementation == 'CPython'")
679 679 requires.append("pyreadline (>=2.0); extra == 'all' and sys.platform == 'win32' and platform.python_implementation == 'CPython'")
680 680 requires.append("mock; extra == 'test' and python_version < '3.3'")
681 681 requires.append("appnope; sys.platform == 'darwin'")
682 requires.append("pexpect; sys.platform != 'win32'")
682 683 for r in requires:
683 684 pkg_info['Requires-Dist'] = r
684 685 write_pkg_info(metadata_path, pkg_info)
685 686
686 687 return bdist_wheel_tag
687 688
688 689 #---------------------------------------------------------------------------
689 690 # Notebook related
690 691 #---------------------------------------------------------------------------
691 692
692 693 class CompileCSS(Command):
693 694 """Recompile Notebook CSS
694 695
695 696 Regenerate the compiled CSS from LESS sources.
696 697
697 698 Requires various dev dependencies, such as invoke and lessc.
698 699 """
699 700 description = "Recompile Notebook CSS"
700 701 user_options = [
701 702 ('minify', 'x', "minify CSS"),
702 703 ('force', 'f', "force recompilation of CSS"),
703 704 ]
704 705
705 706 def initialize_options(self):
706 707 self.minify = False
707 708 self.force = False
708 709
709 710 def finalize_options(self):
710 711 self.minify = bool(self.minify)
711 712 self.force = bool(self.force)
712 713
713 714 def run(self):
714 715 cmd = ['invoke', 'css']
715 716 if self.minify:
716 717 cmd.append('--minify')
717 718 if self.force:
718 719 cmd.append('--force')
719 720 try:
720 721 p = Popen(cmd, cwd=pjoin(repo_root, "IPython", "html"), stderr=PIPE)
721 722 except OSError:
722 723 raise DistutilsExecError("invoke is required to rebuild css (pip install invoke)")
723 724 out, err = p.communicate()
724 725 if p.returncode:
725 726 if sys.version_info[0] >= 3:
726 727 err = err.decode('utf8', 'replace')
727 728 raise DistutilsExecError(err.strip())
728 729
729 730
730 731 class JavascriptVersion(Command):
731 732 """write the javascript version to notebook javascript"""
732 733 description = "Write IPython version to javascript"
733 734 user_options = []
734 735
735 736 def initialize_options(self):
736 737 pass
737 738
738 739 def finalize_options(self):
739 740 pass
740 741
741 742 def run(self):
742 743 nsfile = pjoin(repo_root, "IPython", "html", "static", "base", "js", "namespace.js")
743 744 with open(nsfile) as f:
744 745 lines = f.readlines()
745 746 with open(nsfile, 'w') as f:
746 747 found = False
747 748 for line in lines:
748 749 if line.strip().startswith("IPython.version"):
749 750 line = ' IPython.version = "{0}";\n'.format(version)
750 751 found = True
751 752 f.write(line)
752 753 if not found:
753 754 raise RuntimeError("Didn't find IPython.version line in %s" % nsfile)
754 755
755 756
756 757 def css_js_prerelease(command):
757 758 """decorator for building js/minified css prior to a release"""
758 759 class DecoratedCommand(command):
759 760 def run(self):
760 761 self.distribution.run_command('jsversion')
761 762 css = self.distribution.get_command_obj('css')
762 763 css.minify = True
763 764 try:
764 765 self.distribution.run_command('css')
765 766 except Exception as e:
766 767 log.warn("rebuilding css and sourcemaps failed (not a problem)")
767 768 log.warn(str(e))
768 769 command.run(self)
769 770 return DecoratedCommand
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (2123 lines changed) Show them Hide them
General Comments 0
You need to be logged in to leave comments. Login now