% \section{Implementation}% \label{sec:implementation}%% There are two pieces to this package: a \LTX style file, and a% Python module. They are mutually interdependent, so it makes sense to% document them both here.%% \subsection{The style file}% \label{sec:sty-file}%% \iffalse% tell docstrip to put code into the .sty file%<*latex>% \fi%% All macros and counters intended for use internal to this package% begin with ``|ST@|''.%% \subsubsection{Initialization}%% Let's begin by loading some packages. The key bits of |sageblock| and% friends are stol---um, adapted from the |verbatim| package manual. So% grab the |verbatim| package. We also need the |fancyvrb| package for% the |sageexample| environment% \begin{macrocode}\RequirePackage{verbatim}\RequirePackage{fancyvrb}% \end{macrocode}% and |listings| for the |sagecommandline| environment.% \begin{macrocode}\RequirePackage{listings}\RequirePackage{color}\lstdefinelanguage{Sage}[]{Python}{morekeywords={False,sage,True},sensitive=true}\lstdefinelanguage{SageOutput}[]{}{morekeywords={False,True},sensitive=true}\lstdefinestyle{DefaultSageInputOutput}{nolol,identifierstyle=,name=sagecommandline,xleftmargin=5pt,numbersep=5pt,aboveskip=0pt,belowskip=0pt,breaklines=true,numberstyle=\footnotesize,numbers=right}\lstdefinestyle{DefaultSageInput}{language=Sage,style=DefaultSageInputOutput,basicstyle={\ttfamily\bfseries},commentstyle={\ttfamily\color{dgreencolor}},keywordstyle={\ttfamily\color{dbluecolor}\bfseries},stringstyle={\ttfamily\color{dgraycolor}\bfseries},}\lstdefinestyle{DefaultSageOutput}{language=SageOutput,style=DefaultSageInputOutput,basicstyle={\ttfamily},commentstyle={\ttfamily\color{dgreencolor}},keywordstyle={\ttfamily\color{dbluecolor}},stringstyle={\ttfamily\color{dgraycolor}},}\lstdefinestyle{SageInput}{style=DefaultSageInput,}\lstdefinestyle{SageOutput}{style=DefaultSageOutput,}\definecolor{dbluecolor}{rgb}{0.01,0.02,0.7}\definecolor{dgreencolor}{rgb}{0.2,0.4,0.0}\definecolor{dgraycolor}{rgb}{0.30,0.3,0.30}% \end{macrocode}% Unsurprisingly, the |\sageplot| command works poorly without graphics% support.% \begin{macrocode}\RequirePackage{graphicx}% \end{macrocode}% The |makecmds| package gives us a |\provideenvironment| which we need,% and we use |ifpdf| and |ifthen| in |\sageplot| so we know what kind of% files to look for. Since |ifpdf| doesn't detect running under XeTeX% (which defaults to producing PDFs), we need |ifxetex|. Hopefully the% |ifpdf| package will get support for this and we can drop |ifxetex|.% We also work around ancient \TeX{} distributions that don't have% |ifxetex| and assume that they don't have XeTeX.% \begin{macrocode}\RequirePackage{makecmds}\RequirePackage{ifpdf}\RequirePackage{ifthen}\IfFileExists{ifxetex.sty}{\RequirePackage{ifxetex}}{\newboolean{xetex}\setboolean{xetex}{false}}% \end{macrocode}%% Next set up the counters, default indent, and flags.% \begin{macrocode}\newcounter{ST@inline}\newcounter{ST@plot}\newcounter{ST@cmdline}\setcounter{ST@inline}{0}\setcounter{ST@plot}{0}\setcounter{ST@cmdline}{0}\newlength{\sagetexindent}\setlength{\sagetexindent}{5ex}\newif\ifST@paused\ST@pausedfalse% \end{macrocode}% Set up the file stuff, which will get run at the beginning of the% document, after we know what's happening with the |final| option.% First, we open the |.sage| file:% \begin{macrocode}\AtBeginDocument{\@ifundefined{ST@final}{%\newwrite\ST@sf%\immediate\openout\ST@sf=\jobname.sagetex.sage%% \end{macrocode}% \begin{macro}{\ST@wsf}% We will write a lot of stuff to that file, so make a convenient% abbreviation, then use it to put the initial commands into the% |.sage| file. The hash mark below gets doubled when written to the% file, for some obscure reason related to parameter expansion. It's% valid Python, though, so I haven't bothered figuring out how to get% a single hash. We are assuming that the extension is |.tex|; see the% |initplot| documentation on page~\pageref{initplot} for discussion% of file extensions. (There is now the \texttt{currfile} package% (\url{http://www.ctan.org/pkg/currfile/}) which can figure out file% extensions, apparently.) The ``|(\jobname.sagetex.sage)|'' business% is there because the comment below will get pulled into the% autogenerated |.py| file (second order autogeneration!) and I'd like% to reduce possible confusion if someone is looking around in those% files. Finally, we check for version mismatch and bail if the .py% and .sty versions don't match and the user hasn't disabled checking.% Note that we use |^^J| and not |^^J%| when we need indented lines.% Also, |sagetex.py| now includes a |version| variable which% eliminates all the irritating string munging below, and later we can% remove this stuff and just use |sagetex.version|.% \changes{v2.2.4}{2010/02/15}{Add version mismatch checking.}% \changes{v2.3.3}{2012/01/13}{Improve version mismatch checking,% include Mercurial revision in version string.}% \begin{macrocode}\newcommand{\ST@wsf}[1]{\immediate\write\ST@sf{#1}}%\ST@wsf{%# -*-encoding:utf-8-*-^^J%# Thisfile(\jobname.sagetex.sage)was*autogenerated*from\jobname.texwithsagetex.styversion\ST@ver.^^J%importsagetex^^J%_st_=sagetex.SageTeXProcessor('\jobname',version='\ST@ver',version_check=\ST@versioncheck)}}%% \end{macrocode}% On the other hand, if the |ST@final| flag is set, don't bother with% any of the file stuff, and make |\ST@wsf| a no-op.% \begin{macrocode}{\newcommand{\ST@wsf}[1]{\relax}}}% \end{macrocode}% \end{macro}%% \begin{macro}{\ST@dodfsetup}% The |sageexample| environment writes stuff out to a different file% formatted so that one can run doctests on it. We define a macro that% only sets this up if necessary.% \begin{macrocode}\newcommand{\ST@dodfsetup}{%\@ifundefined{ST@diddfsetup}{%\newwrite\ST@df%\immediate\openout\ST@df=\jobname_doctest.sage%\immediate\write\ST@df{r"""^^J%Thisfilewas*autogenerated*from\jobname.texwithsagetex.sty^^J%version\ST@ver.Itcontainsthecontentsofallthe^^J%sageexampleenvironmentsfrom\jobname.tex.Youshouldbeableto^^J%doctestthisfilewith "sage-t\jobname_doctest.sage".^^J%^^J%Itisalwayssafetodeletethisfile;itisnotusedintypesettingyour^^J%document.^^J}%\AtEndDocument{\immediate\write\ST@df{"""}}%\gdef\ST@diddfsetup{x}}%{\relax}}% \end{macrocode}% \changes{v2.2.5}{2010/03/25}{Write sageexample environment contents to% a separate file, formatted for doctesting}% \end{macro}%% \begin{macro}{\ST@wdf}% This is the compansion to |\ST@wsf|; it writes to the doctest file,% assuming that is has been set up. We ignore the |final| option here% since nothing in this file is relevant to typesetting the document.% \begin{macrocode}\newcommand{\ST@wdf}[1]{\immediate\write\ST@df{#1}}% \end{macrocode}% \end{macro}%% Now we declare our options, which mostly just set flags that we check% at the beginning of the document, and when running the |.sage| file.% \changes{v2.0}{2008/04/04}{Add \texttt{epstopdf} option}% \changes{v2.0}{2008/12/16}{Add \texttt{final} option}%% The |final| option controls whether or not we write the |.sage| file;% the |imagemagick| and |epstopdf| options both want to write something% to that same file. So we put off all the actual file stuff until the% beginning of the document---by that time, we'll have processed the% |final| option (or not) and can check the |\ST@final| flag to see what% to do. (We must do this because we can't specify code that runs if an% option \emph{isn't} defined.)%% For |final|, we set a flag for other guys to check, and if there's no% |.sout| file, we warn the user that something fishy is going on.% \begin{macrocode}\DeclareOption{final}{%\newcommand{\ST@final}{x}%\IfFileExists{\jobname.sagetex.sout}{}{\AtEndDocument{\PackageWarningNoLine{sagetex}%{`final'optionprovided,but\jobname.sagetex.sout^^Jdoesn'texist! NoSageinputwillappearinyourdocument.Removethe `final'^^JoptionandrerunLaTeXonyourdocument}}}}% \end{macrocode}% For |imagemagick|, we set two flags: one for \LTX and one for Sage.% It's important that we set |ST@useimagmagick| \emph{before} the% beginning of the document, so that the graphics commands can check% that. We do wait until the beginning of the document to do file% writing stuff.% \begin{macrocode}\DeclareOption{imagemagick}{%\newcommand{\ST@useimagemagick}{x}%\AtBeginDocument{%\@ifundefined{ST@final}{%\ST@wsf{_st_.useimagemagick=True}}{}}}% \end{macrocode}% For |epstopdf|, we just set a flag for Sage.% \begin{macrocode}\DeclareOption{epstopdf}{%\AtBeginDocument{%\@ifundefined{ST@final}{%\ST@wsf{_st_.useepstopdf=True}}{}}}% \end{macrocode}% By default, we check to see if the .py and .sty file versions match.% But we let the user disable this.% \begin{macrocode}\newcommand{\ST@versioncheck}{True}\DeclareOption{noversioncheck}{%\renewcommand{\ST@versioncheck}{False}}\ProcessOptions\relax% \end{macrocode}% The |\relax| is a little incantation suggested by the ``\LaTeXe{} for% class and package writers'' manual, section 4.7.%% Pull in the |.sout| file if it exists, or do nothing if it doesn't. I% suppose we could do this inside an |AtBeginDocument| but I don't see% any particular reason to do that. It will work whenever we load it. If% the |.sout| file isn't found, print the usual \TeX-style message. This% allows programs% (\href{http://www.phys.psu.edu/~collins/software/latexmk-jcc/}{\texttt{Latexmk}}, for example)% that read the |.log| file or terminal output to detect% the need for another typesetting run to do so. If the ``\texttt{No% file foo.sout}'' line doesn't work for some software package, please% let me know and I can change it to use |PackageInfo| or whatever.% \begin{macrocode}\InputIfFileExists{\jobname.sagetex.sout}{}{\typeout{Nofile\jobname.sagetex.sout.}}% \end{macrocode}% \changes{v2.1.1}{2009/05/14}{Add typeout if .sout file not found}%% The user might load the |hyperref| package after this one (indeed, the% |hyperref| documentation insists that it be loaded last) or not at% all---so when we hit the beginning of the document, provide a dummy% |NoHyper| environment if one hasn't been defined by the |hyperref|% package. We need this for the |\sage| macro below.% \begin{macrocode}\AtBeginDocument{\provideenvironment{NoHyper}{}{}}% \end{macrocode}%% \subsubsection{The \texttt{\protect\bslash sage} and% \texttt{\protect\bslash sagestr} macros}% \label{sec:sagemacro}%% \begin{macro}{\ST@sage}% \changes{v2.1.1}{2009/05/14}{Add ST@sage, sagestr, and refactor.}% This macro combines |\ref|, |\label|, and Sage all at once. First, we% use Sage to get a \LTX representation of whatever you give this% function. The Sage script writes a |\newlabel| line into the |.sout|% file, and we read the output using the |\ref| command. Usually, |\ref|% pulls in a section or theorem number, but it will pull in arbitrary% text just as well.%% The first thing it does it write its argument into the |.sage| file,% along with a counter so we can produce a unique label. We wrap a% try/except around the function call so that we can provide a more% helpful error message in case something goes wrong. (In particular, we% can tell the user which line of the |.tex| file contains the offending% code.) Note the difference between |^^J| and |^^J%|: the newline% immediately after the former puts a space into the output, and the% percent sign in the latter supresses this.% \begin{macrocode}\newcommand{\ST@sage}[1]{\ST@wsf{%try:^^J_st_.inline(\theST@inline, #1)^^J%except:^^J_st_.goboom(\the\inputlineno)}%% \end{macrocode}% The |inline| function of the Python module is documented on page% \pageref{inlinefn}. Back in \LTX-land: if paused, say so.% \begin{macrocode}\ifST@paused\mbox{(Sage\TeX{}ispaused)}%% \end{macrocode}% Otherwise\ldots our use of |\newlabel| and |\ref| seems awfully clever% until you load the |hyperref| package, which gleefully tries to% hyperlink the hell out of everything. This is great until it hits one% of our special |\newlabel|s and gets deeply confused. Fortunately the% |hyperref| folks are willing to accomodate people like us, and give us% a |NoHyper| environment.% \begin{macrocode}\else\begin{NoHyper}\ref{@sageinline\theST@inline}\end{NoHyper}%% \end{macrocode}% Now check if the label has already been defined. (The internal% implementation of labels in \LTX involves defining a macro called% ``|r@@labelname|''.) If it hasn't, we set a flag so that we can tell% the user to run Sage on the |.sage| file at the end of the run.% \begin{macrocode}\@ifundefined{r@@sageinline\theST@inline}{\gdef\ST@rerun{x}}{}%\fi% \end{macrocode}% In any case, the last thing to do is step the counter.% \begin{macrocode}\stepcounter{ST@inline}}% \end{macrocode}% \end{macro}%% \begin{macro}{\sage}% This is the user-visible macro; it runs Sage's |latex()| on its% argument.% \begin{macrocode}\newcommand{\sage}[1]{\ST@sage{latex(#1)}}% \end{macrocode}% \end{macro}%% \begin{macro}{\sagestr}% Like above, but doesn't run |latex()| on its argument.% \begin{macrocode}\newcommand{\sagestr}[1]{\ST@sage{#1}}% \end{macrocode}%% \end{macro}%% \begin{macro}{\percent}% A macro that inserts a percent sign. This is more-or-less stolen from the% \textsf{Docstrip} manual; there they change the catcode inside a group% and use |gdef|, but here we try to be more \LaTeX y and use% |\newcommand|.% \begin{macrocode}\catcode`\%=12\newcommand{\percent}{%}\catcode`\%=14% \end{macrocode}% \end{macro}%% \subsubsection{The \texttt{\protect\bslash sageplot} macro and friends}% \label{sec:sageplotmacro}%% Plotting is rather more complicated, and requires several helper% macros that accompany |\sageplot|.%% \begin{macro}{\ST@plotdir}% A little abbreviation for the plot directory. We don't use% |\graphicspath| because it's% \href{http://www.tex.ac.uk/cgi-bin/texfaq2html?label=graphicspath}{% apparently slow}---also, since we know right where our plots are% going, no need to have \LTX looking for them.% \begin{macrocode}\newcommand{\ST@plotdir}{sage-plots-for-\jobname.tex}% \end{macrocode}% \end{macro}%% \begin{macro}{\ST@missingfilebox}% The code that makes the ``file not found'' box. This shows up in a% couple places below, so let's just define it once.% \begin{macrocode}\newcommand{\ST@missingfilebox}{\framebox[2cm]{\rule[-1cm]{0cm}{2cm}\textbf{??}}}% \end{macrocode}% \end{macro}% \begin{macro}{\sageplot}% \changes{v1.3}{2008/03/08}{Iron out warnings, cool \TikZ flowchart}% This function is similar to |\sage|. The neat thing that we take% advantage of is that commas aren't special for arguments to \LTX% commands, so it's easy to capture a bunch of keyword arguments that% get passed right into a Python function.%% This macro has two optional arguments, which can't be defined using% \LTX's |\newcommand|; we use Scott Pakin's brilliant% \href{http://tug.ctan.org/tex-archive/support/newcommand/}{|newcommand|}% package to create this macro; the options I fed to his script were% similar to this:%\begin{center}% |MACRO sageplot OPT[#1={width}] OPT[#2={notprovided}] #3|%\end{center}% Observe that we are using a Python script to write \LTX code which% writes Python code which writes \LTX code. Crazy!%% Here's the wrapper command which does whatever magic we need to get% two optional arguments.% \begin{macrocode}\newcommand{\sageplot}[1][]{%\@ifnextchar[{\ST@sageplot[#1]}{\ST@sageplot[#1][notprovided]}}% \end{macrocode}% \changes{v2.3.2}{2011/06/17}{Remove ``.75 textwidth'' default option}% The first optional argument |#1| will get shoved right into the% optional argument for |\includegraphics|, so the user has easy control% over the \LTX aspects of the plotting. (Perhaps a future version% of \ST will allow the user to specify in the package options a set of% default options to be used throughout.) The second optional argument% |#2| is the file format and allows us to tell what files to look for.% It defaults to ``notprovided'', which tells the Python module to% create EPS and PDF files. Everything in |#3| gets put into the Python% function call, so the user can put in keyword arguments there which% get interpreted correctly by Python.%% \begin{macro}{\ST@sageplot}% \changes{v2.0}{2008/12/16}{Change to use only keyword arguments: see issue% 2 on bitbucket tracker}% Let's see the real code here. We write a couple lines to the |.sage|% file, including a counter, input line number, and all of the mandatory% argument; all this is wrapped in another try/except.% \begin{macrocode}\def\ST@sageplot[#1][#2]#3{\ST@wsf{try:^^J_st_.plot(\theST@plot,format='#2',_p_=#3)^^Jexcept:^^J_st_.goboom(\the\inputlineno)}%% \end{macrocode}% The Python |plot| function is documented on page~\pageref{plotfn}.%% Now we include the appropriate graphics file. Because the user might% be producing DVI or PDF files, and have supplied a file format or not,% and so on, the logic we follow is a bit complicated.% \autoref{f:sageplottree} shows what we do; for completeness---and% because I think drawing trees with \TikZ is really cool---we show what% |\ST@inclgrfx| does in \autoref{f:stig}. This entire complicated% business is intended to avoid doing an |\includegraphics| command on a% file that doesn't exist, and to issue warnings appropriate to the% situation.%% \begin{figure}% \centering% \begin{tikzpicture}% \tikzstyle{level 1}=[sibling distance=6cm]% \tikzstyle{level 2}=[sibling distance=3cm]% \node [box] {DVI or PDF?}% child {node [box] {Format provided?}% child {node [box] {STig EPS}% edge from parent node[left] {no}}% child {node [box] {IM option set?}% child {node [box, text width=3cm] {Warn that DVI + PNG = bad}% edge from parent node[left] {no}}% child {node [box] {STig EPS}% edge from parent node[right] {yes}}% edge from parent node[right] {yes}}% edge from parent node[left] {DVI}}% child {node [box] {Format provided?}% child {node [box] {STig PDF}% edge from parent node[left] {no}}% child {node [box] {STig \texttt{\#2}}% edge from parent node[right] {yes}}% edge from parent node[right] {PDF}};% \end{tikzpicture}% \caption{The logic tree that \texttt{\bslash sageplot} uses to% decide whether to run \texttt{\bslash includegraphics} or to yell at% the user. ``Format'' is the \texttt{\#2} argument to \texttt{\bslash% sageplot}, ``STig ext''% means a call to \texttt{\bslash ST@inclgrfx} with ``ext'' as the% second argument, and ``IM'' is Imagemagick.}% \label{f:sageplottree}% \end{figure}%% If we are creating a PDF, we check to see if the user asked for a% different format, and use that if necessary:% \begin{macrocode}\ifthenelse{\boolean{pdf}\or\boolean{xetex}}{\ifthenelse{\equal{#2}{notprovided}}%{\ST@inclgrfx{#1}{pdf}}%{\ST@inclgrfx{#1}{#2}}}% \end{macrocode}% Otherwise, we are creating a DVI file, which only supports EPS. If the% user provided a format anyway, don't include the file (since it won't% work) and warn the user about this. (Unless the file doesn't exist, in% which case we do the same thing that |\ST@inclgrfx| does.)% \begin{macrocode}{\ifthenelse{\equal{#2}{notprovided}}%{\ST@inclgrfx{#1}{eps}}%% \end{macrocode}% If a format is provided, we check to see if we're using the% imagemagick option. If not, we're going to issue some sort of warning,% depending on whether the file exists yet or not.% \begin{macrocode}{\@ifundefined{ST@useimagemagick}%{\IfFileExists{\ST@plotdir/plot-\theST@plot.#2}%{\ST@missingfilebox%\PackageWarning{sagetex}{Graphicsfile\ST@plotdir/plot-\theST@plot.#2\spaceonpage\thepage\spacecannotbeusedwithDVIoutput.UsepdflatexorcreateanEPSfile.Plotcommandis}}%{\ST@missingfilebox%\PackageWarning{sagetex}{Graphicsfile\ST@plotdir/plot-\theST@plot.#2\spaceonpage\thepage\spacedoesnotexist.Plotcommandis}%\gdef\ST@rerun{x}}}%% \end{macrocode}% Otherwise, we are using Imagemagick, so try to include an EPS file% anyway.% \begin{macrocode}{\ST@inclgrfx{#1}{eps}}}}% \end{macrocode}% Step the counter and we're done with the usual work.% \begin{macrocode}\stepcounter{ST@plot}}% \end{macrocode}% \end{macro}% \end{macro}%% \begin{macro}{\ST@inclgrfx}% This command includes the requested graphics file (|#2| is the% extension) with the requested options (|#1|) if the file exists. Note% that it just needs to know the extension, since we use a counter for% the filename. If we are paused, it just puts in a little box saying% so.% \begin{macrocode}\newcommand{\ST@inclgrfx}[2]{\ifST@paused\fbox{\rule[-1cm]{0cm}{2cm}Sage\TeX{}ispaused;nographic}\else\IfFileExists{\ST@plotdir/plot-\theST@plot.#2}%{\includegraphics[#1]{\ST@plotdir/plot-\theST@plot.#2}}%% \end{macrocode}% If the file doesn't exist, we try one more thing before giving up: the% Python module will automatically fall back to saving as a PNG file if% saving as an EPS or PDF file fails. So if making a PDF, we look for a% PNG file.%% If the file isn't there, we insert a little box to indicate it wasn't% found, issue a warning that we didn't find a graphics file, then set a% flag that, at the end of the run, tells the user to run Sage again.% \begin{macrocode}{\IfFileExists{\ST@plotdir/plot-\theST@plot.png}%{\ifpdf\ST@inclgrfx{#1}{png}\else\PackageWarning{sagetex}{Graphicsfile\ST@plotdir/plot-\theST@plot.pngonpage\thepage\spacenotsupported;tryusingpdflatex.Plotcommandis}%\fi}%{\ST@missingfilebox%\PackageWarning{sagetex}{Graphicsfile\ST@plotdir/plot-\theST@plot.#2\spaceonpage\thepage\spacedoesnotexist.Plotcommandis}%\gdef\ST@rerun{x}}}\fi}% \end{macrocode}% \autoref{f:stig} makes this a bit clearer.% \begin{figure}% \centering% \begin{tikzpicture}% \tikzstyle{level 1}=[sibling distance=4cm]% \node [box] {Paused?}% child {node [box] {Insert ``we're paused'' box}% edge from parent node[left] {yes}}% child {node [box] {Does EXT file exist?}% child {node [box] {Does a PNG file exist?}% child {node [box] {Making a PDF?}% child {node [box] {\texttt{includegraphics} PNG}% edge from parent node[left] {yes}}% child {node [box, text width=2cm] {Warning: DVI, PNG incompatible}% edge from parent node[left] {no}}% edge from parent node[left] {yes}}% child {node [box, text width = 2.125cm] {Warn user to rerun Sage}% edge from parent node[left] {no}}% edge from parent node[left] {no}}% child {node [box] {Use \texttt{includegraphics}}% edge from parent node[right] {yes}}% edge from parent node[right] {no}};% \end{tikzpicture}% \caption{The logic used by the \texttt{\bslash ST@inclgrfx}% command.}% \label{f:stig}% \end{figure}% \end{macro}%% \subsubsection{Verbatim-like environments}% \label{sec:verbatim-envs}%% \begin{macro}{\ST@beginsfbl}% This is ``begin |.sage| file block'', an internal-use abbreviation% that sets things up when we start writing a chunk of Sage code to% the |.sage| file. It begins with some \TeX{} magic that fixes% spacing, then puts the start of a try/except block in the |.sage|% file---this not only allows the user to indent code without% Sage/Python complaining about indentation, but lets us tell the user% where things went wrong. The |blockbegin| and |blockend| functions% are documented on page~\pageref{blocksbeginend}. The last bit is some% magic from the |verbatim| package manual that makes \LTX respect% line breaks.% \begin{macrocode}\newcommand{\ST@beginsfbl}{%\@bsphack\ST@wsf{%_st_.blockbegin()^^Jtry:}%\let\do\@makeother\dospecials\catcode`\^^M\active}% \end{macrocode}% \end{macro}%% \begin{macro}{\ST@endsfbl}% The companion to |\ST@beginsfbl|.% \begin{macrocode}\newcommand{\ST@endsfbl}{%\ST@wsf{except:^^J_st_.goboom(\the\inputlineno)^^J_st_.blockend()}}% \end{macrocode}% \end{macro}%% Now let's define the ``verbatim-like'' environments. There are four% possibilities, corresponding to the two independent choices of% typesetting the code or not, and writing to the |.sage| file or not.%% \begin{environment}{sageblock}% This environment does both: it typesets your code and puts it into the% |.sage| file for execution by Sage.% \begin{macrocode}\newenvironment{sageblock}{\ST@beginsfbl%% \end{macrocode}% The space between |\ST@wsf{| and |\the| is crucial! It, along with the% ``|try:|'', is what allows the user to indent code if they like. This% line sends stuff to the |.sage| file.% \begin{macrocode}\def\verbatim@processline{\ST@wsf{\the\verbatim@line}%% \end{macrocode}% Next, we typeset your code and start the verbatim environment.% \begin{macrocode}\hspace{\sagetexindent}\the\verbatim@line\par}%\verbatim}%% \end{macrocode}% At the end of the environment, we put a chunk into the |.sage| file% and stop the verbatim environment.% \begin{macrocode}{\ST@endsfbl\endverbatim}% \end{macrocode}% \end{environment}%% \begin{environment}{sagesilent}% This is from the |verbatim| package manual. It's just like the above,% except we don't typeset anything.% \begin{macrocode}\newenvironment{sagesilent}{\ST@beginsfbl%\def\verbatim@processline{\ST@wsf{\the\verbatim@line}}%\verbatim@start}%{\ST@endsfbl\@esphack}% \end{macrocode}% \end{environment}%% \begin{environment}{sageverbatim}% The opposite of |sagesilent|. This is exactly the same as the verbatim% environment, except that we include some indentation to be consistent% with other typeset Sage code.% \begin{macrocode}\newenvironment{sageverbatim}{%\def\verbatim@processline{\hspace{\sagetexindent}\the\verbatim@line\par}%\verbatim}%{\endverbatim}% \end{macrocode}% \end{environment}%% Logically, we now need an environment which neither typesets% \emph{nor} writes code to the |.sage| file. The verbatim package's% |comment| environment does that.\\%% \begin{environment}{sageexample}% Finally, we have an environment which is mostly-but-not-entirely% verbatim; this is the example environment, which takes input like% Sage doctests, and prints out the commands verbatim but nicely% typesets the output of those commands. This and the corresponding% Python function are due to Nicolas M. Thi\'ery.% \begin{macrocode}\newcommand{\sageexampleincludetextoutput}{False}\newenvironment{sageexample}{%\ST@wsf{%try:^^J_st_.doctest(\theST@inline,r"""}%\ST@dodfsetup%\ST@wdf{Sageexample,line\the\inputlineno::^^J}%\begingroup%\@bsphack%\let\do\@makeother\dospecials%\catcode`\^^M\active%\def\verbatim@processline{%\ST@wsf{\the\verbatim@line}%\ST@wdf{\the\verbatim@line}%}%\verbatim@start%}{\@esphack%\endgroup%\ST@wsf{% """,globals(),locals(),\sageexampleincludetextoutput)^^Jexcept:^^J_st_.goboom(\the\inputlineno)}%\ifST@paused%\mbox{(Sage\TeX{}ispaused)}%\else%\begin{NoHyper}\ref{@sageinline\theST@inline}\end{NoHyper}%\@ifundefined{r@@sageinline\theST@inline}{\gdef\ST@rerun{x}}{}%\fi%\ST@wdf{}%\stepcounter{ST@inline}}% \end{macrocode}% \changes{v2.2.4}{2010/03/14}{Add first support for% \texttt{sageexample} environment}% \end{environment}%%% \begin{environment}{sagecommandline}% This environment is similar to the |sageexample| environment, but% typesets the sage output as text with python syntax highlighting.% \begin{macrocode}\newcommand{\sagecommandlinetextoutput}{True}\newlength{\sagecommandlineskip}\setlength{\sagecommandlineskip}{8pt}\newenvironment{sagecommandline}{%\ST@wsf{%try:^^J_st_.commandline(\theST@cmdline,r"""}%\ST@dodfsetup%\ST@wdf{Sagecommandline,line\the\inputlineno::^^J}%\begingroup%\@bsphack%\let\do\@makeother\dospecials%\catcode`\^^M\active%\def\verbatim@processline{%\ST@wsf{\the\verbatim@line}%\ST@wdf{\the\verbatim@line}%}%\verbatim@start%}{\@esphack%\endgroup%\ST@wsf{% """,globals(),locals(),\sagecommandlinetextoutput)^^Jexcept:^^J_st_.goboom(\the\inputlineno)}%\ifST@paused%\mbox{(Sage\TeX{}ispaused)}%\else%\begin{NoHyper}\ref{@sagecmdline\theST@cmdline}\end{NoHyper}%\@ifundefined{r@@sagecmdline\theST@cmdline}{\gdef\ST@rerun{x}}{}%\fi%\ST@wdf{}%\stepcounter{ST@cmdline}}% \end{macrocode}% \end{environment}%% \subsubsection{Pausing \ST}% \label{sec:pausing-sagetex}%% How can one have Sage to stop processing \ST output for a little% while, and then start again? At first I thought I would need some sort% of ``goto'' statement in Python, but later realized that there's a% dead simple solution: write triple quotes to the |.sage| file to% comment out the code. Okay, so this isn't \emph{really} commenting out% the code; PEP 8 says block comments should use ``|#|'' and Sage will% read in the ``commented-out'' code as a string literal. For the% purposes of \ST, I think this is a good decision, though, since (1)% the pausing mechanism is orthogonal to everything else, which makes it% easier to not screw up other code, and (2) it will always work.%% This illustrates what I really like about \ST: it mixes \LTX and% Sage/Python, and often what is difficult or impossible in one system% is trivial in the other.%% \begin{macro}{sagetexpause}% This macro pauses \ST by effectively commenting out code in the% |.sage| file. When running the corresponding |.sage| file, Sage will% skip over any commands issued while \ST is paused.% \begin{macrocode}\newcommand{\sagetexpause}{\ifST@paused\relax\else\ST@wsf{print'SageTeX paused on \jobname.tex line \the\inputlineno'^^J"""}\ST@pausedtrue\fi}% \end{macrocode}% \end{macro}%% \begin{macro}{sagetexunpause}% This is the obvious companion to |\sagetexpause|.% \begin{macrocode}\newcommand{\sagetexunpause}{\ifST@paused\ST@wsf{"""^^Jprint'SageTeX unpaused on \jobname.tex line \the\inputlineno'}\ST@pausedfalse\fi}% \end{macrocode}% \end{macro}%% \subsubsection{End-of-document cleanup}% \label{sec:end-of-doc-cleanup}%% We tell the Sage script to write some information to the |.sout| file,% then check to see if |ST@rerun| ever got defined. If not, all the% inline formulas and plots worked, so do nothing. We check to see if% we're paused first, so that we can finish the triple-quoted string in% the |.sage| file.% \begin{macrocode}\AtEndDocument{\ifST@paused\ST@wsf{"""^^Jprint'SageTeX unpaused at end of \jobname.tex'}\fi\ST@wsf{_st_.endofdoc()}%\@ifundefined{ST@rerun}{}%% \end{macrocode}% Otherwise, we issue a warning to tell the user to run Sage on the% |.sage| file. Part of the reason we do this is that, by using |\ref|% to pull in the inlines, \LTX will complain about undefined references% if you haven't run the Sage script---and for many \LTX users, myself% included, the warning ``there were undefined references'' is a signal% to run \LTX again. But to fix these particular undefined references,% you need to run \emph{Sage}. We also suppress file-not-found errors% for graphics files, and need to tell the user what to do about that.%% At any rate, we tell the user to run Sage if it's necessary.% \begin{macrocode}{\typeout{*********************************************************************}\PackageWarningNoLine{sagetex}{therewereundefinedSageformulasand/orplots.^^JRunSageon\jobname.sagetex.sage,andthenrunLaTeXon\jobname.texagain}}\typeout{*********************************************************************}}% \end{macrocode}%%% \subsection{The Python module}% \label{sec:py-file}%% \iffalse% Hey, docstrip! Stop putting code into the .sty file, and start% putting it into the .py file.%</latex>%<*python>% Thanks.% \fi%% The style file writes things to the |.sage| file and reads them from% the |.sout| file. The Python module provides functions that help% produce the |.sout| file from the |.sage| file.%% \paragraph{A note on Python and \textsf{Docstrip}} There is one tiny% potential source of confusion when documenting Python code with% \textsf{Docstrip}: the percent sign. If you have a long line of Python% code which includes a percent sign for string formatting and you break% the line with a backslash and begin the next line with a percent sign,% that line \emph{will not} be written to the output file. This is only% a problem if you \emph{begin} the line with a (single) percent sign;% there are no troubles otherwise.\\%% On to the code: the |sagetex.py| file is intended to be used as a% module and doesn't do anything useful when called directly, so if% someone does that, warn them. We do this right away so that we print% this and exit before trying to import any Sage modules; that way, this% error message gets printed whether you run the script with Sage or% with Python. Since \ST is now distributed with Sage and |sagetex.py|% now lives almost exclusively deep within the Sage ecosystem, this% check is not so necessary and will be removed by the end of 2011.% \begin{macrocode}importsysif__name__== "__main__":print("""ThisfileispartoftheSageTeXpackage.Itisnotmeanttobecalleddirectly.ThisfilewillbeautomaticallyusedbySagescriptsgeneratedfromaLaTeXdocumentusingtheSageTeXpackage.""")sys.exit()% \end{macrocode}% Munge the version string (which we get from% \texttt{sagetex.dtx}) to extract what we want, then import what% we need:% \begin{macrocode}pyversion=' '.join(__version__.strip('[').split()[0:2])fromsage.misc.lateximportlatexfromsage.misc.preparserimportpreparseimportosimportos.pathimporthashlibimporttracebackimportsubprocessimportshutilfromcollectionsimportdefaultdict% \end{macrocode}% Define an exception class for version mismatches. I suppose I could% just use |ValueError|, but this is easy enough:% \begin{macrocode}classVersionError(Exception):pass% \end{macrocode}% Sometimes our macros that write things to the |.sout| file get% evaluated twice, most commonly in the ``fancy'' AMS environments such% as |align| and |multline|. So we need to keep track of the counters% we've seen so we don't write labels to the |.sout| file more than% once. We have more than one kind of label, so a dictionary is the% natural way to store the counters we've seen for each kind of label.% For convenience let's make a dictionary subclass for which (1) values% default to $-1$, and (2) there's an |increment(key)| function that just% increments the value corresponding to the key.% \begin{macrocode}classMyDict(defaultdict):def__init__(self,*args,**kwargs):defaultdict.__init__(self,*args,**kwargs)self.default_factory=lambda:-1defincrement(self,key):self[key]=self[key]+1% \end{macrocode}% We define a |SageTeXProcessor| class so that it's a bit easier to% carry around internal state. We used to just have some global% variables and a bunch of functions, but this seems a bit nicer and% easier.% \begin{macrocode}classSageTeXProcessor():% \end{macrocode}% If the original |.tex| file has spaces in its name, the |\jobname|% we get is surrounded by double quotes, so fix that. Technically, it% is possible to have double quotes in a legitimate filename, but% dealing with that sort of quoting is% \href{http://tug.org/pipermail/xetex/2006-August/004712.html}{unpleasant}.% And yes, we're ignoring the possibility of tabs and other whitespace% in the filename. Patches for handling pathological filenames welcome.%% \changes{v2.3.1}{2011/02/02}{Handle filenames with% spaces in SageTeXProcessor and sagecommandline env.}% \changes{v2.3.2}{2012/01/13}{Improve version mismatch check. Fixes% trac ticket 8035.}% \begin{macrocode}def__init__(self,jobname,version=None,version_check=True):ifversion !=pyversion:errstr= """versionsof.styand.pyfilesdonotmatch.{0}.sagetex.sagewasgeneratedbysagetex.styversion "{1}",butisbeingprocessedbysagetex.pyversion "{2}".PleasemakesurethatTeXisusingthesagetex.styfromyourcurrentversionofSage;seehttp://www.sagemath.org/doc/installation/sagetex.html.""".format(jobname,version,pyversion)ifversion_check:raiseVersionError,errstrelse:print'**** WARNING! Skipping version check for .sty and .py files, and'printerrstrif' 'injobname:jobname=jobname.strip('"')self.progress('Processing Sage code for {0}.tex...'.format(jobname))self.didinitplot=Falseself.useimagemagick=Falseself.useepstopdf=Falseself.plotdir='sage-plots-for-'+jobname+'.tex'self.filename=jobnameself.name=os.path.splitext(jobname)[0]autogenstr= """% This file was *autogenerated* from {0}.sagetex.sage with% sagetex.py version {1}\n""".format(self.name, version)% \end{macrocode}% Don't remove the space before the percent sign above!%% \LTX environments such as |align| evaluate their arguments twice after% doing |\savecounters@|, so if you do |\sage| inside such an environment,% it will result in two labels with the same name in the |.sout| file and% the user sees a warning when typesetting. So we keep track of the% largest label we've seen so that we don't write two labels with the same% name.% \begin{macrocode}self.max_counter_seen=MyDict()% \end{macrocode}% Open a |.sout.tmp| file and write all our output to that. Then, when% we're done, we move that to |.sout|. The ``autogenerated'' line is% basically the same as the lines that get put at the top of preparsed% Sage files; we are automatically generating a file with Sage, so it% seems reasonable to add it. Add in the version to help debugging% version mismatch problems.% \begin{macrocode}self.souttmp=open(self.filename+'.sagetex.sout.tmp','w')self.souttmp.write(autogenstr)% \end{macrocode}% In addition to the |.sout| file, the |sagecommandline| also needs a% |.scmd| file. As before, we use a |.scmd.tmp| file and rename it% later on. We store the file and position in the data members% \begin{macrocode}self.scmdtmp=open(self.filename+'.sagetex.scmd.tmp','w')self.scmdtmp.write(autogenstr)self.scmdpos=3% \end{macrocode}%% \begin{macro}{progress}% This function just prints stuff. It allows us to not print a% linebreak, so you can get ``|start...|'' (little time spent% processing) ``|end|'' on one line.% \begin{macrocode}defprogress(self,t,linebreak=True):iflinebreak:print(t)else:sys.stdout.write(t)sys.stdout.flush()% \end{macrocode}% \end{macro}%% \begin{macro}{initplot}% \phantomsection\label{initplot}% We only want to create the plots directory if the user actually plots% something. This function creates the directory and sets the% |didinitplot| flag after doing so. We make a directory based on the% \LTX file being processed so that if there are multiple |.tex| files% in a directory, we don't overwrite plots from another file.% \begin{macrocode}definitplot(self):self.progress('Initializing plots directory')% \end{macrocode}% We hard-code the |.tex| extension, which is fine in the overwhelming% majority of cases, although it does cause minor confusion when% building the documentation. If it turns out lots of people use, say, a% |ltx| extension or whatever, We could find out the correct extension,% but it would involve a lot of irritating mucking around---on% |comp.text.tex|, the best solution I found for finding the file% extension is to look through the |.log| file. (Although see the% \texttt{currfile} package.)% \begin{macrocode}ifos.path.isdir(self.plotdir):shutil.rmtree(self.plotdir)os.mkdir(self.plotdir)self.didinitplot=True% \end{macrocode}% \end{macro}%% \begin{macro}{inline}% \phantomsection\label{inlinefn}% This function works with |\sage| from the style file (see% \autoref{sec:sagemacro}) to put Sage output into your \LTX file.% Usually, when you use |\label|, it writes a line such as% \begin{center}% |\newlabel{labelname}{{section number}{page number}}|% \end{center}% to the |.aux| file. When you use the |hyperref| package, there are% more fields in the second argument, but the first two fields are the% same. The |\ref| command just pulls in what's in the first field of% the second argument, so we can hijack this mechanism for our own% nefarious purposes. The function writes a |\newlabel| line with a% label made from a counter and the text from running Sage on |s|.%% When the user does |\sage| inside certain displayed math environments% (|align| is the most common culprit) this function will get called% twice with exactly the same arguments. We check to see what labels% we've seen and immediately bail if we've written this label before.%% The |labelname| defaults to the the name used by the usual |\sage|% inline macro, but this function is also used by the |sagecommandline|% environment. It's important to keep the corresponding labels separate,% because |\sage| macros often (for example) appear inside math mode,% and the labels from |sagecommandline| contain a |lstlistings|% environment---pulling such an environment into math mode produces% strange, unrecoverable errors, and if you can't typeset your file, you% can't produce an updated |.sagetex.sage| file to run Sage on to% produce a reasonable |.sagetext.sout| file that will fix the label% problem. So it works much better to use distinct labels for such% things.% \changes{v2.3.3}{2012/01/16}{check label name when comparing against% maximum counter seen; trac ticket 12267}%% We print out the line number so if something goes wrong, the user can% more easily track down the offending |\sage| command in the source% file.%% That's a lot of explanation for a short function:% \begin{macrocode}definline(self,counter,s,labelname='sageinline'):ifcounter<=self.max_counter_seen[labelname]:returnelse:self.max_counter_seen.increment(labelname)iflabelname=='sageinline':self.progress('Inline formula {0}'.format(counter))eliflabelname=='sagecmdline':pass # outputmessagealreadyprintedelse:raiseValueError,'inline() got a bad labelname "{0}"'.format(labelname)self.souttmp.write(r'\newlabel{@' + labelname + str(counter) + '}{{%\n' + s.rstrip() + '}{}{}{}{}}\n')% \end{macrocode}% We are using five fields, just like |hyperref| does, because that% works whether or not |hyperref| is loaded. Using two fields, as in% plain \LTX, doesn't work if |hyperref| is loaded.% \end{macro}%% \begin{macro}{savecmd}% Analogous to |inline|, this method saves the input string |s| to% the temporary |.scmd| file. As an added bonus, it returns a pair% of line numbers in the |.scmd| file, the first and last line of% the newly-added output.% \begin{macrocode}defsavecmd(self,counter,s):self.scmdtmp.write(s.rstrip()+ "\n")begin=self.scmdposend=begin+len(s.splitlines())-1self.scmdpos=end+1returnbegin,end% \end{macrocode}% \end{macro}%% \begin{macro}{blockbegin}% \begin{macro}{blockend}% \phantomsection\label{blocksbeginend}% This function and its companion used to write stuff to the |.sout|% file, but now they just update the user on our progress evaluating a% code block. The verbatim-like environments of% \autoref{sec:verbatim-envs} use these functions.% \begin{macrocode}defblockbegin(self):self.progress('Code block begin...',False)defblockend(self):self.progress('end')% \end{macrocode}% \end{macro}% \end{macro}%% \begin{macro}{doctest}% This function handles the |sageexample| environment, which typesets% Sage code and its output. We call it doctest because the format is% just like that for doctests in the Sage library.% \begin{macrocode}defdoctest(self,counter,str,globals,locals,include_text_output):current_statement=Nonecurrent_lines=Nonelatex_string= ""
line_iterator=(line.lstrip()forlineinstr.splitlines()) # Gobbleseverythinguntilthefirst "sage:..." blockforlineinline_iterator:ifline.startswith("sage: "):breakelse:returnsage_block=0whileTrue: # Ateachassertline.startswith("sage: ")current_statement=line[6:]current_lines= " "+lineforlineinline_iterator:ifline.startswith("sage: "):breakelifline.startswith("..."):current_statement+="\n"+line[6:]current_lines+="\n "+lineelifinclude_text_output:current_lines+="\n "+lineelse:line=None # wereachedthelastline # Nowwehavedigestedeverythingfromthecurrentsage:... to the next one or to the last line # Letushandleitverbatimboxname= "@sageinline%s-code%s"%(counter,sage_block)self.souttmp.write("\\begin{SaveVerbatim}{%s}\n"%verbatimboxname)self.souttmp.write(current_lines)self.souttmp.write("\n\\end{SaveVerbatim}\n")latex_string+= "\UseVerbatim{%s}\n"%verbatimboxnamecurrent_statement=preparse(current_statement)try: # HowtotestwhetherthecodeisanPythonexpressionorastatement?
# Inthefirstcase,wecomputetheresultandincludeitinthelatexresult=eval(current_statement,globals,locals)% \end{macrocode}% The verbatim stuff seems to end with a bit of vertical space, so don't% start the displaymath environment with unnecessary vertical% space---the displayskip stuff is from \S 11.5 of Herbert Vo\ss's% ``\href{http://www.ctan.org/tex-archive/info/math/voss/mathmode/}{Math% Mode}''. Be careful when using \TeX{} commands and Python 3 (or% 2.6+) curly brace string formatting; either double braces or separate% strings, as below.% \begin{macrocode}latex_string+=r"""\abovedisplayskip=0ptplus3pt\abovedisplayshortskip=0ptplus3pt\begin{displaymath}""" + "\n{0}\n".format(latex(result))+r"\end{displaymath}" + "\n"
exceptSyntaxError: # Ifthisfails,weassumethatthecodewasastatement,andjustexecuteitexeccurrent_statementinglobals,localscurrent_lines=current_statement=NoneiflineisNone:breaksage_block+=1self.inline(counter,latex_string)% \end{macrocode}% \changes{v2.2.5}{2010/03/25}{Fix up spacing in sageexample displaymath envs}% \end{macro}%% \begin{macro}{commandline}% This function handles the |commandline| environment, which% typesets Sage code and its output.% \begin{macrocode}defcommandline(self,counter,str,globals,locals,text_output):self.progress('Sage commandline {0}'.format(counter))current_statement=Nonecurrent_lines=Noneline_iterator=(line.lstrip()forlineinstr.splitlines())latex_string=r"\vspace{\sagecommandlineskip}" + "\n"
bottom_skip='' # Gobbleseverythinguntilthefirst "sage:..." blockforlineinline_iterator:ifline.startswith("sage: "):breakelse:returnsage_block=0whileTrue: # Ateachassertline.startswith("sage: ")current_statement=line[6:]current_lines=lineforlineinline_iterator:ifline.startswith("sage: "):breakelifline.startswith("... "):current_statement+= "\n"+line[6:]current_lines+= "\n"+lineelse:line=None # wereachedthelastline # Nowhaveeverythingfrom "sage:" tothenext "sage:"
ifcurrent_lines.find('#@')>=0:escapeoption=',escapeinside={\\#@}{\\^^M}'else:escapeoption=''begin,end=self.savecmd(counter,current_lines)% \end{macrocode}% If there's a space in the filename, we need to quote it for \TeX.% \begin{macrocode}filename=self.name+'.sagetex.scmd'if' 'infilename:filename='"'+filename+'"'latex_string+=r"\lstinputlisting[firstline={0},lastline={1},firstnumber={2},style=SageInput{3}]{{{4}}}".format(begin,end,begin-2,escapeoption,filename)+"\n"
current_statement=preparse(current_statement)try: # isitanexpression?
result=eval(current_statement,globals,locals)resultstr= "{0}".format(result)begin,end=self.savecmd(counter,resultstr)iftext_output:latex_string+=r"\lstinputlisting[firstline={0},lastline={1},firstnumber={2},style=SageOutput]{{{3}}}".format(begin,end,begin-2,filename)+"\n"
bottom_skip=r"\vspace{\sagecommandlineskip}" + "\n"
else:latex_string+=(r"\begin{displaymath}" + "\n" +latex(result)+ "\n" +r"\end{displaymath}" + "\n" )bottom_skip=''exceptSyntaxError: # mustbeastatement!
execcurrent_statementinglobals,localscurrent_lines=current_statement=NoneiflineisNone:breaksage_block+=1latex_string+=bottom_skip+r"\noindent" + "\n"
self.inline(counter,latex_string,labelname='sagecmdline')% \end{macrocode}% \end{macro}%% \begin{macro}{plot}% \phantomsection\label{plotfn}% I hope it's obvious that this function does plotting. It's the Python% counterpart of |\ST@sageplot| described in \autoref{sec:sageplotmacro}. As% mentioned in the |\sageplot| code, we're taking advantage of two% things: first, that \LTX doesn't treat commas and spaces in macro% arguments specially, and second, that Python (and Sage plotting% functions) has nice support for keyword arguments. The |#3| argument% to |\sageplot| becomes |_p_| and |**kwargs| below.% \begin{macrocode}defplot(self,counter,_p_,format='notprovided',**kwargs):ifnotself.didinitplot:self.initplot()self.progress('Plot {0}'.format(counter))% \end{macrocode}% If the user says nothing about file formats, we default to producing% PDF and EPS. This allows the user to transparently switch between% using a DVI previewer (which usually automatically updates when the% DVI changes, and has support for source specials, which makes the% writing process easier) and making PDFs.\footnote{Yes, there's% \texttt{pdfsync}, but full support for that is still rare in Linux, so% producing EPS and PDF is the best solution for now.}% \begin{macrocode}ifformat=='notprovided':formats=['eps','pdf']else:formats=[format]forfmtinformats:% \end{macrocode}% If we're making a PDF and have been told to use |epstopdf|, do so,% then skip the rest of the loop.% \begin{macrocode}iffmt=='pdf'andself.useepstopdf:epsfile=os.path.join(self.plotdir,'plot-{0}.eps'.format(counter))self.progress('Calling epstopdf to convert plot-{0}.eps to PDF'.format(counter))subprocess.check_call(['epstopdf',epsfile])continue% \end{macrocode}% Some plot objects (mostly 3-D plots) do not support saving to EPS or% PDF files (yet), but everything can be saved to a PNG file. For the% user's convenience, we catch the error when we run into such an% object, save it to a PNG file, then exit the loop.% \begin{macrocode}plotfilename=os.path.join(self.plotdir,'plot-{0}.{1}'.format(counter,fmt))try:_p_.save(filename=plotfilename,**kwargs)exceptValueErrorasinst:if'filetype not supported by save'instr(inst):newfilename=plotfilename[:-3]+'png'print' saving {0} failed; saving to {1} instead.'.format(plotfilename,newfilename)_p_.save(filename=newfilename,**kwargs)breakelse:raise% \end{macrocode}% If the user provides a format \emph{and} specifies the |imagemagick|% option, we try to convert the newly-created file into EPS format.% \begin{macrocode}ifformat !='notprovided'andself.useimagemagick:self.progress('Calling Imagemagick to convert plot-{0}.{1} to EPS'.format(counter,format))self.toeps(counter,format)% \end{macrocode}% \end{macro}%% \begin{macro}{toeps}% This function calls the Imagmagick utility |convert| to, well, convert% something into EPS format. This gets called when the user has% requested the ``|imagemagick|'' option to the \ST\ style file and is% making a graphic file with a nondefault extension.% \begin{macrocode}deftoeps(self,counter,ext):subprocess.check_call(['convert',\'{0}/plot-{1}.{2}'.format(self.plotdir,counter,ext),\'{0}/plot-{1}.eps'.format(self.plotdir,counter)])% \end{macrocode}% We are blindly assuming that the |convert| command exists and will do% the conversion for us; the |check_call| function raises an exception% which, since all these calls get wrapped in try/excepts in the |.sage|% file, should result in a reasonable error message if something strange% happens.% \end{macro}%% \begin{macro}{goboom}% \phantomsection\label{macro:goboom}% When a chunk of Sage code blows up, this function bears the bad news% to the user. Normally in Python the traceback is good enough for% this, but in this case, we start with a |.sage| file (which is% autogenerated) which itself autogenerates a |.py| file---and the% tracebacks the user sees refer to that file, whose line numbers are% basically useless. We want to tell them where in the \LTX file% things went bad, so we do that, give them the traceback, and exit% after removing the |.sout.tmp| and |.scmd.tmp| file.% \begin{macrocode}defgoboom(self,line):print('\n**** Error in Sage code on line {0} of {1}.tex! Traceback\ follows.'.format(line,self.filename))traceback.print_exc()print('\n**** Running Sage on {0}.sage failed! Fix {0}.tex and try\ again.'.format(self.filename))self.souttmp.close()os.remove(self.filename+'.sagetex.sout.tmp')self.scmdtmp.close()os.remove(self.filename+'.sagetex.scmd.tmp')sys.exit(int(1))% \end{macrocode}% We use |int(1)| above to make sure |sys.exit| sees a Python integer;% see% \href{http://trac.sagemath.org/sage_trac/ticket/2861#comment:5}{ticket% \#2861}.% \changes{v2.0.2}{2008/04/21}{Make sure sys.exit sees a Python integer}% \end{macro}%% \begin{macro}{endofdoc}% When we're done processing, we have some cleanup tasks. We% want to put the MD5 sum of the |.sage| file that produced the |.sout|% file we're about to write into the |.sout| file, so that external% programs that build \LTX documents can determine if they need to call Sage% to update the |.sout| file. But there is a problem: we write line% numbers to the |.sage| file so that we can provide useful error% messages---but that means that adding non-\ST text to your% source file will change the MD5 sum, and your program will think it% needs to rerun Sage even though none of the actual \ST macros% changed.%% How do we include line numbers for our error messages but still allow% a program to discover a ``genuine'' change to the |.sage| file?%% The answer is to only find the MD5 sum of \emph{part} of the |.sage|% file. By design, the source file line numbers only appear in calls to% |goboom| and pause/unpause lines, so we will strip those lines out.% What we do below is exactly equivalent to running% \begin{center}% \verb+egrep -v '^( _st_.goboom|print .SageT)' filename.sage | md5sum+% \end{center}% in a shell.% \begin{macrocode}defendofdoc(self):sagef=open(self.filename+'.sagetex.sage','r')m=hashlib.md5()forlineinsagef:ifline[0:12] != " _st_.goboom" andline[0:12] != "print'SageT": m.update(line) s = '%' + m.hexdigest() + '% md5sum of corresponding .sage file\(minus "goboom" andpause/unpauselines)\n'self.souttmp.write(s)self.scmdtmp.write(s)% \end{macrocode}% Now, we do issue warnings to run Sage on the |.sage| file and an% external program might look for those to detect the need to rerun% Sage, but those warnings do not quite capture all situations. (If% you've already produced the |.sout| file and change a |\sage| call, no% warning will be issued since all the |\ref|s find a |\newlabel|.)% Anyway, I think it's easier to grab an MD5 sum out of the end of the% file than parse the output from running |latex| on your file. (The% regular expression |^%[0-9a-f]{32}%| will find the MD5 sum. Note that% there are percent signs on each side of the hex string.)%% Now we are done with the |.sout.tmp| file. Close it, rename it, and% tell the user we're done.% \begin{macrocode}self.souttmp.close()os.rename(self.filename+'.sagetex.sout.tmp',self.filename+'.sagetex.sout')self.scmdtmp.close()os.rename(self.filename+'.sagetex.scmd.tmp',self.filename+'.sagetex.scmd')self.progress('Sage processing complete. Run LaTeX on {0}.tex again.'.format(self.filename))% \end{macrocode}% % \changes{v2.1.1}{2009/05/14}{Fix bug in finding md5 sum introduced by% pause facility}% \end{macro}% \endinput%</python>% Local Variables:% mode: doctex% TeX-master: "sagetex"% End: