summaryrefslogtreecommitdiff
path: root/doc/sphinxext/numpydoc/plot_directive.py
diff options
context:
space:
mode:
Diffstat (limited to 'doc/sphinxext/numpydoc/plot_directive.py')
m---------doc/sphinxext0
-rw-r--r--doc/sphinxext/numpydoc/plot_directive.py642
2 files changed, 0 insertions, 642 deletions
diff --git a/doc/sphinxext b/doc/sphinxext
new file mode 160000
+Subproject 447dd0b59c2fe91ca9643701036d3d04919ddc7
diff --git a/doc/sphinxext/numpydoc/plot_directive.py b/doc/sphinxext/numpydoc/plot_directive.py
deleted file mode 100644
index 7547642a2..000000000
--- a/doc/sphinxext/numpydoc/plot_directive.py
+++ /dev/null
@@ -1,642 +0,0 @@
-"""
-A special directive for generating a matplotlib plot.
-
-.. warning::
-
- This is a hacked version of plot_directive.py from Matplotlib.
- It's very much subject to change!
-
-
-Usage
------
-
-Can be used like this::
-
- .. plot:: examples/example.py
-
- .. plot::
-
- import matplotlib.pyplot as plt
- plt.plot([1,2,3], [4,5,6])
-
- .. plot::
-
- A plotting example:
-
- >>> import matplotlib.pyplot as plt
- >>> plt.plot([1,2,3], [4,5,6])
-
-The content is interpreted as doctest formatted if it has a line starting
-with ``>>>``.
-
-The ``plot`` directive supports the options
-
- format : {'python', 'doctest'}
- Specify the format of the input
-
- include-source : bool
- Whether to display the source code. Default can be changed in conf.py
-
-and the ``image`` directive options ``alt``, ``height``, ``width``,
-``scale``, ``align``, ``class``.
-
-Configuration options
----------------------
-
-The plot directive has the following configuration options:
-
- plot_include_source
- Default value for the include-source option
-
- plot_pre_code
- Code that should be executed before each plot.
-
- plot_basedir
- Base directory, to which plot:: file names are relative to.
- (If None or empty, file names are relative to the directoly where
- the file containing the directive is.)
-
- plot_formats
- File formats to generate. List of tuples or strings::
-
- [(suffix, dpi), suffix, ...]
-
- that determine the file format and the DPI. For entries whose
- DPI was omitted, sensible defaults are chosen.
-
- plot_html_show_formats
- Whether to show links to the files in HTML.
-
-TODO
-----
-
-* Refactor Latex output; now it's plain images, but it would be nice
- to make them appear side-by-side, or in floats.
-
-"""
-from __future__ import division
-
-import sys, os, glob, shutil, imp, warnings, re, textwrap, traceback
-import sphinx
-
-if sys.version_info[0] >= 3:
- from io import StringIO
-else:
- from cStringIO import StringIO
-
-import warnings
-warnings.warn("A plot_directive module is also available under "
- "matplotlib.sphinxext; expect this numpydoc.plot_directive "
- "module to be deprecated after relevant features have been "
- "integrated there.",
- FutureWarning, stacklevel=2)
-
-
-#------------------------------------------------------------------------------
-# Registration hook
-#------------------------------------------------------------------------------
-
-def setup(app):
- setup.app = app
- setup.config = app.config
- setup.confdir = app.confdir
-
- app.add_config_value('plot_pre_code', '', True)
- app.add_config_value('plot_include_source', False, True)
- app.add_config_value('plot_formats', ['png', 'hires.png', 'pdf'], True)
- app.add_config_value('plot_basedir', None, True)
- app.add_config_value('plot_html_show_formats', True, True)
-
- app.add_directive('plot', plot_directive, True, (0, 1, False),
- **plot_directive_options)
-
-#------------------------------------------------------------------------------
-# plot:: directive
-#------------------------------------------------------------------------------
-from docutils.parsers.rst import directives
-from docutils import nodes
-
-def plot_directive(name, arguments, options, content, lineno,
- content_offset, block_text, state, state_machine):
- return run(arguments, content, options, state_machine, state, lineno)
-plot_directive.__doc__ = __doc__
-
-def _option_boolean(arg):
- if not arg or not arg.strip():
- # no argument given, assume used as a flag
- return True
- elif arg.strip().lower() in ('no', '0', 'false'):
- return False
- elif arg.strip().lower() in ('yes', '1', 'true'):
- return True
- else:
- raise ValueError('"%s" unknown boolean' % arg)
-
-def _option_format(arg):
- return directives.choice(arg, ('python', 'lisp'))
-
-def _option_align(arg):
- return directives.choice(arg, ("top", "middle", "bottom", "left", "center",
- "right"))
-
-plot_directive_options = {'alt': directives.unchanged,
- 'height': directives.length_or_unitless,
- 'width': directives.length_or_percentage_or_unitless,
- 'scale': directives.nonnegative_int,
- 'align': _option_align,
- 'class': directives.class_option,
- 'include-source': _option_boolean,
- 'format': _option_format,
- }
-
-#------------------------------------------------------------------------------
-# Generating output
-#------------------------------------------------------------------------------
-
-from docutils import nodes, utils
-
-try:
- # Sphinx depends on either Jinja or Jinja2
- import jinja2
- def format_template(template, **kw):
- return jinja2.Template(template).render(**kw)
-except ImportError:
- import jinja
- def format_template(template, **kw):
- return jinja.from_string(template, **kw)
-
-TEMPLATE = """
-{{ source_code }}
-
-{{ only_html }}
-
- {% if source_link or (html_show_formats and not multi_image) %}
- (
- {%- if source_link -%}
- `Source code <{{ source_link }}>`__
- {%- endif -%}
- {%- if html_show_formats and not multi_image -%}
- {%- for img in images -%}
- {%- for fmt in img.formats -%}
- {%- if source_link or not loop.first -%}, {% endif -%}
- `{{ fmt }} <{{ dest_dir }}/{{ img.basename }}.{{ fmt }}>`__
- {%- endfor -%}
- {%- endfor -%}
- {%- endif -%}
- )
- {% endif %}
-
- {% for img in images %}
- .. figure:: {{ build_dir }}/{{ img.basename }}.png
- {%- for option in options %}
- {{ option }}
- {% endfor %}
-
- {% if html_show_formats and multi_image -%}
- (
- {%- for fmt in img.formats -%}
- {%- if not loop.first -%}, {% endif -%}
- `{{ fmt }} <{{ dest_dir }}/{{ img.basename }}.{{ fmt }}>`__
- {%- endfor -%}
- )
- {%- endif -%}
- {% endfor %}
-
-{{ only_latex }}
-
- {% for img in images %}
- .. image:: {{ build_dir }}/{{ img.basename }}.pdf
- {% endfor %}
-
-"""
-
-class ImageFile(object):
- def __init__(self, basename, dirname):
- self.basename = basename
- self.dirname = dirname
- self.formats = []
-
- def filename(self, format):
- return os.path.join(self.dirname, "%s.%s" % (self.basename, format))
-
- def filenames(self):
- return [self.filename(fmt) for fmt in self.formats]
-
-def run(arguments, content, options, state_machine, state, lineno):
- if arguments and content:
- raise RuntimeError("plot:: directive can't have both args and content")
-
- document = state_machine.document
- config = document.settings.env.config
-
- options.setdefault('include-source', config.plot_include_source)
-
- # determine input
- rst_file = document.attributes['source']
- rst_dir = os.path.dirname(rst_file)
-
- if arguments:
- if not config.plot_basedir:
- source_file_name = os.path.join(rst_dir,
- directives.uri(arguments[0]))
- else:
- source_file_name = os.path.join(setup.confdir, config.plot_basedir,
- directives.uri(arguments[0]))
- code = open(source_file_name, 'r').read()
- output_base = os.path.basename(source_file_name)
- else:
- source_file_name = rst_file
- code = textwrap.dedent("\n".join(map(str, content)))
- counter = document.attributes.get('_plot_counter', 0) + 1
- document.attributes['_plot_counter'] = counter
- base, ext = os.path.splitext(os.path.basename(source_file_name))
- output_base = '%s-%d.py' % (base, counter)
-
- base, source_ext = os.path.splitext(output_base)
- if source_ext in ('.py', '.rst', '.txt'):
- output_base = base
- else:
- source_ext = ''
-
- # ensure that LaTeX includegraphics doesn't choke in foo.bar.pdf filenames
- output_base = output_base.replace('.', '-')
-
- # is it in doctest format?
- is_doctest = contains_doctest(code)
- if 'format' in options:
- if options['format'] == 'python':
- is_doctest = False
- else:
- is_doctest = True
-
- # determine output directory name fragment
- source_rel_name = relpath(source_file_name, setup.confdir)
- source_rel_dir = os.path.dirname(source_rel_name)
- while source_rel_dir.startswith(os.path.sep):
- source_rel_dir = source_rel_dir[1:]
-
- # build_dir: where to place output files (temporarily)
- build_dir = os.path.join(os.path.dirname(setup.app.doctreedir),
- 'plot_directive',
- source_rel_dir)
- if not os.path.exists(build_dir):
- os.makedirs(build_dir)
-
- # output_dir: final location in the builder's directory
- dest_dir = os.path.abspath(os.path.join(setup.app.builder.outdir,
- source_rel_dir))
-
- # how to link to files from the RST file
- dest_dir_link = os.path.join(relpath(setup.confdir, rst_dir),
- source_rel_dir).replace(os.path.sep, '/')
- build_dir_link = relpath(build_dir, rst_dir).replace(os.path.sep, '/')
- source_link = dest_dir_link + '/' + output_base + source_ext
-
- # make figures
- try:
- results = makefig(code, source_file_name, build_dir, output_base,
- config)
- errors = []
- except PlotError as err:
- reporter = state.memo.reporter
- sm = reporter.system_message(
- 2, "Exception occurred in plotting %s: %s" % (output_base, err),
- line=lineno)
- results = [(code, [])]
- errors = [sm]
-
- # generate output restructuredtext
- total_lines = []
- for j, (code_piece, images) in enumerate(results):
- if options['include-source']:
- if is_doctest:
- lines = ['']
- lines += [row.rstrip() for row in code_piece.split('\n')]
- else:
- lines = ['.. code-block:: python', '']
- lines += [' %s' % row.rstrip()
- for row in code_piece.split('\n')]
- source_code = "\n".join(lines)
- else:
- source_code = ""
-
- opts = [':%s: %s' % (key, val) for key, val in list(options.items())
- if key in ('alt', 'height', 'width', 'scale', 'align', 'class')]
-
- only_html = ".. only:: html"
- only_latex = ".. only:: latex"
-
- if j == 0:
- src_link = source_link
- else:
- src_link = None
-
- result = format_template(
- TEMPLATE,
- dest_dir=dest_dir_link,
- build_dir=build_dir_link,
- source_link=src_link,
- multi_image=len(images) > 1,
- only_html=only_html,
- only_latex=only_latex,
- options=opts,
- images=images,
- source_code=source_code,
- html_show_formats=config.plot_html_show_formats)
-
- total_lines.extend(result.split("\n"))
- total_lines.extend("\n")
-
- if total_lines:
- state_machine.insert_input(total_lines, source=source_file_name)
-
- # copy image files to builder's output directory
- if not os.path.exists(dest_dir):
- os.makedirs(dest_dir)
-
- for code_piece, images in results:
- for img in images:
- for fn in img.filenames():
- shutil.copyfile(fn, os.path.join(dest_dir,
- os.path.basename(fn)))
-
- # copy script (if necessary)
- if source_file_name == rst_file:
- target_name = os.path.join(dest_dir, output_base + source_ext)
- f = open(target_name, 'w')
- f.write(unescape_doctest(code))
- f.close()
-
- return errors
-
-
-#------------------------------------------------------------------------------
-# Run code and capture figures
-#------------------------------------------------------------------------------
-
-import matplotlib
-matplotlib.use('Agg')
-import matplotlib.pyplot as plt
-import matplotlib.image as image
-from matplotlib import _pylab_helpers
-
-import exceptions
-
-def contains_doctest(text):
- try:
- # check if it's valid Python as-is
- compile(text, '<string>', 'exec')
- return False
- except SyntaxError:
- pass
- r = re.compile(r'^\s*>>>', re.M)
- m = r.search(text)
- return bool(m)
-
-def unescape_doctest(text):
- """
- Extract code from a piece of text, which contains either Python code
- or doctests.
-
- """
- if not contains_doctest(text):
- return text
-
- code = ""
- for line in text.split("\n"):
- m = re.match(r'^\s*(>>>|\.\.\.) (.*)$', line)
- if m:
- code += m.group(2) + "\n"
- elif line.strip():
- code += "# " + line.strip() + "\n"
- else:
- code += "\n"
- return code
-
-def split_code_at_show(text):
- """
- Split code at plt.show()
-
- """
-
- parts = []
- is_doctest = contains_doctest(text)
-
- part = []
- for line in text.split("\n"):
- if (not is_doctest and line.strip() == 'plt.show()') or \
- (is_doctest and line.strip() == '>>> plt.show()'):
- part.append(line)
- parts.append("\n".join(part))
- part = []
- else:
- part.append(line)
- if "\n".join(part).strip():
- parts.append("\n".join(part))
- return parts
-
-class PlotError(RuntimeError):
- pass
-
-def run_code(code, code_path, ns=None):
- # Change the working directory to the directory of the example, so
- # it can get at its data files, if any.
- pwd = os.getcwd()
- old_sys_path = list(sys.path)
- if code_path is not None:
- dirname = os.path.abspath(os.path.dirname(code_path))
- os.chdir(dirname)
- sys.path.insert(0, dirname)
-
- # Redirect stdout
- stdout = sys.stdout
- sys.stdout = StringIO()
-
- # Reset sys.argv
- old_sys_argv = sys.argv
- sys.argv = [code_path]
-
- try:
- try:
- code = unescape_doctest(code)
- if ns is None:
- ns = {}
- if not ns:
- exec(setup.config.plot_pre_code, ns)
- exec(code, ns)
- except (Exception, SystemExit) as err:
- raise PlotError(traceback.format_exc())
- finally:
- os.chdir(pwd)
- sys.argv = old_sys_argv
- sys.path[:] = old_sys_path
- sys.stdout = stdout
- return ns
-
-
-#------------------------------------------------------------------------------
-# Generating figures
-#------------------------------------------------------------------------------
-
-def out_of_date(original, derived):
- """
- Returns True if derivative is out-of-date wrt original,
- both of which are full file paths.
- """
- return (not os.path.exists(derived)
- or os.stat(derived).st_mtime < os.stat(original).st_mtime)
-
-
-def makefig(code, code_path, output_dir, output_base, config):
- """
- Run a pyplot script *code* and save the images under *output_dir*
- with file names derived from *output_base*
-
- """
-
- # -- Parse format list
- default_dpi = {'png': 80, 'hires.png': 200, 'pdf': 50}
- formats = []
- for fmt in config.plot_formats:
- if isinstance(fmt, str):
- formats.append((fmt, default_dpi.get(fmt, 80)))
- elif type(fmt) in (tuple, list) and len(fmt)==2:
- formats.append((str(fmt[0]), int(fmt[1])))
- else:
- raise PlotError('invalid image format "%r" in plot_formats' % fmt)
-
- # -- Try to determine if all images already exist
-
- code_pieces = split_code_at_show(code)
-
- # Look for single-figure output files first
- all_exists = True
- img = ImageFile(output_base, output_dir)
- for format, dpi in formats:
- if out_of_date(code_path, img.filename(format)):
- all_exists = False
- break
- img.formats.append(format)
-
- if all_exists:
- return [(code, [img])]
-
- # Then look for multi-figure output files
- results = []
- all_exists = True
- for i, code_piece in enumerate(code_pieces):
- images = []
- for j in range(1000):
- img = ImageFile('%s_%02d_%02d' % (output_base, i, j), output_dir)
- for format, dpi in formats:
- if out_of_date(code_path, img.filename(format)):
- all_exists = False
- break
- img.formats.append(format)
-
- # assume that if we have one, we have them all
- if not all_exists:
- all_exists = (j > 0)
- break
- images.append(img)
- if not all_exists:
- break
- results.append((code_piece, images))
-
- if all_exists:
- return results
-
- # -- We didn't find the files, so build them
-
- results = []
- ns = {}
-
- for i, code_piece in enumerate(code_pieces):
- # Clear between runs
- plt.close('all')
-
- # Run code
- run_code(code_piece, code_path, ns)
-
- # Collect images
- images = []
- fig_managers = _pylab_helpers.Gcf.get_all_fig_managers()
- for j, figman in enumerate(fig_managers):
- if len(fig_managers) == 1 and len(code_pieces) == 1:
- img = ImageFile(output_base, output_dir)
- else:
- img = ImageFile("%s_%02d_%02d" % (output_base, i, j),
- output_dir)
- images.append(img)
- for format, dpi in formats:
- try:
- figman.canvas.figure.savefig(img.filename(format), dpi=dpi)
- except exceptions.BaseException as err:
- raise PlotError(traceback.format_exc())
- img.formats.append(format)
-
- # Results
- results.append((code_piece, images))
-
- return results
-
-
-#------------------------------------------------------------------------------
-# Relative pathnames
-#------------------------------------------------------------------------------
-
-try:
- from os.path import relpath
-except ImportError:
- # Copied from Python 2.7
- if 'posix' in sys.builtin_module_names:
- def relpath(path, start=os.path.curdir):
- """Return a relative version of a path"""
- from os.path import sep, curdir, join, abspath, commonprefix, \
- pardir
-
- if not path:
- raise ValueError("no path specified")
-
- start_list = abspath(start).split(sep)
- path_list = abspath(path).split(sep)
-
- # Work out how much of the filepath is shared by start and path.
- i = len(commonprefix([start_list, path_list]))
-
- rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
- if not rel_list:
- return curdir
- return join(*rel_list)
- elif 'nt' in sys.builtin_module_names:
- def relpath(path, start=os.path.curdir):
- """Return a relative version of a path"""
- from os.path import sep, curdir, join, abspath, commonprefix, \
- pardir, splitunc
-
- if not path:
- raise ValueError("no path specified")
- start_list = abspath(start).split(sep)
- path_list = abspath(path).split(sep)
- if start_list[0].lower() != path_list[0].lower():
- unc_path, rest = splitunc(path)
- unc_start, rest = splitunc(start)
- if bool(unc_path) ^ bool(unc_start):
- raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)"
- % (path, start))
- else:
- raise ValueError("path is on drive %s, start on drive %s"
- % (path_list[0], start_list[0]))
- # Work out how much of the filepath is shared by start and path.
- for i in range(min(len(start_list), len(path_list))):
- if start_list[i].lower() != path_list[i].lower():
- break
- else:
- i += 1
-
- rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
- if not rel_list:
- return curdir
- return join(*rel_list)
- else:
- raise RuntimeError("Unsupported platform (no relpath available!)")