├── .travis.yml
├── INSTALL.txt
├── README.md
├── archive
├── matrix2latexMatlab-1.0.0.tar.gz
└── matrix2latexPython-1.0.0.tar.gz
├── archiver.py
├── copying.txt
├── doc
├── Makefile
├── compatibleTable.tex
├── doc.pdf
├── doc.tex
├── facT.tex
├── facV.tex
├── factorial.py
├── features.tex
├── niceTable.tex
├── table_ohm.tex
├── table_ohm2.tex
├── test.tex
├── tex
│ ├── introduction.tex
│ ├── matlab.tex
│ ├── python.tex
│ └── texPreamble.tex
└── tmp.tex
├── doc_sphinx
├── Makefile
├── conf.py
├── index.txt
└── sphinxext
│ ├── apigen.py
│ ├── docscrape.py
│ ├── docscrape_sphinx.py
│ ├── inheritance_diagram.py
│ ├── ipython_console_highlighting.py
│ └── numpydoc.py
├── matrix2latex
├── IOString.py
├── __init__.py
├── error.py
├── fixEngineeringNotation.py
├── matrix2latex.py
├── pagination.py
└── render.py
├── setup.py
├── simpleExample.png
├── src_matlab
├── fixEngineeringNotation.m
└── matrix2latex.m
└── test
├── README.txt
├── test.m
├── test.py
├── test.tex
├── testVersionCompatibility.py
├── test_syntaxError.py
└── test_util.py
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - "2.7"
4 | - "3.4"
5 | - "3.5"
6 | - "3.9"
7 | - "nightly"
8 | - "pypy"
9 | - "pypy3"
10 | # command to install dependencies
11 | install: "pip install ."
12 | # command to run tests
13 | script: ./test/test.py
14 | notifications:
15 | email: false
--------------------------------------------------------------------------------
/INSTALL.txt:
--------------------------------------------------------------------------------
1 | == Python ==
2 | To use the code place the folder matrix2latex/ in our \verb!PYTHONPATH!,
3 | this can be done with the usual
4 | > sudo python setup.py install
5 | Alternativly, your current working directory is always on your \verb!PYTHONPATH!.
6 |
7 | == Matlab ==
8 | Place the matlab files in our MATLABPATH containing at least:
9 | matrix2latex.m and fixEngineeringNotation.m
10 | Your current working directory is always on your MATLABPATH.
11 |
12 | See the following article from mathworks
13 | \url{http://www.mathworks.se/help/techdoc/ref/path.html}.
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/TheChymera/matrix2latex)
2 | Matrix2LaTeX
3 | ============
4 | Takes a Python or MATLAB matrix and outputs a LaTeX table, a LaTeX matrix, or a complete LaTeX document (and optionally calls `latex` for compilation).
5 | A number of configuration options are available.
6 | The default table output is geared towards the standard recommended by IEEE, and uses the latex package booktabs.
7 | Check out the [documentation](https://github.com/TheChymera/matrix2latex/raw/master/doc/doc.pdf "doc.pdf") for more example output and usage.
8 |
9 | Installation
10 | ------------
11 | From source:
12 | ```bash
13 | pip install git+https://github.com/TheChymera/matrix2latex.git
14 | ```
15 | Note that on some systems you may have to replace `pip` by `pip3` to use Python 3 for the installation.
16 | Furthermore, if you only wish to install the package for the current user, you should supply the `--user` flag to the above command.
17 |
18 | Example
19 | -------
20 | The following python code:
21 | ```python
22 | from matrix2latex import matrix2latex
23 | m = [[1, 1], [2, 4], [3, 9]] # python nested list
24 | t = matrix2latex(m)
25 | print(t)
26 | ```
27 | or equivalent matlab code:
28 | ```matlab
29 | m = [1, 1; 2, 4; 3, 9];
30 | filename = '' % do not write to file
31 | t = matrix2latex(m, filename)
32 | ```
33 | produces
34 | ```latex
35 | \begin{table}[ht]
36 | \begin{center}
37 | \begin{tabular}{cc}
38 | \toprule
39 | $1$ & $1$\\
40 | $2$ & $4$\\
41 | $3$ & $9$\\
42 | \bottomrule
43 | \end{tabular}
44 | \end{center}
45 | \end{table}
46 | ```
47 |
48 | 
49 |
50 | Various options are available to change the latex environment (e.g. to a matrix) or to provide
51 | header, footer, caption, label, format and/or alignment. Please see the [documentation](https://github.com/TheChymera/matrix2latex/raw/master/doc/doc.pdf "doc.pdf") for details.
52 |
53 | History
54 | -------
55 | Inspired by the work of koehler@in.tum.de who has written
56 | a similar package only for matlab
57 | http://www.mathworks.com/matlabcentral/fileexchange/4894-matrix2latex
58 |
59 | This project was moved from https://code.google.com/p/matrix2latex/
60 | after code.google.com was discontinued.
61 |
62 | Future goals
63 | ------------
64 | Feel free to contribute to any of the following improvements
65 | or open an [issue](https://github.com/TheChymera/matrix2latex/issues) if you want something done.
66 |
67 | * Clean up the code (object oriented?)
68 | * Make the matlab version identical to the python version
69 | * Add support for more advanced tables. Highlights and multirow.
70 | * Command line interface (say, read in a csv, take arguments on the command line)
71 | * Additional languages (R/perl/julia?)
72 |
73 | Author
74 | ------
75 | Øystein Bjørndal
76 |
--------------------------------------------------------------------------------
/archive/matrix2latexMatlab-1.0.0.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheChymera/matrix2latex/cd682ff87972f7182613a653495ca88fd75c33d6/archive/matrix2latexMatlab-1.0.0.tar.gz
--------------------------------------------------------------------------------
/archive/matrix2latexPython-1.0.0.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheChymera/matrix2latex/cd682ff87972f7182613a653495ca88fd75c33d6/archive/matrix2latexPython-1.0.0.tar.gz
--------------------------------------------------------------------------------
/archiver.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | '''Utility script for creating a versioned .tar.gz of the documenentation and
3 | the code for either matlab or python.
4 | '''
5 | import os
6 |
7 | def callSystem(command):
8 | print('>' + command)
9 | ret = os.system(command)
10 | if ret != 0:
11 | print('Command failed, exiting')
12 | exit(1)
13 |
14 | version = '1.0.0'
15 |
16 | # Ensure tests pass
17 | callSystem('cd test;nosetests-2.7 test.py')
18 | callSystem('cd test;python testVersionCompatibility.py')
19 | # Re-create doc if neccesary
20 | callSystem('cd doc;latexmk -pdf doc.tex')
21 |
22 | include_common = ' doc README.md '
23 |
24 | python_name = 'archive/matrix2latexPython'
25 | include_files = 'matrix2latex setup.py' + include_common
26 | callSystem('git archive --format=tar --prefix={name}{v}/ HEAD {include} | gzip > {name}-{v}.tar.gz'.format(name=python_name,
27 | v=version,
28 | include=include_files))
29 |
30 | matlab_name = 'archive/matrix2latexMatlab'
31 | include_files = 'src_matlab' + include_common
32 | callSystem('git archive --format=tar --prefix={name}{v}/ HEAD {include} | gzip > {name}-{v}.tar.gz'.format(name=matlab_name,
33 | v=version,
34 | include=include_files))
35 |
36 | callSystem('git add {p_name}-{v}.tar.gz {m_name}-{v}.tar.gz'.format(p_name=python_name,
37 | m_name=matlab_name,
38 | v=version))
39 |
--------------------------------------------------------------------------------
/doc/Makefile:
--------------------------------------------------------------------------------
1 |
2 | compilePDF:
3 | latexmk -pdf -pvc doc.tex
4 |
5 | compileDVI:
6 | $(MAKE) clean
7 | latexmk -pvc doc.tex
8 |
9 | compilePreview:
10 | latexmk -pdf doc.tex
11 | osascript -e "tell application \"Preview\" to activate"
12 |
13 | compile:
14 | $(MAKE) clean
15 | rm -f doc.pdf doc.dvi # make sure they are recreated
16 | latexmk -pdf doc.tex # recreate pdf
17 | latexmk doc.tex # recreate dvi
18 | $(MAKE) clean
19 |
20 | clean:
21 | latexmk -c doc.tex
22 | rm -r pythontex-files-doc/
23 | rm -f *.aux *.log *~ *.out *.bbl
24 |
--------------------------------------------------------------------------------
/doc/compatibleTable.tex:
--------------------------------------------------------------------------------
1 | \begin{table}[htp]
2 | \begin{center}
3 | \caption{Does 'python test.py' return 0?}
4 | \label{tab:compatibleTable}
5 | \begin{tabular}{rc}
6 | \toprule
7 | {} & {Compatible}\\
8 | \midrule
9 | {python2.6} & True\\
10 | {python2.7} & True\\
11 | {python3.7} & True\\
12 | \bottomrule
13 | \end{tabular}
14 | \end{center}
15 | \end{table}
--------------------------------------------------------------------------------
/doc/doc.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheChymera/matrix2latex/cd682ff87972f7182613a653495ca88fd75c33d6/doc/doc.pdf
--------------------------------------------------------------------------------
/doc/doc.tex:
--------------------------------------------------------------------------------
1 | % This file is part of matrix2latex.
2 |
3 | % matrix2latex is free software: you can redistribute it and/or modify
4 | % it under the terms of the GNU General Public License as published by
5 | % the Free Software Foundation, either version 3 of the License, or
6 | % (at your option) any later version.
7 |
8 | % matrix2latex is distributed in the hope that it will be useful,
9 | % but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | % GNU General Public License for more details.
12 |
13 | % You should have received a copy of the GNU General Public License
14 | % along with matrix2latex. If not, see .
15 |
16 | \input{tex/texPreamble}
17 | \title{Documentation for matrix2latex}
18 |
19 | \begin{document}
20 | \maketitle
21 | This is the documentation for the matrix2latex package, development can now be found here:
22 | \url{https://github.com/TheChymera/matrix2latex}
23 |
24 | This package provides easy translation of vector/matrices in python or matlab into latex table/matrix code.
25 | The implementation is written in both matlab and python, resulting in some discrepancies in features and usage.
26 | After the introduction we will discuss some
27 | matlab specific usage in chapter \ref{sec:matlab} before showing
28 | all options using the python syntax. Currently the python version is the best maintained.
29 | \chapter{Introduction/features}
30 | \label{sec:introduction}
31 | \input{tex/introduction}
32 |
33 | \chapter{Matlab specific}
34 | \label{sec:matlab}
35 | \input{tex/matlab}
36 |
37 | \chapter{General Usage}
38 | \label{sec:python}
39 | \input{tex/python}
40 |
41 | % %\bibliographystyle{IEEEtran}
42 | % %\bibliography{doc}
43 | \end{document}
44 |
--------------------------------------------------------------------------------
/doc/facT.tex:
--------------------------------------------------------------------------------
1 | \begin{table}[htp]
2 | \begin{center}
3 | \caption{Comparing execution time for
4 | the different factorial implementations}
5 | \label{tab:facT}
6 | \begin{tabular}{rccc}
7 | \toprule
8 | {$n$} & {Built-in [$s$]} & {Recursive [$s$]} & {Sequential [$s$]}\\
9 | \midrule
10 | {0} & $0.235$ & $0.396$ & $0.396$\\
11 | {1} & $0.260$ & $0.439$ & $1.213$\\
12 | {2} & $0.256$ & $0.880$ & $1.358$\\
13 | {3} & $0.311$ & $1.341$ & $1.447$\\
14 | {4} & $0.364$ & $1.810$ & $1.610$\\
15 | {5} & $0.386$ & $2.136$ & $1.717$\\
16 | {6} & $0.435$ & $2.527$ & $1.818$\\
17 | {7} & $0.475$ & $2.971$ & $1.930$\\
18 | {8} & $0.480$ & $3.337$ & $1.987$\\
19 | {9} & $0.522$ & $3.742$ & $2.178$\\
20 | \bottomrule
21 | \end{tabular}
22 | \end{center}
23 | \end{table}
--------------------------------------------------------------------------------
/doc/facV.tex:
--------------------------------------------------------------------------------
1 | \begin{table}[htp]
2 | \begin{center}
3 | \caption{Vertifying that the different factorial
4 | implementations gives the same results}
5 | \label{tab:facV}
6 | \begin{tabular}{rrrr}
7 | \toprule
8 | {$n$} & {Built-in} & {Recursive} & {Sequential}\\
9 | \midrule
10 | {0} & $1$ & $1$ & $1$\\
11 | {1} & $1$ & $1$ & $1$\\
12 | {2} & $2$ & $2$ & $2$\\
13 | {3} & $6$ & $6$ & $6$\\
14 | {4} & $24$ & $24$ & $24$\\
15 | {5} & $120$ & $120$ & $120$\\
16 | {6} & $720$ & $720$ & $720$\\
17 | {7} & $5040$ & $5040$ & $5040$\\
18 | {8} & $40320$ & $40320$ & $40320$\\
19 | {9} & $362880$ & $362880$ & $362880$\\
20 | \bottomrule
21 | \end{tabular}
22 | \end{center}
23 | \end{table}
--------------------------------------------------------------------------------
/doc/factorial.py:
--------------------------------------------------------------------------------
1 | # built in factorial
2 | from math import factorial as factorialMath
3 |
4 | # recursive
5 | def factorialRecursive(n):
6 | if n == 0:
7 | return 1
8 | elif n == 1:
9 | return n
10 | else:
11 | return n*factorialRecursive(n-1)
12 |
13 | # sequential
14 | def factorialSequential(n):
15 | if n == 0:
16 | return 1
17 | res = 1
18 | for k in xrange(2, n+1):
19 | res *= k
20 | return res
21 |
22 | if __name__ == '__main__':
23 | from matrix2latex import matrix2latex
24 |
25 | N = range(0, 10)
26 | table = list()
27 | for func in (factorialMath,
28 | factorialRecursive,
29 | factorialSequential):
30 | row = list()
31 | for n in N:
32 | res = func(n) # call func
33 | row.append(res)# append result to row
34 | table.append(row) # append row to table
35 |
36 | # row labels
37 | rl = ['$n$', 'Built-in', 'Recursive', 'Sequential']
38 | caption = '''Vertifying that the different factorial
39 | implementations gives the same results'''
40 | matrix2latex(table, 'facV', caption=caption,
41 | headerColumn=N, headerRow=rl,
42 | alignment='r', transpose=True)
43 |
44 | import timeit
45 | table = list()
46 | for func in ('factorialMath',
47 | 'factorialRecursive',
48 | 'factorialSequential'):
49 | row = list()
50 | for n in N:
51 | statement = 'factorial.{func}({n})'.format(func=func,
52 | n=n)
53 | setup = 'import factorial'
54 | # measure time
55 | res = timeit.repeat(statement, setup)
56 | row.append(min(res)) # append result
57 | table.append(row) # append row to table
58 |
59 | rl = ['$n$', 'Built-in [$s$]',
60 | 'Recursive [$s$]', 'Sequential [$s$]']
61 | caption = '''Comparing execution time for
62 | the different factorial implementations'''
63 | matrix2latex(table, 'facT', caption=caption,
64 | headerColumn=N, headerRow=rl,
65 | transpose=True, format='$%.3f$')
66 |
--------------------------------------------------------------------------------
/doc/features.tex:
--------------------------------------------------------------------------------
1 | \begin{table}[ht]
2 | \begin{center}
3 | \caption{What feature is currently available in which language?}
4 | \label{tab:features}
5 | \begin{tabular}{rcc}
6 | \toprule
7 | Feature & Python & Matlab\\
8 | \midrule
9 | environment & True & True\\
10 | headerRow & True & True\\
11 | multiline headerRow & True & False\\
12 | headerColumn & True & True\\
13 | multiline headerColumn & False & False\\
14 | transpose & True & True\\
15 | caption & True & True\\
16 | label & True & True\\
17 | format & True & True\\
18 | formatColumn & True & True\\
19 | alignment & True & True\\
20 | \bottomrule
21 | \end{tabular}
22 | \end{center}
23 | \end{table}
--------------------------------------------------------------------------------
/doc/niceTable.tex:
--------------------------------------------------------------------------------
1 | \begin{table}[htp]
2 | \begin{center}
3 | \caption{Nice table!}
4 | \label{tab:niceTable}
5 | \begin{tabular}{cc}
6 | \toprule
7 | {$x$} & {$x^2$}\\
8 | \midrule
9 | $1$ & $1$\\
10 | $2$ & $4$\\
11 | $3$ & $9$\\
12 | \bottomrule
13 | \end{tabular}
14 | \end{center}
15 | \end{table}
--------------------------------------------------------------------------------
/doc/table_ohm.tex:
--------------------------------------------------------------------------------
1 | \begin{table}[htp]
2 | \begin{center}
3 | \caption{Current through $50 \Omega$ resistor}
4 | \label{tab:table_ohm}
5 | \begin{tabular}{cc}
6 | \toprule
7 | {$V$} & {$I=V/R$}\\
8 | \midrule
9 | $1 V$ & $0.02 A$\\
10 | $2 V$ & $0.04 A$\\
11 | $3 V$ & $0.06 A$\\
12 | \bottomrule
13 | \end{tabular}
14 | \end{center}
15 | \end{table}
--------------------------------------------------------------------------------
/doc/table_ohm2.tex:
--------------------------------------------------------------------------------
1 | \begin{table}[htp]
2 | \begin{center}
3 | \caption{Current through $50 \Omega$ resistor}
4 | \label{tab:table_ohm2}
5 | \begin{tabular}{cc}
6 | \toprule
7 | {$V$ [V]} & {$I=V/R$ [A]}\\
8 | \midrule
9 | $1$ & $0.02$\\
10 | $2$ & $0.04$\\
11 | $3$ & $0.06$\\
12 | \bottomrule
13 | \end{tabular}
14 | \end{center}
15 | \end{table}
--------------------------------------------------------------------------------
/doc/test.tex:
--------------------------------------------------------------------------------
1 | \begin{table}[ht]
2 | \begin{center}
3 | \label{tab:test}
4 | \begin{tabular}{lcr}
5 | \toprule
6 | $1.00$ & $3.00$\\
7 | $2.00$ & $4.00$\\
8 | $4.00$ & $6.00$\\
9 | \bottomrule
10 | \end{tabular}
11 | \end{center}
12 | \end{table}
13 |
--------------------------------------------------------------------------------
/doc/tex/introduction.tex:
--------------------------------------------------------------------------------
1 | Takes a python or matlab matrix or nested list and converts to a LaTeX table or matrix.
2 | Author: ob@cakebox.net, inspired by the work of koehler@in.tum.de who has written
3 | a similar package only for matlab
4 | \url{http://www.mathworks.com/matlabcentral/fileexchange/4894-matrix2latex}
5 |
6 | This software is published under the GNU GPL, by the free software
7 | foundation. See:
8 | \url{http://www.gnu.org/licenses/licenses.html#GPL}
9 |
10 | \section{TODO}
11 | \begin{itemize}
12 | \item Improve test suite.
13 | \item Improve error handling.
14 | \item Update matlab code
15 | \end{itemize}
16 |
17 | \section{Compatibility}
18 | Table \ref{tab:features} reflect the current features and whether it is available
19 | in the matlab or python version.
20 | \input{features}
21 | These features are discussed in more detail in chapter \ref{sec:python}.
22 |
23 | \subsection{Python}
24 | The code is written without any dependencies and should be compatible with most python versions.
25 | Table \ref{tab:compatibleTable} reflects the python versions currently installed
26 | on my system\footnote{Mac OS X 10.10, python installed through macport.}
27 | and if the testsuit for matrix2latex passes. If you find a python version where
28 | it doesn't work let me know.
29 | \input{compatibleTable}
30 | \subsection{Matlab}
31 | For the moment it is only tested with matlab R2009b.
32 |
33 | \section{Installation}
34 | The following packages and definitions are recommended in the latex preamble
35 | \begin{pygments}{latex}
36 | % scientific notation, 1\e{9} will print as 1x10^9
37 | \providecommand{\e}[1]{\ensuremath{\times 10^{#1}}}
38 | \usepackage{amsmath} % needed for pmatrix
39 | \usepackage{booktabs} % Fancy tables
40 | \usepackage{caption} % Fixes spacing between caption and table
41 | ...
42 | \begin{document}
43 | ...
44 | \end{pygments}
45 | \subsection{Python}
46 | To use the code place the folder matrix2latex/ in our \verb!PYTHONPATH!,
47 | this can be done with the usual
48 | \begin{verbatim}
49 | sudo python setup.py install
50 | \end{verbatim}
51 | Alternativly, your current working directory is always on your \verb!PYTHONPATH!.
52 |
53 | % Hint: on unix systems do: \\
54 | % \verb!echo $PYTHONPATH! \\
55 | % to see a list of locations. Other users: ask google about \verb!PYTHONPATH!
56 | % for your operation system.
57 |
--------------------------------------------------------------------------------
/doc/tex/matlab.tex:
--------------------------------------------------------------------------------
1 | \section{Installation}
2 | Place the matlab files in our \verb!MATLABPATH!.
3 | Your current working directory is always on your \verb!MATLABPATH!.
4 |
5 | See the following article from mathworks
6 | \url{http://www.mathworks.se/help/techdoc/ref/path.html}.
7 |
8 | \section{Usage}
9 | As matlab does not support keywords, something similar to the environment must be used.
10 | Most of the examples in this document are given in python code only. The matlab version should be identical
11 | in behavior but has slightly different syntax.
12 |
13 | \begin{pygments}{matlab}
14 | matrix2latex(matrix, filename, varargin)
15 | \end{pygments}
16 |
17 | \begin{pygments}{matlab}
18 | % Filename must be suppiled but filename == '' will not write to file.
19 | % filename = 'foo.tex' and filename = 'foo' will write to 'foo.tex'
20 | m = [1, 1; 2, 4; 3, 9];
21 | t = matrix2latex(m, '');
22 | % keyword arguments are given like this
23 | % instead of transpose = True
24 | t = matrix2latex(m, '', 'transpose', true);
25 | % environment is a keyword argument
26 | t = matrix2latex(m, '', 'environment', {'align*', 'pmatrix'});
27 | % if you want to pass in strings you must use cell array
28 | m = {'a', 'b', '1'; '1', '2', '3'}
29 | t = matrix2latex(m, '', 'format', '%s');
30 | \end{pygments}
--------------------------------------------------------------------------------
/doc/tex/python.tex:
--------------------------------------------------------------------------------
1 | \section{Usage}
2 | The python version should be called like this
3 | \begin{pycode}
4 | from matrix2latex import *
5 | matrix=[]
6 | filename=None
7 | environment = list()
8 | keywords = dict()
9 | \end{pycode}
10 | \begin{pygments}{python}
11 | matrix2latex(matrix, filename, *environment, **keywords)
12 | \end{pygments}
13 |
14 |
15 | \subsection{matrix}
16 | \begin{itemize}
17 | \item[Python:] A numpy matrix, a pandas DataFrame/Series or a (nested) list.
18 | Note: these are all converted to a nested list internally for compatibility.
19 | \item[Matlab:] Matrix or cell array.
20 | \end{itemize}
21 | \subsection{Filename}
22 | File to place output, extension .tex is added automatically. File can be included in a LaTeX
23 | document by \verb!\input{filename}!. Output will always be returned in a string. If filename is None
24 | not a string or empty string it is ignored.
25 |
26 | \subsection{*environments}
27 | Use
28 | \pygment{python}{matrix2latex(m, None, "align*", "pmatrix", ...)} for matrix.
29 | This will give
30 | \begin{align*}
31 | \begin{pmatrix}
32 | 1 & 2 \\
33 | 3 & 4
34 | \end{pmatrix}
35 | \end{align*}
36 | Use
37 | \pygment{python}{matrix2latex(m, "test", "table", "center", "tabular" ...)} for table.
38 | Table is default so given no arguments: table, center and tabular will be used.
39 | The above command is then equivalent to \\
40 | \pygment{python}{matrix2latex(m, "test", ...)}
41 |
42 | The above commands looks a bit differently in matlab, since we must specify
43 | that we want to change the environment.
44 | \begin{pygments}{matlab}
45 | matrix2latex(m, None, 'environment', {'align*', 'pmatrix'}, ...)
46 | matrix2latex(m, 'test', 'environment', {'table', 'center', 'tabular'} ...)
47 | matrix2latex(m, 'test', ...)
48 | \end{pygments}
49 |
50 | \subsubsection{Example}
51 | \begin{pyblock}
52 | from matrix2latex import matrix2latex
53 | m = [[1, 1], [2, 4], [3, 9]] # python nested list
54 | t = matrix2latex(m)
55 | print(t)
56 | \end{pyblock}
57 | \begin{pygments}{tex}
58 | \begin{center}
59 | \begin{tabular}{cc}
60 | \toprule
61 | $1$ & $1$\\
62 | $2$ & $4$\\
63 | $3$ & $9$\\
64 | \bottomrule
65 | \end{tabular}
66 | \end{center}
67 | \end{table}
68 | \end{pygments}
69 | \py{t}
70 |
71 | \subsection{**keywords}
72 | \subsubsection{headerRow}
73 | A row at the top used to label the columns.
74 | Must be a list of strings.
75 | Using the same example from above we can add row labels
76 | \begin{pyblock}
77 | hr = ['$x$', '$x^2$']
78 | t = matrix2latex(m, headerRow=hr)
79 | \end{pyblock}
80 |
81 | Which in matlab looks like this (note that cell array must be used for
82 | declaring the header row)
83 | \begin{pygments}{matlab}
84 | hr = {'$x$', '$x^2$'};
85 | t = matrix2latex(m, 'headerRow', hr);
86 | \end{pygments}
87 | \py{t}
88 |
89 | The python version currently supports header rows spanning multiple columns (using \pygment{latex}{\multicolumn}), this
90 | is done by repeating the header info. It also supports multiple rows
91 | by using a nested list.
92 | \begin{pycode}[multi]
93 | from matrix2latex import matrix2latex
94 | \end{pycode}
95 | \begin{pyblock}[multi]
96 | hr = [['Item', 'Item', ''],
97 | ['Animal', 'Description', 'Price (\$)']]
98 | t = [['Gnat', 'per gram', 13.65],
99 | ['', 'each', 0.01],
100 | ['Gnu', 'stuffed', 92.50],
101 | ['Emu', 'stuffed', 33.33],
102 | ['Armadillo', 'frozen', 8.99]]
103 | t = matrix2latex(t, headerRow = hr, alignment='@{}llr@{}')
104 | \end{pyblock}
105 | \py[multi]{t}
106 | To avoid this behavior ensure each consecutive item is unique, for instance:
107 | \pygment{python}{['Item', 'Item ', '']}
108 | (note the space after the second item).
109 |
110 | \subsubsection{headerColumn}
111 | A column used to label the rows.
112 | Must be a list of strings
113 | \subsubsection{transpose}
114 | Flips the table around in case you messed up. Equivalent to
115 | matrix2latex(m.H, ...)
116 | if m is a numpy matrix.
117 | Note that \pygment{python}{headerColumn} is used in the example.
118 | \begin{pyblock}
119 | t = matrix2latex(m, headerColumn=hr, transpose=True)
120 | \end{pyblock}
121 | \py{t}
122 |
123 | \subsubsection{caption}
124 | Use to define a caption for your table.
125 | Inserts \pygment{python}{\caption} after \pygment{python}{\begin{center}},
126 | note that without the center environment the caption is currently ignored.
127 | Always use informative captions!
128 | \begin{pyblock}
129 | t = matrix2latex(m, headerRow=hr,
130 | caption='Nice table!')
131 | \end{pyblock}
132 | \py{t}
133 |
134 | \subsubsection{label}
135 | Used to insert \verb!\label{tab:...}! after \verb!\end{tabular}!
136 | Default is filename without extension.
137 |
138 | We can use \pygment{python}{label='niceTable'} but if we save it to file
139 | the default label is the filename, so:
140 | \begin{pyblock}
141 | matrix2latex(m, 'niceTable', headerRow=hr,
142 | caption='Nice table!')
143 | \end{pyblock}
144 | can be referenced by \verb!\ref{tab:niceTable}!. Table \ref{tab:niceTable}
145 | was included in latex by \verb!\input{niceTable}!.
146 | \input{niceTable}
147 |
148 | \subsubsection{format}
149 | Printf syntax format, e.g. \pygment{python}{$%.2f$}. Default is \pygment{python}{$%g$}.
150 | This format is then used for all the elements in the table.
151 | Using numpy creating tables for common mathematical expressions are easy:
152 | \begin{pyblock}
153 | import numpy as np
154 | m = np.zeros((3, 2))
155 | m[:, 0] = np.arange(1, 3+1)
156 | m[:, 1] = 1./m[:, 0]
157 |
158 | hr = ['$x$', '$1/x$']
159 | t = matrix2latex(m, headerRow=hr,
160 | format='%.2f')
161 | \end{pyblock}
162 | \py{t}
163 |
164 | \subsubsection{formatColumn}
165 | A list of printf-syntax formats, e.g. \pygment{python}{['$%.2f$', '$%g$']}
166 | Must be of same length as the number of columns.
167 | Format i is then used for column i.
168 | This is useful if some of your data should be printed with more significant figures
169 | than other parts, for instance in the example above $x$ are integers and using
170 | $d$ is more appropriate.
171 | \begin{pyblock}
172 | fc = ['$%d$', '$%.2f$']
173 | t = matrix2latex(m, headerRow=hr, formatColumn=fc)
174 | \end{pyblock}
175 | \py{t}
176 |
177 | You could use the format option to add units to you numbers, so for instance
178 | visualizing ohms law
179 | \begin{pyblock}
180 | m2 = np.zeros((3, 2))
181 | R = 50. # ohm
182 | m2[:, 0] = np.arange(1, 3+1)
183 | m2[:, 1] = m[:, 0]/R
184 | c = r'Current through $%g \Omega$ resistor' % R
185 | hr = ['$V$', '$I=V/R$']
186 | t = matrix2latex(m2, 'table_ohm', headerRow=hr,
187 | formatColumn=['$%d V$', '$%.2f A$'],
188 | caption=c)
189 | \end{pyblock}
190 | \input{table_ohm}
191 |
192 | This is however not the recommend way to give units, since they should
193 | be given in the header, see tabel \ref{tab:table_ohm2}.
194 | \begin{pyblock}
195 | hr = ['$V$ [V]', '$I=V/R$ [A]']
196 | t = matrix2latex(m2, 'table_ohm2', headerRow=hr,
197 | formatColumn=['$%d$', '$%.2f$'],
198 | caption=c)
199 | \end{pyblock}
200 | \input{table_ohm2}
201 |
202 | \subsubsection{alignment}
203 | Used as an option when tabular is given as enviroment.
204 | \verb!\begin{tabular}{alignment}!
205 | A latex alignment like c, l or r.
206 | Can be given either as one per column e.g. ``ccc''.
207 | Or if only a single character is given e.g. ``c'',
208 | it will produce the correct amount depending on the number of columns.
209 | Default is ``c'', if you use \pygment{python}{headerColumn} it will always use
210 | ``r'' as the alignment for that column.
211 | \begin{pyblock}
212 | t = matrix2latex(m, alignment='rl')
213 | \end{pyblock}
214 | \py{t}
215 |
216 | But what if I want vertical rules in my table? Well, this package is built
217 | on top of booktabs for publication ready tables and the booktabs documentation clearly
218 | states ``Never, ever use vertical rule''. But as long as you are not publishing your table,
219 | you could simply use
220 | \begin{pyblock}
221 | t = matrix2latex(m, alignment='|r||c|')
222 | \end{pyblock}
223 | \py{t}
224 |
225 | There is some error checking on the alignment but not much, it simply counts the number
226 | of c, l and r in the alignment. All other characters are ignored.
227 |
228 | \subsubsection{position}
229 | Used for the table environment to specify the optional parameter ``position specifier''
230 | Default is \pygment{python}{'[' + 'htp' + ']'}
231 |
232 | If you want to place your table manually, do not use the table environment.
233 |
234 | \subsubsection{General considerations}
235 | Note that many of these options only has an effect when typesetting a table,
236 | if the correct environment is not given the arguments are simply ignored.
237 | To give an example of a very useless function call
238 | \begin{pyblock}
239 | t1 = matrix2latex(m, None, "align*", "pmatrix",
240 | alignment='rc',
241 | caption='hello world',
242 | label='test')
243 | # produces the exact same thing as
244 | t2 = matrix2latex(m, None, "align*", "pmatrix")
245 | assert t1 == t2
246 | \end{pyblock}
247 | \py{t1}
248 | The scary thing is that \pygment{python}{headerColumn} actually works when creating a matrix,
249 | it just looks a bit wierd.
250 |
251 | The options presented by this program represents what I need when creating a table,
252 | if you need a more sophisticated table you must either change the python code
253 | (feel free to submit a patch), manually adjust the output afterwards
254 | or adjust the input (remember that the input can be a nested list of strings).
255 | \url{http://en.wikibooks.org/wiki/LaTeX/Tables} gives an excellent overview
256 | of some advanced table techniques.
257 |
258 | The booktabs.pdf documentation is an excellent guide to proper table creation,
259 | matrix2latex does not incorporate all the features of this package (yet).
260 |
261 | \subsection{Compatibility with other packages}
262 | Bellow support for the python package Pandas and the latex package siunitx will be outlined.
263 | \subsubsection{Pandas support (a python package)}
264 | Some preliminary support for Pandas\footnote{\url{http://pandas.pydata.org/}}
265 | is available, pandas series is shown in table \ref{tab:pd_series}, while
266 | a pandas DataFrame is displayed in table \ref{tab:pd_data_frame}.
267 | \begin{pycode}[pandas]
268 | from matrix2latex import matrix2latex
269 | \end{pycode}
270 | \begin{pyblock}[pandas]
271 | import pandas as pd
272 | s = pd.Series([2, 4, 2, 42, 5],
273 | index=['a', 'b', 'c', 'd', 'e'])
274 | t = matrix2latex(s, caption='Pandas Series',
275 | label='pd_series')
276 |
277 | m = pd.DataFrame([[1, 1], [2, 4], [3, 9]],
278 | index=['item1', 'item2', 'item3'],
279 | columns=['a', 'b'])
280 | t += matrix2latex(m, caption='Pandas Dataframe',
281 | label='pd_data_frame')
282 | \end{pyblock}
283 | \py[pandas]{t}
284 |
285 | Matrix2latex automatically detects and uses the \pygment{python}{index}
286 | and \pygment{python}{columns} attributes for
287 | \pygment{python}{headerColumn} and
288 | \pygment{python}{headerRow} respectively. You can override this behavior by passing
289 | \pygment{python}{headerColumn=None, headerRow=None}.
290 |
291 | \subsubsection{siunitx (a latex package)}
292 | \pygment{python}{siunitx} provides support for physical quantities with units
293 | and introduces a new column type called
294 | \pygment{latex}{S} which is supported by matrix2latex.
295 | Note that the dollar signs must be suppressed, so the
296 | \pygment{python}{format} is changed from the default
297 | \pygment{python}{'$%.2g$'}
298 | to
299 | \pygment{python}{'%.2g'}.
300 | \begin{pycode}[siunitx]
301 | from matrix2latex import matrix2latex
302 | \end{pycode}
303 | \begin{pyblock}[siunitx]
304 | import numpy as np
305 | R1 = 50 # ohm
306 | R2 = 377 # ohm
307 | V = np.array([1, 1.2, 100]) # V
308 | I1 = V/R1 # A
309 | I2 = V/R2
310 | caption = r"""Calculated currents through a
311 | $\SI{%g}{\ohm}$ and $\SI{%g}{\ohm}$
312 | resistor.""" % (R1, R2)
313 | headerRow = [['$V$', r'$V/\SI{%g}{\ohm}$' % R1, r'$V/\SI{%g}{\ohm}$' % R2],
314 | [r'\si{V}', r'\si{A}', r'\si{A}']]
315 | t = matrix2latex([V, I1, I2], caption=caption, transpose=True,
316 | alignment='S', format='%.2g', headerRow=headerRow)
317 | \end{pyblock}
318 | \py[siunitx]{t}
319 | %Both caption and label will do nothing if tabular environment is not used.
320 | % \begin{pycode}
321 | % import sys
322 | % sys.path.append('../')
323 | % \end{pycode}
324 | % \begin{pyblock}
325 | % from matrix2latex import matrix2latex
326 | % from numpy import matrix
327 | % m = matrix("1 2 4;3 4 6") # numpy matrix or
328 | % m = [[1, 2, 4], [3, 4, 6]] # python nested list
329 | % matrix2latex(m, "test", "table", "center", "tabular", format="$%.2f$", alignment="lcr")
330 | % # or since table, center and tabular is default:
331 | % t = matrix2latex(m, format="$%.2f$", alignment="lcr")
332 | % # produces:
333 | % \end{pyblock}
334 | % \py{t}
335 |
336 | \section{Usage examples}
337 | The usefulness of a programming interface to create \LaTeX{}
338 | tables becomes apparent when the data is dynamically created by python.
339 | This can be either because you want flexibility with respect to the tables size
340 | or because the table content is somehow created by python.
341 |
342 | One day you decide to compare different implementations of the
343 | factorial functions, you start by writing the following file
344 | as \verb!factorial.py!
345 | \begin{pyblock}[factorial]
346 | # built in factorial
347 | from math import factorial as factorialMath
348 |
349 | # recursive
350 | def factorialRecursive(n):
351 | if n == 0:
352 | return 1
353 | elif n == 1:
354 | return n
355 | else:
356 | return n*factorialRecursive(n-1)
357 |
358 | # sequential
359 | def factorialSequential(n):
360 | if n == 0:
361 | return 1
362 | res = 1
363 | for k in xrange(2, n+1):
364 | res *= k
365 | return res
366 | \end{pyblock}
367 |
368 | The first thing to do is to verify that the three implementations actually give
369 | the same results, for this we simply loop over the different functions and try for
370 | different values of $n$. The result is shown in table \ref{tab:facV}.
371 | \begin{pyblock}[factorial]
372 | from matrix2latex import matrix2latex
373 | N = range(0, 10)
374 | table = list()
375 | for func in (factorialMath,
376 | factorialRecursive,
377 | factorialSequential):
378 | row = list()
379 | for n in N:
380 | res = func(n) # call func
381 | row.append(res)# append result to row
382 | table.append(row) # append row to table
383 |
384 | # row labels
385 | rl = ['$n$', 'Built-in', 'Recursive', 'Sequential']
386 | caption = '''Vertifying that the different factorial
387 | implementations gives the same results'''
388 | matrix2latex(table, 'facV', caption=caption,
389 | headerColumn=N, headerRow=rl,
390 | alignment='r', transpose=True)
391 | \end{pyblock}
392 | \input{facV}
393 |
394 | What we really wanted to do was to
395 | compare the speed of the different implementations. To do this
396 | we use the python package timeit, shown bellow. The speed comparision is given in table \ref{tab:facT}.
397 | \begin{pyblock}[factorial]
398 | import timeit
399 | table = list()
400 | for func in ('factorialMath',
401 | 'factorialRecursive',
402 | 'factorialSequential'):
403 | row = list()
404 | for n in N:
405 | statement = 'factorial.{func}({n})'.format(func=func,
406 | n=n)
407 | setup = 'import factorial'
408 | # measure time
409 | res = timeit.repeat(statement, setup)
410 | row.append(min(res)) # append result
411 | table.append(row) # append row to table
412 |
413 | rl = ['$n$', 'Built-in [$s$]',
414 | 'Recursive [$s$]', 'Sequential [$s$]']
415 | caption = '''Comparing execution time for
416 | the different factorial implementations'''
417 | matrix2latex(table, 'facT', caption=caption,
418 | headerColumn=N, headerRow=rl,
419 | transpose=True, format='$%.3f$')
420 | \end{pyblock}
421 | \input{facT}
422 |
423 | As an additional example, see \verb!test_compatibility.py! to see how table \ref{tab:compatibleTable}
424 | was created.
425 |
--------------------------------------------------------------------------------
/doc/tex/texPreamble.tex:
--------------------------------------------------------------------------------
1 | % This file is part of matrix2latex.
2 |
3 | % matrix2latex is free software: you can redistribute it and/or modify
4 | % it under the terms of the GNU General Public License as published by
5 | % the Free Software Foundation, either version 3 of the License, or
6 | % (at your option) any later version.
7 |
8 | % matrix2latex is distributed in the hope that it will be useful,
9 | % but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | % GNU General Public License for more details.
12 |
13 | % You should have received a copy of the GNU General Public License
14 | % along with matrix2latex. If not, see .
15 |
16 | \documentclass[12pt, a4paper, reqno]{report}
17 | %\documentclass[a4paper, english]{IEEEtran}
18 | \usepackage[english]{babel}
19 | \usepackage[utf8]{inputenc}
20 |
21 | %\usepackage[cmex10]{amsmath} % Recommended by IEEEtran
22 | %\interdisplaylinepenalty=2500 % Recommended by IEEEtran, long math
23 | \usepackage{amsmath}
24 | \usepackage{amsfonts, amssymb, amsthm}
25 | \allowdisplaybreaks[1] % displaybreak in math is allowed but avoided
26 |
27 | \usepackage{graphicx} % include figures
28 | \usepackage{hyperref} % links
29 | \usepackage{siunitx}
30 | \usepackage{booktabs} % Fancy tables
31 | \usepackage{caption} % Fixes spacing between caption and table
32 |
33 | \usepackage{pythontex}
34 | \setpythontexworkingdir{.}
35 |
36 | \usepackage{listings} % include code
37 | \lstset{language = python,
38 | showspaces = false,
39 | showtabs = false,
40 | showstringspaces = false,
41 | mathescape = true}
42 |
43 |
44 | % Useful macros
45 | \providecommand{\fixme}[1]{\textbf{{\textsc{FIXME:} #1 }}}
46 |
47 | % Math
48 | \providecommand{\e}[1]{\ensuremath{\times 10^{#1}}} % scientific notation
49 |
50 | \author{Øystein Bjørndal}
51 |
--------------------------------------------------------------------------------
/doc/tmp.tex:
--------------------------------------------------------------------------------
1 | \begin{table}[ht]
2 | \begin{center}
3 | \label{tab:tmp}
4 | \begin{tabular}{ccc}
5 | \toprule
6 | $1$ & $2$ & $3$\\
7 | $4$ & $5$ & $6$\\
8 | \bottomrule
9 | \end{tabular}
10 | \end{center}
11 | \end{table}
--------------------------------------------------------------------------------
/doc_sphinx/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = _build
9 |
10 | # User-friendly check for sphinx-build
11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
12 | $(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/)
13 | endif
14 |
15 | # Internal variables.
16 | PAPEROPT_a4 = -D latex_paper_size=a4
17 | PAPEROPT_letter = -D latex_paper_size=letter
18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
19 | # the i18n builder cannot share the environment and doctrees with the others
20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
21 |
22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
23 |
24 | help:
25 | @echo "Please use \`make ' where is one of"
26 | @echo " html to make standalone HTML files"
27 | @echo " dirhtml to make HTML files named index.html in directories"
28 | @echo " singlehtml to make a single large HTML file"
29 | @echo " pickle to make pickle files"
30 | @echo " json to make JSON files"
31 | @echo " htmlhelp to make HTML files and a HTML help project"
32 | @echo " qthelp to make HTML files and a qthelp project"
33 | @echo " devhelp to make HTML files and a Devhelp project"
34 | @echo " epub to make an epub"
35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
36 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
38 | @echo " text to make text files"
39 | @echo " man to make manual pages"
40 | @echo " texinfo to make Texinfo files"
41 | @echo " info to make Texinfo files and run them through makeinfo"
42 | @echo " gettext to make PO message catalogs"
43 | @echo " changes to make an overview of all changed/added/deprecated items"
44 | @echo " xml to make Docutils-native XML files"
45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes"
46 | @echo " linkcheck to check all external links for integrity"
47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
48 |
49 | clean:
50 | rm -rf $(BUILDDIR)/*
51 |
52 | html:
53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
54 | @echo
55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
56 |
57 | dirhtml:
58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
59 | @echo
60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
61 |
62 | singlehtml:
63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
64 | @echo
65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
66 |
67 | pickle:
68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
69 | @echo
70 | @echo "Build finished; now you can process the pickle files."
71 |
72 | json:
73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
74 | @echo
75 | @echo "Build finished; now you can process the JSON files."
76 |
77 | htmlhelp:
78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
79 | @echo
80 | @echo "Build finished; now you can run HTML Help Workshop with the" \
81 | ".hhp project file in $(BUILDDIR)/htmlhelp."
82 |
83 | qthelp:
84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
85 | @echo
86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/matrix2latex.qhcp"
89 | @echo "To view the help file:"
90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/matrix2latex.qhc"
91 |
92 | devhelp:
93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
94 | @echo
95 | @echo "Build finished."
96 | @echo "To view the help file:"
97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/matrix2latex"
98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/matrix2latex"
99 | @echo "# devhelp"
100 |
101 | epub:
102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
103 | @echo
104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
105 |
106 | latex:
107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
108 | @echo
109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
111 | "(use \`make latexpdf' here to do that automatically)."
112 |
113 | latexpdf:
114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
115 | @echo "Running LaTeX files through pdflatex..."
116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
118 |
119 | latexpdfja:
120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
121 | @echo "Running LaTeX files through platex and dvipdfmx..."
122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
124 |
125 | text:
126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
127 | @echo
128 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
129 |
130 | man:
131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
132 | @echo
133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
134 |
135 | texinfo:
136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
137 | @echo
138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
139 | @echo "Run \`make' in that directory to run these through makeinfo" \
140 | "(use \`make info' here to do that automatically)."
141 |
142 | info:
143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
144 | @echo "Running Texinfo files through makeinfo..."
145 | make -C $(BUILDDIR)/texinfo info
146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
147 |
148 | gettext:
149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
150 | @echo
151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
152 |
153 | changes:
154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
155 | @echo
156 | @echo "The overview file is in $(BUILDDIR)/changes."
157 |
158 | linkcheck:
159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
160 | @echo
161 | @echo "Link check complete; look for any errors in the above output " \
162 | "or in $(BUILDDIR)/linkcheck/output.txt."
163 |
164 | doctest:
165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
166 | @echo "Testing of doctests in the sources finished, look at the " \
167 | "results in $(BUILDDIR)/doctest/output.txt."
168 |
169 | xml:
170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
171 | @echo
172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
173 |
174 | pseudoxml:
175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
176 | @echo
177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
178 |
--------------------------------------------------------------------------------
/doc_sphinx/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # matrix2latex documentation build configuration file, created by
4 | # sphinx-quickstart on Sun Feb 9 14:50:03 2014.
5 | #
6 | # This file is execfile()d with the current directory set to its
7 | # containing dir.
8 | #
9 | # Note that not all possible configuration values are present in this
10 | # autogenerated file.
11 | #
12 | # All configuration values have a default; values that are commented out
13 | # serve to show the default.
14 |
15 | import sys
16 | import os
17 |
18 | # If extensions (or modules to document with autodoc) are in another directory,
19 | # add these directories to sys.path here. If the directory is relative to the
20 | # documentation root, use os.path.abspath to make it absolute, like shown here.
21 | #sys.path.insert(0, os.path.abspath('.'))
22 | sys.path.insert(0, os.path.abspath('../matrix2latex'))
23 | # -- General configuration ------------------------------------------------
24 |
25 | # If your documentation needs a minimal Sphinx version, state it here.
26 | #needs_sphinx = '1.0'
27 |
28 | # Add any Sphinx extension module names here, as strings. They can be
29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
30 | # ones.
31 | extensions = [
32 | 'sphinx.ext.autodoc',
33 | 'sphinx.ext.doctest',
34 | 'sphinx.ext.coverage',
35 | 'sphinx.ext.pngmath',
36 | # 'sphinx.ext.mathjax',
37 | 'sphinx.ext.ifconfig',
38 | ]
39 |
40 | # Add any paths that contain templates here, relative to this directory.
41 | templates_path = ['_templates']
42 |
43 | # The suffix of source filenames.
44 | source_suffix = '.txt'
45 |
46 | # The encoding of source files.
47 | #source_encoding = 'utf-8-sig'
48 |
49 | # The master toctree document.
50 | master_doc = 'index'
51 |
52 | # General information about the project.
53 | project = u'matrix2latex'
54 | copyright = u'2014, obtitus@gmail.com'
55 |
56 | # The version info for the project you're documenting, acts as replacement for
57 | # |version| and |release|, also used in various other places throughout the
58 | # built documents.
59 | #
60 | # The short X.Y version.
61 | version = '1.0'
62 | # The full version, including alpha/beta/rc tags.
63 | release = '1.0.0'
64 |
65 | # The language for content autogenerated by Sphinx. Refer to documentation
66 | # for a list of supported languages.
67 | #language = None
68 |
69 | # There are two options for replacing |today|: either, you set today to some
70 | # non-false value, then it is used:
71 | #today = ''
72 | # Else, today_fmt is used as the format for a strftime call.
73 | #today_fmt = '%B %d, %Y'
74 |
75 | # List of patterns, relative to source directory, that match files and
76 | # directories to ignore when looking for source files.
77 | exclude_patterns = ['_build']
78 |
79 | # The reST default role (used for this markup: `text`) to use for all
80 | # documents.
81 | #default_role = None
82 |
83 | # If true, '()' will be appended to :func: etc. cross-reference text.
84 | #add_function_parentheses = True
85 |
86 | # If true, the current module name will be prepended to all description
87 | # unit titles (such as .. function::).
88 | #add_module_names = True
89 |
90 | # If true, sectionauthor and moduleauthor directives will be shown in the
91 | # output. They are ignored by default.
92 | #show_authors = False
93 |
94 | # The name of the Pygments (syntax highlighting) style to use.
95 | pygments_style = 'sphinx'
96 |
97 | # A list of ignored prefixes for module index sorting.
98 | #modindex_common_prefix = []
99 |
100 | # If true, keep warnings as "system message" paragraphs in the built documents.
101 | #keep_warnings = False
102 |
103 |
104 | # -- Options for HTML output ----------------------------------------------
105 |
106 | # The theme to use for HTML and HTML Help pages. See the documentation for
107 | # a list of builtin themes.
108 | html_theme = 'default'
109 |
110 | # Theme options are theme-specific and customize the look and feel of a theme
111 | # further. For a list of options available for each theme, see the
112 | # documentation.
113 | #html_theme_options = {}
114 |
115 | # Add any paths that contain custom themes here, relative to this directory.
116 | #html_theme_path = []
117 |
118 | # The name for this set of Sphinx documents. If None, it defaults to
119 | # " v documentation".
120 | #html_title = None
121 |
122 | # A shorter title for the navigation bar. Default is the same as html_title.
123 | #html_short_title = None
124 |
125 | # The name of an image file (relative to this directory) to place at the top
126 | # of the sidebar.
127 | #html_logo = None
128 |
129 | # The name of an image file (within the static path) to use as favicon of the
130 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
131 | # pixels large.
132 | #html_favicon = None
133 |
134 | # Add any paths that contain custom static files (such as style sheets) here,
135 | # relative to this directory. They are copied after the builtin static files,
136 | # so a file named "default.css" will overwrite the builtin "default.css".
137 | html_static_path = ['_static']
138 |
139 | # Add any extra paths that contain custom files (such as robots.txt or
140 | # .htaccess) here, relative to this directory. These files are copied
141 | # directly to the root of the documentation.
142 | #html_extra_path = []
143 |
144 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
145 | # using the given strftime format.
146 | #html_last_updated_fmt = '%b %d, %Y'
147 |
148 | # If true, SmartyPants will be used to convert quotes and dashes to
149 | # typographically correct entities.
150 | #html_use_smartypants = True
151 |
152 | # Custom sidebar templates, maps document names to template names.
153 | #html_sidebars = {}
154 |
155 | # Additional templates that should be rendered to pages, maps page names to
156 | # template names.
157 | #html_additional_pages = {}
158 |
159 | # If false, no module index is generated.
160 | #html_domain_indices = True
161 |
162 | # If false, no index is generated.
163 | #html_use_index = True
164 |
165 | # If true, the index is split into individual pages for each letter.
166 | #html_split_index = False
167 |
168 | # If true, links to the reST sources are added to the pages.
169 | #html_show_sourcelink = True
170 |
171 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
172 | #html_show_sphinx = True
173 |
174 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
175 | #html_show_copyright = True
176 |
177 | # If true, an OpenSearch description file will be output, and all pages will
178 | # contain a tag referring to it. The value of this option must be the
179 | # base URL from which the finished HTML is served.
180 | #html_use_opensearch = ''
181 |
182 | # This is the file name suffix for HTML files (e.g. ".xhtml").
183 | #html_file_suffix = None
184 |
185 | # Output file base name for HTML help builder.
186 | htmlhelp_basename = 'matrix2latexdoc'
187 |
188 |
189 | # -- Options for LaTeX output ---------------------------------------------
190 |
191 | latex_elements = {
192 | # The paper size ('letterpaper' or 'a4paper').
193 | #'papersize': 'letterpaper',
194 |
195 | # The font size ('10pt', '11pt' or '12pt').
196 | #'pointsize': '10pt',
197 |
198 | # Additional stuff for the LaTeX preamble.
199 | #'preamble': '',
200 | }
201 |
202 | # Grouping the document tree into LaTeX files. List of tuples
203 | # (source start file, target name, title,
204 | # author, documentclass [howto, manual, or own class]).
205 | latex_documents = [
206 | ('index', 'matrix2latex.tex', u'matrix2latex Documentation',
207 | u'obtitus@gmail.com', 'manual'),
208 | ]
209 |
210 | # The name of an image file (relative to this directory) to place at the top of
211 | # the title page.
212 | #latex_logo = None
213 |
214 | # For "manual" documents, if this is true, then toplevel headings are parts,
215 | # not chapters.
216 | #latex_use_parts = False
217 |
218 | # If true, show page references after internal links.
219 | #latex_show_pagerefs = False
220 |
221 | # If true, show URL addresses after external links.
222 | #latex_show_urls = False
223 |
224 | # Documents to append as an appendix to all manuals.
225 | #latex_appendices = []
226 |
227 | # If false, no module index is generated.
228 | #latex_domain_indices = True
229 |
230 |
231 | # -- Options for manual page output ---------------------------------------
232 |
233 | # One entry per manual page. List of tuples
234 | # (source start file, name, description, authors, manual section).
235 | man_pages = [
236 | ('index', 'matrix2latex', u'matrix2latex Documentation',
237 | [u'obtitus@gmail.com'], 1)
238 | ]
239 |
240 | # If true, show URL addresses after external links.
241 | #man_show_urls = False
242 |
243 |
244 | # -- Options for Texinfo output -------------------------------------------
245 |
246 | # Grouping the document tree into Texinfo files. List of tuples
247 | # (source start file, target name, title, author,
248 | # dir menu entry, description, category)
249 | texinfo_documents = [
250 | ('index', 'matrix2latex', u'matrix2latex Documentation',
251 | u'obtitus@gmail.com', 'matrix2latex', 'One line description of project.',
252 | 'Miscellaneous'),
253 | ]
254 |
255 | # Documents to append as an appendix to all manuals.
256 | #texinfo_appendices = []
257 |
258 | # If false, no module index is generated.
259 | #texinfo_domain_indices = True
260 |
261 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
262 | #texinfo_show_urls = 'footnote'
263 |
264 | # If true, do not generate a @detailmenu in the "Top" node's menu.
265 | #texinfo_no_detailmenu = False
266 |
--------------------------------------------------------------------------------
/doc_sphinx/index.txt:
--------------------------------------------------------------------------------
1 | .. matrix2latex documentation master file, created by
2 | sphinx-quickstart on Sun Feb 9 14:50:03 2014.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to matrix2latex's documentation!
7 | ========================================
8 |
9 | Python function syntax
10 | ======================
11 |
12 | .. toctree::
13 | :maxdepth: 2
14 |
15 |
16 | .. autofunction:: matrix2latex.matrix2latex
17 | .. autofunction:: render.matrix2image
18 |
19 | Indices and tables
20 | ==================
21 |
22 | * :ref:`genindex`
23 | * :ref:`modindex`
24 | * :ref:`search`
25 |
26 |
--------------------------------------------------------------------------------
/doc_sphinx/sphinxext/apigen.py:
--------------------------------------------------------------------------------
1 | """Attempt to generate templates for module reference with Sphinx
2 |
3 | XXX - we exclude extension modules
4 |
5 | To include extension modules, first identify them as valid in the
6 | ``_uri2path`` method, then handle them in the ``_parse_module`` script.
7 |
8 | We get functions and classes by parsing the text of .py files.
9 | Alternatively we could import the modules for discovery, and we'd have
10 | to do that for extension modules. This would involve changing the
11 | ``_parse_module`` method to work via import and introspection, and
12 | might involve changing ``discover_modules`` (which determines which
13 | files are modules, and therefore which module URIs will be passed to
14 | ``_parse_module``).
15 |
16 | NOTE: this is a modified version of a script originally shipped with the
17 | PyMVPA project, which we've adapted for NIPY use. PyMVPA is an MIT-licensed
18 | project."""
19 |
20 | # Stdlib imports
21 | import os
22 | import re
23 |
24 | # Functions and classes
25 | class ApiDocWriter(object):
26 | ''' Class for automatic detection and parsing of API docs
27 | to Sphinx-parsable reST format'''
28 |
29 | # only separating first two levels
30 | rst_section_levels = ['*', '=', '-', '~', '^']
31 |
32 | def __init__(self,
33 | package_name,
34 | rst_extension='.rst',
35 | package_skip_patterns=None,
36 | module_skip_patterns=None,
37 | ):
38 | ''' Initialize package for parsing
39 |
40 | Parameters
41 | ----------
42 | package_name : string
43 | Name of the top-level package. *package_name* must be the
44 | name of an importable package
45 | rst_extension : string, optional
46 | Extension for reST files, default '.rst'
47 | package_skip_patterns : None or sequence of {strings, regexps}
48 | Sequence of strings giving URIs of packages to be excluded
49 | Operates on the package path, starting at (including) the
50 | first dot in the package path, after *package_name* - so,
51 | if *package_name* is ``sphinx``, then ``sphinx.util`` will
52 | result in ``.util`` being passed for earching by these
53 | regexps. If is None, gives default. Default is:
54 | ['\.tests$']
55 | module_skip_patterns : None or sequence
56 | Sequence of strings giving URIs of modules to be excluded
57 | Operates on the module name including preceding URI path,
58 | back to the first dot after *package_name*. For example
59 | ``sphinx.util.console`` results in the string to search of
60 | ``.util.console``
61 | If is None, gives default. Default is:
62 | ['\.setup$', '\._']
63 | '''
64 | if package_skip_patterns is None:
65 | package_skip_patterns = ['\\.tests$']
66 | if module_skip_patterns is None:
67 | module_skip_patterns = ['\\.setup$', '\\._']
68 | self.package_name = package_name
69 | self.rst_extension = rst_extension
70 | self.package_skip_patterns = package_skip_patterns
71 | self.module_skip_patterns = module_skip_patterns
72 |
73 | def get_package_name(self):
74 | return self._package_name
75 |
76 | def set_package_name(self, package_name):
77 | ''' Set package_name
78 |
79 | >>> docwriter = ApiDocWriter('sphinx')
80 | >>> import sphinx
81 | >>> docwriter.root_path == sphinx.__path__[0]
82 | True
83 | >>> docwriter.package_name = 'docutils'
84 | >>> import docutils
85 | >>> docwriter.root_path == docutils.__path__[0]
86 | True
87 | '''
88 | # It's also possible to imagine caching the module parsing here
89 | self._package_name = package_name
90 | self.root_module = __import__(package_name)
91 | self.root_path = self.root_module.__path__[0]
92 | self.written_modules = None
93 |
94 | package_name = property(get_package_name, set_package_name, None,
95 | 'get/set package_name')
96 |
97 | def _get_object_name(self, line):
98 | ''' Get second token in line
99 | >>> docwriter = ApiDocWriter('sphinx')
100 | >>> docwriter._get_object_name(" def func(): ")
101 | 'func'
102 | >>> docwriter._get_object_name(" class Klass(object): ")
103 | 'Klass'
104 | >>> docwriter._get_object_name(" class Klass: ")
105 | 'Klass'
106 | '''
107 | name = line.split()[1].split('(')[0].strip()
108 | # in case we have classes which are not derived from object
109 | # ie. old style classes
110 | return name.rstrip(':')
111 |
112 | def _uri2path(self, uri):
113 | ''' Convert uri to absolute filepath
114 |
115 | Parameters
116 | ----------
117 | uri : string
118 | URI of python module to return path for
119 |
120 | Returns
121 | -------
122 | path : None or string
123 | Returns None if there is no valid path for this URI
124 | Otherwise returns absolute file system path for URI
125 |
126 | Examples
127 | --------
128 | >>> docwriter = ApiDocWriter('sphinx')
129 | >>> import sphinx
130 | >>> modpath = sphinx.__path__[0]
131 | >>> res = docwriter._uri2path('sphinx.builder')
132 | >>> res == os.path.join(modpath, 'builder.py')
133 | True
134 | >>> res = docwriter._uri2path('sphinx')
135 | >>> res == os.path.join(modpath, '__init__.py')
136 | True
137 | >>> docwriter._uri2path('sphinx.does_not_exist')
138 |
139 | '''
140 | if uri == self.package_name:
141 | return os.path.join(self.root_path, '__init__.py')
142 | path = uri.replace('.', os.path.sep)
143 | path = path.replace(self.package_name + os.path.sep, '')
144 | path = os.path.join(self.root_path, path)
145 | # XXX maybe check for extensions as well?
146 | if os.path.exists(path + '.py'): # file
147 | path += '.py'
148 | elif os.path.exists(os.path.join(path, '__init__.py')):
149 | path = os.path.join(path, '__init__.py')
150 | else:
151 | return None
152 | return path
153 |
154 | def _path2uri(self, dirpath):
155 | ''' Convert directory path to uri '''
156 | relpath = dirpath.replace(self.root_path, self.package_name)
157 | if relpath.startswith(os.path.sep):
158 | relpath = relpath[1:]
159 | return relpath.replace(os.path.sep, '.')
160 |
161 | def _parse_module(self, uri):
162 | ''' Parse module defined in *uri* '''
163 | filename = self._uri2path(uri)
164 | if filename is None:
165 | # nothing that we could handle here.
166 | return ([],[])
167 | f = open(filename, 'rt')
168 | functions, classes = self._parse_lines(f)
169 | f.close()
170 | return functions, classes
171 |
172 | def _parse_lines(self, linesource):
173 | ''' Parse lines of text for functions and classes '''
174 | functions = []
175 | classes = []
176 | for line in linesource:
177 | if line.startswith('def ') and line.count('('):
178 | # exclude private stuff
179 | name = self._get_object_name(line)
180 | if not name.startswith('_'):
181 | functions.append(name)
182 | elif line.startswith('class '):
183 | # exclude private stuff
184 | name = self._get_object_name(line)
185 | if not name.startswith('_'):
186 | classes.append(name)
187 | else:
188 | pass
189 | functions.sort()
190 | classes.sort()
191 | return functions, classes
192 |
193 | def generate_api_doc(self, uri):
194 | '''Make autodoc documentation template string for a module
195 |
196 | Parameters
197 | ----------
198 | uri : string
199 | python location of module - e.g 'sphinx.builder'
200 |
201 | Returns
202 | -------
203 | S : string
204 | Contents of API doc
205 | '''
206 | # get the names of all classes and functions
207 | functions, classes = self._parse_module(uri)
208 | if not len(functions) and not len(classes):
209 | print('WARNING: Empty -',uri) # dbg
210 | return ''
211 |
212 | # Make a shorter version of the uri that omits the package name for
213 | # titles
214 | uri_short = re.sub(r'^%s\.' % self.package_name,'',uri)
215 |
216 | ad = '.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n'
217 |
218 | chap_title = uri_short
219 | ad += (chap_title+'\n'+ self.rst_section_levels[1] * len(chap_title)
220 | + '\n\n')
221 |
222 | # Set the chapter title to read 'module' for all modules except for the
223 | # main packages
224 | if '.' in uri:
225 | title = 'Module: :mod:`' + uri_short + '`'
226 | else:
227 | title = ':mod:`' + uri_short + '`'
228 | ad += title + '\n' + self.rst_section_levels[2] * len(title)
229 |
230 | if len(classes):
231 | ad += '\nInheritance diagram for ``%s``:\n\n' % uri
232 | ad += '.. inheritance-diagram:: %s \n' % uri
233 | ad += ' :parts: 3\n'
234 |
235 | ad += '\n.. automodule:: ' + uri + '\n'
236 | ad += '\n.. currentmodule:: ' + uri + '\n'
237 | multi_class = len(classes) > 1
238 | multi_fx = len(functions) > 1
239 | if multi_class:
240 | ad += '\n' + 'Classes' + '\n' + \
241 | self.rst_section_levels[2] * 7 + '\n'
242 | elif len(classes) and multi_fx:
243 | ad += '\n' + 'Class' + '\n' + \
244 | self.rst_section_levels[2] * 5 + '\n'
245 | for c in classes:
246 | ad += '\n:class:`' + c + '`\n' \
247 | + self.rst_section_levels[multi_class + 2 ] * \
248 | (len(c)+9) + '\n\n'
249 | ad += '\n.. autoclass:: ' + c + '\n'
250 | # must NOT exclude from index to keep cross-refs working
251 | ad += ' :members:\n' \
252 | ' :undoc-members:\n' \
253 | ' :show-inheritance:\n' \
254 | ' :inherited-members:\n' \
255 | '\n' \
256 | ' .. automethod:: __init__\n'
257 | if multi_fx:
258 | ad += '\n' + 'Functions' + '\n' + \
259 | self.rst_section_levels[2] * 9 + '\n\n'
260 | elif len(functions) and multi_class:
261 | ad += '\n' + 'Function' + '\n' + \
262 | self.rst_section_levels[2] * 8 + '\n\n'
263 | for f in functions:
264 | # must NOT exclude from index to keep cross-refs working
265 | ad += '\n.. autofunction:: ' + uri + '.' + f + '\n\n'
266 | return ad
267 |
268 | def _survives_exclude(self, matchstr, match_type):
269 | ''' Returns True if *matchstr* does not match patterns
270 |
271 | ``self.package_name`` removed from front of string if present
272 |
273 | Examples
274 | --------
275 | >>> dw = ApiDocWriter('sphinx')
276 | >>> dw._survives_exclude('sphinx.okpkg', 'package')
277 | True
278 | >>> dw.package_skip_patterns.append('^\\.badpkg$')
279 | >>> dw._survives_exclude('sphinx.badpkg', 'package')
280 | False
281 | >>> dw._survives_exclude('sphinx.badpkg', 'module')
282 | True
283 | >>> dw._survives_exclude('sphinx.badmod', 'module')
284 | True
285 | >>> dw.module_skip_patterns.append('^\\.badmod$')
286 | >>> dw._survives_exclude('sphinx.badmod', 'module')
287 | False
288 | '''
289 | if match_type == 'module':
290 | patterns = self.module_skip_patterns
291 | elif match_type == 'package':
292 | patterns = self.package_skip_patterns
293 | else:
294 | raise ValueError('Cannot interpret match type "%s"'
295 | % match_type)
296 | # Match to URI without package name
297 | L = len(self.package_name)
298 | if matchstr[:L] == self.package_name:
299 | matchstr = matchstr[L:]
300 | for pat in patterns:
301 | try:
302 | pat.search
303 | except AttributeError:
304 | pat = re.compile(pat)
305 | if pat.search(matchstr):
306 | return False
307 | return True
308 |
309 | def discover_modules(self):
310 | ''' Return module sequence discovered from ``self.package_name``
311 |
312 |
313 | Parameters
314 | ----------
315 | None
316 |
317 | Returns
318 | -------
319 | mods : sequence
320 | Sequence of module names within ``self.package_name``
321 |
322 | Examples
323 | --------
324 | >>> dw = ApiDocWriter('sphinx')
325 | >>> mods = dw.discover_modules()
326 | >>> 'sphinx.util' in mods
327 | True
328 | >>> dw.package_skip_patterns.append('\.util$')
329 | >>> 'sphinx.util' in dw.discover_modules()
330 | False
331 | >>>
332 | '''
333 | modules = [self.package_name]
334 | # raw directory parsing
335 | for dirpath, dirnames, filenames in os.walk(self.root_path):
336 | # Check directory names for packages
337 | root_uri = self._path2uri(os.path.join(self.root_path,
338 | dirpath))
339 | for dirname in dirnames[:]: # copy list - we modify inplace
340 | package_uri = '.'.join((root_uri, dirname))
341 | if (self._uri2path(package_uri) and
342 | self._survives_exclude(package_uri, 'package')):
343 | modules.append(package_uri)
344 | else:
345 | dirnames.remove(dirname)
346 | # Check filenames for modules
347 | for filename in filenames:
348 | module_name = filename[:-3]
349 | module_uri = '.'.join((root_uri, module_name))
350 | if (self._uri2path(module_uri) and
351 | self._survives_exclude(module_uri, 'module')):
352 | modules.append(module_uri)
353 | return sorted(modules)
354 |
355 | def write_modules_api(self, modules,outdir):
356 | # write the list
357 | written_modules = []
358 | for m in modules:
359 | api_str = self.generate_api_doc(m)
360 | if not api_str:
361 | continue
362 | # write out to file
363 | outfile = os.path.join(outdir,
364 | m + self.rst_extension)
365 | fileobj = open(outfile, 'wt')
366 | fileobj.write(api_str)
367 | fileobj.close()
368 | written_modules.append(m)
369 | self.written_modules = written_modules
370 |
371 | def write_api_docs(self, outdir):
372 | """Generate API reST files.
373 |
374 | Parameters
375 | ----------
376 | outdir : string
377 | Directory name in which to store files
378 | We create automatic filenames for each module
379 |
380 | Returns
381 | -------
382 | None
383 |
384 | Notes
385 | -----
386 | Sets self.written_modules to list of written modules
387 | """
388 | if not os.path.exists(outdir):
389 | os.mkdir(outdir)
390 | # compose list of modules
391 | modules = self.discover_modules()
392 | self.write_modules_api(modules,outdir)
393 |
394 | def write_index(self, outdir, froot='gen', relative_to=None):
395 | """Make a reST API index file from written files
396 |
397 | Parameters
398 | ----------
399 | path : string
400 | Filename to write index to
401 | outdir : string
402 | Directory to which to write generated index file
403 | froot : string, optional
404 | root (filename without extension) of filename to write to
405 | Defaults to 'gen'. We add ``self.rst_extension``.
406 | relative_to : string
407 | path to which written filenames are relative. This
408 | component of the written file path will be removed from
409 | outdir, in the generated index. Default is None, meaning,
410 | leave path as it is.
411 | """
412 | if self.written_modules is None:
413 | raise ValueError('No modules written')
414 | # Get full filename path
415 | path = os.path.join(outdir, froot+self.rst_extension)
416 | # Path written into index is relative to rootpath
417 | if relative_to is not None:
418 | relpath = outdir.replace(relative_to + os.path.sep, '')
419 | else:
420 | relpath = outdir
421 | idx = open(path,'wt')
422 | w = idx.write
423 | w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n')
424 | w('.. toctree::\n\n')
425 | for f in self.written_modules:
426 | w(' %s\n' % os.path.join(relpath,f))
427 | idx.close()
428 |
--------------------------------------------------------------------------------
/doc_sphinx/sphinxext/docscrape.py:
--------------------------------------------------------------------------------
1 | """Extract reference documentation from the NumPy source tree.
2 |
3 | """
4 |
5 | import inspect
6 | import textwrap
7 | import re
8 | import pydoc
9 | from StringIO import StringIO
10 | from warnings import warn
11 | 4
12 | class Reader(object):
13 | """A line-based string reader.
14 |
15 | """
16 | def __init__(self, data):
17 | """
18 | Parameters
19 | ----------
20 | data : str
21 | String with lines separated by '\n'.
22 |
23 | """
24 | if isinstance(data,list):
25 | self._str = data
26 | else:
27 | self._str = data.split('\n') # store string as list of lines
28 |
29 | self.reset()
30 |
31 | def __getitem__(self, n):
32 | return self._str[n]
33 |
34 | def reset(self):
35 | self._l = 0 # current line nr
36 |
37 | def read(self):
38 | if not self.eof():
39 | out = self[self._l]
40 | self._l += 1
41 | return out
42 | else:
43 | return ''
44 |
45 | def seek_next_non_empty_line(self):
46 | for l in self[self._l:]:
47 | if l.strip():
48 | break
49 | else:
50 | self._l += 1
51 |
52 | def eof(self):
53 | return self._l >= len(self._str)
54 |
55 | def read_to_condition(self, condition_func):
56 | start = self._l
57 | for line in self[start:]:
58 | if condition_func(line):
59 | return self[start:self._l]
60 | self._l += 1
61 | if self.eof():
62 | return self[start:self._l+1]
63 | return []
64 |
65 | def read_to_next_empty_line(self):
66 | self.seek_next_non_empty_line()
67 | def is_empty(line):
68 | return not line.strip()
69 | return self.read_to_condition(is_empty)
70 |
71 | def read_to_next_unindented_line(self):
72 | def is_unindented(line):
73 | return (line.strip() and (len(line.lstrip()) == len(line)))
74 | return self.read_to_condition(is_unindented)
75 |
76 | def peek(self,n=0):
77 | if self._l + n < len(self._str):
78 | return self[self._l + n]
79 | else:
80 | return ''
81 |
82 | def is_empty(self):
83 | return not ''.join(self._str).strip()
84 |
85 |
86 | class NumpyDocString(object):
87 | def __init__(self,docstring):
88 | docstring = textwrap.dedent(docstring).split('\n')
89 |
90 | self._doc = Reader(docstring)
91 | self._parsed_data = {
92 | 'Signature': '',
93 | 'Summary': [''],
94 | 'Extended Summary': [],
95 | 'Parameters': [],
96 | 'Returns': [],
97 | 'Raises': [],
98 | 'Warns': [],
99 | 'Other Parameters': [],
100 | 'Attributes': [],
101 | 'Methods': [],
102 | 'See Also': [],
103 | 'Notes': [],
104 | 'Warnings': [],
105 | 'References': '',
106 | 'Examples': '',
107 | 'index': {}
108 | }
109 |
110 | self._parse()
111 |
112 | def __getitem__(self,key):
113 | return self._parsed_data[key]
114 |
115 | def __setitem__(self,key,val):
116 | if not self._parsed_data.has_key(key):
117 | warn("Unknown section %s" % key)
118 | else:
119 | self._parsed_data[key] = val
120 |
121 | def _is_at_section(self):
122 | self._doc.seek_next_non_empty_line()
123 |
124 | if self._doc.eof():
125 | return False
126 |
127 | l1 = self._doc.peek().strip() # e.g. Parameters
128 |
129 | if l1.startswith('.. index::'):
130 | return True
131 |
132 | l2 = self._doc.peek(1).strip() # ---------- or ==========
133 | return l2.startswith('-'*len(l1)) or l2.startswith('='*len(l1))
134 |
135 | def _strip(self,doc):
136 | i = 0
137 | j = 0
138 | for i,line in enumerate(doc):
139 | if line.strip(): break
140 |
141 | for j,line in enumerate(doc[::-1]):
142 | if line.strip(): break
143 |
144 | return doc[i:len(doc)-j]
145 |
146 | def _read_to_next_section(self):
147 | section = self._doc.read_to_next_empty_line()
148 |
149 | while not self._is_at_section() and not self._doc.eof():
150 | if not self._doc.peek(-1).strip(): # previous line was empty
151 | section += ['']
152 |
153 | section += self._doc.read_to_next_empty_line()
154 |
155 | return section
156 |
157 | def _read_sections(self):
158 | while not self._doc.eof():
159 | data = self._read_to_next_section()
160 | name = data[0].strip()
161 |
162 | if name.startswith('..'): # index section
163 | yield name, data[1:]
164 | elif len(data) < 2:
165 | yield StopIteration
166 | else:
167 | yield name, self._strip(data[2:])
168 |
169 | def _parse_param_list(self,content):
170 | r = Reader(content)
171 | params = []
172 | while not r.eof():
173 | header = r.read().strip()
174 | if ' : ' in header:
175 | arg_name, arg_type = header.split(' : ')[:2]
176 | else:
177 | arg_name, arg_type = header, ''
178 |
179 | desc = r.read_to_next_unindented_line()
180 | desc = dedent_lines(desc)
181 |
182 | params.append((arg_name,arg_type,desc))
183 |
184 | return params
185 |
186 |
187 | _name_rgx = re.compile(r"^\s*(:(?P\w+):`(?P[a-zA-Z0-9_.-]+)`|"
188 | r" (?P[a-zA-Z0-9_.-]+))\s*", re.X)
189 | def _parse_see_also(self, content):
190 | """
191 | func_name : Descriptive text
192 | continued text
193 | another_func_name : Descriptive text
194 | func_name1, func_name2, :meth:`func_name`, func_name3
195 |
196 | """
197 | items = []
198 |
199 | def parse_item_name(text):
200 | """Match ':role:`name`' or 'name'"""
201 | m = self._name_rgx.match(text)
202 | if m:
203 | g = m.groups()
204 | if g[1] is None:
205 | return g[3], None
206 | else:
207 | return g[2], g[1]
208 | raise ValueError("%s is not a item name" % text)
209 |
210 | def push_item(name, rest):
211 | if not name:
212 | return
213 | name, role = parse_item_name(name)
214 | items.append((name, list(rest), role))
215 | del rest[:]
216 |
217 | current_func = None
218 | rest = []
219 |
220 | for line in content:
221 | if not line.strip(): continue
222 |
223 | m = self._name_rgx.match(line)
224 | if m and line[m.end():].strip().startswith(':'):
225 | push_item(current_func, rest)
226 | current_func, line = line[:m.end()], line[m.end():]
227 | rest = [line.split(':', 1)[1].strip()]
228 | if not rest[0]:
229 | rest = []
230 | elif not line.startswith(' '):
231 | push_item(current_func, rest)
232 | current_func = None
233 | if ',' in line:
234 | for func in line.split(','):
235 | push_item(func, [])
236 | elif line.strip():
237 | current_func = line
238 | elif current_func is not None:
239 | rest.append(line.strip())
240 | push_item(current_func, rest)
241 | return items
242 |
243 | def _parse_index(self, section, content):
244 | """
245 | .. index: default
246 | :refguide: something, else, and more
247 |
248 | """
249 | def strip_each_in(lst):
250 | return [s.strip() for s in lst]
251 |
252 | out = {}
253 | section = section.split('::')
254 | if len(section) > 1:
255 | out['default'] = strip_each_in(section[1].split(','))[0]
256 | for line in content:
257 | line = line.split(':')
258 | if len(line) > 2:
259 | out[line[1]] = strip_each_in(line[2].split(','))
260 | return out
261 |
262 | def _parse_summary(self):
263 | """Grab signature (if given) and summary"""
264 | if self._is_at_section():
265 | return
266 |
267 | summary = self._doc.read_to_next_empty_line()
268 | summary_str = " ".join([s.strip() for s in summary]).strip()
269 | if re.compile('^([\w., ]+=)?\s*[\w\.]+\(.*\)$').match(summary_str):
270 | self['Signature'] = summary_str
271 | if not self._is_at_section():
272 | self['Summary'] = self._doc.read_to_next_empty_line()
273 | else:
274 | self['Summary'] = summary
275 |
276 | if not self._is_at_section():
277 | self['Extended Summary'] = self._read_to_next_section()
278 |
279 | def _parse(self):
280 | self._doc.reset()
281 | self._parse_summary()
282 |
283 | for (section,content) in self._read_sections():
284 | if not section.startswith('..'):
285 | section = ' '.join([s.capitalize() for s in section.split(' ')])
286 | if section in ('Parameters', 'Attributes', 'Methods',
287 | 'Returns', 'Raises', 'Warns'):
288 | self[section] = self._parse_param_list(content)
289 | elif section.startswith('.. index::'):
290 | self['index'] = self._parse_index(section, content)
291 | elif section == 'See Also':
292 | self['See Also'] = self._parse_see_also(content)
293 | else:
294 | self[section] = content
295 |
296 | # string conversion routines
297 |
298 | def _str_header(self, name, symbol='-'):
299 | return [name, len(name)*symbol]
300 |
301 | def _str_indent(self, doc, indent=4):
302 | out = []
303 | for line in doc:
304 | out += [' '*indent + line]
305 | return out
306 |
307 | def _str_signature(self):
308 | if self['Signature']:
309 | return [self['Signature'].replace('*','\*')] + ['']
310 | else:
311 | return ['']
312 |
313 | def _str_summary(self):
314 | if self['Summary']:
315 | return self['Summary'] + ['']
316 | else:
317 | return []
318 |
319 | def _str_extended_summary(self):
320 | if self['Extended Summary']:
321 | return self['Extended Summary'] + ['']
322 | else:
323 | return []
324 |
325 | def _str_param_list(self, name):
326 | out = []
327 | if self[name]:
328 | out += self._str_header(name)
329 | for param,param_type,desc in self[name]:
330 | out += ['%s : %s' % (param, param_type)]
331 | out += self._str_indent(desc)
332 | out += ['']
333 | return out
334 |
335 | def _str_section(self, name):
336 | out = []
337 | if self[name]:
338 | out += self._str_header(name)
339 | out += self[name]
340 | out += ['']
341 | return out
342 |
343 | def _str_see_also(self, func_role):
344 | if not self['See Also']: return []
345 | out = []
346 | out += self._str_header("See Also")
347 | last_had_desc = True
348 | for func, desc, role in self['See Also']:
349 | if role:
350 | link = ':%s:`%s`' % (role, func)
351 | elif func_role:
352 | link = ':%s:`%s`' % (func_role, func)
353 | else:
354 | link = "`%s`_" % func
355 | if desc or last_had_desc:
356 | out += ['']
357 | out += [link]
358 | else:
359 | out[-1] += ", %s" % link
360 | if desc:
361 | out += self._str_indent([' '.join(desc)])
362 | last_had_desc = True
363 | else:
364 | last_had_desc = False
365 | out += ['']
366 | return out
367 |
368 | def _str_index(self):
369 | idx = self['index']
370 | out = []
371 | out += ['.. index:: %s' % idx.get('default','')]
372 | for section, references in idx.iteritems():
373 | if section == 'default':
374 | continue
375 | out += [' :%s: %s' % (section, ', '.join(references))]
376 | return out
377 |
378 | def __str__(self, func_role=''):
379 | out = []
380 | out += self._str_signature()
381 | out += self._str_summary()
382 | out += self._str_extended_summary()
383 | for param_list in ('Parameters','Returns','Raises'):
384 | out += self._str_param_list(param_list)
385 | out += self._str_section('Warnings')
386 | out += self._str_see_also(func_role)
387 | for s in ('Notes','References','Examples'):
388 | out += self._str_section(s)
389 | out += self._str_index()
390 | return '\n'.join(out)
391 |
392 |
393 | def indent(str,indent=4):
394 | indent_str = ' '*indent
395 | if str is None:
396 | return indent_str
397 | lines = str.split('\n')
398 | return '\n'.join(indent_str + l for l in lines)
399 |
400 | def dedent_lines(lines):
401 | """Deindent a list of lines maximally"""
402 | return textwrap.dedent("\n".join(lines)).split("\n")
403 |
404 | def header(text, style='-'):
405 | return text + '\n' + style*len(text) + '\n'
406 |
407 |
408 | class FunctionDoc(NumpyDocString):
409 | def __init__(self, func, role='func', doc=None):
410 | self._f = func
411 | self._role = role # e.g. "func" or "meth"
412 | if doc is None:
413 | doc = inspect.getdoc(func) or ''
414 | try:
415 | NumpyDocString.__init__(self, doc)
416 | except ValueError, e:
417 | print('*'*78)
418 | print("ERROR: '%s' while parsing `%s`" % (e, self._f))
419 | print('*'*78)
420 |
421 | if not self['Signature']:
422 | func, func_name = self.get_func()
423 | try:
424 | # try to read signature
425 | argspec = inspect.getargspec(func)
426 | argspec = inspect.formatargspec(*argspec)
427 | argspec = argspec.replace('*','\*')
428 | signature = '%s%s' % (func_name, argspec)
429 | except TypeError, e:
430 | signature = '%s()' % func_name
431 | self['Signature'] = signature
432 |
433 | def get_func(self):
434 | func_name = getattr(self._f, '__name__', self.__class__.__name__)
435 | if inspect.isclass(self._f):
436 | func = getattr(self._f, '__call__', self._f.__init__)
437 | else:
438 | func = self._f
439 | return func, func_name
440 |
441 | def __str__(self):
442 | out = ''
443 |
444 | func, func_name = self.get_func()
445 | signature = self['Signature'].replace('*', '\*')
446 |
447 | roles = {'func': 'function',
448 | 'meth': 'method'}
449 |
450 | if self._role:
451 | if not roles.has_key(self._role):
452 | print("Warning: invalid role %s" % self._role)
453 | out += '.. %s:: %s\n \n\n' % (roles.get(self._role,''),
454 | func_name)
455 |
456 | out += super(FunctionDoc, self).__str__(func_role=self._role)
457 | return out
458 |
459 |
460 | class ClassDoc(NumpyDocString):
461 | def __init__(self,cls,modulename='',func_doc=FunctionDoc,doc=None):
462 | if not inspect.isclass(cls):
463 | raise ValueError("Initialise using a class. Got %r" % cls)
464 | self._cls = cls
465 |
466 | if modulename and not modulename.endswith('.'):
467 | modulename += '.'
468 | self._mod = modulename
469 | self._name = cls.__name__
470 | self._func_doc = func_doc
471 |
472 | if doc is None:
473 | doc = pydoc.getdoc(cls)
474 |
475 | NumpyDocString.__init__(self, doc)
476 |
477 | @property
478 | def methods(self):
479 | return [name for name,func in inspect.getmembers(self._cls)
480 | if not name.startswith('_') and callable(func)]
481 |
482 | def __str__(self):
483 | out = ''
484 | out += super(ClassDoc, self).__str__()
485 | out += "\n\n"
486 |
487 | #for m in self.methods:
488 | # print("Parsing `%s`" % m)
489 | # out += str(self._func_doc(getattr(self._cls,m), 'meth')) + '\n\n'
490 | # out += '.. index::\n single: %s; %s\n\n' % (self._name, m)
491 |
492 | return out
493 |
494 |
495 |
--------------------------------------------------------------------------------
/doc_sphinx/sphinxext/docscrape_sphinx.py:
--------------------------------------------------------------------------------
1 | import re, inspect, textwrap, pydoc
2 | from docscrape import NumpyDocString, FunctionDoc, ClassDoc
3 |
4 | class SphinxDocString(NumpyDocString):
5 | # string conversion routines
6 | def _str_header(self, name, symbol='`'):
7 | return ['.. rubric:: ' + name, '']
8 |
9 | def _str_field_list(self, name):
10 | return [':' + name + ':']
11 |
12 | def _str_indent(self, doc, indent=4):
13 | out = []
14 | for line in doc:
15 | out += [' '*indent + line]
16 | return out
17 |
18 | def _str_signature(self):
19 | return ['']
20 | if self['Signature']:
21 | return ['``%s``' % self['Signature']] + ['']
22 | else:
23 | return ['']
24 |
25 | def _str_summary(self):
26 | return self['Summary'] + ['']
27 |
28 | def _str_extended_summary(self):
29 | return self['Extended Summary'] + ['']
30 |
31 | def _str_param_list(self, name):
32 | out = []
33 | if self[name]:
34 | out += self._str_field_list(name)
35 | out += ['']
36 | for param,param_type,desc in self[name]:
37 | out += self._str_indent(['**%s** : %s' % (param.strip(),
38 | param_type)])
39 | out += ['']
40 | out += self._str_indent(desc,8)
41 | out += ['']
42 | return out
43 |
44 | def _str_section(self, name):
45 | out = []
46 | if self[name]:
47 | out += self._str_header(name)
48 | out += ['']
49 | content = textwrap.dedent("\n".join(self[name])).split("\n")
50 | out += content
51 | out += ['']
52 | return out
53 |
54 | def _str_see_also(self, func_role):
55 | out = []
56 | if self['See Also']:
57 | see_also = super(SphinxDocString, self)._str_see_also(func_role)
58 | out = ['.. seealso::', '']
59 | out += self._str_indent(see_also[2:])
60 | return out
61 |
62 | def _str_warnings(self):
63 | out = []
64 | if self['Warnings']:
65 | out = ['.. warning::', '']
66 | out += self._str_indent(self['Warnings'])
67 | return out
68 |
69 | def _str_index(self):
70 | idx = self['index']
71 | out = []
72 | if len(idx) == 0:
73 | return out
74 |
75 | out += ['.. index:: %s' % idx.get('default','')]
76 | for section, references in idx.iteritems():
77 | if section == 'default':
78 | continue
79 | elif section == 'refguide':
80 | out += [' single: %s' % (', '.join(references))]
81 | else:
82 | out += [' %s: %s' % (section, ','.join(references))]
83 | return out
84 |
85 | def _str_references(self):
86 | out = []
87 | if self['References']:
88 | out += self._str_header('References')
89 | if isinstance(self['References'], str):
90 | self['References'] = [self['References']]
91 | out.extend(self['References'])
92 | out += ['']
93 | return out
94 |
95 | def __str__(self, indent=0, func_role="obj"):
96 | out = []
97 | out += self._str_signature()
98 | out += self._str_index() + ['']
99 | out += self._str_summary()
100 | out += self._str_extended_summary()
101 | for param_list in ('Parameters', 'Attributes', 'Methods',
102 | 'Returns','Raises'):
103 | out += self._str_param_list(param_list)
104 | out += self._str_warnings()
105 | out += self._str_see_also(func_role)
106 | out += self._str_section('Notes')
107 | out += self._str_references()
108 | out += self._str_section('Examples')
109 | out = self._str_indent(out,indent)
110 | return '\n'.join(out)
111 |
112 | class SphinxFunctionDoc(SphinxDocString, FunctionDoc):
113 | pass
114 |
115 | class SphinxClassDoc(SphinxDocString, ClassDoc):
116 | pass
117 |
118 | def get_doc_object(obj, what=None, doc=None):
119 | if what is None:
120 | if inspect.isclass(obj):
121 | what = 'class'
122 | elif inspect.ismodule(obj):
123 | what = 'module'
124 | elif callable(obj):
125 | what = 'function'
126 | else:
127 | what = 'object'
128 | if what == 'class':
129 | return SphinxClassDoc(obj, '', func_doc=SphinxFunctionDoc, doc=doc)
130 | elif what in ('function', 'method'):
131 | return SphinxFunctionDoc(obj, '', doc=doc)
132 | else:
133 | if doc is None:
134 | doc = pydoc.getdoc(obj)
135 | return SphinxDocString(doc)
136 |
137 |
--------------------------------------------------------------------------------
/doc_sphinx/sphinxext/inheritance_diagram.py:
--------------------------------------------------------------------------------
1 | """
2 | Defines a docutils directive for inserting inheritance diagrams.
3 |
4 | Provide the directive with one or more classes or modules (separated
5 | by whitespace). For modules, all of the classes in that module will
6 | be used.
7 |
8 | Example::
9 |
10 | Given the following classes:
11 |
12 | class A: pass
13 | class B(A): pass
14 | class C(A): pass
15 | class D(B, C): pass
16 | class E(B): pass
17 |
18 | .. inheritance-diagram: D E
19 |
20 | Produces a graph like the following:
21 |
22 | A
23 | / \
24 | B C
25 | / \ /
26 | E D
27 |
28 | The graph is inserted as a PNG+image map into HTML and a PDF in
29 | LaTeX.
30 | """
31 |
32 | import inspect
33 | import os
34 | import re
35 | import subprocess
36 | try:
37 | from hashlib import md5
38 | except ImportError:
39 | from md5 import md5
40 |
41 | from docutils.nodes import Body, Element
42 | from docutils.parsers.rst import directives
43 | from sphinx.roles import xfileref_role
44 |
45 | def my_import(name):
46 | """Module importer - taken from the python documentation.
47 |
48 | This function allows importing names with dots in them."""
49 |
50 | mod = __import__(name)
51 | components = name.split('.')
52 | for comp in components[1:]:
53 | mod = getattr(mod, comp)
54 | return mod
55 |
56 | class DotException(Exception):
57 | pass
58 |
59 | class InheritanceGraph(object):
60 | """
61 | Given a list of classes, determines the set of classes that
62 | they inherit from all the way to the root "object", and then
63 | is able to generate a graphviz dot graph from them.
64 | """
65 | def __init__(self, class_names, show_builtins=False):
66 | """
67 | *class_names* is a list of child classes to show bases from.
68 |
69 | If *show_builtins* is True, then Python builtins will be shown
70 | in the graph.
71 | """
72 | self.class_names = class_names
73 | self.classes = self._import_classes(class_names)
74 | self.all_classes = self._all_classes(self.classes)
75 | if len(self.all_classes) == 0:
76 | raise ValueError("No classes found for inheritance diagram")
77 | self.show_builtins = show_builtins
78 |
79 | py_sig_re = re.compile(r'''^([\w.]*\.)? # class names
80 | (\w+) \s* $ # optionally arguments
81 | ''', re.VERBOSE)
82 |
83 | def _import_class_or_module(self, name):
84 | """
85 | Import a class using its fully-qualified *name*.
86 | """
87 | try:
88 | path, base = self.py_sig_re.match(name).groups()
89 | except:
90 | raise ValueError(
91 | "Invalid class or module '%s' specified for inheritance diagram" % name)
92 | fullname = (path or '') + base
93 | path = (path and path.rstrip('.'))
94 | if not path:
95 | path = base
96 | try:
97 | module = __import__(path, None, None, [])
98 | # We must do an import of the fully qualified name. Otherwise if a
99 | # subpackage 'a.b' is requested where 'import a' does NOT provide
100 | # 'a.b' automatically, then 'a.b' will not be found below. This
101 | # second call will force the equivalent of 'import a.b' to happen
102 | # after the top-level import above.
103 | my_import(fullname)
104 |
105 | except ImportError:
106 | raise ValueError(
107 | "Could not import class or module '%s' specified for inheritance diagram" % name)
108 |
109 | try:
110 | todoc = module
111 | for comp in fullname.split('.')[1:]:
112 | todoc = getattr(todoc, comp)
113 | except AttributeError:
114 | raise ValueError(
115 | "Could not find class or module '%s' specified for inheritance diagram" % name)
116 |
117 | # If a class, just return it
118 | if inspect.isclass(todoc):
119 | return [todoc]
120 | elif inspect.ismodule(todoc):
121 | classes = []
122 | for cls in todoc.__dict__.values():
123 | if inspect.isclass(cls) and cls.__module__ == todoc.__name__:
124 | classes.append(cls)
125 | return classes
126 | raise ValueError(
127 | "'%s' does not resolve to a class or module" % name)
128 |
129 | def _import_classes(self, class_names):
130 | """
131 | Import a list of classes.
132 | """
133 | classes = []
134 | for name in class_names:
135 | classes.extend(self._import_class_or_module(name))
136 | return classes
137 |
138 | def _all_classes(self, classes):
139 | """
140 | Return a list of all classes that are ancestors of *classes*.
141 | """
142 | all_classes = {}
143 |
144 | def recurse(cls):
145 | all_classes[cls] = None
146 | for c in cls.__bases__:
147 | if c not in all_classes:
148 | recurse(c)
149 |
150 | for cls in classes:
151 | recurse(cls)
152 |
153 | return all_classes.keys()
154 |
155 | def class_name(self, cls, parts=0):
156 | """
157 | Given a class object, return a fully-qualified name. This
158 | works for things I've tested in matplotlib so far, but may not
159 | be completely general.
160 | """
161 | module = cls.__module__
162 | if module == '__builtin__':
163 | fullname = cls.__name__
164 | else:
165 | fullname = "%s.%s" % (module, cls.__name__)
166 | if parts == 0:
167 | return fullname
168 | name_parts = fullname.split('.')
169 | return '.'.join(name_parts[-parts:])
170 |
171 | def get_all_class_names(self):
172 | """
173 | Get all of the class names involved in the graph.
174 | """
175 | return [self.class_name(x) for x in self.all_classes]
176 |
177 | # These are the default options for graphviz
178 | default_graph_options = {
179 | "rankdir": "LR",
180 | "size": '"8.0, 12.0"'
181 | }
182 | default_node_options = {
183 | "shape": "box",
184 | "fontsize": 10,
185 | "height": 0.25,
186 | "fontname": "Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",
187 | "style": '"setlinewidth(0.5)"'
188 | }
189 | default_edge_options = {
190 | "arrowsize": 0.5,
191 | "style": '"setlinewidth(0.5)"'
192 | }
193 |
194 | def _format_node_options(self, options):
195 | return ','.join(["%s=%s" % x for x in options.items()])
196 | def _format_graph_options(self, options):
197 | return ''.join(["%s=%s;\n" % x for x in options.items()])
198 |
199 | def generate_dot(self, fd, name, parts=0, urls={},
200 | graph_options={}, node_options={},
201 | edge_options={}):
202 | """
203 | Generate a graphviz dot graph from the classes that
204 | were passed in to __init__.
205 |
206 | *fd* is a Python file-like object to write to.
207 |
208 | *name* is the name of the graph
209 |
210 | *urls* is a dictionary mapping class names to http urls
211 |
212 | *graph_options*, *node_options*, *edge_options* are
213 | dictionaries containing key/value pairs to pass on as graphviz
214 | properties.
215 | """
216 | g_options = self.default_graph_options.copy()
217 | g_options.update(graph_options)
218 | n_options = self.default_node_options.copy()
219 | n_options.update(node_options)
220 | e_options = self.default_edge_options.copy()
221 | e_options.update(edge_options)
222 |
223 | fd.write('digraph %s {\n' % name)
224 | fd.write(self._format_graph_options(g_options))
225 |
226 | for cls in self.all_classes:
227 | if not self.show_builtins and cls in __builtins__.values():
228 | continue
229 |
230 | name = self.class_name(cls, parts)
231 |
232 | # Write the node
233 | this_node_options = n_options.copy()
234 | url = urls.get(self.class_name(cls))
235 | if url is not None:
236 | this_node_options['URL'] = '"%s"' % url
237 | fd.write(' "%s" [%s];\n' %
238 | (name, self._format_node_options(this_node_options)))
239 |
240 | # Write the edges
241 | for base in cls.__bases__:
242 | if not self.show_builtins and base in __builtins__.values():
243 | continue
244 |
245 | base_name = self.class_name(base, parts)
246 | fd.write(' "%s" -> "%s" [%s];\n' %
247 | (base_name, name,
248 | self._format_node_options(e_options)))
249 | fd.write('}\n')
250 |
251 | def run_dot(self, args, name, parts=0, urls={},
252 | graph_options={}, node_options={}, edge_options={}):
253 | """
254 | Run graphviz 'dot' over this graph, returning whatever 'dot'
255 | writes to stdout.
256 |
257 | *args* will be passed along as commandline arguments.
258 |
259 | *name* is the name of the graph
260 |
261 | *urls* is a dictionary mapping class names to http urls
262 |
263 | Raises DotException for any of the many os and
264 | installation-related errors that may occur.
265 | """
266 | try:
267 | dot = subprocess.Popen(['dot'] + list(args),
268 | stdin=subprocess.PIPE, stdout=subprocess.PIPE,
269 | close_fds=True)
270 | except OSError:
271 | raise DotException("Could not execute 'dot'. Are you sure you have 'graphviz' installed?")
272 | except ValueError:
273 | raise DotException("'dot' called with invalid arguments")
274 | except:
275 | raise DotException("Unexpected error calling 'dot'")
276 |
277 | self.generate_dot(dot.stdin, name, parts, urls, graph_options,
278 | node_options, edge_options)
279 | dot.stdin.close()
280 | result = dot.stdout.read()
281 | returncode = dot.wait()
282 | if returncode != 0:
283 | raise DotException("'dot' returned the errorcode %d" % returncode)
284 | return result
285 |
286 | class inheritance_diagram(Body, Element):
287 | """
288 | A docutils node to use as a placeholder for the inheritance
289 | diagram.
290 | """
291 | pass
292 |
293 | def inheritance_diagram_directive(name, arguments, options, content, lineno,
294 | content_offset, block_text, state,
295 | state_machine):
296 | """
297 | Run when the inheritance_diagram directive is first encountered.
298 | """
299 | node = inheritance_diagram()
300 |
301 | class_names = arguments
302 |
303 | # Create a graph starting with the list of classes
304 | graph = InheritanceGraph(class_names)
305 |
306 | # Create xref nodes for each target of the graph's image map and
307 | # add them to the doc tree so that Sphinx can resolve the
308 | # references to real URLs later. These nodes will eventually be
309 | # removed from the doctree after we're done with them.
310 | for name in graph.get_all_class_names():
311 | refnodes, x = xfileref_role(
312 | 'class', ':class:`%s`' % name, name, 0, state)
313 | node.extend(refnodes)
314 | # Store the graph object so we can use it to generate the
315 | # dot file later
316 | node['graph'] = graph
317 | # Store the original content for use as a hash
318 | node['parts'] = options.get('parts', 0)
319 | node['content'] = " ".join(class_names)
320 | return [node]
321 |
322 | def get_graph_hash(node):
323 | return md5(node['content'] + str(node['parts'])).hexdigest()[-10:]
324 |
325 | def html_output_graph(self, node):
326 | """
327 | Output the graph for HTML. This will insert a PNG with clickable
328 | image map.
329 | """
330 | graph = node['graph']
331 | parts = node['parts']
332 |
333 | graph_hash = get_graph_hash(node)
334 | name = "inheritance%s" % graph_hash
335 | path = '_images'
336 | dest_path = os.path.join(setup.app.builder.outdir, path)
337 | if not os.path.exists(dest_path):
338 | os.makedirs(dest_path)
339 | png_path = os.path.join(dest_path, name + ".png")
340 | path = setup.app.builder.imgpath
341 |
342 | # Create a mapping from fully-qualified class names to URLs.
343 | urls = {}
344 | for child in node:
345 | if child.get('refuri') is not None:
346 | urls[child['reftitle']] = child.get('refuri')
347 | elif child.get('refid') is not None:
348 | urls[child['reftitle']] = '#' + child.get('refid')
349 |
350 | # These arguments to dot will save a PNG file to disk and write
351 | # an HTML image map to stdout.
352 | image_map = graph.run_dot(['-Tpng', '-o%s' % png_path, '-Tcmapx'],
353 | name, parts, urls)
354 | return ('
%s' %
355 | (path, name, name, image_map))
356 |
357 | def latex_output_graph(self, node):
358 | """
359 | Output the graph for LaTeX. This will insert a PDF.
360 | """
361 | graph = node['graph']
362 | parts = node['parts']
363 |
364 | graph_hash = get_graph_hash(node)
365 | name = "inheritance%s" % graph_hash
366 | dest_path = os.path.abspath(os.path.join(setup.app.builder.outdir, '_images'))
367 | if not os.path.exists(dest_path):
368 | os.makedirs(dest_path)
369 | pdf_path = os.path.abspath(os.path.join(dest_path, name + ".pdf"))
370 |
371 | graph.run_dot(['-Tpdf', '-o%s' % pdf_path],
372 | name, parts, graph_options={'size': '"6.0,6.0"'})
373 | return '\n\\includegraphics{%s}\n\n' % pdf_path
374 |
375 | def visit_inheritance_diagram(inner_func):
376 | """
377 | This is just a wrapper around html/latex_output_graph to make it
378 | easier to handle errors and insert warnings.
379 | """
380 | def visitor(self, node):
381 | try:
382 | content = inner_func(self, node)
383 | except DotException, e:
384 | # Insert the exception as a warning in the document
385 | warning = self.document.reporter.warning(str(e), line=node.line)
386 | warning.parent = node
387 | node.children = [warning]
388 | else:
389 | source = self.document.attributes['source']
390 | self.body.append(content)
391 | node.children = []
392 | return visitor
393 |
394 | def do_nothing(self, node):
395 | pass
396 |
397 | def setup(app):
398 | setup.app = app
399 | setup.confdir = app.confdir
400 |
401 | app.add_node(
402 | inheritance_diagram,
403 | latex=(visit_inheritance_diagram(latex_output_graph), do_nothing),
404 | html=(visit_inheritance_diagram(html_output_graph), do_nothing))
405 | app.add_directive(
406 | 'inheritance-diagram', inheritance_diagram_directive,
407 | False, (1, 100, 0), parts = directives.nonnegative_int)
408 |
--------------------------------------------------------------------------------
/doc_sphinx/sphinxext/ipython_console_highlighting.py:
--------------------------------------------------------------------------------
1 | """reST directive for syntax-highlighting ipython interactive sessions.
2 |
3 | XXX - See what improvements can be made based on the new (as of Sept 2009)
4 | 'pycon' lexer for the python console. At the very least it will give better
5 | highlighted tracebacks.
6 | """
7 |
8 | #-----------------------------------------------------------------------------
9 | # Needed modules
10 |
11 | # Standard library
12 | import re
13 |
14 | # Third party
15 | from pygments.lexer import Lexer, do_insertions
16 | from pygments.lexers.agile import (PythonConsoleLexer, PythonLexer,
17 | PythonTracebackLexer)
18 | from pygments.token import Comment, Generic
19 |
20 | from sphinx import highlighting
21 |
22 | #-----------------------------------------------------------------------------
23 | # Global constants
24 | line_re = re.compile('.*?\n')
25 |
26 | #-----------------------------------------------------------------------------
27 | # Code begins - classes and functions
28 |
29 | class IPythonConsoleLexer(Lexer):
30 | """
31 | For IPython console output or doctests, such as:
32 |
33 | .. sourcecode:: ipython
34 |
35 | In [1]: a = 'foo'
36 |
37 | In [2]: a
38 | Out[2]: 'foo'
39 |
40 | In [3]: print(a)
41 | foo
42 |
43 | In [4]: 1 / 0
44 |
45 | Notes:
46 |
47 | - Tracebacks are not currently supported.
48 |
49 | - It assumes the default IPython prompts, not customized ones.
50 | """
51 |
52 | name = 'IPython console session'
53 | aliases = ['ipython']
54 | mimetypes = ['text/x-ipython-console']
55 | input_prompt = re.compile("(In \[[0-9]+\]: )|( \.\.\.+:)")
56 | output_prompt = re.compile("(Out\[[0-9]+\]: )|( \.\.\.+:)")
57 | continue_prompt = re.compile(" \.\.\.+:")
58 | tb_start = re.compile("\-+")
59 |
60 | def get_tokens_unprocessed(self, text):
61 | pylexer = PythonLexer(**self.options)
62 | tblexer = PythonTracebackLexer(**self.options)
63 |
64 | curcode = ''
65 | insertions = []
66 | for match in line_re.finditer(text):
67 | line = match.group()
68 | input_prompt = self.input_prompt.match(line)
69 | continue_prompt = self.continue_prompt.match(line.rstrip())
70 | output_prompt = self.output_prompt.match(line)
71 | if line.startswith("#"):
72 | insertions.append((len(curcode),
73 | [(0, Comment, line)]))
74 | elif input_prompt is not None:
75 | insertions.append((len(curcode),
76 | [(0, Generic.Prompt, input_prompt.group())]))
77 | curcode += line[input_prompt.end():]
78 | elif continue_prompt is not None:
79 | insertions.append((len(curcode),
80 | [(0, Generic.Prompt, continue_prompt.group())]))
81 | curcode += line[continue_prompt.end():]
82 | elif output_prompt is not None:
83 | # Use the 'error' token for output. We should probably make
84 | # our own token, but error is typicaly in a bright color like
85 | # red, so it works fine for our output prompts.
86 | insertions.append((len(curcode),
87 | [(0, Generic.Error, output_prompt.group())]))
88 | curcode += line[output_prompt.end():]
89 | else:
90 | if curcode:
91 | for item in do_insertions(insertions,
92 | pylexer.get_tokens_unprocessed(curcode)):
93 | yield item
94 | curcode = ''
95 | insertions = []
96 | yield match.start(), Generic.Output, line
97 | if curcode:
98 | for item in do_insertions(insertions,
99 | pylexer.get_tokens_unprocessed(curcode)):
100 | yield item
101 |
102 |
103 | def setup(app):
104 | """Setup as a sphinx extension."""
105 |
106 | # This is only a lexer, so adding it below to pygments appears sufficient.
107 | # But if somebody knows that the right API usage should be to do that via
108 | # sphinx, by all means fix it here. At least having this setup.py
109 | # suppresses the sphinx warning we'd get without it.
110 | pass
111 |
112 | #-----------------------------------------------------------------------------
113 | # Register the extension as a valid pygments lexer
114 | highlighting.lexers['ipython'] = IPythonConsoleLexer()
115 |
--------------------------------------------------------------------------------
/doc_sphinx/sphinxext/numpydoc.py:
--------------------------------------------------------------------------------
1 | """
2 | ========
3 | numpydoc
4 | ========
5 |
6 | Sphinx extension that handles docstrings in the Numpy standard format. [1]
7 |
8 | It will:
9 |
10 | - Convert Parameters etc. sections to field lists.
11 | - Convert See Also section to a See also entry.
12 | - Renumber references.
13 | - Extract the signature from the docstring, if it can't be determined otherwise.
14 |
15 | .. [1] http://projects.scipy.org/scipy/numpy/wiki/CodingStyleGuidelines#docstring-standard
16 |
17 | """
18 |
19 | import os, re, pydoc
20 | from docscrape_sphinx import get_doc_object, SphinxDocString
21 | import inspect
22 |
23 | def mangle_docstrings(app, what, name, obj, options, lines,
24 | reference_offset=[0]):
25 | if what == 'module':
26 | # Strip top title
27 | title_re = re.compile(r'^\s*[#*=]{4,}\n[a-z0-9 -]+\n[#*=]{4,}\s*',
28 | re.I|re.S)
29 | lines[:] = title_re.sub('', "\n".join(lines)).split("\n")
30 | else:
31 | doc = get_doc_object(obj, what, "\n".join(lines))
32 | lines[:] = str(doc).split("\n")
33 |
34 | if app.config.numpydoc_edit_link and hasattr(obj, '__name__') and \
35 | obj.__name__:
36 | if hasattr(obj, '__module__'):
37 | v = dict(full_name="%s.%s" % (obj.__module__, obj.__name__))
38 | else:
39 | v = dict(full_name=obj.__name__)
40 | lines += ['', '.. htmlonly::', '']
41 | lines += [' %s' % x for x in
42 | (app.config.numpydoc_edit_link % v).split("\n")]
43 |
44 | # replace reference numbers so that there are no duplicates
45 | references = []
46 | for l in lines:
47 | l = l.strip()
48 | if l.startswith('.. ['):
49 | try:
50 | references.append(int(l[len('.. ['):l.index(']')]))
51 | except ValueError:
52 | print("WARNING: invalid reference in %s docstring" % name)
53 |
54 | # Start renaming from the biggest number, otherwise we may
55 | # overwrite references.
56 | references.sort()
57 | if references:
58 | for i, line in enumerate(lines):
59 | for r in references:
60 | new_r = reference_offset[0] + r
61 | lines[i] = lines[i].replace('[%d]_' % r,
62 | '[%d]_' % new_r)
63 | lines[i] = lines[i].replace('.. [%d]' % r,
64 | '.. [%d]' % new_r)
65 |
66 | reference_offset[0] += len(references)
67 |
68 | def mangle_signature(app, what, name, obj, options, sig, retann):
69 | # Do not try to inspect classes that don't define `__init__`
70 | if (inspect.isclass(obj) and
71 | 'initializes x; see ' in pydoc.getdoc(obj.__init__)):
72 | return '', ''
73 |
74 | if not (callable(obj) or hasattr(obj, '__argspec_is_invalid_')): return
75 | if not hasattr(obj, '__doc__'): return
76 |
77 | doc = SphinxDocString(pydoc.getdoc(obj))
78 | if doc['Signature']:
79 | sig = re.sub("^[^(]*", "", doc['Signature'])
80 | return sig, ''
81 |
82 | def initialize(app):
83 | try:
84 | app.connect('autodoc-process-signature', mangle_signature)
85 | except:
86 | monkeypatch_sphinx_ext_autodoc()
87 |
88 | def setup(app, get_doc_object_=get_doc_object):
89 | global get_doc_object
90 | get_doc_object = get_doc_object_
91 |
92 | app.connect('autodoc-process-docstring', mangle_docstrings)
93 | app.connect('builder-inited', initialize)
94 | app.add_config_value('numpydoc_edit_link', None, True)
95 |
96 | #------------------------------------------------------------------------------
97 | # Monkeypatch sphinx.ext.autodoc to accept argspecless autodocs (Sphinx < 0.5)
98 | #------------------------------------------------------------------------------
99 |
100 | def monkeypatch_sphinx_ext_autodoc():
101 | global _original_format_signature
102 | import sphinx.ext.autodoc
103 |
104 | if sphinx.ext.autodoc.format_signature is our_format_signature:
105 | return
106 |
107 | print("[numpydoc] Monkeypatching sphinx.ext.autodoc ...")
108 | _original_format_signature = sphinx.ext.autodoc.format_signature
109 | sphinx.ext.autodoc.format_signature = our_format_signature
110 |
111 | def our_format_signature(what, obj):
112 | r = mangle_signature(None, what, None, obj, None, None, None)
113 | if r is not None:
114 | return r[0]
115 | else:
116 | return _original_format_signature(what, obj)
117 |
--------------------------------------------------------------------------------
/matrix2latex/IOString.py:
--------------------------------------------------------------------------------
1 | # I needed a string object that had the write function
2 |
3 | class IOString: # todo subclass str?
4 | # For a file like object, writes to the file while keeping
5 | # a local buffer.
6 | def __init__(self, fileObject=None):
7 | self.f = fileObject
8 | self.s = ""
9 |
10 | def write(self, s):
11 | try:
12 | self.f.write(s)
13 | except AttributeError:
14 | pass
15 | self.s += s
16 |
17 | def __str__(self):
18 | return self.s
19 |
20 | def close(self):
21 | try:
22 | self.f.close()
23 | except AttributeError:
24 | pass
25 |
--------------------------------------------------------------------------------
/matrix2latex/__init__.py:
--------------------------------------------------------------------------------
1 | """This file is part of matrix2latex.
2 |
3 | matrix2latex is free software: you can redistribute it and/or modify
4 | it under the terms of the GNU General Public License as published by
5 | the Free Software Foundation, either version 3 of the License, or
6 | (at your option) any later version.
7 |
8 | matrix2latex is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU General Public License for more details.
12 |
13 | You should have received a copy of the GNU General Public License
14 | along with matrix2latex. If not, see .
15 | """
16 |
17 | __all__ = ['matrix2latex']
18 |
19 | try:
20 | from matrix2latex import matrix2latex
21 | except ImportError:
22 | # Really ugly hack to please python3 import mechanisms
23 | import sys, os
24 | SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.expanduser(__file__)))
25 |
26 | sys.path.insert(0, SCRIPT_DIR)
27 | from matrix2latex import matrix2latex
28 | matrix2latex = matrix2latex.matrix2latex
29 | del sys.path[0] # NOTE: ensure that matrix2latex does not change sys.path
30 |
--------------------------------------------------------------------------------
/matrix2latex/error.py:
--------------------------------------------------------------------------------
1 | # Error handling for matrix2latex.py,
2 | # todo: don't yell at errors, fix them!
3 | # To clean the code, error handling is moved to small functions
4 | def assertStr(value, key):
5 | pass
6 | # assert isinstance(value, str), \
7 | # "expected %s to be a str, got %s" % (key, type(value))
8 |
9 | def assertKeyFormat(value):
10 | assertStr(value, "format")
11 | assert r"%" in value, \
12 | "expected a format str, got %s" % value
13 | assert value.count("%") == 1,\
14 | "expected a single format, got %s" % value
15 |
16 | def assertKeyAlignment(value, n):
17 | return n
18 | assertStr(value, "alignment")
19 | assert ("c" in value or "l" in value or "r" in value), \
20 | "expected legal alignment c, l or r, got %s" % value
21 | counter = dict()
22 | counter['c'] = 0
23 | counter['l'] = 0
24 | counter['r'] = 0
25 | for v in value:
26 | if v in counter:
27 | counter[v] += 1
28 | else:
29 | counter[v] = 1
30 | length = counter['c'] + counter['l'] + counter['r']
31 | return length
32 | # assert length == n,\
33 | # "Error: %g of %g alignments given '%s'\n" % (length, n, value)
34 |
35 | def assertListString(value, key):
36 | pass
37 | # assert isinstance(value, list),\
38 | # "Expected %s to be a list, got %s" % (key, type(value))
39 | # for e in value:
40 | # assertStr(e, "%s element" % key)
41 |
--------------------------------------------------------------------------------
/matrix2latex/fixEngineeringNotation.py:
--------------------------------------------------------------------------------
1 | """This file is part of matrix2latex.
2 |
3 | matrix2latex is free software: you can redistribute it and/or modify
4 | it under the terms of the GNU General Public License as published by
5 | the Free Software Foundation, either version 3 of the License, or
6 | (at your option) any later version.
7 |
8 | matrix2latex is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU General Public License for more details.
12 |
13 | You should have received a copy of the GNU General Public License
14 | along with matrix2latex. If not, see .
15 | """
16 |
17 | import re
18 |
19 | def fix(s, table=False):
20 | """
21 | input: (string) s
22 | output: (string) s
23 | takes any number in s and replaces the format
24 | '8e-08' with '8\e{-08}'
25 | """
26 | i = re.search('e[-+]\d\d', s)
27 | while i != None:
28 | before = s[0:i.start()]
29 | number = s[i.start()+1:i.start()+4]
30 | after = s[i.end():]
31 | if table:
32 | num = "%(#)+03d" % {'#': int(number)}
33 | else:
34 | num = "%(#)3d" % {'#': int(number)}
35 |
36 | s = '%s\\e{%s}%s' % (before, num, after)
37 | i = re.search('e[-+]\d\d', s)
38 | return s
39 |
--------------------------------------------------------------------------------
/matrix2latex/matrix2latex.py:
--------------------------------------------------------------------------------
1 | """This file is part of matrix2latex.
2 |
3 | matrix2latex is free software: you can redistribute it and/or modify
4 | it under the terms of the GNU General Public License as published by
5 | the Free Software Foundation, either version 3 of the License, or
6 | (at your option) any later version.
7 |
8 | matrix2latex is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU General Public License for more details.
12 |
13 | You should have received a copy of the GNU General Public License
14 | along with matrix2latex. If not, see .
15 | """
16 | import sys
17 | import os.path
18 | import warnings
19 | import math
20 | import re
21 | def isnan(e):
22 | try:
23 | return math.isnan(e)
24 | except (TypeError, AttributeError):
25 | return e == float("nan")
26 |
27 | from fixEngineeringNotation import fix
28 | from error import * # error handling
29 | from IOString import IOString
30 | # Definitions
31 | # Matrix environments where alignment can be utilized. CHECK: Note alignment[0] used!
32 | matrix_alignment = ["pmatrix*","bmatrix*","Bmatrix*","vmatrix*","Vmatrix*"] # Needs mathtools package
33 | # Table environments where alignment can be utilized
34 | table_alignment = ["tabular", "longtable"]
35 |
36 | def matrix2latex(matr, filename=None, *environments, **keywords):
37 | r'''
38 | Takes a python matrix or nested list and converts to a LaTeX table or matrix.
39 | Author: ob@cakebox.net, inspired by the work of koehler@in.tum.de who has written
40 | a `similar package for
41 | matlab `_
42 |
43 | The following packages and definitions are recommended in the latex preamble
44 |
45 | .. code-block:: latex
46 |
47 | \providecommand{\e}[1]{\ensuremath{\times 10^{#1}}} % scientific notation, 1\e{9} will print as 1x10^9
48 | \usepackage{amsmath} % needed for pmatrix
49 | \usepackage{booktabs} % Fancy tables
50 | ...
51 | \begin{document}
52 | ...
53 |
54 | :param list matr: The numpy matrix/array or a nested list to convert.
55 |
56 | :param str filename: File to place output, extension .tex is added automatically. File can be included in a LaTeX
57 | document by ``\input{filename}``. If filename is None
58 | or not a string it is ignored.
59 |
60 | :arg environments: A list specifing the begin and end block.
61 | Example: ``matrix2latex(m, None, "align*", "pmatrix")`` gives the matrix
62 |
63 | .. code-block:: latex
64 |
65 | \begin{align*}
66 | \begin{pmatrix}
67 | 1 & 2 \\
68 | 3 & 4
69 | \end{pmatrix}
70 | \end{align*}
71 |
72 | The default is generating a table using the ``table``, ``center`` and ``tabular``
73 | environment, hence
74 | ``matrix2latex(m, "test", "table", "center", "tabular" ...)``
75 | can be written as
76 | ``matrix2latex(m, "test", ...)``
77 |
78 | :key headerRow:
79 | A row at the top used to label the columns.
80 | Must be a list of strings. Can be a nested list for multiple headings.
81 | If two or more items are repeated, a multicolumn is inserted, so:
82 | ``headerRow=['a', 'a']``
83 | will produces ``\multicolumn{2}{c}{Item}`` with an appropriate cmidrule beneath.
84 | To avoid this behavior ensure each consecutive item is unique, for instance:
85 | ``headerRow=['a', 'a ']``
86 | will produces the expected ``a & a`` (note the space after the second ``a``).
87 |
88 | :key headerColumn:
89 | A column used to label the rows.
90 | Must be a list of strings
91 |
92 | :key transpose:
93 | Flips the table around in case you messed up. Equivalent to
94 | ``matrix2latex(m.H, ...)``
95 | if m is a numpy matrix.
96 |
97 | :key caption:
98 | Use to define a caption for your table.
99 | Inserts ``\caption`` after ``\begin{center}``,
100 | note that without the center environment the caption is currently ignored.
101 |
102 | :key label:
103 | Used to insert ``\label{tab:...}`` after ``\end{tabular}``
104 | Default is filename without extension.
105 |
106 | :key format:
107 | Printf syntax format, e.g. ``$%.2f$``. Default is ``$%g$``.
108 | This format is then used for all the elements in the table.
109 |
110 | :key formatColumn:
111 | A list of printf-syntax formats, e.g. ``[$%.2f$, $%g$]``
112 | Must be of same length as the number of columns.
113 | Format i is then used for column i.
114 | This is useful if some of your data should be printed with more significant figures
115 | than other parts.
116 |
117 | :key alignment:
118 | Used as an option when tabular is given as enviroment.
119 | ``\begin{tabular}{alignment}``
120 | A latex alignment like ``c``, ``l`` or ``r``.
121 | Can be given either as one per column e.g. ``"ccc"``.
122 | Or if only a single character is given e.g. ``"c"``,
123 | it will produce the correct amount depending on the number of columns.
124 | Default is ``"r"``.
125 |
126 | :key position:
127 | Used for the table environment to specify the optional parameter "position specifier"
128 | Default is ``'[' + 'htp' + ']'``
129 | If you want to place your table manually, do not use the table environment.
130 |
131 | Note that many of these options only has an effect when typesetting a table,
132 | if the correct environment is not given the arguments are simply ignored.
133 |
134 | :return str table:
135 | Returns the latex formated output as a string.
136 | '''
137 | headerRow = None
138 | headerColumn = None
139 |
140 | #
141 | # Convert to list
142 | #
143 | # If pandas
144 | try:
145 | headerColumn = list(matr.index)
146 | except (AttributeError, TypeError):
147 | pass
148 | try:
149 | headerRow = [list(matr.columns)]
150 | except (AttributeError, TypeError):
151 | pass
152 | try:
153 | matr = matr.to_records(index=False)
154 | except AttributeError:
155 | pass
156 | # If numpy (vops: must be placed below pandas check)
157 | try:
158 | matr = matr.tolist()
159 | except AttributeError:
160 | pass # lets hope it looks like a list
161 |
162 | #
163 | # Define matrix-size
164 | #
165 | m = len(matr)
166 | try:
167 | n = len(matr[0]) # may raise TypeError
168 | for row in matr:
169 | n = max(n, len(row)) # keep max length
170 | except TypeError: # no length in this dimension (vector...)
171 | # convert [1, 2] to [[1], [2]]
172 | newMatr = list()
173 | [newMatr.append([matr[ix]]) for ix in range(m)]
174 | matr = newMatr
175 | m = len(matr)
176 | n = len(matr[0])
177 | except IndexError:
178 | m = 0
179 | n = 0
180 | #assert m > 0 and n > 0, "Expected positive matrix dimensions, got %g by %g matrix" % (m, n)
181 | # Bug with transpose:
182 | # # If header and/or column labels are longer use those lengths
183 | # try:
184 | # m = max(m, len(keywords['headerColumn'])) # keep max length
185 | # except KeyError:
186 | # pass
187 | # try:
188 | # n = max(n, len(keywords['headerRow'])) # keep max length
189 | # except KeyError:
190 | # pass
191 |
192 | #
193 | # Default values
194 | #
195 |
196 | # Keywords
197 | formatNumber = "$%g$"
198 | formatColumn = None
199 | if n != 0:
200 | alignment = "c"*n # cccc
201 | else:
202 | alignment = "c"
203 |
204 | caption = None
205 | label = None
206 | position = "htp" # position specifier for floating table environment
207 |
208 | #
209 | # Conflicts
210 | #
211 | if "format" in keywords and "formatColumn" in keywords:
212 | warnings.warn('Specifying both format and formatColumn is not supported, using formatColumn')
213 | del keywords["format"]
214 |
215 | #
216 | # User-defined values
217 | #
218 | for key in keywords:
219 | value = keywords[key]
220 | if key == "format":
221 | assertKeyFormat(value)
222 | formatNumber = value
223 | formatColumn = None # never let both formatColumn and formatNumber to be defined
224 | elif key == "formatColumn":
225 | formatColumn = value
226 | formatNumber = None
227 | elif key == "alignment":
228 | if len(value) == 1:
229 | alignment = value*n # rrrr
230 | else:
231 | alignment = value
232 | assertKeyAlignment(alignment, n)
233 | elif key == "headerRow":
234 | if value == None:
235 | headerRow = None
236 | else:
237 | if not(type(value[0]) == list):
238 | value = [value] # just one header
239 | #assertListString(value, "headerRow") # todo: update
240 | headerRow = list(value)
241 | elif key == "headerColumn":
242 | if value == None:
243 | headerColumn = None
244 | else:
245 | assertListString(value, "headerColumn")
246 | headerColumn = list(value)
247 | elif key == "caption":
248 | assertStr(value, "caption")
249 | caption = value
250 | elif key == "label":
251 | assertStr(value, "label")
252 | if value.startswith('tab:'):
253 | label = value[len('tab:'):] # this will be added later in the code, avoids 'tab:tab:' as label
254 | else:
255 | label = value
256 | elif key == "filename":
257 | assertStr(value, "filename")
258 | filename = value
259 | elif key == "position":
260 | assertStr(value, "position")
261 | position = value
262 | elif key == "environments":
263 | environments = value
264 | elif key == "transpose":
265 | newMatr = list(zip(*matr))
266 | # for j in range(0, n):
267 | # row = list()
268 | # for i in range(0, m):
269 | # row.append(matr[i][j])
270 | # newMatr.append(row)
271 | copyKeywords = dict(keywords) # can't del original since we are inside for loop.
272 | del copyKeywords['transpose']
273 | # Recursion!
274 | return matrix2latex(newMatr, filename, *environments, **copyKeywords)
275 | else:
276 | raise ValueError("Error: key not recognized '%s'" % key)
277 |
278 | if headerColumn != None:
279 | alignment = "r" + alignment
280 |
281 | # Environments
282 | if environments is None: # environments=None passed, do not add any environments.
283 | environments = []
284 | elif len(environments) == 0: # no environment give, assume table
285 | environments = ("table", "center", "tabular")
286 |
287 | if formatColumn == None:
288 | formatColumn = list()
289 | for j in range(0, n):
290 | formatColumn.append(formatNumber)
291 |
292 | if headerColumn != None and headerRow != None and len(headerRow[0]) == n:
293 | for i in range(len(headerRow)):
294 | headerRow[i].insert(0, "")
295 |
296 | #
297 | # Set outputFile
298 | #
299 | f = None
300 | if isinstance(filename, str) and filename != '':
301 | if not filename.endswith('.tex'): # assure propper file extension
302 | filename += '.tex'
303 | f = open(filename, 'w')
304 | if label == None:
305 | label = os.path.basename(filename) # get basename
306 | label = label[:-len(".tex")] # remove extension
307 |
308 | f = IOString(f)
309 | #
310 | # Begin block
311 | #
312 | for ixEnv in range(0, len(environments)):
313 | f.write("\t"*ixEnv)
314 | f.write(r"\begin{%s}" % environments[ixEnv])
315 | # special environments:
316 | if environments[ixEnv] == "table":
317 | f.write("[" + position + "]")
318 | elif environments[ixEnv] == "center":
319 | if caption != None:
320 | f.write("\n"+"\t"*ixEnv)
321 | f.write(r"\caption{%s}" % fix(caption))
322 | if label != None:
323 | f.write("\n"+"\t"*ixEnv)
324 | f.write(r"\label{tab:%s}" % label)
325 | elif environments[ixEnv] in table_alignment:
326 | f.write("{" + alignment + "}\n")
327 | f.write("\t"*ixEnv)
328 | f.write(r"\toprule")
329 | elif environments[ixEnv] in matrix_alignment:
330 | f.write("[" + alignment[0] + "]\n") #These environment you can add
331 | # newline
332 | f.write("\n")
333 | tabs = len(environments) # number of \t to use
334 |
335 | #
336 | # Table block
337 | #
338 |
339 | # Row labels
340 | if headerRow != None:
341 | for row in range(len(headerRow)): # for each header
342 | i = 0
343 | start, end = list(), list() # of cmidrule
344 | f.write("\t"*tabs)
345 | while i < len(headerRow[row]): # for each element (skipping repeating ones)
346 | j = 1
347 | # check for legal index then check if current element is equal to next (repeating)
348 | repeating = i+j < len(headerRow[row]) and headerRow[row][i] == headerRow[row][i + j]
349 | if repeating:
350 | while repeating: # figure out how long it repeats (j)
351 | j += 1
352 | repeating = i+j < len(headerRow[row]) and headerRow[row][i] == headerRow[row][i + j]
353 | f.write(r'\multicolumn{%d}{c}{%s}' % (j, headerRow[row][i])) # multicol heading
354 | start.append(i);end.append(j+i)
355 | i += j # skip ahed
356 | else:
357 | f.write('{%s}' % headerRow[row][i]) # normal heading
358 | i += 1
359 | if i < len(headerRow[row]): # if not last element
360 | f.write(' & ')
361 |
362 | f.write(r'\\')
363 | for s, e in zip(start, end):
364 | f.write(r'\cmidrule(r){%d-%d}' % (s+1, e))
365 | f.write('\n')
366 | if len(start) == 0: # do not use if cmidrule is used on last header
367 | f.write('\t'*tabs)
368 | f.write('\\midrule\n')
369 |
370 | # Values
371 | for i in range(0, m):
372 | f.write("\t"*tabs)
373 | for j in range(0, n):
374 |
375 | if j == 0: # first row
376 | if headerColumn != None:
377 | try:
378 | f.write("{%s} & " % headerColumn[i])
379 | except IndexError:
380 | f.write('&')
381 |
382 | try: # get current element
383 | if '%s' not in formatColumn[j]:
384 | try:
385 | e = float(matr[i][j]) # current element
386 | except ValueError: # can't convert to float, use string
387 | formatColumn[j] = '%s'
388 | e = matr[i][j]
389 | except TypeError: # raised for None
390 | e = None
391 | else:
392 | e = matr[i][j]
393 | except IndexError:
394 | e = None
395 |
396 | if e == None or isnan(e):#e == float('NaN'):
397 | f.write("{-}")
398 | elif e == float('inf'):
399 | f.write(r"$\infty$")
400 | elif e == float('-inf'):
401 | f.write(r"$-\infty$")
402 | else:
403 | fcj = formatColumn[j]
404 |
405 | formated = fcj % e
406 | formated = fix(formated, table=True) # fix 1e+2
407 | f.write('%s' % formated)
408 | if j != n-1: # not last row
409 | f.write(" & ")
410 | else: # last row
411 | f.write(r"\\")
412 | f.write("\n")
413 |
414 | #
415 | # End block
416 | #
417 | for ixEnv in range(0, len(environments)):
418 | ixEnv = len(environments)-1 - ixEnv # reverse order
419 | # special environments:
420 | if environments[ixEnv] == "center":
421 | pass
422 | elif environments[ixEnv] == "tabular":
423 | f.write("\t"*ixEnv)
424 | f.write(r"\bottomrule"+"\n")
425 | f.write("\t"*ixEnv)
426 | f.write(r"\end{%s}" % environments[ixEnv])
427 | if ixEnv != 0:
428 | f.write("\n")
429 |
430 | f.close()
431 | return f.__str__()
432 |
433 | if __name__ == '__main__':
434 | # m = matrix('1 2 4;3 4 6')
435 | # m = matrix('1 2 4;2 2 1;2 1 2')
436 | m = [[1, 2, 3], [3, 4, 5]]
437 | print(matrix2latex(m))
438 | print(matrix2latex(m, 'tmp.tex'))
439 | print(matrix2latex(m, None, "table", "center", "tabular", format="$%.2f$", alignment='lcr'))
440 | cl = ["a", "b", "c"]
441 | rl = ['d', 'e', 'f', 'g']
442 | print(matrix2latex(m, None, format="$%.2g$", alignment='lcr',
443 | headerColumn=cl,caption="test", label="2", headerRow=rl))
444 | print(matrix2latex(m, None, "align*", "pmatrix", format="%g", alignment='c'))
445 | print(matrix2latex(m, None, headerColumn=cl, caption="Hello", label="la"))
446 | print(matrix2latex([['a', 'b', '1'], ['1', '2', '3']], format='%s'))
447 |
448 | m = [[1,None,None], [2,2,1], [2,1,2]]
449 | print(matrix2latex(m, transpose=True))
450 |
451 | # TODO:
452 | # m = [[1], [2,2,1], [2,1,2]]
453 | # print(matrix2latex(m, transpose=True))
454 |
--------------------------------------------------------------------------------
/matrix2latex/pagination.py:
--------------------------------------------------------------------------------
1 | """This file is part of matrix2latex.
2 | matrix2latex is free software: you can redistribute it and/or modify
3 | it under the terms of the GNU General Public License as published by
4 | the Free Software Foundation, either version 3 of the License, or
5 | (at your option) any later version.
6 | matrix2latex is distributed in the hope that it will be useful,
7 | but WITHOUT ANY WARRANTY; without even the implied warranty of
8 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 | GNU General Public License for more details.
10 | You should have received a copy of the GNU General Public License
11 | along with matrix2latex. If not, see .
12 | """
13 |
14 | import os
15 | from matrix2latex import matrix2latex
16 | from subprocess import call
17 |
18 | def simple(matrix, headerRow=None, headerColumn=None, Filename=None, font_size=None, clean_latex=True):
19 | """A simple pagination function, that creates a minimal LaTeX document code for an input matrix,
20 | compiles it, and removes the LaTeX traces.
21 |
22 | Arguments:
23 |
24 | matrix
25 | A numpy matrix or a nested list
26 |
27 | Filename
28 | File to place output, extension .tex is added automatically. File can be included in a LaTeX
29 | document by \input{filename}. Output will always be returned in a string. If filename is None
30 | or not a string it is ignored.
31 |
32 | headerRow
33 | A row at the top used to label the columns.
34 | Must be a list of strings. Can be a nested list for multiple headings.
35 | If two or more items are repeated, a multicolumn is inserted, so:
36 | headerRow=['a', 'a']
37 | will produces "\multicolumn{2}{c}{Item}" with an appropriate cmidrule beneath.
38 | To avoid this behavior ensure each consecutive item is unique, for instance:
39 | headerRow=['a', 'a ']
40 | will produces the expected "a & a".
41 |
42 | headerColumn
43 | A column used to label the rows.
44 | Must be a list of strings
45 |
46 | font_size
47 | Specify the global (document and table) font size.
48 | Accepted values are integers from 1 to 10 - these are mapped on the available LaTeX font sizes
49 | https://en.wikibooks.org/wiki/LaTeX/Fonts
50 |
51 | clean_latex
52 | Used to optionally turn off the delete phase for LaTeX traces
53 | Must be bool
54 | """
55 |
56 | latex_font_sizes = {
57 | 1: "\\tiny",
58 | 2: "\\scriptsize",
59 | 3: "\\footnotesize",
60 | 4: "\\small",
61 | 5: "\\normalsize",
62 | 6: "\\large",
63 | 7: "\\Large",
64 | 8: "\\LARGE",
65 | 9: "\\huge",
66 | 10: "\\Huge"
67 | }
68 |
69 | if not Filename:
70 | Filename = "_temp"
71 |
72 | table = matrix2latex(matrix, headerRow=headerRow, headerColumn=headerColumn, environments=['tabular'])
73 |
74 | #determine document font size
75 | if font_size:
76 | document_fontsize = latex_font_sizes[font_size]+"\n"
77 | else:
78 | document_fontsize = ""
79 |
80 | #add header elements (with the prepend operator "+"y in reverse order)
81 | tex = "\\sbox\mt{%\n" + table
82 | tex = document_fontsize + tex
83 | tex = "\\begin{document}\n" + tex
84 | tex = "\\pagenumbering{gobble}\n" + tex
85 | tex = "\\newsavebox\mt\n" + tex
86 | tex = "\\usepackage{booktabs}\n" + tex
87 | tex = "\\usepackage{geometry}\n\\geometry{a4paper,total={210mm,297mm},left=15mm,right=15mm,top=15mm,bottom=15mm}\n" + tex
88 | tex = "\\documentclass{article}\n" + tex
89 |
90 | #add footer elements
91 | tex = tex + "%\n}\n"
92 | tex = tex + \
93 | "\\makeatletter\n" + \
94 | "\\ifdim\\wd\\mt>\\textwidth\n" + \
95 | "\\setlength\\@tempdima {\\paperheight}%\n" + \
96 | "\\setlength\\paperheight {\\paperwidth}%\n" + \
97 | "\\setlength\\paperwidth {\\@tempdima}%\n" + \
98 | "\\setlength\\pdfpageheight{\\paperheight}%\n" + \
99 | "\\setlength\\pdfpagewidth{\\paperwidth}%\n" + \
100 | "\\setlength{\\textwidth}{\\paperwidth}%\n" + \
101 | "\\addtolength{\\textwidth}{-3cm}%\n" + \
102 | "\\setlength{\\hsize}{\\textwidth}%\n" + \
103 | "\\fi\n" + \
104 | "\\makeatother\n" + \
105 | "\\begin{table}[htp]\\setlength{\\hsize}{\\textwidth}%\n" + \
106 | "\\centering\n" + \
107 | "\\usebox\\mt\n" + \
108 | "\\end{table}\n" + \
109 | "\\end{document}\n"
110 |
111 | file_ = open(Filename+".tex", 'w')
112 | file_.write(tex)
113 | file_.close()
114 | call(["pdflatex", Filename+".tex"])
115 |
116 | if clean_latex:
117 | all_files = os.listdir(".")
118 | latex_files = [one_file for one_file in all_files if Filename in one_file]
119 | non_pdf_latex_files = [latex_file for latex_file in latex_files if ".pdf" not in latex_file]
120 | for non_pdf_latex_file in non_pdf_latex_files:
121 | os.remove(non_pdf_latex_file)
122 |
--------------------------------------------------------------------------------
/matrix2latex/render.py:
--------------------------------------------------------------------------------
1 | """This file is part of matrix2latex.
2 |
3 | matrix2latex is free software: you can redistribute it and/or modify
4 | it under the terms of the GNU General Public License as published by
5 | the Free Software Foundation, either version 3 of the License, or
6 | (at your option) any later version.
7 |
8 | matrix2latex is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU General Public License for more details.
12 |
13 | You should have received a copy of the GNU General Public License
14 | along with matrix2latex. If not, see .
15 | """
16 | import os
17 | import shutil
18 | import warnings
19 | import tempfile
20 | import subprocess
21 | from matrix2latex import matrix2latex
22 |
23 | _latex_documentclass = r'\documentclass[varwidth=true, border=2pt, convert=true]{standalone}'
24 | _latex_preamble = r"""\providecommand{\e}[1]{\ensuremath{\times 10^{#1}}}
25 | \usepackage{amsmath}
26 | \usepackage{booktabs}
27 | """
28 | _latex_template = r"""%s
29 | %s
30 | \begin{document}
31 | \pagenumbering{gobble}
32 | %s
33 | \end{document}
34 | """
35 | def matrix2image(matr, filename=None, *args, **kwargs):
36 | r'''
37 | A wrapper around ``matrix2latex(*args, **kwargs)`` that creates a minimal LaTeX document code,
38 | compiles it, an removes the LaTeX traces.
39 |
40 | A number of options are available to support non-standard latex compilers (``pdflatex``, ``tex_options`` and ``output_format``),
41 | temporary file handling (``clean_latex`` and ``working_dir``) and
42 | customizing the latex template code (``latex_preamble``, ``latex_documentclass`` and ``latex_template``),
43 | These all have (hopefully) sensible default values, if you find something particularly lacking, open an issue!
44 |
45 | :param list matr:
46 | The numpy matrix/array or a nested list to convert.
47 | :param str filename:
48 | Base-filename for the output image, defaults to 'rendered'
49 | :key clean_latex=True:
50 | Remove traces of the latex compilation. Use clean_latex=False and working_dir='tmp' to debug.
51 | :key working_dir=None:
52 | A temporary directory where the document is compiled, WARNING: will be removed as long as
53 | clean_latex is True, do not specify an existing directory. Defaults to tempfile.mkdtemp().
54 | :key latex_preamble:
55 | Defaults to ``render._latex_preamble``. To include additional preamble commands, use
56 |
57 | ``latex_preamble = render._latex_preamble + r'\usepackage{my_package}'``
58 | :key latex_documentclass:
59 | Defaults to ``render._latex_documentclass``, for a4 page, call with ``latex_documentclass='\documentclass[a4paper]{article}'``.
60 | :key latex_template:
61 | The latex wrapper code, defaults to ``render._latex_template``, use at your own risk.
62 | :key tex='pdflatex':
63 | The tex renderer. Assumed to produce a '.pdf', if not, remember to also specify an appropriate output_format.
64 | If empty string or None, the document is not compiled.
65 | :key tex_options=['-interaction=nonstopmode', '-shell-escape']:
66 | Options passed to tex renderer
67 | :key output_format='.pdf':
68 | By default it is assumed ``tex='pdflatex'`` produces a '.pdf' and a '.png',
69 | by default the '.pdf' is used, but you may also want to use ``output_format='.png'`` for the png image.
70 | :\*args:
71 | Additional arguments are passed to matrix2latex
72 | :\**kwargs:
73 | Additional keyword-arguments are passed to matrix2latex
74 | :returns working_dir, latex:
75 | A tuple of the working_dir and the latex document as a string.
76 | The working_dir is None if the directory has been succesfully cleaned/removed.
77 |
78 | :raises IOError: if the expected output file was not created.
79 | :raises IOError: if removing files/directories in working_dir fails, this _will_ happend if working_dir suddenly contains folders.
80 |
81 | :raises subprocess.CalledProcessError: if the call to tex indicates a failure.
82 |
83 | :raises UserWarning: if working_dir is an existing directory and clean_latex=True.
84 | '''
85 |
86 | # Options
87 | if filename is None:
88 | filename = 'rendered'
89 | if filename.endswith(('.pdf', '.tex', '.png')):
90 | filename = filename[:-4]
91 |
92 | clean_latex = kwargs.pop('clean_latex', True)
93 | working_dir = kwargs.pop('working_dir', None)
94 | # in the case of working_dir=existing directory and clean_latex=True, keep a list of files that should not be cleaned
95 | existing_files = []
96 | if working_dir is None:
97 | working_dir = tempfile.mkdtemp(prefix='matrix2image')
98 | elif not os.path.exists(working_dir):
99 | os.makedirs(working_dir)
100 | else:
101 | if clean_latex:
102 | warnings.warn('The working directory already exists and clean_latex is True, I will try not to delete any of the files currently in working_dir=%s, but I make no promises.' % working_dir)
103 | # Note: there is a razy condition here, any files generated after this point will be deleted,
104 | # but at least we are not deleting family photos...
105 | # An alternative would be to clean only files known to be generated by pdflatex,
106 | # but odd tex compilers and packages can create odd files.
107 | existing_files = os.listdir(working_dir)
108 |
109 | latex_template = kwargs.pop('latex_template', _latex_template)
110 | latex_preamble = kwargs.pop('latex_preamble', _latex_preamble)
111 | latex_documentclass = kwargs.pop('latex_documentclass', _latex_documentclass)
112 | tex = kwargs.pop('tex', 'pdflatex')
113 | tex_options = kwargs.pop('tex_options', ['-interaction=nonstopmode', '-shell-escape'])
114 | output_format = kwargs.pop('output_format', '.pdf')
115 |
116 | # filenames
117 | tex_filename = os.path.basename(filename) + '.tex'
118 | tex_filename_full = os.path.join(working_dir, tex_filename)
119 | output_filename_final = filename + output_format
120 | output_filename_tmp = os.path.join(working_dir, os.path.basename(filename) + output_format)
121 |
122 | # call, do not write to file but get the latex-table as a string
123 | table = matrix2latex(matr, None, *args, **kwargs)
124 |
125 | # latex document
126 | latex = latex_template % (latex_documentclass, latex_preamble, table)
127 | with open(tex_filename_full, 'w') as f:
128 | f.write(latex)
129 |
130 | # compile document
131 | if tex is not None and tex != '': # I am sure there is a sexy way to write this test
132 | cmd = [tex]
133 | cmd.extend(tex_options)
134 | cmd.append(tex_filename)
135 | subprocess.check_call(cmd, cwd=working_dir)
136 |
137 | # we should now have a output_filename in the working directory
138 | if not(os.path.exists(output_filename_tmp)):
139 | raise IOError('Expected %s to exist after calling %s' % (output_filename_tmp, cmd))
140 |
141 | print(output_filename_tmp, output_filename_final)
142 | shutil.copyfile(output_filename_tmp, output_filename_final)
143 |
144 | if clean_latex:
145 | # only remove related files, then check if empty, then remove
146 | for p in os.listdir(working_dir):
147 | if p not in existing_files:
148 | try:
149 | os.remove(os.path.join(working_dir, p)) # raises OSError if p is a directory, which is unexpected.
150 | except OSError as e:
151 | raise OSError('Trouble removing file/directory:"%s", not cleaning. %s' % (p,e))
152 |
153 | # if empty:
154 | if len(os.listdir(working_dir)) == 0:
155 | shutil.rmtree(working_dir)
156 | working_dir = None # do not return path to non-existing directory
157 | else:
158 | warnings.warn('working_dir not empty, not cleaning %s' % working_dir)
159 |
160 | return working_dir, latex
161 |
162 | if __name__ == '__main__':
163 | # m = [[1, 2, 3], [3, 4, 5]]
164 | # cl = ["a", "b", "c"]
165 | # rl = ['d', 'e', 'f', 'g']
166 |
167 | # matrix2image(m, 'rendered', 'tabular', format="$%.2g$", alignment='lcr',
168 | # headerColumn=cl, caption="test", label="2", headerRow=rl)
169 |
170 | m = [[1, 1], [2, 4], [3, 9]]
171 | matrix2image(m, 'simpleExample', environments=['table', 'huge', 'center', 'tabular'], caption='hello',
172 | clean_latex=True, working_dir='tmp')
173 |
174 | # matrix2image(m, 'simpleExample_a4', environments=['table', 'huge', 'center', 'tabular'], caption='hello',
175 | # clean_latex=False, working_dir='tmp', latex_documentclass='\documentclass[a4paper]{article}'
176 | # )
177 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from distutils.core import setup
2 |
3 | setup(name='matrix2latex',
4 | version='1.9',
5 | description='Takes a python matrix or nested list and converts to a LaTeX table or matrix.',
6 | long_description=open('README.md').read(),
7 | author='obtitus',
8 | author_email='obtitus@gmail.com',
9 | url='https://code.google.com/p/matrix2latex/',
10 | packages=['matrix2latex'],
11 | )
12 |
--------------------------------------------------------------------------------
/simpleExample.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheChymera/matrix2latex/cd682ff87972f7182613a653495ca88fd75c33d6/simpleExample.png
--------------------------------------------------------------------------------
/src_matlab/fixEngineeringNotation.m:
--------------------------------------------------------------------------------
1 | function str=fixEngineeringNotation(str)
2 | reg = regexp(str, 'e[-+]\d\d');
3 | while ~isempty(reg)
4 | i = reg(1);
5 | str = sprintf('%s\\e{%+03d}%s', str(1:i-1), str2num(str(i+1:i+3)), str(i+4:end));
6 | reg = regexp(str, 'e-\d\d');
7 | end
8 | end
--------------------------------------------------------------------------------
/src_matlab/matrix2latex.m:
--------------------------------------------------------------------------------
1 | function table = matrix2latex(matrix, filename, varargin)
2 | %This file is part of matrix2latex.
3 | %
4 | %matrix2latex is free software: you can redistribute it and/or modify
5 | %it under the terms of the GNU General Public License as published by
6 | %the Free Software Foundation, either version 3 of the License, or
7 | %(at your option) any later version.
8 | %
9 | %matrix2latex is distributed in the hope that it will be useful,
10 | %but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | %MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | %GNU General Public License for more details.
13 | %
14 | %You should have received a copy of the GNU General Public License
15 | %along with matrix2latex. If not, see .
16 | %
17 | % A detailed pdf version of this documentation is available as doc.pdf
18 | %Takes a python matrix or nested list and converts to a LaTeX table or matrix.
19 | %Author: ob@cakebox.net, inspired by the work of koehler@in.tum.de who has written
20 | %a similar package for matlab
21 | %\url{http://www.mathworks.com/matlabcentral/fileexchange/4894-matrix2latex}
22 | %
23 | %The following packages and definitions are recommended in the latex preamble
24 | %% scientific notation, 1\e{9} will print as 1x10^9
25 | %\providecommand{\e}[1]{\ensuremath{\times 10^{#1}}}
26 | %\usepackage{amsmath} % needed for pmatrix
27 | %\usepackage{booktabs} % Fancy tables
28 | %...
29 | %\begin{document}
30 | %...
31 | % \input{table}
32 | % ...
33 | %\end{document}
34 | %
35 | %Arguments:
36 | %
37 | %matrix
38 | % A matrix or a cell array
39 | %
40 | %Filename
41 | % File to place output, extension .tex is added automatically. File can be included in a LaTeX
42 | % document by \input{filename}. Output will always be returned in a string. If filename is None,
43 | % empty string or not a string it is ignored.
44 | %
45 | %
46 | %**keywords
47 | %%environments
48 | % Use
49 | %matrix2latex(m, '', 'environmnet' {"align*", "pmatrix"}, ...) for matrix.
50 | % This will give
51 | % \begin{align*}
52 | % \begin{pmatrix}
53 | % 1 & 2 \\
54 | % 3 & 4
55 | % \end{pmatrix}
56 | % \end{align*}
57 | % Use
58 | %matrix2latex(m, 'test', 'environemnt', {"table", "center", "tabular"} ...) for table.
59 | % Table is default so given no arguments: table, center and tabular will be used.
60 | % The above command is then equivalent to \\
61 | %matrix2latex(m, 'test', ...)
62 | %
63 | %%headerRow
64 | % A row at the top used to label the columns.
65 | % Must be a list of strings.
66 | %
67 | %%headerColumn
68 | % A column used to label the rows.
69 | % Must be a list of strings
70 | %
71 | %%transpose
72 | %Flips the table around in case you messed up. Equivalent to
73 | %matrix2latex(m', ...)
74 | %if m is a matrix.
75 | %
76 | %caption
77 | % Use to define a caption for your table.
78 | % Inserts \caption after \begin{center},
79 | % note that without the center environment the caption is currently ignored.
80 | %
81 | %label
82 | %Used to insert \verb!\label{tab:...}! after \verb!\end{tabular}!
83 | %Default is filename without extension.
84 | %
85 | %
86 | %%format
87 | %Printf syntax format, e.g. $%.2f$. Default is $%g$.
88 | % This format is then used for all the elements in the table.
89 | %
90 | %%formatColumn
91 | %A list of printf-syntax formats, e.g. {$%.2f$, $%g$}
92 | %Must be of same length as the number of columns.
93 | %Format i is then used for column i.
94 | %This is useful if some of your data should be printed with more significant figures
95 | %than other parts
96 | %
97 | %%alignment
98 | %Used as an option when tabular is given as enviroment.
99 | %\begin{tabular}{alignment}
100 | %A latex alignment like c, l or r.
101 | %Can be given either as one per column e.g. "ccc".
102 | %Or if only a single character is given e.g. "c",
103 | %it will produce the correct amount depending on the number of columns.
104 | %Default is "r".
105 | %
106 | %Note that many of these options only has an effect when typesetting a table,
107 | %if the correct environment is not given the arguments are simply ignored.
108 | %
109 | if (rem(nargin,2) == 1 || nargin < 2)
110 | error('%s: Incorrect number of arguments', mfilename);
111 | end
112 |
113 | table = '';
114 | width = size(matrix, 2);
115 | height = size(matrix, 1);
116 |
117 | headerColumn = [];
118 | headerRow = [];
119 | if width ~= 0
120 | alignment = repmat('c', 1, width);
121 | else
122 | alignment = 'c';
123 | end
124 |
125 | format = '$%g$';
126 | textsize = [];
127 | caption = [];
128 | label = [];
129 | environment = {'table', 'center', 'tabular'};
130 |
131 | for j=1:2:(nargin-2)
132 | pname = varargin{j};
133 | pval = varargin{j+1};
134 | if strcmpi(pname, 'headerColumn')
135 | headerColumn = pval;
136 | if isnumeric(headerColumn)
137 | headerColumn = cellstr(num2str(headerColumn(:)));
138 | end
139 | alignment = ['r', alignment];
140 | elseif strcmpi(pname, 'headerRow')
141 | headerRow = pval;
142 | if isnumeric(headerRow)
143 | headerRow = cellstr(num2str(headerRow(:)));
144 | end
145 | elseif strcmpi(pname, 'alignment')
146 | %okAlignment = {'l', 'c', 'r'};
147 | %if ~isempty(strmatch(pval, okAlignment, 'exact'))
148 | if length(pval) == 1
149 | alignment = repmat(pval, 1, width);
150 | else
151 | alignment = pval;
152 | end
153 | elseif strcmpi(pname, 'format')
154 | format = lower(pval);
155 | elseif strcmpi(pname, 'formatColumns')
156 | if size(pval) ~= size(matrix, 2)
157 | error('%s: Format columns has wrong length %d', mfilename, size(pval))
158 | else
159 | format = pval;
160 | end
161 | elseif strcmpi(pname, 'size')
162 | okSize = {'tiny', 'scriptsize', 'footnotesize', 'small', 'normalsize', 'large', 'Large', ...
163 | 'LARGE', 'huge', 'Huge'};
164 | if ~isempty(strmatch(pval, okSize, 'exact'))
165 | textsize = pval;
166 | else
167 | warning('%s: Unknown size %s', mfilename, pval)
168 | end
169 | elseif strcmpi(pname, 'caption')
170 | caption = pval;
171 | elseif strcmpi(pname, 'label')
172 | label = ['tab:', pval];
173 | elseif strcmpi(pname, 'transpose')
174 | if pval
175 | matrix = matrix';
176 | varargin{j+1} = false; % set transpose to false
177 | table = matrix2latex(matrix, filename, varargin{:});
178 | return;
179 | end
180 | elseif strcmpi(pname, 'environment')
181 | environment = pval;
182 | else
183 | error('%s: unknown parameter name %s', mfilename, pname)
184 | end
185 | end
186 |
187 | if filename ~= 1
188 | if (length(filename) < 4) || ~strcmp(filename(end-3:end), '.tex')
189 | filename = [filename, '.tex'];
190 | end
191 | %fid = fopen(filename, 'w');
192 | if isempty(label)
193 | label = ['tab:', filename(1:end-4)];
194 | end
195 | %else
196 | %fid = 1; % fprintf will print to standard output
197 | end
198 |
199 | %if isempty(matrix)
200 | % return;
201 | %end
202 |
203 | if isnumeric(matrix)
204 | matrix = num2cell(matrix);
205 | for h=1:height
206 | for w=1:width
207 | if isnan(matrix{h, w})
208 | matrix{h, w} = '{-}';
209 | elseif matrix{h, w} == inf
210 | matrix{h, w} = '$\infty$';
211 | elseif matrix{h, w} == -inf
212 | matrix{h, w} = '$-\infty$';
213 | elseif(~isempty(format))
214 | if iscellstr(format) % if formatColumns
215 | matrix{h, w} = num2str(matrix{h, w}, format{w});
216 | end
217 | matrix{h, w} = num2str(matrix{h, w}, format);
218 | else
219 | matrix{h, w} = num2str(matrix{h, w});
220 | end
221 | matrix{h, w} = fixEngineeringNotation(matrix{h, w});
222 | end
223 | end
224 | end
225 |
226 | if(~isempty(textsize))
227 | table = [table, sprintf('\\begin{%s}\n', textsize)];
228 | end
229 |
230 | for ix = 1:length(environment)
231 | e = environment{ix};
232 | table = [table, sprintf(repmat('\t',1,ix-1))];
233 | if strcmpi(e, 'table')
234 | table = [table, sprintf('\\begin{%s}[htp]\n', e)];
235 | elseif strcmpi(e, 'tabular')
236 | table = [table, sprintf('\\begin{%s}{', e)];
237 | table = [table, sprintf('%s}\n', alignment)];
238 |
239 | table = [table, sprintf(repmat('\t',1,ix-1))];
240 | table = [table, sprintf('\\toprule\n')];
241 | elseif strcmpi(e, 'center')
242 | table = [table, sprintf('\\begin{%s}\n', e)];
243 | if ~isempty(caption)
244 | table = [table, sprintf('\t\\caption{%s}\n', caption)];
245 | end
246 | if ~isempty(label)
247 | table = [table, sprintf('\t\\label{%s}\n', label)];
248 | end
249 | else
250 | table = [table, sprintf('\\begin{%s}\n', e)];
251 | end
252 | end
253 |
254 | if(~isempty(headerRow))
255 | table = [table, sprintf('\t\t\t')];
256 | if ~isempty(headerColumn) && ~isempty(headerRow) && ...
257 | length(headerRow) == width
258 | table = [table, sprintf('{} & ')];
259 | end
260 | for w=1:length(headerRow)-1
261 | table = [table, sprintf('{%s} & ', headerRow{w})];
262 | %\textbf{%s}&', headerRow{w})];
263 | end
264 | if width ~= length(headerRow)
265 | table = [table, sprintf('{%s}\\\\\n', ...
266 | headerRow{width+1})];
267 | else
268 | table = [table, sprintf('{%s}\\\\\n', ...
269 | headerRow{width})];
270 | end
271 | table = [table, sprintf('\t\t\t\\midrule\n')];
272 | end
273 |
274 | for h=1:height
275 | table = [table, sprintf(repmat('\t',1,height))];
276 | if(~isempty(headerColumn))
277 | table = [table, sprintf('{%s} & ', headerColumn{h})];
278 | end
279 | for w=1:width-1
280 | table = [table, sprintf('%s & ', matrix{h, w})];
281 | end
282 | table = [table, sprintf('%s\\\\\n', matrix{h, width})];
283 | end
284 |
285 | for ix = length(environment):-1:1
286 | e = environment{ix};
287 | table = [table, sprintf(repmat('\t',1,ix-1))];
288 | if strcmpi(e, 'tabular')
289 | table = [table, sprintf('\\bottomrule\n')];
290 | table = [table, sprintf(repmat('\t',1,ix-1))];
291 | end
292 | table = [table, sprintf('\\end{%s}\n', e)];
293 | end
294 |
295 | if(~isempty(textsize))
296 | table = [table, sprintf('\\end{%s}', textsize)];
297 | end
298 |
299 | if ~isempty(filename) % if we should write to file
300 | fid = fopen(filename, 'w');
301 | fwrite(fid, table);
302 | fclose(fid);
303 | end
--------------------------------------------------------------------------------
/test/README.txt:
--------------------------------------------------------------------------------
1 | This folder contains simple test scripts for the different languages, the result is compared to the test.tex file.
2 |
--------------------------------------------------------------------------------
/test/test.m:
--------------------------------------------------------------------------------
1 | %This file is part of matrix2latex.
2 | %
3 | %matrix2latex is free software: you can redistribute it and/or modify
4 | %it under the terms of the GNU General Public License as published by
5 | %the Free Software Foundation, either version 3 of the License, or
6 | %(at your option) any later version.
7 | %
8 | %matrix2latex is distributed in the hope that it will be useful,
9 | %but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | %MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | %GNU General Public License for more details.
12 | %
13 | %You should have received a copy of the GNU General Public License
14 | %along with matrix2latex. If not, see .
15 |
16 | % tests for matrix2latex.m
17 | function test()
18 | path(path, '../src_matlab')
19 | m = [1, 2, 3; 4, 5, 6];
20 |
21 | function assertLine(output, b, lineNr)
22 | output = textscan(output, '%s', 'delimiter', '\n');
23 | a = output{1}{lineNr};
24 | a = strtrim(a);
25 | b = strtrim(b);
26 | if ~strcmp(a, b)
27 | error('Invalid! "%s" ~= "%s"', a, b)
28 | end
29 | end
30 | function assertEqual(output, name)
31 | fid = fopen('test.tex');
32 | tline = fgetl(fid);
33 | found = false;
34 | answer = '';
35 | output = textscan(output, '%s', 'delimiter', '\n');
36 | output = output{1};
37 | ix = 1;
38 | while ischar(tline)
39 | if strcmp(tline, ['%%%', name])
40 | found = true;
41 | elseif regexpi(tline, '%%%', 'start')
42 | found = false;
43 | elseif found
44 | answer = [answer, tline]; % append
45 | a = strtrim(output{ix});
46 | b = strtrim(tline);
47 | if ~strcmp(a, b)
48 | output
49 | answer
50 | error('Invalid! "%s" ~= "%s"', a, b)
51 | end
52 | ix = ix + 1;
53 | end
54 | tline = fgetl(fid); % prepare next loop
55 | end
56 | fclose(fid);
57 | end
58 |
59 | function test_simple()
60 | t = matrix2latex(m, '');
61 | assertEqual(t, 'simple');
62 | end
63 | test_simple()
64 |
65 | function test_transpose1()
66 | t = matrix2latex(m, '', 'transpose', true);
67 | assertEqual(t, 'transpose1');
68 | end
69 | test_transpose1()
70 |
71 | function test_transpose2()
72 | hr = {'a', 'b'};
73 | t = matrix2latex(m, '', 'transpose', true, 'headerRow', hr);
74 | assertEqual(t, 'transpose2');
75 | end
76 | test_transpose2()
77 |
78 | function test_file()
79 | matrix2latex(m, 'tmp.tex');
80 | fid = fopen('tmp.tex');
81 | content = fread(fid, '*char');
82 | fclose(fid);
83 | assertEqual(content, 'file');
84 | end
85 | test_file()
86 |
87 | function test_environment1()
88 | t = matrix2latex(m, '', 'environment', {'table', 'center', 'tabular'});
89 | assertEqual(t, 'environment1');
90 | end
91 | test_environment1()
92 |
93 | function test_environment2()
94 | t = matrix2latex(m, '', 'environment', {'foo', 'bar'});
95 | assertEqual(t, 'environment2');
96 | end
97 | test_environment2()
98 |
99 | function test_labels1()
100 | cl = {'a', 'b'};
101 | rl = {'c', 'd', 'e'};
102 | t = matrix2latex(m, '', 'headerRow', rl, 'headerColumn', cl);
103 | assertEqual(t, 'labels1');
104 | end
105 | test_labels1()
106 |
107 | function test_labels2()
108 | % only difference from above test is 'names', note how above function
109 | % handles having too few rowLabels
110 | cl = {'a', 'b'};
111 | rl = {'names', 'c', 'd', 'e'};
112 | t = matrix2latex(m, '', 'headerRow', rl, 'headerColumn', cl);
113 | assertEqual(t, 'labels2');
114 | end
115 | test_labels2()
116 |
117 | % Not possible in matlab
118 | %function test_labels3()
119 | % % pass in environment as dictionary
120 | % e = dict()
121 | % e['columnLabels'] = ['a', 'b']
122 | % e['rowLabels'] = ['names', 'c', 'd', 'e']
123 | % t = matrix2latex(m, None, **e)
124 | % assertEqual(t, 'labels3')
125 | %test_labels3()
126 |
127 | function test_labels4()
128 | t = matrix2latex(m, '', 'caption', 'Hello', 'label', 'la');
129 | assertEqual(t, 'labels4');
130 | end
131 | test_labels4()
132 |
133 | function test_alignment1()
134 | t = matrix2latex(m, '', 'alignment', 'r');
135 | assertLine(t, '\begin{tabular}{rrr}', 3);
136 | end
137 | test_alignment1()
138 |
139 | function test_alignment2()
140 | cl = {'a', 'b'};
141 | rl = {'names', 'c', 'd', 'e'};
142 | t = matrix2latex(m, '', 'alignment', 'r', 'headerColumn', cl, 'headerRow', rl);
143 | assertLine(t, '\begin{tabular}{rrrr}', 3);
144 | end
145 | test_alignment2()
146 |
147 | function test_alignment2b()
148 | rl = {'a', 'b'};
149 | cl = {'names', 'c', 'd', 'e'};
150 | t = matrix2latex(m, '', 'alignment', 'r', 'headerColumn', cl, 'headerRow', ...
151 | rl, 'transpose', true);
152 | assertLine(t, '\begin{tabular}{rrr}', 3);
153 | end
154 | test_alignment2b()
155 |
156 | function test_alignment3()
157 | t = matrix2latex(m, '', 'alignment', 'rcl');
158 | assertLine(t, '\begin{tabular}{rcl}', 3);
159 | end
160 | test_alignment3()
161 |
162 | function test_alignment4()
163 | t = matrix2latex(m, '', 'alignment', 'rcl', 'headerColumn', {'a', 'b'});
164 | assertLine(t, '\begin{tabular}{rrcl}', 3);
165 | end
166 | test_alignment4()
167 |
168 | function test_alignment5()
169 | t = matrix2latex(m, '', 'alignment', 'r|c|l', 'headerColumn', {'a', 'b'});
170 | assertLine(t, '\begin{tabular}{rr|c|l}', 3);
171 | end
172 | test_alignment5()
173 |
174 | function test_alignment_withoutTable()
175 | t = matrix2latex(m, '', 'environment', {'align*', 'pmatrix'}, ...
176 | 'format', '$%.2f$', 'alignment', 'c');
177 | assertEqual(t, 'alignment_withoutTable');
178 | end
179 | test_alignment_withoutTable()
180 |
181 | % numpy, not an issue
182 | %function test_numpy()
183 | % try:
184 | % import numpy as np
185 | % for a in (np.matrix, np.array):
186 | % t = matrix2latex(a(m), None, 'align*', 'pmatrix')
187 | % assertEqual(t, 'numpy')
188 | % % Systems without numpy raises import error,
189 | % % pypy raises attribute since matrix is not implemented, this is ok.
190 | % except (ImportError, AttributeError):
191 | % pass
192 |
193 | function test_string()
194 | t = matrix2latex({'a', 'b', '1'; '1', '2', '3'}, '', 'format', '%s');
195 | assertEqual(t, 'string');
196 | end
197 | test_string()
198 |
199 | function test_none()
200 | m = [1,nan,nan; 2,2,1; 2,1,2];
201 | t = matrix2latex(m, '');
202 | assertEqual(t, 'none');
203 |
204 | t3 = matrix2latex(m, '', 'format', '$%d$');
205 | assertEqual(t3, 'none');
206 | end
207 | test_none()
208 |
209 | % numpy, not an issue
210 | %function test_infty1()
211 | % try:
212 | % import numpy as np
213 | % m = [[1,np.inf,float('inf')], [2,2,float('-inf')], [-np.inf,1,2]]
214 | % t = matrix2latex(m)
215 | % assertEqual(t, 'infty1')
216 | % except (ImportError, AttributeError):
217 | % pass
218 |
219 | function test_infty2()
220 | m = [1,inf, inf;, 2,2, -inf; -inf,1,2];
221 | t = matrix2latex(m, '');
222 | assertEqual(t, 'infty1');
223 | end
224 | test_infty2()
225 |
226 | function test_multicolumn()
227 | hr = {{'Item', 'Item', 'Item', 'Item', 'Price', 'Price', 'test', '', 'Money', 'Money', 'Money'},
228 | {'Animal', 'Description', '(\$)'}}
229 | t = matrix2latex(m, '', 'headerRow', hr)
230 | assertLine(t, '\multicolumn{4}{c}{Item} & \multicolumn{2}{c}{Price} & test & & \multicolumn{3}{c}{Money}\\\cmidrule(r){1-4}\cmidrule(r){5-6}\cmidrule(r){9-11}', 5)
231 | end
232 | %test_multicolumn()
233 |
234 | function test_empty()
235 | t = matrix2latex([], '');
236 | assertEqual(t, 'empty');
237 | end
238 | test_empty()
239 |
240 | function test_nicefloat()
241 | t = matrix2latex([123456e-10; 1e-15;12345e5], '');
242 | assertEqual(t, 'nicefloat');
243 | end
244 | test_nicefloat();
245 |
246 | function test_nicefloat_4g()
247 | t = matrix2latex([123456e-10; 1e-15; 12345e5], '', 'format', '$%.4g$');
248 | assertEqual(t, 'nicefloat_4g');
249 | end
250 | test_nicefloat_4g();
251 |
252 | function test_non_rectangular()
253 | t = matrix2latex([1, 2;
254 | 1, 2, 3;
255 | 5]); % not legal matlab, no need
256 | % to test/support.
257 | assertEqual(t, 'nicefloat_4g');
258 | end
259 | %test_non_rectangular();
260 |
261 | % end of file:
262 | end
--------------------------------------------------------------------------------
/test/test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """This file is part of matrix2latex.
3 |
4 | matrix2latex is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | matrix2latex is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with matrix2latex. If not, see .
16 | """
17 |
18 | # tests for matrix2latex.py
19 | import os
20 | import sys
21 |
22 | sys.path.insert(0, '../')
23 | from matrix2latex import matrix2latex
24 |
25 | try:
26 | from test_syntaxError import *
27 | except SyntaxError:
28 | pass
29 |
30 | from test_util import *
31 |
32 | m = [[1, 2, 3], [4, 5, 6]]
33 |
34 |
35 | def test_simple():
36 | t = matrix2latex(m)
37 | assertEqual(t, "simple")
38 |
39 | def test_transpose1():
40 | t = matrix2latex(m, transpose=True)
41 | assertEqual(t, "transpose1")
42 |
43 | def test_transpose2():
44 | cl = ["a", "b"]
45 | t = matrix2latex(m, transpose=True, headerRow=cl)
46 | assertEqual(t, "transpose2")
47 |
48 | def test_file():
49 | matrix2latex(m, 'tmp.tex')
50 | f = open('tmp.tex')
51 | content = f.read()
52 | f.close()
53 | assertEqual(content, "file")
54 |
55 | def test_environment1():
56 | t = matrix2latex(m, None, "table", "center", "tabular")
57 | assertEqual(t, "environment1")
58 |
59 | def test_environment2():
60 | t = matrix2latex(m, None, "foo", "bar")
61 | assertEqual(t, "environment2")
62 |
63 | def test_labels1():
64 | cl = ["a", "b"]
65 | rl = ["c", "d", "e"]
66 | t = matrix2latex(m, None, headerColumn=cl, headerRow=rl)
67 | assertEqual(t, "labels1")
68 |
69 | def test_labels2():
70 | # only difference from above test is names, note how above function
71 | # handles having too few headerRow
72 | cl = ["a", "b"]
73 | rl = ["names", "c", "d", "e"]
74 | t = matrix2latex(m, None, headerColumn=cl, headerRow=rl)
75 | assertEqual(t, "labels2")
76 |
77 | def test_labels3():
78 | # pass in environment as dictionary
79 | e = dict()
80 | e['headerColumn'] = ["a", "b"]
81 | e['headerRow'] = ["names", "c", "d", "e"]
82 | t = matrix2latex(m, None, **e)
83 | assertEqual(t, "labels3")
84 |
85 | def test_labels4():
86 | t = matrix2latex(m, None, caption="Hello", label="la")
87 | assertEqual(t, "labels4")
88 |
89 | def test_alignment1():
90 | t = matrix2latex(m, alignment='r')
91 | t = t.split('\n')[2].strip()
92 | assert t == r"\begin{tabular}{rrr}", t
93 |
94 | def test_alignment2():
95 | cl = ["a", "b"]
96 | rl = ["names", "c", "d", "e"]
97 | t = matrix2latex(m, alignment='r', headerColumn=cl, headerRow = rl)
98 | t = t.split('\n')[2].strip()
99 | assert t == r"\begin{tabular}{rrrr}", t
100 |
101 | def test_alignment2b():
102 | rl = ["a", "b"]
103 | cl = ["names", "c", "d", "e"]
104 | t = matrix2latex(m, alignment='r', headerColumn=cl, headerRow = rl, transpose=True)
105 | t = t.split('\n')[2].strip()
106 | assert t == r"\begin{tabular}{rrr}", t
107 |
108 | def test_alignment3():
109 | t = matrix2latex(m, alignment='rcl')
110 | t = t.split('\n')[2].strip()
111 | assert t == r"\begin{tabular}{rcl}", t
112 |
113 | def test_alignment4():
114 | t = matrix2latex(m, alignment='rcl', headerColumn=["a", "b"])
115 | t = t.split('\n')[2].strip() # pick out only third line
116 | assert t == r"\begin{tabular}{rrcl}", t
117 |
118 | def test_alignment5():
119 | t = matrix2latex(m, alignment='r|c|l', headerColumn=["a", "b"])
120 | t = t.split('\n')[2].strip() # pick out only third line
121 | assert t == r"\begin{tabular}{rr|c|l}", t
122 |
123 | def test_alignment_withoutTable():
124 | t = matrix2latex(m, None, "align*", "pmatrix", format="$%.2f$", alignment='c')
125 | assertEqual(t, "alignment_withoutTable")
126 |
127 | def test_numpy():
128 | try:
129 | import numpy as np
130 | for a in (np.matrix, np.array):
131 | t = matrix2latex(a(m), None, "align*", "pmatrix")
132 | assertEqual(t, "numpy")
133 | # Systems without numpy raises import error,
134 | # pypy raises attribute since matrix is not implemented, this is ok.
135 | except (ImportError, AttributeError):
136 | pass
137 |
138 | def test_string():
139 | t = matrix2latex([['a', 'b', '1'], ['1', '2', '3']], format='%s')
140 | assertEqual(t, "string")
141 |
142 | def test_none():
143 | m = [[1,None,None], [2,2,1], [2,1,2]]
144 | t = matrix2latex(m)
145 | assertEqual(t, "none")
146 |
147 | m2 = [[1,float('NaN'),float('NaN')], [2,2,1], [2,1,2]]
148 | t2 = matrix2latex(m)
149 | assertEqual(t2, "none")
150 |
151 | t3 = matrix2latex(m, format='$%d$')
152 | assertEqual(t3, "none")
153 |
154 | def test_infty1():
155 | try:
156 | import numpy as np
157 | m = [[1,np.inf,float('inf')], [2,2,float('-inf')], [-np.inf,1,2]]
158 | t = matrix2latex(m)
159 | assertEqual(t, "infty1")
160 | except (ImportError, AttributeError):
161 | pass
162 |
163 | def test_infty2():
164 | # same as above but without numpy
165 | inf = float('inf')
166 | m = [[1,inf,float('inf')], [2,2,float('-inf')], [-inf,1,2]]
167 | t = matrix2latex(m)
168 | assertEqual(t, "infty1")
169 |
170 | def test_multicolumn():
171 | hr = [['Item', 'Item', 'Item', 'Item', 'Price', 'Price', 'test', '', 'Money', 'Money', 'Money'],
172 | ['Animal', 'Description', '(\$)']]
173 | t = matrix2latex(m, headerRow=hr)
174 | t = t.split('\n')[4].strip() # pick out only third line
175 | assert t == r"\multicolumn{4}{c}{Item} & \multicolumn{2}{c}{Price} & {test} & {} & \multicolumn{3}{c}{Money}\\\cmidrule(r){1-4}\cmidrule(r){5-6}\cmidrule(r){9-11}", t
176 |
177 | def test_empty():
178 | t = matrix2latex([])
179 | assertEqual(t, 'empty')
180 |
181 | def test_nicefloat():
182 | t = matrix2latex([123456e-10, 1e-15, 12345e5])
183 | assertEqual(t, 'nicefloat')
184 |
185 | def test_nicefloat_4g():
186 | t = matrix2latex([123456e-10, 1e-15, 12345e5], format='$%.4g$')
187 | assertEqual(t, 'nicefloat_4g')
188 |
189 | def test_non_rectangular():
190 | """Test a nested list with 'missing' elements"""
191 | t = matrix2latex([[1,2],
192 | [1, 2, 3],
193 | [5]])
194 | assertEqual(t, 'non_rectangular')
195 |
196 |
197 | def test_pandas_dataframe():
198 | try:
199 | import pandas as pd
200 | import numpy as np
201 | m = [[1, 1], [2, 4], [3, 9]] # python nested list
202 | m = pd.DataFrame(m)
203 | #m = pd.DataFrame.from_csv('http://chymera.eu/data/test/r_data.csv', parse_dates=False, index_col=False)
204 | # print('PANDAS\n', m)
205 | # print('PANDAS\n', m.to_records())
206 | t = matrix2latex(m)
207 | assertEqual(t, "pandas_dataframe")
208 | except ImportError:
209 | pass
210 |
211 | def test_pandas_series():
212 | try:
213 | import pandas as pd
214 | import numpy as np
215 | s = pd.Series([2, 4, 2, 42, 5], index=['a', 'b', 'c', 'd', 'e'])
216 | # print('PANDAS\n', s)
217 | # print('PANDAS\n', s.to_dict(), s.tolist(), hasattr(s, 'to_dict'))
218 | t = matrix2latex(s)
219 | # print('pandas Series', t)
220 | t2 = matrix2latex(pd.DataFrame(s))
221 | # print('pandas DataFrame', t2)
222 | assertEqual(t, "pandas_series")
223 | assertEqual(t2, "pandas_series_dataFrame")
224 | except ImportError:
225 | pass
226 |
227 | def test_pandas_columns():
228 | try:
229 | import pandas as pd
230 | import numpy as np
231 | d = {'one' : pd.Series([1., 2., 3.], index=['a', 'b', 'c']),
232 | 'two' : pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])}
233 | df = pd.DataFrame(d)
234 | t = matrix2latex(df)
235 | # print('pandas', t, df.to_records())
236 | assertEqual(t, "pandas_columns")
237 |
238 | t = matrix2latex(df, headerRow=None, headerColumn=None)
239 | assertEqual(t, "pandas_columns_noHeaders")
240 | except ImportError:
241 | pass
242 |
243 | # Pandas Panel now depricated, remove test
244 | # def test_pandas_Panel():
245 | # try:
246 | # import pandas as pd
247 | # import numpy as np
248 | # panel = pd.Panel(np.random.randn(3, 5, 4), items=['one', 'two', 'three'],
249 | # major_axis=pd.date_range('1/1/2000', periods=5),
250 | # minor_axis=['a', 'b', 'c', 'd'])
251 | # frame = panel.to_frame()
252 | # # TODO: Needs support for multiple headerColumns
253 | # #t = matrix2latex(frame)
254 | # #print(t)
255 | # #assert False
256 | # except ImportError:
257 | # pass
258 |
259 | if __name__ == '__main__':
260 | import test
261 | for d in sorted(test.__dict__):
262 | if 'test_' in d:
263 | print('RUNNING', d)
264 | eval(d+'()')
265 |
--------------------------------------------------------------------------------
/test/test.tex:
--------------------------------------------------------------------------------
1 | %%%simple
2 | \begin{table}[htp]
3 | \begin{center}
4 | \begin{tabular}{ccc}
5 | \toprule
6 | $1$ & $2$ & $3$\\
7 | $4$ & $5$ & $6$\\
8 | \bottomrule
9 | \end{tabular}
10 | \end{center}
11 | \end{table}
12 | %%%transpose1
13 | \begin{table}[htp]
14 | \begin{center}
15 | \begin{tabular}{cc}
16 | \toprule
17 | $1$ & $4$\\
18 | $2$ & $5$\\
19 | $3$ & $6$\\
20 | \bottomrule
21 | \end{tabular}
22 | \end{center}
23 | \end{table}
24 | %%%transpose2
25 | \begin{table}[htp]
26 | \begin{center}
27 | \begin{tabular}{cc}
28 | \toprule
29 | {a} & {b}\\
30 | \midrule
31 | $1$ & $4$\\
32 | $2$ & $5$\\
33 | $3$ & $6$\\
34 | \bottomrule
35 | \end{tabular}
36 | \end{center}
37 | \end{table}
38 | %%%file
39 | \begin{table}[htp]
40 | \begin{center}
41 | \label{tab:tmp}
42 | \begin{tabular}{ccc}
43 | \toprule
44 | $1$ & $2$ & $3$\\
45 | $4$ & $5$ & $6$\\
46 | \bottomrule
47 | \end{tabular}
48 | \end{center}
49 | \end{table}
50 | %%%environment1
51 | \begin{table}[htp]
52 | \begin{center}
53 | \begin{tabular}{ccc}
54 | \toprule
55 | $1$ & $2$ & $3$\\
56 | $4$ & $5$ & $6$\\
57 | \bottomrule
58 | \end{tabular}
59 | \end{center}
60 | \end{table}
61 | %%%environment2
62 | \begin{foo}
63 | \begin{bar}
64 | $1$ & $2$ & $3$\\
65 | $4$ & $5$ & $6$\\
66 | \end{bar}
67 | \end{foo}
68 | %%%labels1
69 | \begin{table}[htp]
70 | \begin{center}
71 | \begin{tabular}{rccc}
72 | \toprule
73 | {} & {c} & {d} & {e}\\
74 | \midrule
75 | {a} & $1$ & $2$ & $3$\\
76 | {b} & $4$ & $5$ & $6$\\
77 | \bottomrule
78 | \end{tabular}
79 | \end{center}
80 | \end{table}
81 | %%%labels2
82 | \begin{table}[htp]
83 | \begin{center}
84 | \begin{tabular}{rccc}
85 | \toprule
86 | {names} & {c} & {d} & {e}\\
87 | \midrule
88 | {a} & $1$ & $2$ & $3$\\
89 | {b} & $4$ & $5$ & $6$\\
90 | \bottomrule
91 | \end{tabular}
92 | \end{center}
93 | \end{table}
94 | %%%labels3
95 | \begin{table}[htp]
96 | \begin{center}
97 | \begin{tabular}{rccc}
98 | \toprule
99 | {names} & {c} & {d} & {e}\\
100 | \midrule
101 | {a} & $1$ & $2$ & $3$\\
102 | {b} & $4$ & $5$ & $6$\\
103 | \bottomrule
104 | \end{tabular}
105 | \end{center}
106 | \end{table}
107 | %%%labels4
108 | \begin{table}[htp]
109 | \begin{center}
110 | \caption{Hello}
111 | \label{tab:la}
112 | \begin{tabular}{ccc}
113 | \toprule
114 | $1$ & $2$ & $3$\\
115 | $4$ & $5$ & $6$\\
116 | \bottomrule
117 | \end{tabular}
118 | \end{center}
119 | \end{table}
120 | %%%alignment_withoutTable
121 | \begin{align*}
122 | \begin{pmatrix}
123 | $1.00$ & $2.00$ & $3.00$\\
124 | $4.00$ & $5.00$ & $6.00$\\
125 | \end{pmatrix}
126 | \end{align*}
127 | %%%numpy
128 | \begin{align*}
129 | \begin{pmatrix}
130 | $1$ & $2$ & $3$\\
131 | $4$ & $5$ & $6$\\
132 | \end{pmatrix}
133 | \end{align*}
134 | %%%string
135 | \begin{table}[htp]
136 | \begin{center}
137 | \begin{tabular}{ccc}
138 | \toprule
139 | a & b & 1\\
140 | 1 & 2 & 3\\
141 | \bottomrule
142 | \end{tabular}
143 | \end{center}
144 | \end{table}
145 | %%%none
146 | \begin{table}[htp]
147 | \begin{center}
148 | \begin{tabular}{ccc}
149 | \toprule
150 | $1$ & {-} & {-}\\
151 | $2$ & $2$ & $1$\\
152 | $2$ & $1$ & $2$\\
153 | \bottomrule
154 | \end{tabular}
155 | \end{center}
156 | \end{table}
157 | %%%infty1
158 | \begin{table}[htp]
159 | \begin{center}
160 | \begin{tabular}{ccc}
161 | \toprule
162 | $1$ & $\infty$ & $\infty$\\
163 | $2$ & $2$ & $-\infty$\\
164 | $-\infty$ & $1$ & $2$\\
165 | \bottomrule
166 | \end{tabular}
167 | \end{center}
168 | \end{table}
169 | %%%empty
170 | \begin{table}[htp]
171 | \begin{center}
172 | \begin{tabular}{c}
173 | \toprule
174 | \bottomrule
175 | \end{tabular}
176 | \end{center}
177 | \end{table}
178 | %%%nicefloat
179 | \begin{table}[htp]
180 | \begin{center}
181 | \begin{tabular}{c}
182 | \toprule
183 | $1.23456\e{-05}$\\
184 | $1\e{-15}$\\
185 | $1.2345\e{+09}$\\
186 | \bottomrule
187 | \end{tabular}
188 | \end{center}
189 | \end{table}
190 | %%%nicefloat_4g
191 | \begin{table}[htp]
192 | \begin{center}
193 | \begin{tabular}{c}
194 | \toprule
195 | $1.235\e{-05}$\\
196 | $1\e{-15}$\\
197 | $1.234\e{+09}$\\
198 | \bottomrule
199 | \end{tabular}
200 | \end{center}
201 | \end{table}
202 | %%%non_rectangular
203 | \begin{table}[htp]
204 | \begin{center}
205 | \begin{tabular}{ccc}
206 | \toprule
207 | $1$ & $2$ & {-}\\
208 | $1$ & $2$ & $3$\\
209 | $5$ & {-} & {-}\\
210 | \bottomrule
211 | \end{tabular}
212 | \end{center}
213 | \end{table}
214 | %%%format_formatColumn_Warning
215 | \begin{table}[htp]
216 | \begin{center}
217 | \begin{tabular}{cc}
218 | \toprule
219 | 1\e{+15} & 1.23456\e{+15}\\
220 | \bottomrule
221 | \end{tabular}
222 | \end{center}
223 | \end{table}
224 | %%%pandas_dataframe
225 | \begin{table}[htp]
226 | \begin{center}
227 | \begin{tabular}{rcc}
228 | \toprule
229 | {} & {0} & {1}\\
230 | \midrule
231 | {0} & $1$ & $1$\\
232 | {1} & $2$ & $4$\\
233 | {2} & $3$ & $9$\\
234 | \bottomrule
235 | \end{tabular}
236 | \end{center}
237 | \end{table}
238 | %%%pandas_columns
239 | \begin{table}[htp]
240 | \begin{center}
241 | \begin{tabular}{rcc}
242 | \toprule
243 | {} & {one} & {two}\\
244 | \midrule
245 | {a} & $1$ & $1$\\
246 | {b} & $2$ & $2$\\
247 | {c} & $3$ & $3$\\
248 | {d} & {-} & $4$\\
249 | \bottomrule
250 | \end{tabular}
251 | \end{center}
252 | \end{table}
253 | %%%pandas_columns_noHeaders
254 | \begin{table}[htp]
255 | \begin{center}
256 | \begin{tabular}{cc}
257 | \toprule
258 | $1$ & $1$\\
259 | $2$ & $2$\\
260 | $3$ & $3$\\
261 | {-} & $4$\\
262 | \bottomrule
263 | \end{tabular}
264 | \end{center}
265 | \end{table}
266 | %%%pandas_series
267 | \begin{table}[htp]
268 | \begin{center}
269 | \begin{tabular}{rc}
270 | \toprule
271 | {a} & $2$\\
272 | {b} & $4$\\
273 | {c} & $2$\\
274 | {d} & $42$\\
275 | {e} & $5$\\
276 | \bottomrule
277 | \end{tabular}
278 | \end{center}
279 | \end{table}
280 | %%%pandas_series_dataFrame
281 | \begin{table}[htp]
282 | \begin{center}
283 | \begin{tabular}{rc}
284 | \toprule
285 | {} & {0}\\
286 | \midrule
287 | {a} & $2$\\
288 | {b} & $4$\\
289 | {c} & $2$\\
290 | {d} & $42$\\
291 | {e} & $5$\\
292 | \bottomrule
293 | \end{tabular}
294 | \end{center}
295 | \end{table}
--------------------------------------------------------------------------------
/test/testVersionCompatibility.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Tries to run testsuit with python version
3 | # python.
4 | # where major and minor varies between 2-3 and 0-10.
5 | # (expand this if you have say python1.11 or python4.0)
6 | # Now also looks for pypy implementations.
7 | """This file is part of matrix2latex.
8 |
9 | matrix2latex is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | matrix2latex is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with matrix2latex. If not, see .
21 | """
22 | import sys
23 | sys.path.insert(0, '../')
24 |
25 | import os
26 | from subprocess import call
27 | from matrix2latex import matrix2latex
28 |
29 | table = list()
30 | pythonVersions = list()
31 |
32 | def test(python, table, pythonVersions):
33 | ret = call([python, "test.py"], stdout=open(os.devnull, "w"))
34 |
35 | print(python)
36 | print(ret)
37 | if ret == 0: table.append(["True"])
38 | else: table.append(["False"])
39 | pythonVersions.append(python)
40 |
41 |
42 | for major in range(2, 3+1):
43 | for minor in range(0, 10):
44 | python = 'python%d.%d' % (major, minor)
45 |
46 | if call(python + " -c ''", shell=True, stderr=open(os.devnull, "w")) == 0:
47 | test(python, table, pythonVersions)
48 |
49 | if call("pypy-c" + " -c 'pass'", shell=True, stderr=open(os.devnull, "w")) == 0:
50 | test("pypy-c", table, pythonVersions)
51 |
52 | c = "Does 'python test.py' return 0?"
53 | compatibleTable = matrix2latex(table, '../doc/compatibleTable',
54 | headerColumn=pythonVersions, headerRow=['Compatible'],
55 | caption=c)
56 | print(compatibleTable)
57 |
--------------------------------------------------------------------------------
/test/test_syntaxError.py:
--------------------------------------------------------------------------------
1 | """This file is a hack: files using the 'with' syntax will raise a syntax error on pre 2.4, so
2 | these are placed here."""
3 | from __future__ import with_statement
4 | import warnings
5 | from test_util import *
6 |
7 | from matrix2latex import matrix2latex
8 | def test_format_formatColumn_Warning():
9 | # Test for warning: http://stackoverflow.com/a/3892301/1942837
10 | with warnings.catch_warnings(record=True) as w:
11 | warnings.simplefilter("always") # Cause all warnings to always be triggered.
12 | # specify both format and formatColumn
13 | t = matrix2latex([[123456e10, 123456e10]],
14 | format='%g', formatColumn=['%.1g', '%g'])
15 | assert len(w) == 1
16 | assert issubclass(w[-1].category, Warning)
17 | assertEqual(t, 'format_formatColumn_Warning')
18 |
--------------------------------------------------------------------------------
/test/test_util.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.expanduser(__file__)))
4 |
5 | f = open(os.path.join(SCRIPT_DIR, 'test.tex'))
6 | answers = dict()
7 | for line in f:
8 | if line.startswith('%%%'):
9 | name = line[3:-1] # ignore %%% and \n
10 | answers[name] = ''
11 | else:
12 | answers[name] += line
13 | f.close()
14 |
15 |
16 | def loopTwoLists(x, y):
17 | for ix in range(max([len(x), len(y)])):
18 | try: a = x[ix].strip()
19 | except: a = ''
20 | try: b = y[ix].strip()
21 | except: b = ''
22 | yield a, b
23 |
24 | def assertEqual(x, name):
25 | # assert each line is equal, ignoring leading and trailing spaces
26 | print(x)
27 | y = answers[name]
28 | x = x.split('\n')
29 | y = y.split('\n')
30 | correct = True
31 | for a, b in loopTwoLists(x, y):
32 | if a != b:
33 | correct = False # found 1 or more error
34 |
35 | if not(correct):
36 | for a, b in loopTwoLists(x, y):
37 | print(a,b)
38 | raise AssertionError
39 |
--------------------------------------------------------------------------------