diff --git a/README.rst b/README.rst index a3408df..6e01556 100644 --- a/README.rst +++ b/README.rst @@ -1,16 +1,55 @@ +Python in Latex +=============== + +This package enables you to embed Python code (www.python.org) in LaTeX and insert the output into your LaTeX document. + +Installing +---------- + +Copy `python.sty` and `runpy.py` side by side with your document. + + +Using +----- + +In you LaTeX document insert:: + + \usepackage{python} + ... + \begin{python} + print("42") + \end{python} + +Compile the document with the shell escape option and of course Python installed:: + + $ latex -shell-escape document.tex + +or insert/change "shell_escape = t" in your texmf.cnf + **Note**: By default LaTeX and its variants disallowing calling of arbitrary shell commands. python.sty requires unrestricted to the shell in order to execute embedded Python scripts. When running python.sty you must execute ``latex`` or ``pdftex`` with either the ``-enable-write18`` or ``-shell-escape`` command line argument to enabled access to the Python executable. -python.sty and associated scripts -================================= -Discussion and examples at http://pycurious.org/2011/12/the-ultimate-python-latex-environment/ +Package options +--------------- + +The package takes an optional key-value option (`bin`) that can be used to specify the +python binary to use. (If not present `python` is used):: + + \usepackage[bin=python2.6]{python} + + +Python scripts, output, error and return files are saved by default in the same folder as the documents and the folder gets crowded quite easily, If the word `subfolder` is +added to the options, these files will be served to a subfolder named `py`:: + + \usepackage[subfolder]{python} + + +Package content +--------------- -build.sh - Compiles example.tex into example.pdf using rubber. Requires the - shell-escape branch of rubber - (https://code.launchpad.net/~brotchie/rubber/shell-escape) +Original discussion and examples at http://web.archive.org/web/20120423051102/http://pycurious.org/2011/12/the-ultimate-python-latex-environment/ example.tex Example usage of python.sty. @@ -22,3 +61,6 @@ pytex.vim python.sty A LaTeX package that allows direct embedding of python scripts in LaTeX documents. + +runpy.py + A helper python script. diff --git a/build.sh b/build.sh deleted file mode 100755 index eba2773..0000000 --- a/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -mkdir -p .build -rubber -d --shell-escape --into=.build example.tex && cp .build/example.pdf . diff --git a/python.sty b/python.sty index 346c89b..6b68184 100644 --- a/python.sty +++ b/python.sty @@ -12,10 +12,15 @@ %% along with this program; if not, write to the Free Software %% Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. %% + +%% 0.4 Version by HernĂ¡n Grecco +%% 0.3 Version by James Brotchie +%% %% Author: Martin R. Ehmsen, ehmsen@imada.sdu.dk. %% Department of Mathematics and Computer Science, %% University of Southern Denmark, DK %% + %% You can find an online copy of the GPL at %% http://www.gnu.org/copyleft/gpl.html . %% @@ -23,95 +28,162 @@ %% This can either be done by passing -shell-escape as an option to %% latex or by adding/changing "shell_escape = t" in your texmf.cnf . -% 0.21 -> 0.3: Intermediate files are now stored with different filenames. -% The return code of each Python shell execution is stored in an .rc file. -% Any stderr output of a failed Python shell execution is inserted as -% a red verbatim in the resulting document. -% -- James Brotchie + +% Python-sty Changelog +% ==================== +% +% +% 0.4 (2013-11-28) +% ---------------- +% - Python code is only executed if it has changed. +% - Option to save auxiliary files in a subfolder. +% - Option to choose the python binary. +% - Code refactoring. +% - UTF-8 support. +% +% +% 0.3 (2011-12-22) +% ---------------- +% +% - Intermediate files are now stored with different filenames. +% - The return code of each Python shell execution is stored +% in an .rc file. +% - Any stderr output of a failed Python shell execution is +% inserted as a red verbatim in the resulting document. +% -- James Brotchie +% +% 0.21 +% ---- +% +% - Moved \newwrite\@module from \@writemodule and out, since +% no more than 15 \newwrites are allowed (and the previous +% version created a new every time \@writemodule was called. +% % -% 0.2 -> 0.21: Moved \newwrite\@module from \@writemodule and out, since -% no more than 15 \newwrites are allowed (and the previous version created a new -% every time \@writemodule was called. \NeedsTeXFormat{LaTeX2e}[1994/12/01] -\ProvidesPackage{python}[2011/12/22 v0.3 Python in LaTeX] +\ProvidesPackage{python}[2013/11/17 v0.4 Python in LaTeX] \RequirePackage{etoolbox} \RequirePackage{verbatim} \RequirePackage{color} \RequirePackage{ifplatform} - -\newwrite\@out -\newwrite\@module -\newread\@retcode +\RequirePackage{fancyvrb} +\RequirePackage{inputenc} +\RequirePackage{kvoptions} % Put the resulting Python code in separate files. +\newcommand{\@PYT@outname}{\jobname\arabic{@PythonEnvironmentCounter}} \newcounter{@PythonEnvironmentCounter} \setcounter{@PythonEnvironmentCounter}{1} -\newcommand{\@outname}{\jobname\arabic{@PythonEnvironmentCounter}} - -\begingroup \catcode `|=0 \catcode `[=1 -\catcode`]=2 \catcode `\{=12 \catcode `\}=12 -\catcode`\\=12 |gdef|@xpython#1\end{python}[|immediate|write|@out[#1]|end[python]] -|endgroup - -\def\python{\kernel@ifnextchar [{\@python}{\@python[]}} - -\def\@python[#1]{% -\gdef\@pythoninclude{#1} -\immediate\openout\@out=\@outname.py -\newlinechar='15 -\begingroup \catcode`\^^M=12 % -\let\do\@makeother\dospecials\obeyspaces% -\@xpython} - -\def\endpython{% -\endgroup -\immediate\closeout\@out -\@writemodule -\ifwindows -\immediate\write18{type \@pythoninclude\space\@outname.py 2> nul: | python > \@outname.py.out 2> \@outname.py.err & call echo ^\@percentchar ERRORLEVEL^\@percentchar > \@outname.rc} -\else -\immediate\write18{cat \@pythoninclude\space\@outname.py | /usr/bin/env python > \@outname.py.out 2> \@outname.py.err; echo -n $? > \@outname.rc} -\fi -\immediate\input\@outname.py.out - -% Read the return code of the executed Python script. -\immediate\openin\@retcode=\@outname.rc -\immediate\read\@retcode to \rc -\immediate\closein\@retcode - -% If the return code isn't zero then include -% the traceback. -\ifnumequal{\rc}{0}{}{% - \begingroup - \color{red} - \verbatiminput{\@outname.py.err} - \endgroup + +% Package Options +\SetupKeyvalOptions{ + family=PYT, + prefix=PYT@ } -\immediate\stepcounter{@PythonEnvironmentCounter} +\DeclareVoidOption{subfolder}{ + \renewcommand{\@PYT@outname}{py/\jobname\arabic{@PythonEnvironmentCounter}} +} + +\DeclareStringOption[python]{bin}[python] + +\ProcessKeyvalOptions* + +\newcommand{\@pythonbin}{\PYT@bin \space runpy.py \space} + +\typeout{PYTHON: Binary \@pythonbin} + +\newread\@PYT@retcode +\newread\@PYT@diff + +\newcommand{\@PYT@copytocache}{ + \ifwindows + \immediate\write18{copy \@PYT@outname.py \@PYT@outname.py.cache} + \else + \immediate\write18{cp \@PYT@outname.py \@PYT@outname.py.cache} + \fi +} + +\newcommand{\@PYT@deletescript}{ + \ifwindows + \immediate\write18{del \@PYT@outname.py} + \else + \immediate\write18{rm \@PYT@outname.py} + \fi +} + +\newcommand{\@PYT@comparefiles}{ + \ifwindows + \immediate\write18{fc \@PYT@outname.py \@PYT@outname.py.cache & call echo ^\@percentchar ERRORLEVEL ^\@percentchar > \@PYT@outname.cmp.rc} + \else + \immediate\write18{cmp \@PYT@outname.py \@PYT@outname.py.cache; echo $? > \@PYT@outname.cmp.rc} + \fi +} + +\newcommand{\@PYT@executescript}{ + \typeout{PYTHON: Running file \@PYT@outname.py} + \immediate\write18{\@pythonbin \space\@PYT@outname.py} + + % Copy to the current script to keep a cache. + \@PYT@copytocache + \@PYT@deletescript + +} + +\newcommand{\@PYT@returncode}{ + \inputencoding{utf8} + \input{\@PYT@outname} + + % Read the return code of the executed Python script. + \immediate\openin\@PYT@retcode=\@PYT@outname.py.rc + \immediate\read\@PYT@retcode to \rc + \immediate\closein\@PYT@retcode + + % If the return code isn't zero then include + % the traceback. + \ifnumequal{\rc}{0}{ + \typeout{PYTHON: Run was successful} + }{% + \typeout{PYTHON: Run failed} + \begingroup + \color{red} + \verbatiminput{\@PYT@outname.py.err} + \endgroup + } +} + +\newenvironment{python} +{ + \typeout{PYTHON: Writing file \@PYT@outname.py} + \@PYT@deactivateeightbit\VerbatimOut{\@PYT@outname.py}} +{ + \endVerbatimOut + + \@PYT@comparefiles + \immediate\openin\@PYT@diff=\@PYT@outname.cmp.rc + \immediate\read\@PYT@diff to \rc + \immediate\closein\@PYT@diff + + \ifnumequal{\rc}{0}{ + % Old file and new file are identical + \typeout{PYTHON: No need to run the script again} + }{ + % Old file and new file are different or old is not present + \@PYT@executescript + } + \@PYT@returncode + + \immediate\stepcounter{@PythonEnvironmentCounter} +} + +\newcommand{\@PYT@deactivateeightbit}{% + \count@=127 + \loop + \catcode\count@=12 + \ifnum\count@<255 + \advance\count@\@ne + \repeat } -\def\@writemodule{% -\immediate\openout\@module=latex.py -\immediate\write\@module{jobname="\@outname"} -\immediate\closeout\@module} -% BUGS: -% -% \immediate\write18{cat \@pythoninclude\space\jobname.py | python > \jobname.py.out 2>\jobname.py.err} -% -% 1. Watch out for indentation done by aucTeX in Emacs -% -% 2. Let the package accept a "final version" option, such -% that the output of each python run is saved such that it can be -% inserted into the document by hand -% (conference, journals are not likely to compile with -% shell_escape or have python). -% -% \gdef\@prepython{} -% \def\prepython#1{% -% \gdef\@prepython{#1} -% } -% sed -e 's/^ //g' cluster.py -% \immediate\write18{\@prepython\space\jobname.py > \ diff --git a/runpy.py b/runpy.py new file mode 100644 index 0000000..07b525a --- /dev/null +++ b/runpy.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +import sys + +try: + reload(sys).setdefaultencoding("UTF-8") +except: + pass + + +filename = sys.argv[1] + +with open(filename[:-3] + '.tex', "w") as fout: + sys.stdout = fout + with open(filename + '.err', "w") as ferr: + with open(filename + '.rc', 'w') as frc: + with open(filename) as f: + try: + code = compile(f.read(), filename, 'exec') + exec(code, {'print': fout.write}) + frc.write('0') + except Exception as e: + frc.write('1') + import traceback + ferr.write(traceback.format_exc()) + raise e