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