##// END OF EJS Templates
Updated Sphinx extension ipython_directive to work with 0.11.
Skipper Seabold -
Show More
@@ -3,38 +3,38 b''
3
3
4 This directive allows pasting of entire interactive IPython sessions, prompts
4 This directive allows pasting of entire interactive IPython sessions, prompts
5 and all, and their code will actually get re-executed at doc build time, with
5 and all, and their code will actually get re-executed at doc build time, with
6 all prompts renumbered sequentially.
6 all prompts renumbered sequentially. It also allows you to input code as a pure
7 python input by giving the argument python to the directive. The output looks
8 like an interactive ipython section.
7
9
8 To enable this directive, simply list it in your Sphinx ``conf.py`` file
10 To enable this directive, simply list it in your Sphinx ``conf.py`` file
9 (making sure the directory where you placed it is visible to sphinx, as is
11 (making sure the directory where you placed it is visible to sphinx, as is
10 needed for all Sphinx directives).
12 needed for all Sphinx directives).
11
13
12 By default this directive assumes that your prompts are unchanged IPython ones,
14 By default this directive assumes that your prompts are unchanged IPython ones,
13 but this can be customized. For example, the following code in your Sphinx
15 but this can be customized. The configurable options that can be placed in
14 config file will configure this directive for the following input/output
16 conf.py are
15 prompts ``Yade [1]:`` and ``-> [1]:``::
17
16
18 ipython_savefig_dir:
17 import ipython_directive as id
19 The directory in which to save the figures. This is relative to the
18 id.rgxin =re.compile(r'(?:In |Yade )\[(\d+)\]:\s?(.*)\s*')
20 Sphinx source directory. The default is `html_static_path`.
19 id.rgxout=re.compile(r'(?:Out| -> )\[(\d+)\]:\s?(.*)\s*')
21 ipython_rgxin:
20 id.fmtin ='Yade [%d]:'
22 The compiled regular expression to denote the start of IPython input
21 id.fmtout=' -> [%d]:'
23 lines. The default is re.compile('In \[(\d+)\]:\s?(.*)\s*'). You
22
24 shouldn't need to change this.
23 from IPython import Config
25 ipython_rgxout:
24 id.CONFIG = Config(
26 The compiled regular expression to denote the start of IPython output
25 prompt_in1="Yade [\#]:",
27 lines. The default is re.compile('Out\[(\d+)\]:\s?(.*)\s*'). You
26 prompt_in2=" .\D..",
28 shouldn't need to change this.
27 prompt_out=" -> [\#]:"
29 ipython_promptin:
28 )
30 The string to represent the IPython input prompt in the generated ReST.
29 id.reconfig_shell()
31 The default is 'In [%d]:'. This expects that the line numbers are used
30
32 in the prompt.
31 import ipython_console_highlighting as ich
33 ipython_promptout:
32 ich.IPythonConsoleLexer.input_prompt=
34
33 re.compile("(Yade \[[0-9]+\]: )|( \.\.\.+:)")
35 The string to represent the IPython prompt in the generated ReST. The
34 ich.IPythonConsoleLexer.output_prompt=
36 default is 'Out [%d]:'. This expects that the line numbers are used
35 re.compile("(( -> )|(Out)\[[0-9]+\]: )|( \.\.\.+:)")
37 in the prompt.
36 ich.IPythonConsoleLexer.continue_prompt=re.compile(" \.\.\.+:")
37
38
38
39 ToDo
39 ToDo
40 ----
40 ----
@@ -42,8 +42,6 b' ToDo'
42 - Turn the ad-hoc test() function into a real test suite.
42 - Turn the ad-hoc test() function into a real test suite.
43 - Break up ipython-specific functionality from matplotlib stuff into better
43 - Break up ipython-specific functionality from matplotlib stuff into better
44 separated code.
44 separated code.
45 - Make sure %bookmarks used internally are removed on exit.
46
47
45
48 Authors
46 Authors
49 -------
47 -------
@@ -51,6 +49,7 b' Authors'
51 - John D Hunter: orignal author.
49 - John D Hunter: orignal author.
52 - Fernando Perez: refactoring, documentation, cleanups, port to 0.11.
50 - Fernando Perez: refactoring, documentation, cleanups, port to 0.11.
53 - VΓ‘clavΕ milauer <eudoxos-AT-arcig.cz>: Prompt generalizations.
51 - VΓ‘clavΕ milauer <eudoxos-AT-arcig.cz>: Prompt generalizations.
52 - Skipper Seabold, refactoring, cleanups, pure python addition
54 """
53 """
55
54
56 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
@@ -62,6 +61,7 b' import cStringIO'
62 import os
61 import os
63 import re
62 import re
64 import sys
63 import sys
64 import tempfile
65
65
66 # To keep compatibility with various python versions
66 # To keep compatibility with various python versions
67 try:
67 try:
@@ -73,34 +73,26 b' except ImportError:'
73 import matplotlib
73 import matplotlib
74 import sphinx
74 import sphinx
75 from docutils.parsers.rst import directives
75 from docutils.parsers.rst import directives
76 from docutils import nodes
77 from sphinx.util.compat import Directive
76
78
77 matplotlib.use('Agg')
79 matplotlib.use('Agg')
78
80
79 # Our own
81 # Our own
80 from IPython import Config, InteractiveShell
82 from IPython import Config, InteractiveShell
81 from IPython.utils.io import Term
83 from IPython.core.profiledir import ProfileDir
84 from IPython.utils import io
82
85
83 #-----------------------------------------------------------------------------
86 #-----------------------------------------------------------------------------
84 # Globals
87 # Globals
85 #-----------------------------------------------------------------------------
88 #-----------------------------------------------------------------------------
86
89 # for tokenizing blocks
87 sphinx_version = sphinx.__version__.split(".")
88 # The split is necessary for sphinx beta versions where the string is
89 # '6b1'
90 sphinx_version = tuple([int(re.split('[a-z]', x)[0])
91 for x in sphinx_version[:2]])
92
93 COMMENT, INPUT, OUTPUT = range(3)
90 COMMENT, INPUT, OUTPUT = range(3)
94 CONFIG = Config()
95 rgxin = re.compile('In \[(\d+)\]:\s?(.*)\s*')
96 rgxout = re.compile('Out\[(\d+)\]:\s?(.*)\s*')
97 fmtin = 'In [%d]:'
98 fmtout = 'Out[%d]:'
99
91
100 #-----------------------------------------------------------------------------
92 #-----------------------------------------------------------------------------
101 # Functions and class declarations
93 # Functions and class declarations
102 #-----------------------------------------------------------------------------
94 #-----------------------------------------------------------------------------
103 def block_parser(part):
95 def block_parser(part, rgxin, rgxout, fmtin, fmtout):
104 """
96 """
105 part is a string of ipython text, comprised of at most one
97 part is a string of ipython text, comprised of at most one
106 input, one ouput, comments, and blank lines. The block parser
98 input, one ouput, comments, and blank lines. The block parser
@@ -194,20 +186,13 b' def block_parser(part):'
194
186
195 return block
187 return block
196
188
197
198 class EmbeddedSphinxShell(object):
189 class EmbeddedSphinxShell(object):
199 """An embedded IPython instance to run inside Sphinx"""
190 """An embedded IPython instance to run inside Sphinx"""
200
191
201 def __init__(self):
192 def __init__(self):
202
193
203 self.cout = cStringIO.StringIO()
194 self.cout = cStringIO.StringIO()
204 Term.cout = self.cout
205 Term.cerr = self.cout
206
195
207 # For debugging, so we can see normal output, use this:
208 # from IPython.utils.io import Tee
209 #Term.cout = Tee(self.cout, channel='stdout') # dbg
210 #Term.cerr = Tee(self.cout, channel='stderr') # dbg
211
196
212 # Create config object for IPython
197 # Create config object for IPython
213 config = Config()
198 config = Config()
@@ -219,14 +204,28 b' class EmbeddedSphinxShell(object):'
219 config.InteractiveShell.autoindent = False
204 config.InteractiveShell.autoindent = False
220 config.InteractiveShell.colors = 'NoColor'
205 config.InteractiveShell.colors = 'NoColor'
221
206
207 # create a profile so instance history isn't saved
208 tmp_profile_dir = tempfile.mkdtemp(prefix='profile_')
209 profname = 'auto_profile_sphinx_build'
210 pdir = os.path.join(tmp_profile_dir,profname)
211 profile = ProfileDir.create_profile_dir(pdir)
212
222 # Create and initialize ipython, but don't start its mainloop
213 # Create and initialize ipython, but don't start its mainloop
223 IP = InteractiveShell.instance(config=config)
214 IP = InteractiveShell.instance(config=config, profile_dir=profile)
215 # io.stdout redirect must be done *after* instantiating InteractiveShell
216 io.stdout = self.cout
217 io.stderr = self.cout
218
219 # For debugging, so we can see normal output, use this:
220 #from IPython.utils.io import Tee
221 #io.stdout = Tee(self.cout, channel='stdout') # dbg
222 #io.stderr = Tee(self.cout, channel='stderr') # dbg
224
223
225 # Store a few parts of IPython we'll need.
224 # Store a few parts of IPython we'll need.
226 self.IP = IP
225 self.IP = IP
227 self.user_ns = self.IP.user_ns
226 self.user_ns = self.IP.user_ns
228 self.user_global_ns = self.IP.user_global_ns
227 self.user_global_ns = self.IP.user_global_ns
229
228
230 self.input = ''
229 self.input = ''
231 self.output = ''
230 self.output = ''
232
231
@@ -238,27 +237,61 b' class EmbeddedSphinxShell(object):'
238 # pyplot as plt so we can make a call to the plt.gcf().savefig
237 # pyplot as plt so we can make a call to the plt.gcf().savefig
239 self._pyplot_imported = False
238 self._pyplot_imported = False
240
239
241 # we need bookmark the current dir first so we can save
240 def clear_cout(self):
242 # relative to it
243 self.process_input_line('bookmark ipy_basedir')
244 self.cout.seek(0)
241 self.cout.seek(0)
245 self.cout.truncate(0)
242 self.cout.truncate(0)
246
243
247 def process_input_line(self, line):
244 def process_input_line(self, line, store_history=True):
248 """process the input, capturing stdout"""
245 """process the input, capturing stdout"""
249 #print "input='%s'"%self.input
246 #print "input='%s'"%self.input
250 stdout = sys.stdout
247 stdout = sys.stdout
248 splitter = self.IP.input_splitter
251 try:
249 try:
252 sys.stdout = self.cout
250 sys.stdout = self.cout
253 self.IP.push_line(line)
251 splitter.push(line)
252 more = splitter.push_accepts_more()
253 if not more:
254 source_raw = splitter.source_raw_reset()[1]
255 self.IP.run_cell(source_raw, store_history=store_history)
254 finally:
256 finally:
255 sys.stdout = stdout
257 sys.stdout = stdout
256
258
259 def process_image(self, decorator):
260 """
261 # build out an image directive like
262 # .. image:: somefile.png
263 # :width 4in
264 #
265 # from an input like
266 # savefig somefile.png width=4in
267 """
268 savefig_dir = self.savefig_dir
269 source_dir = self.source_dir
270 saveargs = decorator.split(' ')
271 filename = saveargs[1]
272 # insert relative path to image file in source
273 outfile = os.path.relpath(os.path.join(savefig_dir,filename),
274 source_dir)
275
276 imagerows = ['.. image:: %s'%outfile]
277
278 for kwarg in saveargs[2:]:
279 arg, val = kwarg.split('=')
280 arg = arg.strip()
281 val = val.strip()
282 imagerows.append(' :%s: %s'%(arg, val))
283
284 image_file = os.path.basename(outfile) # only return file name
285 image_directive = '\n'.join(imagerows)
286 return image_file, image_directive
287
288
257 # Callbacks for each type of token
289 # Callbacks for each type of token
258 def process_input(self, data, input_prompt, lineno):
290 def process_input(self, data, input_prompt, lineno):
259 """Process data block for INPUT token."""
291 """Process data block for INPUT token."""
260 decorator, input, rest = data
292 decorator, input, rest = data
261 image_file = None
293 image_file = None
294 image_directive = None
262 #print 'INPUT:', data # dbg
295 #print 'INPUT:', data # dbg
263 is_verbatim = decorator=='@verbatim' or self.is_verbatim
296 is_verbatim = decorator=='@verbatim' or self.is_verbatim
264 is_doctest = decorator=='@doctest' or self.is_doctest
297 is_doctest = decorator=='@doctest' or self.is_doctest
@@ -272,48 +305,31 b' class EmbeddedSphinxShell(object):'
272 Nc = len(continuation)
305 Nc = len(continuation)
273
306
274 if is_savefig:
307 if is_savefig:
275 saveargs = decorator.split(' ')
308 image_file, image_directive = self.process_image(decorator)
276 filename = saveargs[1]
277 outfile = os.path.join('_static/%s'%filename)
278 # build out an image directive like
279 # .. image:: somefile.png
280 # :width 4in
281 #
282 # from an input like
283 # savefig somefile.png width=4in
284 imagerows = ['.. image:: %s'%outfile]
285
286 for kwarg in saveargs[2:]:
287 arg, val = kwarg.split('=')
288 arg = arg.strip()
289 val = val.strip()
290 imagerows.append(' :%s: %s'%(arg, val))
291
292 image_file = outfile
293 image_directive = '\n'.join(imagerows)
294
295 # TODO: can we get "rest" from ipython
296 #self.process_input_line('\n'.join(input_lines))
297
309
298 ret = []
310 ret = []
299 is_semicolon = False
311 is_semicolon = False
312 store_history = True
300
313
301 for i, line in enumerate(input_lines):
314 for i, line in enumerate(input_lines):
302 if line.endswith(';'):
315 if line.endswith(';'):
303 is_semicolon = True
316 is_semicolon = True
317 if is_semicolon or is_suppress:
318 store_history = False
304
319
305 if i==0:
320 if i==0:
306 # process the first input line
321 # process the first input line
307 if is_verbatim:
322 if is_verbatim:
308 self.process_input_line('')
323 self.process_input_line('')
324 self.IP.execution_count += 1 # increment it anyway
309 else:
325 else:
310 # only submit the line in non-verbatim mode
326 # only submit the line in non-verbatim mode
311 self.process_input_line(line)
327 self.process_input_line(line, store_history=store_history)
312 formatted_line = '%s %s'%(input_prompt, line)
328 formatted_line = '%s %s'%(input_prompt, line)
313 else:
329 else:
314 # process a continuation line
330 # process a continuation line
315 if not is_verbatim:
331 if not is_verbatim:
316 self.process_input_line(line)
332 self.process_input_line(line, store_history=store_history)
317
333
318 formatted_line = '%s %s'%(continuation, line)
334 formatted_line = '%s %s'%(continuation, line)
319
335
@@ -334,7 +350,8 b' class EmbeddedSphinxShell(object):'
334 ret.append(output)
350 ret.append(output)
335
351
336 self.cout.truncate(0)
352 self.cout.truncate(0)
337 return ret, input_lines, output, is_doctest, image_file
353 return (ret, input_lines, output, is_doctest, image_file,
354 image_directive)
338 #print 'OUTPUT', output # dbg
355 #print 'OUTPUT', output # dbg
339
356
340 def process_output(self, data, output_prompt,
357 def process_output(self, data, output_prompt,
@@ -345,16 +362,19 b' class EmbeddedSphinxShell(object):'
345 found = output
362 found = output
346 if found is not None:
363 if found is not None:
347 found = found.strip()
364 found = found.strip()
348
365
349 # XXX - fperez: in 0.11, 'output' never comes with the prompt
366 # XXX - fperez: in 0.11, 'output' never comes with the prompt
350 # in it, just the actual output text. So I think all this code
367 # in it, just the actual output text. So I think all this code
351 # can be nuked...
368 # can be nuked...
352 ## ind = found.find(output_prompt)
369
353 ## if ind<0:
370 # the above comment does not appear to be accurate... (minrk)
354 ## e='output prompt="%s" does not match out line=%s' % \
371
355 ## (output_prompt, found)
372 ind = found.find(output_prompt)
356 ## raise RuntimeError(e)
373 if ind<0:
357 ## found = found[len(output_prompt):].strip()
374 e='output prompt="%s" does not match out line=%s' % \
375 (output_prompt, found)
376 raise RuntimeError(e)
377 found = found[len(output_prompt):].strip()
358
378
359 if found!=submitted:
379 if found!=submitted:
360 e = ('doctest failure for input_lines="%s" with '
380 e = ('doctest failure for input_lines="%s" with '
@@ -364,36 +384,45 b' class EmbeddedSphinxShell(object):'
364 #print 'doctest PASSED for input_lines="%s" with found_output="%s" and submitted output="%s"'%(input_lines, found, submitted)
384 #print 'doctest PASSED for input_lines="%s" with found_output="%s" and submitted output="%s"'%(input_lines, found, submitted)
365
385
366 def process_comment(self, data):
386 def process_comment(self, data):
367 """Process data block for COMMENT token."""
387 """Process data fPblock for COMMENT token."""
368 if not self.is_suppress:
388 if not self.is_suppress:
369 return [data]
389 return [data]
370
390
391 def save_image(self, image_file):
392 """
393 Saves the image file to disk.
394 """
395 self.ensure_pyplot()
396 command = 'plt.gcf().savefig("%s")'%image_file
397 #print 'SAVEFIG', command # dbg
398 self.process_input_line('bookmark ipy_thisdir', store_history=False)
399 self.process_input_line('cd -b ipy_savedir', store_history=False)
400 self.process_input_line(command, store_history=False)
401 self.process_input_line('cd -b ipy_thisdir', store_history=False)
402 self.process_input_line('bookmark -d ipy_thisdir', store_history=False)
403 self.clear_cout()
404
405
371 def process_block(self, block):
406 def process_block(self, block):
372 """
407 """
373 process block from the block_parser and return a list of processed lines
408 process block from the block_parser and return a list of processed lines
374 """
409 """
375
376 ret = []
410 ret = []
377 output = None
411 output = None
378 input_lines = None
412 input_lines = None
413 lineno = self.IP.execution_count
379
414
380 m = rgxin.match(str(self.IP.outputcache.prompt1).strip())
415 input_prompt = self.promptin%lineno
381 lineno = int(m.group(1))
416 output_prompt = self.promptout%lineno
382
383 input_prompt = fmtin%lineno
384 output_prompt = fmtout%lineno
385 image_file = None
417 image_file = None
386 image_directive = None
418 image_directive = None
387 # XXX - This needs a second refactor. There's too much state being
419
388 # held globally, which makes for a very awkward interface and large,
389 # hard to test functions. I've already broken this up at least into
390 # three separate processors to isolate the logic better, but this only
391 # serves to highlight the coupling. Next we need to clean it up...
392 for token, data in block:
420 for token, data in block:
393 if token==COMMENT:
421 if token==COMMENT:
394 out_data = self.process_comment(data)
422 out_data = self.process_comment(data)
395 elif token==INPUT:
423 elif token==INPUT:
396 out_data, input_lines, output, is_doctest, image_file= \
424 (out_data, input_lines, output, is_doctest, image_file,
425 image_directive) = \
397 self.process_input(data, input_prompt, lineno)
426 self.process_input(data, input_prompt, lineno)
398 elif token==OUTPUT:
427 elif token==OUTPUT:
399 out_data = \
428 out_data = \
@@ -403,88 +432,235 b' class EmbeddedSphinxShell(object):'
403 if out_data:
432 if out_data:
404 ret.extend(out_data)
433 ret.extend(out_data)
405
434
435 # save the image files
406 if image_file is not None:
436 if image_file is not None:
407 self.ensure_pyplot()
437 self.save_image(image_file)
408 command = 'plt.gcf().savefig("%s")'%image_file
438
409 print 'SAVEFIG', command # dbg
410 self.process_input_line('bookmark ipy_thisdir')
411 self.process_input_line('cd -b ipy_basedir')
412 self.process_input_line(command)
413 self.process_input_line('cd -b ipy_thisdir')
414 self.cout.seek(0)
415 self.cout.truncate(0)
416 return ret, image_directive
439 return ret, image_directive
417
440
418 def ensure_pyplot(self):
441 def ensure_pyplot(self):
419 if self._pyplot_imported:
442 if self._pyplot_imported:
420 return
443 return
421 self.process_input_line('import matplotlib.pyplot as plt')
444 self.process_input_line('import matplotlib.pyplot as plt',
445 store_history=False)
422
446
423 # A global instance used below. XXX: not sure why this can't be created inside
447 def process_pure_python(self, content):
424 # ipython_directive itself.
448 """
425 shell = EmbeddedSphinxShell()
449 content is a list of strings. it is unedited directive conent
450
451 This runs it line by line in the InteractiveShell, prepends
452 prompts as needed capturing stderr and stdout, then returns
453 the content as a list as if it were ipython code
454 """
455 output = []
456 savefig = False # keep up with this to clear figure
457 multiline = False # to handle line continuation
458 fmtin = self.promptin
459
460 for lineno, line in enumerate(content):
461
462 line_stripped = line.strip()
463
464 if not len(line):
465 output.append(line) # preserve empty lines in output
466 continue
467
468 # handle decorators
469 if line_stripped.startswith('@'):
470 output.extend([line])
471 if 'savefig' in line:
472 savefig = True # and need to clear figure
473 continue
474
475 # handle comments
476 if line_stripped.startswith('#'):
477 output.extend([line])
478 continue
479
480 # deal with multilines
481 if not multiline: # not currently on a multiline
482
483 if line_stripped.endswith('\\'): # now we are
484 multiline = True
485 cont_len = len(str(lineno)) + 2
486 line_to_process = line.strip('\\')
487 output.extend([u"%s %s" % (fmtin%lineno,line)])
488 continue
489 else: # no we're still not
490 line_to_process = line.strip('\\')
491 else: # we are currently on a multiline
492 line_to_process += line.strip('\\')
493 if line_stripped.endswith('\\'): # and we still are
494 continuation = '.' * cont_len
495 output.extend([(u' %s: '+line_stripped) % continuation])
496 continue
497 # else go ahead and run this multiline then carry on
498
499 # get output of line
500 self.process_input_line(unicode(line_to_process.strip()),
501 store_history=False)
502 out_line = self.cout.getvalue()
503 self.clear_cout()
504
505 # clear current figure if plotted
506 if savefig:
507 self.ensure_pyplot()
508 self.process_input_line('plt.clf()', store_history=False)
509 self.clear_cout()
510 savefig = False
511
512 # line numbers don't actually matter, they're replaced later
513 if not multiline:
514 in_line = u"%s %s" % (fmtin%lineno,line)
515
516 output.extend([in_line])
517 else:
518 output.extend([(u' %s: '+line_stripped) % continuation])
519 multiline = False
520 if len(out_line):
521 output.extend([out_line])
522 output.extend([u''])
523
524 return output
525
526 class IpythonDirective(Directive):
527
528 has_content = True
529 required_arguments = 0
530 optional_arguments = 4 # python, suppress, verbatim, doctest
531 final_argumuent_whitespace = True
532 option_spec = { 'python': directives.unchanged,
533 'suppress' : directives.flag,
534 'verbatim' : directives.flag,
535 'doctest' : directives.flag,
536 }
426
537
427 def reconfig_shell():
428 """Called after setting module-level variables to re-instantiate
429 with the set values (since shell is instantiated first at import-time
430 when module variables have default values)"""
431 global shell
432 shell = EmbeddedSphinxShell()
538 shell = EmbeddedSphinxShell()
433
539
540 def get_config_options(self):
541 # contains sphinx configuration variables
542 config = self.state.document.settings.env.config
543
544 # get config variables to set figure output directory
545 confdir = self.state.document.settings.env.app.confdir
546 savefig_dir = config.ipython_savefig_dir
547 source_dir = os.path.dirname(self.state.document.current_source)
548 if savefig_dir is None:
549 savefig_dir = config.html_static_path
550 if isinstance(savefig_dir, list):
551 savefig_dir = savefig_dir[0] # safe to assume only one path?
552 savefig_dir = os.path.join(confdir, savefig_dir)
553
554 # get regex and prompt stuff
555 rgxin = config.ipython_rgxin
556 rgxout = config.ipython_rgxout
557 promptin = config.ipython_promptin
558 promptout = config.ipython_promptout
559
560 return savefig_dir, source_dir, rgxin, rgxout, promptin, promptout
561
562 def setup(self):
563 # get config values
564 (savefig_dir, source_dir, rgxin,
565 rgxout, promptin, promptout) = self.get_config_options()
566
567 # and attach to shell so we don't have to pass them around
568 self.shell.rgxin = rgxin
569 self.shell.rgxout = rgxout
570 self.shell.promptin = promptin
571 self.shell.promptout = promptout
572 self.shell.savefig_dir = savefig_dir
573 self.shell.source_dir = source_dir
434
574
435 def ipython_directive(name, arguments, options, content, lineno,
575 # setup bookmark for saving figures directory
436 content_offset, block_text, state, state_machine,
437 ):
438
576
439 debug = ipython_directive.DEBUG
577 self.shell.process_input_line('bookmark ipy_savedir %s'%savefig_dir,
440 shell.is_suppress = options.has_key('suppress')
578 store_history=False)
441 shell.is_doctest = options.has_key('doctest')
579 self.shell.clear_cout()
442 shell.is_verbatim = options.has_key('verbatim')
443
580
444 #print 'ipy', shell.is_suppress, options
581 return rgxin, rgxout, promptin, promptout
445 parts = '\n'.join(content).split('\n\n')
446 lines = ['.. sourcecode:: ipython', '']
447
582
448 figures = []
449 for part in parts:
450 block = block_parser(part)
451
583
452 if len(block):
584 def teardown(self):
453 rows, figure = shell.process_block(block)
585 # delete last bookmark
454 for row in rows:
586 self.shell.process_input_line('bookmark -d ipy_savedir',
455 lines.extend([' %s'%line for line in row.split('\n')])
587 store_history=False)
588 self.shell.clear_cout()
456
589
457 if figure is not None:
590 def run(self):
458 figures.append(figure)
591 debug = False
459
592
460 for figure in figures:
593 #TODO, any reason block_parser can't be a method of embeddable shell
461 lines.append('')
594 # then we wouldn't have to carry these around
462 lines.extend(figure.split('\n'))
595 rgxin, rgxout, promptin, promptout = self.setup()
463 lines.append('')
464
596
465 #print lines
597 options = self.options
466 if len(lines)>2:
598 self.shell.is_suppress = 'suppress' in options
467 if debug:
599 self.shell.is_doctest = 'doctest' in options
468 print '\n'.join(lines)
600 self.shell.is_verbatim = 'verbatim' in options
469 else:
470 #print 'INSERTING %d lines'%len(lines)
471 state_machine.insert_input(
472 lines, state_machine.input_lines.source(0))
473
601
474 return []
475
602
476 ipython_directive.DEBUG = False
603 # handle pure python code
477 ipython_directive.DEBUG = True # dbg
604 if 'python' in self.arguments:
605 content = self.content
606 self.content = self.shell.process_pure_python(content)
607
608 parts = '\n'.join(self.content).split('\n\n')
609
610 lines = ['.. code-block:: ipython','']
611 figures = []
612
613 for part in parts:
614
615 block = block_parser(part, rgxin, rgxout, promptin, promptout)
616
617 if len(block):
618 rows, figure = self.shell.process_block(block)
619 for row in rows:
620 lines.extend([' %s'%line for line in row.split('\n')])
621
622 if figure is not None:
623 figures.append(figure)
624
625 #text = '\n'.join(lines)
626 #figs = '\n'.join(figures)
627
628 for figure in figures:
629 lines.append('')
630 lines.extend(figure.split('\n'))
631 lines.append('')
632
633 #print lines
634 if len(lines)>2:
635 if debug:
636 print '\n'.join(lines)
637 else: #NOTE: this raises some errors, what's it for?
638 #print 'INSERTING %d lines'%len(lines)
639 self.state_machine.insert_input(
640 lines, self.state_machine.input_lines.source(0))
641
642 text = '\n'.join(lines)
643 txtnode = nodes.literal_block(text, text)
644 txtnode['language'] = 'ipython'
645 #imgnode = nodes.image(figs)
646
647 # cleanup
648 self.teardown()
649
650 return []#, imgnode]
478
651
479 # Enable as a proper Sphinx directive
652 # Enable as a proper Sphinx directive
480 def setup(app):
653 def setup(app):
481 setup.app = app
654 setup.app = app
482 options = {'suppress': directives.flag,
483 'doctest': directives.flag,
484 'verbatim': directives.flag,
485 }
486
655
487 app.add_directive('ipython', ipython_directive, True, (0, 2, 0), **options)
656 app.add_directive('ipython', IpythonDirective)
657 app.add_config_value('ipython_savefig_dir', None, True)
658 app.add_config_value('ipython_rgxin',
659 re.compile('In \[(\d+)\]:\s?(.*)\s*'), True)
660 app.add_config_value('ipython_rgxout',
661 re.compile('Out\[(\d+)\]:\s?(.*)\s*'), True)
662 app.add_config_value('ipython_promptin', 'In [%d]:', True)
663 app.add_config_value('ipython_promptout', 'Out[%d]:', True)
488
664
489
665
490 # Simple smoke test, needs to be converted to a proper automatic test.
666 # Simple smoke test, needs to be converted to a proper automatic test.
@@ -507,7 +683,7 b" In [3]: im = imread('stinkbug.png')"
507 @savefig mystinkbug.png width=4in
683 @savefig mystinkbug.png width=4in
508 In [4]: imshow(im)
684 In [4]: imshow(im)
509 Out[4]: <matplotlib.image.AxesImage object at 0x39ea850>
685 Out[4]: <matplotlib.image.AxesImage object at 0x39ea850>
510
686
511 """,
687 """,
512 r"""
688 r"""
513
689
@@ -542,7 +718,7 b' In [133]: import numpy.random'
542 In [134]: numpy.random.seed(2358)
718 In [134]: numpy.random.seed(2358)
543
719
544 @doctest
720 @doctest
545 In [135]: np.random.rand(10,2)
721 In [135]: numpy.random.rand(10,2)
546 Out[135]:
722 Out[135]:
547 array([[ 0.64524308, 0.59943846],
723 array([[ 0.64524308, 0.59943846],
548 [ 0.47102322, 0.8715456 ],
724 [ 0.47102322, 0.8715456 ],
@@ -615,6 +791,8 b' In [153]: grid(True)'
615
791
616 """,
792 """,
617 ]
793 ]
794 # skip local-file depending first example:
795 examples = examples[1:]
618
796
619 #ipython_directive.DEBUG = True # dbg
797 #ipython_directive.DEBUG = True # dbg
620 #options = dict(suppress=True) # dbg
798 #options = dict(suppress=True) # dbg
General Comments 0
You need to be logged in to leave comments. Login now