pythontex icon indicating copy to clipboard operation
pythontex copied to clipboard

How to share variables between pyconsole and pyblock?

Open reckoner opened this issue 9 years ago • 16 comments

I read the documentation, and I suppose this sounds strange, but I want to have python variables shared between the pyconsole and pyblock families.

Is this possible?

Not that I'm using the v0.12 version which came with texlive on my ubuntu box.

Thanks

reckoner avatar Feb 06 '15 00:02 reckoner

That's not currently possible, and would actually be difficult to implement since pyconsole is executed via Python's code module (emulate console) while pyblock is treated as a script. If you can give me an idea of what you are trying to do, I might have some ideas. Also, if you haven't looked at the \pyconc command and pyconcode environment (execute console code without showing anything) and the \pycon command (get console output without showing input), those might help.

If you just want shared startup code between the two, along the lines of pythontexcustomcode, that should be doable.

gpoore avatar Feb 06 '15 01:02 gpoore

What I'm trying to do is talk about a function in a module and then illustrate the use of that function in a console. For example

\begin{pyblock}
def foo(x):
    return 2*x
\end{pyblock}

And this is how you use the function,

\begin{pyconsole}
x = 10
foo(x)
\end{pyconsole}

I hope that makes sense. I understand the narrative structure I'm going for is a little bit outside of the design plan of PythonTeX. The problem with your suggestion of using the pyconcode environment is that it results in a lot of duplicated code (one block to be typeset and the other not), which would make it hard for me to maintain as I develop the narrative.

Thanks again

reckoner avatar Feb 06 '15 14:02 reckoner

Something like this should do what you want. This creates a pyconcodeblock environment using fancyvrb's VerbatimOut. pyconcodeblock saves its contents to a temp file. Then this code is brought into the console session and is also typeset. Note that the code is not brought into the default non-console session, so it is not accessible in pycode etc. But if you needed that, you could use the same exec trick with \pyc.

\documentclass{article}

\usepackage{pythontex}

\newenvironment{pyconcodeblock}%
 {\VerbatimEnvironment
  \begin{VerbatimOut}{temp.py}}%
 {\end{VerbatimOut}%
  \pyconc{exec(compile(open('temp.py', 'rb').read(), 'temp.py', 'exec'))}%
  \inputpygments{python}{temp.py}}

\begin{document}

\begin{pyconcodeblock}
def foo(x):
    return 2*x
\end{pyconcodeblock}

\begin{pyconsole}
x = 10
foo(x)
\end{pyconsole}

\end{document}

gpoore avatar Feb 06 '15 20:02 gpoore

Could we/you consider a de-coupling of typesetting (display) and execution? Something like: \begin[show=[true, false, console], execute=[true, false], session=mysession]{pythontex} \end{pythontex} where all combinations of show and execute is legal (where show=false, execute=false is a wierd way of commenting it out, so maybe a warning?).

In addition to a similar function to grab code from an external file, with optional, start_line and end_line arguments.

I have been using pythontex since the start, and the current environment setup still confuses me (I always need to look it up).

On 06 Feb 2015, at 21:05, Geoffrey Poore [email protected] wrote:

Something like this should do what you want. This creates a pyconcodeblock environment using fancyvrb's VerbatimOut. pyconcodeblock saves its contents to a temp file. Then this code is brought into the console session and is also typeset. Note that the code is not brought into the default non-console session, so it is not accessible in pycode etc. But if you needed that, you could use the same exec trick with \pyc.

\documentclass {article}

\usepackage {pythontex}

\newenvironment{pyconcodeblock}%

{ \VerbatimEnvironment

\begin{VerbatimOut}{temp.py}}%

{ \end{VerbatimOut}%

\pyconc{exec(compile(open('temp.py', 'rb').read(), 'temp.py', 'exec'))}%

\inputpygments {python}{temp.py}}

\begin {document}

\begin {pyconcodeblock} def foo(x): return 2*x

\end {pyconcodeblock}

\begin {pyconsole} x = 10 foo(x)

\end {pyconsole}

\end{document} � Reply to this email directly or view it on GitHub.

obtitus avatar Feb 06 '15 20:02 obtitus

@obtitus That's something I would like to do soon. About 2 years ago, I wrote up a list of future features. A generic environment like you describe, and per-session customization for execution conditions, are the main things that remain to be implemented from that list.

I would want a switch for showing code and for showing stdout and/or stderr. And, like you suggest, for turning execution on and off. I'm not sure about show=console, though. With the current implementation, code is executed either as a script or via the code module's console emulation. In principle, it should be possible to go in and out of console mode. But I'm guessing that that would require a highly customized version of the code module.

I've opened a new issue for this (#56), for continued discussion. I will try to post my old notes on this under that issue soon.

gpoore avatar Feb 06 '15 20:02 gpoore

I am afraid, this workaround does not work for me. The example above yields the following error:

----  Messages for pycon:default:default  ----
* PythonTeX stderr - error near line 17 in console code:
    Console code is not typeset, and should have no output
  >>> exec(compile(open('temp.py', 'rb').read(), 'temp.py', 'exec'))
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  IOError: [Errno 2] No such file or directory: 'temp.py'

--------------------------------------------------
PythonTeX:  test - 1 error(s), 0 warning(s)

I am using XeLaTeX invoked with xelatex test && /usr/local/texlive/2014/texmf-dist/scripts/pythontex/pythontex.py test && xelatex test && xelatex test and PythonTeX v0.13 is used here.

XeTeX 3.14159265-2.6-0.99991 (TeX Live 2014)
kpathsea version 6.2.0
Copyright 2014 SIL International, Jonathan Kew and Khaled Hosny.
There is NO warranty.  Redistribution of this software is
covered by the terms of both the XeTeX copyright and
the Lesser GNU General Public License.
For more information about these matters, see the file
named COPYING and the XeTeX source.
Primary author of XeTeX: Jonathan Kew.
Compiled with ICU version 53.1; using 53.1
Compiled with zlib version 1.2.8; using 1.2.8
Compiled with FreeType2 version 2.5.3; using 2.5.3
Compiled with Graphite2 version 1.2.4; using 1.2.4
Compiled with HarfBuzz version 0.9.28; using 0.9.28
Compiled with libpng version 1.6.10; using 1.6.10
Compiled with poppler version 0.25.2
Compiled with fontconfig version 2.11.1; using 2.11.0

The following file structure is created:

% find .
.
./test.pdf
./test.log
./pythontex-files-test
./pythontex-files-test/test.pytxmcr
./pythontex-files-test/pythontex_data.pkl
./pythontex-files-test/test.pytxpyg
./test.tex
./test.pytxcode
./test.aux
./temp.py

If I fix the workaround to with

  \inputpygments{python}{../temp.py}}

instead of

  \inputpygments{python}{temp.py}}

I get

Output written on test.pdf (1 page).
Transcript written on test.log.
This is PythonTeX v0.13
Traceback (most recent call last):
  File "/usr/local/texlive/2014/texmf-dist/scripts/pythontex/pythontex.py", line 62, in <module>
    pythontex.main()
  File "/usr/local/texlive/2014/texmf-dist/scripts/pythontex/pythontex2.py", line 2607, in main
    do_multiprocessing(data, temp_data, old_data, engine_dict)
  File "/usr/local/texlive/2014/texmf-dist/scripts/pythontex/pythontex2.py", line 1329, in do_multiprocessing
    result = task.get()
  File "/usr/lib/python2.7/multiprocessing/pool.py", line 558, in get
    raise self._value
AttributeError: 'NoneType' object has no attribute 'decode'

Any ideas?

meisterluk avatar Apr 27 '15 13:04 meisterluk

@meisterluk Starting in PythonTeX version 0.14, the default working directory is the document root directory, rather that the pythontex-files* directory (since that was causing too much confusion). The above code assumes this for working with the path to temp.py. If you add \setpythontexworkingdir{.} to the preamble of the example, right after loading pythontex, the example should work as-is with PythonTeX 0.13.

Otherwise, you would need to modify the paths in the exec() to get everything to work. Probably something like this:

\newenvironment{pyconcodeblock}%
 {\VerbatimEnvironment
  \begin{VerbatimOut}{temp.py}}%
 {\end{VerbatimOut}%
  \pyconc{exec(compile(open('../temp.py', 'rb').read(), '../temp.py', 'exec'))}%
  \inputpygments{python}{temp.py}}

gpoore avatar Apr 27 '15 17:04 gpoore

@gpoore The \setpythontexworkingdir{.} fix works perfectly fine for me. Thank you :-)

meisterluk avatar Apr 27 '15 17:04 meisterluk

@gpoore Your pyconcodeblock environment works perfectly. However, I wanted to execute some code (which defines a function), use it in a pyconsole, and only later typeset it (this is a book with exercises that illustrate the usage of a function, and only later explain how to write it). Furthermore I need to do this multiple times. I tried using a variation on what you wrote along the following lines (the idea is that rather than writing to a file called temp.py, this can specified by a parameter to the environment):

\newenvironment{pyconcodeblock}[1]%
 {\VerbatimEnvironment
  \begin{VerbatimOut}{#1.py}}%
 {\end{VerbatimOut}%
  \pyconc{exec(compile(open('#1.py', 'rb').read(), '#1.py', 'exec'))}}

but the above generates errors (Illegal parameter number in definition of \pytx@argdetok. \end{pyconcodeblock}).

Does anyone have any advice on how to parametrize (with the filename) @gpoore's solution?

banbh avatar May 22 '16 23:05 banbh

@banbh Environment arguments are only available at the beginning of the environment, not at the end. You will probably want to store #1 in a temp macro, and then use that at the end of the environment. More details.

gpoore avatar May 23 '16 02:05 gpoore

@gpoore Thank you. I read through the tex.stackexchange answer, which was very helpful. Here is my new attempt:

\newenvironment{pyconcodeblck}[1]%
 {\newcommand{\snippetfile}{#1.py}
  \VerbatimEnvironment
  \begin{VerbatimOut}{\snippetfile}}%
 {\end{VerbatimOut}%
  \pyconc{exec(compile(open('\snippetfile', 'rb').read(), '\snippetfile', 'exec'))}}

But now I have a new problem. It seems that the macro does not get expanded in the \pyconc call. When I run pythontex main (my latex file is main.tex) I get

This is PythonTeX 0.14

----  Messages for pycon:default:default  ----
* PythonTeX stderr - error near line 65 in console code:
    Console code is not typeset, and should have no output
  >>> exec(compile(open('\snippetfile ', 'rb').read(), '\snippetfile ', 'exec'))
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  IOError: [Errno 2] No such file or directory: '\\snippetfile '

--------------------------------------------------
PythonTeX:  main - 1 error(s), 0 warning(s)

Any suggestions on how to get macro-expansion to happen?

banbh avatar May 24 '16 01:05 banbh

@banbh All of the commands like \pyconc take literal arguments, so you will have to do the expansion beforehand. You can try something like this (untested).

\newcommand{\expandpyconc}[1]{\expandafter\reallyexpandpyconc\expandafter{#1}}
\newcommand{\reallyexpandpyconc}[1]{\pyconc{exec(compile(open('#1', 'rb').read(), '#1', 'exec'))}}

...

{\end{VerbatimOut}%
  \expandpyconc{\snippetfile}}

gpoore avatar May 24 '16 20:05 gpoore

@gpoore Thank you! It works perfectly. I will post a MWE example in a day or two.

banbh avatar May 25 '16 03:05 banbh

Here is a more-or-less MWE of the macros @gpoore proposed:

\documentclass{article}
\usepackage{pythontex}

\newcommand{\expandpyconc}[1]{\expandafter\reallyexpandpyconc\expandafter{#1}}
\newcommand{\reallyexpandpyconc}[1]{\pyconc{exec(compile(open('#1', 'rb').read(), '#1', 'exec'))}}

\newenvironment{pyconcodeblck}[1]%
 {\newcommand{\snippetfile}{snippet-#1.py}
  \VerbatimEnvironment
  \begin{VerbatimOut}{\snippetfile}}%
 {\end{VerbatimOut}%
  \expandpyconc{\snippetfile}}

\newcommand{\typesetcode}[1]{\inputpygments{python}{snippet-#1.py}}

\begin{pyconcodeblck}{temp}
def foo(x):
    return 2*x
\end{pyconcodeblck}

\begin{pyconcodeblck}{temp2}
def foobar(x):
    return foo(foo(x))
\end{pyconcodeblck}

\begin{document}
Here is an example of some functions:
\begin{pyconsole}
foo(10)
foobar(10)
\end{pyconsole}
Here is foo:
\typesetcode{temp}
And here is foobar:
\typesetcode{temp2} 
\end{document}

It works perfectly for me.

banbh avatar May 26 '16 02:05 banbh

Hello.

First, thank you very much for your answers on this topic.

In the context of a beamer use of pyconcodeblock, I would like to now if there is a way to box the blocks produced by this function (or pyconc). I was not able to find such a way in the PythonTex manual.

Thank you in advance for your help.

cccccp avatar Sep 19 '16 14:09 cccccp

@cccccp There isn't a built-in way to put boxes around the console commands and environments. But you can use the tcolorbox package or other framing packages to put boxes around environments, and \fcolorbox etc. for commands.

gpoore avatar Sep 20 '16 02:09 gpoore