\documentclass[a4paper]{article}
\usepackage{fontspec}
\setmainfont{TeX Gyre Heros}
\setmonofont{TeX Gyre Cursor}[Ligatures=NoCommon]
\usepackage[margin=3cm]{geometry}
\usepackage{parskip}
\linespread{1.1}
\usepackage[listings,breakable]{tcolorbox}
\tcbset{breakable,listing only,size=fbox,colframe=black!10,boxrule=3pt,
  colback=black!10}
\NewDocumentCommand{\inlcode}{O{}v}{\texttt{#1#2}}
\usepackage[colorlinks=true,urlcolor=blue,linkcolor=blue]{hyperref}
\title{The \emph{pyluatex} package}
\author{Tobias Enderle\\\url{https://github.com/tndrle/PyLuaTeX}}
\date{v0.7.0 (2026/04/17)}
\begin{document}
\maketitle
\raggedright
\textbf{Execute Python code on the fly in your \LaTeX{} documents}

PyLuaTeX allows you to execute Python code and to include the resulting output
in your \LaTeX{} documents in a \textit{single compilation run}.
\LaTeX{} documents must be compiled with Lua\LaTeX{} for this to work.


\section{Example}\label{Example}
\begin{enumerate}
\item  \LaTeX{} document \inlcode|example.tex|
   \begin{tcblisting}{}\documentclass{article}

\usepackage{pyluatex}

\begin{python}
import math
import random

random.seed(0)

greeting = 'Hello PyLuaTeX!'
\end{python}

\newcommand{\randint}[2]{\py{random.randint(#1, #2)}}

\begin{document}
\py{greeting}

$\sqrt{371} = \py{math.sqrt(371)}$

\randint{2}{5}
\end{document}
\end{tcblisting}
\item  Compile using Lua\LaTeX{} (shell escape is required)
   \begin{tcblisting}{}lualatex --shell-escape example.tex
\end{tcblisting}
\end{enumerate}
\textbf{Note:} PyLuaTeX starts Python 3 using the command \inlcode|python3| by default.
If \inlcode|python3| does not start Python 3 on your system, find the correct command
and extend \inlcode|\usepackage{pyluatex}| to
\inlcode|\usepackage[executable=<your python command>]{pyluatex}|.
For example, \inlcode|\usepackage[executable=python.exe]{pyluatex}|.

\textbf{Security note:} Running \LaTeX{} with the \inlcode|--shell-escape| option
allows arbitrary code to be executed.
For this reason, it is recommended to \textbf{compile trusted documents only}.

\subsection{Further Examples}\label{Further Examples}
The folder \inlcode|example| contains additional example documents:
\begin{itemize}
\item \inlcode|beamer.tex|\par
  Demonstrates the use of PyLuaTeX environments and typesetting in \textit{BEAMER}
  presentations. In particular, the \inlcode|fragile| option for frames is highlighted.
\item \inlcode|data-visualization.tex|\par
  Demonstrates the visualization of data using \textit{pgfplots} and \textit{pandas}
\item \inlcode|matplotlib-external.tex|\par
  Demonstrates how \textit{matplotlib} plots can be generated and included in a
  document
\item \inlcode|matplotlib-pgf.tex|\par
  Demonstrates how \textit{matplotlib} plots can be generated and included in a
  document using \textit{PGF}
\item \inlcode|readme-example.tex|\par
  The example above
\item \inlcode|repl.tex|\par
  Demonstrates how a Python console/REPL can be run and typeset
\item \inlcode|sessions.tex|\par
  Demonstrates the use of different Python sessions in a document
\item \inlcode|typesetting-example.tex|\par
  The code typesetting example below
\item \inlcode|typesetting-listings.tex|\par
  A detailed example for typesetting code and output with the \textit{listings}
  package
\item \inlcode|typesetting-minted.tex|\par
  A detailed example for typesetting code and output with the \textit{minted} package
\end{itemize}
\section{Reference}\label{Reference}
\subsection{Commands}\label{Commands}
Most of the following commands accept key-value options.
See \nameref{Options} for more details.

\begin{itemize}
\item \inlcode[\bfseries]|\py[<options>]{<code>}|\par
  Executes (object-like) \inlcode|<code>| and writes its string representation to
  the document.\par
  \textit{Allowed options:} \inlcode|ignoreerrors|, \inlcode|quiet|, \inlcode|repl|, \inlcode|session|, \inlcode|store|, \inlcode|verbose|\par
  \textit{Example:} \inlcode|\py{3 + 7}|
\item \inlcode[\bfseries]|\pyc[<options>]{<code>}|\par
  Executes \inlcode|<code>|. Output (e.g. from a call to \inlcode|print()|) is written to
  the document.\par
  \textit{Allowed options:} \inlcode|ignoreerrors|, \inlcode|quiet|, \inlcode|repl|, \inlcode|session|, \inlcode|store|, \inlcode|verbose|\par
  \textit{Examples:} \inlcode|\pyc{x = 5}|, \inlcode|\pyc{print('hello')}|
\item \inlcode[\bfseries]|\pyfile[<options>]{<path>}|\par
  Executes the Python file specified by \inlcode|<path>|.
  Output (e.g. from a call to \inlcode|print()|) is written to the document.\par
  \textit{Allowed options:} \inlcode|ignoreerrors|, \inlcode|quiet|, \inlcode|repl|, \inlcode|session|, \inlcode|store|, \inlcode|verbose|\par
  \textit{Example:} \inlcode|\pyfile{main.py}|
\item \inlcode[\bfseries]|\pyif[<options>]{<test>}{<then clause>}{<else clause>}|\par
  Evaluates the Python boolean expression \inlcode|<test>|, and then executes either
  the \LaTeX{} code in \inlcode|<then clause>| or the \LaTeX{} code in \inlcode|<else clause>|.\par
  \textit{Allowed options:} \inlcode|session|, \inlcode|verbose|\par
  \textit{Example:} \inlcode|\pyif{a == 1}{$a = 1$}{$a \neq 1$}|
\item \inlcode[\bfseries]|\pyoptions{<options>}|\par
  Sets options globally.
  For more information see the \nameref{Options} section.\par
  \textit{Allowed options:} \inlcode|ignoreerrors|, \inlcode|quiet|, \inlcode|repl|, \inlcode|session|, \inlcode|store|, \inlcode|verbose|\par
  \textit{Example:} \inlcode|\pyoptions{verbose,session=main}|
\end{itemize}
The following commands exist as shortcuts and for backward compatibility with
previous versions of PyLuaTeX:

\begin{itemize}
\item \inlcode[\bfseries]|\pyq[<options>]{<code>}|\par
  Executes (object-like) \inlcode|<code>|. Any output to the document is suppressed.\par
  This is equivalent to \inlcode|\py[quiet,<options>]{<code>}|.\par
  \textit{Allowed options:} \inlcode|ignoreerrors|, \inlcode|repl|, \inlcode|session|, \inlcode|store|, \inlcode|verbose|\par
  \textit{Example:} \inlcode|\pyq{3 + 7}|
\item \inlcode[\bfseries]|\pycq[<options>]{<code>}|\par
  Executes \inlcode|<code>|. Any output to the document is suppressed.\par
  This is equivalent to \inlcode|\pyc[quiet,<options>]{<code>}|.\par
  \textit{Allowed options:} \inlcode|ignoreerrors|, \inlcode|repl|, \inlcode|session|, \inlcode|store|, \inlcode|verbose|\par
  \textit{Example:} \inlcode|\pycq{x = 5}|
\item \inlcode[\bfseries]|\pyfileq[<options>]{<path>}|\par
  Executes the Python file specified by \inlcode|<path>|. Any output to the
  document is suppressed.\par
  This is equivalent to \inlcode|\pyfile[quiet,<options>]{<path>}|.\par
  \textit{Allowed options:} \inlcode|ignoreerrors|, \inlcode|repl|, \inlcode|session|, \inlcode|store|, \inlcode|verbose|\par
  \textit{Example:} \inlcode|\pyfileq{main.py}|
\item \inlcode[\bfseries]|\pysession{<name>}|\par
  Sets \inlcode|<name>| globally as Python session for subsequent Python code.
  The session that is active at the beginning is \inlcode|default|.\par
  This is equivalent to \inlcode|\pyoptions{session=<name>}|.\par
  \textit{Example:} \inlcode|\pysession{main}|
\item \inlcode[\bfseries]|\pyoption{<option>}{<value>}|\par
  Assigns \inlcode|<value>| to the option \inlcode|<option>| globally.
  For more information see the \nameref{Options} section.\par
  This is equivalent to \inlcode|\pyoptions{<option>=<value>}|.\par
  \textit{Allowed options:} \inlcode|ignoreerrors|, \inlcode|quiet|, \inlcode|repl|, \inlcode|session|, \inlcode|store|, \inlcode|verbose|\par
  \textit{Example:} \inlcode|\pyoption{verbose}{true}|
\end{itemize}
\subsection{Environments}\label{Environments}
\begin{itemize}
\item \inlcode[\bfseries]|python|\par
  Executes the provided block of Python code.
  The environment handles characters like \inlcode|_|, \inlcode|#|, \inlcode|%|, \inlcode|\|, etc.
  Code on the same line as \inlcode|\begin{python}| is ignored, i.e., code must
  start on the next line.
  If leading spaces are present, they are gobbled automatically up to the
  first level of indentation.\par
  \textit{Allowed options:} \inlcode|ignoreerrors|, \inlcode|quiet|, \inlcode|repl|, \inlcode|session|, \inlcode|store|, \inlcode|verbose|\par
  \textit{Example:}
  \begin{tcblisting}{}\begin{python}
  x = 'Hello PyLuaTeX'
  print(x)
\end{python}
\end{tcblisting}
\end{itemize}
Like commands, environments accept key-value options, e.g.
\begin{tcblisting}{}\begin{python}[ignoreerrors]
  print(1 / 0)
\end{python}
\end{tcblisting}
The opening tag \inlcode|[| must \textbf{directly follow} the \inlcode|\begin{python}|.
Spaces or line breaks between \inlcode|\begin{python}| and \inlcode|[| are not allowed.

The following environments exist as shortcuts and for backward
compatibility with previous versions of PyLuaTeX:

\begin{itemize}
\item \inlcode[\bfseries]|pythonq|\par
  Same as the \inlcode|python| environment, but any output to the document is
  suppressed.\par
  This is equivalent to \inlcode|\begin{python}[quiet,<options>]|.\par
  \textit{Allowed options:} \inlcode|ignoreerrors|, \inlcode|repl|, \inlcode|session|, \inlcode|store|, \inlcode|verbose|
\item \inlcode[\bfseries]|pythonrepl|\par
  Executes the provided block of Python code in an interactive console/REPL.
  Code and output are stored together in the output buffer and can be
  typeset as explained in the section \nameref{Typesetting Code}
  or as shown in the example \inlcode|repl.tex| in the folder \inlcode|example|.\par
  This is equivalent to \inlcode|\begin{python}[quiet,repl,<options>]|.\par
  \textit{Allowed options:} \inlcode|ignoreerrors|, \inlcode|session|, \inlcode|store|, \inlcode|verbose|
\end{itemize}
\subsection{Custom Environments}\label{Custom Environments}
You can create your own environments based on the \inlcode|python|, \inlcode|pythonq| and
\inlcode|pythonrepl| environments.
However, since those are verbatim environments, you have to use the command
\inlcode|\PyLTVerbatimEnv| in your environment definition, e.g.
\begin{tcblisting}{}\NewDocumentEnvironment{custompy}{}
{\PyLTVerbatimEnv\begin{python}}
{\end{python}}
\end{tcblisting}

\subsection{Options}\label{Options}
Options marked with \textit{package option only} are only valid as package
options in \inlcode|\usepackage[...]{pyluatex}|.
All other options can be used as package options and throughout the
document.
Options can be set globally using \inlcode|\pyoptions| or locally for the
various environments and commands.
If a value contains commas, the entire value must be enclosed in
quotation marks.

\begin{itemize}
\item \inlcode[\bfseries]|executable| \quad{} \inlcode|string| \quad{} \textit{default:} \inlcode|python3| \quad{} \textit{package option only}\par
  Specifies the path to the Python executable.\par
  \textit{Example:} \inlcode|\usepackage[executable=/usr/local/bin/python3]{pyluatex}|
\item \inlcode[\bfseries]|ignoreerrors| \quad{} \inlcode|boolean| \quad{} \textit{default:} \inlcode|false|\par
  By default, PyLuaTeX aborts the compilation process when Python reports an
  error. If the \inlcode|ignoreerrors| option is set, the compilation process is not
  aborted.\par
  \textit{Alias:} \inlcode|ignore errors|\par
  \textit{Examples:} \inlcode|\usepackage[ignoreerrors]{pyluatex}|, \inlcode|\py[ignore errors]{1 / 0}|
\item \inlcode[\bfseries]|localimports| \quad{} \inlcode|boolean| \quad{} \textit{default:} \inlcode|true| \quad{} \textit{package option only}\par
  If this option is set, the folder containing the \LaTeX{} input file is added
  to the Python path. This allows local Python packages to be imported.\par
  \textit{Alias:} \inlcode|local imports|\par
  \textit{Example:} \inlcode|\usepackage[localimports=false]{pyluatex}|
\item \inlcode[\bfseries]|quiet| \quad{} \inlcode|boolean| \quad{} \textit{default:} \inlcode|false|\par
  If this option is set, any output to the document is suppressed, even if
  the Python code explicitly calls \inlcode|print()|. This is helpful if you want
  to process code or output further and do your own typesetting. For an
  example, see the \nameref{Typesetting Code} section.\par
  \textit{Alias:} \inlcode|q|\par
  \textit{Examples:} \inlcode|\py[quiet]{7 + 4}|, \inlcode|\py[q]{'Hello'}|
\item \inlcode[\bfseries]|repl| \quad{} \inlcode|boolean| \quad{} \textit{default:} \inlcode|false|\par
  If this option is set, Python code is executed in an interactive
  console/REPL. Code and output are stored together in the output buffer
  and can be typeset as explained in the section
  \nameref{Typesetting Code} or as shown in the example
  \inlcode|repl.tex| in the folder \inlcode|example|. The use of \inlcode|quiet| together with
  \inlcode|repl| is recommended.
\item \inlcode[\bfseries]|session| \quad{} \inlcode|boolean| \quad{} \textit{default:} \inlcode|default|\par
  Sessions provide a way to structure and separate code.
  Variables, function definitions, etc. of one session are only accessible
  by that session. This can be helpful in long documents
  with a lot of code.\par
  \textit{Alias:} \inlcode|s|\par
  \textit{Examples:} \inlcode|\pyc[session=main]{x = 5}|, \inlcode|\py[s=main]{x}|
\item \inlcode[\bfseries]|shutdown| \quad{} \inlcode|choice| \quad{} \textit{default:} \inlcode|veryveryend| \quad{} \textit{package option only}\par
  Specifies when the Python process is shut down.\par
  \textit{Possible values:} \inlcode|veryveryend|, \inlcode|veryenddocument|, \inlcode|off|\par
  PyLuaTeX shuts down the Python interpreter when the compilation is done.
  With the option \inlcode|veryveryend|, Python is shut down in the
  \inlcode|enddocument/end| hook. With the option \inlcode|veryenddocument|, Python is shut
  down in the \inlcode|enddocument/afteraux| hook. With the option \inlcode|off|, Python is
  not shut down explicitly. However, the Python process will shut down when
  the LuaTeX process finishes even if \inlcode|off| is selected. Using \inlcode|off| on
  Windows might lead to problems with SyncTeX, though
  (\href{https://github.com/tndrle/PyLuaTeX/issues/8}{https://github.com/tndrle/PyLuaTeX/issues/8}).\par
  Before v0.6.2, PyLuaTeX used the hooks \inlcode|\AtVeryVeryEnd| and
  \inlcode|\AtVeryEndDocument| of the package \textit{atveryend}. The new hooks
  \inlcode|enddocument/end| and \inlcode|enddocument/afteraux| are equivalent to those of
  the \textit{atveryend} package.\par
  \textit{Example:} \inlcode|\usepackage[shutdown=veryenddocument]{pyluatex}|
\item \inlcode[\bfseries]|store| \quad{} \inlcode|boolean| \quad{} \textit{default:} \inlcode|true|\par
  If this option is set, code and output are stored in buffers.
  See \nameref{Typesetting Code} for more details.\par
  \textit{Example:} \inlcode|\pyc[store=false]{x = 5}|
\item \inlcode[\bfseries]|verbose| \quad{} \inlcode|boolean| \quad{} \textit{default:} \inlcode|false|\par
  If this option is set, Python input and output is written to the \LaTeX{} log
  file.\par
  \textit{Examples:} \inlcode|\usepackage[verbose]{pyluatex}|, \inlcode|\py[verbose]{7 + 4}|
\end{itemize}
\subsection{Logging from Python}\label{Logging from Python}
\begin{tcblisting}{}tex.log(*objects, sep=' ', end='\n')
\end{tcblisting}
Writes \inlcode|objects| to the \LaTeX{} log, separated by \inlcode|sep| and followed by \inlcode|end|.
All elements in \inlcode|objects| are converted to strings using \inlcode|str()|.
Both \inlcode|sep| and \inlcode|end| must be strings.

\textit{Example:}
\begin{tcblisting}{}\begin{python}
  tex.log('This text goes to the LaTeX log.')
\end{python}
\end{tcblisting}

\section{Requirements}\label{Requirements}
\begin{itemize}
\item Lua\LaTeX{}
\item Python 3
\item Linux, macOS or Windows
\end{itemize}
\section{Typesetting Code}\label{Typesetting Code}
Sometimes, in addition to having Python code executed and the output written
to your document, you also want to show the code itself in your document.
PyLuaTeX does not offer any commands or environments that directly typeset code.
However, PyLuaTeX has a \textbf{code and output buffer} which you can use to create
your own typesetting functionality.
This provides a lot of flexibility for your typesetting.

After a PyLuaTeX command or environment has been executed, the corresponding
Python code and output can be accessed via the Lua functions
\inlcode|pyluatex.get_last_code()| and \inlcode|pyluatex.get_last_output()|, respectively.
Both functions return a Lua \href{https://www.lua.org/pil/2.5.html}{table}
(basically an array) where each table item corresponds to a line of code
or output.

A simple example for typesetting code and output using the \textit{listings} package
would be:
\begin{tcblisting}{}\documentclass{article}

\usepackage{pyluatex}
\usepackage{listings}
\usepackage{luacode}

\begin{luacode}
function pytypeset()
  tex.print("\\begin{lstlisting}[language=Python]")
  tex.print(pyluatex.get_last_code())
  tex.print("\\end{lstlisting}")
  tex.print("") -- ensure newline
end
\end{luacode}

\newcommand*{\pytypeset}{%
  \noindent\textbf{Input:}
  \directlua{pytypeset()}
  \textbf{Output:}
  \begin{center}
    \directlua{tex.print(pyluatex.get_last_output())}
  \end{center}
}

\begin{document}

\begin{python}[quiet]
  greeting = 'Hello PyLuaTeX!'
  print(greeting)
\end{python}
\pytypeset

\end{document}
\end{tcblisting}

Notice that we use the \inlcode|python| environment with the \inlcode|quiet| option,
which suppresses any output.
After that, the custom command \inlcode|\pytypeset| is responsible for typesetting
the code and its output.

Using a different code listings package like \textit{minted}, or typesetting inline
code is very easy.
You can also define your own environments that combine Python code and
typesetting. See the \inlcode|typesetting-*.tex| examples in the \inlcode|example| folder.

Use the option \inlcode|repl|, to emulate an interactive Python console/REPL.

\section{How It Works}\label{How It Works}
PyLuaTeX runs a Python \href{https://docs.python.org/3/library/code.html#code.InteractiveInterpreter}{\inlcode[\color{blue}]|InteractiveInterpreter|}
(actually several if you use different sessions) in the background for
on-the-fly code execution. Python code from your \LaTeX{} file is sent to the
background interpreter through a TCP socket. This approach allows your
Python code to be executed and the output to be integrated in your \LaTeX{}
file in a single compilation run. No additional processing steps are needed.
No intermediate files have to be written. No placeholders have to be inserted.

\section{License}\label{License}
\href{http://www.latex-project.org/lppl.txt}{LPPL 1.3c} for \LaTeX{} code and
\href{https://opensource.org/licenses/MIT}{MIT license} for Python and Lua code
and other files.
\end{document}
