texlive[60977] Master/texmf-dist: pyluatex (6nov21)

commits+karl at tug.org commits+karl at tug.org
Sat Nov 6 21:40:01 CET 2021


Revision: 60977
          http://tug.org/svn/texlive?view=revision&revision=60977
Author:   karl
Date:     2021-11-06 21:40:00 +0100 (Sat, 06 Nov 2021)
Log Message:
-----------
pyluatex (6nov21)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/lualatex/pyluatex/README.md
    trunk/Master/texmf-dist/doc/lualatex/pyluatex/pyluatex.pdf
    trunk/Master/texmf-dist/doc/lualatex/pyluatex/pyluatex.tex
    trunk/Master/texmf-dist/tex/lualatex/pyluatex/pyluatex-interpreter.py
    trunk/Master/texmf-dist/tex/lualatex/pyluatex/pyluatex.lua
    trunk/Master/texmf-dist/tex/lualatex/pyluatex/pyluatex.sty

Modified: trunk/Master/texmf-dist/doc/lualatex/pyluatex/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/lualatex/pyluatex/README.md	2021-11-06 20:39:48 UTC (rev 60976)
+++ trunk/Master/texmf-dist/doc/lualatex/pyluatex/README.md	2021-11-06 20:40:00 UTC (rev 60977)
@@ -8,10 +8,6 @@
 ## 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}
 
@@ -36,11 +32,15 @@
 \randint{2}{5}
 \end{document}
 ```
+**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}`.
+
 2\. Compile using LuaLaTeX (shell escape is required)
 ```
 lualatex -shell-escape example.tex
 ```
-
 **Note:** Running LaTeX with the shell escape option enabled allows arbitrary code to be
 executed. For this reason, it is recommended to compile trusted documents only.
 

Modified: trunk/Master/texmf-dist/doc/lualatex/pyluatex/pyluatex.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/lualatex/pyluatex/pyluatex.tex
===================================================================
--- trunk/Master/texmf-dist/doc/lualatex/pyluatex/pyluatex.tex	2021-11-06 20:39:48 UTC (rev 60976)
+++ trunk/Master/texmf-dist/doc/lualatex/pyluatex/pyluatex.tex	2021-11-06 20:40:00 UTC (rev 60977)
@@ -15,7 +15,7 @@
 \usepackage{url}
 \title{The \emph{pyluatex} package}
 \author{Tobias Enderle\\\url{https://github.com/tndrle/PyLuaTeX}}
-\date{v0.3.0 (2021/08/07)}
+\date{v0.4.0 (2021/11/05)}
 \begin{document}
 \maketitle
 \raggedright
@@ -29,10 +29,6 @@
 \section{Example}
 \begin{enumerate}
 \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}
@@ -58,22 +54,23 @@
 \randint{2}{5}
 \end{document}
 \end{tcblisting}
+\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}|.
 \item  Compile using Lua\LaTeX{} (shell escape is required)
 \begin{tcblisting}{breakable,listing only,
     size=fbox,colframe=black!8,boxrule=3pt,colback=black!8}
 lualatex -shell-escape example.tex
 \end{tcblisting}
-\end{enumerate}
 \textbf{Note:} Running \LaTeX{} with the shell escape option enabled allows arbitrary code to be
 executed. For this reason, it is recommended to compile trusted documents only.
-
+\end{enumerate}
 \subsection{Further Examples}
 The folder \inlcode|example| contains additional example documents:
 \begin{itemize}
-\item \inlcode|readme-example.tex|\\[0.5ex]
-  The example above
-\item \inlcode|sessions.tex|\\[0.5ex]
-  Demonstrates the use of different Python sessions in a document
+\item \inlcode|beamer.tex|\\[0.5ex]
+  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|\\[0.5ex]
   Demonstrates the visualization of data using \textit{pgfplots} and \textit{pandas}
 \item \inlcode|matplotlib-external.tex|\\[0.5ex]
@@ -80,6 +77,12 @@
   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|readme-example.tex|\\[0.5ex]
+  The example above
+\item \inlcode|repl.tex|\\[0.5ex]
+  Demonstrates how a Python console/REPL can be run and typeset
+\item \inlcode|sessions.tex|\\[0.5ex]
+  Demonstrates the use of different Python sessions in a document
 \item \inlcode|typesetting-example.tex|\\[0.5ex]
   The code typesetting example below
 \item \inlcode|typesetting-listings.tex|\\[0.5ex]
@@ -127,12 +130,19 @@
 \subsection{Package Options}
 \begin{itemize}
 \item \inlcode|verbose|\\[0.5ex]
-  If this option is enabled, Python input and output is written to the log file.\\[0.5ex]
+  If this option is set, Python input and output is written to the \LaTeX{} log file.\\[0.5ex]
   \textit{Example:} \inlcode|\usepackage[verbose]{pyluatex}|
 \item \inlcode|executable|\\[0.5ex]
   Specifies the path to the Python executable. (default: \inlcode|python3|)\\[0.5ex]
   \textit{Example:} \inlcode|\usepackage[executable=/usr/local/bin/python3]{pyluatex}|
+\item \inlcode|ignoreerrors|\\[0.5ex]
+  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.\\[0.5ex]
+  \textit{Example:} \inlcode|\usepackage[ignoreerrors]{pyluatex}|
 \end{itemize}
+Package options (except for \inlcode|executable|) can be changed in the document with the
+\inlcode|\pyoption| command, e.g. \inlcode|\pyoption{verbose}{true}| or \inlcode|\pyoption{ignoreerrors}{false}|.
+
 \subsection{Macros}
 \begin{itemize}
 \item \inlcode|\py{code}|\\[0.5ex]
@@ -157,6 +167,10 @@
   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]
   \textit{Example:} \inlcode|\pysession{main}|
+\item \inlcode|\pyoption{option}{value}|\\[0.5ex]
+  Assigns \inlcode|value| to the package option \inlcode|option| anywhere in the document. For more information consider
+  the Package Options section.\\[0.5ex]
+  \textit{Example:} \inlcode|\pyoption{verbose}{true}|
 \end{itemize}
 \subsection{Environments}
 \begin{itemize}
@@ -175,8 +189,13 @@
 \end{tcblisting}
 \item \inlcode|pythonq|\\[0.5ex]
   Same as the \inlcode|python| environment, but any output is suppressed.
+\item \inlcode|pythonrepl|\\[0.5ex]
+  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 section
+  Typesetting Code or as shown in the example \inlcode|repl.tex| in the folder
+  \inlcode|example|.
 \end{itemize}
-You can create your own environments based on the \inlcode|python| and \inlcode|pythonq| environments.
+You can create your own environments based on the \inlcode|python|, \inlcode|pythonq| and \inlcode|pythonrepl| environments.
 However, since they are verbatim environments, you have to use the macro \inlcode|\PyLTVerbatimEnv|
 in your environment definition, e.g.
 \begin{tcblisting}{breakable,listing only,
@@ -249,6 +268,8 @@
 You can also define your own environments that combine Python code and typesetting.
 See the \inlcode|typesetting-*.tex| examples in the \inlcode|example| folder.
 
+To emulate an interactive Python console/REPL, the \inlcode|pythonrepl| environment can be used.
+
 \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.

Modified: trunk/Master/texmf-dist/tex/lualatex/pyluatex/pyluatex-interpreter.py
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/pyluatex/pyluatex-interpreter.py	2021-11-06 20:39:48 UTC (rev 60976)
+++ trunk/Master/texmf-dist/tex/lualatex/pyluatex/pyluatex-interpreter.py	2021-11-06 20:40:00 UTC (rev 60977)
@@ -30,8 +30,36 @@
 import json
 import textwrap
 from collections import defaultdict
+import re
 
 class Interpreter(InteractiveInterpreter):
+    def execute_repl(self, code, ignore_errors):
+        self.success = True
+        output = ''
+        incomplete = False
+        for line in re.split('\r?\n', code):
+            output += ('... ' if incomplete else '>>> ') + line + '\n'
+            if incomplete:
+                buffer += '\n' + line
+            else:
+                buffer = line
+            with StringIO() as out, redirect_stdout(out), redirect_stderr(out):
+                try:
+                    code_obj = compile_command(buffer)
+                    if code_obj is not None:
+                        incomplete = False
+                        self.runcode(code_obj)
+                    else:
+                        incomplete = True
+                except:
+                    incomplete = False
+                    traceback.print_exc(limit=0)
+                    self.success = False
+                output += out.getvalue()
+            if not ignore_errors and not self.success:
+                return False, output
+        return self.success, output
+
     def execute(self, code):
         with StringIO() as out, redirect_stdout(out), redirect_stderr(out):
             self.success = True
@@ -61,7 +89,14 @@
 
             data = json.loads(data)
             interpreter = interpreters[data['session']]
-            success, output = interpreter.execute(textwrap.dedent(data['code']))
+            code = textwrap.dedent(data['code'])
+            if data['repl_mode']:
+                success, output = interpreter.execute_repl(
+                    code,
+                    data['ignore_errors']
+                )
+            else:
+                success, output = interpreter.execute(code)
             response = { 'success': success, 'output': output }
             self.wfile.write((json.dumps(response) + '\n').encode('utf-8'))
 

Modified: trunk/Master/texmf-dist/tex/lualatex/pyluatex/pyluatex.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/pyluatex/pyluatex.lua	2021-11-06 20:39:48 UTC (rev 60976)
+++ trunk/Master/texmf-dist/tex/lualatex/pyluatex/pyluatex.lua	2021-11-06 20:40:00 UTC (rev 60977)
@@ -26,6 +26,7 @@
 local socket = require("socket")
 
 pyluatex = pyluatex or {
+    ignore_errors = false,
     verbose = false,
     session = "default"
 }
@@ -38,10 +39,15 @@
 local env_end = nil
 local env_lines = nil
 local parent_env = nil
+local env_repl_mode = false
 
 local last_code = nil
 local last_output = nil
 
+local function trim(s)
+    return (s:gsub("^%s*(.-)%s*$", "%1"))
+end
+
 local function err_cmd(message)
     return "\\PackageError{PyLuaTeX}{" .. message .. "}{}"
 end
@@ -94,7 +100,7 @@
     return t
 end
 
-function pyluatex.execute(code, auto_print, write)
+function pyluatex.execute(code, auto_print, write, repl_mode)
     local full_code
     if auto_print then
         full_code = "print(str(" .. code .. "), end='')"
@@ -104,12 +110,19 @@
 
     if pyluatex.verbose then log_input(full_code) end
 
-    local success, output = request({ session = pyluatex.session, code = full_code })
+    local success, output = request(
+        {
+            session = pyluatex.session,
+            code = full_code,
+            repl_mode = repl_mode,
+            ignore_errors = pyluatex.ignore_errors
+        }
+    )
     last_code = split_lines(code)
     last_output = split_lines(output)
 
-    if success then
-        if pyluatex.verbose then log_output(output) end
+    if success or pyluatex.ignore_errors then
+        if pyluatex.verbose or not success then log_output(output) end
 
         if write then
             tex.print(last_output)
@@ -135,10 +148,14 @@
     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(env_lines, line:sub(1, s - 1))
+        local code_in_line = line:sub(1, s - 1)
+        if trim(code_in_line):len() > 0 then
+            -- only include this line if it contains non-whitespace characters
+            table.insert(env_lines, code_in_line)
+        end
         local code = table.concat(env_lines, "\n")
-        local success = pyluatex.execute(code, false, false)
-        if success then
+        local success = pyluatex.execute(code, false, false, env_repl_mode)
+        if success or pyluatex.ignore_errors then
             return line:sub(s)
         else
             return env_end .. err_cmd("Python error (see above)") .. line:sub(e + 1)
@@ -149,20 +166,14 @@
     end
 end
 
-function pyluatex.record_env(quiet)
-    local name
+function pyluatex.record_env(name, repl_mode)
     if parent_env ~= nil then
         name = parent_env
         parent_env = nil
-    else
-        if quiet then
-            name = "pythonq"
-        else
-            name = "python"
-        end
     end
     env_end = "\\end{" .. name .. "}"
     env_lines = {}
+    env_repl_mode = repl_mode
     luatexbase.add_to_callback("process_input_buffer", record_line, "pyluatex_record_line")
 end
 
@@ -172,12 +183,18 @@
     end
 end
 
-function pyluatex.run_file(path, write)
+function pyluatex.run_file(path, write, repl_mode)
     local f = io.open(path, "r")
     if f then
         local code = f:read("*a")
         f:close()
-        pyluatex.execute(code, false, write)
+        -- ignore trailing new line if present
+        if code:sub(-2) == "\r\n" then
+            code = code:sub(0, -3)
+        elseif code:sub(-1) == "\n" then
+            code = code:sub(0, -2)
+        end
+        pyluatex.execute(code, false, write, repl_mode)
     else
         tex.sprint(err_cmd("File not found: " .. path))
     end
@@ -191,4 +208,26 @@
     return last_output
 end
 
+local function parse_bool(name, value)
+    if value == "true" then
+        return true
+    elseif value == "false" then
+        return false
+    else
+        tex.sprint(
+            err_cmd("Invalid value '" .. value .. "' for option " .. name)
+        )
+    end
+end
+
+function pyluatex.set_option(name, value)
+    if name == "ignoreerrors" then
+        pyluatex.ignore_errors = parse_bool(name, value)
+    elseif name == "verbose" then
+        pyluatex.verbose = parse_bool(name, value)
+    else
+        tex.sprint(err_cmd("Unknown option '" .. name .. "'"))
+    end
+end
+
 return pyluatex

Modified: trunk/Master/texmf-dist/tex/lualatex/pyluatex/pyluatex.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/pyluatex/pyluatex.sty	2021-11-06 20:39:48 UTC (rev 60976)
+++ trunk/Master/texmf-dist/tex/lualatex/pyluatex/pyluatex.sty	2021-11-06 20:40:00 UTC (rev 60977)
@@ -9,7 +9,7 @@
 %% version 2005/12/01 or later.
 
 \NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{pyluatex}[2021/08/07 v0.3.0 Execute Python code on the fly]
+\ProvidesPackage{pyluatex}[2021/11/05 v0.4.0 Execute Python code on the fly]
 
 \RequirePackage{expl3}
 \ExplSyntaxOn
@@ -25,20 +25,33 @@
 
 \RequirePackage{kvoptions}
 \DeclareStringOption[python3]{executable}
-\DeclareVoidOption{verbose}{\directlua{pyluatex.verbose = true}}
+\DeclareBoolOption{ignoreerrors}
+\DeclareBoolOption{verbose}
 \ProcessKeyvalOptions*
 
+\ifpyluatex at ignoreerrors
+    \directlua{pyluatex.ignore_errors = true}
+\else
+    \directlua{pyluatex.ignore_errors = false}
+\fi
+\ifpyluatex at verbose
+    \directlua{pyluatex.verbose = true}
+\else
+    \directlua{pyluatex.verbose = false}
+\fi
 \directlua{pyluatex.start([==[\pyluatex at executable]==])}
 
 \newcommand*{\PyLTVerbatimEnv}{\directlua{pyluatex.set_parent_env([==[\@currenvir]==])}}
 
-\newenvironment{python}{\directlua{pyluatex.record_env(false)}}%
+\newenvironment{python}{\directlua{pyluatex.record_env("python", false)}}%
 {\directlua{pyluatex.print_env()}}
 
-\newenvironment{pythonq}{\directlua{pyluatex.record_env(true)}}{}
+\newenvironment{pythonq}{\directlua{pyluatex.record_env("pythonq", false)}}{}
 
-\newcommand*{\pyluatex at inline}[3]{\directlua{pyluatex.execute([==[#1]==], #2, #3)}}
+\newenvironment{pythonrepl}{\directlua{pyluatex.record_env("pythonrepl", true)}}{}
 
+\newcommand*{\pyluatex at inline}[3]{\directlua{pyluatex.execute([==[#1]==], #2, #3, false)}}
+
 \newcommand*{\py}[1]{\pyluatex at inline{#1}{true}{true}}
 \newcommand*{\pyq}[1]{\pyluatex at inline{#1}{true}{false}}
 
@@ -46,8 +59,10 @@
 \newcommand*{\pycq}[1]{\pyluatex at inline{#1}{false}{false}}
 
 \newcommand*{\pysession}[1]{\directlua{pyluatex.session = [==[#1]==]}}
+\newcommand*{\pyoption}[2]{\directlua{pyluatex.set_option([==[#1]==], [==[#2]==])}}
 
-\newcommand*{\pyfile}[1]{\directlua{pyluatex.run_file([==[#1]==], true)}}
-\newcommand*{\pyfileq}[1]{\directlua{pyluatex.run_file([==[#1]==], false)}}
+\newcommand*{\pyfile}[1]{\directlua{pyluatex.run_file([==[#1]==], true, false)}}
+\newcommand*{\pyfileq}[1]{\directlua{pyluatex.run_file([==[#1]==], false, false)}}
+\newcommand*{\pyfilerepl}[1]{\directlua{pyluatex.run_file([==[#1]==], false, true)}}
 
 \endinput



More information about the tex-live-commits mailing list.