4

I'm working on a package that allows Python code to be entered within certain commands and environments, saves the Python code to external files and executes it, and brings back the results via \input. Similar to SympyTeX and SageTeX, but with additional features.

As a trivial example, \pylab{print 1+1} would save the text string print 1+1 to a file, Python executes this file to obtain 2 and saves this in a second file, and this second file is brought back into the original document with \input.

This is a minimal example. (It will give errors after the first run, before Python is run to create infile.tex.)

\newcommand{\pylab}[1]{
    \newwrite\outputstream
    \immediate\openout\outputstream=outfile.py
    \immediate\write\outputstream{#1}
    \immediate\closeout\outputstream
    \input{infile.tex}
}

The problem is that in more complex cases, the argument for \pylab could contain special characters, for example \pylab{print 3%2} (modulus operator). What's the best way to deal with this? Ideally, my command would act like \verb, so that it would take anything but * as the switch, so as to be able to work around almost any special code characters. I've been reading about verbatim, catcodes, \gdef, etc., but I haven't been able to get anywhere with those yet.

SympyTeX and SageTeX use

\catcode`\%=12
\newcommand{\percent}{%}
\catcode`\%=14

to define a replacement for %, but I want to avoid that route if possible since it means that I can't just directly paste in valid Python code.

4

1 Answer 1

4

You can read the argument verbatim using the follow technique. However this keeps { and } normal so they need to be matched inside the argument.

\documentclass{article}

\makeatletter
\newwrite\@pyout
\newcommand{\pylab}{%
    \begingroup
    % deactivate special characters
    \let\do\@makeother
    \dospecials
    % change '{' and '}' back to normal
    \catcode`\{=1
    \catcode`\}=2
    \@pylab
}
\def\@pylab#1{%
    \endgroup
    \immediate\openout\@pyout=\jobname.py
    \immediate\write\@pyout{#1}%
    \immediate\closeout\@pyout
    \immediate\write18{python \jobname.py > \jobname-py.tex}%
    \input{\jobname-py}%
}

\makeatother


\begin{document}

\pylab{print 3%2}

\end{document}

If you want the behavior of \verb use the following code which is inspired by the actual \verb code.

\documentclass{article}

\makeatletter

\newwrite\@pyout

\newcommand{\pylab}[1]{%
    \begingroup
    \verb@eol@error
    \let\do\@makeother
    \dospecials
    \catcode`#1\active
    \lccode`\~`#1%
    \lowercase{\def\@@pylab##1~}{\endgroup\@pylab{##1}}%
    \@@pylab
}

\def\@pylab#1{%
    \immediate\openout\@pyout=\jobname.py
    \immediate\write\@pyout{#1}%
    \immediate\closeout\@pyout
    \immediate\write18{python \jobname.py > \jobname-py.tex}%
    \input{\jobname-py}%
}

\makeatother

\begin{document}

\pylab|print 3%2|

\end{document}

Update:

With the new version 2011/07/23 of my newverbs package you can simplify the \pylab macro to \newcommand{\pylab}{\begingroup\Collectverb{\@pylab}}.

2
  • 1
    It's possible to avoid writing the \jobname-py.tex file by changing the two lines before the final brace into \@@input|"python \jobname.py" (it still requires shell-escape enabled).
    – egreg
    May 28, 2011 at 20:52
  • Thanks! The first solution does everything I wanted--seems to handle everything I've thrown at it, and since I'm dealing with code, matched braces isn't an issue.
    – G.P.
    May 29, 2011 at 3:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .