[med-svn] [python-multipletau] 01/02: Imported Upstream version 0.1.4
Alex Mestiashvili
malex-guest at moszumanska.debian.org
Thu Apr 24 19:46:52 UTC 2014
This is an automated email from the git hooks/post-receive script.
malex-guest pushed a commit to branch master
in repository python-multipletau.
commit f10b637e2e564e60d43971cee784d32c51fd3029
Author: Alexandre Mestiashvili <alex at biotec.tu-dresden.de>
Date: Thu Apr 24 19:08:30 2014 +0200
Imported Upstream version 0.1.4
---
.gitignore | 39 ++
CHANGELOG.md | 8 +
LICENSE | 29 +
MANIFEST.in | 3 +
README.md | 29 +
README.txt | 16 +
doc/Makefile | 177 ++++++
doc/README.md | 17 +
doc/_extensions/apigen.py | 427 ++++++++++++++
doc/_extensions/copybutton.js | 51 ++
doc/_extensions/docscrape.py | 497 +++++++++++++++++
doc/_extensions/docscrape_sphinx.py | 136 +++++
doc/_extensions/github.py | 714 ++++++++++++++++++++++++
doc/_extensions/hidden_code_block.py | 128 +++++
doc/_extensions/inheritance_diagram.py | 407 ++++++++++++++
doc/_extensions/ipython_console_highlighting.py | 114 ++++
doc/_extensions/ipython_directive.py | 0
doc/_extensions/numpydoc.py | 116 ++++
doc/conf.py | 305 ++++++++++
doc/index.rst | 11 +
doc/make.bat | 242 ++++++++
doc/make.py | 14 +
doc/testing_multipletau.png | Bin 0 -> 77102 bytes
multipletau/__init__.py | 31 +
multipletau/_multipletau.py | 466 ++++++++++++++++
setup.cfg | 5 +
setup.py | 24 +
test/__init__.py | 138 +++++
test/__main__.py | 108 ++++
29 files changed, 4252 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b00b084
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,39 @@
+*.py[cod]
+
+# C extensions
+*.so
+
+# Packages
+*.egg
+*.egg-info
+dist
+build
+eggs
+parts
+bin
+var
+sdist
+develop-eggs
+.installed.cfg
+lib
+lib64
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+nosetests.xml
+
+# Translations
+*.mo
+
+# Mr Developer
+.mr.developer.cfg
+.project
+.pydevproject
+.settings*
+
+# doc build
+doc/_build/**
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..8044355
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,8 @@
+0.1.4
+- integer and boolean input types are now automatically converted to floats
+- `multipletau.correlate` now works with complex data types
+- `multipletau.correlate` now checks if input data are same objects
+- documentation now contains examples
+
+0.1.3
+- first non-cython implementation
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f2af26d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+Copyright (c) 2014 Paul Müller
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ 3. Neither the name of multipletau nor the names of its contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INFRAE OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..b2c1156
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,3 @@
+include CHANGELOG.md
+include README.txt
+include test/*.py
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..faf2982
--- /dev/null
+++ b/README.md
@@ -0,0 +1,29 @@
+multipletau
+===========
+
+This repo contains a multiple-tau algorithm for Python
+
+- **multipletau** multiple-tau package, implemented using [numpy](http://www.numpy.org/)
+- **test** testing the algorithm
+- **doc** the source of the [documentation](http://paulmueller.github.io/multipletau/)
+
+
+Installation
+============
+The package can be installed from the Python package index.
+
+ pip install multipletau
+
+
+Usage
+=====
+
+ >>> import numpy as np
+ >>> import multipletau
+ >>> a = np.linspace(2,5,42)
+ >>> v = np.linspace(1,6,42)
+ >>> multipletau.correlate(a, v, m=2)
+ array([[ 1. , 549.87804878],
+ [ 2. , 530.37477692],
+ [ 4. , 491.85812017],
+ [ 8. , 386.39500297]])
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..b8f0467
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,16 @@
+Multipe-tau correlation is computed on a logarithmic scale (less
+data points are computed) and is thus much faster than conventional
+correlation on a linear scale such as `numpy.correlate`.
+
+Reference
+=========
+The code is fully documented. An online reference is available
+at http://paulmueller.github.io/multipletau/.
+
+
+Installation
+============
+The package can be installed from the Python package index.
+
+ pip install multipletau
+
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 0000000..9ed47b9
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,177 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/multipletau.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/multipletau.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/multipletau"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/multipletau"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/doc/README.md b/doc/README.md
new file mode 100644
index 0000000..b0b54e4
--- /dev/null
+++ b/doc/README.md
@@ -0,0 +1,17 @@
+Documentation of multipletau
+============================
+
+In order to build the [documentation of multipletau](http://paulmueller.github.io/multipletau/) you need to have
+[Sphinx](http://sphinx-doc.org/) installed.
+You can install it by using pip or via apt-get:
+
+ pip install sphinx
+ apt-get install python-sphinx
+
+The documentation can be build with either of these commands:
+
+ python make.py
+ make html
+ make.bat (Windows)
+
+
diff --git a/doc/_extensions/apigen.py b/doc/_extensions/apigen.py
new file mode 100644
index 0000000..1237409
--- /dev/null
+++ b/doc/_extensions/apigen.py
@@ -0,0 +1,427 @@
+"""Attempt to generate templates for module reference with Sphinx
+
+XXX - we exclude extension modules
+
+To include extension modules, first identify them as valid in the
+``_uri2path`` method, then handle them in the ``_parse_module`` script.
+
+We get functions and classes by parsing the text of .py files.
+Alternatively we could import the modules for discovery, and we'd have
+to do that for extension modules. This would involve changing the
+``_parse_module`` method to work via import and introspection, and
+might involve changing ``discover_modules`` (which determines which
+files are modules, and therefore which module URIs will be passed to
+``_parse_module``).
+
+NOTE: this is a modified version of a script originally shipped with the
+PyMVPA project, which we've adapted for NIPY use. PyMVPA is an MIT-licensed
+project."""
+
+# Stdlib imports
+import os
+import re
+
+# Functions and classes
+class ApiDocWriter(object):
+ ''' Class for automatic detection and parsing of API docs
+ to Sphinx-parsable reST format'''
+
+ # only separating first two levels
+ rst_section_levels = ['*', '=', '-', '~', '^']
+
+ def __init__(self,
+ package_name,
+ rst_extension='.rst',
+ package_skip_patterns=None,
+ module_skip_patterns=None,
+ ):
+ ''' Initialize package for parsing
+
+ Parameters
+ ----------
+ package_name : string
+ Name of the top-level package. *package_name* must be the
+ name of an importable package
+ rst_extension : string, optional
+ Extension for reST files, default '.rst'
+ package_skip_patterns : None or sequence of {strings, regexps}
+ Sequence of strings giving URIs of packages to be excluded
+ Operates on the package path, starting at (including) the
+ first dot in the package path, after *package_name* - so,
+ if *package_name* is ``sphinx``, then ``sphinx.util`` will
+ result in ``.util`` being passed for earching by these
+ regexps. If is None, gives default. Default is:
+ ['\.tests$']
+ module_skip_patterns : None or sequence
+ Sequence of strings giving URIs of modules to be excluded
+ Operates on the module name including preceding URI path,
+ back to the first dot after *package_name*. For example
+ ``sphinx.util.console`` results in the string to search of
+ ``.util.console``
+ If is None, gives default. Default is:
+ ['\.setup$', '\._']
+ '''
+ if package_skip_patterns is None:
+ package_skip_patterns = ['\\.tests$']
+ if module_skip_patterns is None:
+ module_skip_patterns = ['\\.setup$', '\\._']
+ self.package_name = package_name
+ self.rst_extension = rst_extension
+ self.package_skip_patterns = package_skip_patterns
+ self.module_skip_patterns = module_skip_patterns
+
+ def get_package_name(self):
+ return self._package_name
+
+ def set_package_name(self, package_name):
+ ''' Set package_name
+
+ >>> docwriter = ApiDocWriter('sphinx')
+ >>> import sphinx
+ >>> docwriter.root_path == sphinx.__path__[0]
+ True
+ >>> docwriter.package_name = 'docutils'
+ >>> import docutils
+ >>> docwriter.root_path == docutils.__path__[0]
+ True
+ '''
+ # It's also possible to imagine caching the module parsing here
+ self._package_name = package_name
+ self.root_module = __import__(package_name)
+ self.root_path = self.root_module.__path__[0]
+ self.written_modules = None
+
+ package_name = property(get_package_name, set_package_name, None,
+ 'get/set package_name')
+
+ def _get_object_name(self, line):
+ ''' Get second token in line
+ >>> docwriter = ApiDocWriter('sphinx')
+ >>> docwriter._get_object_name(" def func(): ")
+ 'func'
+ >>> docwriter._get_object_name(" class Klass(object): ")
+ 'Klass'
+ >>> docwriter._get_object_name(" class Klass: ")
+ 'Klass'
+ '''
+ name = line.split()[1].split('(')[0].strip()
+ # in case we have classes which are not derived from object
+ # ie. old style classes
+ return name.rstrip(':')
+
+ def _uri2path(self, uri):
+ ''' Convert uri to absolute filepath
+
+ Parameters
+ ----------
+ uri : string
+ URI of python module to return path for
+
+ Returns
+ -------
+ path : None or string
+ Returns None if there is no valid path for this URI
+ Otherwise returns absolute file system path for URI
+
+ Examples
+ --------
+ >>> docwriter = ApiDocWriter('sphinx')
+ >>> import sphinx
+ >>> modpath = sphinx.__path__[0]
+ >>> res = docwriter._uri2path('sphinx.builder')
+ >>> res == os.path.join(modpath, 'builder.py')
+ True
+ >>> res = docwriter._uri2path('sphinx')
+ >>> res == os.path.join(modpath, '__init__.py')
+ True
+ >>> docwriter._uri2path('sphinx.does_not_exist')
+
+ '''
+ if uri == self.package_name:
+ return os.path.join(self.root_path, '__init__.py')
+ path = uri.replace('.', os.path.sep)
+ path = path.replace(self.package_name + os.path.sep, '')
+ path = os.path.join(self.root_path, path)
+ # XXX maybe check for extensions as well?
+ if os.path.exists(path + '.py'): # file
+ path += '.py'
+ elif os.path.exists(os.path.join(path, '__init__.py')):
+ path = os.path.join(path, '__init__.py')
+ else:
+ return None
+ return path
+
+ def _path2uri(self, dirpath):
+ ''' Convert directory path to uri '''
+ relpath = dirpath.replace(self.root_path, self.package_name)
+ if relpath.startswith(os.path.sep):
+ relpath = relpath[1:]
+ return relpath.replace(os.path.sep, '.')
+
+ def _parse_module(self, uri):
+ ''' Parse module defined in *uri* '''
+ filename = self._uri2path(uri)
+ if filename is None:
+ # nothing that we could handle here.
+ return ([],[])
+ f = open(filename, 'rt')
+ functions, classes = self._parse_lines(f)
+ f.close()
+ return functions, classes
+
+ def _parse_lines(self, linesource):
+ ''' Parse lines of text for functions and classes '''
+ functions = []
+ classes = []
+ for line in linesource:
+ if line.startswith('def ') and line.count('('):
+ # exclude private stuff
+ name = self._get_object_name(line)
+ if not name.startswith('_'):
+ functions.append(name)
+ elif line.startswith('class '):
+ # exclude private stuff
+ name = self._get_object_name(line)
+ if not name.startswith('_'):
+ classes.append(name)
+ else:
+ pass
+ functions.sort()
+ classes.sort()
+ return functions, classes
+
+ def generate_api_doc(self, uri):
+ '''Make autodoc documentation template string for a module
+
+ Parameters
+ ----------
+ uri : string
+ python location of module - e.g 'sphinx.builder'
+
+ Returns
+ -------
+ S : string
+ Contents of API doc
+ '''
+ # get the names of all classes and functions
+ functions, classes = self._parse_module(uri)
+ if not len(functions) and not len(classes):
+ print 'WARNING: Empty -',uri # dbg
+ return ''
+
+ # Make a shorter version of the uri that omits the package name for
+ # titles
+ uri_short = re.sub(r'^%s\.' % self.package_name,'',uri)
+
+ ad = '.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n'
+
+ chap_title = uri_short
+ ad += (chap_title+'\n'+ self.rst_section_levels[1] * len(chap_title)
+ + '\n\n')
+
+ # Set the chapter title to read 'module' for all modules except for the
+ # main packages
+ if '.' in uri:
+ title = 'Module: :mod:`' + uri_short + '`'
+ else:
+ title = ':mod:`' + uri_short + '`'
+ ad += title + '\n' + self.rst_section_levels[2] * len(title)
+
+ if len(classes):
+ ad += '\nInheritance diagram for ``%s``:\n\n' % uri
+ ad += '.. inheritance-diagram:: %s \n' % uri
+ ad += ' :parts: 3\n'
+
+ ad += '\n.. automodule:: ' + uri + '\n'
+ ad += '\n.. currentmodule:: ' + uri + '\n'
+ multi_class = len(classes) > 1
+ multi_fx = len(functions) > 1
+ if multi_class:
+ ad += '\n' + 'Classes' + '\n' + \
+ self.rst_section_levels[2] * 7 + '\n'
+ elif len(classes) and multi_fx:
+ ad += '\n' + 'Class' + '\n' + \
+ self.rst_section_levels[2] * 5 + '\n'
+ for c in classes:
+ ad += '\n:class:`' + c + '`\n' \
+ + self.rst_section_levels[multi_class + 2 ] * \
+ (len(c)+9) + '\n\n'
+ ad += '\n.. autoclass:: ' + c + '\n'
+ # must NOT exclude from index to keep cross-refs working
+ ad += ' :members:\n' \
+ ' :undoc-members:\n' \
+ ' :show-inheritance:\n' \
+ ' :inherited-members:\n' \
+ '\n' \
+ ' .. automethod:: __init__\n'
+ if multi_fx:
+ ad += '\n' + 'Functions' + '\n' + \
+ self.rst_section_levels[2] * 9 + '\n\n'
+ elif len(functions) and multi_class:
+ ad += '\n' + 'Function' + '\n' + \
+ self.rst_section_levels[2] * 8 + '\n\n'
+ for f in functions:
+ # must NOT exclude from index to keep cross-refs working
+ ad += '\n.. autofunction:: ' + uri + '.' + f + '\n\n'
+ return ad
+
+ def _survives_exclude(self, matchstr, match_type):
+ ''' Returns True if *matchstr* does not match patterns
+
+ ``self.package_name`` removed from front of string if present
+
+ Examples
+ --------
+ >>> dw = ApiDocWriter('sphinx')
+ >>> dw._survives_exclude('sphinx.okpkg', 'package')
+ True
+ >>> dw.package_skip_patterns.append('^\\.badpkg$')
+ >>> dw._survives_exclude('sphinx.badpkg', 'package')
+ False
+ >>> dw._survives_exclude('sphinx.badpkg', 'module')
+ True
+ >>> dw._survives_exclude('sphinx.badmod', 'module')
+ True
+ >>> dw.module_skip_patterns.append('^\\.badmod$')
+ >>> dw._survives_exclude('sphinx.badmod', 'module')
+ False
+ '''
+ if match_type == 'module':
+ patterns = self.module_skip_patterns
+ elif match_type == 'package':
+ patterns = self.package_skip_patterns
+ else:
+ raise ValueError('Cannot interpret match type "%s"'
+ % match_type)
+ # Match to URI without package name
+ L = len(self.package_name)
+ if matchstr[:L] == self.package_name:
+ matchstr = matchstr[L:]
+ for pat in patterns:
+ try:
+ pat.search
+ except AttributeError:
+ pat = re.compile(pat)
+ if pat.search(matchstr):
+ return False
+ return True
+
+ def discover_modules(self):
+ ''' Return module sequence discovered from ``self.package_name``
+
+
+ Parameters
+ ----------
+ None
+
+ Returns
+ -------
+ mods : sequence
+ Sequence of module names within ``self.package_name``
+
+ Examples
+ --------
+ >>> dw = ApiDocWriter('sphinx')
+ >>> mods = dw.discover_modules()
+ >>> 'sphinx.util' in mods
+ True
+ >>> dw.package_skip_patterns.append('\.util$')
+ >>> 'sphinx.util' in dw.discover_modules()
+ False
+ >>>
+ '''
+ modules = [self.package_name]
+ # raw directory parsing
+ for dirpath, dirnames, filenames in os.walk(self.root_path):
+ # Check directory names for packages
+ root_uri = self._path2uri(os.path.join(self.root_path,
+ dirpath))
+ for dirname in dirnames[:]: # copy list - we modify inplace
+ package_uri = '.'.join((root_uri, dirname))
+ if (self._uri2path(package_uri) and
+ self._survives_exclude(package_uri, 'package')):
+ modules.append(package_uri)
+ else:
+ dirnames.remove(dirname)
+ # Check filenames for modules
+ for filename in filenames:
+ module_name = filename[:-3]
+ module_uri = '.'.join((root_uri, module_name))
+ if (self._uri2path(module_uri) and
+ self._survives_exclude(module_uri, 'module')):
+ modules.append(module_uri)
+ return sorted(modules)
+
+ def write_modules_api(self, modules,outdir):
+ # write the list
+ written_modules = []
+ for m in modules:
+ api_str = self.generate_api_doc(m)
+ if not api_str:
+ continue
+ # write out to file
+ outfile = os.path.join(outdir,
+ m + self.rst_extension)
+ fileobj = open(outfile, 'wt')
+ fileobj.write(api_str)
+ fileobj.close()
+ written_modules.append(m)
+ self.written_modules = written_modules
+
+ def write_api_docs(self, outdir):
+ """Generate API reST files.
+
+ Parameters
+ ----------
+ outdir : string
+ Directory name in which to store files
+ We create automatic filenames for each module
+
+ Returns
+ -------
+ None
+
+ Notes
+ -----
+ Sets self.written_modules to list of written modules
+ """
+ if not os.path.exists(outdir):
+ os.mkdir(outdir)
+ # compose list of modules
+ modules = self.discover_modules()
+ self.write_modules_api(modules,outdir)
+
+ def write_index(self, outdir, froot='gen', relative_to=None):
+ """Make a reST API index file from written files
+
+ Parameters
+ ----------
+ path : string
+ Filename to write index to
+ outdir : string
+ Directory to which to write generated index file
+ froot : string, optional
+ root (filename without extension) of filename to write to
+ Defaults to 'gen'. We add ``self.rst_extension``.
+ relative_to : string
+ path to which written filenames are relative. This
+ component of the written file path will be removed from
+ outdir, in the generated index. Default is None, meaning,
+ leave path as it is.
+ """
+ if self.written_modules is None:
+ raise ValueError('No modules written')
+ # Get full filename path
+ path = os.path.join(outdir, froot+self.rst_extension)
+ # Path written into index is relative to rootpath
+ if relative_to is not None:
+ relpath = outdir.replace(relative_to + os.path.sep, '')
+ else:
+ relpath = outdir
+ idx = open(path,'wt')
+ w = idx.write
+ w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n')
+ w('.. toctree::\n\n')
+ for f in self.written_modules:
+ w(' %s\n' % os.path.join(relpath,f))
+ idx.close()
diff --git a/doc/_extensions/copybutton.js b/doc/_extensions/copybutton.js
new file mode 100644
index 0000000..fe39f87
--- /dev/null
+++ b/doc/_extensions/copybutton.js
@@ -0,0 +1,51 @@
+$(document).ready(function() {
+ /* Add a [>>>] button on the top-right corner of code samples to hide
+ * the >>> and ... prompts and the output and thus make the code
+ * copyable. */
+ var div = $('.highlight-python .highlight,' +
+ '.highlight-python3 .highlight')
+ var pre = div.find('pre');
+ // get the styles from the current theme
+ var hide_text = 'Hide the prompts and output';
+ var show_text = 'Show the prompts and output';
+ var border_width = pre.css('border-top-width');
+ var border_style = pre.css('border-top-style');
+ var border_color = pre.css('border-top-color');
+ var button_styles = {
+ 'cursor':'pointer', 'float': 'right', 'margin-top': '-6px', 'margin-right': '-5px',
+ 'border-color': border_color, 'border-style': border_style,
+ 'border-width': border_width, 'color': border_color, 'text-size': '75%',
+ 'font-family': 'monospace', 'padding-left': '0.2em', 'padding-right': '0.2em'
+ }
+ // create and add the button to all the code blocks that contain >>>
+ div.each(function(index) {
+ var jthis = $(this);
+ if (jthis.find('.gp').length > 0) {
+ var button = $('<span class="copybutton">>>></span>');
+ button.css(button_styles)
+ button.attr('title', hide_text);
+ jthis.find('pre').prepend(button);
+ }
+ // tracebacks (.gt) contain bare text elements that need to be
+ // wrapped in a span to work with .nextUntil() (see later)
+ jthis.find('pre:has(.gt)').contents().filter(function() {
+ return ((this.nodeType == 3) && (this.data.trim().length > 0));
+ }).wrap('<span>');
+ });
+ // define the behavior of the button when it's clicked
+ $('.copybutton').toggle(
+ function() {
+ var button = $(this);
+ button.parent().find('.go, .gp, .gt').hide();
+ button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'hidden');
+ button.css('text-decoration', 'line-through');
+ button.attr('title', show_text);
+ },
+ function() {
+ var button = $(this);
+ button.parent().find('.go, .gp, .gt').show();
+ button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'visible');
+ button.css('text-decoration', 'none');
+ button.attr('title', hide_text);
+ });
+});
diff --git a/doc/_extensions/docscrape.py b/doc/_extensions/docscrape.py
new file mode 100644
index 0000000..f374b3d
--- /dev/null
+++ b/doc/_extensions/docscrape.py
@@ -0,0 +1,497 @@
+"""Extract reference documentation from the NumPy source tree.
+
+"""
+
+import inspect
+import textwrap
+import re
+import pydoc
+from StringIO import StringIO
+from warnings import warn
+4
+class Reader(object):
+ """A line-based string reader.
+
+ """
+ def __init__(self, data):
+ """
+ Parameters
+ ----------
+ data : str
+ String with lines separated by '\n'.
+
+ """
+ if isinstance(data,list):
+ self._str = data
+ else:
+ self._str = data.split('\n') # store string as list of lines
+
+ self.reset()
+
+ def __getitem__(self, n):
+ return self._str[n]
+
+ def reset(self):
+ self._l = 0 # current line nr
+
+ def read(self):
+ if not self.eof():
+ out = self[self._l]
+ self._l += 1
+ return out
+ else:
+ return ''
+
+ def seek_next_non_empty_line(self):
+ for l in self[self._l:]:
+ if l.strip():
+ break
+ else:
+ self._l += 1
+
+ def eof(self):
+ return self._l >= len(self._str)
+
+ def read_to_condition(self, condition_func):
+ start = self._l
+ for line in self[start:]:
+ if condition_func(line):
+ return self[start:self._l]
+ self._l += 1
+ if self.eof():
+ return self[start:self._l+1]
+ return []
+
+ def read_to_next_empty_line(self):
+ self.seek_next_non_empty_line()
+ def is_empty(line):
+ return not line.strip()
+ return self.read_to_condition(is_empty)
+
+ def read_to_next_unindented_line(self):
+ def is_unindented(line):
+ return (line.strip() and (len(line.lstrip()) == len(line)))
+ return self.read_to_condition(is_unindented)
+
+ def peek(self,n=0):
+ if self._l + n < len(self._str):
+ return self[self._l + n]
+ else:
+ return ''
+
+ def is_empty(self):
+ return not ''.join(self._str).strip()
+
+
+class NumpyDocString(object):
+ def __init__(self,docstring):
+ docstring = textwrap.dedent(docstring).split('\n')
+
+ self._doc = Reader(docstring)
+ self._parsed_data = {
+ 'Signature': '',
+ 'Summary': [''],
+ 'Extended Summary': [],
+ 'Parameters': [],
+ 'Returns': [],
+ 'Raises': [],
+ 'Warns': [],
+ 'Other Parameters': [],
+ 'Attributes': [],
+ 'Methods': [],
+ 'See Also': [],
+ 'Notes': [],
+ 'Warnings': [],
+ 'References': '',
+ 'Examples': '',
+ 'index': {}
+ }
+
+ self._parse()
+
+ def __getitem__(self,key):
+ return self._parsed_data[key]
+
+ def __setitem__(self,key,val):
+ if not self._parsed_data.has_key(key):
+ warn("Unknown section %s" % key)
+ else:
+ self._parsed_data[key] = val
+
+ def _is_at_section(self):
+ self._doc.seek_next_non_empty_line()
+
+ if self._doc.eof():
+ return False
+
+ l1 = self._doc.peek().strip() # e.g. Parameters
+
+ if l1.startswith('.. index::'):
+ return True
+
+ l2 = self._doc.peek(1).strip() # ---------- or ==========
+ return l2.startswith('-'*len(l1)) or l2.startswith('='*len(l1))
+
+ def _strip(self,doc):
+ i = 0
+ j = 0
+ for i,line in enumerate(doc):
+ if line.strip(): break
+
+ for j,line in enumerate(doc[::-1]):
+ if line.strip(): break
+
+ return doc[i:len(doc)-j]
+
+ def _read_to_next_section(self):
+ section = self._doc.read_to_next_empty_line()
+
+ while not self._is_at_section() and not self._doc.eof():
+ if not self._doc.peek(-1).strip(): # previous line was empty
+ section += ['']
+
+ section += self._doc.read_to_next_empty_line()
+
+ return section
+
+ def _read_sections(self):
+ while not self._doc.eof():
+ data = self._read_to_next_section()
+ name = data[0].strip()
+
+ if name.startswith('..'): # index section
+ yield name, data[1:]
+ elif len(data) < 2:
+ yield StopIteration
+ else:
+ yield name, self._strip(data[2:])
+
+ def _parse_param_list(self,content):
+ r = Reader(content)
+ params = []
+ while not r.eof():
+ header = r.read().strip()
+ if ' : ' in header:
+ arg_name, arg_type = header.split(' : ')[:2]
+ else:
+ arg_name, arg_type = header, ''
+
+ desc = r.read_to_next_unindented_line()
+ desc = dedent_lines(desc)
+
+ params.append((arg_name,arg_type,desc))
+
+ return params
+
+
+ _name_rgx = re.compile(r"^\s*(:(?P<role>\w+):`(?P<name>[a-zA-Z0-9_.-]+)`|"
+ r" (?P<name2>[a-zA-Z0-9_.-]+))\s*", re.X)
+ def _parse_see_also(self, content):
+ """
+ func_name : Descriptive text
+ continued text
+ another_func_name : Descriptive text
+ func_name1, func_name2, :meth:`func_name`, func_name3
+
+ """
+ items = []
+
+ def parse_item_name(text):
+ """Match ':role:`name`' or 'name'"""
+ m = self._name_rgx.match(text)
+ if m:
+ g = m.groups()
+ if g[1] is None:
+ return g[3], None
+ else:
+ return g[2], g[1]
+ raise ValueError("%s is not a item name" % text)
+
+ def push_item(name, rest):
+ if not name:
+ return
+ name, role = parse_item_name(name)
+ items.append((name, list(rest), role))
+ del rest[:]
+
+ current_func = None
+ rest = []
+
+ for line in content:
+ if not line.strip(): continue
+
+ m = self._name_rgx.match(line)
+ if m and line[m.end():].strip().startswith(':'):
+ push_item(current_func, rest)
+ current_func, line = line[:m.end()], line[m.end():]
+ rest = [line.split(':', 1)[1].strip()]
+ if not rest[0]:
+ rest = []
+ elif not line.startswith(' '):
+ push_item(current_func, rest)
+ current_func = None
+ if ',' in line:
+ for func in line.split(','):
+ push_item(func, [])
+ elif line.strip():
+ current_func = line
+ elif current_func is not None:
+ rest.append(line.strip())
+ push_item(current_func, rest)
+ return items
+
+ def _parse_index(self, section, content):
+ """
+ .. index: default
+ :refguide: something, else, and more
+
+ """
+ def strip_each_in(lst):
+ return [s.strip() for s in lst]
+
+ out = {}
+ section = section.split('::')
+ if len(section) > 1:
+ out['default'] = strip_each_in(section[1].split(','))[0]
+ for line in content:
+ line = line.split(':')
+ if len(line) > 2:
+ out[line[1]] = strip_each_in(line[2].split(','))
+ return out
+
+ def _parse_summary(self):
+ """Grab signature (if given) and summary"""
+ if self._is_at_section():
+ return
+
+ summary = self._doc.read_to_next_empty_line()
+ summary_str = " ".join([s.strip() for s in summary]).strip()
+ if re.compile('^([\w., ]+=)?\s*[\w\.]+\(.*\)$').match(summary_str):
+ self['Signature'] = summary_str
+ if not self._is_at_section():
+ self['Summary'] = self._doc.read_to_next_empty_line()
+ else:
+ self['Summary'] = summary
+
+ if not self._is_at_section():
+ self['Extended Summary'] = self._read_to_next_section()
+
+ def _parse(self):
+ self._doc.reset()
+ self._parse_summary()
+
+ for (section,content) in self._read_sections():
+ if not section.startswith('..'):
+ section = ' '.join([s.capitalize() for s in section.split(' ')])
+ if section in ('Parameters', 'Attributes', 'Methods',
+ 'Returns', 'Raises', 'Warns'):
+ self[section] = self._parse_param_list(content)
+ elif section.startswith('.. index::'):
+ self['index'] = self._parse_index(section, content)
+ elif section == 'See Also':
+ self['See Also'] = self._parse_see_also(content)
+ else:
+ self[section] = content
+
+ # string conversion routines
+
+ def _str_header(self, name, symbol='-'):
+ return [name, len(name)*symbol]
+
+ def _str_indent(self, doc, indent=4):
+ out = []
+ for line in doc:
+ out += [' '*indent + line]
+ return out
+
+ def _str_signature(self):
+ if self['Signature']:
+ return [self['Signature'].replace('*','\*')] + ['']
+ else:
+ return ['']
+
+ def _str_summary(self):
+ if self['Summary']:
+ return self['Summary'] + ['']
+ else:
+ return []
+
+ def _str_extended_summary(self):
+ if self['Extended Summary']:
+ return self['Extended Summary'] + ['']
+ else:
+ return []
+
+ def _str_param_list(self, name):
+ out = []
+ if self[name]:
+ out += self._str_header(name)
+ for param,param_type,desc in self[name]:
+ out += ['%s : %s' % (param, param_type)]
+ out += self._str_indent(desc)
+ out += ['']
+ return out
+
+ def _str_section(self, name):
+ out = []
+ if self[name]:
+ out += self._str_header(name)
+ out += self[name]
+ out += ['']
+ return out
+
+ def _str_see_also(self, func_role):
+ if not self['See Also']: return []
+ out = []
+ out += self._str_header("See Also")
+ last_had_desc = True
+ for func, desc, role in self['See Also']:
+ if role:
+ link = ':%s:`%s`' % (role, func)
+ elif func_role:
+ link = ':%s:`%s`' % (func_role, func)
+ else:
+ link = "`%s`_" % func
+ if desc or last_had_desc:
+ out += ['']
+ out += [link]
+ else:
+ out[-1] += ", %s" % link
+ if desc:
+ out += self._str_indent([' '.join(desc)])
+ last_had_desc = True
+ else:
+ last_had_desc = False
+ out += ['']
+ return out
+
+ def _str_index(self):
+ idx = self['index']
+ out = []
+ out += ['.. index:: %s' % idx.get('default','')]
+ for section, references in idx.iteritems():
+ if section == 'default':
+ continue
+ out += [' :%s: %s' % (section, ', '.join(references))]
+ return out
+
+ def __str__(self, func_role=''):
+ out = []
+ out += self._str_signature()
+ out += self._str_summary()
+ out += self._str_extended_summary()
+ for param_list in ('Parameters','Returns','Raises'):
+ out += self._str_param_list(param_list)
+ out += self._str_section('Warnings')
+ out += self._str_see_also(func_role)
+ for s in ('Notes','References','Examples'):
+ out += self._str_section(s)
+ out += self._str_index()
+ return '\n'.join(out)
+
+
+def indent(str,indent=4):
+ indent_str = ' '*indent
+ if str is None:
+ return indent_str
+ lines = str.split('\n')
+ return '\n'.join(indent_str + l for l in lines)
+
+def dedent_lines(lines):
+ """Deindent a list of lines maximally"""
+ return textwrap.dedent("\n".join(lines)).split("\n")
+
+def header(text, style='-'):
+ return text + '\n' + style*len(text) + '\n'
+
+
+class FunctionDoc(NumpyDocString):
+ def __init__(self, func, role='func', doc=None):
+ self._f = func
+ self._role = role # e.g. "func" or "meth"
+ if doc is None:
+ doc = inspect.getdoc(func) or ''
+ try:
+ NumpyDocString.__init__(self, doc)
+ except ValueError, e:
+ print '*'*78
+ print "ERROR: '%s' while parsing `%s`" % (e, self._f)
+ print '*'*78
+ #print "Docstring follows:"
+ #print doclines
+ #print '='*78
+
+ if not self['Signature']:
+ func, func_name = self.get_func()
+ try:
+ # try to read signature
+ argspec = inspect.getargspec(func)
+ argspec = inspect.formatargspec(*argspec)
+ argspec = argspec.replace('*','\*')
+ signature = '%s%s' % (func_name, argspec)
+ except TypeError, e:
+ signature = '%s()' % func_name
+ self['Signature'] = signature
+
+ def get_func(self):
+ func_name = getattr(self._f, '__name__', self.__class__.__name__)
+ if inspect.isclass(self._f):
+ func = getattr(self._f, '__call__', self._f.__init__)
+ else:
+ func = self._f
+ return func, func_name
+
+ def __str__(self):
+ out = ''
+
+ func, func_name = self.get_func()
+ signature = self['Signature'].replace('*', '\*')
+
+ roles = {'func': 'function',
+ 'meth': 'method'}
+
+ if self._role:
+ if not roles.has_key(self._role):
+ print "Warning: invalid role %s" % self._role
+ out += '.. %s:: %s\n \n\n' % (roles.get(self._role,''),
+ func_name)
+
+ out += super(FunctionDoc, self).__str__(func_role=self._role)
+ return out
+
+
+class ClassDoc(NumpyDocString):
+ def __init__(self,cls,modulename='',func_doc=FunctionDoc,doc=None):
+ if not inspect.isclass(cls):
+ raise ValueError("Initialise using a class. Got %r" % cls)
+ self._cls = cls
+
+ if modulename and not modulename.endswith('.'):
+ modulename += '.'
+ self._mod = modulename
+ self._name = cls.__name__
+ self._func_doc = func_doc
+
+ if doc is None:
+ doc = pydoc.getdoc(cls)
+
+ NumpyDocString.__init__(self, doc)
+
+ @property
+ def methods(self):
+ return [name for name,func in inspect.getmembers(self._cls)
+ if not name.startswith('_') and callable(func)]
+
+ def __str__(self):
+ out = ''
+ out += super(ClassDoc, self).__str__()
+ out += "\n\n"
+
+ #for m in self.methods:
+ # print "Parsing `%s`" % m
+ # out += str(self._func_doc(getattr(self._cls,m), 'meth')) + '\n\n'
+ # out += '.. index::\n single: %s; %s\n\n' % (self._name, m)
+
+ return out
+
+
diff --git a/doc/_extensions/docscrape_sphinx.py b/doc/_extensions/docscrape_sphinx.py
new file mode 100644
index 0000000..77ed271
--- /dev/null
+++ b/doc/_extensions/docscrape_sphinx.py
@@ -0,0 +1,136 @@
+import re, inspect, textwrap, pydoc
+from docscrape import NumpyDocString, FunctionDoc, ClassDoc
+
+class SphinxDocString(NumpyDocString):
+ # string conversion routines
+ def _str_header(self, name, symbol='`'):
+ return ['.. rubric:: ' + name, '']
+
+ def _str_field_list(self, name):
+ return [':' + name + ':']
+
+ def _str_indent(self, doc, indent=4):
+ out = []
+ for line in doc:
+ out += [' '*indent + line]
+ return out
+
+ def _str_signature(self):
+ return ['']
+ if self['Signature']:
+ return ['``%s``' % self['Signature']] + ['']
+ else:
+ return ['']
+
+ def _str_summary(self):
+ return self['Summary'] + ['']
+
+ def _str_extended_summary(self):
+ return self['Extended Summary'] + ['']
+
+ def _str_param_list(self, name):
+ out = []
+ if self[name]:
+ out += self._str_field_list(name)
+ out += ['']
+ for param,param_type,desc in self[name]:
+ out += self._str_indent(['**%s** : %s' % (param.strip(),
+ param_type)])
+ out += ['']
+ out += self._str_indent(desc,8)
+ out += ['']
+ return out
+
+ def _str_section(self, name):
+ out = []
+ if self[name]:
+ out += self._str_header(name)
+ out += ['']
+ content = textwrap.dedent("\n".join(self[name])).split("\n")
+ out += content
+ out += ['']
+ return out
+
+ def _str_see_also(self, func_role):
+ out = []
+ if self['See Also']:
+ see_also = super(SphinxDocString, self)._str_see_also(func_role)
+ out = ['.. seealso::', '']
+ out += self._str_indent(see_also[2:])
+ return out
+
+ def _str_warnings(self):
+ out = []
+ if self['Warnings']:
+ out = ['.. warning::', '']
+ out += self._str_indent(self['Warnings'])
+ return out
+
+ def _str_index(self):
+ idx = self['index']
+ out = []
+ if len(idx) == 0:
+ return out
+
+ out += ['.. index:: %s' % idx.get('default','')]
+ for section, references in idx.iteritems():
+ if section == 'default':
+ continue
+ elif section == 'refguide':
+ out += [' single: %s' % (', '.join(references))]
+ else:
+ out += [' %s: %s' % (section, ','.join(references))]
+ return out
+
+ def _str_references(self):
+ out = []
+ if self['References']:
+ out += self._str_header('References')
+ if isinstance(self['References'], str):
+ self['References'] = [self['References']]
+ out.extend(self['References'])
+ out += ['']
+ return out
+
+ def __str__(self, indent=0, func_role="obj"):
+ out = []
+ out += self._str_signature()
+ out += self._str_index() + ['']
+ out += self._str_summary()
+ out += self._str_extended_summary()
+ for param_list in ('Parameters', 'Attributes', 'Methods',
+ 'Returns','Raises'):
+ out += self._str_param_list(param_list)
+ out += self._str_warnings()
+ out += self._str_see_also(func_role)
+ out += self._str_section('Notes')
+ out += self._str_references()
+ out += self._str_section('Examples')
+ out = self._str_indent(out,indent)
+ return '\n'.join(out)
+
+class SphinxFunctionDoc(SphinxDocString, FunctionDoc):
+ pass
+
+class SphinxClassDoc(SphinxDocString, ClassDoc):
+ pass
+
+def get_doc_object(obj, what=None, doc=None):
+ if what is None:
+ if inspect.isclass(obj):
+ what = 'class'
+ elif inspect.ismodule(obj):
+ what = 'module'
+ elif callable(obj):
+ what = 'function'
+ else:
+ what = 'object'
+ if what == 'class':
+ return SphinxClassDoc(obj, '', func_doc=SphinxFunctionDoc, doc=doc)
+ elif what in ('function', 'method'):
+ return SphinxFunctionDoc(obj, '', doc=doc)
+ else:
+ if doc is None:
+ doc = pydoc.getdoc(obj)
+ return SphinxDocString(doc)
+
diff --git a/doc/_extensions/github.py b/doc/_extensions/github.py
new file mode 100644
index 0000000..a19f599
--- /dev/null
+++ b/doc/_extensions/github.py
@@ -0,0 +1,714 @@
+
+
+
+<!DOCTYPE html>
+<html>
+ <head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# githubog: http://ogp.me/ns/fb/githubog#">
+ <meta charset='utf-8'>
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <title>ipython/docs/sphinxext/github.py at master · ipython/ipython · GitHub</title>
+ <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub" />
+ <link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub" />
+ <link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-114.png" />
+ <link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114.png" />
+ <link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-144.png" />
+ <link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144.png" />
+ <link rel="logo" type="image/svg" href="http://github-media-downloads.s3.amazonaws.com/github-logo.svg" />
+ <link rel="xhr-socket" href="/_sockets" />
+
+
+ <meta name="msapplication-TileImage" content="/windows-tile.png" />
+ <meta name="msapplication-TileColor" content="#ffffff" />
+ <meta name="selected-link" value="repo_source" data-pjax-transient />
+ <meta content="collector.githubapp.com" name="octolytics-host" /><meta content="github" name="octolytics-app-id" />
+
+
+
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+
+ <meta content="authenticity_token" name="csrf-param" />
+<meta content="KxkHbu6uuzb5nYpICaiM/tuRIpoRIcTZQEgfU8NliZg=" name="csrf-token" />
+
+ <link href="https://a248.e.akamai.net/assets.github.com/assets/github-d63f89e307e2e357d3b7160b3cd4020463f9bbc1.css" media="all" rel="stylesheet" type="text/css" />
+ <link href="https://a248.e.akamai.net/assets.github.com/assets/github2-4a2696ec075bd8d27843df00793c7e9d6525dded.css" media="all" rel="stylesheet" type="text/css" />
+
+
+
+ <script src="https://a248.e.akamai.net/assets.github.com/assets/frameworks-92d138f450f2960501e28397a2f63b0f100590f0.js" type="text/javascript"></script>
+ <script src="https://a248.e.akamai.net/assets.github.com/assets/github-60bb3beedc339be272bd2acfce1cae3b79371737.js" type="text/javascript"></script>
+
+ <meta http-equiv="x-pjax-version" content="7159fafc2fc92e02281814323bde3687">
+
+ <link data-pjax-transient rel='permalink' href='/ipython/ipython/blob/831d4eb049fa74f8be85ea7495a9724cb754390f/docs/sphinxext/github.py'>
+ <meta property="og:title" content="ipython"/>
+ <meta property="og:type" content="githubog:gitrepository"/>
+ <meta property="og:url" content="https://github.com/ipython/ipython"/>
+ <meta property="og:image" content="https://secure.gravatar.com/avatar/f72497397dd9a0a79c654c8182460bb1?s=420&d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png"/>
+ <meta property="og:site_name" content="GitHub"/>
+ <meta property="og:description" content="ipython - Official repository for IPython itself. Other repos in the IPython organization contain things like the website, documentation builds, etc."/>
+ <meta property="twitter:card" content="summary"/>
+ <meta property="twitter:site" content="@GitHub">
+ <meta property="twitter:title" content="ipython/ipython"/>
+
+ <meta name="description" content="ipython - Official repository for IPython itself. Other repos in the IPython organization contain things like the website, documentation builds, etc." />
+
+
+ <meta content="230453" name="octolytics-dimension-user_id" /><meta content="658518" name="octolytics-dimension-repository_id" />
+ <link href="https://github.com/ipython/ipython/commits/master.atom" rel="alternate" title="Recent Commits to ipython:master" type="application/atom+xml" />
+
+ </head>
+
+
+ <body class="logged_out page-blob windows vis-public env-production ">
+ <div id="wrapper">
+
+
+
+
+
+
+
+
+ <div class="header header-logged-out">
+ <div class="container clearfix">
+
+ <a class="header-logo-wordmark" href="https://github.com/">Github</a>
+
+ <div class="header-actions">
+ <a class="button primary" href="https://github.com/signup">Sign up</a>
+ <a class="button" href="https://github.com/login?return_to=%2Fipython%2Fipython%2Fblob%2Fmaster%2Fdocs%2Fsphinxext%2Fgithub.py">Sign in</a>
+ </div>
+
+ <div class="command-bar js-command-bar in-repository">
+
+
+ <ul class="top-nav">
+ <li class="explore"><a href="https://github.com/explore">Explore</a></li>
+ <li class="features"><a href="https://github.com/features">Features</a></li>
+ <li class="blog"><a href="https://github.com/blog">Blog</a></li>
+ </ul>
+ <form accept-charset="UTF-8" action="/search" class="command-bar-form" id="top_search_form" method="get">
+ <a href="/search/advanced" class="advanced-search-icon tooltipped downwards command-bar-search" id="advanced_search" title="Advanced search"><span class="octicon octicon-gear "></span></a>
+
+ <input type="text" data-hotkey="/ s" name="q" id="js-command-bar-field" placeholder="Search or type a command" tabindex="1" autocapitalize="off"
+
+ data-repo="ipython/ipython"
+ data-branch="master"
+ data-sha="26bb5f152ca86cb76eb66799c54e217638cd4b6d"
+ >
+
+ <input type="hidden" name="nwo" value="ipython/ipython" />
+
+ <div class="select-menu js-menu-container js-select-menu search-context-select-menu">
+ <span class="minibutton select-menu-button js-menu-target">
+ <span class="js-select-button">This repository</span>
+ </span>
+
+ <div class="select-menu-modal-holder js-menu-content js-navigation-container">
+ <div class="select-menu-modal">
+
+ <div class="select-menu-item js-navigation-item selected">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <input type="radio" class="js-search-this-repository" name="search_target" value="repository" checked="checked" />
+ <div class="select-menu-item-text js-select-button-text">This repository</div>
+ </div> <!-- /.select-menu-item -->
+
+ <div class="select-menu-item js-navigation-item">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <input type="radio" name="search_target" value="global" />
+ <div class="select-menu-item-text js-select-button-text">All repositories</div>
+ </div> <!-- /.select-menu-item -->
+
+ </div>
+ </div>
+ </div>
+
+ <span class="octicon help tooltipped downwards" title="Show command bar help">
+ <span class="octicon octicon-question"></span>
+ </span>
+
+
+ <input type="hidden" name="ref" value="cmdform">
+
+ <div class="divider-vertical"></div>
+
+</form>
+ </div>
+
+ </div>
+</div>
+
+
+
+
+
+
+
+ <div class="site hfeed" itemscope itemtype="http://schema.org/WebPage">
+ <div class="hentry">
+
+ <div class="pagehead repohead instapaper_ignore readability-menu ">
+ <div class="container">
+ <div class="title-actions-bar">
+
+
+<ul class="pagehead-actions">
+
+
+
+ <li>
+ <a href="/login?return_to=%2Fipython%2Fipython"
+ class="minibutton js-toggler-target star-button entice tooltipped upwards"
+ title="You must be signed in to use this feature" rel="nofollow">
+ <span class="octicon octicon-star"></span>Star
+ </a>
+ <a class="social-count js-social-count" href="/ipython/ipython/stargazers">
+ 1,895
+ </a>
+ </li>
+ <li>
+ <a href="/login?return_to=%2Fipython%2Fipython"
+ class="minibutton js-toggler-target fork-button entice tooltipped upwards"
+ title="You must be signed in to fork a repository" rel="nofollow">
+ <span class="octicon octicon-git-branch"></span>Fork
+ </a>
+ <a href="/ipython/ipython/network" class="social-count">
+ 586
+ </a>
+ </li>
+</ul>
+
+ <h1 itemscope itemtype="http://data-vocabulary.org/Breadcrumb" class="entry-title public">
+ <span class="repo-label"><span>public</span></span>
+ <span class="mega-octicon octicon-repo"></span>
+ <span class="author vcard">
+ <a href="/ipython" class="url fn" itemprop="url" rel="author">
+ <span itemprop="title">ipython</span>
+ </a></span> /
+ <strong><a href="/ipython/ipython" class="js-current-repository">ipython</a></strong>
+ </h1>
+ </div>
+
+
+ <ul class="tabs">
+ <li class="pulse-nav"><a href="/ipython/ipython/pulse" class="js-selected-navigation-item " data-selected-links="pulse /ipython/ipython/pulse" rel="nofollow"><span class="octicon octicon-pulse"></span></a></li>
+ <li><a href="/ipython/ipython" class="js-selected-navigation-item selected" data-selected-links="repo_source repo_downloads repo_commits repo_tags repo_branches /ipython/ipython">Code</a></li>
+ <li><a href="/ipython/ipython/network" class="js-selected-navigation-item " data-selected-links="repo_network /ipython/ipython/network">Network</a></li>
+ <li><a href="/ipython/ipython/pulls" class="js-selected-navigation-item " data-selected-links="repo_pulls /ipython/ipython/pulls">Pull Requests <span class='counter'>15</span></a></li>
+
+ <li><a href="/ipython/ipython/issues" class="js-selected-navigation-item " data-selected-links="repo_issues /ipython/ipython/issues">Issues <span class='counter'>567</span></a></li>
+
+ <li><a href="/ipython/ipython/wiki" class="js-selected-navigation-item " data-selected-links="repo_wiki /ipython/ipython/wiki">Wiki</a></li>
+
+
+ <li><a href="/ipython/ipython/graphs" class="js-selected-navigation-item " data-selected-links="repo_graphs repo_contributors /ipython/ipython/graphs">Graphs</a></li>
+
+
+ </ul>
+
+<div class="tabnav">
+
+ <span class="tabnav-right">
+ <ul class="tabnav-tabs">
+ <li><a href="/ipython/ipython/tags" class="js-selected-navigation-item tabnav-tab" data-selected-links="repo_tags /ipython/ipython/tags">Tags <span class="counter ">13</span></a></li>
+ </ul>
+ </span>
+
+ <div class="tabnav-widget scope">
+
+
+ <div class="select-menu js-menu-container js-select-menu js-branch-menu">
+ <a class="minibutton select-menu-button js-menu-target" data-hotkey="w" data-ref="master">
+ <span class="octicon octicon-branch"></span>
+ <i>branch:</i>
+ <span class="js-select-button">master</span>
+ </a>
+
+ <div class="select-menu-modal-holder js-menu-content js-navigation-container">
+
+ <div class="select-menu-modal">
+ <div class="select-menu-header">
+ <span class="select-menu-title">Switch branches/tags</span>
+ <span class="octicon octicon-remove-close js-menu-close"></span>
+ </div> <!-- /.select-menu-header -->
+
+ <div class="select-menu-filters">
+ <div class="select-menu-text-filter">
+ <input type="text" id="commitish-filter-field" class="js-filterable-field js-navigation-enable" placeholder="Filter branches/tags">
+ </div>
+ <div class="select-menu-tabs">
+ <ul>
+ <li class="select-menu-tab">
+ <a href="#" data-tab-filter="branches" class="js-select-menu-tab">Branches</a>
+ </li>
+ <li class="select-menu-tab">
+ <a href="#" data-tab-filter="tags" class="js-select-menu-tab">Tags</a>
+ </li>
+ </ul>
+ </div><!-- /.select-menu-tabs -->
+ </div><!-- /.select-menu-filters -->
+
+ <div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket css-truncate" data-tab-filter="branches">
+
+ <div data-filterable-for="commitish-filter-field" data-filterable-type="substring">
+
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/ipython/ipython/blob/0.10.2/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="0.10.2" rel="nofollow" title="0.10.2">0.10.2</a>
+ </div> <!-- /.select-menu-item -->
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/ipython/ipython/blob/0.12.1/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="0.12.1" rel="nofollow" title="0.12.1">0.12.1</a>
+ </div> <!-- /.select-menu-item -->
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/ipython/ipython/blob/0.13.x/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="0.13.x" rel="nofollow" title="0.13.x">0.13.x</a>
+ </div> <!-- /.select-menu-item -->
+ <div class="select-menu-item js-navigation-item selected">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/ipython/ipython/blob/master/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="master" rel="nofollow" title="master">master</a>
+ </div> <!-- /.select-menu-item -->
+ </div>
+
+ <div class="select-menu-no-results">Nothing to show</div>
+ </div> <!-- /.select-menu-list -->
+
+
+ <div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket css-truncate" data-tab-filter="tags">
+ <div data-filterable-for="commitish-filter-field" data-filterable-type="substring">
+
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/ipython/ipython/blob/rel-0.13.2/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.13.2" rel="nofollow" title="rel-0.13.2">rel-0.13.2</a>
+ </div> <!-- /.select-menu-item -->
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/ipython/ipython/blob/rel-0.13.1/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.13.1" rel="nofollow" title="rel-0.13.1">rel-0.13.1</a>
+ </div> <!-- /.select-menu-item -->
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/ipython/ipython/blob/rel-0.13/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.13" rel="nofollow" title="rel-0.13">rel-0.13</a>
+ </div> <!-- /.select-menu-item -->
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/ipython/ipython/blob/rel-0.12.1/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.12.1" rel="nofollow" title="rel-0.12.1">rel-0.12.1</a>
+ </div> <!-- /.select-menu-item -->
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/ipython/ipython/blob/rel-0.12/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.12" rel="nofollow" title="rel-0.12">rel-0.12</a>
+ </div> <!-- /.select-menu-item -->
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/ipython/ipython/blob/rel-0.11/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.11" rel="nofollow" title="rel-0.11">rel-0.11</a>
+ </div> <!-- /.select-menu-item -->
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/ipython/ipython/blob/rel-0.10.2/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.10.2" rel="nofollow" title="rel-0.10.2">rel-0.10.2</a>
+ </div> <!-- /.select-menu-item -->
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/ipython/ipython/blob/rel-0.10.1/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.10.1" rel="nofollow" title="rel-0.10.1">rel-0.10.1</a>
+ </div> <!-- /.select-menu-item -->
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/ipython/ipython/blob/rel-0.10/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.10" rel="nofollow" title="rel-0.10">rel-0.10</a>
+ </div> <!-- /.select-menu-item -->
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/ipython/ipython/blob/rel-0.9.1/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.9.1" rel="nofollow" title="rel-0.9.1">rel-0.9.1</a>
+ </div> <!-- /.select-menu-item -->
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/ipython/ipython/blob/rel-0.9/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.9" rel="nofollow" title="rel-0.9">rel-0.9</a>
+ </div> <!-- /.select-menu-item -->
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/ipython/ipython/blob/rel-0.8.4/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.8.4" rel="nofollow" title="rel-0.8.4">rel-0.8.4</a>
+ </div> <!-- /.select-menu-item -->
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/ipython/ipython/blob/dev-0.11/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="dev-0.11" rel="nofollow" title="dev-0.11">dev-0.11</a>
+ </div> <!-- /.select-menu-item -->
+ </div>
+
+ <div class="select-menu-no-results">Nothing to show</div>
+
+ </div> <!-- /.select-menu-list -->
+
+ </div> <!-- /.select-menu-modal -->
+ </div> <!-- /.select-menu-modal-holder -->
+ </div> <!-- /.select-menu -->
+
+ </div> <!-- /.scope -->
+
+ <ul class="tabnav-tabs">
+ <li><a href="/ipython/ipython" class="selected js-selected-navigation-item tabnav-tab" data-selected-links="repo_source /ipython/ipython">Files</a></li>
+ <li><a href="/ipython/ipython/commits/master" class="js-selected-navigation-item tabnav-tab" data-selected-links="repo_commits /ipython/ipython/commits/master">Commits</a></li>
+ <li><a href="/ipython/ipython/branches" class="js-selected-navigation-item tabnav-tab" data-selected-links="repo_branches /ipython/ipython/branches" rel="nofollow">Branches <span class="counter ">4</span></a></li>
+ </ul>
+
+</div>
+
+
+
+
+
+
+
+ </div>
+ </div><!-- /.repohead -->
+
+ <div id="js-repo-pjax-container" class="container context-loader-container" data-pjax-container>
+
+
+
+<!-- blob contrib key: blob_contributors:v21:5a715cc18d66347c4a5af2652afee082 -->
+<!-- blob contrib frag key: views10/v8/blob_contributors:v21:5a715cc18d66347c4a5af2652afee082 -->
+
+
+<div id="slider">
+ <div class="frame-meta">
+
+ <p title="This is a placeholder element" class="js-history-link-replace hidden"></p>
+
+ <div class="breadcrumb">
+ <span class='bold'><span itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"><a href="/ipython/ipython" class="js-slide-to" data-branch="master" data-direction="back" itemscope="url"><span itemprop="title">ipython</span></a></span></span><span class="separator"> / </span><span itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"><a href="/ipython/ipython/tree/master/docs" class="js-slide-to" data-branch="master" data-direction="back" itemscope="url"><span ite [...]
+ </div>
+
+ <a href="/ipython/ipython/find/master" class="js-slide-to" data-hotkey="t" style="display:none">Show File Finder</a>
+
+
+ <div class="commit commit-loader file-history-tease js-deferred-content" data-url="/ipython/ipython/contributors/master/docs/sphinxext/github.py">
+ Fetching contributors…
+
+ <div class="participation">
+ <p class="loader-loading"><img alt="Octocat-spinner-32-eaf2f5" height="16" src="https://a248.e.akamai.net/assets.github.com/images/spinners/octocat-spinner-32-EAF2F5.gif?1340659530" width="16" /></p>
+ <p class="loader-error">Cannot retrieve contributors at this time</p>
+ </div>
+ </div>
+
+ </div><!-- ./.frame-meta -->
+
+ <div class="frames">
+ <div class="frame" data-permalink-url="/ipython/ipython/blob/831d4eb049fa74f8be85ea7495a9724cb754390f/docs/sphinxext/github.py" data-title="ipython/docs/sphinxext/github.py at master · ipython/ipython · GitHub" data-type="blob">
+
+ <div id="files" class="bubble">
+ <div class="file">
+ <div class="meta">
+ <div class="info">
+ <span class="icon"><b class="octicon octicon-file-text"></b></span>
+ <span class="mode" title="File Mode">file</span>
+ <span>156 lines (132 sloc)</span>
+ <span>5.379 kb</span>
+ </div>
+ <div class="actions">
+ <div class="button-group">
+ <a class="minibutton js-entice" href=""
+ data-entice="You must be signed in and on a branch to make or propose changes">Edit</a>
+ <a href="/ipython/ipython/raw/master/docs/sphinxext/github.py" class="button minibutton " id="raw-url">Raw</a>
+ <a href="/ipython/ipython/blame/master/docs/sphinxext/github.py" class="button minibutton ">Blame</a>
+ <a href="/ipython/ipython/commits/master/docs/sphinxext/github.py" class="button minibutton " rel="nofollow">History</a>
+ </div><!-- /.button-group -->
+ </div><!-- /.actions -->
+
+ </div>
+ <div class="blob-wrapper data type-python js-blob-data">
+ <table class="file-code file-diff">
+ <tr class="file-code-line">
+ <td class="blob-line-nums">
+ <span id="L1" rel="#L1">1</span>
+<span id="L2" rel="#L2">2</span>
+<span id="L3" rel="#L3">3</span>
+<span id="L4" rel="#L4">4</span>
+<span id="L5" rel="#L5">5</span>
+<span id="L6" rel="#L6">6</span>
+<span id="L7" rel="#L7">7</span>
+<span id="L8" rel="#L8">8</span>
+<span id="L9" rel="#L9">9</span>
+<span id="L10" rel="#L10">10</span>
+<span id="L11" rel="#L11">11</span>
+<span id="L12" rel="#L12">12</span>
+<span id="L13" rel="#L13">13</span>
+<span id="L14" rel="#L14">14</span>
+<span id="L15" rel="#L15">15</span>
+<span id="L16" rel="#L16">16</span>
+<span id="L17" rel="#L17">17</span>
+<span id="L18" rel="#L18">18</span>
+<span id="L19" rel="#L19">19</span>
+<span id="L20" rel="#L20">20</span>
+<span id="L21" rel="#L21">21</span>
+<span id="L22" rel="#L22">22</span>
+<span id="L23" rel="#L23">23</span>
+<span id="L24" rel="#L24">24</span>
+<span id="L25" rel="#L25">25</span>
+<span id="L26" rel="#L26">26</span>
+<span id="L27" rel="#L27">27</span>
+<span id="L28" rel="#L28">28</span>
+<span id="L29" rel="#L29">29</span>
+<span id="L30" rel="#L30">30</span>
+<span id="L31" rel="#L31">31</span>
+<span id="L32" rel="#L32">32</span>
+<span id="L33" rel="#L33">33</span>
+<span id="L34" rel="#L34">34</span>
+<span id="L35" rel="#L35">35</span>
+<span id="L36" rel="#L36">36</span>
+<span id="L37" rel="#L37">37</span>
+<span id="L38" rel="#L38">38</span>
+<span id="L39" rel="#L39">39</span>
+<span id="L40" rel="#L40">40</span>
+<span id="L41" rel="#L41">41</span>
+<span id="L42" rel="#L42">42</span>
+<span id="L43" rel="#L43">43</span>
+<span id="L44" rel="#L44">44</span>
+<span id="L45" rel="#L45">45</span>
+<span id="L46" rel="#L46">46</span>
+<span id="L47" rel="#L47">47</span>
+<span id="L48" rel="#L48">48</span>
+<span id="L49" rel="#L49">49</span>
+<span id="L50" rel="#L50">50</span>
+<span id="L51" rel="#L51">51</span>
+<span id="L52" rel="#L52">52</span>
+<span id="L53" rel="#L53">53</span>
+<span id="L54" rel="#L54">54</span>
+<span id="L55" rel="#L55">55</span>
+<span id="L56" rel="#L56">56</span>
+<span id="L57" rel="#L57">57</span>
+<span id="L58" rel="#L58">58</span>
+<span id="L59" rel="#L59">59</span>
+<span id="L60" rel="#L60">60</span>
+<span id="L61" rel="#L61">61</span>
+<span id="L62" rel="#L62">62</span>
+<span id="L63" rel="#L63">63</span>
+<span id="L64" rel="#L64">64</span>
+<span id="L65" rel="#L65">65</span>
+<span id="L66" rel="#L66">66</span>
+<span id="L67" rel="#L67">67</span>
+<span id="L68" rel="#L68">68</span>
+<span id="L69" rel="#L69">69</span>
+<span id="L70" rel="#L70">70</span>
+<span id="L71" rel="#L71">71</span>
+<span id="L72" rel="#L72">72</span>
+<span id="L73" rel="#L73">73</span>
+<span id="L74" rel="#L74">74</span>
+<span id="L75" rel="#L75">75</span>
+<span id="L76" rel="#L76">76</span>
+<span id="L77" rel="#L77">77</span>
+<span id="L78" rel="#L78">78</span>
+<span id="L79" rel="#L79">79</span>
+<span id="L80" rel="#L80">80</span>
+<span id="L81" rel="#L81">81</span>
+<span id="L82" rel="#L82">82</span>
+<span id="L83" rel="#L83">83</span>
+<span id="L84" rel="#L84">84</span>
+<span id="L85" rel="#L85">85</span>
+<span id="L86" rel="#L86">86</span>
+<span id="L87" rel="#L87">87</span>
+<span id="L88" rel="#L88">88</span>
+<span id="L89" rel="#L89">89</span>
+<span id="L90" rel="#L90">90</span>
+<span id="L91" rel="#L91">91</span>
+<span id="L92" rel="#L92">92</span>
+<span id="L93" rel="#L93">93</span>
+<span id="L94" rel="#L94">94</span>
+<span id="L95" rel="#L95">95</span>
+<span id="L96" rel="#L96">96</span>
+<span id="L97" rel="#L97">97</span>
+<span id="L98" rel="#L98">98</span>
+<span id="L99" rel="#L99">99</span>
+<span id="L100" rel="#L100">100</span>
+<span id="L101" rel="#L101">101</span>
+<span id="L102" rel="#L102">102</span>
+<span id="L103" rel="#L103">103</span>
+<span id="L104" rel="#L104">104</span>
+<span id="L105" rel="#L105">105</span>
+<span id="L106" rel="#L106">106</span>
+<span id="L107" rel="#L107">107</span>
+<span id="L108" rel="#L108">108</span>
+<span id="L109" rel="#L109">109</span>
+<span id="L110" rel="#L110">110</span>
+<span id="L111" rel="#L111">111</span>
+<span id="L112" rel="#L112">112</span>
+<span id="L113" rel="#L113">113</span>
+<span id="L114" rel="#L114">114</span>
+<span id="L115" rel="#L115">115</span>
+<span id="L116" rel="#L116">116</span>
+<span id="L117" rel="#L117">117</span>
+<span id="L118" rel="#L118">118</span>
+<span id="L119" rel="#L119">119</span>
+<span id="L120" rel="#L120">120</span>
+<span id="L121" rel="#L121">121</span>
+<span id="L122" rel="#L122">122</span>
+<span id="L123" rel="#L123">123</span>
+<span id="L124" rel="#L124">124</span>
+<span id="L125" rel="#L125">125</span>
+<span id="L126" rel="#L126">126</span>
+<span id="L127" rel="#L127">127</span>
+<span id="L128" rel="#L128">128</span>
+<span id="L129" rel="#L129">129</span>
+<span id="L130" rel="#L130">130</span>
+<span id="L131" rel="#L131">131</span>
+<span id="L132" rel="#L132">132</span>
+<span id="L133" rel="#L133">133</span>
+<span id="L134" rel="#L134">134</span>
+<span id="L135" rel="#L135">135</span>
+<span id="L136" rel="#L136">136</span>
+<span id="L137" rel="#L137">137</span>
+<span id="L138" rel="#L138">138</span>
+<span id="L139" rel="#L139">139</span>
+<span id="L140" rel="#L140">140</span>
+<span id="L141" rel="#L141">141</span>
+<span id="L142" rel="#L142">142</span>
+<span id="L143" rel="#L143">143</span>
+<span id="L144" rel="#L144">144</span>
+<span id="L145" rel="#L145">145</span>
+<span id="L146" rel="#L146">146</span>
+<span id="L147" rel="#L147">147</span>
+<span id="L148" rel="#L148">148</span>
+<span id="L149" rel="#L149">149</span>
+<span id="L150" rel="#L150">150</span>
+<span id="L151" rel="#L151">151</span>
+<span id="L152" rel="#L152">152</span>
+<span id="L153" rel="#L153">153</span>
+<span id="L154" rel="#L154">154</span>
+<span id="L155" rel="#L155">155</span>
+
+ </td>
+ <td class="blob-line-code">
+ <div class="highlight"><pre><div class='line' id='LC1'><span class="sd">"""Define text roles for GitHub</span></div><div class='line' id='LC2'><br/></div><div class='line' id='LC3'><span class="sd">* ghissue - Issue</span></div><div class='line' id='LC4'><span class="sd">* ghpull - Pull Request</span></div><div class='line' id='LC5'><span class="sd">* ghuser - User</span></div><div class='line' id='LC6'><br/></div><div class='line' id='LC7'><span class="s [...]
+ </td>
+ </tr>
+ </table>
+ </div>
+
+ </div>
+ </div>
+
+ <a href="#jump-to-line" rel="facebox" data-hotkey="l" class="js-jump-to-line" style="display:none">Jump to Line</a>
+ <div id="jump-to-line" style="display:none">
+ <h2>Jump to Line</h2>
+ <form accept-charset="UTF-8" class="js-jump-to-line-form">
+ <input class="textfield js-jump-to-line-field" type="text">
+ <div class="full-button">
+ <button type="submit" class="button">Go</button>
+ </div>
+ </form>
+ </div>
+
+ </div>
+ </div>
+</div>
+
+<div id="js-frame-loading-template" class="frame frame-loading large-loading-area" style="display:none;">
+ <img class="js-frame-loading-spinner" src="https://a248.e.akamai.net/assets.github.com/images/spinners/octocat-spinner-128.gif?1347543525" height="64" width="64">
+</div>
+
+
+ </div>
+ </div>
+ <div class="modal-backdrop"></div>
+ </div>
+
+ <div id="footer-push"></div><!-- hack for sticky footer -->
+ </div><!-- end of wrapper - hack for sticky footer -->
+
+ <!-- footer -->
+ <div id="footer">
+ <div class="container clearfix">
+
+ <dl class="footer_nav">
+ <dt>GitHub</dt>
+ <dd><a href="https://github.com/about">About us</a></dd>
+ <dd><a href="https://github.com/blog">Blog</a></dd>
+ <dd><a href="https://github.com/contact">Contact & support</a></dd>
+ <dd><a href="http://enterprise.github.com/">GitHub Enterprise</a></dd>
+ <dd><a href="http://status.github.com/">Site status</a></dd>
+ </dl>
+
+ <dl class="footer_nav">
+ <dt>Applications</dt>
+ <dd><a href="http://mac.github.com/">GitHub for Mac</a></dd>
+ <dd><a href="http://windows.github.com/">GitHub for Windows</a></dd>
+ <dd><a href="http://eclipse.github.com/">GitHub for Eclipse</a></dd>
+ <dd><a href="http://mobile.github.com/">GitHub mobile apps</a></dd>
+ </dl>
+
+ <dl class="footer_nav">
+ <dt>Services</dt>
+ <dd><a href="http://get.gaug.es/">Gauges: Web analytics</a></dd>
+ <dd><a href="http://speakerdeck.com">Speaker Deck: Presentations</a></dd>
+ <dd><a href="https://gist.github.com">Gist: Code snippets</a></dd>
+ <dd><a href="http://jobs.github.com/">Job board</a></dd>
+ </dl>
+
+ <dl class="footer_nav">
+ <dt>Documentation</dt>
+ <dd><a href="http://help.github.com/">GitHub Help</a></dd>
+ <dd><a href="http://developer.github.com/">Developer API</a></dd>
+ <dd><a href="http://github.github.com/github-flavored-markdown/">GitHub Flavored Markdown</a></dd>
+ <dd><a href="http://pages.github.com/">GitHub Pages</a></dd>
+ </dl>
+
+ <dl class="footer_nav">
+ <dt>More</dt>
+ <dd><a href="http://training.github.com/">Training</a></dd>
+ <dd><a href="https://github.com/edu">Students & teachers</a></dd>
+ <dd><a href="http://shop.github.com">The Shop</a></dd>
+ <dd><a href="/plans">Plans & pricing</a></dd>
+ <dd><a href="http://octodex.github.com/">The Octodex</a></dd>
+ </dl>
+
+ <hr class="footer-divider">
+
+
+ <p class="right">© 2013 <span title="0.12974s from fe17.rs.github.com">GitHub</span>, Inc. All rights reserved.</p>
+ <a class="left" href="https://github.com/">
+ <span class="mega-octicon octicon-mark-github"></span>
+ </a>
+ <ul id="legal">
+ <li><a href="https://github.com/site/terms">Terms of Service</a></li>
+ <li><a href="https://github.com/site/privacy">Privacy</a></li>
+ <li><a href="https://github.com/security">Security</a></li>
+ </ul>
+
+ </div><!-- /.container -->
+
+</div><!-- /.#footer -->
+
+
+ <div class="fullscreen-overlay js-fullscreen-overlay" id="fullscreen_overlay">
+ <div class="fullscreen-container js-fullscreen-container">
+ <div class="textarea-wrap">
+ <textarea name="fullscreen-contents" id="fullscreen-contents" class="js-fullscreen-contents" placeholder="" data-suggester="fullscreen_suggester"></textarea>
+ <div class="suggester-container">
+ <div class="suggester fullscreen-suggester js-navigation-container" id="fullscreen_suggester"
+ data-url="/ipython/ipython/suggestions/commit">
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="fullscreen-sidebar">
+ <a href="#" class="exit-fullscreen js-exit-fullscreen tooltipped leftwards" title="Exit Zen Mode">
+ <span class="mega-octicon octicon-screen-normal"></span>
+ </a>
+ <a href="#" class="theme-switcher js-theme-switcher tooltipped leftwards"
+ title="Switch themes">
+ <span class="octicon octicon-color-mode"></span>
+ </a>
+ </div>
+</div>
+
+
+
+ <div id="ajax-error-message" class="flash flash-error">
+ <span class="octicon octicon-alert"></span>
+ Something went wrong with that request. Please try again.
+ <a href="#" class="octicon octicon-remove-close ajax-error-dismiss"></a>
+ </div>
+
+
+
+ <span id='server_response_time' data-time='0.13024' data-host='fe17'></span>
+
+ </body>
+</html>
+
diff --git a/doc/_extensions/hidden_code_block.py b/doc/_extensions/hidden_code_block.py
new file mode 100644
index 0000000..e9ee9fe
--- /dev/null
+++ b/doc/_extensions/hidden_code_block.py
@@ -0,0 +1,128 @@
+"""Simple, inelegant Sphinx extension which adds a directive for a
+highlighted code-block that may be toggled hidden and shown in HTML.
+This is possibly useful for teaching courses.
+
+The directive, like the standard code-block directive, takes
+a language argument and an optional linenos parameter. The
+hidden-code-block adds starthidden and label as optional
+parameters.
+
+Examples:
+
+.. hidden-code-block:: python
+ :starthidden: False
+
+ a = 10
+ b = a + 5
+
+.. hidden-code-block:: python
+ :label: --- SHOW/HIDE ---
+
+ x = 10
+ y = x + 5
+
+Thanks to http://www.javascriptkit.com/javatutors/dom3.shtml for
+inspiration on the javascript.
+
+Thanks to Milad 'animal' Fatenejad for suggesting this extension
+in the first place.
+
+Written by Anthony 'el Scopz' Scopatz, January 2012.
+
+Released under the WTFPL (http://sam.zoy.org/wtfpl/).
+"""
+
+from docutils import nodes
+from docutils.parsers.rst import directives
+from sphinx.directives.code import CodeBlock
+from sphinx.util.compat import make_admonition
+
+HCB_COUNTER = 0
+
+js_showhide = """\
+<script type="text/javascript">
+ function showhide(element){
+ if (!document.getElementById)
+ return
+
+ if (element.style.display == "block")
+ element.style.display = "none"
+ else
+ element.style.display = "block"
+ };
+</script>
+"""
+
+def nice_bool(arg):
+ tvalues = ('true', 't', 'yes', 'y')
+ fvalues = ('false', 'f', 'no', 'n')
+ arg = directives.choice(arg, tvalues + fvalues)
+ return arg in tvalues
+
+
+class hidden_code_block(nodes.General, nodes.FixedTextElement):
+ pass
+
+
+class HiddenCodeBlock(CodeBlock):
+ """Hidden code block is Hidden"""
+
+ option_spec = dict(starthidden=nice_bool,
+ label=str,
+ **CodeBlock.option_spec)
+
+ def run(self):
+ # Body of the method is more or less copied from CodeBlock
+ code = u'\n'.join(self.content)
+ hcb = hidden_code_block(code, code)
+ hcb['language'] = self.arguments[0]
+ hcb['linenos'] = 'linenos' in self.options
+ hcb['starthidden'] = self.options.get('starthidden', True)
+ hcb['label'] = self.options.get('label', '+ show/hide code')
+ hcb.line = self.lineno
+ return [hcb]
+
+
+def visit_hcb_html(self, node):
+ """Visit hidden code block"""
+ global HCB_COUNTER
+ HCB_COUNTER += 1
+
+ # We want to use the original highlighter so that we don't
+ # have to reimplement it. However it raises a SkipNode
+ # error at the end of the function call. Thus we intercept
+ # it and raise it again later.
+ try:
+ self.visit_literal_block(node)
+ except nodes.SkipNode:
+ pass
+
+ # The last element of the body should be the literal code
+ # block that was just made.
+ code_block = self.body[-1]
+
+ fill_header = {'divname': 'hiddencodeblock{}'.format(HCB_COUNTER),
+ 'startdisplay': 'none' if node['starthidden'] else 'block',
+ 'label': node.get('label'),
+ }
+
+ divheader = ("""<a href="javascript:showhide(document.getElementById('{divname}'))">"""
+ """{label}</a><br />"""
+ '''<div id="{divname}" style="display: {startdisplay}">'''
+ ).format(**fill_header)
+
+ code_block = js_showhide + divheader + code_block + "</div>"
+
+ # reassign and exit
+ self.body[-1] = code_block
+ raise nodes.SkipNode
+
+
+def depart_hcb_html(self, node):
+ """Depart hidden code block"""
+ # Stub because of SkipNode in visit
+
+
+def setup(app):
+ app.add_directive('hidden-code-block', HiddenCodeBlock)
+ app.add_node(hidden_code_block, html=(visit_hcb_html, depart_hcb_html))
diff --git a/doc/_extensions/inheritance_diagram.py b/doc/_extensions/inheritance_diagram.py
new file mode 100644
index 0000000..407fc13
--- /dev/null
+++ b/doc/_extensions/inheritance_diagram.py
@@ -0,0 +1,407 @@
+"""
+Defines a docutils directive for inserting inheritance diagrams.
+
+Provide the directive with one or more classes or modules (separated
+by whitespace). For modules, all of the classes in that module will
+be used.
+
+Example::
+
+ Given the following classes:
+
+ class A: pass
+ class B(A): pass
+ class C(A): pass
+ class D(B, C): pass
+ class E(B): pass
+
+ .. inheritance-diagram: D E
+
+ Produces a graph like the following:
+
+ A
+ / \
+ B C
+ / \ /
+ E D
+
+The graph is inserted as a PNG+image map into HTML and a PDF in
+LaTeX.
+"""
+
+import inspect
+import os
+import re
+import subprocess
+try:
+ from hashlib import md5
+except ImportError:
+ from md5 import md5
+
+from docutils.nodes import Body, Element
+from docutils.parsers.rst import directives
+from sphinx.roles import xfileref_role
+
+def my_import(name):
+ """Module importer - taken from the python documentation.
+
+ This function allows importing names with dots in them."""
+
+ mod = __import__(name)
+ components = name.split('.')
+ for comp in components[1:]:
+ mod = getattr(mod, comp)
+ return mod
+
+class DotException(Exception):
+ pass
+
+class InheritanceGraph(object):
+ """
+ Given a list of classes, determines the set of classes that
+ they inherit from all the way to the root "object", and then
+ is able to generate a graphviz dot graph from them.
+ """
+ def __init__(self, class_names, show_builtins=False):
+ """
+ *class_names* is a list of child classes to show bases from.
+
+ If *show_builtins* is True, then Python builtins will be shown
+ in the graph.
+ """
+ self.class_names = class_names
+ self.classes = self._import_classes(class_names)
+ self.all_classes = self._all_classes(self.classes)
+ if len(self.all_classes) == 0:
+ raise ValueError("No classes found for inheritance diagram")
+ self.show_builtins = show_builtins
+
+ py_sig_re = re.compile(r'''^([\w.]*\.)? # class names
+ (\w+) \s* $ # optionally arguments
+ ''', re.VERBOSE)
+
+ def _import_class_or_module(self, name):
+ """
+ Import a class using its fully-qualified *name*.
+ """
+ try:
+ path, base = self.py_sig_re.match(name).groups()
+ except:
+ raise ValueError(
+ "Invalid class or module '%s' specified for inheritance diagram" % name)
+ fullname = (path or '') + base
+ path = (path and path.rstrip('.'))
+ if not path:
+ path = base
+ try:
+ module = __import__(path, None, None, [])
+ # We must do an import of the fully qualified name. Otherwise if a
+ # subpackage 'a.b' is requested where 'import a' does NOT provide
+ # 'a.b' automatically, then 'a.b' will not be found below. This
+ # second call will force the equivalent of 'import a.b' to happen
+ # after the top-level import above.
+ my_import(fullname)
+
+ except ImportError:
+ raise ValueError(
+ "Could not import class or module '%s' specified for inheritance diagram" % name)
+
+ try:
+ todoc = module
+ for comp in fullname.split('.')[1:]:
+ todoc = getattr(todoc, comp)
+ except AttributeError:
+ raise ValueError(
+ "Could not find class or module '%s' specified for inheritance diagram" % name)
+
+ # If a class, just return it
+ if inspect.isclass(todoc):
+ return [todoc]
+ elif inspect.ismodule(todoc):
+ classes = []
+ for cls in todoc.__dict__.values():
+ if inspect.isclass(cls) and cls.__module__ == todoc.__name__:
+ classes.append(cls)
+ return classes
+ raise ValueError(
+ "'%s' does not resolve to a class or module" % name)
+
+ def _import_classes(self, class_names):
+ """
+ Import a list of classes.
+ """
+ classes = []
+ for name in class_names:
+ classes.extend(self._import_class_or_module(name))
+ return classes
+
+ def _all_classes(self, classes):
+ """
+ Return a list of all classes that are ancestors of *classes*.
+ """
+ all_classes = {}
+
+ def recurse(cls):
+ all_classes[cls] = None
+ for c in cls.__bases__:
+ if c not in all_classes:
+ recurse(c)
+
+ for cls in classes:
+ recurse(cls)
+
+ return all_classes.keys()
+
+ def class_name(self, cls, parts=0):
+ """
+ Given a class object, return a fully-qualified name. This
+ works for things I've tested in matplotlib so far, but may not
+ be completely general.
+ """
+ module = cls.__module__
+ if module == '__builtin__':
+ fullname = cls.__name__
+ else:
+ fullname = "%s.%s" % (module, cls.__name__)
+ if parts == 0:
+ return fullname
+ name_parts = fullname.split('.')
+ return '.'.join(name_parts[-parts:])
+
+ def get_all_class_names(self):
+ """
+ Get all of the class names involved in the graph.
+ """
+ return [self.class_name(x) for x in self.all_classes]
+
+ # These are the default options for graphviz
+ default_graph_options = {
+ "rankdir": "LR",
+ "size": '"8.0, 12.0"'
+ }
+ default_node_options = {
+ "shape": "box",
+ "fontsize": 10,
+ "height": 0.25,
+ "fontname": "Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",
+ "style": '"setlinewidth(0.5)"'
+ }
+ default_edge_options = {
+ "arrowsize": 0.5,
+ "style": '"setlinewidth(0.5)"'
+ }
+
+ def _format_node_options(self, options):
+ return ','.join(["%s=%s" % x for x in options.items()])
+ def _format_graph_options(self, options):
+ return ''.join(["%s=%s;\n" % x for x in options.items()])
+
+ def generate_dot(self, fd, name, parts=0, urls={},
+ graph_options={}, node_options={},
+ edge_options={}):
+ """
+ Generate a graphviz dot graph from the classes that
+ were passed in to __init__.
+
+ *fd* is a Python file-like object to write to.
+
+ *name* is the name of the graph
+
+ *urls* is a dictionary mapping class names to http urls
+
+ *graph_options*, *node_options*, *edge_options* are
+ dictionaries containing key/value pairs to pass on as graphviz
+ properties.
+ """
+ g_options = self.default_graph_options.copy()
+ g_options.update(graph_options)
+ n_options = self.default_node_options.copy()
+ n_options.update(node_options)
+ e_options = self.default_edge_options.copy()
+ e_options.update(edge_options)
+
+ fd.write('digraph %s {\n' % name)
+ fd.write(self._format_graph_options(g_options))
+
+ for cls in self.all_classes:
+ if not self.show_builtins and cls in __builtins__.values():
+ continue
+
+ name = self.class_name(cls, parts)
+
+ # Write the node
+ this_node_options = n_options.copy()
+ url = urls.get(self.class_name(cls))
+ if url is not None:
+ this_node_options['URL'] = '"%s"' % url
+ fd.write(' "%s" [%s];\n' %
+ (name, self._format_node_options(this_node_options)))
+
+ # Write the edges
+ for base in cls.__bases__:
+ if not self.show_builtins and base in __builtins__.values():
+ continue
+
+ base_name = self.class_name(base, parts)
+ fd.write(' "%s" -> "%s" [%s];\n' %
+ (base_name, name,
+ self._format_node_options(e_options)))
+ fd.write('}\n')
+
+ def run_dot(self, args, name, parts=0, urls={},
+ graph_options={}, node_options={}, edge_options={}):
+ """
+ Run graphviz 'dot' over this graph, returning whatever 'dot'
+ writes to stdout.
+
+ *args* will be passed along as commandline arguments.
+
+ *name* is the name of the graph
+
+ *urls* is a dictionary mapping class names to http urls
+
+ Raises DotException for any of the many os and
+ installation-related errors that may occur.
+ """
+ try:
+ dot = subprocess.Popen(['dot'] + list(args),
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ close_fds=True)
+ except OSError:
+ raise DotException("Could not execute 'dot'. Are you sure you have 'graphviz' installed?")
+ except ValueError:
+ raise DotException("'dot' called with invalid arguments")
+ except:
+ raise DotException("Unexpected error calling 'dot'")
+
+ self.generate_dot(dot.stdin, name, parts, urls, graph_options,
+ node_options, edge_options)
+ dot.stdin.close()
+ result = dot.stdout.read()
+ returncode = dot.wait()
+ if returncode != 0:
+ raise DotException("'dot' returned the errorcode %d" % returncode)
+ return result
+
+class inheritance_diagram(Body, Element):
+ """
+ A docutils node to use as a placeholder for the inheritance
+ diagram.
+ """
+ pass
+
+def inheritance_diagram_directive(name, arguments, options, content, lineno,
+ content_offset, block_text, state,
+ state_machine):
+ """
+ Run when the inheritance_diagram directive is first encountered.
+ """
+ node = inheritance_diagram()
+
+ class_names = arguments
+
+ # Create a graph starting with the list of classes
+ graph = InheritanceGraph(class_names)
+
+ # Create xref nodes for each target of the graph's image map and
+ # add them to the doc tree so that Sphinx can resolve the
+ # references to real URLs later. These nodes will eventually be
+ # removed from the doctree after we're done with them.
+ for name in graph.get_all_class_names():
+ refnodes, x = xfileref_role(
+ 'class', ':class:`%s`' % name, name, 0, state)
+ node.extend(refnodes)
+ # Store the graph object so we can use it to generate the
+ # dot file later
+ node['graph'] = graph
+ # Store the original content for use as a hash
+ node['parts'] = options.get('parts', 0)
+ node['content'] = " ".join(class_names)
+ return [node]
+
+def get_graph_hash(node):
+ return md5(node['content'] + str(node['parts'])).hexdigest()[-10:]
+
+def html_output_graph(self, node):
+ """
+ Output the graph for HTML. This will insert a PNG with clickable
+ image map.
+ """
+ graph = node['graph']
+ parts = node['parts']
+
+ graph_hash = get_graph_hash(node)
+ name = "inheritance%s" % graph_hash
+ path = '_images'
+ dest_path = os.path.join(setup.app.builder.outdir, path)
+ if not os.path.exists(dest_path):
+ os.makedirs(dest_path)
+ png_path = os.path.join(dest_path, name + ".png")
+ path = setup.app.builder.imgpath
+
+ # Create a mapping from fully-qualified class names to URLs.
+ urls = {}
+ for child in node:
+ if child.get('refuri') is not None:
+ urls[child['reftitle']] = child.get('refuri')
+ elif child.get('refid') is not None:
+ urls[child['reftitle']] = '#' + child.get('refid')
+
+ # These arguments to dot will save a PNG file to disk and write
+ # an HTML image map to stdout.
+ image_map = graph.run_dot(['-Tpng', '-o%s' % png_path, '-Tcmapx'],
+ name, parts, urls)
+ return ('<img src="%s/%s.png" usemap="#%s" class="inheritance"/>%s' %
+ (path, name, name, image_map))
+
+def latex_output_graph(self, node):
+ """
+ Output the graph for LaTeX. This will insert a PDF.
+ """
+ graph = node['graph']
+ parts = node['parts']
+
+ graph_hash = get_graph_hash(node)
+ name = "inheritance%s" % graph_hash
+ dest_path = os.path.abspath(os.path.join(setup.app.builder.outdir, '_images'))
+ if not os.path.exists(dest_path):
+ os.makedirs(dest_path)
+ pdf_path = os.path.abspath(os.path.join(dest_path, name + ".pdf"))
+
+ graph.run_dot(['-Tpdf', '-o%s' % pdf_path],
+ name, parts, graph_options={'size': '"6.0,6.0"'})
+ return '\n\\includegraphics{%s}\n\n' % pdf_path
+
+def visit_inheritance_diagram(inner_func):
+ """
+ This is just a wrapper around html/latex_output_graph to make it
+ easier to handle errors and insert warnings.
+ """
+ def visitor(self, node):
+ try:
+ content = inner_func(self, node)
+ except DotException, e:
+ # Insert the exception as a warning in the document
+ warning = self.document.reporter.warning(str(e), line=node.line)
+ warning.parent = node
+ node.children = [warning]
+ else:
+ source = self.document.attributes['source']
+ self.body.append(content)
+ node.children = []
+ return visitor
+
+def do_nothing(self, node):
+ pass
+
+def setup(app):
+ setup.app = app
+ setup.confdir = app.confdir
+
+ app.add_node(
+ inheritance_diagram,
+ latex=(visit_inheritance_diagram(latex_output_graph), do_nothing),
+ html=(visit_inheritance_diagram(html_output_graph), do_nothing))
+ app.add_directive(
+ 'inheritance-diagram', inheritance_diagram_directive,
+ False, (1, 100, 0), parts = directives.nonnegative_int)
diff --git a/doc/_extensions/ipython_console_highlighting.py b/doc/_extensions/ipython_console_highlighting.py
new file mode 100644
index 0000000..217b779
--- /dev/null
+++ b/doc/_extensions/ipython_console_highlighting.py
@@ -0,0 +1,114 @@
+"""reST directive for syntax-highlighting ipython interactive sessions.
+
+XXX - See what improvements can be made based on the new (as of Sept 2009)
+'pycon' lexer for the python console. At the very least it will give better
+highlighted tracebacks.
+"""
+
+#-----------------------------------------------------------------------------
+# Needed modules
+
+# Standard library
+import re
+
+# Third party
+from pygments.lexer import Lexer, do_insertions
+from pygments.lexers.agile import (PythonConsoleLexer, PythonLexer,
+ PythonTracebackLexer)
+from pygments.token import Comment, Generic
+
+from sphinx import highlighting
+
+#-----------------------------------------------------------------------------
+# Global constants
+line_re = re.compile('.*?\n')
+
+#-----------------------------------------------------------------------------
+# Code begins - classes and functions
+
+class IPythonConsoleLexer(Lexer):
+ """
+ For IPython console output or doctests, such as:
+
+ .. sourcecode:: ipython
+
+ In [1]: a = 'foo'
+
+ In [2]: a
+ Out[2]: 'foo'
+
+ In [3]: print a
+ foo
+
+ In [4]: 1 / 0
+
+ Notes:
+
+ - Tracebacks are not currently supported.
+
+ - It assumes the default IPython prompts, not customized ones.
+ """
+
+ name = 'IPython console session'
+ aliases = ['ipython']
+ mimetypes = ['text/x-ipython-console']
+ input_prompt = re.compile("(In \[[0-9]+\]: )|( \.\.\.+:)")
+ output_prompt = re.compile("(Out\[[0-9]+\]: )|( \.\.\.+:)")
+ continue_prompt = re.compile(" \.\.\.+:")
+ tb_start = re.compile("\-+")
+
+ def get_tokens_unprocessed(self, text):
+ pylexer = PythonLexer(**self.options)
+ tblexer = PythonTracebackLexer(**self.options)
+
+ curcode = ''
+ insertions = []
+ for match in line_re.finditer(text):
+ line = match.group()
+ input_prompt = self.input_prompt.match(line)
+ continue_prompt = self.continue_prompt.match(line.rstrip())
+ output_prompt = self.output_prompt.match(line)
+ if line.startswith("#"):
+ insertions.append((len(curcode),
+ [(0, Comment, line)]))
+ elif input_prompt is not None:
+ insertions.append((len(curcode),
+ [(0, Generic.Prompt, input_prompt.group())]))
+ curcode += line[input_prompt.end():]
+ elif continue_prompt is not None:
+ insertions.append((len(curcode),
+ [(0, Generic.Prompt, continue_prompt.group())]))
+ curcode += line[continue_prompt.end():]
+ elif output_prompt is not None:
+ # Use the 'error' token for output. We should probably make
+ # our own token, but error is typicaly in a bright color like
+ # red, so it works fine for our output prompts.
+ insertions.append((len(curcode),
+ [(0, Generic.Error, output_prompt.group())]))
+ curcode += line[output_prompt.end():]
+ else:
+ if curcode:
+ for item in do_insertions(insertions,
+ pylexer.get_tokens_unprocessed(curcode)):
+ yield item
+ curcode = ''
+ insertions = []
+ yield match.start(), Generic.Output, line
+ if curcode:
+ for item in do_insertions(insertions,
+ pylexer.get_tokens_unprocessed(curcode)):
+ yield item
+
+
+def setup(app):
+ """Setup as a sphinx extension."""
+
+ # This is only a lexer, so adding it below to pygments appears sufficient.
+ # But if somebody knows that the right API usage should be to do that via
+ # sphinx, by all means fix it here. At least having this setup.py
+ # suppresses the sphinx warning we'd get without it.
+ pass
+
+#-----------------------------------------------------------------------------
+# Register the extension as a valid pygments lexer
+highlighting.lexers['ipython'] = IPythonConsoleLexer()
diff --git a/doc/_extensions/ipython_directive.py b/doc/_extensions/ipython_directive.py
new file mode 100644
index 0000000..e69de29
diff --git a/doc/_extensions/numpydoc.py b/doc/_extensions/numpydoc.py
new file mode 100644
index 0000000..ff6c44c
--- /dev/null
+++ b/doc/_extensions/numpydoc.py
@@ -0,0 +1,116 @@
+"""
+========
+numpydoc
+========
+
+Sphinx extension that handles docstrings in the Numpy standard format. [1]
+
+It will:
+
+- Convert Parameters etc. sections to field lists.
+- Convert See Also section to a See also entry.
+- Renumber references.
+- Extract the signature from the docstring, if it can't be determined otherwise.
+
+.. [1] http://projects.scipy.org/scipy/numpy/wiki/CodingStyleGuidelines#docstring-standard
+
+"""
+
+import os, re, pydoc
+from docscrape_sphinx import get_doc_object, SphinxDocString
+import inspect
+
+def mangle_docstrings(app, what, name, obj, options, lines,
+ reference_offset=[0]):
+ if what == 'module':
+ # Strip top title
+ title_re = re.compile(r'^\s*[#*=]{4,}\n[a-z0-9 -]+\n[#*=]{4,}\s*',
+ re.I|re.S)
+ lines[:] = title_re.sub('', "\n".join(lines)).split("\n")
+ else:
+ doc = get_doc_object(obj, what, "\n".join(lines))
+ lines[:] = str(doc).split("\n")
+
+ if app.config.numpydoc_edit_link and hasattr(obj, '__name__') and \
+ obj.__name__:
+ if hasattr(obj, '__module__'):
+ v = dict(full_name="%s.%s" % (obj.__module__, obj.__name__))
+ else:
+ v = dict(full_name=obj.__name__)
+ lines += ['', '.. htmlonly::', '']
+ lines += [' %s' % x for x in
+ (app.config.numpydoc_edit_link % v).split("\n")]
+
+ # replace reference numbers so that there are no duplicates
+ references = []
+ for l in lines:
+ l = l.strip()
+ if l.startswith('.. ['):
+ try:
+ references.append(int(l[len('.. ['):l.index(']')]))
+ except ValueError:
+ print "WARNING: invalid reference in %s docstring" % name
+
+ # Start renaming from the biggest number, otherwise we may
+ # overwrite references.
+ references.sort()
+ if references:
+ for i, line in enumerate(lines):
+ for r in references:
+ new_r = reference_offset[0] + r
+ lines[i] = lines[i].replace('[%d]_' % r,
+ '[%d]_' % new_r)
+ lines[i] = lines[i].replace('.. [%d]' % r,
+ '.. [%d]' % new_r)
+
+ reference_offset[0] += len(references)
+
+def mangle_signature(app, what, name, obj, options, sig, retann):
+ # Do not try to inspect classes that don't define `__init__`
+ if (inspect.isclass(obj) and
+ 'initializes x; see ' in pydoc.getdoc(obj.__init__)):
+ return '', ''
+
+ if not (callable(obj) or hasattr(obj, '__argspec_is_invalid_')): return
+ if not hasattr(obj, '__doc__'): return
+
+ doc = SphinxDocString(pydoc.getdoc(obj))
+ if doc['Signature']:
+ sig = re.sub("^[^(]*", "", doc['Signature'])
+ return sig, ''
+
+def initialize(app):
+ try:
+ app.connect('autodoc-process-signature', mangle_signature)
+ except:
+ monkeypatch_sphinx_ext_autodoc()
+
+def setup(app, get_doc_object_=get_doc_object):
+ global get_doc_object
+ get_doc_object = get_doc_object_
+
+ app.connect('autodoc-process-docstring', mangle_docstrings)
+ app.connect('builder-inited', initialize)
+ app.add_config_value('numpydoc_edit_link', None, True)
+
+#------------------------------------------------------------------------------
+# Monkeypatch sphinx.ext.autodoc to accept argspecless autodocs (Sphinx < 0.5)
+#------------------------------------------------------------------------------
+
+def monkeypatch_sphinx_ext_autodoc():
+ global _original_format_signature
+ import sphinx.ext.autodoc
+
+ if sphinx.ext.autodoc.format_signature is our_format_signature:
+ return
+
+ print "[numpydoc] Monkeypatching sphinx.ext.autodoc ..."
+ _original_format_signature = sphinx.ext.autodoc.format_signature
+ sphinx.ext.autodoc.format_signature = our_format_signature
+
+def our_format_signature(what, obj):
+ r = mangle_signature(None, what, None, obj, None, None, None)
+ if r is not None:
+ return r[0]
+ else:
+ return _original_format_signature(what, obj)
diff --git a/doc/conf.py b/doc/conf.py
new file mode 100644
index 0000000..ed3d742
--- /dev/null
+++ b/doc/conf.py
@@ -0,0 +1,305 @@
+# -*- coding: utf-8 -*-
+#
+# multipletau documentation build configuration file, created by
+# sphinx-quickstart on Sat Feb 22 09:35:49 2014.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+sys.path.insert(0, os.path.abspath('../multipletau'))
+sys.path.insert(0, os.path.abspath('../'))
+
+sys.path.append(os.path.abspath('_extensions'))
+
+import multipletau
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+#extensions = [
+# 'sphinx.ext.autodoc',
+# 'sphinx.ext.doctest',
+# 'sphinx.ext.coverage',
+# 'sphinx.ext.pngmath',
+# 'sphinx.ext.viewcode',
+#]
+
+
+extensions = [
+# 'matplotlib.sphinxext.mathmpl',
+# 'matplotlib.sphinxext.only_directives',
+# 'matplotlib.sphinxext.plot_directive',
+# 'sphinx.ext.viewcode',
+# 'ipython_directive',
+ 'sphinx.ext.intersphinx',
+# 'sphinx.ext.autosummary',
+ 'sphinx.ext.autodoc',
+# 'sphinx.ext.doctest',
+# 'ipython_console_highlighting',
+# 'sphinx.ext.pngmath',
+ 'sphinx.ext.mathjax',
+# 'sphinx.ext.todo',
+# 'inheritance_diagram',
+ 'numpydoc',
+# 'hidden_code_block',
+# 'sphinx.ext.autosummary'
+ ]
+
+
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'multipletau'
+copyright = u'2014, Paul Müller'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = multipletau.__version__
+# The full version, including alpha/beta/rc tags.
+release = multipletau.__version__
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+#pygments_style = 'default'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'nature'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+#html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'multipletaudoc'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ ('index', 'multipletau.tex', u'multipletau Documentation',
+ u'Paul Müller', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'multipletau', u'multipletau Documentation',
+ [u'Paul Müller'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'multipletau', u'multipletau Documentation',
+ u'Paul Müller', 'multipletau',
+ 'A multiple-tau algorithm that utilizes numpy.',
+ 'Numeric'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
+
+# -----------------------------------------------------------------------------
+# intersphinx
+# -----------------------------------------------------------------------------
+_python_doc_base = 'http://docs.python.org/2.7'
+intersphinx_mapping = {
+ _python_doc_base: None,
+ 'http://docs.scipy.org/doc/numpy': None,
+ 'http://docs.scipy.org/doc/scipy/reference': None,
+}
+
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 0000000..2ae9de5
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,11 @@
+multipletau reference
+=====================
+.. automodule:: multipletau
+ :members: autocorrelate, correlate, correlate_numpy
+
+
+testing multipletau
+===================
+.. automodule:: test
+ :members:
+
diff --git a/doc/make.bat b/doc/make.bat
new file mode 100644
index 0000000..cd549e4
--- /dev/null
+++ b/doc/make.bat
@@ -0,0 +1,242 @@
+ at ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^<target^>` where ^<target^> is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. xml to make Docutils-native XML files
+ echo. pseudoxml to make pseudoxml-XML files for display purposes
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PKSLiver.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PKSLiver.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdf" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf
+ cd %BUILDDIR%/..
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdfja" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf-ja
+ cd %BUILDDIR%/..
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+if "%1" == "xml" (
+ %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The XML files are in %BUILDDIR%/xml.
+ goto end
+)
+
+if "%1" == "pseudoxml" (
+ %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
+ goto end
+)
+
+:end
diff --git a/doc/make.py b/doc/make.py
new file mode 100644
index 0000000..adec117
--- /dev/null
+++ b/doc/make.py
@@ -0,0 +1,14 @@
+import os
+import sys
+sys.path.insert(0, '..')
+import shutil
+import fnmatch
+
+
+os.system('make html')
+
+# os.system('make latex')
+# olddir = os.curdir
+# os.chdir('_build\\latex\\')
+# os.system('pdflatex multipletau.tex')
+# os.chdir(olddir)
diff --git a/doc/testing_multipletau.png b/doc/testing_multipletau.png
new file mode 100644
index 0000000..c1bdb0e
Binary files /dev/null and b/doc/testing_multipletau.png differ
diff --git a/multipletau/__init__.py b/multipletau/__init__.py
new file mode 100644
index 0000000..79b5fbc
--- /dev/null
+++ b/multipletau/__init__.py
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+"""
+ Provides a multiple-τ algorithm for Python 2.7 and Python 3.x and
+ requires the package :py:mod:`numpy`.
+
+ Multipe-τ correlation is computed on a logarithmic scale (less
+ data points are computed) and is thus much faster than conventional
+ correlation on a linear scale such as :py:func:`numpy.correlate`.
+
+ Recommended literature:
+
+ - Klaus Schaetzel and Rainer Peters; *Noise on multiple-tau photon
+ correlation data*. Proc. SPIE 1430, Photon Correlation
+ Spectroscopy: Multicomponent Systems, 109 (June 1, 1991);
+ http://doi.org/10.1117/12.44160
+
+ - Thorsten Wohland, Rudolf Rigler, and Horst Vogel; *The Standard
+ Deviation in Fluorescence Correlation Spectroscopy*. Biophysical
+ Journal, 80 (June 1, 2001);
+ http://dx.doi.org/10.1016/S0006-3495(01)76264-9
+
+ The source code of multipletau is available at
+ https://github.com/paulmueller/multipletau.
+"""
+from ._multipletau import *
+
+__version__ = "0.1.4"
+__author__ = "Paul Mueller"
+__email__ = "paul.mueller at biotec.tu-dresden.de"
+__license__ = "OpenBSD"
diff --git a/multipletau/_multipletau.py b/multipletau/_multipletau.py
new file mode 100755
index 0000000..159b2e9
--- /dev/null
+++ b/multipletau/_multipletau.py
@@ -0,0 +1,466 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+"""
+ A multiple-τ algorithm for Python 2.7 and 3.x.
+
+ Copyright (c) 2014 Paul Müller
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ 3. Neither the name of multipletau nor the names of its contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INFRAE OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+from __future__ import division
+
+import numpy as np
+import warnings
+
+__all__ = ["autocorrelate", "correlate", "correlate_numpy"]
+
+
+def autocorrelate(a, m=16, deltat=1, normalize=False,
+ copy=True, dtype=None):
+ """
+ Autocorrelation of a 1-dimensional sequence on a log2-scale.
+
+ This computes the correlation according to
+ :py:func:`numpy.correlate` for positive :math:`k` on a base 2
+ logarithmic scale.
+
+ numpy.correlate(a, a, mode="full")[len(a)-1:]
+
+ :math:`z_k = \Sigma_n a_n a_{n+k}`
+
+
+ Note that only the correlation in the positive direction is
+ computed.
+
+ Parameters
+ ----------
+ a : array_like
+ input sequence of real numbers
+ m : even integer
+ defines the number of points on one level, must be an
+ even integer
+ deltat : float
+ distance between bins
+ normalize : bool
+ normalize the result to the square of the average input
+ signal and the factor `M-k`.
+ copy : bool
+ copy input array, set to False to save memory
+ dtype : dtype, optional
+ The type of the returned array and of the accumulator in
+ which the elements are summed. By default, the dtype of
+ `a` is used.
+
+
+ Returns
+ -------
+ autocorrelation : ndarray
+ Nx2 array containing lag time and autocorrelation
+
+
+ Notes
+ -----
+ The algorithm computes the correlation with the convention of the
+ curve decaying to zero.
+
+ For experiments like e.g. fluorescence correlation spectroscopy,
+ the signal can be normalized to `M-k` by invoking:
+
+ normalize = True
+
+ For emulating the numpy.correlate behavior on a logarithmic
+ scale (default behavior) use:
+
+ normalize = False
+
+
+ Examples
+ --------
+ >>> from numpy import dtype
+ >>> from multipletau import autocorrelate
+ >>> autocorrelate(range(42), m=2, dtype=dtype(float))
+ array([[ 1.00000000e+00, 2.29600000e+04],
+ [ 2.00000000e+00, 2.21000000e+04],
+ [ 4.00000000e+00, 2.03775000e+04],
+ [ 8.00000000e+00, 1.50612000e+04]])
+
+ """
+ traceavg = np.average(a)
+ if normalize and traceavg == 0:
+ raise ZeroDivisionError("Normalization not possible. "+
+ "The average of the input *binned_array* is zero.")
+
+ trace = np.array(a, dtype=dtype, copy=copy)
+ dtype = trace.dtype
+
+ if dtype.kind in ["b","i","u"]:
+ warnings.warn("Converting input data type ({}) to float.".
+ format(dtype))
+ dtype = np.dtype(float)
+ trace = np.array(a, dtype=dtype, copy=copy)
+
+ # Complex data
+ if dtype.kind == "c":
+ raise NotImplementedError(
+ "Please use `multipletau.correlate` for complex data.")
+
+
+ # Check parameters
+ if np.around(m/2) != m/2:
+ mold = 1*m
+ m = int((np.around(m/2)+1) * 2)
+ warnings.warn("Invalid value of m={}. Using m={} instead"
+ .format(mold,m))
+ else:
+ m = int(m)
+
+ N = N0 = len(trace)
+
+ # Find out the length of the correlation function.
+ # The integer k defines how many times we can average over
+ # two neighboring array elements in order to obtain an array of
+ # length just larger than m.
+ k = int(np.floor(np.log2(N/m)))
+
+ # In the base2 multiple-tau scheme, the length of the correlation
+ # array is (only taking into account values that are computed from
+ # traces that are just larger than m):
+ lenG = np.int(np.floor(m + k*m/2))
+
+ G = np.zeros((lenG, 2), dtype=dtype)
+
+ normstat = np.zeros(lenG, dtype=dtype)
+ normnump = np.zeros(lenG, dtype=dtype)
+
+ # We use the fluctuation of the signal around the mean
+ if normalize:
+ trace -= traceavg
+ if N < 2*m:
+ # Otherwise the following for-loop will fail:
+ raise ValueError("len(binned_array) must be larger than 2m.")
+ ## Calculate autocorrelation function for first m bins
+ # Discrete convolution of m elements
+ for n in range(1,m+1):
+ G[n-1,0] = deltat * n
+ # This is the computationally intensive step
+ G[n-1,1] = np.sum(trace[:N-n]*trace[n:], dtype=dtype)
+ normstat[n-1] = N-n
+ normnump[n-1] = N
+ # Now that we calculated the first m elements of G, let us
+ # go on with the next m/2 elements.
+ # Check if len(trace) is even:
+ if N%2 == 1:
+ N -= 1
+ # Add up every second element
+ trace = (trace[:N:2]+trace[1:N+1:2])/2
+ N /= 2
+ ## Start iteration for each m/2 values
+ for step in range(1,k+1):
+ ## Get the next m/2 values via correlation of the trace
+ for n in range(1,int(m/2)+1):
+ idx = int(m + n - 1 + (step-1)*m/2)
+ if len(trace[:N-(n+m/2)]) == 0:
+ # This is a shortcut that stops the iteration once the
+ # length of the trace is too small to compute a corre-
+ # lation. The actual length of the correlation function
+ # does not only depend on k - We also must be able to
+ # perform the sum with repect to k for all elements.
+ # For small N, the sum over zero elements would be
+ # computed here.
+ #
+ # One could make this for loop go up to maxval, where
+ # maxval1 = int(m/2)
+ # maxval2 = int(N-m/2-1)
+ # maxval = min(maxval1, maxval2)
+ # However, we then would also need to find out which
+ # element in G is the last element...
+ G = G[:idx-1]
+ normstat = normstat[:idx-1]
+ normnump = normnump[:idx-1]
+ # Note that this break only breaks out of the current
+ # for loop. However, we are already in the last loop
+ # of the step-for-loop. That is because we calculated
+ # k in advance.
+ break
+ else:
+ G[idx,0] = deltat * (n+m/2) * 2**step
+ # This is the computationally intensive step
+ G[idx,1] = np.sum(trace[:N-(n+m/2)]*trace[(n+m/2):],
+ dtype=dtype)
+ normstat[idx] = N-(n+m/2)
+ normnump[idx] = N
+ # Check if len(trace) is even:
+ if N%2 == 1:
+ N -= 1
+ # Add up every second element
+ trace = (trace[:N:2]+trace[1:N+1:2])/2
+ N /= 2
+
+ if normalize:
+ G[:,1] /= traceavg**2 * normstat
+ else:
+ G[:,1] *= N0/normnump
+
+ return G
+
+
+
+def correlate(a, v, m=16, deltat=1, normalize=False,
+ copy=True, dtype=None):
+ """
+ Cross-correlation of two 1-dimensional sequences
+ on a log2-scale.
+
+ This computes the cross-correlation according to
+ :py:func:`numpy.correlate` for positive :math:`k` on a base 2
+ logarithmic scale.
+
+ numpy.correlate(a, v, mode="full")[len(a)-1:]
+
+ :math:`z_k = \Sigma_n a_n v_{n+k}`
+
+ Note that only the correlation
+ in the positive direction is computed.
+
+
+ Parameters
+ ----------
+ a, v : array_like
+ input sequences with equal length
+ m : even integer
+ defines the number of points on one level, must be an
+ even integer
+ deltat : float
+ distance between bins
+ normalize : bool
+ normalize the result to the square of the average input
+ signal and the factor `M-k`.
+ copy : bool
+ copy input array, set to False to save memory
+ dtype : dtype, optional
+ The type of the returned array and of the accumulator in
+ which the elements are summed. By default, the dtype of
+ `a` is used.
+
+
+ Returns
+ -------
+ crosscorrelation : ndarray
+ Nx2 array containing lag time and cross-correlation
+
+
+ Notes
+ -----
+ The algorithm computes the correlation with the convention of the
+ curve decaying to zero.
+
+ For experiments like e.g. fluorescence correlation spectroscopy,
+ the signal can be normalized to `M-k` by invoking:
+
+ normalize = True
+
+ For emulating the numpy.correlate behavior on a logarithmic
+ scale (default behavior) use:
+
+ normalize = False
+
+
+ Examples
+ --------
+ >>> from numpy import dtype
+ >>> from multipletau import correlate
+ >>> correlate(range(42), range(1,43), m=2, dtype=dtype(float))
+ array([[ 1.00000000e+00, 2.38210000e+04],
+ [ 2.00000000e+00, 2.29600000e+04],
+ [ 4.00000000e+00, 2.12325000e+04],
+ [ 8.00000000e+00, 1.58508000e+04]])
+
+ """
+ ## See `autocorrelation` for better documented code.
+ traceavg1 = np.average(v)
+ traceavg2 = np.average(a)
+ if normalize and traceavg1*traceavg2 == 0:
+ raise ZeroDivisionError("Normalization not possible. "+
+ "The average of the input *binned_array* is zero.")
+
+ trace1 = np.array(v, dtype=dtype, copy=copy)
+ dtype = trace1.dtype
+
+ if dtype.kind in ["b","i","u"]:
+ warnings.warn(
+ "Converting input data type ({}) to float.".format(dtype))
+ dtype = np.dtype(float)
+ trace1 = np.array(v, dtype=dtype, copy=copy)
+
+ # Prevent traces from overwriting each other
+ if a is v:
+ # Force copying trace 2
+ copy = True
+
+ trace2 = np.array(a, dtype=dtype, copy=copy)
+
+ # Complex data
+ if dtype.kind == "c":
+ trace1.imag *= -1
+
+ # Check parameters
+ if np.around(m/2) != m/2:
+ mold = 1*m
+ m = int((np.around(m/2)+1) * 2)
+ warnings.warn("Invalid value of m={}. Using m={} instead"
+ .format(mold,m))
+ else:
+ m = int(m)
+
+ if len(a) != len(v):
+ raise ValueError("Input arrays must be of equal length.")
+
+ N = N0 = len(trace1)
+ # Find out the length of the correlation function.
+ # The integer k defines how many times we can average over
+ # two neighboring array elements in order to obtain an array of
+ # length just larger than m.
+ k = int(np.floor(np.log2(N/m)))
+
+ # In the base2 multiple-tau scheme, the length of the correlation
+ # array is (only taking into account values that are computed from
+ # traces that are just larger than m):
+ lenG = np.int(np.floor(m + k*m/2))
+
+ G = np.zeros((lenG, 2), dtype=dtype)
+ normstat = np.zeros(lenG, dtype=dtype)
+ normnump = np.zeros(lenG, dtype=dtype)
+
+ # We use the fluctuation of the signal around the mean
+ if normalize:
+ trace1 -= traceavg1
+ trace2 -= traceavg2
+ if N < 2*m:
+ # Otherwise the following for-loop will fail:
+ raise ValueError("len(binned_array) must be larger than 2m.")
+ # Calculate autocorrelation function for first m bins
+ for n in range(1,m+1):
+ G[n-1,0] = deltat * n
+ G[n-1,1] = np.sum(trace1[:N-n]*trace2[n:])
+ normstat[n-1] = N-n
+ normnump[n-1] = N
+ # Check if len(trace) is even:
+ if N%2 == 1:
+ N -= 1
+ # Add up every second element
+ trace1 = (trace1[:N:2]+trace1[1:N+1:2])/2
+ trace2 = (trace2[:N:2]+trace2[1:N+1:2])/2
+ N /= 2
+
+ for step in range(1,k+1):
+ # Get the next m/2 values of the trace
+ for n in range(1,int(m/2)+1):
+ idx = int(m + n - 1 + (step-1)*m/2)
+ if len(trace1[:N-(n+m/2)]) == 0:
+ # Abort
+ G = G[:idx-1]
+ normstat = normstat[:idx-1]
+ normnump = normnump[:idx-1]
+ break
+ else:
+ G[idx,0] = deltat * (n+m/2) * 2**step
+ G[idx,1] = np.sum(trace1[:N-(n+m/2)]*trace2[(n+m/2):])
+ normstat[idx] = N-(n+m/2)
+ normnump[idx] = N
+
+ # Check if len(trace) is even:
+ if N%2 == 1:
+ N -= 1
+ # Add up every second element
+ trace1 = (trace1[:N:2]+trace1[1:N+1:2])/2
+ trace2 = (trace2[:N:2]+trace2[1:N+1:2])/2
+ N /= 2
+
+ if normalize:
+ G[:,1] /= traceavg1*traceavg2 * normstat
+ else:
+ G[:,1] *= N0/normnump
+
+ return G
+
+
+
+def correlate_numpy(a, v, deltat=1, normalize=False,
+ dtype=None, copy=True):
+ """
+ Convenience function that wraps around numpy.correlate and
+ returns the data as multipletau.correlate does.
+
+ Parameters
+ ----------
+ a, v : array_like
+ input sequences
+ deltat : float
+ distance between bins
+ normalize : bool
+ normalize the result to the square of the average input
+ signal and the factor (M-k). The resulting curve follows
+ the convention of decaying to zero for large lag times.
+ copy : bool
+ copy input array, set to False to save memory
+ dtype : dtype, optional
+ The type of the returned array and of the accumulator in
+ which the elements are summed. By default, the dtype of
+ `a` is used.
+
+ Returns
+ -------
+ crosscorrelation : ndarray
+ Nx2 array containing lag time and cross-correlation
+ """
+
+ avg = np.average(a)
+ vvg = np.average(v)
+
+ if dtype is None:
+ dtype = a.dtype
+
+ if len(a) != len(v):
+ raise ValueError("Arrays must be of same length.")
+
+ ab = np.array(a, dtype=dtype, copy=copy)
+ vb = np.array(v, dtype=dtype, copy=copy)
+
+ Gd = np.correlate(ab-avg, vb-vvg, mode="full")[len(ab)-1:]
+
+ if normalize:
+ N = len(Gd)
+ m = N - np.arange(N)
+ Gd /= m * avg * vvg
+ G = np.zeros((len(Gd),2))
+ G[:,1] = Gd
+ G[:,0] = np.arange(len(Gd))*deltat
+ return G
+
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..861a9f5
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build =
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..783eb3a
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+# To create a distribution package for pip or easy-install:
+# python setup.py sdist
+from setuptools import setup, find_packages
+from os.path import join, dirname, realpath
+from warnings import warn
+
+import multipletau
+
+setup(
+ name='multipletau',
+ author='Paul Mueller',
+ author_email='paul.mueller at biotec.tu-dresden.de',
+ url='https://github.com/paulmueller/multipletau',
+ version=multipletau.__version__,
+ packages=['multipletau'],
+ package_dir={'multipletau': 'multipletau'},
+ license="OpenBSD",
+ description='A multiple-tau algorithm for Python/NumPy.',
+ long_description=open(join(dirname(__file__), 'README.txt')).read(),
+ install_requires=["NumPy >= 1.5.1"]
+ )
+
+
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644
index 0000000..a39687e
--- /dev/null
+++ b/test/__init__.py
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+""" This module is used for testing the multiple-tau algorithm.
+ Executing this module as a package by invoking
+
+ python -m test
+
+ creates plots that illustrate the difference between
+ :py:func:`multipletau.correlate` and :py:func:`numpy.correlate`.
+
+ .. image:: testing_multipletau.png
+ :width: 500 px
+
+"""
+
+from __future__ import division
+from __future__ import print_function
+
+import numpy as np
+
+__all__=["noise_exponential", "noise_cross_exponential"]
+
+def noise_exponential(N, tau=20, variance=1, deltat=1):
+ """
+ Generate exponentially correlated noise.
+
+ Parameters
+ ----------
+ N : integer
+ Total number of samples
+ tau : float
+ Correlation time of the exponential in `deltat`
+ variance : float
+ Variance of the noise
+ deltat : float
+ Bin size of output array, defines the time scale of `tau`
+
+ Returns
+ -------
+ a : ndarray
+ Exponentially correlated noise.
+ """
+ # time constant (inverse of correlationtime tau)
+ g = deltat/tau
+ # variance
+ s0 = variance
+
+ # normalization factor (memory of the trace)
+ exp_g = np.exp(-g)
+ one_exp_g = 1-exp_g
+ z_norm_factor = np.sqrt(1-np.exp(-2*g))/one_exp_g
+
+ # create random number array
+ # generates random numbers in interval [0,1)
+ randarray = np.random.random(N)
+ # make numbers random in interval [-1,1)
+ randarray = 2*(randarray-0.5)
+
+ # simulate exponential random behavior
+ a = np.zeros(N)
+ a[0] = one_exp_g*randarray[0]
+ b = 1* a
+ for i in np.arange(N-1)+1:
+ a[i] = exp_g*a[i-1] + one_exp_g*randarray[i]
+
+ # Solving the equation iteratively leads to this equation:
+ #j = np.arange(i)
+ #a[i] = a[0]*exp_g**(i) + \
+ # one_exp_g)*np.sum(exp_g**(i-1-j)*randarray[1:i+1])
+
+ a = a * z_norm_factor*s0
+ return a
+
+
+def noise_cross_exponential(N, tau=20, variance=1, deltat=1):
+ """
+ Generate exponentially cross-correlated noise.
+
+ Parameters
+ ----------
+ N : integer
+ Total number of samples
+ tau : float
+ Correlation time of the exponential in `deltat`
+ variance : float
+ Variance of the noise
+ deltat : float
+ Bin size of output array, defines the time scale of `tau`
+
+ Returns
+ -------
+ a, randarray : ndarrays
+ Array `a` has positive exponential correlation to the 'truly'
+ random array `randarray`.
+ """
+ # length of mean0 trace
+ N_steps = N
+ # time constant (inverse of correlationtime tau)
+ g = deltat/tau
+ # variance
+ s0 = variance
+ # normalization factor (memory of the trace)
+ exp_g = np.exp(-g)
+ one_exp_g = 1-exp_g
+ z_norm_factor = np.sqrt(1-np.exp(-2*g))/one_exp_g
+
+ # create random number array
+ # generates random numbers in interval [0,1)
+ randarray = np.random.random(N)
+ # make numbers random in interval [-1,1)
+ randarray = 2*(randarray-0.5)
+
+ # simulate exponential random behavior
+ a = np.zeros(N)
+ a[0] = one_exp_g*randarray[0]
+
+ b = np.zeros(N)
+ b[0] = one_exp_g*randarray[0]
+ # slow
+ #for i in np.arange(N-1)+1:
+ # for j in np.arange(i-1):
+ # a[i] += exp_g**j*randarray[i-j]
+ # a[i] += one_exp_g*randarray[i]
+ # faster
+ j = np.arange(N+5)
+ for i in np.arange(N-1)+1:
+ a[i] += np.sum(exp_g**j[2:i+1] * randarray[2:i+1][::-1])
+ a[i] += one_exp_g*randarray[i]
+
+ a *= z_norm_factor*s0
+ randarray = randarray * z_norm_factor*s0
+
+ return a, randarray
+
+def test():
+ from .__main__ import test
+ test()
+
diff --git a/test/__main__.py b/test/__main__.py
new file mode 100644
index 0000000..a1f7bc9
--- /dev/null
+++ b/test/__main__.py
@@ -0,0 +1,108 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+from .__init__ import noise_exponential, noise_cross_exponential
+
+def test():
+ import numpy as np
+ import os
+ import sys
+ from matplotlib import pylab as plt
+ sys.path.append(os.path.realpath(os.path.dirname(__file__)+"/../"))
+ from multipletau import autocorrelate, correlate, correlate_numpy
+ ## Starting parameters
+ N = np.int(np.pi*1e3)
+ countrate = 250. * 1e-3 # in Hz
+ taudiff = 55. # in us
+ deltat = 2e-6 # time discretization [s]
+ normalize = True
+
+ # time factor
+ taudiff *= deltat
+
+ ##
+ ## Autocorrelation
+ ##
+ print("Creating noise for autocorrelation")
+ data = noise_exponential(N, taudiff, deltat=deltat)
+ data += - np.average(data)
+ if normalize:
+ data += countrate
+ # multipletau
+ print("Performing autocorrelation (multipletau).")
+ G = autocorrelate(data, deltat=deltat, normalize=normalize)
+ # numpy.correlate for comparison
+ if len(data) < 1e5:
+ print("Performing autocorrelation (numpy).")
+ Gd = correlate_numpy(data, data, deltat=deltat,
+ normalize=normalize)
+ # Calculate the expected curve
+ x = G[:,0]
+ amp = np.correlate(data-np.average(data), data-np.average(data),
+ mode="valid")
+ if normalize:
+ amp /= len(data) * countrate**2
+ y = amp*np.exp(-x/taudiff)
+
+ ##
+ ## Cross-correlation
+ ##
+ print("Creating noise for cross-correlation")
+ a, v = noise_cross_exponential(N, taudiff, deltat=deltat)
+ a += - np.average(a)
+ v += - np.average(v)
+ if normalize:
+ a += countrate
+ v += countrate
+ # multipletau
+ Gccforw = correlate(a, v, deltat=deltat, normalize=normalize)
+ Gccback = correlate(v, a, deltat=deltat, normalize=normalize)
+ if len(a) < 1e5:
+ print("Performing autocorrelation (numpy).")
+ Gdccforw = correlate_numpy(a, v, deltat=deltat, normalize=normalize)
+ # Calculate the expected curve
+ xcc = Gccforw[:,0]
+ ampcc = np.correlate(a-np.average(a), v-np.average(v), mode="valid")
+
+ if normalize:
+ ampcc /= len(a) * countrate**2
+ ycc = ampcc*np.exp(-xcc/taudiff)
+
+
+ ##
+ ## Plotting
+ ##
+
+ # AC
+ fig = plt.figure()
+ fig.canvas.set_window_title('testing multipletau')
+ ax = fig.add_subplot(2,1,1)
+ ax.set_xscale('log')
+ plt.plot(x, y, "g-", label="input model")
+ plt.plot(G[:,0], G[:,1], "r-", label="autocorrelate")
+ if len(data) < 1e5:
+ plt.plot(Gd[:,0], Gd[:,1] , "b--", label="correlate (numpy)")
+ plt.xlabel("lag channel")
+ plt.ylabel("autocorrelation")
+ plt.legend(loc=0, fontsize='small')
+ plt.ylim( -amp*.2, amp*1.2)
+
+
+ ## CC
+ ax = fig.add_subplot(2,1,2)
+ ax.set_xscale('log')
+ plt.plot(xcc, ycc, "g-", label="input model")
+ plt.plot(Gccforw[:,0], Gccforw[:,1], "r-", label="forward")
+ if len(data) < 1e5:
+ plt.plot(Gdccforw[:,0], Gdccforw[:,1] , "b--", label="forward (numpy)")
+ plt.plot(Gccback[:,0], Gccback[:,1], "r--", label="backward")
+ plt.xlabel("lag channel")
+ plt.ylabel("cross-correlation")
+ plt.legend(loc=0, fontsize='small')
+
+ plt.ylim( -ampcc*.2, ampcc*1.2)
+
+ plt.tight_layout()
+ plt.show()
+
+if __name__ == '__main__':
+ test()
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/python-multipletau.git
More information about the debian-med-commit
mailing list