##// END OF EJS Templates
Using a class varilable instead of temp files for keeping track of docs....
Ahmet Bakan -
Show More
@@ -1,830 +1,822 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 matplotlib.use('Agg')
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 499 try:
500 500 mod = ast.parse(
501 501 '\n'.join(content[multiline_start:lineno+1]))
502 502 if isinstance(mod.body[0], ast.FunctionDef):
503 503 # check to see if we have the whole function
504 504 for element in mod.body[0].body:
505 505 if isinstance(element, ast.Return):
506 506 multiline = False
507 507 else:
508 508 output.append(u'')
509 509 multiline = False
510 510 except Exception:
511 511 pass
512 512
513 513 if savefig: # clear figure if plotted
514 514 self.ensure_pyplot()
515 515 self.process_input_line('plt.clf()', store_history=False)
516 516 self.clear_cout()
517 517 savefig = False
518 518
519 519 return output
520 520
521 521 class IpythonDirective(Directive):
522 522
523 523 has_content = True
524 524 required_arguments = 0
525 525 optional_arguments = 4 # python, suppress, verbatim, doctest
526 526 final_argumuent_whitespace = True
527 527 option_spec = { 'python': directives.unchanged,
528 528 'suppress' : directives.flag,
529 529 'verbatim' : directives.flag,
530 530 'doctest' : directives.flag,
531 531 }
532 532
533 533 shell = EmbeddedSphinxShell()
534 534
535 seen_docs = set()
536
535 537 def get_config_options(self):
536 538 # contains sphinx configuration variables
537 539 config = self.state.document.settings.env.config
538 540
539 541 # get config variables to set figure output directory
540 542 confdir = self.state.document.settings.env.app.confdir
541 543 savefig_dir = config.ipython_savefig_dir
542 544 source_dir = os.path.dirname(self.state.document.current_source)
543 545 if savefig_dir is None:
544 546 savefig_dir = config.html_static_path
545 547 if isinstance(savefig_dir, list):
546 548 savefig_dir = savefig_dir[0] # safe to assume only one path?
547 549 savefig_dir = os.path.join(confdir, savefig_dir)
548 550
549 551 # get regex and prompt stuff
550 552 rgxin = config.ipython_rgxin
551 553 rgxout = config.ipython_rgxout
552 554 promptin = config.ipython_promptin
553 555 promptout = config.ipython_promptout
554 556
555 557 return savefig_dir, source_dir, rgxin, rgxout, promptin, promptout
556 558
557 559 def setup(self):
558 560 # reset the execution count if we haven't processed this doc
559 561 #NOTE: this may be borked if there are multiple seen_doc tmp files
560 562 #check time stamp?
561 seen_docs = [i for i in os.listdir(tempfile.tempdir)
562 if i.startswith('seen_doc')]
563 if seen_docs:
564 fname = os.path.join(tempfile.tempdir, seen_docs[0])
565 docs = open(fname).read().split('\n')
566 if not self.state.document.current_source in docs:
563
564
565 if not self.state.document.current_source in self.seen_docs:
567 566 self.shell.IP.history_manager.reset()
568 567 self.shell.IP.execution_count = 1
569 else: # haven't processed any docs yet
570 docs = []
568 self.seen_docs.add(self.state.document.current_source)
569
571 570
572 571
573 572 # get config values
574 573 (savefig_dir, source_dir, rgxin,
575 574 rgxout, promptin, promptout) = self.get_config_options()
576 575
577 576 # and attach to shell so we don't have to pass them around
578 577 self.shell.rgxin = rgxin
579 578 self.shell.rgxout = rgxout
580 579 self.shell.promptin = promptin
581 580 self.shell.promptout = promptout
582 581 self.shell.savefig_dir = savefig_dir
583 582 self.shell.source_dir = source_dir
584 583
585 584 # setup bookmark for saving figures directory
586 585
587 586 self.shell.process_input_line('bookmark ipy_savedir %s'%savefig_dir,
588 587 store_history=False)
589 588 self.shell.clear_cout()
590 589
591 # write the filename to a tempfile because it's been "seen" now
592 if not self.state.document.current_source in docs:
593 fd, fname = tempfile.mkstemp(prefix="seen_doc", text=True)
594 fout = open(fname, 'a')
595 fout.write(self.state.document.current_source+'\n')
596 fout.close()
597
598 590 return rgxin, rgxout, promptin, promptout
599 591
600 592
601 593 def teardown(self):
602 594 # delete last bookmark
603 595 self.shell.process_input_line('bookmark -d ipy_savedir',
604 596 store_history=False)
605 597 self.shell.clear_cout()
606 598
607 599 def run(self):
608 600 debug = False
609 601
610 602 #TODO, any reason block_parser can't be a method of embeddable shell
611 603 # then we wouldn't have to carry these around
612 604 rgxin, rgxout, promptin, promptout = self.setup()
613 605
614 606 options = self.options
615 607 self.shell.is_suppress = 'suppress' in options
616 608 self.shell.is_doctest = 'doctest' in options
617 609 self.shell.is_verbatim = 'verbatim' in options
618 610
619 611
620 612 # handle pure python code
621 613 if 'python' in self.arguments:
622 614 content = self.content
623 615 self.content = self.shell.process_pure_python(content)
624 616
625 617 parts = '\n'.join(self.content).split('\n\n')
626 618
627 619 lines = ['.. code-block:: ipython','']
628 620 figures = []
629 621
630 622 for part in parts:
631 623
632 624 block = block_parser(part, rgxin, rgxout, promptin, promptout)
633 625
634 626 if len(block):
635 627 rows, figure = self.shell.process_block(block)
636 628 for row in rows:
637 629 lines.extend([' %s'%line for line in row.split('\n')])
638 630
639 631 if figure is not None:
640 632 figures.append(figure)
641 633
642 634 #text = '\n'.join(lines)
643 635 #figs = '\n'.join(figures)
644 636
645 637 for figure in figures:
646 638 lines.append('')
647 639 lines.extend(figure.split('\n'))
648 640 lines.append('')
649 641
650 642 #print lines
651 643 if len(lines)>2:
652 644 if debug:
653 645 print '\n'.join(lines)
654 646 else: #NOTE: this raises some errors, what's it for?
655 647 #print 'INSERTING %d lines'%len(lines)
656 648 self.state_machine.insert_input(
657 649 lines, self.state_machine.input_lines.source(0))
658 650
659 651 text = '\n'.join(lines)
660 652 txtnode = nodes.literal_block(text, text)
661 653 txtnode['language'] = 'ipython'
662 654 #imgnode = nodes.image(figs)
663 655
664 656 # cleanup
665 657 self.teardown()
666 658
667 659 return []#, imgnode]
668 660
669 661 # Enable as a proper Sphinx directive
670 662 def setup(app):
671 663 setup.app = app
672 664
673 665 app.add_directive('ipython', IpythonDirective)
674 666 app.add_config_value('ipython_savefig_dir', None, True)
675 667 app.add_config_value('ipython_rgxin',
676 668 re.compile('In \[(\d+)\]:\s?(.*)\s*'), True)
677 669 app.add_config_value('ipython_rgxout',
678 670 re.compile('Out\[(\d+)\]:\s?(.*)\s*'), True)
679 671 app.add_config_value('ipython_promptin', 'In [%d]:', True)
680 672 app.add_config_value('ipython_promptout', 'Out[%d]:', True)
681 673
682 674
683 675 # Simple smoke test, needs to be converted to a proper automatic test.
684 676 def test():
685 677
686 678 examples = [
687 679 r"""
688 680 In [9]: pwd
689 681 Out[9]: '/home/jdhunter/py4science/book'
690 682
691 683 In [10]: cd bookdata/
692 684 /home/jdhunter/py4science/book/bookdata
693 685
694 686 In [2]: from pylab import *
695 687
696 688 In [2]: ion()
697 689
698 690 In [3]: im = imread('stinkbug.png')
699 691
700 692 @savefig mystinkbug.png width=4in
701 693 In [4]: imshow(im)
702 694 Out[4]: <matplotlib.image.AxesImage object at 0x39ea850>
703 695
704 696 """,
705 697 r"""
706 698
707 699 In [1]: x = 'hello world'
708 700
709 701 # string methods can be
710 702 # used to alter the string
711 703 @doctest
712 704 In [2]: x.upper()
713 705 Out[2]: 'HELLO WORLD'
714 706
715 707 @verbatim
716 708 In [3]: x.st<TAB>
717 709 x.startswith x.strip
718 710 """,
719 711 r"""
720 712
721 713 In [130]: url = 'http://ichart.finance.yahoo.com/table.csv?s=CROX\
722 714 .....: &d=9&e=22&f=2009&g=d&a=1&br=8&c=2006&ignore=.csv'
723 715
724 716 In [131]: print url.split('&')
725 717 ['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']
726 718
727 719 In [60]: import urllib
728 720
729 721 """,
730 722 r"""\
731 723
732 724 In [133]: import numpy.random
733 725
734 726 @suppress
735 727 In [134]: numpy.random.seed(2358)
736 728
737 729 @doctest
738 730 In [135]: numpy.random.rand(10,2)
739 731 Out[135]:
740 732 array([[ 0.64524308, 0.59943846],
741 733 [ 0.47102322, 0.8715456 ],
742 734 [ 0.29370834, 0.74776844],
743 735 [ 0.99539577, 0.1313423 ],
744 736 [ 0.16250302, 0.21103583],
745 737 [ 0.81626524, 0.1312433 ],
746 738 [ 0.67338089, 0.72302393],
747 739 [ 0.7566368 , 0.07033696],
748 740 [ 0.22591016, 0.77731835],
749 741 [ 0.0072729 , 0.34273127]])
750 742
751 743 """,
752 744
753 745 r"""
754 746 In [106]: print x
755 747 jdh
756 748
757 749 In [109]: for i in range(10):
758 750 .....: print i
759 751 .....:
760 752 .....:
761 753 0
762 754 1
763 755 2
764 756 3
765 757 4
766 758 5
767 759 6
768 760 7
769 761 8
770 762 9
771 763 """,
772 764
773 765 r"""
774 766
775 767 In [144]: from pylab import *
776 768
777 769 In [145]: ion()
778 770
779 771 # use a semicolon to suppress the output
780 772 @savefig test_hist.png width=4in
781 773 In [151]: hist(np.random.randn(10000), 100);
782 774
783 775
784 776 @savefig test_plot.png width=4in
785 777 In [151]: plot(np.random.randn(10000), 'o');
786 778 """,
787 779
788 780 r"""
789 781 # use a semicolon to suppress the output
790 782 In [151]: plt.clf()
791 783
792 784 @savefig plot_simple.png width=4in
793 785 In [151]: plot([1,2,3])
794 786
795 787 @savefig hist_simple.png width=4in
796 788 In [151]: hist(np.random.randn(10000), 100);
797 789
798 790 """,
799 791 r"""
800 792 # update the current fig
801 793 In [151]: ylabel('number')
802 794
803 795 In [152]: title('normal distribution')
804 796
805 797
806 798 @savefig hist_with_text.png
807 799 In [153]: grid(True)
808 800
809 801 """,
810 802 ]
811 803 # skip local-file depending first example:
812 804 examples = examples[1:]
813 805
814 806 #ipython_directive.DEBUG = True # dbg
815 807 #options = dict(suppress=True) # dbg
816 808 options = dict()
817 809 for example in examples:
818 810 content = example.split('\n')
819 811 ipython_directive('debug', arguments=None, options=options,
820 812 content=content, lineno=0,
821 813 content_offset=None, block_text=None,
822 814 state=None, state_machine=None,
823 815 )
824 816
825 817 # Run test suite as a script
826 818 if __name__=='__main__':
827 819 if not os.path.isdir('_static'):
828 820 os.mkdir('_static')
829 821 test()
830 822 print 'All OK? Check figures in _static/'
General Comments 0
You need to be logged in to leave comments. Login now