##// END OF EJS Templates
PEP8 fix
y-p -
Show More
@@ -1,1042 +1,1042 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Sphinx directive to support embedded IPython code.
4 4
5 5 This directive allows pasting of entire interactive IPython sessions, prompts
6 6 and all, and their code will actually get re-executed at doc build time, with
7 7 all prompts renumbered sequentially. It also allows you to input code as a pure
8 8 python input by giving the argument python to the directive. The output looks
9 9 like an interactive ipython section.
10 10
11 11 To enable this directive, simply list it in your Sphinx ``conf.py`` file
12 12 (making sure the directory where you placed it is visible to sphinx, as is
13 13 needed for all Sphinx directives). For example, to enable syntax highlighting
14 14 and the IPython directive::
15 15
16 16 extensions = ['IPython.sphinxext.ipython_console_highlighting',
17 17 'IPython.sphinxext.ipython_directive']
18 18
19 19 The IPython directive outputs code-blocks with the language 'ipython'. So
20 20 if you do not have the syntax highlighting extension enabled as well, then
21 21 all rendered code-blocks will be uncolored. By default this directive assumes
22 22 that your prompts are unchanged IPython ones, but this can be customized.
23 23 The configurable options that can be placed in conf.py are:
24 24
25 25 ipython_savefig_dir:
26 26 The directory in which to save the figures. This is relative to the
27 27 Sphinx source directory. The default is `html_static_path`.
28 28 ipython_rgxin:
29 29 The compiled regular expression to denote the start of IPython input
30 30 lines. The default is re.compile('In \[(\d+)\]:\s?(.*)\s*'). You
31 31 shouldn't need to change this.
32 32 ipython_rgxout:
33 33 The compiled regular expression to denote the start of IPython output
34 34 lines. The default is re.compile('Out\[(\d+)\]:\s?(.*)\s*'). You
35 35 shouldn't need to change this.
36 36 ipython_promptin:
37 37 The string to represent the IPython input prompt in the generated ReST.
38 38 The default is 'In [%d]:'. This expects that the line numbers are used
39 39 in the prompt.
40 40 ipython_promptout:
41 41 The string to represent the IPython prompt in the generated ReST. The
42 42 default is 'Out [%d]:'. This expects that the line numbers are used
43 43 in the prompt.
44 44 ipython_mplbackend:
45 45 The string which specifies if the embedded Sphinx shell should import
46 46 Matplotlib and set the backend. The value specifies a backend that is
47 47 passed to `matplotlib.use()` before any lines in `ipython_execlines` are
48 48 executed. If not specified in conf.py, then the default value of 'agg' is
49 49 used. To use the IPython directive without matplotlib as a dependency, set
50 50 the value to `None`. It may end up that matplotlib is still imported
51 51 if the user specifies so in `ipython_execlines` or makes use of the
52 52 @savefig pseudo decorator.
53 53 ipython_execlines:
54 54 A list of strings to be exec'd in the embedded Sphinx shell. Typical
55 55 usage is to make certain packages always available. Set this to an empty
56 56 list if you wish to have no imports always available. If specified in
57 57 conf.py as `None`, then it has the effect of making no imports available.
58 58 If omitted from conf.py altogether, then the default value of
59 59 ['import numpy as np', 'import matplotlib.pyplot as plt'] is used.
60 60 ipython_holdcount
61 61 When the @suppress pseudo-decorator is used, the execution count can be
62 62 incremented or not. The default behavior is to hold the execution count,
63 63 corresponding to a value of `True`. Set this to `False` to increment
64 64 the execution count after each suppressed command.
65 65
66 66 As an example, to use the IPython directive when `matplotlib` is not available,
67 67 one sets the backend to `None`::
68 68
69 69 ipython_mplbackend = None
70 70
71 71 An example usage of the directive is:
72 72
73 73 .. code-block:: rst
74 74
75 75 .. ipython::
76 76
77 77 In [1]: x = 1
78 78
79 79 In [2]: y = x**2
80 80
81 81 In [3]: print(y)
82 82
83 83 See http://matplotlib.org/sampledoc/ipython_directive.html for additional
84 84 documentation.
85 85
86 86 ToDo
87 87 ----
88 88
89 89 - Turn the ad-hoc test() function into a real test suite.
90 90 - Break up ipython-specific functionality from matplotlib stuff into better
91 91 separated code.
92 92
93 93 Authors
94 94 -------
95 95
96 96 - John D Hunter: orignal author.
97 97 - Fernando Perez: refactoring, documentation, cleanups, port to 0.11.
98 98 - VΓ‘clavΕ milauer <eudoxos-AT-arcig.cz>: Prompt generalizations.
99 99 - Skipper Seabold, refactoring, cleanups, pure python addition
100 100 """
101 101 from __future__ import print_function
102 102
103 103 #-----------------------------------------------------------------------------
104 104 # Imports
105 105 #-----------------------------------------------------------------------------
106 106
107 107 # Stdlib
108 108 import os
109 109 import re
110 110 import sys
111 111 import tempfile
112 112 import ast
113 113 import warnings
114 114
115 115 # To keep compatibility with various python versions
116 116 try:
117 117 from hashlib import md5
118 118 except ImportError:
119 119 from md5 import md5
120 120
121 121 # Third-party
122 122 import sphinx
123 123 from docutils.parsers.rst import directives
124 124 from docutils import nodes
125 125 from sphinx.util.compat import Directive
126 126
127 127 # Our own
128 128 from IPython import Config, InteractiveShell
129 129 from IPython.core.profiledir import ProfileDir
130 130 from IPython.utils import io
131 131 from IPython.utils.py3compat import PY3
132 132
133 133 if PY3:
134 134 from io import StringIO
135 135 else:
136 136 from StringIO import StringIO
137 137
138 138 #-----------------------------------------------------------------------------
139 139 # Globals
140 140 #-----------------------------------------------------------------------------
141 141 # for tokenizing blocks
142 142 COMMENT, INPUT, OUTPUT = range(3)
143 143
144 144 #-----------------------------------------------------------------------------
145 145 # Functions and class declarations
146 146 #-----------------------------------------------------------------------------
147 147
148 148 def block_parser(part, rgxin, rgxout, fmtin, fmtout):
149 149 """
150 150 part is a string of ipython text, comprised of at most one
151 151 input, one ouput, comments, and blank lines. The block parser
152 152 parses the text into a list of::
153 153
154 154 blocks = [ (TOKEN0, data0), (TOKEN1, data1), ...]
155 155
156 156 where TOKEN is one of [COMMENT | INPUT | OUTPUT ] and
157 157 data is, depending on the type of token::
158 158
159 159 COMMENT : the comment string
160 160
161 161 INPUT: the (DECORATOR, INPUT_LINE, REST) where
162 162 DECORATOR: the input decorator (or None)
163 163 INPUT_LINE: the input as string (possibly multi-line)
164 164 REST : any stdout generated by the input line (not OUTPUT)
165 165
166 166 OUTPUT: the output string, possibly multi-line
167 167
168 168 """
169 169 block = []
170 170 lines = part.split('\n')
171 171 N = len(lines)
172 172 i = 0
173 173 decorator = None
174 174 while 1:
175 175
176 176 if i==N:
177 177 # nothing left to parse -- the last line
178 178 break
179 179
180 180 line = lines[i]
181 181 i += 1
182 182 line_stripped = line.strip()
183 183 if line_stripped.startswith('#'):
184 184 block.append((COMMENT, line))
185 185 continue
186 186
187 187 if line_stripped.startswith('@'):
188 188 # we're assuming at most one decorator -- may need to
189 189 # rethink
190 190 decorator = line_stripped
191 191 continue
192 192
193 193 # does this look like an input line?
194 194 matchin = rgxin.match(line)
195 195 if matchin:
196 196 lineno, inputline = int(matchin.group(1)), matchin.group(2)
197 197
198 198 # the ....: continuation string
199 199 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
200 200 Nc = len(continuation)
201 201 # input lines can continue on for more than one line, if
202 202 # we have a '\' line continuation char or a function call
203 203 # echo line 'print'. The input line can only be
204 204 # terminated by the end of the block or an output line, so
205 205 # we parse out the rest of the input line if it is
206 206 # multiline as well as any echo text
207 207
208 208 rest = []
209 209 while i<N:
210 210
211 211 # look ahead; if the next line is blank, or a comment, or
212 212 # an output line, we're done
213 213
214 214 nextline = lines[i]
215 215 matchout = rgxout.match(nextline)
216 216 #print "nextline=%s, continuation=%s, starts=%s"%(nextline, continuation, nextline.startswith(continuation))
217 217 if matchout or nextline.startswith('#'):
218 218 break
219 219 elif nextline.startswith(continuation):
220 220 nextline = nextline[Nc:]
221 221 if nextline and nextline[0] == ' ':
222 222 nextline = nextline[1:]
223 223
224 224 inputline += '\n' + nextline
225 225 else:
226 226 rest.append(nextline)
227 227 i+= 1
228 228
229 229 block.append((INPUT, (decorator, inputline, '\n'.join(rest))))
230 230 continue
231 231
232 232 # if it looks like an output line grab all the text to the end
233 233 # of the block
234 234 matchout = rgxout.match(line)
235 235 if matchout:
236 236 lineno, output = int(matchout.group(1)), matchout.group(2)
237 237 if i<N-1:
238 238 output = '\n'.join([output] + lines[i:])
239 239
240 240 block.append((OUTPUT, output))
241 241 break
242 242
243 243 return block
244 244
245 245
246 246 class EmbeddedSphinxShell(object):
247 247 """An embedded IPython instance to run inside Sphinx"""
248 248
249 def __init__(self, exec_lines=None,state=None):
249 def __init__(self, exec_lines=None, state=None):
250 250
251 251 self.cout = StringIO()
252 252
253 253 if exec_lines is None:
254 254 exec_lines = []
255 255
256 256 self.state = state
257 257
258 258 # Create config object for IPython
259 259 config = Config()
260 260 config.InteractiveShell.autocall = False
261 261 config.InteractiveShell.autoindent = False
262 262 config.InteractiveShell.colors = 'NoColor'
263 263
264 264 # create a profile so instance history isn't saved
265 265 tmp_profile_dir = tempfile.mkdtemp(prefix='profile_')
266 266 profname = 'auto_profile_sphinx_build'
267 267 pdir = os.path.join(tmp_profile_dir,profname)
268 268 profile = ProfileDir.create_profile_dir(pdir)
269 269
270 270 # Create and initialize global ipython, but don't start its mainloop.
271 271 # This will persist across different EmbededSphinxShell instances.
272 272 IP = InteractiveShell.instance(config=config, profile_dir=profile)
273 273
274 274 # io.stdout redirect must be done after instantiating InteractiveShell
275 275 io.stdout = self.cout
276 276 io.stderr = self.cout
277 277
278 278 # For debugging, so we can see normal output, use this:
279 279 #from IPython.utils.io import Tee
280 280 #io.stdout = Tee(self.cout, channel='stdout') # dbg
281 281 #io.stderr = Tee(self.cout, channel='stderr') # dbg
282 282
283 283 # Store a few parts of IPython we'll need.
284 284 self.IP = IP
285 285 self.user_ns = self.IP.user_ns
286 286 self.user_global_ns = self.IP.user_global_ns
287 287
288 288 self.input = ''
289 289 self.output = ''
290 290
291 291 self.is_verbatim = False
292 292 self.is_doctest = False
293 293 self.is_suppress = False
294 294
295 295 # Optionally, provide more detailed information to shell.
296 296 self.directive = None
297 297
298 298 # on the first call to the savefig decorator, we'll import
299 299 # pyplot as plt so we can make a call to the plt.gcf().savefig
300 300 self._pyplot_imported = False
301 301
302 302 # Prepopulate the namespace.
303 303 for line in exec_lines:
304 304 self.process_input_line(line, store_history=False)
305 305
306 306 def clear_cout(self):
307 307 self.cout.seek(0)
308 308 self.cout.truncate(0)
309 309
310 310 def process_input_line(self, line, store_history=True):
311 311 """process the input, capturing stdout"""
312 312
313 313 stdout = sys.stdout
314 314 splitter = self.IP.input_splitter
315 315 try:
316 316 sys.stdout = self.cout
317 317 splitter.push(line)
318 318 more = splitter.push_accepts_more()
319 319 if not more:
320 320 source_raw = splitter.source_raw_reset()[1]
321 321 self.IP.run_cell(source_raw, store_history=store_history)
322 322 finally:
323 323 sys.stdout = stdout
324 324
325 325 def process_image(self, decorator):
326 326 """
327 327 # build out an image directive like
328 328 # .. image:: somefile.png
329 329 # :width 4in
330 330 #
331 331 # from an input like
332 332 # savefig somefile.png width=4in
333 333 """
334 334 savefig_dir = self.savefig_dir
335 335 source_dir = self.source_dir
336 336 saveargs = decorator.split(' ')
337 337 filename = saveargs[1]
338 338 # insert relative path to image file in source
339 339 outfile = os.path.relpath(os.path.join(savefig_dir,filename),
340 340 source_dir)
341 341
342 342 imagerows = ['.. image:: %s'%outfile]
343 343
344 344 for kwarg in saveargs[2:]:
345 345 arg, val = kwarg.split('=')
346 346 arg = arg.strip()
347 347 val = val.strip()
348 348 imagerows.append(' :%s: %s'%(arg, val))
349 349
350 350 image_file = os.path.basename(outfile) # only return file name
351 351 image_directive = '\n'.join(imagerows)
352 352 return image_file, image_directive
353 353
354 354 # Callbacks for each type of token
355 355 def process_input(self, data, input_prompt, lineno):
356 356 """
357 357 Process data block for INPUT token.
358 358
359 359 """
360 360 decorator, input, rest = data
361 361 image_file = None
362 362 image_directive = None
363 363
364 364 is_verbatim = decorator=='@verbatim' or self.is_verbatim
365 365 is_doctest = (decorator is not None and \
366 366 decorator.startswith('@doctest')) or self.is_doctest
367 367 is_suppress = decorator=='@suppress' or self.is_suppress
368 368 is_okexcept = decorator=='@okexcept' or self.is_okexcept
369 369 is_okwarning = decorator=='@okwarning' or self.is_okwarning
370 370 is_savefig = decorator is not None and \
371 371 decorator.startswith('@savefig')
372 372
373 373 input_lines = input.split('\n')
374 374 if len(input_lines) > 1:
375 375 if input_lines[-1] != "":
376 376 input_lines.append('') # make sure there's a blank line
377 377 # so splitter buffer gets reset
378 378
379 379 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
380 380
381 381 if is_savefig:
382 382 image_file, image_directive = self.process_image(decorator)
383 383
384 384 ret = []
385 385 is_semicolon = False
386 386
387 387 # Hold the execution count, if requested to do so.
388 388 if is_suppress and self.hold_count:
389 389 store_history = False
390 390 else:
391 391 store_history = True
392 392
393 393 # Note: catch_warnings is not thread safe
394 394 with warnings.catch_warnings(record=True) as ws:
395 395 for i, line in enumerate(input_lines):
396 396 if line.endswith(';'):
397 397 is_semicolon = True
398 398
399 399 if i == 0:
400 400 # process the first input line
401 401 if is_verbatim:
402 402 self.process_input_line('')
403 403 self.IP.execution_count += 1 # increment it anyway
404 404 else:
405 405 # only submit the line in non-verbatim mode
406 406 self.process_input_line(line, store_history=store_history)
407 407 formatted_line = '%s %s'%(input_prompt, line)
408 408 else:
409 409 # process a continuation line
410 410 if not is_verbatim:
411 411 self.process_input_line(line, store_history=store_history)
412 412
413 413 formatted_line = '%s %s'%(continuation, line)
414 414
415 415 if not is_suppress:
416 416 ret.append(formatted_line)
417 417
418 418 if not is_suppress and len(rest.strip()) and is_verbatim:
419 419 # the "rest" is the standard output of the
420 420 # input, which needs to be added in
421 421 # verbatim mode
422 422 ret.append(rest)
423 423
424 424 self.cout.seek(0)
425 425 output = self.cout.read()
426 426 if not is_suppress and not is_semicolon:
427 427 ret.append(output)
428 428 elif is_semicolon: # get spacing right
429 429 ret.append('')
430 430
431 431 # context information
432 432 filename = self.state.document.current_source
433 433 lineno = self.state.document.current_line
434 434
435 435 # output any exceptions raised during execution to stdout
436 436 # unless :okexcept: has been specified.
437 437 if not is_okexcept and "Traceback" in output:
438 438 s = "\nException in %s at block ending on line %s\n" % (filename, lineno)
439 439 s += "Specify :okexcept: as an option in the ipython:: block to suppress this message\n"
440 440 sys.stdout.write('\n\n>>>' + ('-' * 73))
441 441 sys.stdout.write(s)
442 442 sys.stdout.write(output)
443 443 sys.stdout.write('<<<' + ('-' * 73) + '\n\n')
444 444
445 445 # output any warning raised during execution to stdout
446 446 # unless :okwarning: has been specified.
447 447 if not is_okwarning:
448 448 for w in ws:
449 449 s = "\nWarning in %s at block ending on line %s\n" % (filename, lineno)
450 450 s += "Specify :okwarning: as an option in the ipython:: block to suppress this message\n"
451 451 sys.stdout.write('\n\n>>>' + ('-' * 73))
452 452 sys.stdout.write(s)
453 453 sys.stdout.write(('-' * 76) + '\n')
454 454 s=warnings.formatwarning(w.message, w.category,
455 455 w.filename, w.lineno, w.line)
456 456 sys.stdout.write(s)
457 457 sys.stdout.write('<<<' + ('-' * 73) + '\n')
458 458
459 459 self.cout.truncate(0)
460 460 return (ret, input_lines, output, is_doctest, decorator, image_file,
461 461 image_directive)
462 462
463 463
464 464 def process_output(self, data, output_prompt,
465 465 input_lines, output, is_doctest, decorator, image_file):
466 466 """
467 467 Process data block for OUTPUT token.
468 468
469 469 """
470 470 TAB = ' ' * 4
471 471
472 472 if is_doctest and output is not None:
473 473
474 474 found = output
475 475 found = found.strip()
476 476 submitted = data.strip()
477 477
478 478 if self.directive is None:
479 479 source = 'Unavailable'
480 480 content = 'Unavailable'
481 481 else:
482 482 source = self.directive.state.document.current_source
483 483 content = self.directive.content
484 484 # Add tabs and join into a single string.
485 485 content = '\n'.join([TAB + line for line in content])
486 486
487 487 # Make sure the output contains the output prompt.
488 488 ind = found.find(output_prompt)
489 489 if ind < 0:
490 490 e = ('output does not contain output prompt\n\n'
491 491 'Document source: {0}\n\n'
492 492 'Raw content: \n{1}\n\n'
493 493 'Input line(s):\n{TAB}{2}\n\n'
494 494 'Output line(s):\n{TAB}{3}\n\n')
495 495 e = e.format(source, content, '\n'.join(input_lines),
496 496 repr(found), TAB=TAB)
497 497 raise RuntimeError(e)
498 498 found = found[len(output_prompt):].strip()
499 499
500 500 # Handle the actual doctest comparison.
501 501 if decorator.strip() == '@doctest':
502 502 # Standard doctest
503 503 if found != submitted:
504 504 e = ('doctest failure\n\n'
505 505 'Document source: {0}\n\n'
506 506 'Raw content: \n{1}\n\n'
507 507 'On input line(s):\n{TAB}{2}\n\n'
508 508 'we found output:\n{TAB}{3}\n\n'
509 509 'instead of the expected:\n{TAB}{4}\n\n')
510 510 e = e.format(source, content, '\n'.join(input_lines),
511 511 repr(found), repr(submitted), TAB=TAB)
512 512 raise RuntimeError(e)
513 513 else:
514 514 self.custom_doctest(decorator, input_lines, found, submitted)
515 515
516 516 def process_comment(self, data):
517 517 """Process data fPblock for COMMENT token."""
518 518 if not self.is_suppress:
519 519 return [data]
520 520
521 521 def save_image(self, image_file):
522 522 """
523 523 Saves the image file to disk.
524 524 """
525 525 self.ensure_pyplot()
526 526 command = 'plt.gcf().savefig("%s")'%image_file
527 527 #print 'SAVEFIG', command # dbg
528 528 self.process_input_line('bookmark ipy_thisdir', store_history=False)
529 529 self.process_input_line('cd -b ipy_savedir', store_history=False)
530 530 self.process_input_line(command, store_history=False)
531 531 self.process_input_line('cd -b ipy_thisdir', store_history=False)
532 532 self.process_input_line('bookmark -d ipy_thisdir', store_history=False)
533 533 self.clear_cout()
534 534
535 535 def process_block(self, block):
536 536 """
537 537 process block from the block_parser and return a list of processed lines
538 538 """
539 539 ret = []
540 540 output = None
541 541 input_lines = None
542 542 lineno = self.IP.execution_count
543 543
544 544 input_prompt = self.promptin % lineno
545 545 output_prompt = self.promptout % lineno
546 546 image_file = None
547 547 image_directive = None
548 548
549 549 for token, data in block:
550 550 if token == COMMENT:
551 551 out_data = self.process_comment(data)
552 552 elif token == INPUT:
553 553 (out_data, input_lines, output, is_doctest, decorator,
554 554 image_file, image_directive) = \
555 555 self.process_input(data, input_prompt, lineno)
556 556 elif token == OUTPUT:
557 557 out_data = \
558 558 self.process_output(data, output_prompt,
559 559 input_lines, output, is_doctest,
560 560 decorator, image_file)
561 561 if out_data:
562 562 ret.extend(out_data)
563 563
564 564 # save the image files
565 565 if image_file is not None:
566 566 self.save_image(image_file)
567 567
568 568 return ret, image_directive
569 569
570 570 def ensure_pyplot(self):
571 571 """
572 572 Ensures that pyplot has been imported into the embedded IPython shell.
573 573
574 574 Also, makes sure to set the backend appropriately if not set already.
575 575
576 576 """
577 577 # We are here if the @figure pseudo decorator was used. Thus, it's
578 578 # possible that we could be here even if python_mplbackend were set to
579 579 # `None`. That's also strange and perhaps worthy of raising an
580 580 # exception, but for now, we just set the backend to 'agg'.
581 581
582 582 if not self._pyplot_imported:
583 583 if 'matplotlib.backends' not in sys.modules:
584 584 # Then ipython_matplotlib was set to None but there was a
585 585 # call to the @figure decorator (and ipython_execlines did
586 586 # not set a backend).
587 587 #raise Exception("No backend was set, but @figure was used!")
588 588 import matplotlib
589 589 matplotlib.use('agg')
590 590
591 591 # Always import pyplot into embedded shell.
592 592 self.process_input_line('import matplotlib.pyplot as plt',
593 593 store_history=False)
594 594 self._pyplot_imported = True
595 595
596 596 def process_pure_python(self, content):
597 597 """
598 598 content is a list of strings. it is unedited directive content
599 599
600 600 This runs it line by line in the InteractiveShell, prepends
601 601 prompts as needed capturing stderr and stdout, then returns
602 602 the content as a list as if it were ipython code
603 603 """
604 604 output = []
605 605 savefig = False # keep up with this to clear figure
606 606 multiline = False # to handle line continuation
607 607 multiline_start = None
608 608 fmtin = self.promptin
609 609
610 610 ct = 0
611 611
612 612 for lineno, line in enumerate(content):
613 613
614 614 line_stripped = line.strip()
615 615 if not len(line):
616 616 output.append(line)
617 617 continue
618 618
619 619 # handle decorators
620 620 if line_stripped.startswith('@'):
621 621 output.extend([line])
622 622 if 'savefig' in line:
623 623 savefig = True # and need to clear figure
624 624 continue
625 625
626 626 # handle comments
627 627 if line_stripped.startswith('#'):
628 628 output.extend([line])
629 629 continue
630 630
631 631 # deal with lines checking for multiline
632 632 continuation = u' %s:'% ''.join(['.']*(len(str(ct))+2))
633 633 if not multiline:
634 634 modified = u"%s %s" % (fmtin % ct, line_stripped)
635 635 output.append(modified)
636 636 ct += 1
637 637 try:
638 638 ast.parse(line_stripped)
639 639 output.append(u'')
640 640 except Exception: # on a multiline
641 641 multiline = True
642 642 multiline_start = lineno
643 643 else: # still on a multiline
644 644 modified = u'%s %s' % (continuation, line)
645 645 output.append(modified)
646 646
647 647 # if the next line is indented, it should be part of multiline
648 648 if len(content) > lineno + 1:
649 649 nextline = content[lineno + 1]
650 650 if len(nextline) - len(nextline.lstrip()) > 3:
651 651 continue
652 652 try:
653 653 mod = ast.parse(
654 654 '\n'.join(content[multiline_start:lineno+1]))
655 655 if isinstance(mod.body[0], ast.FunctionDef):
656 656 # check to see if we have the whole function
657 657 for element in mod.body[0].body:
658 658 if isinstance(element, ast.Return):
659 659 multiline = False
660 660 else:
661 661 output.append(u'')
662 662 multiline = False
663 663 except Exception:
664 664 pass
665 665
666 666 if savefig: # clear figure if plotted
667 667 self.ensure_pyplot()
668 668 self.process_input_line('plt.clf()', store_history=False)
669 669 self.clear_cout()
670 670 savefig = False
671 671
672 672 return output
673 673
674 674 def custom_doctest(self, decorator, input_lines, found, submitted):
675 675 """
676 676 Perform a specialized doctest.
677 677
678 678 """
679 679 from .custom_doctests import doctests
680 680
681 681 args = decorator.split()
682 682 doctest_type = args[1]
683 683 if doctest_type in doctests:
684 684 doctests[doctest_type](self, args, input_lines, found, submitted)
685 685 else:
686 686 e = "Invalid option to @doctest: {0}".format(doctest_type)
687 687 raise Exception(e)
688 688
689 689
690 690 class IPythonDirective(Directive):
691 691
692 692 has_content = True
693 693 required_arguments = 0
694 694 optional_arguments = 4 # python, suppress, verbatim, doctest
695 695 final_argumuent_whitespace = True
696 696 option_spec = { 'python': directives.unchanged,
697 697 'suppress' : directives.flag,
698 698 'verbatim' : directives.flag,
699 699 'doctest' : directives.flag,
700 700 'okexcept': directives.flag,
701 701 'okwarning': directives.flag
702 702 }
703 703
704 704 shell = None
705 705
706 706 seen_docs = set()
707 707
708 708 def get_config_options(self):
709 709 # contains sphinx configuration variables
710 710 config = self.state.document.settings.env.config
711 711
712 712 # get config variables to set figure output directory
713 713 confdir = self.state.document.settings.env.app.confdir
714 714 savefig_dir = config.ipython_savefig_dir
715 715 source_dir = os.path.dirname(self.state.document.current_source)
716 716 if savefig_dir is None:
717 717 savefig_dir = config.html_static_path
718 718 if isinstance(savefig_dir, list):
719 719 savefig_dir = savefig_dir[0] # safe to assume only one path?
720 720 savefig_dir = os.path.join(confdir, savefig_dir)
721 721
722 722 # get regex and prompt stuff
723 723 rgxin = config.ipython_rgxin
724 724 rgxout = config.ipython_rgxout
725 725 promptin = config.ipython_promptin
726 726 promptout = config.ipython_promptout
727 727 mplbackend = config.ipython_mplbackend
728 728 exec_lines = config.ipython_execlines
729 729 hold_count = config.ipython_holdcount
730 730
731 731 return (savefig_dir, source_dir, rgxin, rgxout,
732 732 promptin, promptout, mplbackend, exec_lines, hold_count)
733 733
734 734 def setup(self):
735 735 # Get configuration values.
736 736 (savefig_dir, source_dir, rgxin, rgxout, promptin, promptout,
737 737 mplbackend, exec_lines, hold_count) = self.get_config_options()
738 738
739 739 if self.shell is None:
740 740 # We will be here many times. However, when the
741 741 # EmbeddedSphinxShell is created, its interactive shell member
742 742 # is the same for each instance.
743 743
744 744 if mplbackend:
745 745 import matplotlib
746 746 # Repeated calls to use() will not hurt us since `mplbackend`
747 747 # is the same each time.
748 748 matplotlib.use(mplbackend)
749 749
750 750 # Must be called after (potentially) importing matplotlib and
751 751 # setting its backend since exec_lines might import pylab.
752 752 self.shell = EmbeddedSphinxShell(exec_lines, self.state)
753 753
754 754 # Store IPython directive to enable better error messages
755 755 self.shell.directive = self
756 756
757 757 # reset the execution count if we haven't processed this doc
758 758 #NOTE: this may be borked if there are multiple seen_doc tmp files
759 759 #check time stamp?
760 760 if not self.state.document.current_source in self.seen_docs:
761 761 self.shell.IP.history_manager.reset()
762 762 self.shell.IP.execution_count = 1
763 763 self.shell.IP.prompt_manager.width = 0
764 764 self.seen_docs.add(self.state.document.current_source)
765 765
766 766 # and attach to shell so we don't have to pass them around
767 767 self.shell.rgxin = rgxin
768 768 self.shell.rgxout = rgxout
769 769 self.shell.promptin = promptin
770 770 self.shell.promptout = promptout
771 771 self.shell.savefig_dir = savefig_dir
772 772 self.shell.source_dir = source_dir
773 773 self.shell.hold_count = hold_count
774 774
775 775 # setup bookmark for saving figures directory
776 776 self.shell.process_input_line('bookmark ipy_savedir %s'%savefig_dir,
777 777 store_history=False)
778 778 self.shell.clear_cout()
779 779
780 780 return rgxin, rgxout, promptin, promptout
781 781
782 782 def teardown(self):
783 783 # delete last bookmark
784 784 self.shell.process_input_line('bookmark -d ipy_savedir',
785 785 store_history=False)
786 786 self.shell.clear_cout()
787 787
788 788 def run(self):
789 789 debug = False
790 790
791 791 #TODO, any reason block_parser can't be a method of embeddable shell
792 792 # then we wouldn't have to carry these around
793 793 rgxin, rgxout, promptin, promptout = self.setup()
794 794
795 795 options = self.options
796 796 self.shell.is_suppress = 'suppress' in options
797 797 self.shell.is_doctest = 'doctest' in options
798 798 self.shell.is_verbatim = 'verbatim' in options
799 799 self.shell.is_okexcept = 'okexcept' in options
800 800 self.shell.is_okwarning = 'okwarning' in options
801 801
802 802 # handle pure python code
803 803 if 'python' in self.arguments:
804 804 content = self.content
805 805 self.content = self.shell.process_pure_python(content)
806 806
807 807 parts = '\n'.join(self.content).split('\n\n')
808 808
809 809 lines = ['.. code-block:: ipython', '']
810 810 figures = []
811 811
812 812 for part in parts:
813 813 block = block_parser(part, rgxin, rgxout, promptin, promptout)
814 814 if len(block):
815 815 rows, figure = self.shell.process_block(block)
816 816 for row in rows:
817 817 lines.extend([' %s'%line for line in row.split('\n')])
818 818
819 819 if figure is not None:
820 820 figures.append(figure)
821 821
822 822 for figure in figures:
823 823 lines.append('')
824 824 lines.extend(figure.split('\n'))
825 825 lines.append('')
826 826
827 827 if len(lines)>2:
828 828 if debug:
829 829 print('\n'.join(lines))
830 830 else:
831 831 # This has to do with input, not output. But if we comment
832 832 # these lines out, then no IPython code will appear in the
833 833 # final output.
834 834 self.state_machine.insert_input(
835 835 lines, self.state_machine.input_lines.source(0))
836 836
837 837 # cleanup
838 838 self.teardown()
839 839
840 840 return []
841 841
842 842 # Enable as a proper Sphinx directive
843 843 def setup(app):
844 844 setup.app = app
845 845
846 846 app.add_directive('ipython', IPythonDirective)
847 847 app.add_config_value('ipython_savefig_dir', None, 'env')
848 848 app.add_config_value('ipython_rgxin',
849 849 re.compile('In \[(\d+)\]:\s?(.*)\s*'), 'env')
850 850 app.add_config_value('ipython_rgxout',
851 851 re.compile('Out\[(\d+)\]:\s?(.*)\s*'), 'env')
852 852 app.add_config_value('ipython_promptin', 'In [%d]:', 'env')
853 853 app.add_config_value('ipython_promptout', 'Out[%d]:', 'env')
854 854
855 855 # We could just let matplotlib pick whatever is specified as the default
856 856 # backend in the matplotlibrc file, but this would cause issues if the
857 857 # backend didn't work in headless environments. For this reason, 'agg'
858 858 # is a good default backend choice.
859 859 app.add_config_value('ipython_mplbackend', 'agg', 'env')
860 860
861 861 # If the user sets this config value to `None`, then EmbeddedSphinxShell's
862 862 # __init__ method will treat it as [].
863 863 execlines = ['import numpy as np', 'import matplotlib.pyplot as plt']
864 864 app.add_config_value('ipython_execlines', execlines, 'env')
865 865
866 866 app.add_config_value('ipython_holdcount', True, 'env')
867 867
868 868 # Simple smoke test, needs to be converted to a proper automatic test.
869 869 def test():
870 870
871 871 examples = [
872 872 r"""
873 873 In [9]: pwd
874 874 Out[9]: '/home/jdhunter/py4science/book'
875 875
876 876 In [10]: cd bookdata/
877 877 /home/jdhunter/py4science/book/bookdata
878 878
879 879 In [2]: from pylab import *
880 880
881 881 In [2]: ion()
882 882
883 883 In [3]: im = imread('stinkbug.png')
884 884
885 885 @savefig mystinkbug.png width=4in
886 886 In [4]: imshow(im)
887 887 Out[4]: <matplotlib.image.AxesImage object at 0x39ea850>
888 888
889 889 """,
890 890 r"""
891 891
892 892 In [1]: x = 'hello world'
893 893
894 894 # string methods can be
895 895 # used to alter the string
896 896 @doctest
897 897 In [2]: x.upper()
898 898 Out[2]: 'HELLO WORLD'
899 899
900 900 @verbatim
901 901 In [3]: x.st<TAB>
902 902 x.startswith x.strip
903 903 """,
904 904 r"""
905 905
906 906 In [130]: url = 'http://ichart.finance.yahoo.com/table.csv?s=CROX\
907 907 .....: &d=9&e=22&f=2009&g=d&a=1&br=8&c=2006&ignore=.csv'
908 908
909 909 In [131]: print url.split('&')
910 910 ['http://ichart.finance.yahoo.com/table.csv?s=CROX', 'd=9', 'e=22', 'f=2009', 'g=d', 'a=1', 'b=8', 'c=2006', 'ignore=.csv']
911 911
912 912 In [60]: import urllib
913 913
914 914 """,
915 915 r"""\
916 916
917 917 In [133]: import numpy.random
918 918
919 919 @suppress
920 920 In [134]: numpy.random.seed(2358)
921 921
922 922 @doctest
923 923 In [135]: numpy.random.rand(10,2)
924 924 Out[135]:
925 925 array([[ 0.64524308, 0.59943846],
926 926 [ 0.47102322, 0.8715456 ],
927 927 [ 0.29370834, 0.74776844],
928 928 [ 0.99539577, 0.1313423 ],
929 929 [ 0.16250302, 0.21103583],
930 930 [ 0.81626524, 0.1312433 ],
931 931 [ 0.67338089, 0.72302393],
932 932 [ 0.7566368 , 0.07033696],
933 933 [ 0.22591016, 0.77731835],
934 934 [ 0.0072729 , 0.34273127]])
935 935
936 936 """,
937 937
938 938 r"""
939 939 In [106]: print x
940 940 jdh
941 941
942 942 In [109]: for i in range(10):
943 943 .....: print i
944 944 .....:
945 945 .....:
946 946 0
947 947 1
948 948 2
949 949 3
950 950 4
951 951 5
952 952 6
953 953 7
954 954 8
955 955 9
956 956 """,
957 957
958 958 r"""
959 959
960 960 In [144]: from pylab import *
961 961
962 962 In [145]: ion()
963 963
964 964 # use a semicolon to suppress the output
965 965 @savefig test_hist.png width=4in
966 966 In [151]: hist(np.random.randn(10000), 100);
967 967
968 968
969 969 @savefig test_plot.png width=4in
970 970 In [151]: plot(np.random.randn(10000), 'o');
971 971 """,
972 972
973 973 r"""
974 974 # use a semicolon to suppress the output
975 975 In [151]: plt.clf()
976 976
977 977 @savefig plot_simple.png width=4in
978 978 In [151]: plot([1,2,3])
979 979
980 980 @savefig hist_simple.png width=4in
981 981 In [151]: hist(np.random.randn(10000), 100);
982 982
983 983 """,
984 984 r"""
985 985 # update the current fig
986 986 In [151]: ylabel('number')
987 987
988 988 In [152]: title('normal distribution')
989 989
990 990
991 991 @savefig hist_with_text.png
992 992 In [153]: grid(True)
993 993
994 994 @doctest float
995 995 In [154]: 0.1 + 0.2
996 996 Out[154]: 0.3
997 997
998 998 @doctest float
999 999 In [155]: np.arange(16).reshape(4,4)
1000 1000 Out[155]:
1001 1001 array([[ 0, 1, 2, 3],
1002 1002 [ 4, 5, 6, 7],
1003 1003 [ 8, 9, 10, 11],
1004 1004 [12, 13, 14, 15]])
1005 1005
1006 1006 In [1]: x = np.arange(16, dtype=float).reshape(4,4)
1007 1007
1008 1008 In [2]: x[0,0] = np.inf
1009 1009
1010 1010 In [3]: x[0,1] = np.nan
1011 1011
1012 1012 @doctest float
1013 1013 In [4]: x
1014 1014 Out[4]:
1015 1015 array([[ inf, nan, 2., 3.],
1016 1016 [ 4., 5., 6., 7.],
1017 1017 [ 8., 9., 10., 11.],
1018 1018 [ 12., 13., 14., 15.]])
1019 1019
1020 1020
1021 1021 """,
1022 1022 ]
1023 1023 # skip local-file depending first example:
1024 1024 examples = examples[1:]
1025 1025
1026 1026 #ipython_directive.DEBUG = True # dbg
1027 1027 #options = dict(suppress=True) # dbg
1028 1028 options = dict()
1029 1029 for example in examples:
1030 1030 content = example.split('\n')
1031 1031 IPythonDirective('debug', arguments=None, options=options,
1032 1032 content=content, lineno=0,
1033 1033 content_offset=None, block_text=None,
1034 1034 state=None, state_machine=None,
1035 1035 )
1036 1036
1037 1037 # Run test suite as a script
1038 1038 if __name__=='__main__':
1039 1039 if not os.path.isdir('_static'):
1040 1040 os.mkdir('_static')
1041 1041 test()
1042 1042 print('All OK? Check figures in _static/')
General Comments 0
You need to be logged in to leave comments. Login now