3

I'm having a bit of an issue trying to get a piece of code up and running. I'm working on extending python.sty file for embedding python into a LaTex document (adding persistent data and the ability to pass the results of LaTex commands to Python). At the high level, this style file provides functions that let you do:

\begin{python}
print r"Raw output, goes right into the Tex file."
\end{python}

I'm looking to make a simple function that allows passing data from Tex/Latex to the body of that python code but I'm running into real fun times with fragility issues. Foolishly, I assumed that something along these lines would work:

\xdef\internalSetPythonVariable#1#2{\PythonPersistenceInternalName["#1"] = r"#2"}
\xdef\setPythonVariable#1#2{\begin{python}\internalSetPythonVariable{#1}{#2}\end{python}}

Clearly, that one wouldn't work because the python environment messes with all the commands inside- so that was out. But I thought, "Fine, I'll just make sure that the stuff inside the python environment is all text by the time we even START that stupid environment." So I tried something like this:

\xdef\internalSetPythonVariable#1#2{\PythonPersistenceInternalName["#1"] = r"#2"}
\xdef\setPythonVariable#1#2{\expandafter\begin{python}\internalSetPythonVariable{#1}{#2}\end{python}}

However, the expandafter doesn't seem to want to work on the \begin stuff and now I'm a bit stuck. I keep getting the ever-so-helpful "! Argument of \reserved@a has an extra }" error. In general, I think this is a fairly simple issue in concept: I want to be able to evaluate everything inside an environment to text before the environment ever starts.

To note, there don't seem to be many other ways to accomplish this. By the time the environment is running in full gear, it's treating everything as fully text-only (no macros, no anything). Additionally, since I'm using these functions to read/write variables, I need them to be one-liners that take arguments. And it would seem rather silly to have to make a whole new environment that accepts optional params just for this purpose.

So does anybody know how to get those args fully expanded before things start? I've tried fiddling with it like crazy and gotten nowhere. With that said, if I can accomplish this- I'm hoping avoid having to ever do mundane junk with TeX again (see: conditionals, loops, non-typesetting macros).

6
  • 1
    Additionally, while I don't have the ability to- I would love it if someone would tag this post with "Python" also. It befuddles me that the tag doesn't exist here, given that there's at least a few different projects relating to Python driving or being driven by LaTex/Tex.
    – Namey
    May 10, 2011 at 10:17
  • If you are interested in scripting TeX like this, have you looked at LuaTeX? May 10, 2011 at 11:51
  • I have looked into Lua a bit, but there were a couple of things that kept me from going that route. Firstly, there's not a lot of packages for it compared to Python for things I'd be likely to do. Secondly, I've been using Python for a bunch of other projects and would be useful to hook into TeX. Thirdly, I find Python to just plain be a good language, one of my favorites. Lua just doesn't seem to have all the features I'd want.
    – Namey
    May 10, 2011 at 18:10
  • Also, many thanks to Philipp for adding the Python tag, I appreciate it.
    – Namey
    May 10, 2011 at 18:55
  • Still plugging at this. Other possible solutions I have thought of: \detokenize followed by \scantokens to emulate writing to a file and then reading from it. However, this doesn't seem to be doing what it should. Doesn't fail, but it doesn't run anything when it scans. (text after scantokens is the same as before it)
    – Namey
    May 11, 2011 at 7:50

2 Answers 2

2

You need to make sure that the \begin and \end macros aren't expanded in the \xdef. You need to use \noexpand not \expandafter for this. The latter only affects the order of expansion.

\xdef\setPythonVariable#1#2{\noexpand\begin{python}\internalSetPythonVariable{#1}{#2}\noexpand\end{python}}
5
  • Awesome, thanks. I'll try this out. I think this highlights a bit of a misunderstanding that I had about macros. From reading this, "not expanding" a macro means that it expands at runtime I assume? (I mean, has to be evaluated sometime). Expanding it means that it expands immediately on definition (in some order, which one painstakingly imposes). Am I correct on that?
    – Namey
    May 10, 2011 at 18:54
  • @Namey: Yes, that's right. The \edef primitive (and its global sibling \xdef) expands the content before assigning to the given macro. The \noexpand will protect the following macro (or other token) to be expanded during that process. Of course that macro(s) (here \begin and \end) will finally be expanded when the macro which contains it (here \setPythonVariable) is used/expanded. May 10, 2011 at 19:01
  • I'm afraid this still doesn't seem to accomplish what I want. Due to the internal mechanisms of how begin and end occur, the begin command doesn't correctly identify the end in this form (it thinks there is one too many } or it swallows the whole file).
    – Namey
    May 10, 2011 at 20:15
  • @Namey: I was afraid that his might happen. It might need the \end{python} on a line of its own. You could write the code to an external file instead and read this back in... May 10, 2011 at 20:25
  • Yah, I was kind of afraid of the whole "writing to file and reading." Is there any way to make a macro that accomplishes the same thing without having to actually... write to a file? Like a way to turn everything into string and then have an equivalent of an "eval" run on it? I'm trying to keep the file generation to a minimum.
    – Namey
    May 11, 2011 at 0:04
1

One answer I can propose to this that worked for me was to use some commands that I basically treat as temporary variables to pass the required information. It's kind of ugly and I can't say I like using a temp variable approach but it works and that's enough for me. Moreover, while I'm not a huge fan of using temp variable type commands- there's little risk of them being monkeyed with between when they're defined and when they're used (not like TeX is going multithreaded, to my knowledge...).

So here's one proposed solution. I can't say it's ideal, but it works all right:

\newcommand\PythonActiveVariableName{}      % Store name of active variable
\newcommand\PythonActiveVariableValue{} % Value of the active variable

\gdef\getPythonVariable#1{\renewcommand{\PythonActiveVariableName}{#1}%
\getPythonActiveVariableValue}

\gdef\setPythonVariable#1#2{\renewcommand{\PythonActiveVariableName}{#1}%
\renewcommand{\PythonActiveVariableValue}{#2}\setPythonActiveVariableValue}

Unlike the prior thing, this subverts using the python environment and instead uses some custom commands that do a similar thing but for fixed text. While the python environment in python.sty is very fragile with respect to trying to pass things, it's pretty simple to make static duplicates (which is what \getPythonActiveVariableValue and \setPythonActiveVariableValue do- a macro that works without args).

So, this is probably as good as I'm going to get. With that said, I'll leave this open a bit longer and see if there are any other thoughts. I still feel like something with detockenizing and tokenizing makes a lot of sense, and would be portable for a number of environments.

All told, I still find it confusing that LaTex has the oddity that if you put:

\begin{something}
<text>
\end{something}

Into your text, then it will work. But then if you do a similar thing inside a macro, sometimes it will fail. Do macros use different catcodes or something? With a better idea on why the rules are inconsistent, I'd probably be able to make a better fix.

You must log in to answer this question.

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