summaryrefslogtreecommitdiff
path: root/macros/luatex
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2021-07-27 03:01:57 +0000
committerNorbert Preining <norbert@preining.info>2021-07-27 03:01:57 +0000
commitcef58753eba8ad7e2071195dbd92454febe82d32 (patch)
treedf909696e42fb25ce108e30258c7f935ded86079 /macros/luatex
parent84465d50d746dfed1bfbe7bb5065279665fa87de (diff)
CTAN sync 202107270301
Diffstat (limited to 'macros/luatex')
-rw-r--r--macros/luatex/latex/pyluatex/README.md6
-rw-r--r--macros/luatex/latex/pyluatex/pyluatex.lua85
-rw-r--r--macros/luatex/latex/pyluatex/pyluatex.pdfbin65417 -> 78301 bytes
-rw-r--r--macros/luatex/latex/pyluatex/pyluatex.sty19
-rw-r--r--macros/luatex/latex/pyluatex/pyluatex.tex100
5 files changed, 166 insertions, 44 deletions
diff --git a/macros/luatex/latex/pyluatex/README.md b/macros/luatex/latex/pyluatex/README.md
index a555ec2008..6c8015dce9 100644
--- a/macros/luatex/latex/pyluatex/README.md
+++ b/macros/luatex/latex/pyluatex/README.md
@@ -7,6 +7,11 @@ LaTeX documents must be compiled with LuaLaTeX for this to work.
## Example
1\. LaTeX document `example.tex`
+
+**Note:** PyLuaTeX starts Python 3 using the command `python3` by default.
+If `python3` does not start Python 3 on your system, find the correct command
+and replace `\usepackage{pyluatex}` with `\usepackage[executable={your python command}]{pyluatex}`.
+For example, `\usepackage[executable=python.exe]{pyluatex}`.
```latex
\documentclass{article}
@@ -31,6 +36,7 @@ $\sqrt{371} = \py{math.sqrt(371)}$
\randint{2}{5}
\end{document}
```
+
2\. Compile using LuaLaTeX (shell escape is required)
```
lualatex -shell-escape example.tex
diff --git a/macros/luatex/latex/pyluatex/pyluatex.lua b/macros/luatex/latex/pyluatex/pyluatex.lua
index 4f904afe15..506b8bf9cf 100644
--- a/macros/luatex/latex/pyluatex/pyluatex.lua
+++ b/macros/luatex/latex/pyluatex/pyluatex.lua
@@ -35,10 +35,11 @@ local folder = file.pathpart(file.collapsepath(status.filename, true))
local script = file.join(folder, "pyluatex-interpreter.py")
local tcp = nil
-local python_lines = {}
-local python_output = nil
+local env_end = nil
+local env_lines = nil
-local env_end = "\\end{python}"
+local last_code = nil
+local last_output = nil
local function err_cmd(message)
return "\\PackageError{PyLuaTeX}{" .. message .. "}{}"
@@ -80,74 +81,100 @@ local function log_output(code)
texio.write_nl("PyLuaTeX output: " .. code)
end
-local function print_lines(str)
- for s in str:gmatch("[^\r\n]+") do
- tex.sprint(s)
+local function split_lines(str)
+ if str:sub(-1) ~= "\n" then
+ str = str .. "\n"
end
+
+ local t = {}
+ for s in str:gmatch("(.-)\r?\n") do
+ table.insert(t, s)
+ end
+ return t
end
-function pyluatex.execute(code, write)
- if pyluatex.verbose then log_input(code) end
+function pyluatex.execute(code, auto_print, write)
+ local full_code
+ if auto_print then
+ full_code = "print(str(" .. code .. "), end='')"
+ else
+ full_code = code
+ end
+
+ if pyluatex.verbose then log_input(full_code) end
+
+ local success, output = request({ session = pyluatex.session, code = full_code })
+ last_code = split_lines(code)
+ last_output = split_lines(output)
- local success, output = request({ session = pyluatex.session, code = code })
if success then
if pyluatex.verbose then log_output(output) end
+
if write then
- print_lines(output)
- else
- return output
+ tex.print(last_output)
end
else
- if not pyluatex.verbose then log_input(code) end
+ if not pyluatex.verbose then log_input(full_code) end
log_output(output)
if write then
tex.sprint(err_cmd("Python error (see above)"))
end
end
- return nil
+
+ return success
end
function pyluatex.print_env()
- if python_output ~= nil then
- print_lines(python_output)
- python_output = nil
+ if last_output ~= nil then
+ tex.print(last_output)
end
end
local function record_line(line)
- local s, e = line:find(env_end)
+ local s, e = line:find(env_end, 1, true)
if s ~= nil then
luatexbase.remove_from_callback("process_input_buffer", "pyluatex_record_line")
- table.insert(python_lines, line:sub(1, s - 1))
- local code = table.concat(python_lines, "\n")
- local output = pyluatex.execute(code, false)
- if output ~= nil then
- python_output = output
+ table.insert(env_lines, line:sub(1, s - 1))
+ local code = table.concat(env_lines, "\n")
+ local success = pyluatex.execute(code, false, false)
+ if success then
return line:sub(s)
else
return env_end .. err_cmd("Python error (see above)") .. line:sub(e + 1)
end
else
- table.insert(python_lines, line)
+ table.insert(env_lines, line)
return ""
end
end
-function pyluatex.record_env()
- python_lines = {}
- python_output = nil
+function pyluatex.record_env(quiet)
+ if quiet then
+ env_end = "\\end{pythonq}"
+ else
+ env_end = "\\end{python}"
+ end
+ env_lines = {}
luatexbase.add_to_callback("process_input_buffer", record_line, "pyluatex_record_line")
end
-function pyluatex.run_file(path)
+function pyluatex.run_file(path, write)
local f = io.open(path, "r")
if f then
local code = f:read("*a")
f:close()
- pyluatex.execute(code, true)
+ pyluatex.execute(code, false, write)
else
tex.sprint(err_cmd("File not found: " .. path))
end
end
+function pyluatex.get_last_code()
+ return last_code
+end
+
+function pyluatex.get_last_output()
+ return last_output
+end
+
return pyluatex
diff --git a/macros/luatex/latex/pyluatex/pyluatex.pdf b/macros/luatex/latex/pyluatex/pyluatex.pdf
index 4e93fb2935..58d2de88e1 100644
--- a/macros/luatex/latex/pyluatex/pyluatex.pdf
+++ b/macros/luatex/latex/pyluatex/pyluatex.pdf
Binary files differ
diff --git a/macros/luatex/latex/pyluatex/pyluatex.sty b/macros/luatex/latex/pyluatex/pyluatex.sty
index b9ce3f9717..0709ba7600 100644
--- a/macros/luatex/latex/pyluatex/pyluatex.sty
+++ b/macros/luatex/latex/pyluatex/pyluatex.sty
@@ -9,7 +9,7 @@
%% version 2005/12/01 or later.
\NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{pyluatex}[2021/07/15 v0.1.3 Execute Python code on the fly]
+\ProvidesPackage{pyluatex}[2021/07/26 v0.2.0 Execute Python code on the fly]
\RequirePackage{expl3}
\ExplSyntaxOn
@@ -30,17 +30,22 @@
\directlua{pyluatex.start([==[\pyluatex@executable]==])}
-\newenvironment{python}{\directlua{pyluatex.record_env()}}%
+\newenvironment{python}{\directlua{pyluatex.record_env(false)}}%
{\directlua{pyluatex.print_env()}}
-\newcommand*{\py}[1]{%
-\directlua{pyluatex.execute([==[print(str(#1), end='')]==], true)}%
-}
+\newenvironment{pythonq}{\directlua{pyluatex.record_env(true)}}{}
+
+\newcommand*{\python@inline}[3]{\directlua{pyluatex.execute([==[#1]==], #2, #3)}}
+
+\newcommand*{\py}[1]{\python@inline{#1}{true}{true}}
+\newcommand*{\pyq}[1]{\python@inline{#1}{true}{false}}
-\newcommand*{\pyc}[1]{\directlua{pyluatex.execute([==[#1]==], true)}}
+\newcommand*{\pyc}[1]{\python@inline{#1}{false}{true}}
+\newcommand*{\pycq}[1]{\python@inline{#1}{false}{false}}
\newcommand*{\pysession}[1]{\directlua{pyluatex.session = [==[#1]==]}}
-\newcommand*{\pyfile}[1]{\directlua{pyluatex.run_file([==[#1]==])}}
+\newcommand*{\pyfile}[1]{\directlua{pyluatex.run_file([==[#1]==], true)}}
+\newcommand*{\pyfileq}[1]{\directlua{pyluatex.run_file([==[#1]==], false)}}
\endinput
diff --git a/macros/luatex/latex/pyluatex/pyluatex.tex b/macros/luatex/latex/pyluatex/pyluatex.tex
index c64d757945..ffaf503914 100644
--- a/macros/luatex/latex/pyluatex/pyluatex.tex
+++ b/macros/luatex/latex/pyluatex/pyluatex.tex
@@ -15,7 +15,7 @@
\usepackage{url}
\title{The \emph{pyluatex} package}
\author{Tobias Enderle\\\url{https://github.com/tndrle/PyLuaTeX}}
-\date{v0.1.3 (2021/07/15)}
+\date{v0.2.0 (2021/07/26)}
\begin{document}
\maketitle
\raggedright
@@ -28,7 +28,11 @@ PyLuaTeX allows you to execute Python code and to include the resulting output i
\section{Example}
\begin{enumerate}
-\item \LaTeX{} document \inlcode|example.tex|
+\item \LaTeX{} document \inlcode|example.tex|\\[0.5ex]
+\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 replace \inlcode|\usepackage{pyluatex}| with \inlcode|\usepackage[executable={your python command}]{pyluatex}|.
+For example, \inlcode|\usepackage[executable=python.exe]{pyluatex}|.
\begin{tcblisting}{breakable,listing only,
size=fbox,colframe=black!8,boxrule=3pt,colback=black!8}
\documentclass{article}
@@ -54,6 +58,8 @@ $\sqrt{371} = \py{math.sqrt(371)}$
\randint{2}{5}
\end{document}
\end{tcblisting}
+\end{enumerate}
+\begin{enumerate}
\item Compile using Lua\LaTeX{} (shell escape is required)
\begin{tcblisting}{breakable,listing only,
size=fbox,colframe=black!8,boxrule=3pt,colback=black!8}
@@ -76,12 +82,20 @@ The folder \inlcode|example| contains additional example documents:
Demonstrates how \textit{matplotlib} plots can be generated and included in a document
\item \inlcode|matplotlib-pgf.tex|\\[0.5ex]
Demonstrates how \textit{matplotlib} plots can be generated and included in a document using \textit{PGF}
+\item \inlcode|typesetting-example.tex|\\[0.5ex]
+ The code typesetting example below
+\item \inlcode|typesetting-listings.tex|\\[0.5ex]
+ A detailed example for typesetting code and output with the \textit{listings} package
+\item \inlcode|typesetting-minted.tex|\\[0.5ex]
+ A detailed example for typesetting code and output with the \textit{minted} package
\end{itemize}
For more intricate use cases have a look at our tests in the folder \inlcode|test|.
\section{Installation}
-PyLuaTeX is available on CTAN\footnote{\url{https://ctan.org/pkg/pyluatex}} and in MiKTeX.
-It will be available in TeX Live soon (when you read this it probably already is).
+PyLuaTeX is available in TeX Live, MiKTeX, and on CTAN\footnote{\url{https://ctan.org/pkg/pyluatex}} as \inlcode|pyluatex|.
+
+To install PyLuaTeX in \textbf{TeX Live} run \inlcode|tlmgr install pyluatex|.\\[0.5ex]
+In \textbf{MiKTeX}, PyLuaTeX can be installed in the \textit{MiKTeX Console}.
To install PyLuaTeX \textbf{manually}, do the following steps:
\begin{enumerate}
@@ -106,6 +120,12 @@ TEXMF/tex/latex/pyluatex/
\section{Reference}
PyLuaTeX offers a simple set of options, macros and environments.
+Most macros and environments are available as \textit{quiet} versions as well.
+They have the suffix \inlcode|q| in their name, e.g. \inlcode|\pycq| or \inlcode|\pyfileq|.
+The quiet versions suppress any output, 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 Typesetting Code section.
+
\subsection{Package Options}
\begin{itemize}
\item \inlcode|verbose|\\[0.5ex]
@@ -118,14 +138,23 @@ PyLuaTeX offers a simple set of options, macros and environments.
\subsection{Macros}
\begin{itemize}
\item \inlcode|\py{code}|\\[0.5ex]
- Executes \inlcode|code| and writes the output to the document.\\[0.5ex]
+ Executes (object-like) \inlcode|code| and writes its string representation to the document.\\[0.5ex]
\textit{Example:} \inlcode|\py{3 + 7}|
+\item \inlcode|\pyq{code}|\\[0.5ex]
+ Executes (object-like) \inlcode|code|. Any output is suppressed.\\[0.5ex]
+ \textit{Example:} \inlcode|\pyq{3 + 7}|
\item \inlcode|\pyc{code}|\\[0.5ex]
- Executes \inlcode|code|\\[0.5ex]
- \textit{Example:} \inlcode|\pyc{x = 5}|
+ Executes \inlcode|code|. Output (e.g. from a call to \inlcode|print()|) is written to the document.\\[0.5ex]
+ \textit{Examples:} \inlcode|\pyc{x = 5}|, \inlcode|\pyc{print('hello')}|
+\item \inlcode|\pycq{code}|\\[0.5ex]
+ Executes \inlcode|code|. Any output is suppressed.\\[0.5ex]
+ \textit{Example:} \inlcode|\pycq{x = 5}|
\item \inlcode|\pyfile{path}|\\[0.5ex]
- Executes the Python file specified by \inlcode|path|.\\[0.5ex]
+ Executes the Python file specified by \inlcode|path|. Output (e.g. from a call to \inlcode|print()|) is written to the document.\\[0.5ex]
\textit{Example:} \inlcode|\pyfile{main.py}|
+\item \inlcode|\pyfileq{path}|\\[0.5ex]
+ Executes the Python file specified by \inlcode|path|. Any output is suppressed.\\[0.5ex]
+ \textit{Example:} \inlcode|\pyfileq{main.py}|
\item \inlcode|\pysession{session}|\\[0.5ex]
Selects \inlcode|session| as Python session for subsequent Python code.\\[0.5ex]
The session that is active at the beginning is \inlcode|default|.\\[0.5ex]
@@ -146,6 +175,8 @@ PyLuaTeX offers a simple set of options, macros and environments.
print(x)
\end{python}
\end{tcblisting}
+\item \inlcode|pythonq|\\[0.5ex]
+ Same as the \inlcode|python| environment, but any output is suppressed.
\end{itemize}
\section{Requirements}
\begin{itemize}
@@ -156,6 +187,59 @@ PyLuaTeX offers a simple set of options, macros and environments.
Our automated tests currently use TeX Live 2021 and Python 3.7+ on
Ubuntu 20.04, macOS Catalina 10.15 and Windows Server 2019.
+\section{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 macros 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 macro 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 table\footnote{\url{https://www.lua.org/pil/2.5.html}} 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}{breakable,listing only,
+ size=fbox,colframe=black!8,boxrule=3pt,colback=black!8}
+\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{pythonq}
+greeting = 'Hello PyLuaTeX!'
+print(greeting)
+\end{pythonq}
+\pytypeset
+
+\end{document}
+\end{tcblisting}
+
+Notice that we use the \inlcode|pythonq| environment, which suppresses any output.
+After that, the custom macro \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.
+See the \inlcode|typesetting-*.tex| examples in the \inlcode|example| folder.
+
\section{How It Works}
PyLuaTeX runs a Python \inlcode|InteractiveInterpreter|\footnote{\url{https://docs.python.org/3/library/code.html#code.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.