##// END OF EJS Templates
Keep track of seen documents to reset line counts across pages
Skipper Seabold -
Show More
@@ -1,813 +1,835 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 store_history = True
318 318
319 319 for i, line in enumerate(input_lines):
320 320 if line.endswith(';'):
321 321 is_semicolon = True
322 322 if is_suppress:
323 323 store_history = False
324 324
325 325 if i==0:
326 326 # process the first input line
327 327 if is_verbatim:
328 328 self.process_input_line('')
329 329 self.IP.execution_count += 1 # increment it anyway
330 330 else:
331 331 # only submit the line in non-verbatim mode
332 332 self.process_input_line(line, store_history=store_history)
333 333 formatted_line = '%s %s'%(input_prompt, line)
334 334 else:
335 335 # process a continuation line
336 336 if not is_verbatim:
337 337 self.process_input_line(line, store_history=store_history)
338 338
339 339 formatted_line = '%s %s'%(continuation, line)
340 340
341 341 if not is_suppress:
342 342 ret.append(formatted_line)
343 343
344 344 if not is_suppress and len(rest.strip()) and is_verbatim:
345 345 # the "rest" is the standard output of the
346 346 # input, which needs to be added in
347 347 # verbatim mode
348 348 ret.append(rest)
349 349
350 350 self.cout.seek(0)
351 351 output = self.cout.read()
352 352 if not is_suppress and not is_semicolon:
353 353 ret.append(output)
354 354 elif is_semicolon: # get spacing right
355 355 ret.append('')
356 356
357 357 self.cout.truncate(0)
358 358 return (ret, input_lines, output, is_doctest, image_file,
359 359 image_directive)
360 360 #print 'OUTPUT', output # dbg
361 361
362 362 def process_output(self, data, output_prompt,
363 363 input_lines, output, is_doctest, image_file):
364 364 """Process data block for OUTPUT token."""
365 365 if is_doctest:
366 366 submitted = data.strip()
367 367 found = output
368 368 if found is not None:
369 369 found = found.strip()
370 370
371 371 # XXX - fperez: in 0.11, 'output' never comes with the prompt
372 372 # in it, just the actual output text. So I think all this code
373 373 # can be nuked...
374 374
375 375 # the above comment does not appear to be accurate... (minrk)
376 376
377 377 ind = found.find(output_prompt)
378 378 if ind<0:
379 379 e='output prompt="%s" does not match out line=%s' % \
380 380 (output_prompt, found)
381 381 raise RuntimeError(e)
382 382 found = found[len(output_prompt):].strip()
383 383
384 384 if found!=submitted:
385 385 e = ('doctest failure for input_lines="%s" with '
386 386 'found_output="%s" and submitted output="%s"' %
387 387 (input_lines, found, submitted) )
388 388 raise RuntimeError(e)
389 389 #print 'doctest PASSED for input_lines="%s" with found_output="%s" and submitted output="%s"'%(input_lines, found, submitted)
390 390
391 391 def process_comment(self, data):
392 392 """Process data fPblock for COMMENT token."""
393 393 if not self.is_suppress:
394 394 return [data]
395 395
396 396 def save_image(self, image_file):
397 397 """
398 398 Saves the image file to disk.
399 399 """
400 400 self.ensure_pyplot()
401 401 command = 'plt.gcf().savefig("%s")'%image_file
402 402 #print 'SAVEFIG', command # dbg
403 403 self.process_input_line('bookmark ipy_thisdir', store_history=False)
404 404 self.process_input_line('cd -b ipy_savedir', store_history=False)
405 405 self.process_input_line(command, store_history=False)
406 406 self.process_input_line('cd -b ipy_thisdir', store_history=False)
407 407 self.process_input_line('bookmark -d ipy_thisdir', store_history=False)
408 408 self.clear_cout()
409 409
410 410
411 411 def process_block(self, block):
412 412 """
413 413 process block from the block_parser and return a list of processed lines
414 414 """
415 415 ret = []
416 416 output = None
417 417 input_lines = None
418 418 lineno = self.IP.execution_count
419 419
420 420 input_prompt = self.promptin%lineno
421 421 output_prompt = self.promptout%lineno
422 422 image_file = None
423 423 image_directive = None
424 424
425 425 for token, data in block:
426 426 if token==COMMENT:
427 427 out_data = self.process_comment(data)
428 428 elif token==INPUT:
429 429 (out_data, input_lines, output, is_doctest, image_file,
430 430 image_directive) = \
431 431 self.process_input(data, input_prompt, lineno)
432 432 elif token==OUTPUT:
433 433 out_data = \
434 434 self.process_output(data, output_prompt,
435 435 input_lines, output, is_doctest,
436 436 image_file)
437 437 if out_data:
438 438 ret.extend(out_data)
439 439
440 440 # save the image files
441 441 if image_file is not None:
442 442 self.save_image(image_file)
443 443
444 444 return ret, image_directive
445 445
446 446 def ensure_pyplot(self):
447 447 if self._pyplot_imported:
448 448 return
449 449 self.process_input_line('import matplotlib.pyplot as plt',
450 450 store_history=False)
451 451
452 452 def process_pure_python(self, content):
453 453 """
454 454 content is a list of strings. it is unedited directive conent
455 455
456 456 This runs it line by line in the InteractiveShell, prepends
457 457 prompts as needed capturing stderr and stdout, then returns
458 458 the content as a list as if it were ipython code
459 459 """
460 460 output = []
461 461 savefig = False # keep up with this to clear figure
462 462 multiline = False # to handle line continuation
463 463 multiline_start = None
464 464 fmtin = self.promptin
465 465
466 466 ct = 0
467 467
468 468 for lineno, line in enumerate(content):
469 469
470 470 line_stripped = line.strip()
471 471 if not len(line):
472 472 output.append(line)
473 473 continue
474 474
475 475 # handle decorators
476 476 if line_stripped.startswith('@'):
477 477 output.extend([line])
478 478 if 'savefig' in line:
479 479 savefig = True # and need to clear figure
480 480 continue
481 481
482 482 # handle comments
483 483 if line_stripped.startswith('#'):
484 484 output.extend([line])
485 485 continue
486 486
487 487 # deal with lines checking for multiline
488 488 continuation = u' %s:'% ''.join(['.']*(len(str(ct))+2))
489 489 if not multiline:
490 490 modified = u"%s %s" % (fmtin % ct, line_stripped)
491 491 output.append(modified)
492 492 ct += 1
493 493 try:
494 494 ast.parse(line_stripped)
495 495 output.append(u'')
496 496 except Exception: # on a multiline
497 497 multiline = True
498 498 multiline_start = lineno
499 499 if line_stripped.startswith('def '):
500 500 is_function = True
501 501 else: # still on a multiline
502 502 modified = u'%s %s' % (continuation, line)
503 503 output.append(modified)
504 504 try:
505 505 mod = ast.parse(
506 506 '\n'.join(content[multiline_start:lineno+1]))
507 507 if isinstance(mod.body[0], ast.FunctionDef):
508 508 # check to see if we have the whole function
509 509 for element in mod.body[0].body:
510 510 if isinstance(element, ast.Return):
511 511 multiline = False
512 512 else:
513 513 output.append(u'')
514 514 multiline = False
515 515 except Exception:
516 516 pass
517 517
518 518 if savefig: # clear figure if plotted
519 519 self.ensure_pyplot()
520 520 self.process_input_line('plt.clf()', store_history=False)
521 521 self.clear_cout()
522 522 savefig = False
523 523
524 524 return output
525 525
526 526 class IpythonDirective(Directive):
527 527
528 528 has_content = True
529 529 required_arguments = 0
530 530 optional_arguments = 4 # python, suppress, verbatim, doctest
531 531 final_argumuent_whitespace = True
532 532 option_spec = { 'python': directives.unchanged,
533 533 'suppress' : directives.flag,
534 534 'verbatim' : directives.flag,
535 535 'doctest' : directives.flag,
536 536 }
537 537
538 538 shell = EmbeddedSphinxShell()
539 539
540 540 def get_config_options(self):
541 541 # contains sphinx configuration variables
542 542 config = self.state.document.settings.env.config
543 543
544 544 # get config variables to set figure output directory
545 545 confdir = self.state.document.settings.env.app.confdir
546 546 savefig_dir = config.ipython_savefig_dir
547 547 source_dir = os.path.dirname(self.state.document.current_source)
548 548 if savefig_dir is None:
549 549 savefig_dir = config.html_static_path
550 550 if isinstance(savefig_dir, list):
551 551 savefig_dir = savefig_dir[0] # safe to assume only one path?
552 552 savefig_dir = os.path.join(confdir, savefig_dir)
553 553
554 554 # get regex and prompt stuff
555 555 rgxin = config.ipython_rgxin
556 556 rgxout = config.ipython_rgxout
557 557 promptin = config.ipython_promptin
558 558 promptout = config.ipython_promptout
559 559
560 560 return savefig_dir, source_dir, rgxin, rgxout, promptin, promptout
561 561
562 562 def setup(self):
563 # reset the execution count if we haven't processed this doc
564 #NOTE: this may be borked if there are multiple seen_doc tmp files
565 #check time stamp?
566 seen_docs = [i for i in os.listdir(tempfile.tempdir)
567 if i.startswith('seen_doc')]
568 if seen_docs:
569 fname = os.path.join(tempfile.tempdir, seen_docs[0])
570 docs = open(fname).read().split('\n')
571 if not self.state.document.current_source in docs:
572 self.shell.IP.history_manager.reset()
573 self.shell.IP.execution_count = 1
574 else: # haven't processed any docs yet
575 docs = []
576
577
563 578 # get config values
564 579 (savefig_dir, source_dir, rgxin,
565 580 rgxout, promptin, promptout) = self.get_config_options()
566 581
567 582 # and attach to shell so we don't have to pass them around
568 583 self.shell.rgxin = rgxin
569 584 self.shell.rgxout = rgxout
570 585 self.shell.promptin = promptin
571 586 self.shell.promptout = promptout
572 587 self.shell.savefig_dir = savefig_dir
573 588 self.shell.source_dir = source_dir
574 589
575 590 # setup bookmark for saving figures directory
576 591
577 592 self.shell.process_input_line('bookmark ipy_savedir %s'%savefig_dir,
578 593 store_history=False)
579 594 self.shell.clear_cout()
580 595
596 # write the filename to a tempfile because it's been "seen" now
597 if not self.state.document.current_source in docs:
598 fd, fname = tempfile.mkstemp(prefix="seen_doc", text=True)
599 fout = open(fname, 'a')
600 fout.write(self.state.document.current_source+'\n')
601 fout.close()
602
581 603 return rgxin, rgxout, promptin, promptout
582 604
583 605
584 606 def teardown(self):
585 607 # delete last bookmark
586 608 self.shell.process_input_line('bookmark -d ipy_savedir',
587 609 store_history=False)
588 610 self.shell.clear_cout()
589 611
590 612 def run(self):
591 613 debug = False
592 614
593 615 #TODO, any reason block_parser can't be a method of embeddable shell
594 616 # then we wouldn't have to carry these around
595 617 rgxin, rgxout, promptin, promptout = self.setup()
596 618
597 619 options = self.options
598 620 self.shell.is_suppress = 'suppress' in options
599 621 self.shell.is_doctest = 'doctest' in options
600 622 self.shell.is_verbatim = 'verbatim' in options
601 623
602 624
603 625 # handle pure python code
604 626 if 'python' in self.arguments:
605 627 content = self.content
606 628 self.content = self.shell.process_pure_python(content)
607 629
608 630 parts = '\n'.join(self.content).split('\n\n')
609 631
610 632 lines = ['.. code-block:: ipython','']
611 633 figures = []
612 634
613 635 for part in parts:
614 636
615 637 block = block_parser(part, rgxin, rgxout, promptin, promptout)
616 638
617 639 if len(block):
618 640 rows, figure = self.shell.process_block(block)
619 641 for row in rows:
620 642 lines.extend([' %s'%line for line in row.split('\n')])
621 643
622 644 if figure is not None:
623 645 figures.append(figure)
624 646
625 647 #text = '\n'.join(lines)
626 648 #figs = '\n'.join(figures)
627 649
628 650 for figure in figures:
629 651 lines.append('')
630 652 lines.extend(figure.split('\n'))
631 653 lines.append('')
632 654
633 655 #print lines
634 656 if len(lines)>2:
635 657 if debug:
636 658 print '\n'.join(lines)
637 659 else: #NOTE: this raises some errors, what's it for?
638 660 #print 'INSERTING %d lines'%len(lines)
639 661 self.state_machine.insert_input(
640 662 lines, self.state_machine.input_lines.source(0))
641 663
642 664 text = '\n'.join(lines)
643 665 txtnode = nodes.literal_block(text, text)
644 666 txtnode['language'] = 'ipython'
645 667 #imgnode = nodes.image(figs)
646 668
647 669 # cleanup
648 670 self.teardown()
649 671
650 672 return []#, imgnode]
651 673
652 674 # Enable as a proper Sphinx directive
653 675 def setup(app):
654 676 setup.app = app
655 677
656 678 app.add_directive('ipython', IpythonDirective)
657 679 app.add_config_value('ipython_savefig_dir', None, True)
658 680 app.add_config_value('ipython_rgxin',
659 681 re.compile('In \[(\d+)\]:\s?(.*)\s*'), True)
660 682 app.add_config_value('ipython_rgxout',
661 683 re.compile('Out\[(\d+)\]:\s?(.*)\s*'), True)
662 684 app.add_config_value('ipython_promptin', 'In [%d]:', True)
663 685 app.add_config_value('ipython_promptout', 'Out[%d]:', True)
664 686
665 687
666 688 # Simple smoke test, needs to be converted to a proper automatic test.
667 689 def test():
668 690
669 691 examples = [
670 692 r"""
671 693 In [9]: pwd
672 694 Out[9]: '/home/jdhunter/py4science/book'
673 695
674 696 In [10]: cd bookdata/
675 697 /home/jdhunter/py4science/book/bookdata
676 698
677 699 In [2]: from pylab import *
678 700
679 701 In [2]: ion()
680 702
681 703 In [3]: im = imread('stinkbug.png')
682 704
683 705 @savefig mystinkbug.png width=4in
684 706 In [4]: imshow(im)
685 707 Out[4]: <matplotlib.image.AxesImage object at 0x39ea850>
686 708
687 709 """,
688 710 r"""
689 711
690 712 In [1]: x = 'hello world'
691 713
692 714 # string methods can be
693 715 # used to alter the string
694 716 @doctest
695 717 In [2]: x.upper()
696 718 Out[2]: 'HELLO WORLD'
697 719
698 720 @verbatim
699 721 In [3]: x.st<TAB>
700 722 x.startswith x.strip
701 723 """,
702 724 r"""
703 725
704 726 In [130]: url = 'http://ichart.finance.yahoo.com/table.csv?s=CROX\
705 727 .....: &d=9&e=22&f=2009&g=d&a=1&br=8&c=2006&ignore=.csv'
706 728
707 729 In [131]: print url.split('&')
708 730 ['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']
709 731
710 732 In [60]: import urllib
711 733
712 734 """,
713 735 r"""\
714 736
715 737 In [133]: import numpy.random
716 738
717 739 @suppress
718 740 In [134]: numpy.random.seed(2358)
719 741
720 742 @doctest
721 743 In [135]: numpy.random.rand(10,2)
722 744 Out[135]:
723 745 array([[ 0.64524308, 0.59943846],
724 746 [ 0.47102322, 0.8715456 ],
725 747 [ 0.29370834, 0.74776844],
726 748 [ 0.99539577, 0.1313423 ],
727 749 [ 0.16250302, 0.21103583],
728 750 [ 0.81626524, 0.1312433 ],
729 751 [ 0.67338089, 0.72302393],
730 752 [ 0.7566368 , 0.07033696],
731 753 [ 0.22591016, 0.77731835],
732 754 [ 0.0072729 , 0.34273127]])
733 755
734 756 """,
735 757
736 758 r"""
737 759 In [106]: print x
738 760 jdh
739 761
740 762 In [109]: for i in range(10):
741 763 .....: print i
742 764 .....:
743 765 .....:
744 766 0
745 767 1
746 768 2
747 769 3
748 770 4
749 771 5
750 772 6
751 773 7
752 774 8
753 775 9
754 776 """,
755 777
756 778 r"""
757 779
758 780 In [144]: from pylab import *
759 781
760 782 In [145]: ion()
761 783
762 784 # use a semicolon to suppress the output
763 785 @savefig test_hist.png width=4in
764 786 In [151]: hist(np.random.randn(10000), 100);
765 787
766 788
767 789 @savefig test_plot.png width=4in
768 790 In [151]: plot(np.random.randn(10000), 'o');
769 791 """,
770 792
771 793 r"""
772 794 # use a semicolon to suppress the output
773 795 In [151]: plt.clf()
774 796
775 797 @savefig plot_simple.png width=4in
776 798 In [151]: plot([1,2,3])
777 799
778 800 @savefig hist_simple.png width=4in
779 801 In [151]: hist(np.random.randn(10000), 100);
780 802
781 803 """,
782 804 r"""
783 805 # update the current fig
784 806 In [151]: ylabel('number')
785 807
786 808 In [152]: title('normal distribution')
787 809
788 810
789 811 @savefig hist_with_text.png
790 812 In [153]: grid(True)
791 813
792 814 """,
793 815 ]
794 816 # skip local-file depending first example:
795 817 examples = examples[1:]
796 818
797 819 #ipython_directive.DEBUG = True # dbg
798 820 #options = dict(suppress=True) # dbg
799 821 options = dict()
800 822 for example in examples:
801 823 content = example.split('\n')
802 824 ipython_directive('debug', arguments=None, options=options,
803 825 content=content, lineno=0,
804 826 content_offset=None, block_text=None,
805 827 state=None, state_machine=None,
806 828 )
807 829
808 830 # Run test suite as a script
809 831 if __name__=='__main__':
810 832 if not os.path.isdir('_static'):
811 833 os.mkdir('_static')
812 834 test()
813 835 print 'All OK? Check figures in _static/'
General Comments 0
You need to be logged in to leave comments. Login now