From 64ab66d47ecf5fe3d30572da8af6e4f9aa73cf98 Mon Sep 17 00:00:00 2001
From: Geoffrey Poore
Date: Wed, 3 Oct 2018 05:34:19 -0500
Subject: [PATCH 01/58] update for Julia versions 0.7 and 1.0 (closes #126 and
#130), improved Julia test
---
pythontex/pythontex.dtx | 8 ++++----
pythontex/pythontex.sty | 2 +-
pythontex/pythontex_engines.py | 8 ++++----
test/julia/julia_test.tex | 6 +++---
4 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/pythontex/pythontex.dtx b/pythontex/pythontex.dtx
index 3fc47d2..3b39522 100644
--- a/pythontex/pythontex.dtx
+++ b/pythontex/pythontex.dtx
@@ -1,6 +1,6 @@
% \iffalse meta-comment
%
-% Copyright (C) 2012-2017 by Geoffrey M. Poore
+% Copyright (C) 2012-2018 by Geoffrey M. Poore
% ---------------------------------------------------------------------------
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3
@@ -26,7 +26,7 @@
%\NeedsTeXFormat{LaTeX2e}[1999/12/01]
%\ProvidesPackage{pythontex}
%<*package>
- [2017/09/03 v0.17dev execute and typeset Python code and other languages]
+ [2018/10/03 v0.17dev execute and typeset Python code and other languages]
%
%
%<*driver>
@@ -154,9 +154,9 @@
%
%
%
-% \begin{changelog}{v0.17}{2017/??/??}
+% \begin{changelog}{v0.17}{2018/10/??}
% \begin{itemize}
-% \item Updated Julia support for Julia version 0.6 (\#107).
+% \item Updated Julia support for Julia versions 0.6 (\#107), and 0.7 and 1.0 (\#126, \#130).
% \end{itemize}
% \end{changelog}
%
diff --git a/pythontex/pythontex.sty b/pythontex/pythontex.sty
index 84bce65..4186e74 100644
--- a/pythontex/pythontex.sty
+++ b/pythontex/pythontex.sty
@@ -20,7 +20,7 @@
%%
\NeedsTeXFormat{LaTeX2e}[1999/12/01]
\ProvidesPackage{pythontex}
- [2017/09/03 v0.17dev execute and typeset Python code and other languages]
+ [2018/10/03 v0.17dev execute and typeset Python code and other languages]
\newcommand{\pytx@packagename}{PythonTeX}
\newcommand{\pytx@packageversion}{0.17dev}
diff --git a/pythontex/pythontex_engines.py b/pythontex/pythontex_engines.py
index 1746c08..874a7be 100644
--- a/pythontex/pythontex_engines.py
+++ b/pythontex/pythontex_engines.py
@@ -950,7 +950,7 @@ def cleanup
# Currently, Julia only supports UTF-8
# So can't set stdout and stderr encoding
- type JuliaTeXUtils
+ mutable struct JuliaTeXUtils
id::AbstractString
family::AbstractString
session::AbstractString
@@ -983,8 +983,8 @@ def cleanup
function JuliaTeXUtils()
self = new()
self.self = self
- self._dependencies = Array{{AbstractString}}(0)
- self._created = Array{{AbstractString}}(0)
+ self._dependencies = AbstractString[]
+ self._created = AbstractString[]
self._context_raw = ""
function formatter(expr)
@@ -1095,7 +1095,7 @@ def cleanup
jltex.line = "{line}"
println("{stdoutdelim}")
- write(STDERR, "{stderrdelim}\\n")
+ write(stderr, "{stderrdelim}\\n")
jltex.before()
{code}
diff --git a/test/julia/julia_test.tex b/test/julia/julia_test.tex
index 53bf815..612b9e5 100644
--- a/test/julia/julia_test.tex
+++ b/test/julia/julia_test.tex
@@ -82,13 +82,13 @@ \section*{Julia stderr}
Julia a
\end{juliablock}
-\stderrpythontex
+\stderrpythontex[][breaklines, breakafter=\\/]
\begin{juliablock}[err2][numbers=left]
1+
\end{juliablock}
-\stderrpythontex
+\stderrpythontex[][breaklines, breakafter=\\/]
\begin{juliablock}[err3][numbers=left]
# Comment
@@ -96,7 +96,7 @@ \section*{Julia stderr}
1 + "ab"
\end{juliablock}
-\stderrpythontex
+\stderrpythontex[][breaklines, breakafter=\\/]
From 4a6fcc4a51a5874540571219c746e4958bca8843 Mon Sep 17 00:00:00 2001
From: Geoffrey Poore
Date: Sun, 7 Oct 2018 17:13:16 -0500
Subject: [PATCH 02/58] better error messages when Weave.jl is not installed or
raises errors (#131)
---
pythontex/pythontex.dtx | 3 ++-
pythontex/pythontex2.py | 6 ++++++
pythontex/pythontex3.py | 6 ++++++
3 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/pythontex/pythontex.dtx b/pythontex/pythontex.dtx
index 3b39522..f128a57 100644
--- a/pythontex/pythontex.dtx
+++ b/pythontex/pythontex.dtx
@@ -26,7 +26,7 @@
%\NeedsTeXFormat{LaTeX2e}[1999/12/01]
%\ProvidesPackage{pythontex}
%<*package>
- [2018/10/03 v0.17dev execute and typeset Python code and other languages]
+ [2018/10/07 v0.17dev execute and typeset Python code and other languages]
%
%
%<*driver>
@@ -157,6 +157,7 @@
% \begin{changelog}{v0.17}{2018/10/??}
% \begin{itemize}
% \item Updated Julia support for Julia versions 0.6 (\#107), and 0.7 and 1.0 (\#126, \#130).
+% \item There are now meaningful error messages for Julia console when Weave.jl is not installed or raises errors (\#131).
% \end{itemize}
% \end{changelog}
%
diff --git a/pythontex/pythontex2.py b/pythontex/pythontex2.py
index 54b7312..af6e20f 100644
--- a/pythontex/pythontex2.py
+++ b/pythontex/pythontex2.py
@@ -1567,6 +1567,12 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
messages.append('* PythonTeX error')
messages.append(' Missing output file for ' + key_run.replace('#', ':'))
errors += 1
+ elif family == 'juliacon' and proc.returncode == 1:
+ messages.append('* PythonTeX error')
+ messages.append(' Running code for Julia console failed')
+ with open(err_file_name, encoding='utf8') as f:
+ messages.append(f.read())
+ errors += 1
else:
if family == 'juliacon':
with open(out_file_name.rsplit('.', 1)[0] + '.tex', 'r', encoding=encoding) as f:
diff --git a/pythontex/pythontex3.py b/pythontex/pythontex3.py
index 92add2b..3297604 100644
--- a/pythontex/pythontex3.py
+++ b/pythontex/pythontex3.py
@@ -1567,6 +1567,12 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
messages.append('* PythonTeX error')
messages.append(' Missing output file for ' + key_run.replace('#', ':'))
errors += 1
+ elif family == 'juliacon' and proc.returncode == 1:
+ messages.append('* PythonTeX error')
+ messages.append(' Running code for Julia console failed')
+ with open(err_file_name, encoding='utf8') as f:
+ messages.append(f.read())
+ errors += 1
else:
if family == 'juliacon':
with open(out_file_name.rsplit('.', 1)[0] + '.tex', 'r', encoding=encoding) as f:
From c2dd55ce09e556540894e114097eeefd563c0b6a Mon Sep 17 00:00:00 2001
From: Geoffrey Poore
Date: Fri, 19 Oct 2018 19:42:13 -0500
Subject: [PATCH 03/58] pythontexcustomcode and \pythontexcustomc now set
pytex.context(closes #65)
---
pythontex/pythontex.dtx | 9 +++++----
pythontex/pythontex.sty | 6 +++---
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/pythontex/pythontex.dtx b/pythontex/pythontex.dtx
index f128a57..ec1a498 100644
--- a/pythontex/pythontex.dtx
+++ b/pythontex/pythontex.dtx
@@ -26,7 +26,7 @@
%\NeedsTeXFormat{LaTeX2e}[1999/12/01]
%\ProvidesPackage{pythontex}
%<*package>
- [2018/10/07 v0.17dev execute and typeset Python code and other languages]
+ [2018/10/19 v0.17dev execute and typeset Python code and other languages]
%
%
%<*driver>
@@ -134,7 +134,7 @@
%
% \fi
%
-% \CheckSum{3218}
+% \CheckSum{3216}
%
% \CharacterTable
% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
@@ -158,6 +158,7 @@
% \begin{itemize}
% \item Updated Julia support for Julia versions 0.6 (\#107), and 0.7 and 1.0 (\#126, \#130).
% \item There are now meaningful error messages for Julia console when Weave.jl is not installed or raises errors (\#131).
+% \item \texttt{pythontexcustomcode} and \texttt{\string\pythontexcustomc} now set \texttt{pytex.context} (\#65).
% \end{itemize}
% \end{changelog}
%
@@ -3357,7 +3358,7 @@
}%
\xdef\pytx@type{CC:#2:#1}%
\edef\pytx@cmd{c}%
- \def\pytx@context{}%
+ \pytx@SetContext
\def\pytx@group{none}%
\let\pytx@InlineShow\@empty
\let\pytx@InlineSave\pytx@InlineSaveCode
@@ -3750,7 +3751,7 @@
}%
\xdef\pytx@type{CC:#2:#1}%
\edef\pytx@cmd{code}%
- \def\pytx@context{}%
+ \pytx@SetContext
\def\pytx@group{none}%
\pytx@BeginCodeEnv[none]}%
{\end{VerbatimOut}%
diff --git a/pythontex/pythontex.sty b/pythontex/pythontex.sty
index 4186e74..c6ab78c 100644
--- a/pythontex/pythontex.sty
+++ b/pythontex/pythontex.sty
@@ -20,7 +20,7 @@
%%
\NeedsTeXFormat{LaTeX2e}[1999/12/01]
\ProvidesPackage{pythontex}
- [2018/10/03 v0.17dev execute and typeset Python code and other languages]
+ [2018/10/19 v0.17dev execute and typeset Python code and other languages]
\newcommand{\pytx@packagename}{PythonTeX}
\newcommand{\pytx@packageversion}{0.17dev}
@@ -846,7 +846,7 @@
}%
\xdef\pytx@type{CC:#2:#1}%
\edef\pytx@cmd{c}%
- \def\pytx@context{}%
+ \pytx@SetContext
\def\pytx@group{none}%
\let\pytx@InlineShow\@empty
\let\pytx@InlineSave\pytx@InlineSaveCode
@@ -1068,7 +1068,7 @@
}%
\xdef\pytx@type{CC:#2:#1}%
\edef\pytx@cmd{code}%
- \def\pytx@context{}%
+ \pytx@SetContext
\def\pytx@group{none}%
\pytx@BeginCodeEnv[none]}%
{\end{VerbatimOut}%
From b1d03d9a77d33ca721ca8b66ec60aa5c57269766 Mon Sep 17 00:00:00 2001
From: Geoffrey Poore
Date: Sat, 20 Oct 2018 13:38:28 -0500
Subject: [PATCH 04/58] Added basic support for R scripts (not console) (#121)
---
README.rst | 4 ++--
pythontex/pythontex.dtx | 11 ++++++++++-
pythontex/pythontex.sty | 1 +
pythontex/pythontex_engines.py | 31 +++++++++++++++++++++++++++++--
4 files changed, 42 insertions(+), 5 deletions(-)
diff --git a/README.rst b/README.rst
index 2e2859f..646ace5 100644
--- a/README.rst
+++ b/README.rst
@@ -30,8 +30,8 @@ results to be included within the original document. It also provides syntax
highlighting for code within LaTeX documents via the Pygments syntax
highlighter.
-PythonTeX also provides support for Ruby, Julia, Octave, Sage, Bash, and Rust.
-Support for additional languages is coming soon.
+PythonTeX also provides support for Ruby, Julia, Octave, Sage, Bash, Rust,
+and R. Support for additional languages is coming soon.
See ``pythontex_quickstart.pdf`` to get started, and ``pythontex_gallery.pdf``
for examples of what is possible with PythonTeX. PythonTeX is included in
diff --git a/pythontex/pythontex.dtx b/pythontex/pythontex.dtx
index ec1a498..176fb87 100644
--- a/pythontex/pythontex.dtx
+++ b/pythontex/pythontex.dtx
@@ -134,7 +134,7 @@
%
% \fi
%
-% \CheckSum{3216}
+% \CheckSum{3218}
%
% \CharacterTable
% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
@@ -159,6 +159,7 @@
% \item Updated Julia support for Julia versions 0.6 (\#107), and 0.7 and 1.0 (\#126, \#130).
% \item There are now meaningful error messages for Julia console when Weave.jl is not installed or raises errors (\#131).
% \item \texttt{pythontexcustomcode} and \texttt{\string\pythontexcustomc} now set \texttt{pytex.context} (\#65).
+% \item Added \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{\string\Rc}, \texttt{Rcode}, ...) (\#121). This provides basic support for R scripts (not console). There is currently no utilities class or equivalent.
% \end{itemize}
% \end{changelog}
%
@@ -1596,6 +1597,13 @@
% Additionally, when using |\rust| and |\rs|, keep in mind that these wrap code in a block, so you \emph{cannot} use |rstex| by value in these contexts (both shared and mutable references are still fine, though).
%
%
+% \subsection{R}
+%
+% Support for R scripts was added in v0.17. There is not yet an equivalent of the Python or Julia consoles. Support for R is very basic; there is not currently a utilities class or any related features.
+%
+% R code is executed with |Rscript|. The |methods| library is loaded automatically as part of the code template. Expressions passed to the |\R| command are converted into strings via |toString()|.
+%
+%
% \subsection{Adding support for a new language}
%
% Adding support for an additional language involves creating two templates, creating a new instance of a class, and using a \pytex\ macro. In some cases, additional changes may be necessary for full support. The information below does not deal with creating |console| families; additional support for user-defined |console| families will be added in the future.
@@ -4115,6 +4123,7 @@
\ifstrequal{#1}{sage}{\makepythontexfamily[pyglexer=sage]{sage}}{}%
\ifstrequal{#1}{rust}{\makepythontexfamily[pyglexer=rust]{rust}}{}%
\ifstrequal{#1}{rs}{\makepythontexfamily[pyglexer=rust]{rs}}{}%
+ \ifstrequal{#1}{R}{\makepythontexfamily[pyglexer=r]{R}}{}%
}
\expandafter\docsvlist\expandafter{\pytx@families}
% \end{macrocode}
diff --git a/pythontex/pythontex.sty b/pythontex/pythontex.sty
index c6ab78c..52ff411 100644
--- a/pythontex/pythontex.sty
+++ b/pythontex/pythontex.sty
@@ -1342,6 +1342,7 @@
\ifstrequal{#1}{sage}{\makepythontexfamily[pyglexer=sage]{sage}}{}%
\ifstrequal{#1}{rust}{\makepythontexfamily[pyglexer=rust]{rust}}{}%
\ifstrequal{#1}{rs}{\makepythontexfamily[pyglexer=rust]{rs}}{}%
+ \ifstrequal{#1}{R}{\makepythontexfamily[pyglexer=r]{R}}{}%
}
\expandafter\docsvlist\expandafter{\pytx@families}
\newbool{pytx@listingenv}
diff --git a/pythontex/pythontex_engines.py b/pythontex/pythontex_engines.py
index 874a7be..d73d94c 100644
--- a/pythontex/pythontex_engines.py
+++ b/pythontex/pythontex_engines.py
@@ -17,7 +17,7 @@
-Copyright (c) 2012-2017, Geoffrey M. Poore
+Copyright (c) 2012-2018, Geoffrey M. Poore
All rights reserved.
Licensed under the BSD 3-Clause License:
http://www.opensource.org/licenses/BSD-3-Clause
@@ -33,7 +33,8 @@
from collections import OrderedDict, namedtuple
-interpreter_dict = {k:k for k in ('python', 'ruby', 'julia', 'octave', 'bash', 'sage', 'rustc')}
+interpreter_dict = {k:k for k in ('python', 'ruby', 'julia', 'octave', 'bash',
+ 'sage', 'rustc', 'Rscript')}
# The {file} field needs to be replaced by itself, since the actual
# substitution of the real file can only be done at runtime, whereas the
# substitution for the interpreter should be done when the engine is
@@ -1541,3 +1542,29 @@ def cleanup
created='{File}.exe')
SubCodeEngine('rust', 'rs')
+
+
+r_template = '''
+ library(methods)
+ setwd("{workingdir}")
+ {body}
+ write("{dependencies_delim}", stdout())
+ write("{created_delim}", stdout())
+ '''
+
+r_wrapper = '''
+ write("{stdoutdelim}", stdout())
+ write("{stderrdelim}", stderr())
+ {code}
+ '''
+
+r_sub = '''
+ write("{field_delim}", stdout())
+ write(toString({field}), stdout())
+ '''
+
+CodeEngine('R', 'R', '.R',
+ '{Rscript} "{file}.R"',
+ r_template, r_wrapper, 'write(toString({code}), stdout())', r_sub,
+ ['error', 'Error'], ['warning', 'Warning'],
+ 'line {number}')
From 333d40166a9521c1955984293639c6f0facb5c86 Mon Sep 17 00:00:00 2001
From: Geoffrey Poore
Date: Sat, 20 Oct 2018 13:45:53 -0500
Subject: [PATCH 05/58] tests for R (#121)
---
test/R/R_test.tex | 103 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 103 insertions(+)
create mode 100644 test/R/R_test.tex
diff --git a/test/R/R_test.tex b/test/R/R_test.tex
new file mode 100644
index 0000000..233c3d0
--- /dev/null
+++ b/test/R/R_test.tex
@@ -0,0 +1,103 @@
+\documentclass[11pt]{article}
+
+% Engine-specific settings
+% pdftex:
+\ifcsname pdfmatch\endcsname
+ \usepackage[T1]{fontenc}
+ \usepackage[utf8]{inputenc}
+\fi
+% xetex:
+\ifcsname XeTeXinterchartoks\endcsname
+ \usepackage{fontspec}
+ \defaultfontfeatures{Ligatures=TeX}
+\fi
+% luatex:
+\ifcsname directlua\endcsname
+ \usepackage{fontspec}
+\fi
+% End engine-specific settings
+
+\usepackage{lmodern}
+\usepackage{amssymb,amsmath}
+\usepackage{graphicx}
+\usepackage{fullpage}
+\usepackage[keeptemps=all, makestderr, usefamily={R}]{pythontex}
+
+
+\begin{document}
+
+
+
+\section*{R}
+
+\subsection*{Commands}
+
+\R{2^8}
+
+\Rc{write(2^16, stdout())}
+
+\Rb{cat(2^32)}
+
+\printpythontex
+
+\Rv{cat(2^32)}
+
+\Rs{\LaTeX\ and then \textcolor{blue}{!{"R"}} and back to \LaTeX.}
+
+
+\subsection*{Environments}
+
+Code:
+\begin{Rcode}
+cat("A string.", " ")
+cat(2^8)
+\end{Rcode}
+
+Block:
+\begin{Rblock}
+cat("A string.", " ")
+cat(2^8)
+\end{Rblock}
+
+\printpythontex
+
+Verbatim:
+\begin{Rverbatim}
+cat("A string.", " ")
+cat(2^8)
+\end{Rverbatim}
+
+Sub:
+\begin{Rsub}
+\LaTeX\ and then \textcolor{blue}{!{"R"}} and back to \LaTeX.
+\end{Rsub}
+
+
+\section*{R stderr}
+
+
+\begin{Rblock}[err1][numbers=left]
+# Comment
+s <- "R a
+\end{Rblock}
+
+\stderrpythontex[][breaklines, breakafter=\\/]
+
+\begin{Rblock}[err2][numbers=left]
+1++
+\end{Rblock}
+
+\stderrpythontex[][breaklines, breakafter=\\/]
+
+\begin{Rblock}[err3][numbers=left]
+# Comment
+# Another comment
+1 + "ab"
+\end{Rblock}
+
+\stderrpythontex[][breaklines, breakafter=\\/]
+
+
+
+\end{document}
+
From 4073dd2f946400316ef52ca0844988e725a21311 Mon Sep 17 00:00:00 2001
From: Geoffrey Poore
Date: Mon, 22 Oct 2018 21:03:36 -0500
Subject: [PATCH 06/58] added Rconsole environment (#121); improved Julia
console
---
pythontex/pythontex.dtx | 81 ++++++++++++++++++++++++----------
pythontex/pythontex.sty | 66 +++++++++++++++++++--------
pythontex/pythontex2.py | 44 +++++++++++++-----
pythontex/pythontex3.py | 44 +++++++++++++-----
pythontex/pythontex_engines.py | 20 +++++++++
5 files changed, 190 insertions(+), 65 deletions(-)
diff --git a/pythontex/pythontex.dtx b/pythontex/pythontex.dtx
index 176fb87..ec09801 100644
--- a/pythontex/pythontex.dtx
+++ b/pythontex/pythontex.dtx
@@ -26,7 +26,7 @@
%\NeedsTeXFormat{LaTeX2e}[1999/12/01]
%\ProvidesPackage{pythontex}
%<*package>
- [2018/10/19 v0.17dev execute and typeset Python code and other languages]
+ [2018/10/22 v0.17dev execute and typeset Python code and other languages]
%
%
%<*driver>
@@ -134,7 +134,7 @@
%
% \fi
%
-% \CheckSum{3218}
+% \CheckSum{3276}
%
% \CharacterTable
% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
@@ -159,7 +159,7 @@
% \item Updated Julia support for Julia versions 0.6 (\#107), and 0.7 and 1.0 (\#126, \#130).
% \item There are now meaningful error messages for Julia console when Weave.jl is not installed or raises errors (\#131).
% \item \texttt{pythontexcustomcode} and \texttt{\string\pythontexcustomc} now set \texttt{pytex.context} (\#65).
-% \item Added \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{\string\Rc}, \texttt{Rcode}, ...) (\#121). This provides basic support for R scripts (not console). There is currently no utilities class or equivalent.
+% \item Added support for R. The \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{\string\Rc}, \texttt{Rcode}, ...) executes code as a script. There is currently no utilities class or equivalent. The \texttt{Rcon} family (\texttt{Rconsole}) executes code to emulate an interactive R session (\#121).
% \end{itemize}
% \end{changelog}
%
@@ -1599,9 +1599,14 @@
%
% \subsection{R}
%
-% Support for R scripts was added in v0.17. There is not yet an equivalent of the Python or Julia consoles. Support for R is very basic; there is not currently a utilities class or any related features.
+% Support for R was added in v0.17.
%
-% R code is executed with |Rscript|. The |methods| library is loaded automatically as part of the code template. Expressions passed to the |\R| command are converted into strings via |toString()|.
+% Loading \pytex\ with |usefamily=R| enables
+the \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{\string\Rc}, \texttt{Rcode}, ...). These execute code with |Rscript|. The |methods| library is loaded automatically as part of the code template. Expressions passed to the |\R| command are converted into strings via |toString()|. There is currently no utilities class or related features.
+%
+% \subsubsection*{Console}
+%
+% Loading \pytex\ with |usefamily=Rcon| enables the \texttt{Rconsole} environment, which executes code to emulate an interactive R session. Code is executed with |Rscript|. The option |echo=TRUE| is used to intersperse code with output, while |error=function(){}| is used to avoid halting on errors. A null graphics device, |pdf(file=NULL)|, is created by default, so plots that are to be included in the document require explicit commands. The |methods| library is loaded automatically as part of the code template.
%
%
% \subsection{Adding support for a new language}
@@ -4098,24 +4103,30 @@
\ifstrequal{#1}{ruby}{\makepythontexfamily[pyglexer=ruby]{ruby}}{}%
\ifstrequal{#1}{rb}{\makepythontexfamily[pyglexer=ruby]{rb}}{}%
\ifstrequal{#1}{julia}{\makepythontexfamily[pyglexer=julia]{julia}}{}%
- \ifstrequal{#1}{juliacon}{\pgfkeys{/PYTX/family, name=juliacon, default, pyglexer=jlcon}%
- \expandafter\xdef\csname pytx@macroformatter@juliacon\endcsname{\pytx@tmp@pprinter}%
- \expandafter\gdef\csname pytx@fvsettings@juliacon\endcsname{}%
- \expandafter\xdef\csname pytx@pyglexer@juliacon\endcsname{\pytx@tmp@pyglexer}%
- \expandafter\xdef\csname pytx@pygopt@juliacon\endcsname{\pytx@tmp@pygopt}%
- \expandafter\xdef\csname pytx@console@juliacon\endcsname{\pytx@tmp@console}%
- \AtEndDocument{\immediate\write\pytx@codefile{pygfamily=juliacon|%
- \csname pytx@pyglexer@juliacon\endcsname|%
- \csname pytx@pygopt@juliacon\endcsname}%
- }%
- \pytx@MakeCodeFV{juliacon}%
- \newenvironment{juliaconsole}%
- {\VerbatimEnvironment
- \def\pytx@type{juliacon}%
- \pytx@ConfigPygments
- \begin{juliaconcode}}%
- {\end{juliaconcode}%
- \ifbool{pytx@opt@autoprint}{}{\printpythontex{}}}}{}%
+ \ifstrequal{#1}{juliacon}{%
+ \pgfkeys{/PYTX/family, name=juliacon, default, pyglexer=jlcon}%
+ \expandafter\xdef\csname pytx@macroformatter@juliacon\endcsname{\pytx@tmp@pprinter}%
+ \expandafter\gdef\csname pytx@fvsettings@juliacon\endcsname{}%
+ \expandafter\xdef\csname pytx@pyglexer@juliacon\endcsname{\pytx@tmp@pyglexer}%
+ \expandafter\xdef\csname pytx@pygopt@juliacon\endcsname{\pytx@tmp@pygopt}%
+ \expandafter\xdef\csname pytx@console@juliacon\endcsname{\pytx@tmp@console}%
+ \AtEndDocument{\immediate\write\pytx@codefile{pygfamily=juliacon|%
+ \csname pytx@pyglexer@juliacon\endcsname|%
+ \csname pytx@pygopt@juliacon\endcsname}%
+ }%
+ \pytx@MakeCodeFV{juliacon}%
+ \newenvironment{juliaconsole}%
+ {\VerbatimEnvironment
+ \def\pytx@type{juliacon}%
+ \pytx@ConfigPygments
+ \begin{juliaconcode}}%
+ {\end{juliaconcode}%
+ \ifbool{pytx@opt@autoprint}{}{\printpythontex{}}%
+ \IfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
+ {}%
+ {\textbf{??~\pytx@packagename~??}}%
+ \PackageWarning{\pytx@packagename}{Non-existent console content}}
+ }{}%
\ifstrequal{#1}{jl}{\makepythontexfamily[pyglexer=julia]{jl}}{}%
\ifstrequal{#1}{matlab}{\makepythontexfamily[pyglexer=matlab]{matlab}}{}%
\ifstrequal{#1}{octave}{\makepythontexfamily[pyglexer=octave]{octave}}{}%
@@ -4124,6 +4135,30 @@
\ifstrequal{#1}{rust}{\makepythontexfamily[pyglexer=rust]{rust}}{}%
\ifstrequal{#1}{rs}{\makepythontexfamily[pyglexer=rust]{rs}}{}%
\ifstrequal{#1}{R}{\makepythontexfamily[pyglexer=r]{R}}{}%
+ \ifstrequal{#1}{Rcon}{%
+ \pgfkeys{/PYTX/family, name=Rcon, default, pyglexer=rconsole}%
+ \expandafter\xdef\csname pytx@macroformatter@Rcon\endcsname{\pytx@tmp@pprinter}%
+ \expandafter\gdef\csname pytx@fvsettings@Rcon\endcsname{}%
+ \expandafter\xdef\csname pytx@pyglexer@Rcon\endcsname{\pytx@tmp@pyglexer}%
+ \expandafter\xdef\csname pytx@pygopt@Rcon\endcsname{\pytx@tmp@pygopt}%
+ \expandafter\xdef\csname pytx@console@Rcon\endcsname{\pytx@tmp@console}%
+ \AtEndDocument{\immediate\write\pytx@codefile{pygfamily=Rcon|%
+ \csname pytx@pyglexer@Rcon\endcsname|%
+ \csname pytx@pygopt@Rcon\endcsname}%
+ }%
+ \pytx@MakeCodeFV{Rcon}%
+ \newenvironment{Rconsole}%
+ {\VerbatimEnvironment
+ \def\pytx@type{Rcon}%
+ \pytx@ConfigPygments
+ \begin{Rconcode}}%
+ {\end{Rconcode}%
+ \ifbool{pytx@opt@autoprint}{}{\printpythontex{}}%
+ \IfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
+ {}%
+ {\textbf{??~\pytx@packagename~??}%
+ \PackageWarning{\pytx@packagename}{Non-existent console content}}}
+ }{}%
}
\expandafter\docsvlist\expandafter{\pytx@families}
% \end{macrocode}
diff --git a/pythontex/pythontex.sty b/pythontex/pythontex.sty
index 52ff411..1145f18 100644
--- a/pythontex/pythontex.sty
+++ b/pythontex/pythontex.sty
@@ -1317,24 +1317,30 @@
\ifstrequal{#1}{ruby}{\makepythontexfamily[pyglexer=ruby]{ruby}}{}%
\ifstrequal{#1}{rb}{\makepythontexfamily[pyglexer=ruby]{rb}}{}%
\ifstrequal{#1}{julia}{\makepythontexfamily[pyglexer=julia]{julia}}{}%
- \ifstrequal{#1}{juliacon}{\pgfkeys{/PYTX/family, name=juliacon, default, pyglexer=jlcon}%
- \expandafter\xdef\csname pytx@macroformatter@juliacon\endcsname{\pytx@tmp@pprinter}%
- \expandafter\gdef\csname pytx@fvsettings@juliacon\endcsname{}%
- \expandafter\xdef\csname pytx@pyglexer@juliacon\endcsname{\pytx@tmp@pyglexer}%
- \expandafter\xdef\csname pytx@pygopt@juliacon\endcsname{\pytx@tmp@pygopt}%
- \expandafter\xdef\csname pytx@console@juliacon\endcsname{\pytx@tmp@console}%
- \AtEndDocument{\immediate\write\pytx@codefile{pygfamily=juliacon|%
- \csname pytx@pyglexer@juliacon\endcsname|%
- \csname pytx@pygopt@juliacon\endcsname}%
- }%
- \pytx@MakeCodeFV{juliacon}%
- \newenvironment{juliaconsole}%
- {\VerbatimEnvironment
- \def\pytx@type{juliacon}%
- \pytx@ConfigPygments
- \begin{juliaconcode}}%
- {\end{juliaconcode}%
- \ifbool{pytx@opt@autoprint}{}{\printpythontex{}}}}{}%
+ \ifstrequal{#1}{juliacon}{%
+ \pgfkeys{/PYTX/family, name=juliacon, default, pyglexer=jlcon}%
+ \expandafter\xdef\csname pytx@macroformatter@juliacon\endcsname{\pytx@tmp@pprinter}%
+ \expandafter\gdef\csname pytx@fvsettings@juliacon\endcsname{}%
+ \expandafter\xdef\csname pytx@pyglexer@juliacon\endcsname{\pytx@tmp@pyglexer}%
+ \expandafter\xdef\csname pytx@pygopt@juliacon\endcsname{\pytx@tmp@pygopt}%
+ \expandafter\xdef\csname pytx@console@juliacon\endcsname{\pytx@tmp@console}%
+ \AtEndDocument{\immediate\write\pytx@codefile{pygfamily=juliacon|%
+ \csname pytx@pyglexer@juliacon\endcsname|%
+ \csname pytx@pygopt@juliacon\endcsname}%
+ }%
+ \pytx@MakeCodeFV{juliacon}%
+ \newenvironment{juliaconsole}%
+ {\VerbatimEnvironment
+ \def\pytx@type{juliacon}%
+ \pytx@ConfigPygments
+ \begin{juliaconcode}}%
+ {\end{juliaconcode}%
+ \ifbool{pytx@opt@autoprint}{}{\printpythontex{}}%
+ \IfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
+ {}%
+ {\textbf{??~\pytx@packagename~??}}%
+ \PackageWarning{\pytx@packagename}{Non-existent console content}}
+ }{}%
\ifstrequal{#1}{jl}{\makepythontexfamily[pyglexer=julia]{jl}}{}%
\ifstrequal{#1}{matlab}{\makepythontexfamily[pyglexer=matlab]{matlab}}{}%
\ifstrequal{#1}{octave}{\makepythontexfamily[pyglexer=octave]{octave}}{}%
@@ -1343,6 +1349,30 @@
\ifstrequal{#1}{rust}{\makepythontexfamily[pyglexer=rust]{rust}}{}%
\ifstrequal{#1}{rs}{\makepythontexfamily[pyglexer=rust]{rs}}{}%
\ifstrequal{#1}{R}{\makepythontexfamily[pyglexer=r]{R}}{}%
+ \ifstrequal{#1}{Rcon}{%
+ \pgfkeys{/PYTX/family, name=Rcon, default, pyglexer=rconsole}%
+ \expandafter\xdef\csname pytx@macroformatter@Rcon\endcsname{\pytx@tmp@pprinter}%
+ \expandafter\gdef\csname pytx@fvsettings@Rcon\endcsname{}%
+ \expandafter\xdef\csname pytx@pyglexer@Rcon\endcsname{\pytx@tmp@pyglexer}%
+ \expandafter\xdef\csname pytx@pygopt@Rcon\endcsname{\pytx@tmp@pygopt}%
+ \expandafter\xdef\csname pytx@console@Rcon\endcsname{\pytx@tmp@console}%
+ \AtEndDocument{\immediate\write\pytx@codefile{pygfamily=Rcon|%
+ \csname pytx@pyglexer@Rcon\endcsname|%
+ \csname pytx@pygopt@Rcon\endcsname}%
+ }%
+ \pytx@MakeCodeFV{Rcon}%
+ \newenvironment{Rconsole}%
+ {\VerbatimEnvironment
+ \def\pytx@type{Rcon}%
+ \pytx@ConfigPygments
+ \begin{Rconcode}}%
+ {\end{Rconcode}%
+ \ifbool{pytx@opt@autoprint}{}{\printpythontex{}}%
+ \IfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
+ {}%
+ {\textbf{??~\pytx@packagename~??}%
+ \PackageWarning{\pytx@packagename}{Non-existent console content}}}
+ }{}%
}
\expandafter\docsvlist\expandafter{\pytx@families}
\newbool{pytx@listingenv}
diff --git a/pythontex/pythontex2.py b/pythontex/pythontex2.py
index af6e20f..615fef0 100644
--- a/pythontex/pythontex2.py
+++ b/pythontex/pythontex2.py
@@ -1535,7 +1535,10 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
# Add any created files due to the command
# This needs to be done before attempts to execute, to prevent orphans
try:
- proc = subprocess.Popen(exec_cmd, stdout=out_file, stderr=err_file)
+ if family != 'Rcon':
+ proc = subprocess.Popen(exec_cmd, stdout=out_file, stderr=err_file)
+ else:
+ proc = subprocess.Popen(exec_cmd, stdout=out_file, stderr=subprocess.STDOUT)
except WindowsError as e:
if e.errno == 2:
# Batch files won't be found when called without extension. They
@@ -1546,7 +1549,10 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
# under Windows; a list is not required.
exec_cmd_string = ' '.join(exec_cmd)
exec_cmd_string = 'cmd /C "@echo off & call {0} & if errorlevel 1 exit 1"'.format(exec_cmd_string)
- proc = subprocess.Popen(exec_cmd_string, stdout=out_file, stderr=err_file)
+ if family != 'Rcon':
+ proc = subprocess.Popen(exec_cmd_string, stdout=out_file, stderr=err_file)
+ else:
+ proc = subprocess.Popen(exec_cmd_string, stdout=out_file, stderr=subprocess.STDOUT)
else:
raise
@@ -1589,6 +1595,19 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
tex_data_lines.append('=>PYTHONTEX:DEPENDENCIES#\n=>PYTHONTEX:CREATED#\n')
with open(out_file_name, 'w', encoding=encoding) as f:
f.write(''.join(tex_data_lines))
+ elif family == 'Rcon':
+ with open(out_file_name, 'r', encoding=encoding) as f:
+ stdout_lines = f.readlines()
+ for n, line in enumerate(stdout_lines):
+ if line.startswith('> =>PYTHONTEX:'):
+ stdout_lines[n] = line[2:]
+ elif line.startswith('> write("=>PYTHONTEX:'):
+ stdout_lines[n] = ''
+ while stdout_lines and not stdout_lines[-1].strip('> \n'):
+ stdout_lines.pop()
+ stdout_lines.append('=>PYTHONTEX:DEPENDENCIES#\n=>PYTHONTEX:CREATED#\n')
+ with open(out_file_name, 'w', encoding=encoding) as f:
+ f.write(''.join(stdout_lines))
f = open(out_file_name, 'r', encoding=encoding)
out = f.read()
@@ -1646,7 +1665,7 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
else:
dependencies[dep] = (os.path.getmtime(dep_file), '')
- if family == 'juliacon':
+ if family in ('juliacon', 'Rcon'):
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import LatexFormatter
@@ -1664,13 +1683,14 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
instance, command = delims.split('#')
if content or command in ('s', 'sub'):
if instance.endswith('CC'):
- messages.append('* PythonTeX warning')
- messages.append(' Custom code for "' + family + '" attempted to print or write to stdout')
- messages.append(' This is not supported; use a normal code command or environment')
- messages.append(' The following content was written:')
- messages.append('')
- messages.extend([' ' + l for l in content.splitlines()])
- warnings += 1
+ if family not in ('juliacon', 'Rcon'):
+ messages.append('* PythonTeX warning')
+ messages.append(' Custom code for "' + family + '" attempted to print or write to stdout')
+ messages.append(' This is not supported; use a normal code command or environment')
+ messages.append(' The following content was written:')
+ messages.append('')
+ messages.extend([' ' + l for l in content.splitlines()])
+ warnings += 1
elif command == 'i':
content = r'\pytx@SVMCR{pytx@MCR@' + key_run.replace('#', '@') + '@' + instance + '}\n' + content.rstrip('\n') + '\\endpytx@SVMCR\n\n'
macros.append(content)
@@ -1688,7 +1708,7 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
# Remove newline added by printing, prevent
# LaTeX from adding a space after content
content = content.rsplit('\n', 1)[0] + '\\endinput\n'
- if family == 'juliacon':
+ if family in ('juliacon', 'Rcon'):
content = highlight(content, lexer[family], formatter[family])
f.write(content)
f.close()
@@ -1699,7 +1719,7 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
messages.append('* PythonTeX error')
messages.append(' Missing stderr file for ' + key_run.replace('#', ':'))
errors += 1
- elif family == 'juliacon':
+ elif family in ('juliacon', 'Rcon'):
pass
else:
# Open error and code files.
diff --git a/pythontex/pythontex3.py b/pythontex/pythontex3.py
index 3297604..65b8691 100644
--- a/pythontex/pythontex3.py
+++ b/pythontex/pythontex3.py
@@ -1535,7 +1535,10 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
# Add any created files due to the command
# This needs to be done before attempts to execute, to prevent orphans
try:
- proc = subprocess.Popen(exec_cmd, stdout=out_file, stderr=err_file)
+ if family != 'Rcon':
+ proc = subprocess.Popen(exec_cmd, stdout=out_file, stderr=err_file)
+ else:
+ proc = subprocess.Popen(exec_cmd, stdout=out_file, stderr=subprocess.STDOUT)
except WindowsError as e:
if e.errno == 2:
# Batch files won't be found when called without extension. They
@@ -1546,7 +1549,10 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
# under Windows; a list is not required.
exec_cmd_string = ' '.join(exec_cmd)
exec_cmd_string = 'cmd /C "@echo off & call {0} & if errorlevel 1 exit 1"'.format(exec_cmd_string)
- proc = subprocess.Popen(exec_cmd_string, stdout=out_file, stderr=err_file)
+ if family != 'Rcon':
+ proc = subprocess.Popen(exec_cmd_string, stdout=out_file, stderr=err_file)
+ else:
+ proc = subprocess.Popen(exec_cmd_string, stdout=out_file, stderr=subprocess.STDOUT)
else:
raise
@@ -1589,6 +1595,19 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
tex_data_lines.append('=>PYTHONTEX:DEPENDENCIES#\n=>PYTHONTEX:CREATED#\n')
with open(out_file_name, 'w', encoding=encoding) as f:
f.write(''.join(tex_data_lines))
+ elif family == 'Rcon':
+ with open(out_file_name, 'r', encoding=encoding) as f:
+ stdout_lines = f.readlines()
+ for n, line in enumerate(stdout_lines):
+ if line.startswith('> =>PYTHONTEX:'):
+ stdout_lines[n] = line[2:]
+ elif line.startswith('> write("=>PYTHONTEX:'):
+ stdout_lines[n] = ''
+ while stdout_lines and not stdout_lines[-1].strip('> \n'):
+ stdout_lines.pop()
+ stdout_lines.append('=>PYTHONTEX:DEPENDENCIES#\n=>PYTHONTEX:CREATED#\n')
+ with open(out_file_name, 'w', encoding=encoding) as f:
+ f.write(''.join(stdout_lines))
f = open(out_file_name, 'r', encoding=encoding)
out = f.read()
@@ -1646,7 +1665,7 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
else:
dependencies[dep] = (os.path.getmtime(dep_file), '')
- if family == 'juliacon':
+ if family in ('juliacon', 'Rcon'):
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import LatexFormatter
@@ -1664,13 +1683,14 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
instance, command = delims.split('#')
if content or command in ('s', 'sub'):
if instance.endswith('CC'):
- messages.append('* PythonTeX warning')
- messages.append(' Custom code for "' + family + '" attempted to print or write to stdout')
- messages.append(' This is not supported; use a normal code command or environment')
- messages.append(' The following content was written:')
- messages.append('')
- messages.extend([' ' + l for l in content.splitlines()])
- warnings += 1
+ if family not in ('juliacon', 'Rcon'):
+ messages.append('* PythonTeX warning')
+ messages.append(' Custom code for "' + family + '" attempted to print or write to stdout')
+ messages.append(' This is not supported; use a normal code command or environment')
+ messages.append(' The following content was written:')
+ messages.append('')
+ messages.extend([' ' + l for l in content.splitlines()])
+ warnings += 1
elif command == 'i':
content = r'\pytx@SVMCR{pytx@MCR@' + key_run.replace('#', '@') + '@' + instance + '}\n' + content.rstrip('\n') + '\\endpytx@SVMCR\n\n'
macros.append(content)
@@ -1688,7 +1708,7 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
# Remove newline added by printing, prevent
# LaTeX from adding a space after content
content = content.rsplit('\n', 1)[0] + '\\endinput\n'
- if family == 'juliacon':
+ if family in ('juliacon', 'Rcon'):
content = highlight(content, lexer[family], formatter[family])
f.write(content)
f.close()
@@ -1699,7 +1719,7 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
messages.append('* PythonTeX error')
messages.append(' Missing stderr file for ' + key_run.replace('#', ':'))
errors += 1
- elif family == 'juliacon':
+ elif family in ('juliacon', 'Rcon'):
pass
else:
# Open error and code files.
diff --git a/pythontex/pythontex_engines.py b/pythontex/pythontex_engines.py
index d73d94c..e4fa1d8 100644
--- a/pythontex/pythontex_engines.py
+++ b/pythontex/pythontex_engines.py
@@ -1568,3 +1568,23 @@ def cleanup
r_template, r_wrapper, 'write(toString({code}), stdout())', r_sub,
['error', 'Error'], ['warning', 'Warning'],
'line {number}')
+
+
+rcon_template = '''
+ options(echo=TRUE, error=function(){{}})
+ pdf(file=NULL)
+ library(methods)
+ setwd("{workingdir}")
+ {body}
+ '''
+
+rcon_wrapper = '''
+ write("{stdoutdelim}", stdout())
+ {code}
+ '''
+
+CodeEngine('Rcon', 'R', '.R',
+ '{Rscript} "{file}.R"',
+ rcon_template, rcon_wrapper, '', '',
+ ['error', 'Error'], ['warning', 'Warning'],
+ '')
From f7fe7eba327a13f0def3ab5f3f60b51da5bfaa8b Mon Sep 17 00:00:00 2001
From: Geoffrey Poore
Date: Thu, 25 Oct 2018 05:04:08 -0500
Subject: [PATCH 07/58] all R templates now create a null graphics device,
after setting working dir, to avoid automatic creation of plot files with
default names
---
pythontex/pythontex.dtx | 4 ++--
pythontex/pythontex_engines.py | 3 ++-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/pythontex/pythontex.dtx b/pythontex/pythontex.dtx
index ec09801..8f849a8 100644
--- a/pythontex/pythontex.dtx
+++ b/pythontex/pythontex.dtx
@@ -1602,11 +1602,11 @@
% Support for R was added in v0.17.
%
% Loading \pytex\ with |usefamily=R| enables
-the \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{\string\Rc}, \texttt{Rcode}, ...). These execute code with |Rscript|. The |methods| library is loaded automatically as part of the code template. Expressions passed to the |\R| command are converted into strings via |toString()|. There is currently no utilities class or related features.
+the \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{\string\Rc}, \texttt{Rcode}, ...). These execute code with |Rscript|. The |methods| library is loaded automatically as part of the template code. Expressions passed to the |\R| command are converted into strings via |toString()|. There is currently no utilities class or related features. A null graphics device, |pdf(file=NULL)|, is created by default to avoid the automatic, unintentional creation of plot files with default names. Plots that are to be saved require explicit graphics commands.
%
% \subsubsection*{Console}
%
-% Loading \pytex\ with |usefamily=Rcon| enables the \texttt{Rconsole} environment, which executes code to emulate an interactive R session. Code is executed with |Rscript|. The option |echo=TRUE| is used to intersperse code with output, while |error=function(){}| is used to avoid halting on errors. A null graphics device, |pdf(file=NULL)|, is created by default, so plots that are to be included in the document require explicit commands. The |methods| library is loaded automatically as part of the code template.
+% Loading \pytex\ with |usefamily=Rcon| enables the \texttt{Rconsole} environment, which executes code to emulate an interactive R session. Code is executed with |Rscript|. The |methods| library is loaded automatically as part of the template code. The option |echo=TRUE| is used to intersperse code with output, while |error=function(){}| is used to avoid halting on errors. A null graphics device, |pdf(file=NULL)|, is created by default to avoid the automatic, unintentional creation of plot files with default names. Plots that are to be saved require explicit graphics commands.
%
%
% \subsection{Adding support for a new language}
diff --git a/pythontex/pythontex_engines.py b/pythontex/pythontex_engines.py
index e4fa1d8..16f391d 100644
--- a/pythontex/pythontex_engines.py
+++ b/pythontex/pythontex_engines.py
@@ -1547,6 +1547,7 @@ def cleanup
r_template = '''
library(methods)
setwd("{workingdir}")
+ pdf(file=NULL)
{body}
write("{dependencies_delim}", stdout())
write("{created_delim}", stdout())
@@ -1572,9 +1573,9 @@ def cleanup
rcon_template = '''
options(echo=TRUE, error=function(){{}})
- pdf(file=NULL)
library(methods)
setwd("{workingdir}")
+ pdf(file=NULL)
{body}
'''
From 82a07297e32e6ef3ae9d8e2d90d579de2e98d459 Mon Sep 17 00:00:00 2001
From: Geoffrey Poore
Date: Thu, 25 Oct 2018 06:07:40 -0500
Subject: [PATCH 08/58] fixed parsing bug when Rconsole ends with a command
that creates output without a trailing newline
---
pythontex/pythontex2.py | 20 +++++++++++++++++---
pythontex/pythontex3.py | 20 +++++++++++++++++---
2 files changed, 34 insertions(+), 6 deletions(-)
diff --git a/pythontex/pythontex2.py b/pythontex/pythontex2.py
index 615fef0..f5044a4 100644
--- a/pythontex/pythontex2.py
+++ b/pythontex/pythontex2.py
@@ -1601,9 +1601,19 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
for n, line in enumerate(stdout_lines):
if line.startswith('> =>PYTHONTEX:'):
stdout_lines[n] = line[2:]
- elif line.startswith('> write("=>PYTHONTEX:'):
- stdout_lines[n] = ''
- while stdout_lines and not stdout_lines[-1].strip('> \n'):
+ elif '> write("=>PYTHONTEX:' in line:
+ if line.startswith('> write("=>PYTHONTEX:'):
+ stdout_lines[n] = ''
+ else:
+ # cat() and similar functions can result in the
+ # prompt not being at the start of a new line. In
+ # that case, preserve the prompt to accurately
+ # emulate the console. If there is a following
+ # console environment, this effectively amounts
+ # to adding an extra empty line (pressing ENTER)
+ # between the two.
+ stdout_lines[n] = line.split('write("=>PYTHONTEX:', 1)[0]
+ while stdout_lines and (stdout_lines[-1].startswith('>') and not stdout_lines[-1][1:].strip(' \n')):
stdout_lines.pop()
stdout_lines.append('=>PYTHONTEX:DEPENDENCIES#\n=>PYTHONTEX:CREATED#\n')
with open(out_file_name, 'w', encoding=encoding) as f:
@@ -1680,6 +1690,10 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
for block in out.split('=>PYTHONTEX:STDOUT#')[1:]:
if block:
delims, content = block.split('#\n', 1)
+ if not content.endswith('\n'):
+ # Content might not end with a newline. For example,
+ # Rcon with something like cat() as the last function.
+ content += '\n'
instance, command = delims.split('#')
if content or command in ('s', 'sub'):
if instance.endswith('CC'):
diff --git a/pythontex/pythontex3.py b/pythontex/pythontex3.py
index 65b8691..63ab909 100644
--- a/pythontex/pythontex3.py
+++ b/pythontex/pythontex3.py
@@ -1601,9 +1601,19 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
for n, line in enumerate(stdout_lines):
if line.startswith('> =>PYTHONTEX:'):
stdout_lines[n] = line[2:]
- elif line.startswith('> write("=>PYTHONTEX:'):
- stdout_lines[n] = ''
- while stdout_lines and not stdout_lines[-1].strip('> \n'):
+ elif '> write("=>PYTHONTEX:' in line:
+ if line.startswith('> write("=>PYTHONTEX:'):
+ stdout_lines[n] = ''
+ else:
+ # cat() and similar functions can result in the
+ # prompt not being at the start of a new line. In
+ # that case, preserve the prompt to accurately
+ # emulate the console. If there is a following
+ # console environment, this effectively amounts
+ # to adding an extra empty line (pressing ENTER)
+ # between the two.
+ stdout_lines[n] = line.split('write("=>PYTHONTEX:', 1)[0]
+ while stdout_lines and (stdout_lines[-1].startswith('>') and not stdout_lines[-1][1:].strip(' \n')):
stdout_lines.pop()
stdout_lines.append('=>PYTHONTEX:DEPENDENCIES#\n=>PYTHONTEX:CREATED#\n')
with open(out_file_name, 'w', encoding=encoding) as f:
@@ -1680,6 +1690,10 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
for block in out.split('=>PYTHONTEX:STDOUT#')[1:]:
if block:
delims, content = block.split('#\n', 1)
+ if not content.endswith('\n'):
+ # Content might not end with a newline. For example,
+ # Rcon with something like cat() as the last function.
+ content += '\n'
instance, command = delims.split('#')
if content or command in ('s', 'sub'):
if instance.endswith('CC'):
From a2cc3ba01597ac1983b01ea88e9fb6d79d491f43 Mon Sep 17 00:00:00 2001
From: Geoffrey Poore
Date: Thu, 25 Oct 2018 06:09:38 -0500
Subject: [PATCH 09/58] add Rconsole tests
---
test/R/Rcon_test.tex | 79 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 79 insertions(+)
create mode 100644 test/R/Rcon_test.tex
diff --git a/test/R/Rcon_test.tex b/test/R/Rcon_test.tex
new file mode 100644
index 0000000..8e17411
--- /dev/null
+++ b/test/R/Rcon_test.tex
@@ -0,0 +1,79 @@
+\documentclass[11pt]{article}
+
+% Engine-specific settings
+% pdftex:
+\ifcsname pdfmatch\endcsname
+ \usepackage[T1]{fontenc}
+ \usepackage[utf8]{inputenc}
+\fi
+% xetex:
+\ifcsname XeTeXinterchartoks\endcsname
+ \usepackage{fontspec}
+ \defaultfontfeatures{Ligatures=TeX}
+\fi
+% luatex:
+\ifcsname directlua\endcsname
+ \usepackage{fontspec}
+\fi
+% End engine-specific settings
+
+\usepackage{lmodern}
+\usepackage{amssymb,amsmath}
+\usepackage{graphicx}
+\usepackage{fullpage}
+\usepackage[keeptemps=all, makestderr, usefamily={Rcon}]{pythontex}
+
+
+\begin{document}
+
+
+\section*{R Console}
+
+
+\subsection*{Basics}
+
+Console math and printing.
+
+\begin{Rconsole}
+1+4
+print("Some text ...")
+\end{Rconsole}
+
+Test \pygment{r}{cat()} without a trailing newline \verb|\n|. This should produce output followed immediately on the same line by the prompt and the next command.
+\begin{Rconsole}
+cat("Some", "text", "...")
+cat("And", "some", "more", "...")
+\end{Rconsole}
+
+After a final \pygment{r}{cat()} without a trailing newline \verb|\n|, the next environment should start normally, with the prompt at the beginning of the line.
+
+\begin{Rconsole}
+write("stdout", stdout())
+write("stderr", stderr())
+\end{Rconsole}
+
+
+\subsection*{Continuity between environments}
+
+Set a variable.
+\begin{Rconsole}
+x <- 2^12
+\end{Rconsole}
+
+Retrieve variable value.
+\begin{Rconsole}
+x
+\end{Rconsole}
+
+
+\subsection*{Continue after errors}
+
+\begin{Rconsole}
+1+"a"
+3*6
+\end{Rconsole}
+
+
+
+\end{document}
+
From 042dccd94ce1ad87df59238d6c2c2900f18ee2ff Mon Sep 17 00:00:00 2001
From: Geoffrey Poore
Date: Thu, 25 Oct 2018 10:03:18 -0500
Subject: [PATCH 10/58] PythonTeX-specific fancyvrb settings now work with
Julia and R consoles
---
pythontex/pythontex.dtx | 52 +++++++++++++++++++++++++++--------------
pythontex/pythontex.sty | 46 ++++++++++++++++++++++++------------
2 files changed, 65 insertions(+), 33 deletions(-)
diff --git a/pythontex/pythontex.dtx b/pythontex/pythontex.dtx
index 8f849a8..396641d 100644
--- a/pythontex/pythontex.dtx
+++ b/pythontex/pythontex.dtx
@@ -26,7 +26,7 @@
%\NeedsTeXFormat{LaTeX2e}[1999/12/01]
%\ProvidesPackage{pythontex}
%<*package>
- [2018/10/22 v0.17dev execute and typeset Python code and other languages]
+ [2018/10/25 v0.17dev execute and typeset Python code and other languages]
%
%
%<*driver>
@@ -134,7 +134,7 @@
%
% \fi
%
-% \CheckSum{3276}
+% \CheckSum{3321}
%
% \CharacterTable
% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
@@ -160,6 +160,7 @@
% \item There are now meaningful error messages for Julia console when Weave.jl is not installed or raises errors (\#131).
% \item \texttt{pythontexcustomcode} and \texttt{\string\pythontexcustomc} now set \texttt{pytex.context} (\#65).
% \item Added support for R. The \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{\string\Rc}, \texttt{Rcode}, ...) executes code as a script. There is currently no utilities class or equivalent. The \texttt{Rcon} family (\texttt{Rconsole}) executes code to emulate an interactive R session (\#121).
+% \item \texttt{fancyvrb} settings from \texttt{\string\setpythontexfv} and console environments now work with Julia and R consoles.
% \end{itemize}
% \end{changelog}
%
@@ -1601,8 +1602,7 @@
%
% Support for R was added in v0.17.
%
-% Loading \pytex\ with |usefamily=R| enables
-the \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{\string\Rc}, \texttt{Rcode}, ...). These execute code with |Rscript|. The |methods| library is loaded automatically as part of the template code. Expressions passed to the |\R| command are converted into strings via |toString()|. There is currently no utilities class or related features. A null graphics device, |pdf(file=NULL)|, is created by default to avoid the automatic, unintentional creation of plot files with default names. Plots that are to be saved require explicit graphics commands.
+% Loading \pytex\ with |usefamily=R| enables the \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{\string\Rc}, \texttt{Rcode}, ...). These execute code with |Rscript|. The |methods| library is loaded automatically as part of the template code. Expressions passed to the |\R| command are converted into strings via |toString()|. There is currently no utilities class or related features. A null graphics device, |pdf(file=NULL)|, is created by default to avoid the automatic, unintentional creation of plot files with default names. Plots that are to be saved require explicit graphics commands.
%
% \subsubsection*{Console}
%
@@ -3705,6 +3705,30 @@ the \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{
}%
{\end{VerbatimOut}%
\xdef\pytx@stdfile{\pytx@type_\pytx@session_\pytx@group_\arabic{\pytx@counter}}%
+ \ifdefstring{\pytx@type}{juliacon}{%
+ \setcounter{FancyVerbLine}{\value{\pytx@linecount}}%
+ \pytx@FVSet
+ \ifdefstring{\pytx@fvopttmp}{}{}{\expandafter\fvset\expandafter{\pytx@fvopttmp}}%
+ \pytx@ConfigPygments
+ \InputIfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
+ {}%
+ {\par\textbf{??~\pytx@packagename~??}\par
+ \PackageWarning{\pytx@packagename}{Non-existent console content}}%
+ \setcounter{\pytx@linecount}{\value{FancyVerbLine}}%
+ \let\pytx@EnvAutoprint\relax
+ }{}%
+ \ifdefstring{\pytx@type}{Rcon}{%
+ \setcounter{FancyVerbLine}{\value{\pytx@linecount}}%
+ \pytx@FVSet
+ \ifdefstring{\pytx@fvopttmp}{}{}{\expandafter\fvset\expandafter{\pytx@fvopttmp}}%
+ \pytx@ConfigPygments
+ \InputIfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
+ {}%
+ {\par\textbf{??~\pytx@packagename~??}\par
+ \PackageWarning{\pytx@packagename}{Non-existent console content}}%
+ \setcounter{\pytx@linecount}{\value{FancyVerbLine}}%
+ \let\pytx@EnvAutoprint\relax
+ }{}%
\setcounter{FancyVerbLine}{\value{pytx@FancyVerbLineTemp}}%
\stepcounter{\pytx@counter}%
\pytx@EnvAutoprint
@@ -3721,6 +3745,8 @@ the \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{
\ifstrempty{#1}{\edef\pytx@session{default}}{\StrSubstitute{#1}{:}{-}[\pytx@session]}%
\xdef\pytx@counter{pytx@\pytx@type @\pytx@session @\pytx@group}%
\pytx@CheckCounter{\pytx@counter}%
+ \edef\pytx@linecount{\pytx@counter @line}%
+ \pytx@CheckCounter{\pytx@linecount}%
\pytx@WriteCodefileInfo
\begingroup
\obeylines
@@ -3734,6 +3760,8 @@ the \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{
% As described above, this macro captures a second optional argument, if present, and then starts the |VerbatimOut| environment. Note that |VerbatimOut| does not have a mandatory argument, because we are invoking our custom |\pytx@FVB@VerbatimOut| macro. The default |fancyvrb| macro needs an argument to tell it the name of the file to which to save the verbatim content. But in our case, we are always writing to the same file, and the custom macro accounts for this by not having a mandatory file name argument. We must perform the typical |FancyVerbLine| trickery, to prevent the |fancyvrb| line counter from being affected by \textbf{writing} content!
% \begin{macrocode}
\def\pytx@BeginCodeEnv@i[#1]{%
+ \def\pytx@fvopttmp{#1}%
+ \def\pytx@argspprint{#1}%
\setcounter{pytx@FancyVerbLineTemp}{\value{FancyVerbLine}}%
\let\FVB@VerbatimOut\pytx@FVB@VerbatimOut
\let\FVE@VerbatimOut\pytx@FVE@VerbatimOut
@@ -4118,14 +4146,8 @@ the \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{
\newenvironment{juliaconsole}%
{\VerbatimEnvironment
\def\pytx@type{juliacon}%
- \pytx@ConfigPygments
\begin{juliaconcode}}%
- {\end{juliaconcode}%
- \ifbool{pytx@opt@autoprint}{}{\printpythontex{}}%
- \IfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
- {}%
- {\textbf{??~\pytx@packagename~??}}%
- \PackageWarning{\pytx@packagename}{Non-existent console content}}
+ {\end{juliaconcode}}
}{}%
\ifstrequal{#1}{jl}{\makepythontexfamily[pyglexer=julia]{jl}}{}%
\ifstrequal{#1}{matlab}{\makepythontexfamily[pyglexer=matlab]{matlab}}{}%
@@ -4150,14 +4172,8 @@ the \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{
\newenvironment{Rconsole}%
{\VerbatimEnvironment
\def\pytx@type{Rcon}%
- \pytx@ConfigPygments
\begin{Rconcode}}%
- {\end{Rconcode}%
- \ifbool{pytx@opt@autoprint}{}{\printpythontex{}}%
- \IfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
- {}%
- {\textbf{??~\pytx@packagename~??}%
- \PackageWarning{\pytx@packagename}{Non-existent console content}}}
+ {\end{Rconcode}}
}{}%
}
\expandafter\docsvlist\expandafter{\pytx@families}
diff --git a/pythontex/pythontex.sty b/pythontex/pythontex.sty
index 1145f18..2a97e2c 100644
--- a/pythontex/pythontex.sty
+++ b/pythontex/pythontex.sty
@@ -20,7 +20,7 @@
%%
\NeedsTeXFormat{LaTeX2e}[1999/12/01]
\ProvidesPackage{pythontex}
- [2018/10/19 v0.17dev execute and typeset Python code and other languages]
+ [2018/10/25 v0.17dev execute and typeset Python code and other languages]
\newcommand{\pytx@packagename}{PythonTeX}
\newcommand{\pytx@packageversion}{0.17dev}
@@ -1037,6 +1037,30 @@
}%
{\end{VerbatimOut}%
\xdef\pytx@stdfile{\pytx@type_\pytx@session_\pytx@group_\arabic{\pytx@counter}}%
+ \ifdefstring{\pytx@type}{juliacon}{%
+ \setcounter{FancyVerbLine}{\value{\pytx@linecount}}%
+ \pytx@FVSet
+ \ifdefstring{\pytx@fvopttmp}{}{}{\expandafter\fvset\expandafter{\pytx@fvopttmp}}%
+ \pytx@ConfigPygments
+ \InputIfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
+ {}%
+ {\par\textbf{??~\pytx@packagename~??}\par
+ \PackageWarning{\pytx@packagename}{Non-existent console content}}%
+ \setcounter{\pytx@linecount}{\value{FancyVerbLine}}%
+ \let\pytx@EnvAutoprint\relax
+ }{}%
+ \ifdefstring{\pytx@type}{Rcon}{%
+ \setcounter{FancyVerbLine}{\value{\pytx@linecount}}%
+ \pytx@FVSet
+ \ifdefstring{\pytx@fvopttmp}{}{}{\expandafter\fvset\expandafter{\pytx@fvopttmp}}%
+ \pytx@ConfigPygments
+ \InputIfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
+ {}%
+ {\par\textbf{??~\pytx@packagename~??}\par
+ \PackageWarning{\pytx@packagename}{Non-existent console content}}%
+ \setcounter{\pytx@linecount}{\value{FancyVerbLine}}%
+ \let\pytx@EnvAutoprint\relax
+ }{}%
\setcounter{FancyVerbLine}{\value{pytx@FancyVerbLineTemp}}%
\stepcounter{\pytx@counter}%
\pytx@EnvAutoprint
@@ -1046,12 +1070,16 @@
\ifstrempty{#1}{\edef\pytx@session{default}}{\StrSubstitute{#1}{:}{-}[\pytx@session]}%
\xdef\pytx@counter{pytx@\pytx@type @\pytx@session @\pytx@group}%
\pytx@CheckCounter{\pytx@counter}%
+ \edef\pytx@linecount{\pytx@counter @line}%
+ \pytx@CheckCounter{\pytx@linecount}%
\pytx@WriteCodefileInfo
\begingroup
\obeylines
\@ifnextchar[{\endgroup\pytx@BeginCodeEnv@i}{\endgroup\pytx@BeginCodeEnv@i[]}%
}%
\def\pytx@BeginCodeEnv@i[#1]{%
+ \def\pytx@fvopttmp{#1}%
+ \def\pytx@argspprint{#1}%
\setcounter{pytx@FancyVerbLineTemp}{\value{FancyVerbLine}}%
\let\FVB@VerbatimOut\pytx@FVB@VerbatimOut
\let\FVE@VerbatimOut\pytx@FVE@VerbatimOut
@@ -1332,14 +1360,8 @@
\newenvironment{juliaconsole}%
{\VerbatimEnvironment
\def\pytx@type{juliacon}%
- \pytx@ConfigPygments
\begin{juliaconcode}}%
- {\end{juliaconcode}%
- \ifbool{pytx@opt@autoprint}{}{\printpythontex{}}%
- \IfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
- {}%
- {\textbf{??~\pytx@packagename~??}}%
- \PackageWarning{\pytx@packagename}{Non-existent console content}}
+ {\end{juliaconcode}}
}{}%
\ifstrequal{#1}{jl}{\makepythontexfamily[pyglexer=julia]{jl}}{}%
\ifstrequal{#1}{matlab}{\makepythontexfamily[pyglexer=matlab]{matlab}}{}%
@@ -1364,14 +1386,8 @@
\newenvironment{Rconsole}%
{\VerbatimEnvironment
\def\pytx@type{Rcon}%
- \pytx@ConfigPygments
\begin{Rconcode}}%
- {\end{Rconcode}%
- \ifbool{pytx@opt@autoprint}{}{\printpythontex{}}%
- \IfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
- {}%
- {\textbf{??~\pytx@packagename~??}%
- \PackageWarning{\pytx@packagename}{Non-existent console content}}}
+ {\end{Rconcode}}
}{}%
}
\expandafter\docsvlist\expandafter{\pytx@families}
From 19af348029938670702cfb14d7557b1a57f5fc6c Mon Sep 17 00:00:00 2001
From: Geoffrey Poore
Date: Thu, 25 Oct 2018 10:05:52 -0500
Subject: [PATCH 11/58] tests for juliaconsole
---
test/julia/juliacon_test.tex | 76 ++++++++++++++++++++++++++++++++++++
1 file changed, 76 insertions(+)
create mode 100644 test/julia/juliacon_test.tex
diff --git a/test/julia/juliacon_test.tex b/test/julia/juliacon_test.tex
new file mode 100644
index 0000000..7a8138e
--- /dev/null
+++ b/test/julia/juliacon_test.tex
@@ -0,0 +1,76 @@
+\documentclass[11pt]{article}
+
+% Engine-specific settings
+% pdftex:
+\ifcsname pdfmatch\endcsname
+ \usepackage[T1]{fontenc}
+ \usepackage[utf8]{inputenc}
+\fi
+% xetex:
+\ifcsname XeTeXinterchartoks\endcsname
+ \usepackage{fontspec}
+ \defaultfontfeatures{Ligatures=TeX}
+\fi
+% luatex:
+\ifcsname directlua\endcsname
+ \usepackage{fontspec}
+\fi
+% End engine-specific settings
+
+\usepackage{lmodern}
+\usepackage{amssymb,amsmath}
+\usepackage{graphicx}
+\usepackage{fullpage}
+\usepackage[keeptemps=all, makestderr, usefamily={juliacon,julia}]{pythontex}
+
+
+\begin{document}
+
+
+\section*{Julia Console}
+
+
+\subsection*{Basics}
+
+Console math and printing.
+
+\begin{juliaconsole}
+1+4
+println("Some text ...")
+\end{juliaconsole}
+
+Test code that generates output without a trailing newline \verb|\n|. Since there is always a newline before the \verb|julia>| prompt, this should affect vertical layout, but not line breaks.
+\begin{juliaconsole}
+print("Some text ...")
+print("And some more ...")
+\end{juliaconsole}
+
+The next environment should start normally, with the prompt at the beginning of the line.
+\begin{juliaconsole}
+println("After ...")
+\end{juliaconsole}
+
+
+
+\subsection*{Continuity between environments}
+
+Set a variable.
+\begin{juliaconsole}
+x = 2^12
+\end{juliaconsole}
+
+Retrieve variable value.
+\begin{juliaconsole}
+x
+\end{juliaconsole}
+
+
+\subsection*{Continue after errors}
+
+\begin{juliaconsole}[][breaklines]
+1+"a"
+3*6
+\end{juliaconsole}
+
+
+\end{document}
From a389c7b50bbb8fc4082c29fa642882b0f125dc90 Mon Sep 17 00:00:00 2001
From: Geoffrey Poore
Date: Sun, 28 Oct 2018 13:40:11 -0500
Subject: [PATCH 12/58] update docs to reflect Weave.jl error-handling
capabilities
---
pythontex/pythontex.dtx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pythontex/pythontex.dtx b/pythontex/pythontex.dtx
index 396641d..b729d96 100644
--- a/pythontex/pythontex.dtx
+++ b/pythontex/pythontex.dtx
@@ -26,7 +26,7 @@
%\NeedsTeXFormat{LaTeX2e}[1999/12/01]
%\ProvidesPackage{pythontex}
%<*package>
- [2018/10/25 v0.17dev execute and typeset Python code and other languages]
+ [2018/10/28 v0.17dev execute and typeset Python code and other languages]
%
%
%<*driver>
@@ -1560,7 +1560,7 @@
%
% \subsubsection*{Console}
%
-% Preliminary Julia console support was added in v0.16, under the base name |juliacon|. Unlike the Python console environments that allow invalid input, currently everything entered in a |juliaconsole| environment must be valid. The Julia console uses \href{https://github.com/mpastell/Weave.jl}{\textsf{Weave.jl}} internally to evaluate code, and it currently does not support invalid input.
+% Julia console support was added in v0.16. It may be enabled by loading \pytex\ with |usefamily=juliacon|. The |juliaconsole| environment uses \href{https://github.com/mpastell/Weave.jl}{\textsf{Weave.jl}} internally to evaluate code.
%
%
% \subsection{Octave}
From 03eba323d05f2f48a20807343fa25f0605969ac8 Mon Sep 17 00:00:00 2001
From: Geoffrey Poore
Date: Sun, 28 Oct 2018 22:17:13 -0500
Subject: [PATCH 13/58] juliacon now works with pythontexcustomcode, and there
are now proper juliaconcode and Rconcode environments (#134)
---
pythontex/pythontex.dtx | 113 ++++++++++++++++++----------------------
pythontex/pythontex.sty | 99 ++++++++++++++---------------------
pythontex/pythontex2.py | 61 +++++++++++++---------
pythontex/pythontex3.py | 61 +++++++++++++---------
4 files changed, 159 insertions(+), 175 deletions(-)
diff --git a/pythontex/pythontex.dtx b/pythontex/pythontex.dtx
index b729d96..fe8e3ed 100644
--- a/pythontex/pythontex.dtx
+++ b/pythontex/pythontex.dtx
@@ -134,7 +134,7 @@
%
% \fi
%
-% \CheckSum{3321}
+% \CheckSum{3277}
%
% \CharacterTable
% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
@@ -161,6 +161,7 @@
% \item \texttt{pythontexcustomcode} and \texttt{\string\pythontexcustomc} now set \texttt{pytex.context} (\#65).
% \item Added support for R. The \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{\string\Rc}, \texttt{Rcode}, ...) executes code as a script. There is currently no utilities class or equivalent. The \texttt{Rcon} family (\texttt{Rconsole}) executes code to emulate an interactive R session (\#121).
% \item \texttt{fancyvrb} settings from \texttt{\string\setpythontexfv} and console environments now work with Julia and R consoles.
+% \item \texttt{pythontexcustomcode} now works with |juliacon|. There are now proper \texttt{juliaconcode} and \texttt{Rconcode} environments that execute code but typeset nothing, to parallel \texttt{pyconcode} (\#134).
% \end{itemize}
% \end{changelog}
%
@@ -1560,7 +1561,7 @@
%
% \subsubsection*{Console}
%
-% Julia console support was added in v0.16. It may be enabled by loading \pytex\ with |usefamily=juliacon|. The |juliaconsole| environment uses \href{https://github.com/mpastell/Weave.jl}{\textsf{Weave.jl}} internally to evaluate code.
+% Julia console support was added in v0.16. It may be enabled by loading \pytex\ with |usefamily=juliacon|. The |juliaconsole| environment uses \href{https://github.com/mpastell/Weave.jl}{\textsf{Weave.jl}} internally to evaluate code. There is also a |juliaconcode| environment that executes code but typesets nothing.
%
%
% \subsection{Octave}
@@ -1602,11 +1603,11 @@
%
% Support for R was added in v0.17.
%
-% Loading \pytex\ with |usefamily=R| enables the \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{\string\Rc}, \texttt{Rcode}, ...). These execute code with |Rscript|. The |methods| library is loaded automatically as part of the template code. Expressions passed to the |\R| command are converted into strings via |toString()|. There is currently no utilities class or related features. A null graphics device, |pdf(file=NULL)|, is created by default to avoid the automatic, unintentional creation of plot files with default names. Plots that are to be saved require explicit graphics commands.
+% Loading \pytex\ with |usefamily=R| enables the |R| family of commands and environments (|\R|, |\Rc|, |Rcode|, ...). These execute code with |Rscript|. The |methods| library is loaded automatically as part of the template code. Expressions passed to the |\R| command are converted into strings via |toString()|. There is currently no utilities class or related features. A null graphics device, |pdf(file=NULL)|, is created by default to avoid the automatic, unintentional creation of plot files with default names. Plots that are to be saved require explicit graphics commands.
%
% \subsubsection*{Console}
%
-% Loading \pytex\ with |usefamily=Rcon| enables the \texttt{Rconsole} environment, which executes code to emulate an interactive R session. Code is executed with |Rscript|. The |methods| library is loaded automatically as part of the template code. The option |echo=TRUE| is used to intersperse code with output, while |error=function(){}| is used to avoid halting on errors. A null graphics device, |pdf(file=NULL)|, is created by default to avoid the automatic, unintentional creation of plot files with default names. Plots that are to be saved require explicit graphics commands.
+% Loading \pytex\ with |usefamily=Rcon| enables the |Rconsole| environment, which executes code to emulate an interactive R session. There is also an |Rconcode| environment that executes code but typesets nothing. Code is executed with |Rscript|. The |methods| library is loaded automatically as part of the template code. The option |echo=TRUE| is used to intersperse code with output, while |error=function(){}| is used to avoid halting on errors. A null graphics device, |pdf(file=NULL)|, is created by default to avoid the automatic, unintentional creation of plot files with default names. Plots that are to be saved require explicit graphics commands.
%
%
% \subsection{Adding support for a new language}
@@ -3705,30 +3706,22 @@
}%
{\end{VerbatimOut}%
\xdef\pytx@stdfile{\pytx@type_\pytx@session_\pytx@group_\arabic{\pytx@counter}}%
- \ifdefstring{\pytx@type}{juliacon}{%
- \setcounter{FancyVerbLine}{\value{\pytx@linecount}}%
- \pytx@FVSet
- \ifdefstring{\pytx@fvopttmp}{}{}{\expandafter\fvset\expandafter{\pytx@fvopttmp}}%
- \pytx@ConfigPygments
- \InputIfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
- {}%
- {\par\textbf{??~\pytx@packagename~??}\par
- \PackageWarning{\pytx@packagename}{Non-existent console content}}%
- \setcounter{\pytx@linecount}{\value{FancyVerbLine}}%
- \let\pytx@EnvAutoprint\relax
- }{}%
- \ifdefstring{\pytx@type}{Rcon}{%
- \setcounter{FancyVerbLine}{\value{\pytx@linecount}}%
- \pytx@FVSet
- \ifdefstring{\pytx@fvopttmp}{}{}{\expandafter\fvset\expandafter{\pytx@fvopttmp}}%
- \pytx@ConfigPygments
- \InputIfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
- {}%
- {\par\textbf{??~\pytx@packagename~??}\par
- \PackageWarning{\pytx@packagename}{Non-existent console content}}%
- \setcounter{\pytx@linecount}{\value{FancyVerbLine}}%
+ \ifcsname pytx@nonpyconsole@\pytx@type\endcsname
+ \ifcsname pytx@code@as@console\endcsname
+ \setcounter{FancyVerbLine}{\value{\pytx@linecount}}%
+ \pytx@FVSet
+ \ifdefstring{\pytx@fvopttmp}{}{}{\expandafter\fvset\expandafter{\pytx@fvopttmp}}%
+ \pytx@ConfigPygments
+ \InputIfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
+ {\DepyFile{p:\pytx@outputdir/\pytx@stdfile.stdout}}%
+ {\par\textbf{??~\pytx@packagename~??}\par
+ \PackageWarning{\pytx@packagename}{Non-existent console content}}%
+ \setcounter{\pytx@linecount}{\value{FancyVerbLine}}%
+ \else
+ \fi
\let\pytx@EnvAutoprint\relax
- }{}%
+ \else
+ \fi
\setcounter{FancyVerbLine}{\value{pytx@FancyVerbLineTemp}}%
\stepcounter{\pytx@counter}%
\pytx@EnvAutoprint
@@ -4049,6 +4042,34 @@
\@onlypreamble\makepythontexfamily
% \end{macrocode}
% \end{macro}
+%
+%
+% \begin{macro}{\makepythontexfamily@con}
+% This macro creates |console| and |code| environments for non-Python consoles. \pytex\ was not designed with commands and environments for non-Python consoles. Non-Python consoles are currently created via specially customized |code| environments. Note that simply creating these |console| and |code| environments is typically not enough to create non-Python consoles; |pythontex2.py| and |pythontex3.py| usually also require customization. This macro's definition should not be treated as stable; it will change in the future. The ultimate long-term goal is to eliminate it entirely, by redesigning the code execution core of \pytex\ to accomodate non-Python consoles more easily.
+% \begin{macrocode}
+\newcommand{\makepythontexfamily@con}[2][text]{%
+ \pgfkeys{/PYTX/family, name=#2con, default, pyglexer=#1, console=true}%
+ \expandafter\xdef\csname pytx@macroformatter@#2con\endcsname{\pytx@tmp@pprinter}%
+ \expandafter\gdef\csname pytx@fvsettings@#2con\endcsname{}%
+ \expandafter\xdef\csname pytx@pyglexer@#2con\endcsname{\pytx@tmp@pyglexer}%
+ \expandafter\xdef\csname pytx@pygopt@#2con\endcsname{\pytx@tmp@pygopt}%
+ \expandafter\xdef\csname pytx@console@#2con\endcsname{\pytx@tmp@console}%
+ \AtEndDocument{\immediate\write\pytx@codefile{pygfamily=#2con|%
+ \csname pytx@pyglexer@#2con\endcsname|%
+ \csname pytx@pygopt@#2con\endcsname}%
+ }%
+ \pytx@MakeCodeFV{#2con}%
+ \expandafter\global\expandafter\let\csname pytx@nonpyconsole@#2con\endcsname\relax
+ \newenvironment{#2console}%
+ {\VerbatimEnvironment
+ \def\pytx@type{#2con}%
+ \let\pytx@code@as@console\relax
+ \begin{#2concode}}%
+ {\end{#2concode}}%
+}
+% \end{macrocode}
+% \end{macro}
+
%
% \begin{macro}{\setpythontexpyglexer}
% We need to be able to reset the lexer associated with a family after the family has already been created.
@@ -4131,24 +4152,7 @@
\ifstrequal{#1}{ruby}{\makepythontexfamily[pyglexer=ruby]{ruby}}{}%
\ifstrequal{#1}{rb}{\makepythontexfamily[pyglexer=ruby]{rb}}{}%
\ifstrequal{#1}{julia}{\makepythontexfamily[pyglexer=julia]{julia}}{}%
- \ifstrequal{#1}{juliacon}{%
- \pgfkeys{/PYTX/family, name=juliacon, default, pyglexer=jlcon}%
- \expandafter\xdef\csname pytx@macroformatter@juliacon\endcsname{\pytx@tmp@pprinter}%
- \expandafter\gdef\csname pytx@fvsettings@juliacon\endcsname{}%
- \expandafter\xdef\csname pytx@pyglexer@juliacon\endcsname{\pytx@tmp@pyglexer}%
- \expandafter\xdef\csname pytx@pygopt@juliacon\endcsname{\pytx@tmp@pygopt}%
- \expandafter\xdef\csname pytx@console@juliacon\endcsname{\pytx@tmp@console}%
- \AtEndDocument{\immediate\write\pytx@codefile{pygfamily=juliacon|%
- \csname pytx@pyglexer@juliacon\endcsname|%
- \csname pytx@pygopt@juliacon\endcsname}%
- }%
- \pytx@MakeCodeFV{juliacon}%
- \newenvironment{juliaconsole}%
- {\VerbatimEnvironment
- \def\pytx@type{juliacon}%
- \begin{juliaconcode}}%
- {\end{juliaconcode}}
- }{}%
+ \ifstrequal{#1}{juliacon}{\makepythontexfamily@con[jlcon]{julia}}{}%
\ifstrequal{#1}{jl}{\makepythontexfamily[pyglexer=julia]{jl}}{}%
\ifstrequal{#1}{matlab}{\makepythontexfamily[pyglexer=matlab]{matlab}}{}%
\ifstrequal{#1}{octave}{\makepythontexfamily[pyglexer=octave]{octave}}{}%
@@ -4157,24 +4161,7 @@
\ifstrequal{#1}{rust}{\makepythontexfamily[pyglexer=rust]{rust}}{}%
\ifstrequal{#1}{rs}{\makepythontexfamily[pyglexer=rust]{rs}}{}%
\ifstrequal{#1}{R}{\makepythontexfamily[pyglexer=r]{R}}{}%
- \ifstrequal{#1}{Rcon}{%
- \pgfkeys{/PYTX/family, name=Rcon, default, pyglexer=rconsole}%
- \expandafter\xdef\csname pytx@macroformatter@Rcon\endcsname{\pytx@tmp@pprinter}%
- \expandafter\gdef\csname pytx@fvsettings@Rcon\endcsname{}%
- \expandafter\xdef\csname pytx@pyglexer@Rcon\endcsname{\pytx@tmp@pyglexer}%
- \expandafter\xdef\csname pytx@pygopt@Rcon\endcsname{\pytx@tmp@pygopt}%
- \expandafter\xdef\csname pytx@console@Rcon\endcsname{\pytx@tmp@console}%
- \AtEndDocument{\immediate\write\pytx@codefile{pygfamily=Rcon|%
- \csname pytx@pyglexer@Rcon\endcsname|%
- \csname pytx@pygopt@Rcon\endcsname}%
- }%
- \pytx@MakeCodeFV{Rcon}%
- \newenvironment{Rconsole}%
- {\VerbatimEnvironment
- \def\pytx@type{Rcon}%
- \begin{Rconcode}}%
- {\end{Rconcode}}
- }{}%
+ \ifstrequal{#1}{Rcon}{\makepythontexfamily@con[rconsole]{R}}{}%
}
\expandafter\docsvlist\expandafter{\pytx@families}
% \end{macrocode}
diff --git a/pythontex/pythontex.sty b/pythontex/pythontex.sty
index 2a97e2c..69f7f2c 100644
--- a/pythontex/pythontex.sty
+++ b/pythontex/pythontex.sty
@@ -20,7 +20,7 @@
%%
\NeedsTeXFormat{LaTeX2e}[1999/12/01]
\ProvidesPackage{pythontex}
- [2018/10/25 v0.17dev execute and typeset Python code and other languages]
+ [2018/10/28 v0.17dev execute and typeset Python code and other languages]
\newcommand{\pytx@packagename}{PythonTeX}
\newcommand{\pytx@packageversion}{0.17dev}
@@ -1037,30 +1037,22 @@
}%
{\end{VerbatimOut}%
\xdef\pytx@stdfile{\pytx@type_\pytx@session_\pytx@group_\arabic{\pytx@counter}}%
- \ifdefstring{\pytx@type}{juliacon}{%
- \setcounter{FancyVerbLine}{\value{\pytx@linecount}}%
- \pytx@FVSet
- \ifdefstring{\pytx@fvopttmp}{}{}{\expandafter\fvset\expandafter{\pytx@fvopttmp}}%
- \pytx@ConfigPygments
- \InputIfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
- {}%
- {\par\textbf{??~\pytx@packagename~??}\par
- \PackageWarning{\pytx@packagename}{Non-existent console content}}%
- \setcounter{\pytx@linecount}{\value{FancyVerbLine}}%
- \let\pytx@EnvAutoprint\relax
- }{}%
- \ifdefstring{\pytx@type}{Rcon}{%
- \setcounter{FancyVerbLine}{\value{\pytx@linecount}}%
- \pytx@FVSet
- \ifdefstring{\pytx@fvopttmp}{}{}{\expandafter\fvset\expandafter{\pytx@fvopttmp}}%
- \pytx@ConfigPygments
- \InputIfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
- {}%
- {\par\textbf{??~\pytx@packagename~??}\par
- \PackageWarning{\pytx@packagename}{Non-existent console content}}%
- \setcounter{\pytx@linecount}{\value{FancyVerbLine}}%
+ \ifcsname pytx@nonpyconsole@\pytx@type\endcsname
+ \ifcsname pytx@code@as@console\endcsname
+ \setcounter{FancyVerbLine}{\value{\pytx@linecount}}%
+ \pytx@FVSet
+ \ifdefstring{\pytx@fvopttmp}{}{}{\expandafter\fvset\expandafter{\pytx@fvopttmp}}%
+ \pytx@ConfigPygments
+ \InputIfFileExists{\pytx@outputdir/\pytx@stdfile.stdout}%
+ {\DepyFile{p:\pytx@outputdir/\pytx@stdfile.stdout}}%
+ {\par\textbf{??~\pytx@packagename~??}\par
+ \PackageWarning{\pytx@packagename}{Non-existent console content}}%
+ \setcounter{\pytx@linecount}{\value{FancyVerbLine}}%
+ \else
+ \fi
\let\pytx@EnvAutoprint\relax
- }{}%
+ \else
+ \fi
\setcounter{FancyVerbLine}{\value{pytx@FancyVerbLineTemp}}%
\stepcounter{\pytx@counter}%
\pytx@EnvAutoprint
@@ -1293,6 +1285,27 @@
\newcounter{pytx@#2@default@default}%
}
\@onlypreamble\makepythontexfamily
+\newcommand{\makepythontexfamily@con}[2][text]{%
+ \pgfkeys{/PYTX/family, name=#2con, default, pyglexer=#1, console=true}%
+ \expandafter\xdef\csname pytx@macroformatter@#2con\endcsname{\pytx@tmp@pprinter}%
+ \expandafter\gdef\csname pytx@fvsettings@#2con\endcsname{}%
+ \expandafter\xdef\csname pytx@pyglexer@#2con\endcsname{\pytx@tmp@pyglexer}%
+ \expandafter\xdef\csname pytx@pygopt@#2con\endcsname{\pytx@tmp@pygopt}%
+ \expandafter\xdef\csname pytx@console@#2con\endcsname{\pytx@tmp@console}%
+ \AtEndDocument{\immediate\write\pytx@codefile{pygfamily=#2con|%
+ \csname pytx@pyglexer@#2con\endcsname|%
+ \csname pytx@pygopt@#2con\endcsname}%
+ }%
+ \pytx@MakeCodeFV{#2con}%
+ \expandafter\global\expandafter\let\csname pytx@nonpyconsole@#2con\endcsname\relax
+ \newenvironment{#2console}%
+ {\VerbatimEnvironment
+ \def\pytx@type{#2con}%
+ \let\pytx@code@as@console\relax
+ \begin{#2concode}}%
+ {\end{#2concode}}%
+}
+
\newcommand{\setpythontexpyglexer}[2][]{%
\Depythontex{cmd:setpythontexpyglexer:om:n}%
\ifstrempty{#1}{\def\pytx@pyglexer{#2}}{%
@@ -1345,24 +1358,7 @@
\ifstrequal{#1}{ruby}{\makepythontexfamily[pyglexer=ruby]{ruby}}{}%
\ifstrequal{#1}{rb}{\makepythontexfamily[pyglexer=ruby]{rb}}{}%
\ifstrequal{#1}{julia}{\makepythontexfamily[pyglexer=julia]{julia}}{}%
- \ifstrequal{#1}{juliacon}{%
- \pgfkeys{/PYTX/family, name=juliacon, default, pyglexer=jlcon}%
- \expandafter\xdef\csname pytx@macroformatter@juliacon\endcsname{\pytx@tmp@pprinter}%
- \expandafter\gdef\csname pytx@fvsettings@juliacon\endcsname{}%
- \expandafter\xdef\csname pytx@pyglexer@juliacon\endcsname{\pytx@tmp@pyglexer}%
- \expandafter\xdef\csname pytx@pygopt@juliacon\endcsname{\pytx@tmp@pygopt}%
- \expandafter\xdef\csname pytx@console@juliacon\endcsname{\pytx@tmp@console}%
- \AtEndDocument{\immediate\write\pytx@codefile{pygfamily=juliacon|%
- \csname pytx@pyglexer@juliacon\endcsname|%
- \csname pytx@pygopt@juliacon\endcsname}%
- }%
- \pytx@MakeCodeFV{juliacon}%
- \newenvironment{juliaconsole}%
- {\VerbatimEnvironment
- \def\pytx@type{juliacon}%
- \begin{juliaconcode}}%
- {\end{juliaconcode}}
- }{}%
+ \ifstrequal{#1}{juliacon}{\makepythontexfamily@con[jlcon]{julia}}{}%
\ifstrequal{#1}{jl}{\makepythontexfamily[pyglexer=julia]{jl}}{}%
\ifstrequal{#1}{matlab}{\makepythontexfamily[pyglexer=matlab]{matlab}}{}%
\ifstrequal{#1}{octave}{\makepythontexfamily[pyglexer=octave]{octave}}{}%
@@ -1371,24 +1367,7 @@
\ifstrequal{#1}{rust}{\makepythontexfamily[pyglexer=rust]{rust}}{}%
\ifstrequal{#1}{rs}{\makepythontexfamily[pyglexer=rust]{rs}}{}%
\ifstrequal{#1}{R}{\makepythontexfamily[pyglexer=r]{R}}{}%
- \ifstrequal{#1}{Rcon}{%
- \pgfkeys{/PYTX/family, name=Rcon, default, pyglexer=rconsole}%
- \expandafter\xdef\csname pytx@macroformatter@Rcon\endcsname{\pytx@tmp@pprinter}%
- \expandafter\gdef\csname pytx@fvsettings@Rcon\endcsname{}%
- \expandafter\xdef\csname pytx@pyglexer@Rcon\endcsname{\pytx@tmp@pyglexer}%
- \expandafter\xdef\csname pytx@pygopt@Rcon\endcsname{\pytx@tmp@pygopt}%
- \expandafter\xdef\csname pytx@console@Rcon\endcsname{\pytx@tmp@console}%
- \AtEndDocument{\immediate\write\pytx@codefile{pygfamily=Rcon|%
- \csname pytx@pyglexer@Rcon\endcsname|%
- \csname pytx@pygopt@Rcon\endcsname}%
- }%
- \pytx@MakeCodeFV{Rcon}%
- \newenvironment{Rconsole}%
- {\VerbatimEnvironment
- \def\pytx@type{Rcon}%
- \begin{Rconcode}}%
- {\end{Rconcode}}
- }{}%
+ \ifstrequal{#1}{Rcon}{\makepythontexfamily@con[rconsole]{R}}{}%
}
\expandafter\docsvlist\expandafter{\pytx@families}
\newbool{pytx@listingenv}
diff --git a/pythontex/pythontex2.py b/pythontex/pythontex2.py
index f5044a4..0e989fd 100644
--- a/pythontex/pythontex2.py
+++ b/pythontex/pythontex2.py
@@ -62,6 +62,7 @@
from pythontex_engines import *
import textwrap
import platform
+import itertools
if sys.version_info[0] == 2:
try:
@@ -1275,7 +1276,11 @@ def do_multiprocessing(data, temp_data, old_data, engine_dict):
for key in code_dict:
family = key.split('#')[0]
# Uncomment the following for debugging, and comment out what follows
- '''run_code(encoding, outputdir, workingdir, code_dict[key],
+ '''run_code(encoding, outputdir,
+ workingdir,
+ cc_dict_begin[family],
+ code_dict[key],
+ cc_dict_end[family],
engine_dict[family].language,
engine_dict[family].commands,
engine_dict[family].created,
@@ -1287,9 +1292,12 @@ def do_multiprocessing(data, temp_data, old_data, engine_dict):
engine_dict[family].linenumbers,
engine_dict[family].lookbehind,
keeptemps, hashdependencies,
- pygments_settings)'''
+ pygments_settings]))'''
tasks.append(pool.apply_async(run_code, [encoding, outputdir,
- workingdir, code_dict[key],
+ workingdir,
+ cc_dict_begin[family],
+ code_dict[key],
+ cc_dict_end[family],
engine_dict[family].language,
engine_dict[family].commands,
engine_dict[family].created,
@@ -1474,7 +1482,8 @@ def do_multiprocessing(data, temp_data, old_data, engine_dict):
-def run_code(encoding, outputdir, workingdir, code_list, language, commands,
+def run_code(encoding, outputdir, workingdir,
+ cc_begin_list, code_list, cc_end_list, language, commands,
command_created, extension, makestderr, stderrfilename,
code_index, errorsig, warningsig, linesig, stderrlookbehind,
keeptemps, hashdependencies, pygments_settings):
@@ -1583,11 +1592,11 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
if family == 'juliacon':
with open(out_file_name.rsplit('.', 1)[0] + '.tex', 'r', encoding=encoding) as f:
tex_data_lines = f.readlines()
- inst = 0
+ code_iter = itertools.chain(cc_begin_list, code_list, cc_end_list)
for n, line in enumerate(tex_data_lines):
if line.rstrip() == '\\begin{juliaterm}':
- tex_data_lines[n] = '=>PYTHONTEX:STDOUT#{0}#code#\n'.format(inst)
- inst += 1
+ c = next(code_iter)
+ tex_data_lines[n] = '=>PYTHONTEX:STDOUT#{0}#code#\n'.format(c.instance)
if n != 0:
tex_data_lines[n-1] = ''
if line.rstrip() == '\\end{juliaterm}':
@@ -1598,24 +1607,24 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
elif family == 'Rcon':
with open(out_file_name, 'r', encoding=encoding) as f:
stdout_lines = f.readlines()
- for n, line in enumerate(stdout_lines):
- if line.startswith('> =>PYTHONTEX:'):
- stdout_lines[n] = line[2:]
- elif '> write("=>PYTHONTEX:' in line:
- if line.startswith('> write("=>PYTHONTEX:'):
- stdout_lines[n] = ''
- else:
- # cat() and similar functions can result in the
- # prompt not being at the start of a new line. In
- # that case, preserve the prompt to accurately
- # emulate the console. If there is a following
- # console environment, this effectively amounts
- # to adding an extra empty line (pressing ENTER)
- # between the two.
- stdout_lines[n] = line.split('write("=>PYTHONTEX:', 1)[0]
- while stdout_lines and (stdout_lines[-1].startswith('>') and not stdout_lines[-1][1:].strip(' \n')):
- stdout_lines.pop()
- stdout_lines.append('=>PYTHONTEX:DEPENDENCIES#\n=>PYTHONTEX:CREATED#\n')
+ for n, line in enumerate(stdout_lines):
+ if line.startswith('> =>PYTHONTEX:'):
+ stdout_lines[n] = line[2:]
+ elif '> write("=>PYTHONTEX:' in line:
+ if line.startswith('> write("=>PYTHONTEX:'):
+ stdout_lines[n] = ''
+ else:
+ # cat() and similar functions can result in the
+ # prompt not being at the start of a new line. In
+ # that case, preserve the prompt to accurately
+ # emulate the console. If there is a following
+ # console environment, this effectively amounts
+ # to adding an extra empty line (pressing ENTER)
+ # between the two.
+ stdout_lines[n] = line.split('write("=>PYTHONTEX:', 1)[0]
+ while stdout_lines and (stdout_lines[-1].startswith('>') and not stdout_lines[-1][1:].strip(' \n')):
+ stdout_lines.pop()
+ stdout_lines.append('=>PYTHONTEX:DEPENDENCIES#\n=>PYTHONTEX:CREATED#\n')
with open(out_file_name, 'w', encoding=encoding) as f:
f.write(''.join(stdout_lines))
@@ -1690,7 +1699,7 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
for block in out.split('=>PYTHONTEX:STDOUT#')[1:]:
if block:
delims, content = block.split('#\n', 1)
- if not content.endswith('\n'):
+ if content and not content.endswith('\n'):
# Content might not end with a newline. For example,
# Rcon with something like cat() as the last function.
content += '\n'
diff --git a/pythontex/pythontex3.py b/pythontex/pythontex3.py
index 63ab909..d19c8f1 100644
--- a/pythontex/pythontex3.py
+++ b/pythontex/pythontex3.py
@@ -62,6 +62,7 @@
from pythontex_engines import *
import textwrap
import platform
+import itertools
if sys.version_info[0] == 2:
try:
@@ -1275,7 +1276,11 @@ def do_multiprocessing(data, temp_data, old_data, engine_dict):
for key in code_dict:
family = key.split('#')[0]
# Uncomment the following for debugging, and comment out what follows
- '''run_code(encoding, outputdir, workingdir, code_dict[key],
+ '''run_code(encoding, outputdir,
+ workingdir,
+ cc_dict_begin[family],
+ code_dict[key],
+ cc_dict_end[family],
engine_dict[family].language,
engine_dict[family].commands,
engine_dict[family].created,
@@ -1287,9 +1292,12 @@ def do_multiprocessing(data, temp_data, old_data, engine_dict):
engine_dict[family].linenumbers,
engine_dict[family].lookbehind,
keeptemps, hashdependencies,
- pygments_settings)'''
+ pygments_settings]))'''
tasks.append(pool.apply_async(run_code, [encoding, outputdir,
- workingdir, code_dict[key],
+ workingdir,
+ cc_dict_begin[family],
+ code_dict[key],
+ cc_dict_end[family],
engine_dict[family].language,
engine_dict[family].commands,
engine_dict[family].created,
@@ -1474,7 +1482,8 @@ def do_multiprocessing(data, temp_data, old_data, engine_dict):
-def run_code(encoding, outputdir, workingdir, code_list, language, commands,
+def run_code(encoding, outputdir, workingdir,
+ cc_begin_list, code_list, cc_end_list, language, commands,
command_created, extension, makestderr, stderrfilename,
code_index, errorsig, warningsig, linesig, stderrlookbehind,
keeptemps, hashdependencies, pygments_settings):
@@ -1583,11 +1592,11 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
if family == 'juliacon':
with open(out_file_name.rsplit('.', 1)[0] + '.tex', 'r', encoding=encoding) as f:
tex_data_lines = f.readlines()
- inst = 0
+ code_iter = itertools.chain(cc_begin_list, code_list, cc_end_list)
for n, line in enumerate(tex_data_lines):
if line.rstrip() == '\\begin{juliaterm}':
- tex_data_lines[n] = '=>PYTHONTEX:STDOUT#{0}#code#\n'.format(inst)
- inst += 1
+ c = next(code_iter)
+ tex_data_lines[n] = '=>PYTHONTEX:STDOUT#{0}#code#\n'.format(c.instance)
if n != 0:
tex_data_lines[n-1] = ''
if line.rstrip() == '\\end{juliaterm}':
@@ -1598,24 +1607,24 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
elif family == 'Rcon':
with open(out_file_name, 'r', encoding=encoding) as f:
stdout_lines = f.readlines()
- for n, line in enumerate(stdout_lines):
- if line.startswith('> =>PYTHONTEX:'):
- stdout_lines[n] = line[2:]
- elif '> write("=>PYTHONTEX:' in line:
- if line.startswith('> write("=>PYTHONTEX:'):
- stdout_lines[n] = ''
- else:
- # cat() and similar functions can result in the
- # prompt not being at the start of a new line. In
- # that case, preserve the prompt to accurately
- # emulate the console. If there is a following
- # console environment, this effectively amounts
- # to adding an extra empty line (pressing ENTER)
- # between the two.
- stdout_lines[n] = line.split('write("=>PYTHONTEX:', 1)[0]
- while stdout_lines and (stdout_lines[-1].startswith('>') and not stdout_lines[-1][1:].strip(' \n')):
- stdout_lines.pop()
- stdout_lines.append('=>PYTHONTEX:DEPENDENCIES#\n=>PYTHONTEX:CREATED#\n')
+ for n, line in enumerate(stdout_lines):
+ if line.startswith('> =>PYTHONTEX:'):
+ stdout_lines[n] = line[2:]
+ elif '> write("=>PYTHONTEX:' in line:
+ if line.startswith('> write("=>PYTHONTEX:'):
+ stdout_lines[n] = ''
+ else:
+ # cat() and similar functions can result in the
+ # prompt not being at the start of a new line. In
+ # that case, preserve the prompt to accurately
+ # emulate the console. If there is a following
+ # console environment, this effectively amounts
+ # to adding an extra empty line (pressing ENTER)
+ # between the two.
+ stdout_lines[n] = line.split('write("=>PYTHONTEX:', 1)[0]
+ while stdout_lines and (stdout_lines[-1].startswith('>') and not stdout_lines[-1][1:].strip(' \n')):
+ stdout_lines.pop()
+ stdout_lines.append('=>PYTHONTEX:DEPENDENCIES#\n=>PYTHONTEX:CREATED#\n')
with open(out_file_name, 'w', encoding=encoding) as f:
f.write(''.join(stdout_lines))
@@ -1690,7 +1699,7 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands,
for block in out.split('=>PYTHONTEX:STDOUT#')[1:]:
if block:
delims, content = block.split('#\n', 1)
- if not content.endswith('\n'):
+ if content and not content.endswith('\n'):
# Content might not end with a newline. For example,
# Rcon with something like cat() as the last function.
content += '\n'
From 274ce68df40b98ad569968b2e287cb5b3ec2511e Mon Sep 17 00:00:00 2001
From: Geoffrey Poore
Date: Tue, 30 Oct 2018 21:58:20 -0500
Subject: [PATCH 14/58] added basic Perl support
---
pythontex/pythontex.dtx | 14 ++++-
pythontex/pythontex.sty | 4 +-
pythontex/pythontex_engines.py | 35 ++++++++++-
test/perl/perl_test.tex | 103 +++++++++++++++++++++++++++++++++
4 files changed, 152 insertions(+), 4 deletions(-)
create mode 100644 test/perl/perl_test.tex
diff --git a/pythontex/pythontex.dtx b/pythontex/pythontex.dtx
index fe8e3ed..97ec256 100644
--- a/pythontex/pythontex.dtx
+++ b/pythontex/pythontex.dtx
@@ -26,7 +26,7 @@
%\NeedsTeXFormat{LaTeX2e}[1999/12/01]
%\ProvidesPackage{pythontex}
%<*package>
- [2018/10/28 v0.17dev execute and typeset Python code and other languages]
+ [2018/10/30 v0.17dev execute and typeset Python code and other languages]
%
%
%<*driver>
@@ -134,7 +134,7 @@
%
% \fi
%
-% \CheckSum{3277}
+% \CheckSum{3281}
%
% \CharacterTable
% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
@@ -162,6 +162,7 @@
% \item Added support for R. The \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{\string\Rc}, \texttt{Rcode}, ...) executes code as a script. There is currently no utilities class or equivalent. The \texttt{Rcon} family (\texttt{Rconsole}) executes code to emulate an interactive R session (\#121).
% \item \texttt{fancyvrb} settings from \texttt{\string\setpythontexfv} and console environments now work with Julia and R consoles.
% \item \texttt{pythontexcustomcode} now works with |juliacon|. There are now proper \texttt{juliaconcode} and \texttt{Rconcode} environments that execute code but typeset nothing, to parallel \texttt{pyconcode} (\#134).
+% \item Added support for Perl with the \texttt{perl} and \texttt{pl} families of commands and environments. There is currently no utilities class or equivalent.
% \end{itemize}
% \end{changelog}
%
@@ -1610,6 +1611,13 @@
% Loading \pytex\ with |usefamily=Rcon| enables the |Rconsole| environment, which executes code to emulate an interactive R session. There is also an |Rconcode| environment that executes code but typesets nothing. Code is executed with |Rscript|. The |methods| library is loaded automatically as part of the template code. The option |echo=TRUE| is used to intersperse code with output, while |error=function(){}| is used to avoid halting on errors. A null graphics device, |pdf(file=NULL)|, is created by default to avoid the automatic, unintentional creation of plot files with default names. Plots that are to be saved require explicit graphics commands.
%
%
+% \subsection{Perl}
+%
+% Support for Perl was added in v0.17.
+%
+% Loading \pytex\ with |usefamily=perl| enables the |perl| family of commands and environments. Alternatively, |usefamily=pl| may be used to enable the |pl| family. There is currently no utilities class or related features.
+%
+%
% \subsection{Adding support for a new language}
%
% Adding support for an additional language involves creating two templates, creating a new instance of a class, and using a \pytex\ macro. In some cases, additional changes may be necessary for full support. The information below does not deal with creating |console| families; additional support for user-defined |console| families will be added in the future.
@@ -4162,6 +4170,8 @@
\ifstrequal{#1}{rs}{\makepythontexfamily[pyglexer=rust]{rs}}{}%
\ifstrequal{#1}{R}{\makepythontexfamily[pyglexer=r]{R}}{}%
\ifstrequal{#1}{Rcon}{\makepythontexfamily@con[rconsole]{R}}{}%
+ \ifstrequal{#1}{perl}{\makepythontexfamily[pyglexer=perl]{perl}}{}%
+ \ifstrequal{#1}{pl}{\makepythontexfamily[pyglexer=perl]{pl}}{}%
}
\expandafter\docsvlist\expandafter{\pytx@families}
% \end{macrocode}
diff --git a/pythontex/pythontex.sty b/pythontex/pythontex.sty
index 69f7f2c..5d98ade 100644
--- a/pythontex/pythontex.sty
+++ b/pythontex/pythontex.sty
@@ -20,7 +20,7 @@
%%
\NeedsTeXFormat{LaTeX2e}[1999/12/01]
\ProvidesPackage{pythontex}
- [2018/10/28 v0.17dev execute and typeset Python code and other languages]
+ [2018/10/30 v0.17dev execute and typeset Python code and other languages]
\newcommand{\pytx@packagename}{PythonTeX}
\newcommand{\pytx@packageversion}{0.17dev}
@@ -1368,6 +1368,8 @@
\ifstrequal{#1}{rs}{\makepythontexfamily[pyglexer=rust]{rs}}{}%
\ifstrequal{#1}{R}{\makepythontexfamily[pyglexer=r]{R}}{}%
\ifstrequal{#1}{Rcon}{\makepythontexfamily@con[rconsole]{R}}{}%
+ \ifstrequal{#1}{perl}{\makepythontexfamily[pyglexer=perl]{perl}}{}%
+ \ifstrequal{#1}{pl}{\makepythontexfamily[pyglexer=perl]{pl}}{}%
}
\expandafter\docsvlist\expandafter{\pytx@families}
\newbool{pytx@listingenv}
diff --git a/pythontex/pythontex_engines.py b/pythontex/pythontex_engines.py
index 16f391d..d08022c 100644
--- a/pythontex/pythontex_engines.py
+++ b/pythontex/pythontex_engines.py
@@ -34,7 +34,7 @@
interpreter_dict = {k:k for k in ('python', 'ruby', 'julia', 'octave', 'bash',
- 'sage', 'rustc', 'Rscript')}
+ 'sage', 'rustc', 'Rscript', 'perl')}
# The {file} field needs to be replaced by itself, since the actual
# substitution of the real file can only be done at runtime, whereas the
# substitution for the interpreter should be done when the engine is
@@ -1589,3 +1589,36 @@ def cleanup
rcon_template, rcon_wrapper, '', '',
['error', 'Error'], ['warning', 'Warning'],
'')
+
+
+perl_template = '''
+ use v5.14;
+ use utf8;
+ use strict;
+ use autodie;
+ use warnings;
+ use warnings qw(FATAL utf8);
+ use feature qw(unicode_strings);
+ use open qw(:encoding(UTF-8) :std);
+ chdir("{workingdir}");
+ {body}
+ print STDOUT "{dependencies_delim}\\n";
+ print STDOUT "{created_delim}\\n";
+ '''
+
+perl_wrapper = '''
+ print STDOUT "{stdoutdelim}\\n";
+ print STDERR "{stderrdelim}\\n";
+ {code}
+ '''
+
+perl_sub = '''
+ print STDOUT "{field_delim}\\n";
+ print STDOUT "" . ({field});
+ '''
+
+CodeEngine('perl', 'perl', '.pl',
+ '{perl} "{file}.pl"',
+ perl_template, perl_wrapper, 'print STDOUT "" . ({code});', perl_sub,
+ ['error', 'Error'], ['warning', 'Warning'],
+ 'line {number}')
diff --git a/test/perl/perl_test.tex b/test/perl/perl_test.tex
new file mode 100644
index 0000000..766f8fc
--- /dev/null
+++ b/test/perl/perl_test.tex
@@ -0,0 +1,103 @@
+\documentclass[11pt]{article}
+
+% Engine-specific settings
+% pdftex:
+\ifcsname pdfmatch\endcsname
+ \usepackage[T1]{fontenc}
+ \usepackage[utf8]{inputenc}
+\fi
+% xetex:
+\ifcsname XeTeXinterchartoks\endcsname
+ \usepackage{fontspec}
+ \defaultfontfeatures{Ligatures=TeX}
+\fi
+% luatex:
+\ifcsname directlua\endcsname
+ \usepackage{fontspec}
+\fi
+% End engine-specific settings
+
+\usepackage{lmodern}
+\usepackage{amssymb,amsmath}
+\usepackage{graphicx}
+\usepackage{fullpage}
+\usepackage[keeptemps=all, makestderr, usefamily={perl}]{pythontex}
+
+
+\begin{document}
+
+
+
+\section*{Perl}
+
+\subsection*{Commands}
+
+\perl{2**8}
+
+\perlc{print 2**16;}
+
+\perlb{print 2**32;}
+
+\printpythontex
+
+\perlv{print 2**32;}
+
+\perls{\LaTeX\ and then \textcolor{blue}{!{"Perl"}} and back to \LaTeX.}
+
+
+\subsection*{Environments}
+
+Code:
+\begin{perlcode}
+print "A string." . " ";
+print 2**8;
+\end{perlcode}
+
+Block:
+\begin{perlblock}
+print "A string." . " ";
+print 2**8;
+\end{perlblock}
+
+\printpythontex
+
+Verbatim:
+\begin{perlverbatim}
+print "A string." . " ";
+print 2**8;
+\end{perlverbatim}
+
+Sub:
+\begin{perlsub}
+\LaTeX\ and then \textcolor{blue}{!{"Perl"}} and back to \LaTeX.
+\end{perlsub}
+
+
+\section*{Perl stderr}
+
+
+\begin{perlblock}[err1][numbers=left]
+# Comment
+s = "Perl a
+\end{perlblock}
+
+\stderrpythontex[][breaklines, breakafter=\\/]
+
+\begin{perlblock}[err2][numbers=left]
+1+;
+\end{perlblock}
+
+\stderrpythontex[][breaklines, breakafter=\\/]
+
+\begin{perlblock}[err3][numbers=left]
+# Comment
+# Another comment
+"a" "b";
+\end{perlblock}
+
+\stderrpythontex[][breaklines, breakafter=\\/]
+
+
+
+\end{document}
+
From 9bd93fae48dd0da4c3cb0dd17566984a7447adcf Mon Sep 17 00:00:00 2001
From: Geoffrey Poore
Date: Sat, 10 Nov 2018 13:00:28 -0600
Subject: [PATCH 15/58] added basic Perl 6 support (closes #104); fixed Perl 5
bugs in tests and pl family; under Windows, capitalization of script paths in
stderr is now preserved
---
pythontex/pythontex.dtx | 17 ++++-
pythontex/pythontex.sty | 4 +-
pythontex/pythontex2.py | 60 +++++++++++++---
pythontex/pythontex3.py | 60 +++++++++++++---
pythontex/pythontex_engines.py | 35 ++++++++-
test/perl/perl_test.tex | 2 +-
test/perl6/perl6_test.tex | 127 +++++++++++++++++++++++++++++++++
7 files changed, 280 insertions(+), 25 deletions(-)
create mode 100644 test/perl6/perl6_test.tex
diff --git a/pythontex/pythontex.dtx b/pythontex/pythontex.dtx
index 97ec256..d20371f 100644
--- a/pythontex/pythontex.dtx
+++ b/pythontex/pythontex.dtx
@@ -26,7 +26,7 @@
%\NeedsTeXFormat{LaTeX2e}[1999/12/01]
%\ProvidesPackage{pythontex}
%<*package>
- [2018/10/30 v0.17dev execute and typeset Python code and other languages]
+ [2018/11/10 v0.17dev execute and typeset Python code and other languages]
%
%
%<*driver>
@@ -134,7 +134,7 @@
%
% \fi
%
-% \CheckSum{3281}
+% \CheckSum{3285}
%
% \CharacterTable
% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
@@ -154,7 +154,7 @@
%
%
%
-% \begin{changelog}{v0.17}{2018/10/??}
+% \begin{changelog}{v0.17}{2018/11/??}
% \begin{itemize}
% \item Updated Julia support for Julia versions 0.6 (\#107), and 0.7 and 1.0 (\#126, \#130).
% \item There are now meaningful error messages for Julia console when Weave.jl is not installed or raises errors (\#131).
@@ -163,6 +163,8 @@
% \item \texttt{fancyvrb} settings from \texttt{\string\setpythontexfv} and console environments now work with Julia and R consoles.
% \item \texttt{pythontexcustomcode} now works with |juliacon|. There are now proper \texttt{juliaconcode} and \texttt{Rconcode} environments that execute code but typeset nothing, to parallel \texttt{pyconcode} (\#134).
% \item Added support for Perl with the \texttt{perl} and \texttt{pl} families of commands and environments. There is currently no utilities class or equivalent.
+% \item Added support for Perl 6 with the \texttt{perlsix} and \texttt{psix} families of commands and environments (\#104). There is currently no utilities class or equivalent.
+% \item Under Windows, capitalization of script paths in \texttt{stderr} is now preserved.
% \end{itemize}
% \end{changelog}
%
@@ -1618,6 +1620,13 @@
% Loading \pytex\ with |usefamily=perl| enables the |perl| family of commands and environments. Alternatively, |usefamily=pl| may be used to enable the |pl| family. There is currently no utilities class or related features.
%
%
+% \subsection{Perl 6}
+%
+% Support for Perl 6 was added in v0.17.
+%
+% Loading \pytex\ with |usefamily=perlsix| enables the |perlsix| family of commands and environments. Alternatively, |usefamily=psix| may be used to enable the |psix| family. There is currently no utilities class or related features.
+%
+%
% \subsection{Adding support for a new language}
%
% Adding support for an additional language involves creating two templates, creating a new instance of a class, and using a \pytex\ macro. In some cases, additional changes may be necessary for full support. The information below does not deal with creating |console| families; additional support for user-defined |console| families will be added in the future.
@@ -4172,6 +4181,8 @@
\ifstrequal{#1}{Rcon}{\makepythontexfamily@con[rconsole]{R}}{}%
\ifstrequal{#1}{perl}{\makepythontexfamily[pyglexer=perl]{perl}}{}%
\ifstrequal{#1}{pl}{\makepythontexfamily[pyglexer=perl]{pl}}{}%
+ \ifstrequal{#1}{perlsix}{\makepythontexfamily[pyglexer=perl6]{perlsix}}{}%
+ \ifstrequal{#1}{psix}{\makepythontexfamily[pyglexer=perl6]{psix}}{}%
}
\expandafter\docsvlist\expandafter{\pytx@families}
% \end{macrocode}
diff --git a/pythontex/pythontex.sty b/pythontex/pythontex.sty
index 5d98ade..2f53e97 100644
--- a/pythontex/pythontex.sty
+++ b/pythontex/pythontex.sty
@@ -20,7 +20,7 @@
%%
\NeedsTeXFormat{LaTeX2e}[1999/12/01]
\ProvidesPackage{pythontex}
- [2018/10/30 v0.17dev execute and typeset Python code and other languages]
+ [2018/11/10 v0.17dev execute and typeset Python code and other languages]
\newcommand{\pytx@packagename}{PythonTeX}
\newcommand{\pytx@packageversion}{0.17dev}
@@ -1370,6 +1370,8 @@
\ifstrequal{#1}{Rcon}{\makepythontexfamily@con[rconsole]{R}}{}%
\ifstrequal{#1}{perl}{\makepythontexfamily[pyglexer=perl]{perl}}{}%
\ifstrequal{#1}{pl}{\makepythontexfamily[pyglexer=perl]{pl}}{}%
+ \ifstrequal{#1}{perlsix}{\makepythontexfamily[pyglexer=perl6]{perlsix}}{}%
+ \ifstrequal{#1}{psix}{\makepythontexfamily[pyglexer=perl6]{psix}}{}%
}
\expandafter\docsvlist\expandafter{\pytx@families}
\newbool{pytx@listingenv}
diff --git a/pythontex/pythontex2.py b/pythontex/pythontex2.py
index 0e989fd..f6e967a 100644
--- a/pythontex/pythontex2.py
+++ b/pythontex/pythontex2.py
@@ -1214,9 +1214,13 @@ def do_multiprocessing(data, temp_data, old_data, engine_dict):
# Must double-escape any backslashes so that they survive `shlex.split()`
script = basename
if os.path.isabs(os.path.expanduser(os.path.normcase(outputdir))):
- script_full = os.path.expanduser(os.path.normcase(os.path.join(outputdir, basename)))
+ script_full = os.path.expanduser(os.path.join(outputdir, basename))
else:
- script_full = os.path.expanduser(os.path.normcase(os.path.join(orig_cwd, outputdir, basename)))
+ script_full = os.path.expanduser(os.path.join(orig_cwd, outputdir, basename))
+ if platform.system() == 'Windows':
+ script_full = script_full.replace('/', '\\')
+ else:
+ script_full = script_full.replace('\\', '/')
# `shlex.split()` only works with Unicode after 2.7.2
if (sys.version_info.major == 2 and sys.version_info.micro < 3):
exec_cmd = shlex.split(bytes(command.format(file=script.replace('\\', '\\\\'), File=script_full.replace('\\', '\\\\'))))
@@ -1521,11 +1525,19 @@ def run_code(encoding, outputdir, workingdir,
err_file_name = os.path.expanduser(os.path.normcase(os.path.join(outputdir, basename + '.err')))
out_file = open(out_file_name, 'w', encoding=encoding)
err_file = open(err_file_name, 'w', encoding=encoding)
- script = os.path.expanduser(os.path.normcase(os.path.join(outputdir, basename)))
+ script = os.path.expanduser(os.path.join(outputdir, basename))
+ if platform.system() == 'Windows':
+ script = script.replace('/', '\\')
+ else:
+ script = script.replace('\\', '/')
if os.path.isabs(script):
script_full = script
else:
- script_full = os.path.expanduser(os.path.normcase(os.path.join(os.getcwd(), outputdir, basename)))
+ script_full = os.path.expanduser(os.path.join(os.getcwd(), outputdir, basename))
+ if platform.system() == 'Windows':
+ script_full = script_full.replace('/', '\\')
+ else:
+ script_full = script_full.replace('\\', '/')
# #### Need to revise so that intermediate files can be detected and cleaned up
for f in command_created:
files.append(f.format(file=script, File=script_full))
@@ -1782,7 +1794,7 @@ def run_code(encoding, outputdir, workingdir,
index_next = index_now
start_errgobble = None
for n, line in enumerate(err_ud):
- if basename in line:
+ if basename in line and (family not in ('perlsix', 'psix') or '.p6:' in line or '.p6 line' in line):
# Get the gobbleation. This is used to determine if
# other lines containing the basename are a continuation,
# or separate messages.
@@ -1828,7 +1840,7 @@ def run_code(encoding, outputdir, workingdir,
# both the error and warning patterns, default to
# error.
past_line = err_ud[index]
- if (index < n and basename in past_line):
+ if (index < n and basename in past_line and (family not in ('perlsix', 'psix') or '.p6:' in past_line or '.p6 line' in past_line)):
break
for pattern in warningsig:
if pattern in past_line:
@@ -1892,8 +1904,9 @@ def run_code(encoding, outputdir, workingdir,
index_now_last = index_now
index_next_last = index_next
err_key_last_int = -1
+ p6_sorry_search = False
for n, line in enumerate(err_ud):
- if basename in line:
+ if basename in line and (family not in ('perlsix', 'psix') or '.p6:' in line or '.p6 line' in line):
# Determine the corresponding line number in the document
found = False
for pattern in linesig:
@@ -1955,6 +1968,35 @@ def run_code(encoding, outputdir, workingdir,
line = line.replace(fullbasename + '.' + extension, '')
elif stderrfilename == 'genericscript':
line = line.replace(fullbasename + '.' + extension, '
+
+
-
+
General Python interaction
We can typeset code that is passed to Python, and bring back the results.
This can be simple. For example, print('Python says hi!') returns the following:
@@ -62,181 +227,183 @@ General Python interaction
Python says hi!
Python interaction can also be more complex. print(str(2**2**2) + r'\endinput') returns 16. In this case, the printed result includes LaTeX code, which is correctly interpreted by LaTeX to ensure that there is not an extra space after the 16. Printed output is saved to a file and brought back in via \input, and the \endinput command stops input immediately, before LaTeX gets to the end of the line and inserts a space character there, after the 16.
-Printing works, but as the last example demonstrates, you have to be careful about spacing if you have text immediately after the printed content. In that case, it’s usually best to assemble text within a PythonTeX environment and store the text in a variable. Then you can bring in the text later, using the \py command. The \py command brings in a string representation of its argument. First we create the text.
-mytext = '$1 + 1 = {0}$'.format(1 + 1)
+Printing works, but as the last example demonstrates, you have to be careful about spacing if you have text immediately after the printed content. In that case, it’s usually best to assemble text within a PythonTeX environment and store the text in a variable. Then you can bring in the text later, using the \py command. The \py command brings in a string representation of its argument. First we create the text.
+mytext = '$1 + 1 = {0}$'.format(1 + 1)
Then we bring it in: \(1 + 1 = 2\). The \py command can even bring in verbatim content.
We don’t have to typeset the code we’re executing. It can be hidden. And then we can access it later: This is a message from Python.
It is also possible to perform variable substitution or string interpolation. The earlier result could be recreated: \(1 + 1 = 2\).
Pygments highlighting
-PythonTeX supports syntax highlighting via Pygments. Any language supported by Pygments can be highlighted. Unicode is supported. Consider this snippet copied and pasted from a Python 3 interactive session. (Using random strings of Unicode for variable names is probably not a good idea, but PythonTeX will happily highlight it for you.)
->>> âæéöø = 123
->>> ßçñðŠ = 456
->>> âæéöø + ßçñðŠ
-579
+PythonTeX supports syntax highlighting via Pygments. Any language supported by Pygments can be highlighted. Unicode is supported. Consider this snippet copied and pasted from a Python 3 interactive session. (Using random strings of Unicode for variable names is probably not a good idea, but PythonTeX will happily highlight it for you.)
+>>> âæéöø = 123
+>>> ßçñðŠ = 456
+>>> âæéöø + ßçñðŠ
+579
There is also a Pygments command for inline use: \pygment.
Python console environment
-PythonTeX includes an environment that emulates a Python interactive session. Commands are entered within the environment, each line is treated as input to an interactive session, and the result is typeset.
->>> x = 123
->>> y = 345
->>> z = x + y
->>> z
-468
->>> def f(expr):
-... return(expr**4)
-...
->>> f(x)
-228886641
->>> print('Python says hi from the console!')
-Python says hi from the console!
+PythonTeX includes an environment that emulates a Python interactive session. Commands are entered within the environment, each line is treated as input to an interactive session, and the result is typeset.
+>>> x = 123
+>>> y = 345
+>>> z = x + y
+>>> z
+468
+>>> def f(expr):
+... return(expr**4)
+...
+>>> f(x)
+228886641
+>>> print('Python says hi from the console!')
+Python says hi from the console!
It is possible to refer to the values of console variables later on in inline contexts, using the \pycon command. For example, the value of \(z\) was 468.
Basic SymPy interaction
-PythonTeX allows us to perform algebraic manipulations with SymPy and then properly typeset the results.
+PythonTeX allows us to perform algebraic manipulations with SymPy and then properly typeset the results.
We create three variables, and define \(z\) in terms of the other two.
-var('x, y, z')
-z = x + y
+var('x, y, z')
+z = x + y
Now we can access what \(z\) is equal to:
\[z=x + y\]
Many things are possible, including some very nice calculus.
-f = x**3 + cos(x)**5
-g = Integral(f, x)
-\[\int x^{3} + \cos^{5}{\left (x \right )}\, dx=\frac{x^{4}}{4} + \frac{1}{5} \sin^{5}{\left (x \right )} - \frac{2}{3} \sin^{3}{\left (x \right )} + \sin{\left (x \right )}\]
+f = x**3 + cos(x)**5
+g = Integral(f, x)
+\[\int \left(x^{3} + \cos^{5}{\left(x \right)}\right)\, dx=\frac{x^{4}}{4} + \frac{\sin^{5}{\left(x \right)}}{5} - \frac{2 \sin^{3}{\left(x \right)}}{3} + \sin{\left(x \right)}\]
It’s easy to use arbitrary symbols in equations.
-phi = Symbol(r'\phi')
-h = Integral(exp(-phi**2), (phi, 0, oo))
-\[\int_{0}^{\infty} e^{- \phi^{2}}\, d\phi=\frac{\sqrt{\pi}}{2}\]
+phi = Symbol(r'\phi')
+h = Integral(exp(-phi**2), (phi, 0, oo))
+\[\int\limits_{0}^{\infty} e^{- \phi^{2}}\, d\phi=\frac{\sqrt{\pi}}{2}\]
Plots with matplotlib
We can create plots with matplotlib, perfectly matching the plot fonts with the document fonts. No more searching for the code that created a figure!
It is possible to pass page dimensions and similar contextual information from the LaTeX side to the Python side. If you want your figures to be, for example, a particular fraction of the page width, you can pass the value of \textwidth to the Python side, and use it in creating your figures. See \setpythontexcontext in the main documentation for details.
You may want to use matplotlib’s PGF backend when creating plots.
-rc('text', usetex=True)
-rc('font', family='serif')
-rc('font', size=10.0)
-rc('legend', fontsize=10.0)
-rc('font', weight='normal')
-x = linspace(0, 10)
-figure(figsize=(4, 2.5))
-plot(x, sin(x), label='$\sin(x)$')
-xlabel(r'$x\mathrm{-axis}$')
-ylabel(r'$y\mathrm{-axis}$')
-legend(loc='lower right')
-savefig('myplot.png', bbox_inches='tight')
+rc('text', usetex=True)
+rc('font', family='serif')
+rc('font', size=10.0)
+rc('legend', fontsize=10.0)
+rc('font', weight='normal')
+x = linspace(0, 10)
+figure(figsize=(4, 2.5))
+plot(x, sin(x), label='$\sin(x)$')
+xlabel(r'$x\mathrm{-axis}$')
+ylabel(r'$y\mathrm{-axis}$')
+legend(loc='lower right')
+savefig('myplot.png', bbox_inches='tight')
+

+
Basic pylab interaction
-from scipy.integrate import quad
-myintegral = quad(lambda x: e**-x**2, 0, inf)[0]
+from scipy.integrate import quad
+myintegral = quad(lambda x: e**-x**2, 0, inf)[0]
\[\int_0^\infty e^{-x^2}\,dx = 0.886226925452758\]
An automated derivative and integral table
-PythonTeX allows some amazing document automation, such as this derivative and integral table. Try typing that by hand, fast!
-from re import sub
-
-var('x')
-
-# Create a list of functions to include in the table
-funcs = ['sin(x)', 'cos(x)', 'tan(x)',
- 'sin(x)**2', 'cos(x)**2', 'tan(x)**2',
- 'asin(x)', 'acos(x)', 'atan(x)',
- 'sinh(x)', 'cosh(x)', 'tanh(x)']
-
-print(r'\begin{align*}')
-
-for func in funcs:
- # Put in some vertical space when switching to arc and hyperbolic funcs
- if func == 'asin(x)' or func == 'sinh(x)':
- print(r'&\\')
- myderiv = 'Derivative(' + func + ', x)'
- myint = 'Integral(' + func + ', x)'
- print(latex(eval(myderiv)) + '&=' +
- latex(eval(myderiv + '.doit()')) + r'\quad & \quad')
- print(latex(eval(myint)) + '&=' +
- latex(eval(myint+'.doit()')) + r'\\')
-print(r'\end{align*}')
+PythonTeX allows some amazing document automation, such as this derivative and integral table. Try typing that by hand, fast!
+from re import sub
+
+var('x')
+
+# Create a list of functions to include in the table
+funcs = ['sin(x)', 'cos(x)', 'tan(x)',
+ 'sin(x)**2', 'cos(x)**2', 'tan(x)**2',
+ 'asin(x)', 'acos(x)', 'atan(x)',
+ 'sinh(x)', 'cosh(x)', 'tanh(x)']
+
+print(r'\begin{align*}')
+
+for func in funcs:
+ # Put in some vertical space when switching to arc and hyperbolic funcs
+ if func == 'asin(x)' or func == 'sinh(x)':
+ print(r'&\\')
+ myderiv = 'Derivative(' + func + ', x)'
+ myint = 'Integral(' + func + ', x)'
+ print(latex(eval(myderiv)) + '&=' +
+ latex(eval(myderiv + '.doit()')) + r'\quad & \quad')
+ print(latex(eval(myint)) + '&=' +
+ latex(eval(myint+'.doit()')) + r'\\')
+print(r'\end{align*}')
\[\begin{aligned}
-\frac{d}{d x} \sin{\left (x \right )}&=\cos{\left (x \right )}\quad & \quad
-\int \sin{\left (x \right )}\, dx&=- \cos{\left (x \right )}\\
-\frac{d}{d x} \cos{\left (x \right )}&=- \sin{\left (x \right )}\quad & \quad
-\int \cos{\left (x \right )}\, dx&=\sin{\left (x \right )}\\
-\frac{d}{d x} \tan{\left (x \right )}&=\tan^{2}{\left (x \right )} + 1\quad & \quad
-\int \tan{\left (x \right )}\, dx&=- \frac{1}{2} \log{\left (\sin^{2}{\left (x \right )} - 1 \right )}\\
-\frac{d}{d x} \sin^{2}{\left (x \right )}&=2 \sin{\left (x \right )} \cos{\left (x \right )}\quad & \quad
-\int \sin^{2}{\left (x \right )}\, dx&=\frac{x}{2} - \frac{1}{2} \sin{\left (x \right )} \cos{\left (x \right )}\\
-\frac{d}{d x} \cos^{2}{\left (x \right )}&=- 2 \sin{\left (x \right )} \cos{\left (x \right )}\quad & \quad
-\int \cos^{2}{\left (x \right )}\, dx&=\frac{x}{2} + \frac{1}{2} \sin{\left (x \right )} \cos{\left (x \right )}\\
-\frac{d}{d x} \tan^{2}{\left (x \right )}&=\left(2 \tan^{2}{\left (x \right )} + 2\right) \tan{\left (x \right )}\quad & \quad
-\int \tan^{2}{\left (x \right )}\, dx&=- x + \frac{\sin{\left (x \right )}}{\cos{\left (x \right )}}\\
+\frac{d}{d x} \sin{\left(x \right)}&=\cos{\left(x \right)}\quad & \quad
+\int \sin{\left(x \right)}\, dx&=- \cos{\left(x \right)}\\
+\frac{d}{d x} \cos{\left(x \right)}&=- \sin{\left(x \right)}\quad & \quad
+\int \cos{\left(x \right)}\, dx&=\sin{\left(x \right)}\\
+\frac{d}{d x} \tan{\left(x \right)}&=\tan^{2}{\left(x \right)} + 1\quad & \quad
+\int \tan{\left(x \right)}\, dx&=- \log{\left(\cos{\left(x \right)} \right)}\\
+\frac{d}{d x} \sin^{2}{\left(x \right)}&=2 \sin{\left(x \right)} \cos{\left(x \right)}\quad & \quad
+\int \sin^{2}{\left(x \right)}\, dx&=\frac{x}{2} - \frac{\sin{\left(x \right)} \cos{\left(x \right)}}{2}\\
+\frac{d}{d x} \cos^{2}{\left(x \right)}&=- 2 \sin{\left(x \right)} \cos{\left(x \right)}\quad & \quad
+\int \cos^{2}{\left(x \right)}\, dx&=\frac{x}{2} + \frac{\sin{\left(x \right)} \cos{\left(x \right)}}{2}\\
+\frac{d}{d x} \tan^{2}{\left(x \right)}&=\left(2 \tan^{2}{\left(x \right)} + 2\right) \tan{\left(x \right)}\quad & \quad
+\int \tan^{2}{\left(x \right)}\, dx&=- x + \frac{\sin{\left(x \right)}}{\cos{\left(x \right)}}\\
&\\
-\frac{d}{d x} \operatorname{asin}{\left (x \right )}&=\frac{1}{\sqrt{- x^{2} + 1}}\quad & \quad
-\int \operatorname{asin}{\left (x \right )}\, dx&=x \operatorname{asin}{\left (x \right )} + \sqrt{- x^{2} + 1}\\
-\frac{d}{d x} \operatorname{acos}{\left (x \right )}&=- \frac{1}{\sqrt{- x^{2} + 1}}\quad & \quad
-\int \operatorname{acos}{\left (x \right )}\, dx&=x \operatorname{acos}{\left (x \right )} - \sqrt{- x^{2} + 1}\\
-\frac{d}{d x} \operatorname{atan}{\left (x \right )}&=\frac{1}{x^{2} + 1}\quad & \quad
-\int \operatorname{atan}{\left (x \right )}\, dx&=x \operatorname{atan}{\left (x \right )} - \frac{1}{2} \log{\left (x^{2} + 1 \right )}\\
+\frac{d}{d x} \operatorname{asin}{\left(x \right)}&=\frac{1}{\sqrt{1 - x^{2}}}\quad & \quad
+\int \operatorname{asin}{\left(x \right)}\, dx&=x \operatorname{asin}{\left(x \right)} + \sqrt{1 - x^{2}}\\
+\frac{d}{d x} \operatorname{acos}{\left(x \right)}&=- \frac{1}{\sqrt{1 - x^{2}}}\quad & \quad
+\int \operatorname{acos}{\left(x \right)}\, dx&=x \operatorname{acos}{\left(x \right)} - \sqrt{1 - x^{2}}\\
+\frac{d}{d x} \operatorname{atan}{\left(x \right)}&=\frac{1}{x^{2} + 1}\quad & \quad
+\int \operatorname{atan}{\left(x \right)}\, dx&=x \operatorname{atan}{\left(x \right)} - \frac{\log{\left(x^{2} + 1 \right)}}{2}\\
&\\
-\frac{d}{d x} \sinh{\left (x \right )}&=\cosh{\left (x \right )}\quad & \quad
-\int \sinh{\left (x \right )}\, dx&=\cosh{\left (x \right )}\\
-\frac{d}{d x} \cosh{\left (x \right )}&=\sinh{\left (x \right )}\quad & \quad
-\int \cosh{\left (x \right )}\, dx&=\sinh{\left (x \right )}\\
-\frac{d}{d x} \tanh{\left (x \right )}&=- \tanh^{2}{\left (x \right )} + 1\quad & \quad
-\int \tanh{\left (x \right )}\, dx&=x - \log{\left (\tanh{\left (x \right )} + 1 \right )}\\\end{aligned}\]
+\frac{d}{d x} \sinh{\left(x \right)}&=\cosh{\left(x \right)}\quad & \quad
+\int \sinh{\left(x \right)}\, dx&=\cosh{\left(x \right)}\\
+\frac{d}{d x} \cosh{\left(x \right)}&=\sinh{\left(x \right)}\quad & \quad
+\int \cosh{\left(x \right)}\, dx&=\sinh{\left(x \right)}\\
+\frac{d}{d x} \tanh{\left(x \right)}&=1 - \tanh^{2}{\left(x \right)}\quad & \quad
+\int \tanh{\left(x \right)}\, dx&=x - \log{\left(\tanh{\left(x \right)} + 1 \right)}\\\end{aligned}\]
Step-by-step solutions
Using SymPy, it is possible to typeset step-by-step solutions. In this particular case, we also use the mdframed package to place a colored background behind our code.
-x, y, z = symbols('x,y,z')
-f = Symbol('f(x,y,z)')
-
-# Define limits of integration
-x_llim = 0
-x_ulim = 2
-y_llim = 0
-y_ulim = 3
-z_llim = 0
-z_ulim = 4
-
-print(r'\begin{align*}')
-
-# Notice how I define f as a symbol, then later as an actual function
-left = Integral(f, (x, x_llim, x_ulim), (y, y_llim, y_ulim), (z, z_llim, z_ulim))
-f = x*y + y*sin(z) + cos(x+y)
-right = Integral(f, (x, x_llim, x_ulim), (y, y_llim, y_ulim), (z, z_llim, z_ulim))
-print(latex(left) + '&=' + latex(right) + r'\\')
-
-# For each step, I move limits from an outer integral to an inner, evaluated
-# integral until the outer integral is no longer needed
-right = Integral(Integral(f, (z, z_llim, z_ulim)).doit(), (x, x_llim, x_ulim),
- (y, y_llim, y_ulim))
-print('&=' + latex(right) + r'\\')
-
-right = Integral(Integral(f, (z, z_llim, z_ulim), (y, y_llim, y_ulim)).doit(),
- (x, x_llim, x_ulim))
-print('&=' + latex(right) + r'\\')
-
-right = Integral(f, (z, z_llim, z_ulim), (y, y_llim, y_ulim),
- (x, x_llim, x_ulim)).doit()
-print('&=' + latex(right) + r'\\')
-
-print('&=' + latex(N(right)) + r'\\')
-
-print(r'\end{align*}')
+x, y, z = symbols('x,y,z')
+f = Symbol('f(x,y,z)')
+
+# Define limits of integration
+x_llim = 0
+x_ulim = 2
+y_llim = 0
+y_ulim = 3
+z_llim = 0
+z_ulim = 4
+
+print(r'\begin{align*}')
+
+# Notice how I define f as a symbol, then later as an actual function
+left = Integral(f, (x, x_llim, x_ulim), (y, y_llim, y_ulim), (z, z_llim, z_ulim))
+f = x*y + y*sin(z) + cos(x+y)
+right = Integral(f, (x, x_llim, x_ulim), (y, y_llim, y_ulim), (z, z_llim, z_ulim))
+print(latex(left) + '&=' + latex(right) + r'\\')
+
+# For each step, I move limits from an outer integral to an inner, evaluated
+# integral until the outer integral is no longer needed
+right = Integral(Integral(f, (z, z_llim, z_ulim)).doit(), (x, x_llim, x_ulim),
+ (y, y_llim, y_ulim))
+print('&=' + latex(right) + r'\\')
+
+right = Integral(Integral(f, (z, z_llim, z_ulim), (y, y_llim, y_ulim)).doit(),
+ (x, x_llim, x_ulim))
+print('&=' + latex(right) + r'\\')
+
+right = Integral(f, (z, z_llim, z_ulim), (y, y_llim, y_ulim),
+ (x, x_llim, x_ulim)).doit()
+print('&=' + latex(right) + r'\\')
+
+print('&=' + latex(N(right)) + r'\\')
+
+print(r'\end{align*}')
\[\begin{aligned}
-\int_{0}^{4}\int_{0}^{3}\int_{0}^{2} f(x,y,z)\, dx\, dy\, dz&=\int_{0}^{4}\int_{0}^{3}\int_{0}^{2} x y + y \sin{\left (z \right )} + \cos{\left (x + y \right )}\, dx\, dy\, dz\\
-&=\int_{0}^{3}\int_{0}^{2} 4 x y - y \cos{\left (4 \right )} + y + 4 \cos{\left (x + y \right )}\, dx\, dy\\
-&=\int_{0}^{2} 18 x - 4 \sin{\left (x \right )} + 4 \sin{\left (x + 3 \right )} - \frac{9}{2} \cos{\left (4 \right )} + \frac{9}{2}\, dx\\
-&=4 \cos{\left (3 \right )} + 4 \cos{\left (2 \right )} - 4 \cos{\left (5 \right )} - 9 \cos{\left (4 \right )} + 41\\
+\int\limits_{0}^{4}\int\limits_{0}^{3}\int\limits_{0}^{2} f(x,y,z)\, dx\, dy\, dz&=\int\limits_{0}^{4}\int\limits_{0}^{3}\int\limits_{0}^{2} \left(x y + y \sin{\left(z \right)} + \cos{\left(x + y \right)}\right)\, dx\, dy\, dz\\
+&=\int\limits_{0}^{3}\int\limits_{0}^{2} \left(4 x y - y \cos{\left(4 \right)} + y + 4 \cos{\left(x + y \right)}\right)\, dx\, dy\\
+&=\int\limits_{0}^{2} \left(18 x - 4 \sin{\left(x \right)} + 4 \sin{\left(x + 3 \right)} - \frac{9 \cos{\left(4 \right)}}{2} + \frac{9}{2}\right)\, dx\\
+&=4 \cos{\left(3 \right)} + 4 \cos{\left(2 \right)} - 4 \cos{\left(5 \right)} - 9 \cos{\left(4 \right)} + 41\\
&=40.1235865133293\\\end{aligned}\]
Including stderr
-PythonTeX allows code to be typset next to the stderr it produces. This requires the package option makestderr.
-x = 123
-y = 345
-z = x + y +
+PythonTeX allows code to be typset next to the stderr it produces. This requires the package option makestderr.
+x = 123
+y = 345
+z = x + y +
This code causes a syntax error:
File "py_errorsession_9.py", line 3
z = x + y +
^
SyntaxError: invalid syntax
The package option stderrfilename allows the file name that appears in the error message to be customized.
-
+