summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/latex2pydata/latex2pydata.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/contrib/latex2pydata/latex2pydata.dtx')
-rw-r--r--macros/latex/contrib/latex2pydata/latex2pydata.dtx282
1 files changed, 169 insertions, 113 deletions
diff --git a/macros/latex/contrib/latex2pydata/latex2pydata.dtx b/macros/latex/contrib/latex2pydata/latex2pydata.dtx
index c49c732037..a9d1751557 100644
--- a/macros/latex/contrib/latex2pydata/latex2pydata.dtx
+++ b/macros/latex/contrib/latex2pydata/latex2pydata.dtx
@@ -1,6 +1,6 @@
% \iffalse meta-comment
%
-% Copyright (C) 2023 by Geoffrey M. Poore <gpoore@gmail.com>
+% Copyright (C) 2023-2024 by Geoffrey M. Poore <gpoore@gmail.com>
% ---------------------------------------------------------------------------
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3c
@@ -26,7 +26,7 @@
%<package>\NeedsTeXFormat{LaTeX2e}[1999/12/01]
%<package>\ProvidesPackage{latex2pydata}
%<*package>
- [2023/11/19 v0.1 latex2pydata - write data to file in Python literal format]
+ [2024/05/16 v0.2.0 latex2pydata - write data to file in Python literal format]
%</package>
%
%<*driver>
@@ -67,23 +67,7 @@
\usepackage{tcolorbox}
\tcbuselibrary{listings}
-% strip leading percent symbols
-\def\tcbverbatimwrite#1{%
- \@bsphack
- \tcb@set@verbatim@finish%
- \tcb@allocate@tcb@out%
- \immediate\openout\tcb@out #1
- \tcb@verbatim@begin@hook%
- \let\do\@makeother\dospecials
- \tcb@verbatim@change@percent\catcode`\^^M\active \catcode`\^^I=12
- \def\verbatim@processline{%
- \immediate\write\tcb@out
- {\expandafter\@gobble\the\verbatim@line}}%
- \verbatim@start}%
-
-% fix redefinition by tcolorbox
-\def\verbatim@processline{%
- \expandafter\check@percent\the\verbatim@line\par}
+\tcbset{verbatim ignore percent}
\usepackage{hyperref}
\hypersetup{
@@ -248,27 +232,11 @@
\edef\hashchar{\string#}
-\newcommand{\changestext}{}
-\NewEnviron{changelog}[2]{%
- \g@addto@macro\changestext{\item[#1] (#2) \begin{itemize}}%
- \expandafter\g@addto@macro\expandafter\changestext\expandafter{\BODY}%
- \g@addto@macro\changestext{\end{itemize}}%
-}
-\newcommand{\PrintChangelog}{%
- %\addcontentsline{toc}{section}{Changelog}
- %\section*{Changelog}%
- \section{Changelog}
- \label{sec:changelog}
- \begin{description}%
- \changestext
- \end{description}%
-}
-
\begingroup
\catcode`\#=12\relax
\gdef\astliteval{\href{https://docs.python.org/3/library/ast.html#ast.literal_eval}{\code{ast.literal_eval()}}}
\endgroup
-\def\pydatapy{\href{https://github.com/gpoore/latex2pydata_py}{\pkg{latex2pydata} Python package}}
+\def\pydatapy{\href{https://github.com/gpoore/latex2pydata/tree/main/python}{\pkg{latex2pydata} Python package}}
\def\fvextra{\href{https://github.com/gpoore/fvextra/}{\pkg{fvextra}}}
\def\fancyvrb{\href{https://ctan.org/pkg/fancyvrb}{\pkg{fancyvrb}}}
@@ -286,11 +254,6 @@
% \fi
%
%
-% \begin{changelog}{v0.1}{2023-11-19}
-% \item Initial release.
-% \end{changelog}
-%
-%
% \DoNotIndex{\newcommand,\newenvironment}
% \DoNotIndex{\#,\$,\%,\&,\@,\\,\{,\},\^,\_,\~,\ }
% \DoNotIndex{\@ne}
@@ -303,7 +266,7 @@
% \GetFileInfo{latex2pydata.dtx}
%
% \title{\vspace{-0.5in}The \pydata\ package}
-% \author{Geoffrey M.\ Poore \\ \href{mailto://gpoore@gmail.com}{\texttt{gpoore@gmail.com}} \\ \href{https://github.com/gpoore/latex2pydata_tex}{\Verb{github.com/gpoore/latex2pydata_tex}}}
+% \author{Geoffrey M.\ Poore \\ \href{mailto://gpoore@gmail.com}{\texttt{gpoore@gmail.com}} \\ \href{https://github.com/gpoore/latex2pydata/tree/main/latex}{\Verb{github.com/gpoore/latex2pydata/tree/main/latex}}}
% \date{\fileversion~from \filedate}
%
% \maketitle
@@ -385,6 +348,8 @@
%
% \subsection{File handling}
%
+% All file handling commands operate globally (|\global|, |\gdef|, etc.).
+%
% \DescMacro{\pydatasetfilehandle\marg{filehandle}}
% Configure writing to file using an existing file handle created with |\newwrite|. This allows manual management of the file handle. For example:
% \begin{Verbatim}[gobble=2]
@@ -398,10 +363,12 @@
%
% To switch from one file handle to another, simply use |\pydatasetfilehandle| with the new file handle. When the file handle is no longer in use, |\pydatareleasefilehandle| is recommended (but not required) to remove references to the file handle and perform basic checking for incomplete or malformed data written to file.
%
+% |\pydatasetfilehandle| sets the file handle globally.
+%
% \DescMacro{\pydatareleasefilehandle\marg{filehandle}}
% When a file handle is no longer needed, remove references to it. Also perform basic checking for incomplete or malformed data written to file.
%
-% This should only be used once per file handle, after all data has been written. It is not needed when switching from one file handle to another when both files remain open; in that case, only |\pydatasetfilehandle| is needed.
+% This should only be used once per opened file, after all data has been written, just before the file is closed. It is not needed when switching from one file handle to another when both files remain open; in that case, only |\pydatasetfilehandle| is needed. If |\pydatareleasefilehandle| is used before all data is written, or it is used multiple times while writing to the same file, then it is no longer possible to detect incomplete or malformed data.
%
% \DescMacro{\pydatasetfilename\marg{filename}}
% Configure a file for writing based on filename, opening the file if necessary. For example:
@@ -412,6 +379,8 @@
%
% To switch from one file to another, simply use |\pydatasetfilename| with the new filename. When the file is no longer in use, |\pydataclosefilename| is recommended.
%
+% |\pydatasetfilename| sets the filename globally.
+%
% \DescMacro{\pydataclosefilename\marg{filename}}
% Close a file previously opened with |\pydatasetfilename|. Also perform basic checking for incomplete or malformed data written to file.
%
@@ -465,7 +434,13 @@
% \subsection{Writing keys and values}
% All keys must be single-line strings of text without a newline. Both single-line and multi-line values are supported. Keys and values are written to the file previously configured via |\pydatasetfilehandle| or |\pydatasetfilename|.
%
-% The \pydata\ commands read keys and values verbatim. When these commands are used inside other commands, they use macros from \fvextra\ to attempt to interpret their arguments as verbatim. However, there are limitations in this case because the arguments are already tokenized:
+% Commands for writing keys and values may read these keys and values in one of two ways.
+% \begin{itemize}
+% \item Commands whose names contain |key| or |value| read these arguments verbatim, as described below.
+% \item Commands whose names contain |edefkey| or |edefvalue| read these arguments normally, then expand the arguments via |\edef|, and finally interpret the result as verbatim text.
+% \end{itemize}
+%
+% The \pydata\ commands that read keys and values verbatim have some limitations. When these commands are used inside other commands, they use macros from \fvextra\ to attempt to interpret their arguments as verbatim. However, there are limitations in this case because the arguments are already tokenized:
% \begin{itemize}
% \item |#| and |%| cannot be used.
% \item Curly braces are only allowed in pairs.
@@ -473,7 +448,7 @@
% \item Be careful with backslashes. A backslash that is followed by one or more ASCII letters will cause a following space to be lost, if the space is not immediately followed by an ASCII letter.
% \item A single |^| is fine, but |^^| will serve as an escape sequence for an ASCII command character.
% \end{itemize}
-% When the \pydata\ commands are used inside other commands that pass their arguments to the \pydata\ commands, it will usually be best to avoid these limitations by defining the other commands to read their arguments verbatim. Consider using the \href{https://ctan.org/pkg/xparse}{\pkg{xparse}} package. It is also possible to use |\FVExtraReadVArg| from \fvextra; for an example, see the implementation of |\pydatawritekey|.
+% When the \pydata\ commands are used inside other commands that pass their arguments to the \pydata\ commands, it may be best to avoid these limitations by defining the other commands to read their arguments verbatim. Consider using the \href{https://ctan.org/pkg/xparse}{\pkg{xparse}} package. It is also possible to use |\FVExtraReadVArg| from \fvextra; for an example, see the implementation of |\pydatawritekey|.
%
% Because the \pydata\ commands treat keys and values as verbatim, any desired macro expansion must be performed before passing the keys and values to the \pydata\ commands.
%
@@ -486,11 +461,22 @@
% \DescMacro{\pydatawritekeyvalue\marg{key}\marg{value}}
% Write a key and a single-line value to file simultaneously.
%
+% \DescMacro{\pydatawritekeyedefvalue\marg{key}\marg{value}}
+% Write a key and a single-line value to file simultaneously. The value is expanded via |\edef| before being interpreted as verbatim text and then written.
+%
% \DescEnv{pydatawritemlvalue}
% Write a multi-line value to file.
%
% This environment uses \fvextra\ and \fancyvrb\ internally to capture the environment contents verbatim. If a new environment is defined as a wrapper for |pydatawritemlvalue|, then |\VerbatimEnvironment| must be used at the beginning of the new environment definition. This configures \fancyvrb\ to find the end of the new environment correctly.
%
+% \DescMacro{\pydatawritemlvaluestart}
+%
+% \DescMacro{\pydatawritemlvalueline\marg{line}}
+%
+% \DescMacro{\pydatawritemlvalueend}
+% These commands allow writing a multi-line value to file one line at a time. \meta{line} is interpreted verbatim.
+%
+%
%
% \subsection{Buffer}
%
@@ -498,6 +484,8 @@
%
% A buffer consists of a sequence of macros of the form |\|\meta{buffername}|line|\meta{n}, where each line of data corresponds to a macro and \meta{n} is an integer greater than or equal to one (one-based indexing). The length of the buffer is stored in the counter \meta{buffername}|length|. Buffers are limited to containing comma-separated key-value data, without any opening or closing dict delimiters |{}|.
%
+% All buffer commands that set the buffer or modify the buffer operate globally (|\global|, |\gdef|, etc.).
+%
%
% \subsubsection{Creating and deleting buffers}
%
@@ -538,15 +526,23 @@
% \DescMacro{\pydatabufferkeyvalue\marg{key}\marg{value}}
% Append a key and a single-line value to the buffer simultaneously.
%
+% \DescMacro{\pydatabufferkeyedefvalue\marg{key}\marg{value}}
+% Append a key and a single-line value to the buffer simultaneously. The value is expanded via |\edef| before being interpreted as verbatim text and then buffered.
+%
% \DescEnv{pydatabuffermlvalue}
% Append a multi-line value to the buffer.
%
% This environment uses \fvextra\ and \fancyvrb\ internally to capture the environment contents verbatim. If a new environment is defined as a wrapper for |pydatabuffermlvalue|, then |\VerbatimEnvironment| must be used at the beginning of the new environment definition. This configures \fancyvrb\ to find the end of the new environment correctly.
%
+% \DescMacro{\pydatabuffermlvaluestart}
+%
+% \DescMacro{\pydatabuffermlvalueline\marg{line}}
+%
+% \DescMacro{\pydatabuffermlvalueend}
+% These commands allow buffering a multi-line value one line at a time. \meta{line} is interpreted verbatim.
%
%
%
-% \PrintChangelog
%
% \StopEventually{\PrintIndex}
%
@@ -582,7 +578,7 @@
% \begin{macrocode}
\RequirePackage{etoolbox}
\RequirePackage{fvextra}
-\IfPackageAtLeastTF{fvextra}{2023/11/19}%
+\IfPackageAtLeastTF{fvextra}{2024/05/16}%
{}{\pydata@error{package fvextra is outdated; upgrade to the latest version}}
\RequirePackage{pdftexcmds}
% \end{macrocode}
@@ -599,55 +595,83 @@
% \end{macro}
%
%
+% \begin{macro}{\pydata@newglobalbool, \pydata@provideglobalbool}
+% Variants of \pkg{etoolbox}'s |\newbool| and |\providebool| that create bools whose state is always global. When these global bools are used with |\setbool|, |\booltrue|, or |\boolfalse|, the global state is updated regardless of whether the command is prefixed with |\global|. These use a global variant of \LaTeX's |\newif| internally.
+% \begin{macrocode}
+\def\pydata@gnewif#1{%
+ \count@\escapechar
+ \escapechar\m@ne
+ \global\let#1\iffalse
+ \pydata@gif#1\iftrue
+ \pydata@gif#1\iffalse
+ \escapechar\count@}
+\def\pydata@gif#1#2{%
+ \expandafter\gdef\csname
+ \expandafter\@gobbletwo\string#1\expandafter\@gobbletwo\string#2\endcsname
+ {\global\let#1#2}}
+\newrobustcmd*{\pydata@newglobalbool}[1]{%
+ \begingroup
+ \let\newif\pydata@gnewif
+ \newbool{#1}%
+ \endgroup}
+\newrobustcmd*{\pydata@provideglobalbool}[1]{%
+ \begingroup
+ \let\newif\pydata@gnewif
+ \providebool{#1}%
+ \endgroup}
+% \end{macrocode}
+% \end{macro}
+%
+%
%
% \subsection{State}
%
-% Track state of writing data and of buffering data.
+% Track state of writing data and of buffering data. Notice that bools for tracking state are a special, custom variant that is always global.
%
% \begin{macro}{pydata@canwrite}
% Whether data can be written. False if a file handle has not been set or if the top-level data structure has been closed.
% \begin{macrocode}
-\newbool{pydata@canwrite}
+\pydata@newglobalbool{pydata@canwrite}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{pydata@hasmeta}
% Whether metadata was written. Metadata is a \code{dict[str, str | dict[str, str]]}.
% \begin{macrocode}
-\newbool{pydata@hasmeta}
+\pydata@newglobalbool{pydata@hasmeta}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{pydata@topexists}
% Whether the top-level data structure has been configured. The top-level data structure can be a list or a dict. The overall data structure must be either |dict[str, str]| or |list[dict[str, str]]|.
% \begin{macrocode}
-\newbool{pydata@topexists}
+\pydata@newglobalbool{pydata@topexists}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{pydata@topislist}
% Whether the top-level data structure is a list.
% \begin{macrocode}
-\newbool{pydata@topislist}
+\pydata@newglobalbool{pydata@topislist}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{pydata@indict}
% Whether a dict has been opened.
% \begin{macrocode}
-\newbool{pydata@indict}
+\pydata@newglobalbool{pydata@indict}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{pydata@haskey}
% Whether a key has been written (waiting for a value).
% \begin{macrocode}
-\newbool{pydata@haskey}
+\pydata@newglobalbool{pydata@haskey}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\pydata@fhstartstate, \pydata@fhstopstate}
-% Start and stop state tracking for a file handle (|\newwrite|). Each file handle has its own set of state bools of the form |pydata@|\meta{boolname}|@|\meta{fh}. When a file handle is in use, the values of these bools are copied into the |pydata@|\meta{boolname} bools; when the file handle is no longer in use, |pydata@|\meta{boolname} values are copied back into |pydata@|\meta{boolname}|@|\meta{fh}.
+% \begin{macro}{\pydata@fhstartstate, \pydata@fhstopstate, \pydata@fhresetstate}
+% Start and stop state tracking for a file handle (|\newwrite|), or reset state after writing is complete. Each file handle has its own set of state bools of the form |pydata@|\meta{boolname}|@|\meta{fh}. When a file handle is in use, the values of these bools are copied into the |pydata@|\meta{boolname} bools; when the file handle is no longer in use, |pydata@|\meta{boolname} values are copied back into |pydata@|\meta{boolname}|@|\meta{fh}.
% \begin{macrocode}
\def\pydata@fhstartstate#1{%
\expandafter\pydata@fhstartstate@i\expandafter{\number#1}}
@@ -659,10 +683,13 @@
\booltrue{pydata@fhnewstate}%
\fi
\def\do##1{%
- \providebool{pydata@##1@#1}%
+ \pydata@provideglobalbool{pydata@##1@#1}%
\ifbool{pydata@##1@#1}{\booltrue{pydata@##1}}{\boolfalse{pydata@##1}}}%
\docsvlist{canwrite, hasmeta, topexists, topislist, indict, haskey}%
- \ifbool{pydata@fhnewstate}{\booltrue{pydata@canwrite}{}}{}}
+ \ifbool{pydata@fhnewstate}%
+ {\booltrue{pydata@canwrite}}{}%
+ \ifbool{pydata@fhisreleased@#1}%
+ {\boolfalse{pydata@fhisreleased@#1}\booltrue{pydata@canwrite}}{}}
\def\pydata@fhstopstate#1{%
\expandafter\pydata@fhstopstate@i\expandafter{\number#1}}
\def\pydata@fhstopstate@i#1{%
@@ -672,6 +699,12 @@
\boolfalse{pydata@##1}}%
\docsvlist{canwrite, hasmeta, topexists, topislist, indict, haskey}%
\fi}
+\def\pydata@fhresetstate#1{%
+ \expandafter\pydata@fhresetstate@i\expandafter{\number#1}}
+\def\pydata@fhresetstate@i#1{%
+ \def\do##1{%
+ \boolfalse{pydata@##1@#1}}%
+ \docsvlist{canwrite, hasmeta, topexists, topislist, indict, haskey}}
% \end{macrocode}
% \end{macro}
%
@@ -680,7 +713,7 @@
%
% If multiple buffers are in use, all buffers use the same |pydata@bufferhaskey|. Inconsistent state is avoided by requiring that |\pydatasetbuffername| can only be invoked when |pydata@bufferhaskey| is false.
% \begin{macrocode}
-\newbool{pydata@bufferhaskey}
+\pydata@newglobalbool{pydata@bufferhaskey}
% \end{macrocode}
% \end{macro}
%
@@ -709,49 +742,68 @@
% Set and release file handle. Release isn't strictly required, but it is necessary for basic data checking on the \LaTeX\ side.
% \begin{macrocode}
\def\pydatasetfilehandle#1{%
+ \if\relax\detokenize{#1}\relax
+ \pydata@error{Missing file handle}%
+ \fi
\ifx\pydata@filehandle\relax
+ \else\ifx\pydata@filehandle#1\relax
\else
\pydata@fhstopstate{\pydata@filehandle}%
- \fi
- \let\pydata@filehandle#1\relax
- \pydata@fhstartstate{#1}}
+ \fi\fi
+ \ifx\pydata@filehandle#1\relax
+ \else
+ \global\let\pydata@filehandle#1\relax
+ \pydata@provideglobalbool{pydata@fhisreleased@\number#1}%
+ \pydata@fhstartstate{#1}%
+ \fi}
\def\pydatareleasefilehandle#1{%
- \ifx\pydata@filehandle\relax
+ \ifcsname ifpydata@canwrite@\number#1\endcsname
\else
- \ifx\pydata@filehandle#1\relax
- \pydata@fhstopstate{#1}%
- \let\pydata@filehandle\relax
- \fi
+ \pydata@error{Unknown file handle #1}%
\fi
- \ifcsname ifpydata@canwrite@\number#1\endcsname
- \ifbool{pydata@canwrite@\number#1}%
- {\ifbool{pydata@haskey@\number#1}%
- {\pydata@error{Incomplete data: key is waiting for value}}{}%
- \ifbool{pydata@indict@\number#1}%
- {\pydata@error{Incomplete data: dict is not closed}}{}%
- \ifbool{pydata@topislist@\number#1}%
- {\pydata@error{Incomplete data: list is not closed}}{}}%
- {}%
- \fi}
+ \ifx\pydata@filehandle#1\relax
+ \pydata@fhstopstate{#1}%
+ \global\let\pydata@filehandle\relax
+ \fi
+ \ifbool{pydata@canwrite@\number#1}%
+ {\ifbool{pydata@haskey@\number#1}%
+ {\pydata@error{Incomplete data: key is waiting for value}}{}%
+ \ifbool{pydata@indict@\number#1}%
+ {\pydata@error{Incomplete data: dict is not closed}}{}%
+ \ifbool{pydata@topislist@\number#1}%
+ {\pydata@error{Incomplete data: list is not closed}}{}}%
+ {}%
+ \pydata@fhresetstate{#1}%
+ \booltrue{pydata@fhisreleased@\number#1}}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\pydatasetfilename, \pydataclosefilename}
-% Shortcut for creating a |\newwrite| and then passing the file handle to |\pydatasetfilehandle|. Automatically attempt to close the file handle (if it still exists) at the end of the document. This isn't strictly required since \TeX\ will \href{https://tex.stackexchange.com/a/337291}{automatically close open writes}. Invoking the close macro is necessary for basic data checking on the \LaTeX\ side.
+% Shortcut for creating a |\newwrite| and then passing the file handle to |\pydatasetfilehandle|. File handles are global. If the close macro is not invoked, then basic data checking on the \LaTeX\ side will not be performed. However, \TeX\ will \href{https://tex.stackexchange.com/a/337291}{automatically close open writes at the end of the compile}.
% \begin{macrocode}
\def\pydatasetfilename#1{%
+ \if\relax\detokenize{#1}\relax
+ \pydata@error{Missing filename}%
+ \fi
\ifcsname pydata@fh@#1\endcsname
\else
\expandafter\newwrite\csname pydata@fh@#1\endcsname
- \expandafter\immediate\expandafter\openout\csname pydata@fh@#1\endcsname=#1\relax
- \AtEndDocument{\pydataclosefilename{#1}}%
\fi
+ \pydata@provideglobalbool{pydata@fileisopen@#1}%
+ \ifbool{pydata@fileisopen@#1}%
+ {}%
+ {\expandafter\immediate\expandafter\openout\csname pydata@fh@#1\endcsname=#1\relax
+ \booltrue{pydata@fileisopen@#1}}%
\expandafter\pydatasetfilehandle\expandafter{\csname pydata@fh@#1\endcsname}}
\def\pydataclosefilename#1{%
\ifcsname pydata@fh@#1\endcsname
- \expandafter\pydatareleasefilehandle\expandafter{\csname pydata@fh@#1\endcsname}%
- \expandafter\immediate\expandafter\closeout\csname pydata@fh@#1\endcsname
- \expandafter\let\csname pydata@fh@#1\endcsname\pydata@undefined
+ \ifbool{pydata@fileisopen@#1}%
+ {\expandafter\pydatareleasefilehandle\expandafter{\csname pydata@fh@#1\endcsname}%
+ \expandafter\immediate\expandafter\closeout\csname pydata@fh@#1\endcsname
+ \boolfalse{pydata@fileisopen@#1}}%
+ {}%
+ \else
+ \pydata@error{Unknown file name "#1"}%
\fi}
% \end{macrocode}
% \end{macro}
@@ -779,14 +831,13 @@
\ifbool{pydata@bufferhaskey}%
{\pydata@error{Cannot change buffers when a buffered key is waiting for a value}}%
{}%
- \def\pydata@buffername{#1}%
- \def\pydata@bufferlinename{#1line}%
- \def\pydata@bufferlengthname{#1length}%
+ \gdef\pydata@buffername{#1}%
+ \gdef\pydata@bufferlinename{#1line}%
+ \gdef\pydata@bufferlengthname{#1length}%
\ifcsname c@\pydata@bufferlengthname\endcsname
\else
\expandafter\newcounter\expandafter{\pydata@bufferlengthname}%
- \fi
- \expandafter\setcounter\expandafter{\pydata@bufferlengthname}{0}}
+ \fi}
\pydatasetbuffername{pydata@defaultbuffer}
% \end{macrocode}
% \end{macro}
@@ -798,7 +849,10 @@
\ifnum\expandafter\value\expandafter{\pydata@bufferlengthname}<1\relax
\pydata@error{Cannot write empty buffer}%
\fi
- \ifbool{pydata@indict}{}{\pydata@error{Cannot write a buffer unless in a dict}}%
+ \pydata@checkfilehandle
+ \ifbool{pydata@indict}{}{\pydata@error{Cannot write buffer unless in a dict}}%
+ \ifbool{pydata@haskey}%
+ {\pydata@error{Cannot write buffer when file has a key waiting for a value}}{}%
\ifbool{pydata@bufferhaskey}%
{\pydata@error{Cannot write buffer when a buffered key is waiting for a value}}{}%
\setcounter{pydata@bufferindex}{1}%
@@ -824,7 +878,7 @@
\fi
\setcounter{pydata@bufferindex}{1}%
\loop\unless\ifnum\value{pydata@bufferindex}>\value{#1length}\relax
- \expandafter\let
+ \expandafter\global\expandafter\let
\csname#1line\arabic{pydata@bufferindex}\endcsname\pydata@undefined
\stepcounter{pydata@bufferindex}%
\repeat
@@ -948,7 +1002,7 @@
\else
\pydata@error{Invalid schema missing setting #1}%
\fi
- \def\pydata@schemamissing{#1}}
+ \gdef\pydata@schemamissing{#1}}
\pydatasetschemamissing{error}
% \end{macrocode}
% \end{macro}
@@ -972,7 +1026,7 @@
% Delete existing schema. This isn't done automatically upon writing so that a schema can be defined and then reused.
% \begin{macrocode}
\def\pydataclearschema{%
- \def\pydata@schema{}}
+ \gdef\pydata@schema{}}
% \end{macrocode}
% \end{macro}
%
@@ -1111,7 +1165,7 @@
\ifbool{pydata@bufferhaskey}%
{\pydata@error{Cannot buffer a key when waiting for a value}}{}%
\expandafter\stepcounter\expandafter{\pydata@bufferlengthname}%
- \expandafter\edef\csname
+ \expandafter\xdef\csname
\pydata@bufferlinename\expandafter\arabic\expandafter{\pydata@bufferlengthname}%
\endcsname{%
\pydata@quotestr{#1}:%
@@ -1140,7 +1194,7 @@
\ifbool{pydata@bufferhaskey}%
{}{\pydata@error{Cannot buffer value when waiting for a key}}%
\expandafter\stepcounter\expandafter{\pydata@bufferlengthname}%
- \expandafter\edef\csname
+ \expandafter\xdef\csname
\pydata@bufferlinename\expandafter\arabic\expandafter{\pydata@bufferlengthname}%
\endcsname{%
\pydata@quotestr{#1},%
@@ -1150,7 +1204,7 @@
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\pydatawritekeyvalue, \pydatabufferkeyvalue}
+% \begin{macro}{\pydatawritekeyvalue, \pydatawritekeyedefvalue, \pydatabufferkeyvalue, \pydatabufferkeyedefvalue}
% Write a key and a single-line value to file simultaneously, or append them to the buffer.
% \begin{macrocode}
\begingroup
@@ -1166,6 +1220,13 @@
\immediate\write\pydata@filehandle{%
\pydata@quotestr{#1}: \pydata@quotestr{#2},%
}}
+\gdef\pydatawritekeyedefvalue{%
+ \FVExtraReadVArg{\FVExtraDetokenizeVArg{\pydatawritekeyedefvalue@i}}}
+\gdef\pydatawritekeyedefvalue@i#1#2{%
+ \edef\pydata@tmp{#2}%
+ \expandafter\pydatawritekeyedefvalue@ii\expandafter{\pydata@tmp}{#1}}
+\gdef\pydatawritekeyedefvalue@ii#1#2{%
+ \FVExtraDetokenizeVArg{\pydatawritekeyvalue@ii{#2}}{#1}}
\gdef\pydatabufferkeyvalue{%
\FVExtraReadVArg{\FVExtraDetokenizeVArg{\pydatabufferkeyvalue@i}}}
\gdef\pydatabufferkeyvalue@i#1{%
@@ -1174,11 +1235,18 @@
\ifbool{pydata@bufferhaskey}%
{\pydata@error{Cannot buffer a key when waiting for a value}}{}%
\expandafter\stepcounter\expandafter{\pydata@bufferlengthname}%
- \expandafter\edef\csname
+ \expandafter\xdef\csname
\pydata@bufferlinename\expandafter\arabic\expandafter{\pydata@bufferlengthname}%
\endcsname{%
\pydata@quotestr{#1}: \pydata@quotestr{#2},%
}}
+\gdef\pydatabufferkeyedefvalue{%
+ \FVExtraReadVArg{\FVExtraDetokenizeVArg{\pydatabufferkeyedefvalue@i}}}
+\gdef\pydatabufferkeyedefvalue@i#1#2{%
+ \edef\pydata@tmp{#2}%
+ \expandafter\pydatabufferkeyedefvalue@ii\expandafter{\pydata@tmp}{#1}}
+\gdef\pydatabufferkeyedefvalue@ii#1#2{%
+ \FVExtraDetokenizeVArg{\pydatabufferkeyvalue@ii{#2}}{#1}}
\endgroup
% \end{macrocode}
% \end{macro}
@@ -1209,7 +1277,7 @@
\ifbool{pydata@bufferhaskey}%
{}{\pydata@error{Cannot buffer value when waiting for a key}}%
\expandafter\stepcounter\expandafter{\pydata@bufferlengthname}%
- \expandafter\edef\csname
+ \expandafter\xdef\csname
\pydata@bufferlinename\expandafter\arabic\expandafter{\pydata@bufferlengthname}%
\endcsname{%
\pydata@mlstropen
@@ -1218,7 +1286,7 @@
\ifbool{pydata@bufferhaskey}%
{}{\pydata@error{Cannot buffer value when waiting for a key}}%
\expandafter\stepcounter\expandafter{\pydata@bufferlengthname}%
- \expandafter\edef\csname
+ \expandafter\xdef\csname
\pydata@bufferlinename\expandafter\arabic\expandafter{\pydata@bufferlengthname}%
\endcsname{%
\pydata@escstrtext{#1}%
@@ -1227,7 +1295,7 @@
\ifbool{pydata@bufferhaskey}%
{}{\pydata@error{Cannot buffer value when waiting for a key}}%
\expandafter\stepcounter\expandafter{\pydata@bufferlengthname}%
- \expandafter\edef\csname
+ \expandafter\xdef\csname
\pydata@bufferlinename\expandafter\arabic\expandafter{\pydata@bufferlengthname}%
\endcsname{%
\pydata@mlstrclose,%
@@ -1252,21 +1320,9 @@
% \begin{macrocode}
\newenvironment{pydatabuffermlvalue}%
{\VerbatimEnvironment
- \begin{VerbatimBuffer}[buffername=pydata@tmpbuffer, globalbuffer=true]}%
- {\end{VerbatimBuffer}}
-\AfterEndEnvironment{pydatabuffermlvalue}{%
\pydatabuffermlvaluestart
- \setcounter{pydata@bufferindex}{1}%
- \loop\unless\ifnum\value{pydata@bufferindex}>\value{pydata@tmpbufferlength}\relax
- \expandafter\let\expandafter\pydata@tmpbufferline
- \csname pydata@tmpbufferline\arabic{pydata@bufferindex}\endcsname
- \expandafter\let
- \csname pydata@tmpbufferline\arabic{pydata@bufferindex}\endcsname\pydata@undefined
- \expandafter\pydatabuffermlvalueline\expandafter{\pydata@tmpbufferline}%
- \stepcounter{pydata@bufferindex}%
- \repeat
- \setcounter{pydata@tmpbufferlength}{0}%
- \setcounter{pydata@bufferindex}{0}%
+ \begin{VerbatimBuffer}[bufferer=\pydatabuffermlvalueline]}%
+ {\end{VerbatimBuffer}%
\pydatabuffermlvalueend}
% \end{macrocode}
% \end{macro}