texlive[71283] Master/texmf-dist: latex2pydata (18may24)

commits+karl at tug.org commits+karl at tug.org
Sat May 18 00:15:18 CEST 2024


Revision: 71283
          https://tug.org/svn/texlive?view=revision&revision=71283
Author:   karl
Date:     2024-05-18 00:15:18 +0200 (Sat, 18 May 2024)
Log Message:
-----------
latex2pydata (18may24)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/latex/latex2pydata/README
    trunk/Master/texmf-dist/doc/latex/latex2pydata/latex2pydata.pdf
    trunk/Master/texmf-dist/source/latex/latex2pydata/latex2pydata.dtx
    trunk/Master/texmf-dist/source/latex/latex2pydata/latex2pydata.ins
    trunk/Master/texmf-dist/tex/latex/latex2pydata/latex2pydata.sty

Added Paths:
-----------
    trunk/Master/texmf-dist/doc/latex/latex2pydata/CHANGELOG.md

Added: trunk/Master/texmf-dist/doc/latex/latex2pydata/CHANGELOG.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/latex2pydata/CHANGELOG.md	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/latex/latex2pydata/CHANGELOG.md	2024-05-17 22:15:18 UTC (rev 71283)
@@ -0,0 +1,39 @@
+# Changelog — latex2pydata LaTeX package
+
+
+## v0.2.0 (2024-05-16)
+
+*  Operations on file handles, file names, and buffers are now global.
+   This prevents errors due to groups.
+
+*  `\pydatasetfilehandle`, `\pydatareleasefilehandle`, `\pydatasetfilename`,
+   and `\pydataclosefilename` are redesigned to deal with cases where the same
+   file is opened, written, closed, and then later reopened and overwritten.
+   `\pydatasetfilename` now reuses file handles when the same file is
+   opened and closed multiple times.  `\pydataclosefilename` no longer
+   attempts to close files `\AtEndDocument`, since that can interfere with
+   files that need to remain open as long as possible.
+
+*  Added new commands `\pydatawritekeyedefvalue` and
+   `\pydatabufferkeyedefvalue`.  These `\edef` the value before interpreting
+   it as verbatim text.
+
+*  Simplified implementation of `pydatabuffermlvalue` environment, based on
+   latest `fvextra`.
+
+*  Added error messages for unknown file handles and file names.
+
+*  Added additional state and data checks in `\pydatawritebuffer`.
+
+*  Added documentation for `\pydatawritemlvaluestart`,
+   `\pydatawritemlvalueline`, `\pydatawritemlvalueend`,
+   `\pydatabuffermlvaluestart`, `\pydatabuffermlvalueline`,
+   `\pydatabuffermlvalueend`.
+
+*  Updated `tcblisting` usage in docs for compatibility with the latest
+   `tcolorbox`.
+
+
+## v0.1.0 (2023-11-19)
+
+*  Initial release.


Property changes on: trunk/Master/texmf-dist/doc/latex/latex2pydata/CHANGELOG.md
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/texmf-dist/doc/latex/latex2pydata/README
===================================================================
--- trunk/Master/texmf-dist/doc/latex/latex2pydata/README	2024-05-17 22:15:02 UTC (rev 71282)
+++ trunk/Master/texmf-dist/doc/latex/latex2pydata/README	2024-05-17 22:15:18 UTC (rev 71283)
@@ -2,7 +2,7 @@
 
 Author:  Geoffrey M. Poore
 License:  LPPL v1.3c or later
-Development:  https://github.com/gpoore/latex2pydata_tex
+Development:  https://github.com/gpoore/latex2pydata/tree/main/latex
 
 
 latex2pydata is a LaTeX package for writing data to file using Python literal
@@ -9,7 +9,8 @@
 syntax (https://docs.python.org/3/reference/lexical_analysis.html#literals).
 The data may be loaded safely in Python using the ast.literal_eval() function
 (https://docs.python.org/3/library/ast.html#ast.literal_eval) or the
-latex2pydata Python package https://github.com/gpoore/latex2pydata_py).
+latex2pydata Python package
+https://github.com/gpoore/latex2pydata/tree/main/python).
 
 The top-level data structure can be configured as either a Python dict or a
 list of dicts.  Within dicts, all keys and values are written to file as
@@ -20,10 +21,10 @@
 
 The data is suitable for direct loading in Python with ast.literal_eval().
 It is also possible to load data using the latex2pydata Python package
-(https://github.com/gpoore/latex2pydata_py).  This functions as a wrapper for
-ast.literal_eval().  The package requires all keys to match the regex
-"[A-Za-z_][0-9A-Za-z_]*".  Periods in keys are interpreted as key paths and
-indicate sub-dicts.  For example, the key path "main.sub" represents a key
+(https://github.com/gpoore/latex2pydata/tree/main/python).  This functions as
+a wrapper for ast.literal_eval().  The package requires all keys to match the
+regex "[A-Za-z_][0-9A-Za-z_]*".  Periods in keys are interpreted as key paths
+and indicate sub-dicts.  For example, the key path "main.sub" represents a key
 "main" in the main dict that maps to a sub-dict containing a key "sub".  The
 Python package supports the schema features provided by the LaTeX package, so
 that data types other than dicts of strings are possible.

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

Modified: trunk/Master/texmf-dist/source/latex/latex2pydata/latex2pydata.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/latex2pydata/latex2pydata.dtx	2024-05-17 22:15:02 UTC (rev 71282)
+++ trunk/Master/texmf-dist/source/latex/latex2pydata/latex2pydata.dtx	2024-05-17 22:15:18 UTC (rev 71283)
@@ -1,6 +1,6 @@
 % \iffalse meta-comment
 %
-% Copyright (C) 2023 by Geoffrey M. Poore <gpoore at gmail.com>
+% Copyright (C) 2023-2024 by Geoffrey M. Poore <gpoore at 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,24 +67,8 @@
 
 \usepackage{tcolorbox}
 \tcbuselibrary{listings}
-% strip leading percent symbols
-\def\tcbverbatimwrite#1{%
-  \@bsphack
-  \tcb at set@verbatim at finish%
-  \tcb at allocate@tcb at out%
-  \immediate\openout\tcb at out #1
-  \tcb at verbatim@begin at hook%
-  \let\do\@makeother\dospecials
-  \tcb at verbatim@change at percent\catcode`\^^M\active \catcode`\^^I=12
-  \def\verbatim at processline{%
-    \immediate\write\tcb at out
-      {\expandafter\@gobble\the\verbatim at line}}%
-  \verbatim at start}%
+\tcbset{verbatim ignore percent}
 
-% fix redefinition by tcolorbox
-\def\verbatim at processline{%
-  \expandafter\check at percent\the\verbatim at line\par}
-
 \usepackage{hyperref}
 \hypersetup{
   pdftitle=The latex2pydata package: write key-value data to file in Python literal format,
@@ -248,27 +232,11 @@
 
 \edef\hashchar{\string#}
 
-\newcommand{\changestext}{}
-\NewEnviron{changelog}[2]{%
-    \g at addto@macro\changestext{\item[#1] (#2) \begin{itemize}}%
-    \expandafter\g at addto@macro\expandafter\changestext\expandafter{\BODY}%
-    \g at 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 at 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 at 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,8 +434,14 @@
 % \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.
 % \item Multiple adjacent spaces will be collapsed into a single space.
@@ -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,12 +461,23 @@
 % \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}
 %
 % Key-value data can be written to file once a dict is opened with |\pydatawritedictopen|. It is also possible to accumulate key-value data in a ``buffer.''  This is convenient when the data serves as input to an external program that generates cached content.  Buffered data can be hashed in memory without being written to file, so the existence of cached content can be checked efficiently.
@@ -498,7 +484,9 @@
 %
 % 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}
 %
 % \DescMacro{\pydatasetbuffername\marg{buffername}}
@@ -538,16 +526,24 @@
 % \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}
 %
 % \section{Implementation}
@@ -582,7 +578,7 @@
 %    \begin{macrocode}
 \RequirePackage{etoolbox}
 \RequirePackage{fvextra}
-\IfPackageAtLeastTF{fvextra}{2023/11/19}%
+\IfPackageAtLeastTF{fvextra}{2024/05/16}%
  {}{\pydata at error{package fvextra is outdated; upgrade to the latest version}}
 \RequirePackage{pdftexcmds}
 %    \end{macrocode}
@@ -599,15 +595,43 @@
 % \end{macro}
 %
 %
+% \begin{macro}{\pydata at newglobalbool, \pydata at 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 at gnewif#1{%
+  \count@\escapechar
+  \escapechar\m at ne
+  \global\let#1\iffalse
+  \pydata at gif#1\iftrue
+  \pydata at gif#1\iffalse
+  \escapechar\count@}
+\def\pydata at gif#1#2{%
+  \expandafter\gdef\csname
+    \expandafter\@gobbletwo\string#1\expandafter\@gobbletwo\string#2\endcsname
+     {\global\let#1#2}}
+\newrobustcmd*{\pydata at newglobalbool}[1]{%
+  \begingroup
+  \let\newif\pydata at gnewif
+  \newbool{#1}%
+  \endgroup}
+\newrobustcmd*{\pydata at provideglobalbool}[1]{%
+  \begingroup
+  \let\newif\pydata at 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 at 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 at canwrite}
+\pydata at newglobalbool{pydata at canwrite}
 %    \end{macrocode}
 % \end{macro}
 %
@@ -614,7 +638,7 @@
 % \begin{macro}{pydata at hasmeta}
 % Whether metadata was written.  Metadata is a \code{dict[str, str | dict[str, str]]}.
 %    \begin{macrocode}
-\newbool{pydata at hasmeta}
+\pydata at newglobalbool{pydata at hasmeta}
 %    \end{macrocode}
 % \end{macro}
 %
@@ -621,7 +645,7 @@
 % \begin{macro}{pydata at 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 at topexists}
+\pydata at newglobalbool{pydata at topexists}
 %    \end{macrocode}
 % \end{macro}
 %
@@ -628,7 +652,7 @@
 % \begin{macro}{pydata at topislist}
 % Whether the top-level data structure is a list.
 %    \begin{macrocode}
-\newbool{pydata at topislist}
+\pydata at newglobalbool{pydata at topislist}
 %    \end{macrocode}
 % \end{macro}
 %
@@ -635,7 +659,7 @@
 % \begin{macro}{pydata at indict}
 % Whether a dict has been opened.
 %    \begin{macrocode}
-\newbool{pydata at indict}
+\pydata at newglobalbool{pydata at indict}
 %    \end{macrocode}
 % \end{macro}
 %
@@ -642,12 +666,12 @@
 % \begin{macro}{pydata at haskey}
 % Whether a key has been written (waiting for a value).
 %    \begin{macrocode}
-\newbool{pydata at haskey}
+\pydata at newglobalbool{pydata at haskey}
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}{\pydata at fhstartstate, \pydata at 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 at fhstartstate, \pydata at fhstopstate, \pydata at 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 at fhstartstate#1{%
   \expandafter\pydata at fhstartstate@i\expandafter{\number#1}}
@@ -659,10 +683,13 @@
     \booltrue{pydata at fhnewstate}%
   \fi
   \def\do##1{%
-    \providebool{pydata@##1@#1}%
+    \pydata at provideglobalbool{pydata@##1@#1}%
     \ifbool{pydata@##1@#1}{\booltrue{pydata@##1}}{\boolfalse{pydata@##1}}}%
   \docsvlist{canwrite, hasmeta, topexists, topislist, indict, haskey}%
-  \ifbool{pydata at fhnewstate}{\booltrue{pydata at canwrite}{}}{}}
+  \ifbool{pydata at fhnewstate}%
+   {\booltrue{pydata at canwrite}}{}%
+  \ifbool{pydata at fhisreleased@#1}%
+   {\boolfalse{pydata at fhisreleased@#1}\booltrue{pydata at canwrite}}{}}
 \def\pydata at fhstopstate#1{%
   \expandafter\pydata at fhstopstate@i\expandafter{\number#1}}
 \def\pydata at fhstopstate@i#1{%
@@ -672,6 +699,12 @@
       \boolfalse{pydata@##1}}%
     \docsvlist{canwrite, hasmeta, topexists, topislist, indict, haskey}%
   \fi}
+\def\pydata at fhresetstate#1{%
+  \expandafter\pydata at fhresetstate@i\expandafter{\number#1}}
+\def\pydata at 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 at bufferhaskey|.  Inconsistent state is avoided by requiring that |\pydatasetbuffername| can only be invoked when |pydata at bufferhaskey| is false.
 %    \begin{macrocode}
-\newbool{pydata at bufferhaskey}
+\pydata at newglobalbool{pydata at 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 at error{Missing file handle}%
+  \fi
   \ifx\pydata at filehandle\relax
+  \else\ifx\pydata at filehandle#1\relax
   \else
     \pydata at fhstopstate{\pydata at filehandle}%
-  \fi
-  \let\pydata at filehandle#1\relax
-  \pydata at fhstartstate{#1}}
+  \fi\fi
+  \ifx\pydata at filehandle#1\relax
+  \else
+    \global\let\pydata at filehandle#1\relax
+    \pydata at provideglobalbool{pydata at fhisreleased@\number#1}%
+    \pydata at fhstartstate{#1}%
+  \fi}
 \def\pydatareleasefilehandle#1{%
-  \ifx\pydata at filehandle\relax
+  \ifcsname ifpydata at canwrite@\number#1\endcsname
   \else
-    \ifx\pydata at filehandle#1\relax
-      \pydata at fhstopstate{#1}%
-      \let\pydata at filehandle\relax
-    \fi
+    \pydata at error{Unknown file handle #1}%
   \fi
-  \ifcsname ifpydata at canwrite@\number#1\endcsname
-    \ifbool{pydata at canwrite@\number#1}%
-     {\ifbool{pydata at haskey@\number#1}%
-       {\pydata at error{Incomplete data: key is waiting for value}}{}%
-      \ifbool{pydata at indict@\number#1}%
-       {\pydata at error{Incomplete data: dict is not closed}}{}%
-      \ifbool{pydata at topislist@\number#1}%
-       {\pydata at error{Incomplete data: list is not closed}}{}}%
-    {}%
-  \fi}
+  \ifx\pydata at filehandle#1\relax
+    \pydata at fhstopstate{#1}%
+    \global\let\pydata at filehandle\relax
+  \fi
+  \ifbool{pydata at canwrite@\number#1}%
+   {\ifbool{pydata at haskey@\number#1}%
+     {\pydata at error{Incomplete data: key is waiting for value}}{}%
+    \ifbool{pydata at indict@\number#1}%
+     {\pydata at error{Incomplete data: dict is not closed}}{}%
+    \ifbool{pydata at topislist@\number#1}%
+     {\pydata at error{Incomplete data: list is not closed}}{}}%
+   {}%
+  \pydata at fhresetstate{#1}%
+  \booltrue{pydata at 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 at error{Missing filename}%
+  \fi
   \ifcsname pydata at fh@#1\endcsname
   \else
     \expandafter\newwrite\csname pydata at fh@#1\endcsname
-    \expandafter\immediate\expandafter\openout\csname pydata at fh@#1\endcsname=#1\relax
-    \AtEndDocument{\pydataclosefilename{#1}}%
   \fi
+  \pydata at provideglobalbool{pydata at fileisopen@#1}%
+  \ifbool{pydata at fileisopen@#1}%
+   {}%
+   {\expandafter\immediate\expandafter\openout\csname pydata at fh@#1\endcsname=#1\relax
+    \booltrue{pydata at fileisopen@#1}}%
   \expandafter\pydatasetfilehandle\expandafter{\csname pydata at fh@#1\endcsname}}
 \def\pydataclosefilename#1{%
   \ifcsname pydata at fh@#1\endcsname
-    \expandafter\pydatareleasefilehandle\expandafter{\csname pydata at fh@#1\endcsname}%
-    \expandafter\immediate\expandafter\closeout\csname pydata at fh@#1\endcsname
-    \expandafter\let\csname pydata at fh@#1\endcsname\pydata at undefined
+    \ifbool{pydata at fileisopen@#1}%
+     {\expandafter\pydatareleasefilehandle\expandafter{\csname pydata at fh@#1\endcsname}%
+      \expandafter\immediate\expandafter\closeout\csname pydata at fh@#1\endcsname
+      \boolfalse{pydata at fileisopen@#1}}%
+     {}%
+  \else
+    \pydata at error{Unknown file name "#1"}%
   \fi}
 %    \end{macrocode}
 % \end{macro}
@@ -779,14 +831,13 @@
   \ifbool{pydata at bufferhaskey}%
    {\pydata at error{Cannot change buffers when a buffered key is waiting for a value}}%
    {}%
-  \def\pydata at buffername{#1}%
-  \def\pydata at bufferlinename{#1line}%
-  \def\pydata at bufferlengthname{#1length}%
+  \gdef\pydata at buffername{#1}%
+  \gdef\pydata at bufferlinename{#1line}%
+  \gdef\pydata at bufferlengthname{#1length}%
   \ifcsname c@\pydata at bufferlengthname\endcsname
   \else
     \expandafter\newcounter\expandafter{\pydata at bufferlengthname}%
-  \fi
-  \expandafter\setcounter\expandafter{\pydata at bufferlengthname}{0}}
+  \fi}
 \pydatasetbuffername{pydata at defaultbuffer}
 %    \end{macrocode}
 % \end{macro}
@@ -798,7 +849,10 @@
   \ifnum\expandafter\value\expandafter{\pydata at bufferlengthname}<1\relax
     \pydata at error{Cannot write empty buffer}%
   \fi
-  \ifbool{pydata at indict}{}{\pydata at error{Cannot write a buffer unless in a dict}}%
+  \pydata at checkfilehandle
+  \ifbool{pydata at indict}{}{\pydata at error{Cannot write buffer unless in a dict}}%
+  \ifbool{pydata at haskey}%
+   {\pydata at error{Cannot write buffer when file has a key waiting for a value}}{}%
   \ifbool{pydata at bufferhaskey}%
    {\pydata at error{Cannot write buffer when a buffered key is waiting for a value}}{}%
   \setcounter{pydata at bufferindex}{1}%
@@ -824,7 +878,7 @@
   \fi
   \setcounter{pydata at bufferindex}{1}%
   \loop\unless\ifnum\value{pydata at bufferindex}>\value{#1length}\relax
-    \expandafter\let
+    \expandafter\global\expandafter\let
       \csname#1line\arabic{pydata at bufferindex}\endcsname\pydata at undefined
     \stepcounter{pydata at bufferindex}%
   \repeat
@@ -948,7 +1002,7 @@
   \else
     \pydata at error{Invalid schema missing setting #1}%
   \fi
-  \def\pydata at schemamissing{#1}}
+  \gdef\pydata at 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 at schema{}}
+  \gdef\pydata at schema{}}
 %    \end{macrocode}
 % \end{macro}
 %
@@ -1111,7 +1165,7 @@
   \ifbool{pydata at bufferhaskey}%
    {\pydata at error{Cannot buffer a key when waiting for a value}}{}%
   \expandafter\stepcounter\expandafter{\pydata at bufferlengthname}%
-  \expandafter\edef\csname
+  \expandafter\xdef\csname
     \pydata at bufferlinename\expandafter\arabic\expandafter{\pydata at bufferlengthname}%
     \endcsname{%
       \pydata at quotestr{#1}:%
@@ -1140,7 +1194,7 @@
   \ifbool{pydata at bufferhaskey}%
    {}{\pydata at error{Cannot buffer value when waiting for a key}}%
   \expandafter\stepcounter\expandafter{\pydata at bufferlengthname}%
-  \expandafter\edef\csname
+  \expandafter\xdef\csname
     \pydata at bufferlinename\expandafter\arabic\expandafter{\pydata at bufferlengthname}%
     \endcsname{%
       \pydata at 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 at filehandle{%
     \pydata at quotestr{#1}: \pydata at quotestr{#2},%
   }}
+\gdef\pydatawritekeyedefvalue{%
+  \FVExtraReadVArg{\FVExtraDetokenizeVArg{\pydatawritekeyedefvalue at i}}}
+\gdef\pydatawritekeyedefvalue at i#1#2{%
+  \edef\pydata at tmp{#2}%
+  \expandafter\pydatawritekeyedefvalue at ii\expandafter{\pydata at tmp}{#1}}
+\gdef\pydatawritekeyedefvalue at ii#1#2{%
+  \FVExtraDetokenizeVArg{\pydatawritekeyvalue at ii{#2}}{#1}}
 \gdef\pydatabufferkeyvalue{%
   \FVExtraReadVArg{\FVExtraDetokenizeVArg{\pydatabufferkeyvalue at i}}}
 \gdef\pydatabufferkeyvalue at i#1{%
@@ -1174,11 +1235,18 @@
   \ifbool{pydata at bufferhaskey}%
    {\pydata at error{Cannot buffer a key when waiting for a value}}{}%
   \expandafter\stepcounter\expandafter{\pydata at bufferlengthname}%
-  \expandafter\edef\csname
+  \expandafter\xdef\csname
     \pydata at bufferlinename\expandafter\arabic\expandafter{\pydata at bufferlengthname}%
     \endcsname{%
       \pydata at quotestr{#1}: \pydata at quotestr{#2},%
     }}
+\gdef\pydatabufferkeyedefvalue{%
+  \FVExtraReadVArg{\FVExtraDetokenizeVArg{\pydatabufferkeyedefvalue at i}}}
+\gdef\pydatabufferkeyedefvalue at i#1#2{%
+  \edef\pydata at tmp{#2}%
+  \expandafter\pydatabufferkeyedefvalue at ii\expandafter{\pydata at tmp}{#1}}
+\gdef\pydatabufferkeyedefvalue at ii#1#2{%
+  \FVExtraDetokenizeVArg{\pydatabufferkeyvalue at ii{#2}}{#1}}
 \endgroup
 %    \end{macrocode}
 % \end{macro}
@@ -1209,7 +1277,7 @@
   \ifbool{pydata at bufferhaskey}%
    {}{\pydata at error{Cannot buffer value when waiting for a key}}%
   \expandafter\stepcounter\expandafter{\pydata at bufferlengthname}%
-  \expandafter\edef\csname
+  \expandafter\xdef\csname
     \pydata at bufferlinename\expandafter\arabic\expandafter{\pydata at bufferlengthname}%
     \endcsname{%
       \pydata at mlstropen
@@ -1218,7 +1286,7 @@
   \ifbool{pydata at bufferhaskey}%
    {}{\pydata at error{Cannot buffer value when waiting for a key}}%
   \expandafter\stepcounter\expandafter{\pydata at bufferlengthname}%
-  \expandafter\edef\csname
+  \expandafter\xdef\csname
     \pydata at bufferlinename\expandafter\arabic\expandafter{\pydata at bufferlengthname}%
     \endcsname{%
       \pydata at escstrtext{#1}%
@@ -1227,7 +1295,7 @@
   \ifbool{pydata at bufferhaskey}%
    {}{\pydata at error{Cannot buffer value when waiting for a key}}%
   \expandafter\stepcounter\expandafter{\pydata at bufferlengthname}%
-  \expandafter\edef\csname
+  \expandafter\xdef\csname
     \pydata at bufferlinename\expandafter\arabic\expandafter{\pydata at bufferlengthname}%
     \endcsname{%
       \pydata at mlstrclose,%
@@ -1252,21 +1320,9 @@
 %    \begin{macrocode}
 \newenvironment{pydatabuffermlvalue}%
  {\VerbatimEnvironment
-  \begin{VerbatimBuffer}[buffername=pydata at tmpbuffer, globalbuffer=true]}%
- {\end{VerbatimBuffer}}
-\AfterEndEnvironment{pydatabuffermlvalue}{%
   \pydatabuffermlvaluestart
-  \setcounter{pydata at bufferindex}{1}%
-  \loop\unless\ifnum\value{pydata at bufferindex}>\value{pydata at tmpbufferlength}\relax
-    \expandafter\let\expandafter\pydata at tmpbufferline
-      \csname pydata at tmpbufferline\arabic{pydata at bufferindex}\endcsname
-    \expandafter\let
-      \csname pydata at tmpbufferline\arabic{pydata at bufferindex}\endcsname\pydata at undefined
-    \expandafter\pydatabuffermlvalueline\expandafter{\pydata at tmpbufferline}%
-    \stepcounter{pydata at bufferindex}%
-  \repeat
-  \setcounter{pydata at tmpbufferlength}{0}%
-  \setcounter{pydata at bufferindex}{0}%
+  \begin{VerbatimBuffer}[bufferer=\pydatabuffermlvalueline]}%
+ {\end{VerbatimBuffer}%
   \pydatabuffermlvalueend}
 %    \end{macrocode}
 % \end{macro}

Modified: trunk/Master/texmf-dist/source/latex/latex2pydata/latex2pydata.ins
===================================================================
--- trunk/Master/texmf-dist/source/latex/latex2pydata/latex2pydata.ins	2024-05-17 22:15:02 UTC (rev 71282)
+++ trunk/Master/texmf-dist/source/latex/latex2pydata/latex2pydata.ins	2024-05-17 22:15:18 UTC (rev 71283)
@@ -1,4 +1,4 @@
-%% Copyright (C) 2023 by Geoffrey M. Poore <gpoore at gmail.com>
+%% Copyright (C) 2023-2024 by Geoffrey M. Poore <gpoore at 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 @@
 
 This is a generated file.
 
-Copyright (C) 2023 by Geoffrey M. Poore <gpoore at gmail.com>
+Copyright (C) 2023-2024 by Geoffrey M. Poore <gpoore at gmail.com>
 --------------------------------------------------------------------------
 This work may be distributed and/or modified under the
 conditions of the LaTeX Project Public License, either version 1.3c

Modified: trunk/Master/texmf-dist/tex/latex/latex2pydata/latex2pydata.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/latex2pydata/latex2pydata.sty	2024-05-17 22:15:02 UTC (rev 71282)
+++ trunk/Master/texmf-dist/tex/latex/latex2pydata/latex2pydata.sty	2024-05-17 22:15:18 UTC (rev 71283)
@@ -8,7 +8,7 @@
 %% 
 %% This is a generated file.
 %% 
-%% Copyright (C) 2023 by Geoffrey M. Poore <gpoore at gmail.com>
+%% Copyright (C) 2023-2024 by Geoffrey M. Poore <gpoore at gmail.com>
 %% --------------------------------------------------------------------------
 %% This work may be distributed and/or modified under the
 %% conditions of the LaTeX Project Public License, either version 1.3c
@@ -20,7 +20,7 @@
 %% 
 \NeedsTeXFormat{LaTeX2e}[1999/12/01]
 \ProvidesPackage{latex2pydata}
-    [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]
 \def\pydata at error#1{%
   \PackageError{latex2pydata}{#1}{}%
   \batchmode\read -1 to \pydata at exitnow}
@@ -28,16 +28,37 @@
   \PackageWarning{latex2pydata}{#1}}
 \RequirePackage{etoolbox}
 \RequirePackage{fvextra}
-\IfPackageAtLeastTF{fvextra}{2023/11/19}%
+\IfPackageAtLeastTF{fvextra}{2024/05/16}%
  {}{\pydata at error{package fvextra is outdated; upgrade to the latest version}}
 \RequirePackage{pdftexcmds}
 \def\pydata at empty{}
-\newbool{pydata at canwrite}
-\newbool{pydata at hasmeta}
-\newbool{pydata at topexists}
-\newbool{pydata at topislist}
-\newbool{pydata at indict}
-\newbool{pydata at haskey}
+\def\pydata at gnewif#1{%
+  \count@\escapechar
+  \escapechar\m at ne
+  \global\let#1\iffalse
+  \pydata at gif#1\iftrue
+  \pydata at gif#1\iffalse
+  \escapechar\count@}
+\def\pydata at gif#1#2{%
+  \expandafter\gdef\csname
+    \expandafter\@gobbletwo\string#1\expandafter\@gobbletwo\string#2\endcsname
+     {\global\let#1#2}}
+\newrobustcmd*{\pydata at newglobalbool}[1]{%
+  \begingroup
+  \let\newif\pydata at gnewif
+  \newbool{#1}%
+  \endgroup}
+\newrobustcmd*{\pydata at provideglobalbool}[1]{%
+  \begingroup
+  \let\newif\pydata at gnewif
+  \providebool{#1}%
+  \endgroup}
+\pydata at newglobalbool{pydata at canwrite}
+\pydata at newglobalbool{pydata at hasmeta}
+\pydata at newglobalbool{pydata at topexists}
+\pydata at newglobalbool{pydata at topislist}
+\pydata at newglobalbool{pydata at indict}
+\pydata at newglobalbool{pydata at haskey}
 \def\pydata at fhstartstate#1{%
   \expandafter\pydata at fhstartstate@i\expandafter{\number#1}}
 \newbool{pydata at fhnewstate}
@@ -48,10 +69,13 @@
     \booltrue{pydata at fhnewstate}%
   \fi
   \def\do##1{%
-    \providebool{pydata@##1@#1}%
+    \pydata at provideglobalbool{pydata@##1@#1}%
     \ifbool{pydata@##1@#1}{\booltrue{pydata@##1}}{\boolfalse{pydata@##1}}}%
   \docsvlist{canwrite, hasmeta, topexists, topislist, indict, haskey}%
-  \ifbool{pydata at fhnewstate}{\booltrue{pydata at canwrite}{}}{}}
+  \ifbool{pydata at fhnewstate}%
+   {\booltrue{pydata at canwrite}}{}%
+  \ifbool{pydata at fhisreleased@#1}%
+   {\boolfalse{pydata at fhisreleased@#1}\booltrue{pydata at canwrite}}{}}
 \def\pydata at fhstopstate#1{%
   \expandafter\pydata at fhstopstate@i\expandafter{\number#1}}
 \def\pydata at fhstopstate@i#1{%
@@ -61,7 +85,13 @@
       \boolfalse{pydata@##1}}%
     \docsvlist{canwrite, hasmeta, topexists, topislist, indict, haskey}%
   \fi}
-\newbool{pydata at bufferhaskey}
+\def\pydata at fhresetstate#1{%
+  \expandafter\pydata at fhresetstate@i\expandafter{\number#1}}
+\def\pydata at fhresetstate@i#1{%
+  \def\do##1{%
+    \boolfalse{pydata@##1@#1}}%
+  \docsvlist{canwrite, hasmeta, topexists, topislist, indict, haskey}}
+\pydata at newglobalbool{pydata at bufferhaskey}
 \let\pydata at filehandle\relax
 \def\pydata at checkfilehandle{%
   \ifx\pydata at filehandle\relax
@@ -68,43 +98,62 @@
     \pydata at error{Undefined file handle; use \string\pydatasetfilehandle}%
   \fi}
 \def\pydatasetfilehandle#1{%
+  \if\relax\detokenize{#1}\relax
+    \pydata at error{Missing file handle}%
+  \fi
   \ifx\pydata at filehandle\relax
+  \else\ifx\pydata at filehandle#1\relax
   \else
     \pydata at fhstopstate{\pydata at filehandle}%
-  \fi
-  \let\pydata at filehandle#1\relax
-  \pydata at fhstartstate{#1}}
+  \fi\fi
+  \ifx\pydata at filehandle#1\relax
+  \else
+    \global\let\pydata at filehandle#1\relax
+    \pydata at provideglobalbool{pydata at fhisreleased@\number#1}%
+    \pydata at fhstartstate{#1}%
+  \fi}
 \def\pydatareleasefilehandle#1{%
-  \ifx\pydata at filehandle\relax
+  \ifcsname ifpydata at canwrite@\number#1\endcsname
   \else
-    \ifx\pydata at filehandle#1\relax
-      \pydata at fhstopstate{#1}%
-      \let\pydata at filehandle\relax
-    \fi
+    \pydata at error{Unknown file handle #1}%
   \fi
-  \ifcsname ifpydata at canwrite@\number#1\endcsname
-    \ifbool{pydata at canwrite@\number#1}%
-     {\ifbool{pydata at haskey@\number#1}%
-       {\pydata at error{Incomplete data: key is waiting for value}}{}%
-      \ifbool{pydata at indict@\number#1}%
-       {\pydata at error{Incomplete data: dict is not closed}}{}%
-      \ifbool{pydata at topislist@\number#1}%
-       {\pydata at error{Incomplete data: list is not closed}}{}}%
-    {}%
-  \fi}
+  \ifx\pydata at filehandle#1\relax
+    \pydata at fhstopstate{#1}%
+    \global\let\pydata at filehandle\relax
+  \fi
+  \ifbool{pydata at canwrite@\number#1}%
+   {\ifbool{pydata at haskey@\number#1}%
+     {\pydata at error{Incomplete data: key is waiting for value}}{}%
+    \ifbool{pydata at indict@\number#1}%
+     {\pydata at error{Incomplete data: dict is not closed}}{}%
+    \ifbool{pydata at topislist@\number#1}%
+     {\pydata at error{Incomplete data: list is not closed}}{}}%
+   {}%
+  \pydata at fhresetstate{#1}%
+  \booltrue{pydata at fhisreleased@\number#1}}
 \def\pydatasetfilename#1{%
+  \if\relax\detokenize{#1}\relax
+    \pydata at error{Missing filename}%
+  \fi
   \ifcsname pydata at fh@#1\endcsname
   \else
     \expandafter\newwrite\csname pydata at fh@#1\endcsname
-    \expandafter\immediate\expandafter\openout\csname pydata at fh@#1\endcsname=#1\relax
-    \AtEndDocument{\pydataclosefilename{#1}}%
   \fi
+  \pydata at provideglobalbool{pydata at fileisopen@#1}%
+  \ifbool{pydata at fileisopen@#1}%
+   {}%
+   {\expandafter\immediate\expandafter\openout\csname pydata at fh@#1\endcsname=#1\relax
+    \booltrue{pydata at fileisopen@#1}}%
   \expandafter\pydatasetfilehandle\expandafter{\csname pydata at fh@#1\endcsname}}
 \def\pydataclosefilename#1{%
   \ifcsname pydata at fh@#1\endcsname
-    \expandafter\pydatareleasefilehandle\expandafter{\csname pydata at fh@#1\endcsname}%
-    \expandafter\immediate\expandafter\closeout\csname pydata at fh@#1\endcsname
-    \expandafter\let\csname pydata at fh@#1\endcsname\pydata at undefined
+    \ifbool{pydata at fileisopen@#1}%
+     {\expandafter\pydatareleasefilehandle\expandafter{\csname pydata at fh@#1\endcsname}%
+      \expandafter\immediate\expandafter\closeout\csname pydata at fh@#1\endcsname
+      \boolfalse{pydata at fileisopen@#1}}%
+     {}%
+  \else
+    \pydata at error{Unknown file name "#1"}%
   \fi}
 \newcounter{pydata at bufferindex}
 \setcounter{pydata at bufferindex}{0}
@@ -112,20 +161,22 @@
   \ifbool{pydata at bufferhaskey}%
    {\pydata at error{Cannot change buffers when a buffered key is waiting for a value}}%
    {}%
-  \def\pydata at buffername{#1}%
-  \def\pydata at bufferlinename{#1line}%
-  \def\pydata at bufferlengthname{#1length}%
+  \gdef\pydata at buffername{#1}%
+  \gdef\pydata at bufferlinename{#1line}%
+  \gdef\pydata at bufferlengthname{#1length}%
   \ifcsname c@\pydata at bufferlengthname\endcsname
   \else
     \expandafter\newcounter\expandafter{\pydata at bufferlengthname}%
-  \fi
-  \expandafter\setcounter\expandafter{\pydata at bufferlengthname}{0}}
+  \fi}
 \pydatasetbuffername{pydata at defaultbuffer}
 \def\pydatawritebuffer{%
   \ifnum\expandafter\value\expandafter{\pydata at bufferlengthname}<1\relax
     \pydata at error{Cannot write empty buffer}%
   \fi
-  \ifbool{pydata at indict}{}{\pydata at error{Cannot write a buffer unless in a dict}}%
+  \pydata at checkfilehandle
+  \ifbool{pydata at indict}{}{\pydata at error{Cannot write buffer unless in a dict}}%
+  \ifbool{pydata at haskey}%
+   {\pydata at error{Cannot write buffer when file has a key waiting for a value}}{}%
   \ifbool{pydata at bufferhaskey}%
    {\pydata at error{Cannot write buffer when a buffered key is waiting for a value}}{}%
   \setcounter{pydata at bufferindex}{1}%
@@ -144,7 +195,7 @@
   \fi
   \setcounter{pydata at bufferindex}{1}%
   \loop\unless\ifnum\value{pydata at bufferindex}>\value{#1length}\relax
-    \expandafter\let
+    \expandafter\global\expandafter\let
       \csname#1line\arabic{pydata at bufferindex}\endcsname\pydata at undefined
     \stepcounter{pydata at bufferindex}%
   \repeat
@@ -211,7 +262,7 @@
   \else
     \pydata at error{Invalid schema missing setting #1}%
   \fi
-  \def\pydata at schemamissing{#1}}
+  \gdef\pydata at schemamissing{#1}}
 \pydatasetschemamissing{error}
 \begingroup
 \catcode`\:=12\relax
@@ -223,7 +274,7 @@
     \pydata at schema\pydata at quotestr{#1}: \pydata at quotestr{#2}, }}
 \endgroup
 \def\pydataclearschema{%
-  \def\pydata at schema{}}
+  \gdef\pydata at schema{}}
 \def\pydataclearmeta{%
   \pydatasetschemamissing{error}%
   \pydataclearschema}
@@ -322,7 +373,7 @@
   \ifbool{pydata at bufferhaskey}%
    {\pydata at error{Cannot buffer a key when waiting for a value}}{}%
   \expandafter\stepcounter\expandafter{\pydata at bufferlengthname}%
-  \expandafter\edef\csname
+  \expandafter\xdef\csname
     \pydata at bufferlinename\expandafter\arabic\expandafter{\pydata at bufferlengthname}%
     \endcsname{%
       \pydata at quotestr{#1}:%
@@ -345,7 +396,7 @@
   \ifbool{pydata at bufferhaskey}%
    {}{\pydata at error{Cannot buffer value when waiting for a key}}%
   \expandafter\stepcounter\expandafter{\pydata at bufferlengthname}%
-  \expandafter\edef\csname
+  \expandafter\xdef\csname
     \pydata at bufferlinename\expandafter\arabic\expandafter{\pydata at bufferlengthname}%
     \endcsname{%
       \pydata at quotestr{#1},%
@@ -365,6 +416,13 @@
   \immediate\write\pydata at filehandle{%
     \pydata at quotestr{#1}: \pydata at quotestr{#2},%
   }}
+\gdef\pydatawritekeyedefvalue{%
+  \FVExtraReadVArg{\FVExtraDetokenizeVArg{\pydatawritekeyedefvalue at i}}}
+\gdef\pydatawritekeyedefvalue at i#1#2{%
+  \edef\pydata at tmp{#2}%
+  \expandafter\pydatawritekeyedefvalue at ii\expandafter{\pydata at tmp}{#1}}
+\gdef\pydatawritekeyedefvalue at ii#1#2{%
+  \FVExtraDetokenizeVArg{\pydatawritekeyvalue at ii{#2}}{#1}}
 \gdef\pydatabufferkeyvalue{%
   \FVExtraReadVArg{\FVExtraDetokenizeVArg{\pydatabufferkeyvalue at i}}}
 \gdef\pydatabufferkeyvalue at i#1{%
@@ -373,11 +431,18 @@
   \ifbool{pydata at bufferhaskey}%
    {\pydata at error{Cannot buffer a key when waiting for a value}}{}%
   \expandafter\stepcounter\expandafter{\pydata at bufferlengthname}%
-  \expandafter\edef\csname
+  \expandafter\xdef\csname
     \pydata at bufferlinename\expandafter\arabic\expandafter{\pydata at bufferlengthname}%
     \endcsname{%
       \pydata at quotestr{#1}: \pydata at quotestr{#2},%
     }}
+\gdef\pydatabufferkeyedefvalue{%
+  \FVExtraReadVArg{\FVExtraDetokenizeVArg{\pydatabufferkeyedefvalue at i}}}
+\gdef\pydatabufferkeyedefvalue at i#1#2{%
+  \edef\pydata at tmp{#2}%
+  \expandafter\pydatabufferkeyedefvalue at ii\expandafter{\pydata at tmp}{#1}}
+\gdef\pydatabufferkeyedefvalue at ii#1#2{%
+  \FVExtraDetokenizeVArg{\pydatabufferkeyvalue at ii{#2}}{#1}}
 \endgroup
 \begingroup
 \catcode`\,=12\relax
@@ -401,7 +466,7 @@
   \ifbool{pydata at bufferhaskey}%
    {}{\pydata at error{Cannot buffer value when waiting for a key}}%
   \expandafter\stepcounter\expandafter{\pydata at bufferlengthname}%
-  \expandafter\edef\csname
+  \expandafter\xdef\csname
     \pydata at bufferlinename\expandafter\arabic\expandafter{\pydata at bufferlengthname}%
     \endcsname{%
       \pydata at mlstropen
@@ -410,7 +475,7 @@
   \ifbool{pydata at bufferhaskey}%
    {}{\pydata at error{Cannot buffer value when waiting for a key}}%
   \expandafter\stepcounter\expandafter{\pydata at bufferlengthname}%
-  \expandafter\edef\csname
+  \expandafter\xdef\csname
     \pydata at bufferlinename\expandafter\arabic\expandafter{\pydata at bufferlengthname}%
     \endcsname{%
       \pydata at escstrtext{#1}%
@@ -419,7 +484,7 @@
   \ifbool{pydata at bufferhaskey}%
    {}{\pydata at error{Cannot buffer value when waiting for a key}}%
   \expandafter\stepcounter\expandafter{\pydata at bufferlengthname}%
-  \expandafter\edef\csname
+  \expandafter\xdef\csname
     \pydata at bufferlinename\expandafter\arabic\expandafter{\pydata at bufferlengthname}%
     \endcsname{%
       \pydata at mlstrclose,%
@@ -434,21 +499,9 @@
 \AfterEndEnvironment{pydatawritemlvalue}{\pydatawritemlvalueend}
 \newenvironment{pydatabuffermlvalue}%
  {\VerbatimEnvironment
-  \begin{VerbatimBuffer}[buffername=pydata at tmpbuffer, globalbuffer=true]}%
- {\end{VerbatimBuffer}}
-\AfterEndEnvironment{pydatabuffermlvalue}{%
   \pydatabuffermlvaluestart
-  \setcounter{pydata at bufferindex}{1}%
-  \loop\unless\ifnum\value{pydata at bufferindex}>\value{pydata at tmpbufferlength}\relax
-    \expandafter\let\expandafter\pydata at tmpbufferline
-      \csname pydata at tmpbufferline\arabic{pydata at bufferindex}\endcsname
-    \expandafter\let
-      \csname pydata at tmpbufferline\arabic{pydata at bufferindex}\endcsname\pydata at undefined
-    \expandafter\pydatabuffermlvalueline\expandafter{\pydata at tmpbufferline}%
-    \stepcounter{pydata at bufferindex}%
-  \repeat
-  \setcounter{pydata at tmpbufferlength}{0}%
-  \setcounter{pydata at bufferindex}{0}%
+  \begin{VerbatimBuffer}[bufferer=\pydatabuffermlvalueline]}%
+ {\end{VerbatimBuffer}%
   \pydatabuffermlvalueend}
 %% \Finale
 \endinput



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