|
From: <jd...@us...> - 2010-12-13 15:27:58
|
Revision: 8830
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=8830&view=rev
Author: jdh2358
Date: 2010-12-13 15:27:48 +0000 (Mon, 13 Dec 2010)
Log Message:
-----------
added ipython directive
Added Paths:
-----------
branches/v1_0_maint/lib/matplotlib/sphinxext/ipython_directive.py
Added: branches/v1_0_maint/lib/matplotlib/sphinxext/ipython_directive.py
===================================================================
--- branches/v1_0_maint/lib/matplotlib/sphinxext/ipython_directive.py (rev 0)
+++ branches/v1_0_maint/lib/matplotlib/sphinxext/ipython_directive.py 2010-12-13 15:27:48 UTC (rev 8830)
@@ -0,0 +1,567 @@
+import sys, os, shutil, imp, warnings, cStringIO, re
+
+import IPython
+from IPython.Shell import MatplotlibShell
+
+try:
+ from hashlib import md5
+except ImportError:
+ from md5 import md5
+
+from docutils.parsers.rst import directives
+import sphinx
+
+
+sphinx_version = sphinx.__version__.split(".")
+# The split is necessary for sphinx beta versions where the string is
+# '6b1'
+sphinx_version = tuple([int(re.split('[a-z]', x)[0])
+ for x in sphinx_version[:2]])
+
+
+
+COMMENT, INPUT, OUTPUT = range(3)
+rgxin = re.compile('In \[(\d+)\]:\s?(.*)\s*')
+rgxout = re.compile('Out\[(\d+)\]:\s?(.*)\s*')
+fmtin = 'In [%d]:'
+fmtout = 'Out[%d]:'
+
+def block_parser(part):
+ """
+ part is a string of ipython text, comprised of at most one
+ input, one ouput, comments, and blank lines. The block parser
+ parses the text into a list of::
+
+ blocks = [ (TOKEN0, data0), (TOKEN1, data1), ...]
+ where TOKEN is one of [COMMENT | INPUT | OUTPUT ] and
+ data is, depending on the type of token::
+
+ COMMENT : the comment string
+
+ INPUT: the (DECORATOR, INPUT_LINE, REST) where
+ DECORATOR: the input decorator (or None)
+ INPUT_LINE: the input as string (possibly multi-line)
+ REST : any stdout generated by the input line (not OUTPUT)
+
+
+ OUTPUT: the output string, possibly multi-line
+
+ """
+
+ block = []
+ lines = part.split('\n')
+ #print 'PARSE', lines
+ N = len(lines)
+ i = 0
+ decorator = None
+ while 1:
+
+ if i==N:
+ # nothing left to parse -- the last line
+ break
+
+ line = lines[i]
+ i += 1
+ line_stripped = line.strip()
+ if line_stripped.startswith('#'):
+ block.append((COMMENT, line))
+ continue
+
+
+ if line_stripped.startswith('@'):
+ # we're assuming at most one decorator -- may need to
+ # rethink
+ decorator = line_stripped
+ continue
+
+ # does this look like an input line?
+ matchin = rgxin.match(line)
+ if matchin:
+ lineno, inputline = int(matchin.group(1)), matchin.group(2)
+
+ # the ....: continuation string
+ continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
+ Nc = len(continuation)
+ # input lines can continue on for more than one line, if
+ # we have a '\' line continuation char or a function call
+ # echo line 'print'. The input line can only be
+ # terminated by the end of the block or an output line, so
+ # we parse out the rest of the input line if it is
+ # multiline as well as any echo text
+
+ rest = []
+ while i<N:
+
+ # look ahead; if the next line is blank, or a comment, or
+ # an output line, we're done
+
+ nextline = lines[i]
+ matchout = rgxout.match(nextline)
+ #print "nextline=%s, continuation=%s, starts=%s"%(nextline, continuation, nextline.startswith(continuation))
+ if matchout or nextline.startswith('#'):
+ break
+ elif nextline.startswith(continuation):
+ inputline += '\n' + nextline[Nc:]
+ else:
+ rest.append(nextline)
+ i+= 1
+
+ block.append((INPUT, (decorator, inputline, '\n'.join(rest))))
+ continue
+
+ # if it looks like an output line grab all the text to the end
+ # of the block
+ matchout = rgxout.match(line)
+ if matchout:
+ lineno, output = int(matchout.group(1)), matchout.group(2)
+ if i<N-1:
+ output = '\n'.join([output] + lines[i:])
+
+ #print 'OUTPUT', output
+ block.append((OUTPUT, output))
+ break
+
+ #print 'returning block', block
+ return block
+
+
+
+import matplotlib
+matplotlib.use('Agg')
+
+
+class EmbeddedSphinxShell:
+
+ def __init__(self):
+
+ self.cout = cStringIO.StringIO()
+
+ IPython.Shell.Term.cout = self.cout
+ IPython.Shell.Term.cerr = self.cout
+ argv = ['-autocall', '0']
+ self.user_ns = {}
+ self.user_glocal_ns = {}
+
+ self.IP = IPython.ipmaker.make_IPython(
+ argv, self.user_ns, self.user_glocal_ns, embedded=True,
+ #shell_class=IPython.Shell.InteractiveShell,
+ shell_class=MatplotlibShell,
+ rc_override=dict(colors = 'NoColor'))
+
+ self.input = ''
+ self.output = ''
+
+ self.is_verbatim = False
+ self.is_doctest = False
+ self.is_suppress = False
+
+ # on the first call to the savefig decorator, we'll import
+ # pyplot as plt so we can make a call to the plt.gcf().savefig
+ self._pyplot_imported = False
+
+ # we need bookmark the current dir first so we can save
+ # relative to it
+ self.process_input('bookmark ipy_basedir')
+ self.cout.seek(0)
+ self.cout.truncate(0)
+
+ def process_input(self, line):
+ 'process the input, capturing stdout'
+ #print "input='%s'"%self.input
+ stdout = sys.stdout
+ sys.stdout = self.cout
+ #self.IP.resetbuffer()
+ self.IP.push(self.IP.prefilter(line, 0))
+ #self.IP.runlines(line)
+ sys.stdout = stdout
+
+
+ def process_block(self, block):
+ """
+ process block from the block_parser and return a list of processed lines
+ """
+
+ #print 'BLOCK', block
+ ret = []
+
+ output = None
+ input_lines = None
+
+ m = rgxin.match(str(self.IP.outputcache.prompt1).strip())
+ lineno = int(m.group(1))
+
+ input_prompt = fmtin%lineno
+ output_prompt = fmtout%lineno
+ image_file = None
+ image_directive = None
+ for token, data in block:
+
+ if token==COMMENT:
+ if not self.is_suppress:
+ ret.append(data)
+
+ elif token==INPUT:
+
+ decorator, input, rest = data
+ #print 'INPUT:', data
+ is_verbatim = decorator=='@verbatim' or self.is_verbatim
+ is_doctest = decorator=='@doctest' or self.is_doctest
+ is_suppress = decorator=='@suppress' or self.is_suppress
+ is_savefig = decorator is not None and decorator.startswith('@savefig')
+ #print 'is_verbatim=%s, is_doctest=%s, is_suppress=%s, is_savefig=%s'%(is_verbatim, is_doctest, is_suppress, is_savefig)
+ input_lines = input.split('\n')
+
+
+ continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
+ Nc = len(continuation)
+
+ if is_savefig:
+ saveargs = decorator.split(' ')
+ filename = saveargs[1]
+ outfile = os.path.join('_static/%s'%filename)
+ # build out an image directive like
+ # .. image:: somefile.png
+ # :width 4in
+ #
+ # from an input like
+ # savefig somefile.png width=4in
+ imagerows = ['.. image:: %s'%outfile]
+
+ for kwarg in saveargs[2:]:
+ arg, val = kwarg.split('=')
+ arg = arg.strip()
+ val = val.strip()
+ imagerows.append(' :%s: %s'%(arg, val))
+
+
+ image_file = outfile
+ image_directive = '\n'.join(imagerows)
+
+
+
+ # TODO: can we get "rest" from ipython
+ #self.process_input('\n'.join(input_lines))
+
+
+ is_semicolon = False
+ for i, line in enumerate(input_lines):
+ if line.endswith(';'):
+ is_semicolon = True
+
+ if i==0:
+ # process the first input line
+ if is_verbatim:
+ self.process_input('')
+ else:
+ # only submit the line in non-verbatim mode
+ self.process_input(line)
+ formatted_line = '%s %s'%(input_prompt, line)
+ else:
+ # process a continuation line
+ if not is_verbatim:
+ self.process_input(line)
+
+ formatted_line = '%s %s'%(continuation, line)
+
+
+ if not is_suppress:
+ ret.append(formatted_line)
+
+ if not is_suppress:
+ if len(rest.strip()):
+ if is_verbatim:
+ # the "rest" is the standard output of the
+ # input, which needs to be added in
+ # verbatim mode
+ ret.append("%s"%rest)
+ ret.append('')
+
+ self.cout.seek(0)
+ output = self.cout.read()
+ if not is_suppress and not is_semicolon and not is_verbatim:
+ ret.append(output)
+
+ self.cout.truncate(0)
+
+
+
+
+ elif token==OUTPUT:
+ #print 'token==OUTPUT is_verbatim=%s'%is_verbatim
+ if is_verbatim:
+ # construct a mock output prompt
+ output = '%s %s\n'%(fmtout%lineno, data)
+ ret.append(output)
+
+ #print 'token==OUTPUT', output
+ if is_doctest:
+ submitted = data.strip()
+ found = output
+ if found is not None:
+ ind = found.find(output_prompt)
+ if ind<0:
+ raise RuntimeError('output prompt="%s" does not match out line=%s'%(output_prompt, found))
+ found = found[len(output_prompt):].strip()
+
+ if found!=submitted:
+ raise RuntimeError('doctest failure for input_lines="%s" with found_output="%s" and submitted output="%s"'%(input_lines, found, submitted))
+ #print 'doctest PASSED for input_lines="%s" with found_output="%s" and submitted output="%s"'%(input_lines, found, submitted)
+
+
+ if image_file is not None:
+ self.insure_pyplot()
+ command = 'plt.gcf().savefig("%s")'%image_file
+ #print 'SAVEFIG', command
+ self.process_input('bookmark ipy_thisdir')
+ self.process_input('cd -b ipy_basedir')
+ self.process_input(command)
+ self.process_input('cd -b ipy_thisdir')
+ self.cout.seek(0)
+ self.cout.truncate(0)
+
+ #print 'returning', ret, figure
+ return ret, image_directive
+
+
+ def insure_pyplot(self):
+ if self._pyplot_imported:
+ return
+ self.process_input('import matplotlib.pyplot as plt')
+
+
+
+shell = EmbeddedSphinxShell()
+
+
+def ipython_directive(name, arguments, options, content, lineno,
+ content_offset, block_text, state, state_machine,
+ ):
+
+ debug = ipython_directive.DEBUG
+ shell.is_suppress = options.has_key('suppress')
+ shell.is_doctest = options.has_key('doctest')
+ shell.is_verbatim = options.has_key('verbatim')
+
+ #print 'ipy', shell.is_suppress, options
+ parts = '\n'.join(content).split('\n\n')
+ lines = ['.. sourcecode:: ipython', '']
+
+ figures = []
+ for part in parts:
+ block = block_parser(part)
+
+ if len(block):
+ rows, figure = shell.process_block(block)
+ for row in rows:
+ lines.extend([' %s'%line for line in row.split('\n')])
+
+ if figure is not None:
+ figures.append(figure)
+
+ for figure in figures:
+ lines.append('')
+ lines.extend(figure.split('\n'))
+ lines.append('')
+
+ #print lines
+ if len(lines)>2:
+ if debug:
+ print '\n'.join(lines)
+ else:
+ #print 'INSERTING %d lines'%len(lines)
+ state_machine.insert_input(
+ lines, state_machine.input_lines.source(0))
+
+ return []
+
+ipython_directive.DEBUG = False
+
+def setup(app):
+ setup.app = app
+ options = {
+ 'suppress': directives.flag,
+ 'doctest': directives.flag,
+ 'verbatim': directives.flag,
+ }
+
+
+ app.add_directive('ipython', ipython_directive, True, (0, 2, 0), **options)
+
+
+def test():
+
+ examples = [
+ r"""
+In [9]: pwd
+Out[9]: '/home/jdhunter/py4science/book'
+
+In [10]: cd bookdata/
+/home/jdhunter/py4science/book/bookdata
+
+In [2]: from pylab import *
+
+In [2]: ion()
+
+In [3]: im = imread('stinkbug.png')
+
+@savefig mystinkbug.png width=4in
+In [4]: imshow(im)
+Out[4]: <matplotlib.image.AxesImage object at 0x39ea850>
+
+""",
+ r"""
+
+In [1]: x = 'hello world'
+
+# string methods can be
+# used to alter the string
+@doctest
+In [2]: x.upper()
+Out[2]: 'HELLO WORLD'
+
+@verbatim
+In [3]: x.st<TAB>
+x.startswith x.strip
+""",
+ r"""
+
+In [130]: url = 'http://ichart.finance.yahoo.com/table.csv?s=CROX\
+ .....: &d=9&e=22&f=2009&g=d&a=1&br=8&c=2006&ignore=.csv'
+
+In [131]: print url.split('&')
+--------> print(url.split('&'))
+['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']
+
+In [60]: import urllib
+
+""",
+ r"""\
+
+In [133]: import numpy.random
+
+@suppress
+In [134]: numpy.random.seed(2358)
+
+@doctest
+In [135]: np.random.rand(10,2)
+Out[135]:
+array([[ 0.64524308, 0.59943846],
+ [ 0.47102322, 0.8715456 ],
+ [ 0.29370834, 0.74776844],
+ [ 0.99539577, 0.1313423 ],
+ [ 0.16250302, 0.21103583],
+ [ 0.81626524, 0.1312433 ],
+ [ 0.67338089, 0.72302393],
+ [ 0.7566368 , 0.07033696],
+ [ 0.22591016, 0.77731835],
+ [ 0.0072729 , 0.34273127]])
+
+""",
+
+ r"""
+In [106]: print x
+--------> print(x)
+jdh
+
+In [109]: for i in range(10):
+ .....: print i
+ .....:
+ .....:
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
+""",
+
+ r"""
+
+In [144]: from pylab import *
+
+In [145]: ion()
+
+# use a semicolon to suppress the output
+@savefig test_hist.png width=4in
+In [151]: hist(np.random.randn(10000), 100);
+
+
+@savefig test_plot.png width=4in
+In [151]: plot(np.random.randn(10000), 'o');
+ """,
+
+ r"""
+# use a semicolon to suppress the output
+In [151]: plt.clf()
+
+@savefig plot_simple.png width=4in
+In [151]: plot([1,2,3])
+
+@savefig hist_simple.png width=4in
+In [151]: hist(np.random.randn(10000), 100);
+
+""",
+ r"""
+# update the current fig
+In [151]: ylabel('number')
+
+In [152]: title('normal distribution')
+
+
+@savefig hist_with_text.png
+In [153]: grid(True)
+
+ """,
+
+
+ r"""
+
+In [239]: 1/2
+@verbatim
+Out[239]: 0
+
+In [240]: 1.0/2.0
+Out[240]: 0.5
+""",
+
+ r"""
+@verbatim
+In [6]: pwd
+Out[6]: '/home/jdhunter/mypy'
+""",
+
+ r"""
+@verbatim
+In [151]: myfile.upper?
+Type: builtin_function_or_method
+Base Class: <type 'builtin_function_or_method'>
+String Form: <built-in method upper of str object at 0x980e2f0>
+Namespace: Interactive
+Docstring:
+ S.upper() -> string
+ Return a copy of the string S converted to uppercase.
+ """
+ ]
+
+
+
+ ipython_directive.DEBUG = True
+ #options = dict(suppress=True)
+ options = dict()
+ for example in examples:
+ content = example.split('\n')
+ ipython_directive('debug', arguments=None, options=options,
+ content=content, lineno=0,
+ content_offset=None, block_text=None,
+ state=None, state_machine=None,
+ )
+
+
+if __name__=='__main__':
+ test()
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|