gpoore / pythontex

A LaTeX package that executes Python and other code in LaTeX documents, and includes the output
882 stars 101 forks source link

link python scripts - a collection of use cases. #20

Closed TheChymera closed 10 years ago

TheChymera commented 11 years ago

Hi, I have some rather complex data processing scripts which I use to output graphics via matplotlib - how can I link them in a .tex document? (like simply call the script and have latex grab the output).

Is this possible?

gpoore commented 11 years ago

One option would be just to use a makefile (or similar approach) to run the scripts when needed, and then \includegraphics the output. If you already have scripts that can handle things, trying to incorporate PythonTeX might ultimately make things more complex than necessary.

If you wanted to use PythonTeX to actually run the scripts, you'd probably want something like this (untested):

\begin{pycode}
import subprocess
pytex.add_dependencies("data.txt")
pytex.add_dependencies("plotting_script.py")
subprocess.call(["python", "plotting_script.py"])
\end{pycode}

You would want to track the data as a dependency to make sure that the plot is recreated every time the data changes. You would want to track the plotting script to account for any changes there as well. Depending on how everything works, you might want to have a way to manage where the resultant graphics are saved.

TheChymera commented 11 years ago

so you mean I add this to run the script

\begin{pycode}
import subprocess
pytex.add_dependencies("data.txt")
pytex.add_dependencies("plotting_script.py")
subprocess.call(["python", "plotting_script.py"])
\end{pycode}

And then I include the graphics from the hard link where the script outputs them? What if I don't want my script to physically save the data? can I "grab" the shown plot somehow?

gpoore commented 11 years ago

If the data is generated by the script, you wouldn't have to save it. But you would have to save the plot and then \includegraphics. Of course, you could automate the saving and even the generation of the \includegraphics (for example, you could print() an appropriate \includegraphics with PythonTeX).

I believe the only way around actually saving the plot--the only way to "grab" it--would be to create an interface using the luatex engine that would allow TeX to directly input the Python output. That would probably have to be relatively low-level, and wouldn't be something that PythonTeX would be relevant for. Even if you had such an interface, it would mean that you would have to recreate your plot every time the document is compiled (no "cached" version), which will often be an unacceptable performance penalty.

TheChymera commented 11 years ago

Sorry, I meant "What if I don't want my script to physically save the plot?"

What if I include a run_scripts.tex file which includes this

\begin{pycode}
import subprocess
pytex.add_dependencies("data.txt")
pytex.add_dependencies("plotting_script.py")
subprocess.call(["python", "plotting_script.py"])
\end{pycode}

for every single script/data combination I need; and then simply \includegraphics from the locations where I tell the scripts to save my files?

Would that only recreate my plots if the scripts/data change? or would it always recreate them at compile time? how does pythinTeX track if files have been changed (I actually did not know it offers this functionality) - date stamps? checksums?

gpoore commented 11 years ago

By default, a session will only be re-executed when its code is modified (detected by hashing) or it produces errors. Of course, that doesn't cover the possibility of modified external files or imported code.

The pytex.add_dependencies() method tracks one or more files (comma separated) for modification via timestamp. Any time PythonTeX runs and detects modified dependencies, the entire session is executed again. If you want hashes, just use the command-line option --hashdependencies or the package option hashdependencies.

Note on internal workings: The names of files specified in add_dependencies() are ultimately passed back to PythonTeX and stored for future runs. Everything on the list of dependencies is checked for modification every time pythontex.py is invoked. But the code in which the dependencies are specified only runs if it is modified or the dependencies are modified. The code doesn't have to be run for dependencies to be checked.

TheChymera commented 11 years ago

ok, this actually all sounds fabulous - what about parsing options to my script? When I run my script from the cli I want to have a figure pop-up, however when I run it for my .tex document I want no plot shown, but rather a figure saved. I was thinking of maybe setting some variable in my python script which I can somehow override in the subprocess.call(["python", "plotting_script.py"]) call...

What would be the proper way of doing this?

Also, if I am using some sort of python config file - is there any way to runtime-override it when calling my subprocess?

gpoore commented 11 years ago

You probably want to look at sys.argv. argparse is a standard module for parsing command-line arguments and settings default values, but there are lots of others.

obtitus commented 11 years ago

The way I do this is something like this, in plotting_script.py:

import pylab as plt
def main(*args, **kwargs):
  <plotting code>

if __name__ == '__main__':
  main("data.txt") # or sys.argv[1] or whatever
  plt.show()

And in the tex document:

\begin{pycode}
import subprocess
pytex.add_dependencies("data.txt")
pytex.add_dependencies("plotting_script.py")
import plotting_script
plotting_script.main("data.txt")
fig = latexFigure(savefig())
\end{pycode}
\pylab{fig}

Where you can find savefig and latexFigure at the wiki: https://github.com/gpoore/pythontex/wiki/matplotlib

The advantage is that the saved file can now scale with the document which I intend to insert it into. Say I need a smaller font for a article class and a larger one for a beamer presentation.

TheChymera commented 11 years ago

and I can do plotting_script.main("data.txt", kwarg1=X, kwarg2=Y) to pass specific arguments?

obtitus commented 11 years ago

Correct.

The beauty of it is that you can now pass your data into the latex document, say plotting_script contains

def main(filename, kwarg1, kwarg2):
  data = readFile(filename)
  plotData(data, kwarg1, kwarg2)
  return data

(where you would need to define readFile and plotData...)

Then the latex code can look something like this

\begin{pylabcode}
pytex.add_dependencies("data.txt")
pytex.add_dependencies("plotting_script.py")
import plotting_script
data = plotting_script.main("data.txt")
fig = latexFigure(savefig('data'))
\end{pycode}

\pylab{fig}
In figure \ref{pic:data} we see that the 
maximum is \pylab{max(data)}. % or some other metric, mean(data), sum(data), len(data)...

If, for some reason, your data now changes, the python code will be re-executed and both your figure and the text will be updated.

TheChymera commented 11 years ago

what do I do (instead of pytex.add_dependencies("data.txt") ) if I have multiple (dozens or hundreds) of data files (one per participant in my case).

TheChymera commented 11 years ago

aaaaand, I'm experiencing another issue, namely (with this code):

\begin{pycode}
import subprocess
import imp
pytex.add_dependencies("src/faceRT/results/px6")
pytex.add_dependencies("src/faceRT/analysis/RTforCategories.py")
foo = imp.load_source('RTforCategories', '/home/chymera/src/faceRT/analysis/RTforCategories.py')
RTforCategories.main()
\end{pycode}

The import of my plotting script works just dandy, BUT the imports from within my script are not handled well - you can see RTforCategories.py here.

obtitus commented 11 years ago

This has very little to do with pythontex, please consider asking your question on some python forum.

Your first problem is solved by a loop.

for f in our_list: pytex.add_dependencies(f)

Your second problem is due to data_functions not being present on your pythonpath, one solution is something like this: import sys sys.path.append(absolute_pathto data_functions.py)

On 10. okt. 2013, at 14:55, Horea Christian wrote:

aaaaand, I'm experiencing another issue, namely (with this code):

\begin{pycode} import subprocess import imp pytex.add_dependencies("src/faceRT/results/px6") pytex.add_dependencies("src/faceRT/analysis/RTforCategories.py") foo = imp.load_source('RTforCategories', '/home/chymera/src/faceRT/analysis/RTforCategories.py') RTforCategories.main()

\ end{pycode} The import of my plotting script works just dandy, BUT the imports from within my script are not handled well (http://bpaste.net/show/139308/) - you can see RTforCategories.py at https://github.com/TheChymera/facesRT/blob/master/analysis/RTforCategories.py

— Reply to this email directly or view it on GitHub.

TheChymera commented 11 years ago

Ok, all is almost running - now just the ConfigParser won't work if I call it from within pythontex.

My code:

\documentclass[12pt]{article} 
\usepackage{graphicx} 
\usepackage{wrapfig} % Allows in-line images 
\usepackage{pythontex} 
\setpythontexworkingdir{.} 
\begin{document} 

This is an example of using pythontex 

\begin{pycode} 
print(3+3)
\end{pycode} 

\begin{pycode}
import subprocess
import sys
pytex.add_dependencies('/home/chymera/src/faceRT/analysis/RTforCategories.py')
pytex.add_dependencies('/home/chymera/src/faceRT/analysis/data_functions.py')
pytex.add_dependencies('/home/chymera/src/faceRT/analysis/gen.cfg')
sys.path.append('/home/chymera/src/faceRT/analysis/')
import RTforCategories
RTforCategories.main()
\end{pycode}

\end{document}

You can find the scripts I call here.

My error output is

chymera@desktophost ~/wip $ pythontex.py demo.tex -v
This is PythonTeX v0.12

* PythonTeX will run the following processes:
    - Code process py:default:default

----  Messages for py:default:default  ----
  Traceback (most recent call last):
* PythonTeX stderr - error on line 22:
    File "<outputdir>/py_default_default.py", line 71, in <module>
      RTforCategories.main()
    File "/home/chymera/src/faceRT/analysis/RTforCategories.py", line 14, in main
      data_all = get_and_filter_results(experiment, source, prepixelation)
    File "/home/chymera/src/faceRT/analysis/data_functions.py", line 27, in get_and_filter_results
      else: prepixelation = config.getint('Data', 'prepixelation')
    File "/usr/lib64/python2.7/ConfigParser.py", line 359, in getint
      return self._get(section, int, option)
    File "/usr/lib64/python2.7/ConfigParser.py", line 356, in _get
      return conv(self.get(section, option))
    File "/usr/lib64/python2.7/ConfigParser.py", line 607, in get
      raise NoSectionError(section)
  ConfigParser.NoSectionError: No section: 'Data'

--------------------------------------------------
PythonTeX:  demo - 1 error(s), 0 warning(s)
obtitus commented 11 years ago

Hmm, might be path related again. I assume the scipt works fine outside of pythontex?

From scanning your code: are you sure the right config file is being used, I belive the cullprit might be the: cfg_file = filter(lambda x: x.endswith('.cfg'), listdir(path.dirname(path.realpath(file))))

The error message tells you that python is unable to find "Data" in the config file, which is a strong indication that the wrong config file is being read (or maybe it's not reading a config file at all?).

Good luck tracing down the problem.

On 13. okt. 2013, at 20:34, Horea Christian wrote:

Ok, all is almost running - now just the ConfigParser won't work if I call it from within pythontex.

My code:

\documentclass[12pt]{article}

\usepackage{graphicx}

\usepackage{wrapfig} % Allows in-line images \usepackage{pythontex}

\setpythontexworkingdir{.}

\begin{document}

This is an example of using pythontex

\begin{pycode}

print(3+3)

\end{pycode}

\begin{pycode}

import subprocess import sys pytex.add _ dependencies('/home/chymera/src/faceRT/analysis/RTforCategories.py') pytex.add dependencies('/home/chymera/src/faceRT/analysis/data functions.py') pytex.add _ dependencies('/home/chymera/src/faceRT/analysis/gen.cfg') sys.path.append('/home/chymera/src/faceRT/analysis/') import RTforCategories RTforCategories.main()

\end{pycode}

\end{document} You can find the scripts I call here.

My error output is

chymera@desktophost ~/wip $ pythontex.py demo.tex -v This is PythonTeX v0.12

  • PythonTeX will run the following processes:
    • Code process py:default:default

---- Messages for py:default:default ---- Traceback (most recent call last):

  • PythonTeX stderr - error on line 22: File "/py_default_default.py", line 71, in RTforCategories.main() File "/home/chymera/src/faceRT/analysis/RTforCategories.py", line 14, in main data_all = get_and_filter_results(experiment, source, prepixelation) File "/home/chymera/src/faceRT/analysis/data_functions.py", line 27, in get_and_filter_results else: prepixelation = config.getint('Data', 'prepixelation') File "/usr/lib64/python2.7/ConfigParser.py", line 359, in getint return self._get(section, int, option) File "/usr/lib64/python2.7/ConfigParser.py", line 356, in _get return conv(self.get(section, option)) File "/usr/lib64/python2.7/ConfigParser.py", line 607, in get raise NoSectionError(section) ConfigParser.NoSectionError: No section: 'Data'

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

— Reply to this email directly or view it on GitHub.

gpoore commented 11 years ago

@TheChymera At this point, you're getting into modules I'm not familiar with and that aren't really related to PythonTeX. I don't have any suggestions beyond what @obtitus has already given, except that in data_functions.py, line 25, I think you want the code after the else: indented.

If you can come up with an example that runs fine without PythonTeX, but gives errors with PythonTeX, that's something I could dig into further.

TheChymera commented 11 years ago

@gpoore, my code is an example of something that runs fine outside PythonTeX, but not inside it. @obtitus, yes, you are right, it works well outside pythontex, and loads the correct data file (there is only one in that directory, it) I find it hard to debug because print calls which I put in the script won't get printed when I run pythontex (even with -v) - which is the condition I want to debug.

gpoore commented 11 years ago

@TheChymera Everything that is printed is redirected to file by PythonTeX so it can be included in the document. If you run PythonTeX, while using the package option keeptemps in the document, there will be a .out file in the PythonTeX output directory that contains all the stdout. So you could look in there. Otherwise, you could use the package option makestderr and then use \printpythontex and \stderrpythontex after each block of code to see what it produces.

Alternatively, after you've used keeptemps once, a copy of all the scripts that PythonTeX executes will be left in the output directory. You could try to run those directly, using python <script> --manual. The --manual flag tells the script that you are running it yourself, rather than through PythonTeX's automated system, and tries to make adjustments accordingly so that things should still work.

TheChymera commented 11 years ago

Hi again - thanks your your input! I was able to debug the issue (apparently I did not specify an absolute path for my config file, which confused the script when it was called by pythonTeX and not from its own directory).

In any case, having solved that issue I can move on to the next :) you suggested I use savefig and latexFigure from here. That sadly fails like so - any ideas what's wrong?

And here is my current testing code.

gpoore commented 11 years ago

The pythontexcustomcode environment has a required argument, {py}, {sympy}, {pylab}, etc.

TheChymera commented 11 years ago

@gpoore you mean I should replace the %s signs with one of those alternatives?

gpoore commented 11 years ago

@TheChymera You need \begin{pythontexcustomcode}[begin]{pylab} on line 6.

TheChymera commented 11 years ago

@gpoore Thanks for that :) Do you have any idea why I am getting this error with this code?

gpoore commented 11 years ago

@TheChymera You need pytex.input_type, pytex.input_session, pytex.input_group with underscores _. The wiki page was using an old, outdated syntax from some time back; I've updated it.

TheChymera commented 11 years ago

@gpoore I still get this error. :(

gpoore commented 11 years ago

@TheChymera Apparently I failed to completely update the wiki. Some things were renamed between the beta releases and the first full release (it's stable now). You need

name = "autoFig{}{}{}-{}".format(pytex.input_family, pytex.input_session, pytex.input_restart, figCount)

So pytex.input_type -> pytex.input_family and pytex.input_group -> pytex.input_restart.

TheChymera commented 11 years ago

@gpoore I appear to be getting closer - but still not quite there. The current error. Also, what'S line 51 about?

gpoore commented 11 years ago

@TheChymera I've completely rewritten the code on the wiki. I fixed some bugs and made some modifications. Since the code was from @obtitus, I will let @obtitus decide if anything needs to be changed back to the way it was for the long term.

There also seem to be some issues with GitHub's markdown. Inside a code block (````), it is taking **four** backslashes (\\) to produce two (\`) in the output. So it looks like a bug in GitHub's markdown. But this only happens on the wiki. I just tried it here in the issues, and everything works as expected in code blocks. In any case, that was eliminating some escapes.

The wiki code should work now, which should finally get things working for you.

obtitus commented 11 years ago

Neat, I made two small adjustments to the wiki (I have not tested the code, so there might be other small bugs).

@TheChymera Your error looks really wierd, but seems to be matplotlib backend related? Just try a call to: plt.savefig('name.pdf') both in a standard python session and within a pythontex block.

TheChymera commented 11 years ago

I think you still have to change def latex_figure to def latexFigure. In any case, that does not seem to solve all issues.

Many thanks for walking me through this!

obtitus commented 11 years ago

Hmm, could you post your entire code again? Preferably as a minimal example...

Do you have other backends you could try? This could be something odd with your setup (or just a typo somewhere).

TheChymera commented 11 years ago

Sure - take a look at my code - what do you mean by “other backends”?

gpoore commented 11 years ago

@TheChymera Your code works fine on my system. What version of matplotlib are you using? If it isn't recent, that might be an issue.

Regarding the function name for latex_figure(), I was just making things a bit more PEP 8-compliant, but you can always rename to fit your preferences.

TheChymera commented 11 years ago

@gpoore But you saw in the first part of the paste that if I do not rename it the script complains about not being able to find latexFigure . I have matplotlib 1.3.0 - any ideas what else it could be?

gpoore commented 11 years ago

@TheChymera Regarding the function name, I just meant that it could be called anything. You could use latex_figure() everywhere, or just rename it to latexFigure() everywhere. It's largely a matter of preference.

I did a little quick searching, and the error that you have is due to matplotlib. Since I don't get the error and am also using 1.3.0, my guess is that there is some system-specific issue. I'd suggest starting with some very basic plotting code, and then trying to add in the various functions gradually to see what is triggering the error. Maybe start with something like

\begin{pylabcode}
x = linspace(0, pi)
plt.plot(x, sin(x), label='sin(x)')
plt.plot(x, cos(x), label='cos(x)')
savefig('test.pdf')
\end{pylabcode}

and work up from there.

Another thing you could try is renaming the savefig() function from the wiki to save_fig(). I doubt that would do anything. But since matplotlib defines a savefig() of its own (and also has a savefig() method for figures) I suppose it's possible there might be some sort of weird interaction.

TheChymera commented 11 years ago

Ok, so I have this minimal example and it fails with this error. Any ideas what matplotlib issue this may be? So what is matplotlib not doing which it should be doing?

gpoore commented 11 years ago

@TheChymera At this point, I'd suggest running that code in a separate .py file to make sure the error is completely independent of PythonTeX. If so (it certainly appears to be), then you should open an issue at matplotlib. This error appears to be coming from deep inside the matplotlib internals, and I don't know anything about those.

obtitus commented 11 years ago

Well, google gives me these related issue (no solution): http://stackoverflow.com/questions/16312481/matplotlib-wont-plot-dbus-issue http://stackoverflow.com/questions/19088551/matplotlib-error-in-sys-exitfunc http://matplotlib.1069221.n5.nabble.com/warnings-bugs-with-GTK-backends-td42138.html

Your problem is most likely GTK related, you can read more about backends here: http://matplotlib.org/faq/usage_faq.html#what-is-a-backend I would suggest you try another backend.

Have you tried the minimal code in a .py file?

Do you have multiple installs of python? That might cause some confussion...

On 18. okt. 2013, at 17:52, Horea Christian wrote:

Ok, so I have this minimal example and it fails with this error. Any ideas what matplotlib issue this may be?

— Reply to this email directly or view it on GitHub.

TheChymera commented 11 years ago

how can I see what backend I am currently using? Yes, I have python 2.7 and 3.2, but only 2.7 is active.

obtitus commented 11 years ago

Your error is related to gtk, so you are using the gtk backend. See the url I gave you for ways of setting the backend explicitly

On 18. okt. 2013, at 18:19, Horea Christian wrote:

how can I see what backend I am currently using? Yes, I have python 2.7 and 3.2, but only 2.7 is active.

— Reply to this email directly or view it on GitHub.

TheChymera commented 11 years ago

Turns out the error isn't fatal, so I still get my desired output, I just have to live with it complaining... or I can always end my code with show() and that will not complain but rather pop up a window.

In any case... My code also contains some print messages - and these also get printed by invoking \pylab{fig}. Here's a minimal example. What can I do to stop my print messages from being printed?

gpoore commented 11 years ago

Printed content in a code environment is automatically included, regardless of later commands like \pylab{fig}. To disable that, use the package option autoprint=false.

TheChymera commented 11 years ago

where/how do I use that option?

gpoore commented 11 years ago

\usepackage[autoprint=false]{pythontex}

TheChymera commented 11 years ago

Thanks :) Wow, this is awesome. I still need to implement it in my actual documents but everything seems to work!

TheChymera commented 11 years ago

Ok, so indeed everything works - I read your matplotlib wiki article again, and I decided it may be a good idea to use the pgf backend (the text in my figures is way too small as is).

I tried my hand at cooking a preamble section which would allow me to do that - and I ended up with this

\begin{pythontexcustomcode}[begin]{pylab}

import matplotlib as mpl
mpl.use("pgf")
pgf_with_custom_preamble = {
    "font.family": "serif", # use serif/main font for text elements
    "text.usetex": True,    # use inline math for ticks
    "pgf.rcfonts": False,   # don't setup fonts from rc parameters
    "pgf.preamble": [
         r"\usepackage{units}",         # load additional packages
         r"\usepackage{metalogo}",
         r"\usepackage{unicode-math}",  # unicode math setup
         r"\setmathfont{xits-math.otf}",
         r"\setmainfont{DejaVu Serif}", # serif font via preamble
         ]
}
mpl.rcParams.update(pgf_with_custom_preamble)

# Set the prefix used for figure labels
fig_label_prefix = 'fig'
# Track figure numbers to create unique auto-generated names
fig_count = 0

def save_fig(name='', legend=False, fig=None, ext='.pgf'):
    '''
    Save the current figure (or `fig`) to file using `plt.save_fig()`.
    If called with no arguments, automatically generate a unique filename.
    Return the filename.
    '''
    # Get name (without extension) and extension
    if not name:
        global fig_count
        # Need underscores or other delimiters between `input_*` variables
        # to ensure uniqueness
        name = 'auto_fig_{}_{}_{}-{}'.format(pytex.input_family, pytex.input_session, pytex.input_restart, fig_count)
        fig_count += 1
    else:
        if len(name) > 4 and name[:-4] in ['.pdf', '.svg', '.png', '.jpg']:
            name, ext = name.rsplit('.', 1)

    # Get current figure if figure isn't specified
    if not fig:
        fig = gcf()

    # Put a nice legend on top of the figure (you may need to adjust this!)
    # Only create legend if axis has labels
    if legend and len(fig.gca().get_legend_handles_labels()[0]) != 0: 
        for ax in fig.axes:
            if ax.is_first_row():
                box = ax.get_position()
                ax.set_position([box.x0, box.y0, box.width, box.height*0.9])
        leg = ax.legend(loc="upper center", bbox_to_anchor=(0.5, 1.04), ncol=3, bbox_transform=fig.transFigure, frameon=False)
    fig.savefig(name + ext)
    fig.clf()
    return name

def latex_environment(name, content='', option=''):
    '''
    Simple helper function to write the `\begin...\end` LaTeX block.
    '''
    return '\\begin{%s}%s\n%s\n\\end{%s}' % (name, option, content, name)

def latex_figure(name=None, caption='', label='', width=0.8):
    ''''
    Auto wrap `name` in a LaTeX figure environment.
    Width is a fraction of `\textwidth`.
    '''
    if not name:
        name = save_fig()
    content = '\\centering\n'
    content += '\\includegraphics[width=%f\\textwidth]{%s}\n' % (width, name)
    if not label:
        label = name
    if caption and not caption.rstrip().endswith('.'):
        caption += '.'
    # `\label` needs to be in `\caption` to avoid issues in some cases
    content += "\\caption{%s\\label{%s:%s}}\n" % (caption, fig_label_prefix, label)
    return latex_environment('figure', content, '[htp]')
\end{pythontexcustomcode}

Which gives me this error message.

Any ideas how I can switch the backend before all that other stuff is called?

gpoore commented 11 years ago

You will have to use the py family of commands and environments, rather than the pylab family. matplotlib.use() must be called before pylab is imported, like the message says. There isn't a way to do that in the pylab family, which automatically imports pylab first thing. So you need to use py, which doesn't import anything automatically except for a couple things like sys that are needed to make everything work behind the scenes.

TheChymera commented 11 years ago

ok, I keep getting lots of weird error s with the py family - what do I have to import manually?

gpoore commented 11 years ago

After matplotlib.use() at the very beginning, you will need from pylab import *. That import is the only thing the pylab family has that py doesn't.

TheChymera commented 11 years ago

hmmmm, with the following code:

\documentclass[12pt]{article} 
\usepackage{graphicx} 
\usepackage{wrapfig} % Allows in-line images 
\usepackage[autoprint=false]{pythontex}
%~ \setpythontexworkingdir{.} 
\begin{pythontexcustomcode}[begin]{py}
import matplotlib as mpl
mpl.use("pgf")
pgf_with_custom_preamble = {
    "font.family": "serif", # use serif/main font for text elements
    "text.usetex": True,    # use inline math for ticks
    "pgf.rcfonts": False,   # don't setup fonts from rc parameters
    "pgf.preamble": [
         r"\usepackage{units}",         # load additional packages
         r"\usepackage{metalogo}",
         r"\usepackage{unicode-math}",  # unicode math setup
         r"\setmathfont{xits-math.otf}",
         r"\setmainfont{DejaVu Serif}", # serif font via preamble
         ]
}
mpl.rcParams.update(pgf_with_custom_preamble)

from pylab import *

# Set the prefix used for figure labels
fig_label_prefix = 'fig'
# Track figure numbers to create unique auto-generated names
fig_count = 0

def save_fig(name='', legend=False, fig=None, ext='.pgf'):
    '''
    Save the current figure (or `fig`) to file using `plt.save_fig()`.
    If called with no arguments, automatically generate a unique filename.
    Return the filename.
    '''
    # Get name (without extension) and extension
    if not name:
        global fig_count
        # Need underscores or other delimiters between `input_*` variables
        # to ensure uniqueness
        name = 'auto_fig_{}_{}_{}-{}'.format(pytex.input_family, pytex.input_session, pytex.input_restart, fig_count)
        fig_count += 1
    else:
        if len(name) > 4 and name[:-4] in ['.pdf', '.svg', '.png', '.jpg']:
            name, ext = name.rsplit('.', 1)

    # Get current figure if figure isn't specified
    if not fig:
        fig = gcf()

    # Put a nice legend on top of the figure (you may need to adjust this!)
    # Only create legend if axis has labels
    if legend and len(fig.gca().get_legend_handles_labels()[0]) != 0: 
        for ax in fig.axes:
            if ax.is_first_row():
                box = ax.get_position()
                ax.set_position([box.x0, box.y0, box.width, box.height*0.9])
        leg = ax.legend(loc="upper center", bbox_to_anchor=(0.5, 1.04), ncol=3, bbox_transform=fig.transFigure, frameon=False)
    fig.savefig(name + ext)
    fig.clf()
    return name

def latex_environment(name, content='', option=''):
    '''
    Simple helper function to write the `\begin...\end` LaTeX block.
    '''
    return '\\begin{%s}%s\n%s\n\\end{%s}' % (name, option, content, name)

def latex_figure(name=None, caption='', label='', width=0.8):
    ''''
    Auto wrap `name` in a LaTeX figure environment.
    Width is a fraction of `\textwidth`.
    '''
    if not name:
        name = save_fig()
    content = '\\centering\n'
    content += '\\includegraphics[width=%f\\textwidth]{%s}\n' % (width, name)
    if not label:
        label = name
    if caption and not caption.rstrip().endswith('.'):
        caption += '.'
    # `\label` needs to be in `\caption` to avoid issues in some cases
    content += "\\caption{%s\\label{%s:%s}}\n" % (caption, fig_label_prefix, label)
    return latex_environment('figure', content, '[htp]')
\end{pythontexcustomcode}
\begin{document} 
This is an example of using pythontex 
\begin{pylabcode}
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, np.pi)
plt.plot(x, np.sin(x), label='sin(x)')
plt.plot(x, np.cos(x), label='cos(x)')
print('fooooooooo')
fig = latex_figure(save_fig())
show()
\end{pylabcode}
\pylab{fig}
\end{document}

I get this error.