##// END OF EJS Templates
Fixed a problem with evaluating indented lines in pure python code....
Ahmet Bakan -
Show More
@@ -1,822 +1,828 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Sphinx directive to support embedded IPython code.
3 3
4 4 This directive allows pasting of entire interactive IPython sessions, prompts
5 5 and all, and their code will actually get re-executed at doc build time, with
6 6 all prompts renumbered sequentially. It also allows you to input code as a pure
7 7 python input by giving the argument python to the directive. The output looks
8 8 like an interactive ipython section.
9 9
10 10 To enable this directive, simply list it in your Sphinx ``conf.py`` file
11 11 (making sure the directory where you placed it is visible to sphinx, as is
12 12 needed for all Sphinx directives).
13 13
14 14 By default this directive assumes that your prompts are unchanged IPython ones,
15 15 but this can be customized. The configurable options that can be placed in
16 16 conf.py are
17 17
18 18 ipython_savefig_dir:
19 19 The directory in which to save the figures. This is relative to the
20 20 Sphinx source directory. The default is `html_static_path`.
21 21 ipython_rgxin:
22 22 The compiled regular expression to denote the start of IPython input
23 23 lines. The default is re.compile('In \[(\d+)\]:\s?(.*)\s*'). You
24 24 shouldn't need to change this.
25 25 ipython_rgxout:
26 26 The compiled regular expression to denote the start of IPython output
27 27 lines. The default is re.compile('Out\[(\d+)\]:\s?(.*)\s*'). You
28 28 shouldn't need to change this.
29 29 ipython_promptin:
30 30 The string to represent the IPython input prompt in the generated ReST.
31 31 The default is 'In [%d]:'. This expects that the line numbers are used
32 32 in the prompt.
33 33 ipython_promptout:
34 34
35 35 The string to represent the IPython prompt in the generated ReST. The
36 36 default is 'Out [%d]:'. This expects that the line numbers are used
37 37 in the prompt.
38 38
39 39 ToDo
40 40 ----
41 41
42 42 - Turn the ad-hoc test() function into a real test suite.
43 43 - Break up ipython-specific functionality from matplotlib stuff into better
44 44 separated code.
45 45
46 46 Authors
47 47 -------
48 48
49 49 - John D Hunter: orignal author.
50 50 - Fernando Perez: refactoring, documentation, cleanups, port to 0.11.
51 51 - VΓ‘clavΕ milauer <eudoxos-AT-arcig.cz>: Prompt generalizations.
52 52 - Skipper Seabold, refactoring, cleanups, pure python addition
53 53 """
54 54
55 55 #-----------------------------------------------------------------------------
56 56 # Imports
57 57 #-----------------------------------------------------------------------------
58 58
59 59 # Stdlib
60 60 import cStringIO
61 61 import os
62 62 import re
63 63 import sys
64 64 import tempfile
65 65 import ast
66 66
67 67 # To keep compatibility with various python versions
68 68 try:
69 69 from hashlib import md5
70 70 except ImportError:
71 71 from md5 import md5
72 72
73 73 # Third-party
74 74 import matplotlib
75 75 import sphinx
76 76 from docutils.parsers.rst import directives
77 77 from docutils import nodes
78 78 from sphinx.util.compat import Directive
79 79
80 80 #matplotlib.use('Agg')
81 81
82 82 # Our own
83 83 from IPython import Config, InteractiveShell
84 84 from IPython.core.profiledir import ProfileDir
85 85 from IPython.utils import io
86 86
87 87 #-----------------------------------------------------------------------------
88 88 # Globals
89 89 #-----------------------------------------------------------------------------
90 90 # for tokenizing blocks
91 91 COMMENT, INPUT, OUTPUT = range(3)
92 92
93 93 #-----------------------------------------------------------------------------
94 94 # Functions and class declarations
95 95 #-----------------------------------------------------------------------------
96 96 def block_parser(part, rgxin, rgxout, fmtin, fmtout):
97 97 """
98 98 part is a string of ipython text, comprised of at most one
99 99 input, one ouput, comments, and blank lines. The block parser
100 100 parses the text into a list of::
101 101
102 102 blocks = [ (TOKEN0, data0), (TOKEN1, data1), ...]
103 103
104 104 where TOKEN is one of [COMMENT | INPUT | OUTPUT ] and
105 105 data is, depending on the type of token::
106 106
107 107 COMMENT : the comment string
108 108
109 109 INPUT: the (DECORATOR, INPUT_LINE, REST) where
110 110 DECORATOR: the input decorator (or None)
111 111 INPUT_LINE: the input as string (possibly multi-line)
112 112 REST : any stdout generated by the input line (not OUTPUT)
113 113
114 114
115 115 OUTPUT: the output string, possibly multi-line
116 116 """
117 117
118 118 block = []
119 119 lines = part.split('\n')
120 120 N = len(lines)
121 121 i = 0
122 122 decorator = None
123 123 while 1:
124 124
125 125 if i==N:
126 126 # nothing left to parse -- the last line
127 127 break
128 128
129 129 line = lines[i]
130 130 i += 1
131 131 line_stripped = line.strip()
132 132 if line_stripped.startswith('#'):
133 133 block.append((COMMENT, line))
134 134 continue
135 135
136 136 if line_stripped.startswith('@'):
137 137 # we're assuming at most one decorator -- may need to
138 138 # rethink
139 139 decorator = line_stripped
140 140 continue
141 141
142 142 # does this look like an input line?
143 143 matchin = rgxin.match(line)
144 144 if matchin:
145 145 lineno, inputline = int(matchin.group(1)), matchin.group(2)
146 146
147 147 # the ....: continuation string
148 148 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
149 149 Nc = len(continuation)
150 150 # input lines can continue on for more than one line, if
151 151 # we have a '\' line continuation char or a function call
152 152 # echo line 'print'. The input line can only be
153 153 # terminated by the end of the block or an output line, so
154 154 # we parse out the rest of the input line if it is
155 155 # multiline as well as any echo text
156 156
157 157 rest = []
158 158 while i<N:
159 159
160 160 # look ahead; if the next line is blank, or a comment, or
161 161 # an output line, we're done
162 162
163 163 nextline = lines[i]
164 164 matchout = rgxout.match(nextline)
165 165 #print "nextline=%s, continuation=%s, starts=%s"%(nextline, continuation, nextline.startswith(continuation))
166 166 if matchout or nextline.startswith('#'):
167 167 break
168 168 elif nextline.startswith(continuation):
169 169 inputline += '\n' + nextline[Nc:]
170 170 else:
171 171 rest.append(nextline)
172 172 i+= 1
173 173
174 174 block.append((INPUT, (decorator, inputline, '\n'.join(rest))))
175 175 continue
176 176
177 177 # if it looks like an output line grab all the text to the end
178 178 # of the block
179 179 matchout = rgxout.match(line)
180 180 if matchout:
181 181 lineno, output = int(matchout.group(1)), matchout.group(2)
182 182 if i<N-1:
183 183 output = '\n'.join([output] + lines[i:])
184 184
185 185 block.append((OUTPUT, output))
186 186 break
187 187
188 188 return block
189 189
190 190 class EmbeddedSphinxShell(object):
191 191 """An embedded IPython instance to run inside Sphinx"""
192 192
193 193 def __init__(self):
194 194
195 195 self.cout = cStringIO.StringIO()
196 196
197 197
198 198 # Create config object for IPython
199 199 config = Config()
200 200 config.Global.display_banner = False
201 201 config.Global.exec_lines = ['import numpy as np',
202 202 'from pylab import *'
203 203 ]
204 204 config.InteractiveShell.autocall = False
205 205 config.InteractiveShell.autoindent = False
206 206 config.InteractiveShell.colors = 'NoColor'
207 207
208 208 # create a profile so instance history isn't saved
209 209 tmp_profile_dir = tempfile.mkdtemp(prefix='profile_')
210 210 profname = 'auto_profile_sphinx_build'
211 211 pdir = os.path.join(tmp_profile_dir,profname)
212 212 profile = ProfileDir.create_profile_dir(pdir)
213 213
214 214 # Create and initialize ipython, but don't start its mainloop
215 215 IP = InteractiveShell.instance(config=config, profile_dir=profile)
216 216 # io.stdout redirect must be done *after* instantiating InteractiveShell
217 217 io.stdout = self.cout
218 218 io.stderr = self.cout
219 219
220 220 # For debugging, so we can see normal output, use this:
221 221 #from IPython.utils.io import Tee
222 222 #io.stdout = Tee(self.cout, channel='stdout') # dbg
223 223 #io.stderr = Tee(self.cout, channel='stderr') # dbg
224 224
225 225 # Store a few parts of IPython we'll need.
226 226 self.IP = IP
227 227 self.user_ns = self.IP.user_ns
228 228 self.user_global_ns = self.IP.user_global_ns
229 229
230 230 self.input = ''
231 231 self.output = ''
232 232
233 233 self.is_verbatim = False
234 234 self.is_doctest = False
235 235 self.is_suppress = False
236 236
237 237 # on the first call to the savefig decorator, we'll import
238 238 # pyplot as plt so we can make a call to the plt.gcf().savefig
239 239 self._pyplot_imported = False
240 240
241 241 def clear_cout(self):
242 242 self.cout.seek(0)
243 243 self.cout.truncate(0)
244 244
245 245 def process_input_line(self, line, store_history=True):
246 246 """process the input, capturing stdout"""
247 247 #print "input='%s'"%self.input
248 248 stdout = sys.stdout
249 249 splitter = self.IP.input_splitter
250 250 try:
251 251 sys.stdout = self.cout
252 252 splitter.push(line)
253 253 more = splitter.push_accepts_more()
254 254 if not more:
255 255 source_raw = splitter.source_raw_reset()[1]
256 256 self.IP.run_cell(source_raw, store_history=store_history)
257 257 finally:
258 258 sys.stdout = stdout
259 259
260 260 def process_image(self, decorator):
261 261 """
262 262 # build out an image directive like
263 263 # .. image:: somefile.png
264 264 # :width 4in
265 265 #
266 266 # from an input like
267 267 # savefig somefile.png width=4in
268 268 """
269 269 savefig_dir = self.savefig_dir
270 270 source_dir = self.source_dir
271 271 saveargs = decorator.split(' ')
272 272 filename = saveargs[1]
273 273 # insert relative path to image file in source
274 274 outfile = os.path.relpath(os.path.join(savefig_dir,filename),
275 275 source_dir)
276 276
277 277 imagerows = ['.. image:: %s'%outfile]
278 278
279 279 for kwarg in saveargs[2:]:
280 280 arg, val = kwarg.split('=')
281 281 arg = arg.strip()
282 282 val = val.strip()
283 283 imagerows.append(' :%s: %s'%(arg, val))
284 284
285 285 image_file = os.path.basename(outfile) # only return file name
286 286 image_directive = '\n'.join(imagerows)
287 287 return image_file, image_directive
288 288
289 289
290 290 # Callbacks for each type of token
291 291 def process_input(self, data, input_prompt, lineno):
292 292 """Process data block for INPUT token."""
293 293 decorator, input, rest = data
294 294 image_file = None
295 295 image_directive = None
296 296 #print 'INPUT:', data # dbg
297 297 is_verbatim = decorator=='@verbatim' or self.is_verbatim
298 298 is_doctest = decorator=='@doctest' or self.is_doctest
299 299 is_suppress = decorator=='@suppress' or self.is_suppress
300 300 is_savefig = decorator is not None and \
301 301 decorator.startswith('@savefig')
302 302
303 303 input_lines = input.split('\n')
304 304 if len(input_lines) > 1:
305 305 if input_lines[-1] != "":
306 306 input_lines.append('') # make sure there's a blank line
307 307 # so splitter buffer gets reset
308 308
309 309 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
310 310 Nc = len(continuation)
311 311
312 312 if is_savefig:
313 313 image_file, image_directive = self.process_image(decorator)
314 314
315 315 ret = []
316 316 is_semicolon = False
317 317
318 318 for i, line in enumerate(input_lines):
319 319 if line.endswith(';'):
320 320 is_semicolon = True
321 321
322 322 if i==0:
323 323 # process the first input line
324 324 if is_verbatim:
325 325 self.process_input_line('')
326 326 self.IP.execution_count += 1 # increment it anyway
327 327 else:
328 328 # only submit the line in non-verbatim mode
329 329 self.process_input_line(line, store_history=True)
330 330 formatted_line = '%s %s'%(input_prompt, line)
331 331 else:
332 332 # process a continuation line
333 333 if not is_verbatim:
334 334 self.process_input_line(line, store_history=True)
335 335
336 336 formatted_line = '%s %s'%(continuation, line)
337 337
338 338 if not is_suppress:
339 339 ret.append(formatted_line)
340 340
341 341 if not is_suppress and len(rest.strip()) and is_verbatim:
342 342 # the "rest" is the standard output of the
343 343 # input, which needs to be added in
344 344 # verbatim mode
345 345 ret.append(rest)
346 346
347 347 self.cout.seek(0)
348 348 output = self.cout.read()
349 349 if not is_suppress and not is_semicolon:
350 350 ret.append(output)
351 351 elif is_semicolon: # get spacing right
352 352 ret.append('')
353 353
354 354 self.cout.truncate(0)
355 355 return (ret, input_lines, output, is_doctest, image_file,
356 356 image_directive)
357 357 #print 'OUTPUT', output # dbg
358 358
359 359 def process_output(self, data, output_prompt,
360 360 input_lines, output, is_doctest, image_file):
361 361 """Process data block for OUTPUT token."""
362 362 if is_doctest:
363 363 submitted = data.strip()
364 364 found = output
365 365 if found is not None:
366 366 found = found.strip()
367 367
368 368 # XXX - fperez: in 0.11, 'output' never comes with the prompt
369 369 # in it, just the actual output text. So I think all this code
370 370 # can be nuked...
371 371
372 372 # the above comment does not appear to be accurate... (minrk)
373 373
374 374 ind = found.find(output_prompt)
375 375 if ind<0:
376 376 e='output prompt="%s" does not match out line=%s' % \
377 377 (output_prompt, found)
378 378 raise RuntimeError(e)
379 379 found = found[len(output_prompt):].strip()
380 380
381 381 if found!=submitted:
382 382 e = ('doctest failure for input_lines="%s" with '
383 383 'found_output="%s" and submitted output="%s"' %
384 384 (input_lines, found, submitted) )
385 385 raise RuntimeError(e)
386 386 #print 'doctest PASSED for input_lines="%s" with found_output="%s" and submitted output="%s"'%(input_lines, found, submitted)
387 387
388 388 def process_comment(self, data):
389 389 """Process data fPblock for COMMENT token."""
390 390 if not self.is_suppress:
391 391 return [data]
392 392
393 393 def save_image(self, image_file):
394 394 """
395 395 Saves the image file to disk.
396 396 """
397 397 self.ensure_pyplot()
398 398 command = 'plt.gcf().savefig("%s")'%image_file
399 399 #print 'SAVEFIG', command # dbg
400 400 self.process_input_line('bookmark ipy_thisdir', store_history=False)
401 401 self.process_input_line('cd -b ipy_savedir', store_history=False)
402 402 self.process_input_line(command, store_history=False)
403 403 self.process_input_line('cd -b ipy_thisdir', store_history=False)
404 404 self.process_input_line('bookmark -d ipy_thisdir', store_history=False)
405 405 self.clear_cout()
406 406
407 407
408 408 def process_block(self, block):
409 409 """
410 410 process block from the block_parser and return a list of processed lines
411 411 """
412 412 ret = []
413 413 output = None
414 414 input_lines = None
415 415 lineno = self.IP.execution_count
416 416
417 417 input_prompt = self.promptin%lineno
418 418 output_prompt = self.promptout%lineno
419 419 image_file = None
420 420 image_directive = None
421 421
422 422 for token, data in block:
423 423 if token==COMMENT:
424 424 out_data = self.process_comment(data)
425 425 elif token==INPUT:
426 426 (out_data, input_lines, output, is_doctest, image_file,
427 427 image_directive) = \
428 428 self.process_input(data, input_prompt, lineno)
429 429 elif token==OUTPUT:
430 430 out_data = \
431 431 self.process_output(data, output_prompt,
432 432 input_lines, output, is_doctest,
433 433 image_file)
434 434 if out_data:
435 435 ret.extend(out_data)
436 436
437 437 # save the image files
438 438 if image_file is not None:
439 439 self.save_image(image_file)
440 440
441 441 return ret, image_directive
442 442
443 443 def ensure_pyplot(self):
444 444 if self._pyplot_imported:
445 445 return
446 446 self.process_input_line('import matplotlib.pyplot as plt',
447 447 store_history=False)
448 448
449 449 def process_pure_python(self, content):
450 450 """
451 451 content is a list of strings. it is unedited directive conent
452 452
453 453 This runs it line by line in the InteractiveShell, prepends
454 454 prompts as needed capturing stderr and stdout, then returns
455 455 the content as a list as if it were ipython code
456 456 """
457 457 output = []
458 458 savefig = False # keep up with this to clear figure
459 459 multiline = False # to handle line continuation
460 460 multiline_start = None
461 461 fmtin = self.promptin
462 462
463 463 ct = 0
464 464
465 465 for lineno, line in enumerate(content):
466 466
467 467 line_stripped = line.strip()
468 468 if not len(line):
469 469 output.append(line)
470 470 continue
471 471
472 472 # handle decorators
473 473 if line_stripped.startswith('@'):
474 474 output.extend([line])
475 475 if 'savefig' in line:
476 476 savefig = True # and need to clear figure
477 477 continue
478 478
479 479 # handle comments
480 480 if line_stripped.startswith('#'):
481 481 output.extend([line])
482 482 continue
483 483
484 484 # deal with lines checking for multiline
485 485 continuation = u' %s:'% ''.join(['.']*(len(str(ct))+2))
486 486 if not multiline:
487 487 modified = u"%s %s" % (fmtin % ct, line_stripped)
488 488 output.append(modified)
489 489 ct += 1
490 490 try:
491 491 ast.parse(line_stripped)
492 492 output.append(u'')
493 493 except Exception: # on a multiline
494 494 multiline = True
495 495 multiline_start = lineno
496 496 else: # still on a multiline
497 497 modified = u'%s %s' % (continuation, line)
498 498 output.append(modified)
499
500 # if the next line is indented, it should be part of multiline
501 if len(content) > lineno + 1:
502 nextline = content[lineno + 1]
503 if len(nextline) - len(nextline.lstrip()) > 3:
504 continue
499 505 try:
500 506 mod = ast.parse(
501 507 '\n'.join(content[multiline_start:lineno+1]))
502 508 if isinstance(mod.body[0], ast.FunctionDef):
503 509 # check to see if we have the whole function
504 510 for element in mod.body[0].body:
505 511 if isinstance(element, ast.Return):
506 512 multiline = False
507 513 else:
508 514 output.append(u'')
509 515 multiline = False
510 516 except Exception:
511 517 pass
512 518
513 519 if savefig: # clear figure if plotted
514 520 self.ensure_pyplot()
515 521 self.process_input_line('plt.clf()', store_history=False)
516 522 self.clear_cout()
517 523 savefig = False
518 524
519 525 return output
520 526
521 527 class IpythonDirective(Directive):
522 528
523 529 has_content = True
524 530 required_arguments = 0
525 531 optional_arguments = 4 # python, suppress, verbatim, doctest
526 532 final_argumuent_whitespace = True
527 533 option_spec = { 'python': directives.unchanged,
528 534 'suppress' : directives.flag,
529 535 'verbatim' : directives.flag,
530 536 'doctest' : directives.flag,
531 537 }
532 538
533 539 shell = EmbeddedSphinxShell()
534 540
535 541 seen_docs = set()
536 542
537 543 def get_config_options(self):
538 544 # contains sphinx configuration variables
539 545 config = self.state.document.settings.env.config
540 546
541 547 # get config variables to set figure output directory
542 548 confdir = self.state.document.settings.env.app.confdir
543 549 savefig_dir = config.ipython_savefig_dir
544 550 source_dir = os.path.dirname(self.state.document.current_source)
545 551 if savefig_dir is None:
546 552 savefig_dir = config.html_static_path
547 553 if isinstance(savefig_dir, list):
548 554 savefig_dir = savefig_dir[0] # safe to assume only one path?
549 555 savefig_dir = os.path.join(confdir, savefig_dir)
550 556
551 557 # get regex and prompt stuff
552 558 rgxin = config.ipython_rgxin
553 559 rgxout = config.ipython_rgxout
554 560 promptin = config.ipython_promptin
555 561 promptout = config.ipython_promptout
556 562
557 563 return savefig_dir, source_dir, rgxin, rgxout, promptin, promptout
558 564
559 565 def setup(self):
560 566 # reset the execution count if we haven't processed this doc
561 567 #NOTE: this may be borked if there are multiple seen_doc tmp files
562 568 #check time stamp?
563 569
564 570
565 571 if not self.state.document.current_source in self.seen_docs:
566 572 self.shell.IP.history_manager.reset()
567 573 self.shell.IP.execution_count = 1
568 574 self.seen_docs.add(self.state.document.current_source)
569 575
570 576
571 577
572 578 # get config values
573 579 (savefig_dir, source_dir, rgxin,
574 580 rgxout, promptin, promptout) = self.get_config_options()
575 581
576 582 # and attach to shell so we don't have to pass them around
577 583 self.shell.rgxin = rgxin
578 584 self.shell.rgxout = rgxout
579 585 self.shell.promptin = promptin
580 586 self.shell.promptout = promptout
581 587 self.shell.savefig_dir = savefig_dir
582 588 self.shell.source_dir = source_dir
583 589
584 590 # setup bookmark for saving figures directory
585 591
586 592 self.shell.process_input_line('bookmark ipy_savedir %s'%savefig_dir,
587 593 store_history=False)
588 594 self.shell.clear_cout()
589 595
590 596 return rgxin, rgxout, promptin, promptout
591 597
592 598
593 599 def teardown(self):
594 600 # delete last bookmark
595 601 self.shell.process_input_line('bookmark -d ipy_savedir',
596 602 store_history=False)
597 603 self.shell.clear_cout()
598 604
599 605 def run(self):
600 606 debug = False
601 607
602 608 #TODO, any reason block_parser can't be a method of embeddable shell
603 609 # then we wouldn't have to carry these around
604 610 rgxin, rgxout, promptin, promptout = self.setup()
605 611
606 612 options = self.options
607 613 self.shell.is_suppress = 'suppress' in options
608 614 self.shell.is_doctest = 'doctest' in options
609 615 self.shell.is_verbatim = 'verbatim' in options
610 616
611 617
612 618 # handle pure python code
613 619 if 'python' in self.arguments:
614 620 content = self.content
615 621 self.content = self.shell.process_pure_python(content)
616 622
617 623 parts = '\n'.join(self.content).split('\n\n')
618 624
619 625 lines = ['.. code-block:: ipython','']
620 626 figures = []
621 627
622 628 for part in parts:
623 629
624 630 block = block_parser(part, rgxin, rgxout, promptin, promptout)
625 631
626 632 if len(block):
627 633 rows, figure = self.shell.process_block(block)
628 634 for row in rows:
629 635 lines.extend([' %s'%line for line in row.split('\n')])
630 636
631 637 if figure is not None:
632 638 figures.append(figure)
633 639
634 640 #text = '\n'.join(lines)
635 641 #figs = '\n'.join(figures)
636 642
637 643 for figure in figures:
638 644 lines.append('')
639 645 lines.extend(figure.split('\n'))
640 646 lines.append('')
641 647
642 648 #print lines
643 649 if len(lines)>2:
644 650 if debug:
645 651 print '\n'.join(lines)
646 652 else: #NOTE: this raises some errors, what's it for?
647 653 #print 'INSERTING %d lines'%len(lines)
648 654 self.state_machine.insert_input(
649 655 lines, self.state_machine.input_lines.source(0))
650 656
651 657 text = '\n'.join(lines)
652 658 txtnode = nodes.literal_block(text, text)
653 659 txtnode['language'] = 'ipython'
654 660 #imgnode = nodes.image(figs)
655 661
656 662 # cleanup
657 663 self.teardown()
658 664
659 665 return []#, imgnode]
660 666
661 667 # Enable as a proper Sphinx directive
662 668 def setup(app):
663 669 setup.app = app
664 670
665 671 app.add_directive('ipython', IpythonDirective)
666 672 app.add_config_value('ipython_savefig_dir', None, True)
667 673 app.add_config_value('ipython_rgxin',
668 674 re.compile('In \[(\d+)\]:\s?(.*)\s*'), True)
669 675 app.add_config_value('ipython_rgxout',
670 676 re.compile('Out\[(\d+)\]:\s?(.*)\s*'), True)
671 677 app.add_config_value('ipython_promptin', 'In [%d]:', True)
672 678 app.add_config_value('ipython_promptout', 'Out[%d]:', True)
673 679
674 680
675 681 # Simple smoke test, needs to be converted to a proper automatic test.
676 682 def test():
677 683
678 684 examples = [
679 685 r"""
680 686 In [9]: pwd
681 687 Out[9]: '/home/jdhunter/py4science/book'
682 688
683 689 In [10]: cd bookdata/
684 690 /home/jdhunter/py4science/book/bookdata
685 691
686 692 In [2]: from pylab import *
687 693
688 694 In [2]: ion()
689 695
690 696 In [3]: im = imread('stinkbug.png')
691 697
692 698 @savefig mystinkbug.png width=4in
693 699 In [4]: imshow(im)
694 700 Out[4]: <matplotlib.image.AxesImage object at 0x39ea850>
695 701
696 702 """,
697 703 r"""
698 704
699 705 In [1]: x = 'hello world'
700 706
701 707 # string methods can be
702 708 # used to alter the string
703 709 @doctest
704 710 In [2]: x.upper()
705 711 Out[2]: 'HELLO WORLD'
706 712
707 713 @verbatim
708 714 In [3]: x.st<TAB>
709 715 x.startswith x.strip
710 716 """,
711 717 r"""
712 718
713 719 In [130]: url = 'http://ichart.finance.yahoo.com/table.csv?s=CROX\
714 720 .....: &d=9&e=22&f=2009&g=d&a=1&br=8&c=2006&ignore=.csv'
715 721
716 722 In [131]: print url.split('&')
717 723 ['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']
718 724
719 725 In [60]: import urllib
720 726
721 727 """,
722 728 r"""\
723 729
724 730 In [133]: import numpy.random
725 731
726 732 @suppress
727 733 In [134]: numpy.random.seed(2358)
728 734
729 735 @doctest
730 736 In [135]: numpy.random.rand(10,2)
731 737 Out[135]:
732 738 array([[ 0.64524308, 0.59943846],
733 739 [ 0.47102322, 0.8715456 ],
734 740 [ 0.29370834, 0.74776844],
735 741 [ 0.99539577, 0.1313423 ],
736 742 [ 0.16250302, 0.21103583],
737 743 [ 0.81626524, 0.1312433 ],
738 744 [ 0.67338089, 0.72302393],
739 745 [ 0.7566368 , 0.07033696],
740 746 [ 0.22591016, 0.77731835],
741 747 [ 0.0072729 , 0.34273127]])
742 748
743 749 """,
744 750
745 751 r"""
746 752 In [106]: print x
747 753 jdh
748 754
749 755 In [109]: for i in range(10):
750 756 .....: print i
751 757 .....:
752 758 .....:
753 759 0
754 760 1
755 761 2
756 762 3
757 763 4
758 764 5
759 765 6
760 766 7
761 767 8
762 768 9
763 769 """,
764 770
765 771 r"""
766 772
767 773 In [144]: from pylab import *
768 774
769 775 In [145]: ion()
770 776
771 777 # use a semicolon to suppress the output
772 778 @savefig test_hist.png width=4in
773 779 In [151]: hist(np.random.randn(10000), 100);
774 780
775 781
776 782 @savefig test_plot.png width=4in
777 783 In [151]: plot(np.random.randn(10000), 'o');
778 784 """,
779 785
780 786 r"""
781 787 # use a semicolon to suppress the output
782 788 In [151]: plt.clf()
783 789
784 790 @savefig plot_simple.png width=4in
785 791 In [151]: plot([1,2,3])
786 792
787 793 @savefig hist_simple.png width=4in
788 794 In [151]: hist(np.random.randn(10000), 100);
789 795
790 796 """,
791 797 r"""
792 798 # update the current fig
793 799 In [151]: ylabel('number')
794 800
795 801 In [152]: title('normal distribution')
796 802
797 803
798 804 @savefig hist_with_text.png
799 805 In [153]: grid(True)
800 806
801 807 """,
802 808 ]
803 809 # skip local-file depending first example:
804 810 examples = examples[1:]
805 811
806 812 #ipython_directive.DEBUG = True # dbg
807 813 #options = dict(suppress=True) # dbg
808 814 options = dict()
809 815 for example in examples:
810 816 content = example.split('\n')
811 817 ipython_directive('debug', arguments=None, options=options,
812 818 content=content, lineno=0,
813 819 content_offset=None, block_text=None,
814 820 state=None, state_machine=None,
815 821 )
816 822
817 823 # Run test suite as a script
818 824 if __name__=='__main__':
819 825 if not os.path.isdir('_static'):
820 826 os.mkdir('_static')
821 827 test()
822 828 print 'All OK? Check figures in _static/'
General Comments 0
You need to be logged in to leave comments. Login now