Exsheets and pythontex compatibility

The question and solution environments use internally the __exsheets_questions_internal: and __exsheets_solution_internal: environments that are defined with \NewEnviron from the environ package.

The contents of an environment defined with \NewEnviron is absorbed as a macro argument, which makes any verbatim-like command or environment illegal inside it.

You'd get the same problem with \verb or verbatim in a question or solution environment.

The package documentation discusses the problem in Part III, where the exsheets-listing subpackage is described. Maybe something like that can be done also for allowing pycode environments, but the syntax of lstquestion and lstsolution doesn't seem like the better for complex usage.


egreg's answer covers most of the details. Here is a little additional information.

The lstquestion and lstsolution environments allow listings, by writing environment contents to an auxiliary file and then reading back the auxiliary file at the appropriate point. Since the environment is saved as a file rather than captured as a macro argument, verbatim material works fine. The same approach could be applied to allow pycode and similar environments to function. You would need versions of question and solution that write their contents to an auxiliary file rather than using \NewEnviron. This is essentially the same approach that beamer takes with its fragile option.

As egreg has noted, \verb wouldn't work within a question or solution environment. This might raise the question of why \pyc and similar pythontex commands do work, since in general they need verbatim arguments. The answer is that \pyc works very differently from \verb. \pyc and similar commands use \scantokens to retokenize their arguments, which essentially restores the arguments to verbatim status. However, since the arguments are initially tokenized when the contents of the environment is read as a macro argument, this approach fails if the argument to \pyc contains a # or % character, or an unmatched brace. (I have some ideas about working around this in a future version of pythontex with a properly defined \#, \%, \{, and \}.)

That explains how something like \pyc{...} works, where the argument is delimited by curly braces. The case where another character is used (for example, \pyc/.../) is a little trickier. \pyc takes the first delimiting character and, on the fly, creates a macro that will capture everything until the character appears again. Then it invokes this new macro to actually capture the argument. This makes the argument-capturing mechanism essentially catcode-independent (again, with the exception of #, %, and unmatched braces).