##// END OF EJS Templates
Merge pull request #6571 from Carreau/jsonable...
Thomas Kluyver -
r18042:0e7a4bfb merge
parent child Browse files
Show More
@@ -0,0 +1,17
1 # coding: utf-8
2 """Test suite for our sysinfo utilities."""
3
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
6
7 import json
8 import nose.tools as nt
9
10 from IPython.utils import sysinfo
11
12
13 def test_json_getsysinfo():
14 """
15 test that it is easily jsonable and don't return bytes somewhere.
16 """
17 json.dumps(sysinfo.get_sys_info())
@@ -1,656 +1,653
1 1 """Implementation of basic magic functions.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2012 The IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14 from __future__ import print_function
15 15
16 16 # Stdlib
17 17 import io
18 18 import json
19 19 import sys
20 20 from pprint import pformat
21 21
22 22 # Our own packages
23 23 from IPython.core import magic_arguments, page
24 24 from IPython.core.error import UsageError
25 25 from IPython.core.magic import Magics, magics_class, line_magic, magic_escapes
26 26 from IPython.utils.text import format_screen, dedent, indent
27 27 from IPython.testing.skipdoctest import skip_doctest
28 28 from IPython.utils.ipstruct import Struct
29 29 from IPython.utils.path import unquote_filename
30 30 from IPython.utils.py3compat import unicode_type
31 31 from IPython.utils.warn import warn, error
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Magics class implementation
35 35 #-----------------------------------------------------------------------------
36 36
37 37 class MagicsDisplay(object):
38 38 def __init__(self, magics_manager):
39 39 self.magics_manager = magics_manager
40 40
41 41 def _lsmagic(self):
42 42 """The main implementation of the %lsmagic"""
43 43 mesc = magic_escapes['line']
44 44 cesc = magic_escapes['cell']
45 45 mman = self.magics_manager
46 46 magics = mman.lsmagic()
47 47 out = ['Available line magics:',
48 48 mesc + (' '+mesc).join(sorted(magics['line'])),
49 49 '',
50 50 'Available cell magics:',
51 51 cesc + (' '+cesc).join(sorted(magics['cell'])),
52 52 '',
53 53 mman.auto_status()]
54 54 return '\n'.join(out)
55 55
56 56 def _repr_pretty_(self, p, cycle):
57 57 p.text(self._lsmagic())
58 58
59 59 def __str__(self):
60 60 return self._lsmagic()
61 61
62 62 def _jsonable(self):
63 63 """turn magics dict into jsonable dict of the same structure
64 64
65 65 replaces object instances with their class names as strings
66 66 """
67 67 magic_dict = {}
68 68 mman = self.magics_manager
69 69 magics = mman.lsmagic()
70 70 for key, subdict in magics.items():
71 71 d = {}
72 72 magic_dict[key] = d
73 73 for name, obj in subdict.items():
74 74 try:
75 75 classname = obj.__self__.__class__.__name__
76 76 except AttributeError:
77 77 classname = 'Other'
78 78
79 79 d[name] = classname
80 80 return magic_dict
81 81
82 82 def _repr_json_(self):
83 83 return json.dumps(self._jsonable())
84 84
85 85
86 86 @magics_class
87 87 class BasicMagics(Magics):
88 88 """Magics that provide central IPython functionality.
89 89
90 90 These are various magics that don't fit into specific categories but that
91 91 are all part of the base 'IPython experience'."""
92 92
93 93 @magic_arguments.magic_arguments()
94 94 @magic_arguments.argument(
95 95 '-l', '--line', action='store_true',
96 96 help="""Create a line magic alias."""
97 97 )
98 98 @magic_arguments.argument(
99 99 '-c', '--cell', action='store_true',
100 100 help="""Create a cell magic alias."""
101 101 )
102 102 @magic_arguments.argument(
103 103 'name',
104 104 help="""Name of the magic to be created."""
105 105 )
106 106 @magic_arguments.argument(
107 107 'target',
108 108 help="""Name of the existing line or cell magic."""
109 109 )
110 110 @line_magic
111 111 def alias_magic(self, line=''):
112 112 """Create an alias for an existing line or cell magic.
113 113
114 114 Examples
115 115 --------
116 116 ::
117 117
118 118 In [1]: %alias_magic t timeit
119 119 Created `%t` as an alias for `%timeit`.
120 120 Created `%%t` as an alias for `%%timeit`.
121 121
122 122 In [2]: %t -n1 pass
123 123 1 loops, best of 3: 954 ns per loop
124 124
125 125 In [3]: %%t -n1
126 126 ...: pass
127 127 ...:
128 128 1 loops, best of 3: 954 ns per loop
129 129
130 130 In [4]: %alias_magic --cell whereami pwd
131 131 UsageError: Cell magic function `%%pwd` not found.
132 132 In [5]: %alias_magic --line whereami pwd
133 133 Created `%whereami` as an alias for `%pwd`.
134 134
135 135 In [6]: %whereami
136 136 Out[6]: u'/home/testuser'
137 137 """
138 138 args = magic_arguments.parse_argstring(self.alias_magic, line)
139 139 shell = self.shell
140 140 mman = self.shell.magics_manager
141 141 escs = ''.join(magic_escapes.values())
142 142
143 143 target = args.target.lstrip(escs)
144 144 name = args.name.lstrip(escs)
145 145
146 146 # Find the requested magics.
147 147 m_line = shell.find_magic(target, 'line')
148 148 m_cell = shell.find_magic(target, 'cell')
149 149 if args.line and m_line is None:
150 150 raise UsageError('Line magic function `%s%s` not found.' %
151 151 (magic_escapes['line'], target))
152 152 if args.cell and m_cell is None:
153 153 raise UsageError('Cell magic function `%s%s` not found.' %
154 154 (magic_escapes['cell'], target))
155 155
156 156 # If --line and --cell are not specified, default to the ones
157 157 # that are available.
158 158 if not args.line and not args.cell:
159 159 if not m_line and not m_cell:
160 160 raise UsageError(
161 161 'No line or cell magic with name `%s` found.' % target
162 162 )
163 163 args.line = bool(m_line)
164 164 args.cell = bool(m_cell)
165 165
166 166 if args.line:
167 167 mman.register_alias(name, target, 'line')
168 168 print('Created `%s%s` as an alias for `%s%s`.' % (
169 169 magic_escapes['line'], name,
170 170 magic_escapes['line'], target))
171 171
172 172 if args.cell:
173 173 mman.register_alias(name, target, 'cell')
174 174 print('Created `%s%s` as an alias for `%s%s`.' % (
175 175 magic_escapes['cell'], name,
176 176 magic_escapes['cell'], target))
177 177
178 178 @line_magic
179 179 def lsmagic(self, parameter_s=''):
180 180 """List currently available magic functions."""
181 181 return MagicsDisplay(self.shell.magics_manager)
182 182
183 183 def _magic_docs(self, brief=False, rest=False):
184 184 """Return docstrings from magic functions."""
185 185 mman = self.shell.magics_manager
186 186 docs = mman.lsmagic_docs(brief, missing='No documentation')
187 187
188 188 if rest:
189 189 format_string = '**%s%s**::\n\n%s\n\n'
190 190 else:
191 191 format_string = '%s%s:\n%s\n'
192 192
193 193 return ''.join(
194 194 [format_string % (magic_escapes['line'], fname,
195 195 indent(dedent(fndoc)))
196 196 for fname, fndoc in sorted(docs['line'].items())]
197 197 +
198 198 [format_string % (magic_escapes['cell'], fname,
199 199 indent(dedent(fndoc)))
200 200 for fname, fndoc in sorted(docs['cell'].items())]
201 201 )
202 202
203 203 @line_magic
204 204 def magic(self, parameter_s=''):
205 205 """Print information about the magic function system.
206 206
207 207 Supported formats: -latex, -brief, -rest
208 208 """
209 209
210 210 mode = ''
211 211 try:
212 212 mode = parameter_s.split()[0][1:]
213 213 if mode == 'rest':
214 214 rest_docs = []
215 215 except IndexError:
216 216 pass
217 217
218 218 brief = (mode == 'brief')
219 219 rest = (mode == 'rest')
220 220 magic_docs = self._magic_docs(brief, rest)
221 221
222 222 if mode == 'latex':
223 223 print(self.format_latex(magic_docs))
224 224 return
225 225 else:
226 226 magic_docs = format_screen(magic_docs)
227 227
228 228 out = ["""
229 229 IPython's 'magic' functions
230 230 ===========================
231 231
232 232 The magic function system provides a series of functions which allow you to
233 233 control the behavior of IPython itself, plus a lot of system-type
234 234 features. There are two kinds of magics, line-oriented and cell-oriented.
235 235
236 236 Line magics are prefixed with the % character and work much like OS
237 237 command-line calls: they get as an argument the rest of the line, where
238 238 arguments are passed without parentheses or quotes. For example, this will
239 239 time the given statement::
240 240
241 241 %timeit range(1000)
242 242
243 243 Cell magics are prefixed with a double %%, and they are functions that get as
244 244 an argument not only the rest of the line, but also the lines below it in a
245 245 separate argument. These magics are called with two arguments: the rest of the
246 246 call line and the body of the cell, consisting of the lines below the first.
247 247 For example::
248 248
249 249 %%timeit x = numpy.random.randn((100, 100))
250 250 numpy.linalg.svd(x)
251 251
252 252 will time the execution of the numpy svd routine, running the assignment of x
253 253 as part of the setup phase, which is not timed.
254 254
255 255 In a line-oriented client (the terminal or Qt console IPython), starting a new
256 256 input with %% will automatically enter cell mode, and IPython will continue
257 257 reading input until a blank line is given. In the notebook, simply type the
258 258 whole cell as one entity, but keep in mind that the %% escape can only be at
259 259 the very start of the cell.
260 260
261 261 NOTE: If you have 'automagic' enabled (via the command line option or with the
262 262 %automagic function), you don't need to type in the % explicitly for line
263 263 magics; cell magics always require an explicit '%%' escape. By default,
264 264 IPython ships with automagic on, so you should only rarely need the % escape.
265 265
266 266 Example: typing '%cd mydir' (without the quotes) changes you working directory
267 267 to 'mydir', if it exists.
268 268
269 269 For a list of the available magic functions, use %lsmagic. For a description
270 270 of any of them, type %magic_name?, e.g. '%cd?'.
271 271
272 272 Currently the magic system has the following functions:""",
273 273 magic_docs,
274 274 "Summary of magic functions (from %slsmagic):" % magic_escapes['line'],
275 275 str(self.lsmagic()),
276 276 ]
277 277 page.page('\n'.join(out))
278 278
279 279
280 280 @line_magic
281 281 def page(self, parameter_s=''):
282 282 """Pretty print the object and display it through a pager.
283 283
284 284 %page [options] OBJECT
285 285
286 286 If no object is given, use _ (last output).
287 287
288 288 Options:
289 289
290 290 -r: page str(object), don't pretty-print it."""
291 291
292 292 # After a function contributed by Olivier Aubert, slightly modified.
293 293
294 294 # Process options/args
295 295 opts, args = self.parse_options(parameter_s, 'r')
296 296 raw = 'r' in opts
297 297
298 298 oname = args and args or '_'
299 299 info = self.shell._ofind(oname)
300 300 if info['found']:
301 301 txt = (raw and str or pformat)( info['obj'] )
302 302 page.page(txt)
303 303 else:
304 304 print('Object `%s` not found' % oname)
305 305
306 306 @line_magic
307 307 def profile(self, parameter_s=''):
308 308 """Print your currently active IPython profile.
309 309
310 310 See Also
311 311 --------
312 312 prun : run code using the Python profiler
313 313 (:meth:`~IPython.core.magics.execution.ExecutionMagics.prun`)
314 314 """
315 315 warn("%profile is now deprecated. Please use get_ipython().profile instead.")
316 316 from IPython.core.application import BaseIPythonApplication
317 317 if BaseIPythonApplication.initialized():
318 318 print(BaseIPythonApplication.instance().profile)
319 319 else:
320 320 error("profile is an application-level value, but you don't appear to be in an IPython application")
321 321
322 322 @line_magic
323 323 def pprint(self, parameter_s=''):
324 324 """Toggle pretty printing on/off."""
325 325 ptformatter = self.shell.display_formatter.formatters['text/plain']
326 326 ptformatter.pprint = bool(1 - ptformatter.pprint)
327 327 print('Pretty printing has been turned',
328 328 ['OFF','ON'][ptformatter.pprint])
329 329
330 330 @line_magic
331 331 def colors(self, parameter_s=''):
332 332 """Switch color scheme for prompts, info system and exception handlers.
333 333
334 334 Currently implemented schemes: NoColor, Linux, LightBG.
335 335
336 336 Color scheme names are not case-sensitive.
337 337
338 338 Examples
339 339 --------
340 340 To get a plain black and white terminal::
341 341
342 342 %colors nocolor
343 343 """
344 344 def color_switch_err(name):
345 345 warn('Error changing %s color schemes.\n%s' %
346 346 (name, sys.exc_info()[1]))
347 347
348 348
349 349 new_scheme = parameter_s.strip()
350 350 if not new_scheme:
351 351 raise UsageError(
352 352 "%colors: you must specify a color scheme. See '%colors?'")
353 353 # local shortcut
354 354 shell = self.shell
355 355
356 356 import IPython.utils.rlineimpl as readline
357 357
358 358 if not shell.colors_force and \
359 359 not readline.have_readline and \
360 360 (sys.platform == "win32" or sys.platform == "cli"):
361 361 msg = """\
362 362 Proper color support under MS Windows requires the pyreadline library.
363 363 You can find it at:
364 364 http://ipython.org/pyreadline.html
365 Gary's readline needs the ctypes module, from:
366 http://starship.python.net/crew/theller/ctypes
367 (Note that ctypes is already part of Python versions 2.5 and newer).
368 365
369 366 Defaulting color scheme to 'NoColor'"""
370 367 new_scheme = 'NoColor'
371 368 warn(msg)
372 369
373 370 # readline option is 0
374 371 if not shell.colors_force and not shell.has_readline:
375 372 new_scheme = 'NoColor'
376 373
377 374 # Set prompt colors
378 375 try:
379 376 shell.prompt_manager.color_scheme = new_scheme
380 377 except:
381 378 color_switch_err('prompt')
382 379 else:
383 380 shell.colors = \
384 381 shell.prompt_manager.color_scheme_table.active_scheme_name
385 382 # Set exception colors
386 383 try:
387 384 shell.InteractiveTB.set_colors(scheme = new_scheme)
388 385 shell.SyntaxTB.set_colors(scheme = new_scheme)
389 386 except:
390 387 color_switch_err('exception')
391 388
392 389 # Set info (for 'object?') colors
393 390 if shell.color_info:
394 391 try:
395 392 shell.inspector.set_active_scheme(new_scheme)
396 393 except:
397 394 color_switch_err('object inspector')
398 395 else:
399 396 shell.inspector.set_active_scheme('NoColor')
400 397
401 398 @line_magic
402 399 def xmode(self, parameter_s=''):
403 400 """Switch modes for the exception handlers.
404 401
405 402 Valid modes: Plain, Context and Verbose.
406 403
407 404 If called without arguments, acts as a toggle."""
408 405
409 406 def xmode_switch_err(name):
410 407 warn('Error changing %s exception modes.\n%s' %
411 408 (name,sys.exc_info()[1]))
412 409
413 410 shell = self.shell
414 411 new_mode = parameter_s.strip().capitalize()
415 412 try:
416 413 shell.InteractiveTB.set_mode(mode=new_mode)
417 414 print('Exception reporting mode:',shell.InteractiveTB.mode)
418 415 except:
419 416 xmode_switch_err('user')
420 417
421 418 @line_magic
422 419 def quickref(self,arg):
423 420 """ Show a quick reference sheet """
424 421 from IPython.core.usage import quick_reference
425 422 qr = quick_reference + self._magic_docs(brief=True)
426 423 page.page(qr)
427 424
428 425 @line_magic
429 426 def doctest_mode(self, parameter_s=''):
430 427 """Toggle doctest mode on and off.
431 428
432 429 This mode is intended to make IPython behave as much as possible like a
433 430 plain Python shell, from the perspective of how its prompts, exceptions
434 431 and output look. This makes it easy to copy and paste parts of a
435 432 session into doctests. It does so by:
436 433
437 434 - Changing the prompts to the classic ``>>>`` ones.
438 435 - Changing the exception reporting mode to 'Plain'.
439 436 - Disabling pretty-printing of output.
440 437
441 438 Note that IPython also supports the pasting of code snippets that have
442 439 leading '>>>' and '...' prompts in them. This means that you can paste
443 440 doctests from files or docstrings (even if they have leading
444 441 whitespace), and the code will execute correctly. You can then use
445 442 '%history -t' to see the translated history; this will give you the
446 443 input after removal of all the leading prompts and whitespace, which
447 444 can be pasted back into an editor.
448 445
449 446 With these features, you can switch into this mode easily whenever you
450 447 need to do testing and changes to doctests, without having to leave
451 448 your existing IPython session.
452 449 """
453 450
454 451 # Shorthands
455 452 shell = self.shell
456 453 pm = shell.prompt_manager
457 454 meta = shell.meta
458 455 disp_formatter = self.shell.display_formatter
459 456 ptformatter = disp_formatter.formatters['text/plain']
460 457 # dstore is a data store kept in the instance metadata bag to track any
461 458 # changes we make, so we can undo them later.
462 459 dstore = meta.setdefault('doctest_mode',Struct())
463 460 save_dstore = dstore.setdefault
464 461
465 462 # save a few values we'll need to recover later
466 463 mode = save_dstore('mode',False)
467 464 save_dstore('rc_pprint',ptformatter.pprint)
468 465 save_dstore('xmode',shell.InteractiveTB.mode)
469 466 save_dstore('rc_separate_out',shell.separate_out)
470 467 save_dstore('rc_separate_out2',shell.separate_out2)
471 468 save_dstore('rc_prompts_pad_left',pm.justify)
472 469 save_dstore('rc_separate_in',shell.separate_in)
473 470 save_dstore('rc_active_types',disp_formatter.active_types)
474 471 save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template))
475 472
476 473 if mode == False:
477 474 # turn on
478 475 pm.in_template = '>>> '
479 476 pm.in2_template = '... '
480 477 pm.out_template = ''
481 478
482 479 # Prompt separators like plain python
483 480 shell.separate_in = ''
484 481 shell.separate_out = ''
485 482 shell.separate_out2 = ''
486 483
487 484 pm.justify = False
488 485
489 486 ptformatter.pprint = False
490 487 disp_formatter.active_types = ['text/plain']
491 488
492 489 shell.magic('xmode Plain')
493 490 else:
494 491 # turn off
495 492 pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates
496 493
497 494 shell.separate_in = dstore.rc_separate_in
498 495
499 496 shell.separate_out = dstore.rc_separate_out
500 497 shell.separate_out2 = dstore.rc_separate_out2
501 498
502 499 pm.justify = dstore.rc_prompts_pad_left
503 500
504 501 ptformatter.pprint = dstore.rc_pprint
505 502 disp_formatter.active_types = dstore.rc_active_types
506 503
507 504 shell.magic('xmode ' + dstore.xmode)
508 505
509 506 # Store new mode and inform
510 507 dstore.mode = bool(1-int(mode))
511 508 mode_label = ['OFF','ON'][dstore.mode]
512 509 print('Doctest mode is:', mode_label)
513 510
514 511 @line_magic
515 512 def gui(self, parameter_s=''):
516 513 """Enable or disable IPython GUI event loop integration.
517 514
518 515 %gui [GUINAME]
519 516
520 517 This magic replaces IPython's threaded shells that were activated
521 518 using the (pylab/wthread/etc.) command line flags. GUI toolkits
522 519 can now be enabled at runtime and keyboard
523 520 interrupts should work without any problems. The following toolkits
524 521 are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX)::
525 522
526 523 %gui wx # enable wxPython event loop integration
527 524 %gui qt4|qt # enable PyQt4 event loop integration
528 525 %gui qt5 # enable PyQt5 event loop integration
529 526 %gui gtk # enable PyGTK event loop integration
530 527 %gui gtk3 # enable Gtk3 event loop integration
531 528 %gui tk # enable Tk event loop integration
532 529 %gui osx # enable Cocoa event loop integration
533 530 # (requires %matplotlib 1.1)
534 531 %gui # disable all event loop integration
535 532
536 533 WARNING: after any of these has been called you can simply create
537 534 an application object, but DO NOT start the event loop yourself, as
538 535 we have already handled that.
539 536 """
540 537 opts, arg = self.parse_options(parameter_s, '')
541 538 if arg=='': arg = None
542 539 try:
543 540 return self.shell.enable_gui(arg)
544 541 except Exception as e:
545 542 # print simple error message, rather than traceback if we can't
546 543 # hook up the GUI
547 544 error(str(e))
548 545
549 546 @skip_doctest
550 547 @line_magic
551 548 def precision(self, s=''):
552 549 """Set floating point precision for pretty printing.
553 550
554 551 Can set either integer precision or a format string.
555 552
556 553 If numpy has been imported and precision is an int,
557 554 numpy display precision will also be set, via ``numpy.set_printoptions``.
558 555
559 556 If no argument is given, defaults will be restored.
560 557
561 558 Examples
562 559 --------
563 560 ::
564 561
565 562 In [1]: from math import pi
566 563
567 564 In [2]: %precision 3
568 565 Out[2]: u'%.3f'
569 566
570 567 In [3]: pi
571 568 Out[3]: 3.142
572 569
573 570 In [4]: %precision %i
574 571 Out[4]: u'%i'
575 572
576 573 In [5]: pi
577 574 Out[5]: 3
578 575
579 576 In [6]: %precision %e
580 577 Out[6]: u'%e'
581 578
582 579 In [7]: pi**10
583 580 Out[7]: 9.364805e+04
584 581
585 582 In [8]: %precision
586 583 Out[8]: u'%r'
587 584
588 585 In [9]: pi**10
589 586 Out[9]: 93648.047476082982
590 587 """
591 588 ptformatter = self.shell.display_formatter.formatters['text/plain']
592 589 ptformatter.float_precision = s
593 590 return ptformatter.float_format
594 591
595 592 @magic_arguments.magic_arguments()
596 593 @magic_arguments.argument(
597 594 '-e', '--export', action='store_true', default=False,
598 595 help='Export IPython history as a notebook. The filename argument '
599 596 'is used to specify the notebook name and format. For example '
600 597 'a filename of notebook.ipynb will result in a notebook name '
601 598 'of "notebook" and a format of "json". Likewise using a ".py" '
602 599 'file extension will write the notebook as a Python script'
603 600 )
604 601 @magic_arguments.argument(
605 602 '-f', '--format',
606 603 help='Convert an existing IPython notebook to a new format. This option '
607 604 'specifies the new format and can have the values: json, py. '
608 605 'The target filename is chosen automatically based on the new '
609 606 'format. The filename argument gives the name of the source file.'
610 607 )
611 608 @magic_arguments.argument(
612 609 'filename', type=unicode_type,
613 610 help='Notebook name or filename'
614 611 )
615 612 @line_magic
616 613 def notebook(self, s):
617 614 """Export and convert IPython notebooks.
618 615
619 616 This function can export the current IPython history to a notebook file
620 617 or can convert an existing notebook file into a different format. For
621 618 example, to export the history to "foo.ipynb" do "%notebook -e foo.ipynb".
622 619 To export the history to "foo.py" do "%notebook -e foo.py". To convert
623 620 "foo.ipynb" to "foo.json" do "%notebook -f json foo.ipynb". Possible
624 621 formats include (json/ipynb, py).
625 622 """
626 623 args = magic_arguments.parse_argstring(self.notebook, s)
627 624
628 625 from IPython.nbformat import current
629 626 args.filename = unquote_filename(args.filename)
630 627 if args.export:
631 628 fname, name, format = current.parse_filename(args.filename)
632 629 cells = []
633 630 hist = list(self.shell.history_manager.get_range())
634 631 for session, prompt_number, input in hist[:-1]:
635 632 cells.append(current.new_code_cell(prompt_number=prompt_number,
636 633 input=input))
637 634 worksheet = current.new_worksheet(cells=cells)
638 635 nb = current.new_notebook(name=name,worksheets=[worksheet])
639 636 with io.open(fname, 'w', encoding='utf-8') as f:
640 637 current.write(nb, f, format);
641 638 elif args.format is not None:
642 639 old_fname, old_name, old_format = current.parse_filename(args.filename)
643 640 new_format = args.format
644 641 if new_format == u'xml':
645 642 raise ValueError('Notebooks cannot be written as xml.')
646 643 elif new_format == u'ipynb' or new_format == u'json':
647 644 new_fname = old_name + u'.ipynb'
648 645 new_format = u'json'
649 646 elif new_format == u'py':
650 647 new_fname = old_name + u'.py'
651 648 else:
652 649 raise ValueError('Invalid notebook format: %s' % new_format)
653 650 with io.open(old_fname, 'r', encoding='utf-8') as f:
654 651 nb = current.read(f, old_format)
655 652 with io.open(new_fname, 'w', encoding='utf-8') as f:
656 653 current.write(nb, f, new_format)
@@ -1,2 +1,2
1 1 # GENERATED BY setup.py
2 commit = ""
2 commit = u""
@@ -1,170 +1,167
1 1 # encoding: utf-8
2 2 """
3 3 Utilities for getting information about IPython and the system it's running in.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-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
17 17 import os
18 18 import platform
19 19 import pprint
20 20 import sys
21 21 import subprocess
22 22
23 23 from IPython.core import release
24 24 from IPython.utils import py3compat, _sysinfo, encoding
25 25
26 26 #-----------------------------------------------------------------------------
27 27 # Code
28 28 #-----------------------------------------------------------------------------
29 29
30 30 def pkg_commit_hash(pkg_path):
31 31 """Get short form of commit hash given directory `pkg_path`
32 32
33 33 We get the commit hash from (in order of preference):
34 34
35 35 * IPython.utils._sysinfo.commit
36 36 * git output, if we are in a git repository
37 37
38 38 If these fail, we return a not-found placeholder tuple
39 39
40 40 Parameters
41 41 ----------
42 42 pkg_path : str
43 43 directory containing package
44 44 only used for getting commit from active repo
45 45
46 46 Returns
47 47 -------
48 48 hash_from : str
49 49 Where we got the hash from - description
50 50 hash_str : str
51 51 short form of hash
52 52 """
53 53 # Try and get commit from written commit text file
54 54 if _sysinfo.commit:
55 55 return "installation", _sysinfo.commit
56 56
57 57 # maybe we are in a repository
58 58 proc = subprocess.Popen('git rev-parse --short HEAD',
59 59 stdout=subprocess.PIPE,
60 60 stderr=subprocess.PIPE,
61 61 cwd=pkg_path, shell=True)
62 62 repo_commit, _ = proc.communicate()
63 63 if repo_commit:
64 return 'repository', repo_commit.strip()
65 return '(none found)', '<not found>'
64 return 'repository', repo_commit.strip().decode('ascii')
65 return '(none found)', u'<not found>'
66 66
67 67
68 68 def pkg_info(pkg_path):
69 69 """Return dict describing the context of this package
70 70
71 71 Parameters
72 72 ----------
73 73 pkg_path : str
74 74 path containing __init__.py for package
75 75
76 76 Returns
77 77 -------
78 78 context : dict
79 79 with named parameters of interest
80 80 """
81 81 src, hsh = pkg_commit_hash(pkg_path)
82 82 return dict(
83 83 ipython_version=release.version,
84 84 ipython_path=pkg_path,
85 85 commit_source=src,
86 86 commit_hash=hsh,
87 87 sys_version=sys.version,
88 88 sys_executable=sys.executable,
89 89 sys_platform=sys.platform,
90 90 platform=platform.platform(),
91 91 os_name=os.name,
92 92 default_encoding=encoding.DEFAULT_ENCODING,
93 93 )
94 94
95 95 def get_sys_info():
96 96 """Return useful information about IPython and the system, as a dict."""
97 97 p = os.path
98 98 path = p.realpath(p.dirname(p.abspath(p.join(__file__, '..'))))
99 99 return pkg_info(path)
100 100
101 101 @py3compat.doctest_refactor_print
102 102 def sys_info():
103 103 """Return useful information about IPython and the system, as a string.
104 104
105 105 Examples
106 106 --------
107 107 ::
108 108
109 109 In [2]: print sys_info()
110 110 {'commit_hash': '144fdae', # random
111 111 'commit_source': 'repository',
112 112 'ipython_path': '/home/fperez/usr/lib/python2.6/site-packages/IPython',
113 113 'ipython_version': '0.11.dev',
114 114 'os_name': 'posix',
115 115 'platform': 'Linux-2.6.35-22-generic-i686-with-Ubuntu-10.10-maverick',
116 116 'sys_executable': '/usr/bin/python',
117 117 'sys_platform': 'linux2',
118 118 'sys_version': '2.6.6 (r266:84292, Sep 15 2010, 15:52:39) \\n[GCC 4.4.5]'}
119 119 """
120 120 return pprint.pformat(get_sys_info())
121 121
122 122 def _num_cpus_unix():
123 123 """Return the number of active CPUs on a Unix system."""
124 124 return os.sysconf("SC_NPROCESSORS_ONLN")
125 125
126 126
127 127 def _num_cpus_darwin():
128 128 """Return the number of active CPUs on a Darwin system."""
129 129 p = subprocess.Popen(['sysctl','-n','hw.ncpu'],stdout=subprocess.PIPE)
130 130 return p.stdout.read()
131 131
132 132
133 133 def _num_cpus_windows():
134 134 """Return the number of active CPUs on a Windows system."""
135 135 return os.environ.get("NUMBER_OF_PROCESSORS")
136 136
137 137
138 138 def num_cpus():
139 139 """Return the effective number of CPUs in the system as an integer.
140 140
141 141 This cross-platform function makes an attempt at finding the total number of
142 142 available CPUs in the system, as returned by various underlying system and
143 143 python calls.
144 144
145 145 If it can't find a sensible answer, it returns 1 (though an error *may* make
146 146 it return a large positive number that's actually incorrect).
147 147 """
148 148
149 149 # Many thanks to the Parallel Python project (http://www.parallelpython.com)
150 150 # for the names of the keys we needed to look up for this function. This
151 151 # code was inspired by their equivalent function.
152 152
153 153 ncpufuncs = {'Linux':_num_cpus_unix,
154 154 'Darwin':_num_cpus_darwin,
155 'Windows':_num_cpus_windows,
156 # On Vista, python < 2.5.2 has a bug and returns 'Microsoft'
157 # See http://bugs.python.org/issue1082 for details.
158 'Microsoft':_num_cpus_windows,
155 'Windows':_num_cpus_windows
159 156 }
160 157
161 158 ncpufunc = ncpufuncs.get(platform.system(),
162 159 # default to unix version (Solaris, AIX, etc)
163 160 _num_cpus_unix)
164 161
165 162 try:
166 163 ncpus = max(1,int(ncpufunc()))
167 164 except:
168 165 ncpus = 1
169 166 return ncpus
170 167
@@ -1,730 +1,730
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 fnmatch import fnmatch
28 28 from glob import glob
29 29 from subprocess import check_call
30 30
31 31 from setupext import install_data_ext
32 32
33 33 #-------------------------------------------------------------------------------
34 34 # Useful globals and utility functions
35 35 #-------------------------------------------------------------------------------
36 36
37 37 # A few handy globals
38 38 isfile = os.path.isfile
39 39 pjoin = os.path.join
40 40 repo_root = os.path.dirname(os.path.abspath(__file__))
41 41
42 42 def oscmd(s):
43 43 print(">", s)
44 44 os.system(s)
45 45
46 46 # Py3 compatibility hacks, without assuming IPython itself is installed with
47 47 # the full py3compat machinery.
48 48
49 49 try:
50 50 execfile
51 51 except NameError:
52 52 def execfile(fname, globs, locs=None):
53 53 locs = locs or globs
54 54 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
55 55
56 56 # A little utility we'll need below, since glob() does NOT allow you to do
57 57 # exclusion on multiple endings!
58 58 def file_doesnt_endwith(test,endings):
59 59 """Return true if test is a file and its name does NOT end with any
60 60 of the strings listed in endings."""
61 61 if not isfile(test):
62 62 return False
63 63 for e in endings:
64 64 if test.endswith(e):
65 65 return False
66 66 return True
67 67
68 68 #---------------------------------------------------------------------------
69 69 # Basic project information
70 70 #---------------------------------------------------------------------------
71 71
72 72 # release.py contains version, authors, license, url, keywords, etc.
73 73 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
74 74
75 75 # Create a dict with the basic information
76 76 # This dict is eventually passed to setup after additional keys are added.
77 77 setup_args = dict(
78 78 name = name,
79 79 version = version,
80 80 description = description,
81 81 long_description = long_description,
82 82 author = author,
83 83 author_email = author_email,
84 84 url = url,
85 85 download_url = download_url,
86 86 license = license,
87 87 platforms = platforms,
88 88 keywords = keywords,
89 89 classifiers = classifiers,
90 90 cmdclass = {'install_data': install_data_ext},
91 91 )
92 92
93 93
94 94 #---------------------------------------------------------------------------
95 95 # Find packages
96 96 #---------------------------------------------------------------------------
97 97
98 98 def find_packages():
99 99 """
100 100 Find all of IPython's packages.
101 101 """
102 102 excludes = ['deathrow', 'quarantine']
103 103 packages = []
104 104 for dir,subdirs,files in os.walk('IPython'):
105 105 package = dir.replace(os.path.sep, '.')
106 106 if any(package.startswith('IPython.'+exc) for exc in excludes):
107 107 # package is to be excluded (e.g. deathrow)
108 108 continue
109 109 if '__init__.py' not in files:
110 110 # not a package
111 111 continue
112 112 packages.append(package)
113 113 return packages
114 114
115 115 #---------------------------------------------------------------------------
116 116 # Find package data
117 117 #---------------------------------------------------------------------------
118 118
119 119 def find_package_data():
120 120 """
121 121 Find IPython's package_data.
122 122 """
123 123 # This is not enough for these things to appear in an sdist.
124 124 # We need to muck with the MANIFEST to get this to work
125 125
126 126 # exclude components and less from the walk;
127 127 # we will build the components separately
128 128 excludes = [
129 129 pjoin('static', 'components'),
130 130 pjoin('static', '*', 'less'),
131 131 ]
132 132
133 133 # walk notebook resources:
134 134 cwd = os.getcwd()
135 135 os.chdir(os.path.join('IPython', 'html'))
136 136 static_data = []
137 137 for parent, dirs, files in os.walk('static'):
138 138 if any(fnmatch(parent, pat) for pat in excludes):
139 139 # prevent descending into subdirs
140 140 dirs[:] = []
141 141 continue
142 142 for f in files:
143 143 static_data.append(pjoin(parent, f))
144 144
145 145 components = pjoin("static", "components")
146 146 # select the components we actually need to install
147 147 # (there are lots of resources we bundle for sdist-reasons that we don't actually use)
148 148 static_data.extend([
149 149 pjoin(components, "backbone", "backbone-min.js"),
150 150 pjoin(components, "bootstrap", "js", "bootstrap.min.js"),
151 151 pjoin(components, "bootstrap-tour", "build", "css", "bootstrap-tour.min.css"),
152 152 pjoin(components, "bootstrap-tour", "build", "js", "bootstrap-tour.min.js"),
153 153 pjoin(components, "font-awesome", "fonts", "*.*"),
154 154 pjoin(components, "google-caja", "html-css-sanitizer-minified.js"),
155 155 pjoin(components, "highlight.js", "build", "highlight.pack.js"),
156 156 pjoin(components, "jquery", "jquery.min.js"),
157 157 pjoin(components, "jquery-ui", "ui", "minified", "jquery-ui.min.js"),
158 158 pjoin(components, "jquery-ui", "themes", "smoothness", "jquery-ui.min.css"),
159 159 pjoin(components, "jquery-ui", "themes", "smoothness", "images", "*"),
160 160 pjoin(components, "marked", "lib", "marked.js"),
161 161 pjoin(components, "requirejs", "require.js"),
162 162 pjoin(components, "underscore", "underscore-min.js"),
163 163 pjoin(components, "moment", "moment.js"),
164 164 pjoin(components, "moment", "min","moment.min.js"),
165 165 ])
166 166
167 167 # Ship all of Codemirror's CSS and JS
168 168 for parent, dirs, files in os.walk(pjoin(components, 'codemirror')):
169 169 for f in files:
170 170 if f.endswith(('.js', '.css')):
171 171 static_data.append(pjoin(parent, f))
172 172
173 173 os.chdir(os.path.join('tests',))
174 174 js_tests = glob('*.js') + glob('*/*.js')
175 175
176 176 os.chdir(os.path.join(cwd, 'IPython', 'nbconvert'))
177 177 nbconvert_templates = [os.path.join(dirpath, '*.*')
178 178 for dirpath, _, _ in os.walk('templates')]
179 179
180 180 os.chdir(cwd)
181 181
182 182 package_data = {
183 183 'IPython.config.profile' : ['README*', '*/*.py'],
184 184 'IPython.core.tests' : ['*.png', '*.jpg'],
185 185 'IPython.lib.tests' : ['*.wav'],
186 186 'IPython.testing.plugin' : ['*.txt'],
187 187 'IPython.html' : ['templates/*'] + static_data,
188 188 'IPython.html.tests' : js_tests,
189 189 'IPython.qt.console' : ['resources/icon/*.svg'],
190 190 'IPython.nbconvert' : nbconvert_templates +
191 191 [
192 192 'tests/files/*.*',
193 193 'exporters/tests/files/*.*',
194 194 'preprocessors/tests/files/*.*',
195 195 ],
196 196 'IPython.nbconvert.filters' : ['marked.js'],
197 197 'IPython.nbformat' : ['tests/*.ipynb','v3/v3.withref.json']
198 198 }
199 199
200 200 return package_data
201 201
202 202
203 203 def check_package_data(package_data):
204 204 """verify that package_data globs make sense"""
205 205 print("checking package data")
206 206 for pkg, data in package_data.items():
207 207 pkg_root = pjoin(*pkg.split('.'))
208 208 for d in data:
209 209 path = pjoin(pkg_root, d)
210 210 if '*' in path:
211 211 assert len(glob(path)) > 0, "No files match pattern %s" % path
212 212 else:
213 213 assert os.path.exists(path), "Missing package data: %s" % path
214 214
215 215
216 216 def check_package_data_first(command):
217 217 """decorator for checking package_data before running a given command
218 218
219 219 Probably only needs to wrap build_py
220 220 """
221 221 class DecoratedCommand(command):
222 222 def run(self):
223 223 check_package_data(self.package_data)
224 224 command.run(self)
225 225 return DecoratedCommand
226 226
227 227
228 228 #---------------------------------------------------------------------------
229 229 # Find data files
230 230 #---------------------------------------------------------------------------
231 231
232 232 def make_dir_struct(tag,base,out_base):
233 233 """Make the directory structure of all files below a starting dir.
234 234
235 235 This is just a convenience routine to help build a nested directory
236 236 hierarchy because distutils is too stupid to do this by itself.
237 237
238 238 XXX - this needs a proper docstring!
239 239 """
240 240
241 241 # we'll use these a lot below
242 242 lbase = len(base)
243 243 pathsep = os.path.sep
244 244 lpathsep = len(pathsep)
245 245
246 246 out = []
247 247 for (dirpath,dirnames,filenames) in os.walk(base):
248 248 # we need to strip out the dirpath from the base to map it to the
249 249 # output (installation) path. This requires possibly stripping the
250 250 # path separator, because otherwise pjoin will not work correctly
251 251 # (pjoin('foo/','/bar') returns '/bar').
252 252
253 253 dp_eff = dirpath[lbase:]
254 254 if dp_eff.startswith(pathsep):
255 255 dp_eff = dp_eff[lpathsep:]
256 256 # The output path must be anchored at the out_base marker
257 257 out_path = pjoin(out_base,dp_eff)
258 258 # Now we can generate the final filenames. Since os.walk only produces
259 259 # filenames, we must join back with the dirpath to get full valid file
260 260 # paths:
261 261 pfiles = [pjoin(dirpath,f) for f in filenames]
262 262 # Finally, generate the entry we need, which is a pari of (output
263 263 # path, files) for use as a data_files parameter in install_data.
264 264 out.append((out_path, pfiles))
265 265
266 266 return out
267 267
268 268
269 269 def find_data_files():
270 270 """
271 271 Find IPython's data_files.
272 272
273 273 Just man pages at this point.
274 274 """
275 275
276 276 manpagebase = pjoin('share', 'man', 'man1')
277 277
278 278 # Simple file lists can be made by hand
279 279 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
280 280 if not manpages:
281 281 # When running from a source tree, the manpages aren't gzipped
282 282 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
283 283
284 284 # And assemble the entire output list
285 285 data_files = [ (manpagebase, manpages) ]
286 286
287 287 return data_files
288 288
289 289
290 290 def make_man_update_target(manpage):
291 291 """Return a target_update-compliant tuple for the given manpage.
292 292
293 293 Parameters
294 294 ----------
295 295 manpage : string
296 296 Name of the manpage, must include the section number (trailing number).
297 297
298 298 Example
299 299 -------
300 300
301 301 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
302 302 ('docs/man/ipython.1.gz',
303 303 ['docs/man/ipython.1'],
304 304 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
305 305 """
306 306 man_dir = pjoin('docs', 'man')
307 307 manpage_gz = manpage + '.gz'
308 308 manpath = pjoin(man_dir, manpage)
309 309 manpath_gz = pjoin(man_dir, manpage_gz)
310 310 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
311 311 locals() )
312 312 return (manpath_gz, [manpath], gz_cmd)
313 313
314 314 # The two functions below are copied from IPython.utils.path, so we don't need
315 315 # to import IPython during setup, which fails on Python 3.
316 316
317 317 def target_outdated(target,deps):
318 318 """Determine whether a target is out of date.
319 319
320 320 target_outdated(target,deps) -> 1/0
321 321
322 322 deps: list of filenames which MUST exist.
323 323 target: single filename which may or may not exist.
324 324
325 325 If target doesn't exist or is older than any file listed in deps, return
326 326 true, otherwise return false.
327 327 """
328 328 try:
329 329 target_time = os.path.getmtime(target)
330 330 except os.error:
331 331 return 1
332 332 for dep in deps:
333 333 dep_time = os.path.getmtime(dep)
334 334 if dep_time > target_time:
335 335 #print "For target",target,"Dep failed:",dep # dbg
336 336 #print "times (dep,tar):",dep_time,target_time # dbg
337 337 return 1
338 338 return 0
339 339
340 340
341 341 def target_update(target,deps,cmd):
342 342 """Update a target with a given command given a list of dependencies.
343 343
344 344 target_update(target,deps,cmd) -> runs cmd if target is outdated.
345 345
346 346 This is just a wrapper around target_outdated() which calls the given
347 347 command if target is outdated."""
348 348
349 349 if target_outdated(target,deps):
350 350 os.system(cmd)
351 351
352 352 #---------------------------------------------------------------------------
353 353 # Find scripts
354 354 #---------------------------------------------------------------------------
355 355
356 356 def find_entry_points():
357 357 """Find IPython's scripts.
358 358
359 359 if entry_points is True:
360 360 return setuptools entry_point-style definitions
361 361 else:
362 362 return file paths of plain scripts [default]
363 363
364 364 suffix is appended to script names if entry_points is True, so that the
365 365 Python 3 scripts get named "ipython3" etc.
366 366 """
367 367 ep = [
368 368 'ipython%s = IPython:start_ipython',
369 369 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
370 370 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
371 371 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
372 372 'iptest%s = IPython.testing.iptestcontroller:main',
373 373 ]
374 374 suffix = str(sys.version_info[0])
375 375 return [e % '' for e in ep] + [e % suffix for e in ep]
376 376
377 377 script_src = """#!{executable}
378 378 # This script was automatically generated by setup.py
379 379 if __name__ == '__main__':
380 380 from {mod} import {func}
381 381 {func}()
382 382 """
383 383
384 384 class build_scripts_entrypt(build_scripts):
385 385 def run(self):
386 386 self.mkpath(self.build_dir)
387 387 outfiles = []
388 388 for script in find_entry_points():
389 389 name, entrypt = script.split('=')
390 390 name = name.strip()
391 391 entrypt = entrypt.strip()
392 392 outfile = os.path.join(self.build_dir, name)
393 393 outfiles.append(outfile)
394 394 print('Writing script to', outfile)
395 395
396 396 mod, func = entrypt.split(':')
397 397 with open(outfile, 'w') as f:
398 398 f.write(script_src.format(executable=sys.executable,
399 399 mod=mod, func=func))
400 400
401 401 return outfiles, outfiles
402 402
403 403 class install_lib_symlink(Command):
404 404 user_options = [
405 405 ('install-dir=', 'd', "directory to install to"),
406 406 ]
407 407
408 408 def initialize_options(self):
409 409 self.install_dir = None
410 410
411 411 def finalize_options(self):
412 412 self.set_undefined_options('symlink',
413 413 ('install_lib', 'install_dir'),
414 414 )
415 415
416 416 def run(self):
417 417 if sys.platform == 'win32':
418 418 raise Exception("This doesn't work on Windows.")
419 419 pkg = os.path.join(os.getcwd(), 'IPython')
420 420 dest = os.path.join(self.install_dir, 'IPython')
421 421 if os.path.islink(dest):
422 422 print('removing existing symlink at %s' % dest)
423 423 os.unlink(dest)
424 424 print('symlinking %s -> %s' % (pkg, dest))
425 425 os.symlink(pkg, dest)
426 426
427 427 class unsymlink(install):
428 428 def run(self):
429 429 dest = os.path.join(self.install_lib, 'IPython')
430 430 if os.path.islink(dest):
431 431 print('removing symlink at %s' % dest)
432 432 os.unlink(dest)
433 433 else:
434 434 print('No symlink exists at %s' % dest)
435 435
436 436 class install_symlinked(install):
437 437 def run(self):
438 438 if sys.platform == 'win32':
439 439 raise Exception("This doesn't work on Windows.")
440 440
441 441 # Run all sub-commands (at least those that need to be run)
442 442 for cmd_name in self.get_sub_commands():
443 443 self.run_command(cmd_name)
444 444
445 445 # 'sub_commands': a list of commands this command might have to run to
446 446 # get its work done. See cmd.py for more info.
447 447 sub_commands = [('install_lib_symlink', lambda self:True),
448 448 ('install_scripts_sym', lambda self:True),
449 449 ]
450 450
451 451 class install_scripts_for_symlink(install_scripts):
452 452 """Redefined to get options from 'symlink' instead of 'install'.
453 453
454 454 I love distutils almost as much as I love setuptools.
455 455 """
456 456 def finalize_options(self):
457 457 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
458 458 self.set_undefined_options('symlink',
459 459 ('install_scripts', 'install_dir'),
460 460 ('force', 'force'),
461 461 ('skip_build', 'skip_build'),
462 462 )
463 463
464 464 #---------------------------------------------------------------------------
465 465 # Verify all dependencies
466 466 #---------------------------------------------------------------------------
467 467
468 468 def check_for_dependencies():
469 469 """Check for IPython's dependencies.
470 470
471 471 This function should NOT be called if running under setuptools!
472 472 """
473 473 from setupext.setupext import (
474 474 print_line, print_raw, print_status,
475 475 check_for_sphinx, check_for_pygments,
476 476 check_for_nose, check_for_pexpect,
477 477 check_for_pyzmq, check_for_readline,
478 478 check_for_jinja2, check_for_tornado
479 479 )
480 480 print_line()
481 481 print_raw("BUILDING IPYTHON")
482 482 print_status('python', sys.version)
483 483 print_status('platform', sys.platform)
484 484 if sys.platform == 'win32':
485 485 print_status('Windows version', sys.getwindowsversion())
486 486
487 487 print_raw("")
488 488 print_raw("OPTIONAL DEPENDENCIES")
489 489
490 490 check_for_sphinx()
491 491 check_for_pygments()
492 492 check_for_nose()
493 493 if os.name == 'posix':
494 494 check_for_pexpect()
495 495 check_for_pyzmq()
496 496 check_for_tornado()
497 497 check_for_readline()
498 498 check_for_jinja2()
499 499
500 500 #---------------------------------------------------------------------------
501 501 # VCS related
502 502 #---------------------------------------------------------------------------
503 503
504 504 # utils.submodule has checks for submodule status
505 505 execfile(pjoin('IPython','utils','submodule.py'), globals())
506 506
507 507 class UpdateSubmodules(Command):
508 508 """Update git submodules
509 509
510 510 IPython's external javascript dependencies live in a separate repo.
511 511 """
512 512 description = "Update git submodules"
513 513 user_options = []
514 514
515 515 def initialize_options(self):
516 516 pass
517 517
518 518 def finalize_options(self):
519 519 pass
520 520
521 521 def run(self):
522 522 failure = False
523 523 try:
524 524 self.spawn('git submodule init'.split())
525 525 self.spawn('git submodule update --recursive'.split())
526 526 except Exception as e:
527 527 failure = e
528 528 print(e)
529 529
530 530 if not check_submodule_status(repo_root) == 'clean':
531 531 print("submodules could not be checked out")
532 532 sys.exit(1)
533 533
534 534
535 535 def git_prebuild(pkg_dir, build_cmd=build_py):
536 536 """Return extended build or sdist command class for recording commit
537 537
538 538 records git commit in IPython.utils._sysinfo.commit
539 539
540 540 for use in IPython.utils.sysinfo.sys_info() calls after installation.
541 541
542 542 Also ensures that submodules exist prior to running
543 543 """
544 544
545 545 class MyBuildPy(build_cmd):
546 546 ''' Subclass to write commit data into installation tree '''
547 547 def run(self):
548 548 build_cmd.run(self)
549 549 # this one will only fire for build commands
550 550 if hasattr(self, 'build_lib'):
551 551 self._record_commit(self.build_lib)
552 552
553 553 def make_release_tree(self, base_dir, files):
554 554 # this one will fire for sdist
555 555 build_cmd.make_release_tree(self, base_dir, files)
556 556 self._record_commit(base_dir)
557 557
558 558 def _record_commit(self, base_dir):
559 559 import subprocess
560 560 proc = subprocess.Popen('git rev-parse --short HEAD',
561 561 stdout=subprocess.PIPE,
562 562 stderr=subprocess.PIPE,
563 563 shell=True)
564 564 repo_commit, _ = proc.communicate()
565 565 repo_commit = repo_commit.strip().decode("ascii")
566 566
567 567 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
568 568 if os.path.isfile(out_pth) and not repo_commit:
569 569 # nothing to write, don't clobber
570 570 return
571 571
572 572 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
573 573
574 574 # remove to avoid overwriting original via hard link
575 575 try:
576 576 os.remove(out_pth)
577 577 except (IOError, OSError):
578 578 pass
579 579 with open(out_pth, 'w') as out_file:
580 580 out_file.writelines([
581 581 '# GENERATED BY setup.py\n',
582 'commit = "%s"\n' % repo_commit,
582 'commit = u"%s"\n' % repo_commit,
583 583 ])
584 584 return require_submodules(MyBuildPy)
585 585
586 586
587 587 def require_submodules(command):
588 588 """decorator for instructing a command to check for submodules before running"""
589 589 class DecoratedCommand(command):
590 590 def run(self):
591 591 if not check_submodule_status(repo_root) == 'clean':
592 592 print("submodules missing! Run `setup.py submodule` and try again")
593 593 sys.exit(1)
594 594 command.run(self)
595 595 return DecoratedCommand
596 596
597 597 #---------------------------------------------------------------------------
598 598 # bdist related
599 599 #---------------------------------------------------------------------------
600 600
601 601 def get_bdist_wheel():
602 602 """Construct bdist_wheel command for building wheels
603 603
604 604 Constructs py2-none-any tag, instead of py2.7-none-any
605 605 """
606 606 class RequiresWheel(Command):
607 607 description = "Dummy command for missing bdist_wheel"
608 608 user_options = []
609 609
610 610 def initialize_options(self):
611 611 pass
612 612
613 613 def finalize_options(self):
614 614 pass
615 615
616 616 def run(self):
617 617 print("bdist_wheel requires the wheel package")
618 618 sys.exit(1)
619 619
620 620 if 'setuptools' not in sys.modules:
621 621 return RequiresWheel
622 622 else:
623 623 try:
624 624 from wheel.bdist_wheel import bdist_wheel, read_pkg_info, write_pkg_info
625 625 except ImportError:
626 626 return RequiresWheel
627 627
628 628 class bdist_wheel_tag(bdist_wheel):
629 629
630 630 def add_requirements(self, metadata_path):
631 631 """transform platform-dependent requirements"""
632 632 pkg_info = read_pkg_info(metadata_path)
633 633 # pkg_info is an email.Message object (?!)
634 634 # we have to remove the unconditional 'readline' and/or 'pyreadline' entries
635 635 # and transform them to conditionals
636 636 requires = pkg_info.get_all('Requires-Dist')
637 637 del pkg_info['Requires-Dist']
638 638 def _remove_startswith(lis, prefix):
639 639 """like list.remove, but with startswith instead of =="""
640 640 found = False
641 641 for idx, item in enumerate(lis):
642 642 if item.startswith(prefix):
643 643 found = True
644 644 break
645 645 if found:
646 646 lis.pop(idx)
647 647
648 648 for pkg in ("gnureadline", "pyreadline", "mock"):
649 649 _remove_startswith(requires, pkg)
650 650 requires.append("gnureadline; sys.platform == 'darwin' and platform.python_implementation == 'CPython'")
651 651 requires.append("pyreadline (>=2.0); extra == 'terminal' and sys.platform == 'win32' and platform.python_implementation == 'CPython'")
652 652 requires.append("pyreadline (>=2.0); extra == 'all' and sys.platform == 'win32' and platform.python_implementation == 'CPython'")
653 653 requires.append("mock; extra == 'test' and python_version < '3.3'")
654 654 for r in requires:
655 655 pkg_info['Requires-Dist'] = r
656 656 write_pkg_info(metadata_path, pkg_info)
657 657
658 658 return bdist_wheel_tag
659 659
660 660 #---------------------------------------------------------------------------
661 661 # Notebook related
662 662 #---------------------------------------------------------------------------
663 663
664 664 class CompileCSS(Command):
665 665 """Recompile Notebook CSS
666 666
667 667 Regenerate the compiled CSS from LESS sources.
668 668
669 669 Requires various dev dependencies, such as fabric and lessc.
670 670 """
671 671 description = "Recompile Notebook CSS"
672 672 user_options = [
673 673 ('minify', 'x', "minify CSS"),
674 674 ('force', 'f', "force recompilation of CSS"),
675 675 ]
676 676
677 677 def initialize_options(self):
678 678 self.minify = False
679 679 self.force = False
680 680
681 681 def finalize_options(self):
682 682 self.minify = bool(self.minify)
683 683 self.force = bool(self.force)
684 684
685 685 def run(self):
686 686 check_call([
687 687 "fab",
688 688 "css:minify=%s,force=%s" % (self.minify, self.force),
689 689 ], cwd=pjoin(repo_root, "IPython", "html"),
690 690 )
691 691
692 692
693 693 class JavascriptVersion(Command):
694 694 """write the javascript version to notebook javascript"""
695 695 description = "Write IPython version to javascript"
696 696 user_options = []
697 697
698 698 def initialize_options(self):
699 699 pass
700 700
701 701 def finalize_options(self):
702 702 pass
703 703
704 704 def run(self):
705 705 nsfile = pjoin(repo_root, "IPython", "html", "static", "base", "js", "namespace.js")
706 706 with open(nsfile) as f:
707 707 lines = f.readlines()
708 708 with open(nsfile, 'w') as f:
709 709 for line in lines:
710 710 if line.startswith("IPython.version"):
711 711 line = 'IPython.version = "{0}";\n'.format(version)
712 712 f.write(line)
713 713
714 714
715 715 def css_js_prerelease(command, strict=True):
716 716 """decorator for building js/minified css prior to a release"""
717 717 class DecoratedCommand(command):
718 718 def run(self):
719 719 self.distribution.run_command('jsversion')
720 720 css = self.distribution.get_command_obj('css')
721 721 css.minify = True
722 722 try:
723 723 self.distribution.run_command('css')
724 724 except Exception as e:
725 725 if strict:
726 726 raise
727 727 else:
728 728 log.warn("Failed to build css sourcemaps: %s" % e)
729 729 command.run(self)
730 730 return DecoratedCommand
General Comments 0
You need to be logged in to leave comments. Login now