From da0bd16b0ba9d42d044af47137003788f0b7f773 Mon Sep 17 00:00:00 2001
From: Norbert Preining <norbert@preining.info>
Date: Sat, 24 Dec 2022 03:01:20 +0000
Subject: CTAN sync 202212240301

---
 .../contrib/pythonimmediate/pythonimmediate.sty    | 183 +++++++++++++++++++++
 1 file changed, 183 insertions(+)
 create mode 100644 macros/latex/contrib/pythonimmediate/pythonimmediate.sty

(limited to 'macros/latex/contrib/pythonimmediate/pythonimmediate.sty')

diff --git a/macros/latex/contrib/pythonimmediate/pythonimmediate.sty b/macros/latex/contrib/pythonimmediate/pythonimmediate.sty
new file mode 100644
index 0000000000..6ed72780bf
--- /dev/null
+++ b/macros/latex/contrib/pythonimmediate/pythonimmediate.sty
@@ -0,0 +1,183 @@
+% File: pythonimmediate.sty
+% Copyright 2022 user202729
+%
+% This work  may be  distributed and/or  modified under  the conditions  of the
+% LaTeX Project Public License (LPPL),  either version 1.3c  of this license or
+% (at your option) any later version.  The latest version of this license is in
+% the file:
+%
+%   http://www.latex-project.org/lppl.txt
+%
+% This work has the LPPL maintenance status `maintained'.
+% 
+% The Current Maintainer of this work is user202729.
+
+\ProvidesExplPackage{pythonimmediate}{2022/12/23}{0.0.0}{Library to run Python code}
+
+\RequirePackage{saveenv}
+\RequirePackage{currfile}
+\RequirePackage{l3keys2e}
+\RequirePackage{precattl}
+
+
+\cs_generate_variant:Nn \str_set:Nn {NV}
+\cs_generate_variant:Nn \str_if_eq:nnT {VnT}
+\cs_generate_variant:Nn \msg_error:nnn {nnV}
+\cs_generate_variant:Nn \str_if_eq:nnF {xnF}
+\cs_generate_variant:Nn \str_range:nnn {Vnn}
+\cs_generate_variant:Nn \str_if_eq:nnF {VnF}
+\cs_generate_variant:Nn \str_if_in:nnF {VnF}
+\cs_generate_variant:Nn \tl_build_put_right:Nn {NV}
+
+%\bench before~rescan.
+%\bench after~rescan.
+
+%\GenerateVariantsFile:n{\rescansynclastfilename}
+
+\str_set:Nn \_pythonimmediate_outputdir{.}
+\tl_set:Nn \_pythonimmediate_textopy_script_path{}
+\str_set:Nn \_pythonimmediate_mode{multiprocessing-network}
+\str_set:Nn \_pythonimmediate_python_executable{python3}
+\keys_define:nn{pythonimmediate}{
+	outputdir.tl_set_x:N=\_pythonimmediate_outputdir,
+	%outputdir.default:n={.},  % huh does not work?
+
+	mode.tl_set:N=\_pythonimmediate_mode,
+	scriptpath.tl_set_x:N=\_pythonimmediate_textopy_script_path,
+
+	python-executable.tl_set:N=\_pythonimmediate_python_executable,
+}
+\ProcessKeysOptions{pythonimmediate}
+
+\str_set:NV \_pythonimmediate_outputdir \_pythonimmediate_outputdir
+\str_set:NV \_pythonimmediate_mode \_pythonimmediate_mode
+\str_set:NV \_pythonimmediate_textopy_script_path \_pythonimmediate_textopy_script_path
+
+\msg_new:nnn {pythonimmediate} {shell-fail} {Shell~command~execution~failed!}
+\msg_new:nnn {pythonimmediate} {cannot-determine-script-path} {Cannot~determine~script~path!}
+\msg_new:nnn {pythonimmediate} {cannot-read-line} {Cannot~read~line!}
+\msg_new:nnn {pythonimmediate} {internal-error} {Internal~error!}
+\msg_new:nnn {pythonimmediate} {invalid-mode} {Invalid~mode:~'#1'.}
+
+
+\bool_new:N \_pythonimmediate_mode_multiprocessing_network
+\bool_new:N \_pythonimmediate_mode_unnamed_pipe
+\str_if_eq:VnT \_pythonimmediate_mode {multiprocessing-network} {\bool_set_true:N \_pythonimmediate_mode_multiprocessing_network}
+\str_if_eq:VnT \_pythonimmediate_mode {unnamed-pipe} {\bool_set_true:N \_pythonimmediate_mode_unnamed_pipe}
+\bool_if:nF {\_pythonimmediate_mode_multiprocessing_network || \_pythonimmediate_mode_unnamed_pipe} {
+    \msg_error:nnV {pythonimmediate} {invalid-mode} \_pythonimmediate_mode
+}
+
+% we need to persistently open the file anyway, so using LaTeX3 stream reference counting doesn't help
+\newread \_pythonimmediate_read_file
+
+
+
+
+%\bench before~setup.
+
+\begingroup
+	\endlinechar=-1~
+	\tl_if_empty:NT \_pythonimmediate_textopy_script_path {
+		% ======== first use kpsewhich to get the _pythonimmediate_script_path here ========
+		% (abuse \_pythonimmediate_read_file variable for this purpose)
+		\openin \_pythonimmediate_read_file=|"kpsewhich~ pythonimmediate_script_textopy.py"~
+		\readline \_pythonimmediate_read_file to \_pythonimmediate_textopy_script_path
+		%\bench after~get~textopy~path.
+		\ifeof \_pythonimmediate_read_file
+			\msg_error:nn {pythonimmediate} {cannot-determine-script-path}
+		\fi
+	}
+
+	\str_if_eq:xnF {\str_range:Vnn \_pythonimmediate_textopy_script_path {-10} {-1}} {textopy.py} {
+		\msg_error:nn {pythonimmediate} {cannot-determine-script-path}
+	}
+
+	\newwrite \_pythonimmediate_write_file
+
+	%\bench before~openin.
+
+	% ======== open persistent pipes to the child processes
+	\bool_if:NTF \_pythonimmediate_mode_unnamed_pipe {  % in this case make sure the pipe remains open...
+		\openin \_pythonimmediate_read_file=|"sleep~ 0.5s|\_pythonimmediate_python_executable \space \str_range:Vnn \_pythonimmediate_textopy_script_path {1} {-11} pytotex.py \space \_pythonimmediate_mode"~  % we must use the primitive here to use the pipe file path
+		% TODO sleep infinity causes some resource leak (the process will not exit after TeX exits). Need to fix
+		% but sleep too little might be problematic that it exits before the setup is done
+		% we can just assume machines are not that slow
+	} {
+		\openin \_pythonimmediate_read_file=|"\_pythonimmediate_python_executable \space \str_range:Vnn \_pythonimmediate_textopy_script_path {1} {-11} pytotex.py \space \_pythonimmediate_mode"~  % we must use the primitive here to use the pipe file path
+	}
+
+	%\bench after~openin.
+
+	%\bench before~openout.
+	\immediate\openout \_pythonimmediate_write_file=|"\_pythonimmediate_python_executable \space \_pythonimmediate_textopy_script_path \space \_pythonimmediate_mode"~
+	% both processes must be before the \readline above so that the 2 Python processes are started "in parallel"
+	%\bench after~openout.
+
+	\readline \_pythonimmediate_read_file to \_pythonimmediate_dummy_line  % endlinechar still -1
+	%\bench after~read~line.
+	\bool_if:NT \_pythonimmediate_mode_multiprocessing_network {
+		\str_if_eq:VnF \_pythonimmediate_dummy_line {listener-setup-done} {
+			\msg_error:nn {pythonimmediate} {cannot-read-line}
+		}
+	}
+	\bool_if:NT \_pythonimmediate_mode_unnamed_pipe {
+		\str_if_in:VnF \_pythonimmediate_dummy_line {pytotex_pid=} {
+			\msg_error:nn {pythonimmediate} {cannot-read-line}
+		}
+	}
+	\bool_if:NT \_pythonimmediate_mode_unnamed_pipe {
+		\immediate\write\_pythonimmediate_write_file {\_pythonimmediate_dummy_line}  % which contains pytotex's pid
+	}
+\endgroup
+
+%\bench after~setup.
+
+
+% read one block of \TeX\ code from Python, store into the specified variable
+% the block is delimited using |surround_delimiter()| in Python i.e. the first and last line are identical
+% new lines are represented with ^^J
+\cs_new_protected:Npn \_pythonimmediate_gread_block:N #1 {
+	\begingroup
+		\endlinechar=10~  % affect \readline
+		\readline \_pythonimmediate_read_file to \_pythonimmediate_delimiter
+
+		\tl_build_gbegin:N #1
+		\readline \_pythonimmediate_read_file to \_pythonimmediate_line
+
+		%\bench read~first~line.
+
+		\bool_do_until:nn {\tl_if_eq_p:NN \_pythonimmediate_delimiter \_pythonimmediate_line} {
+			\tl_build_gput_right:NV #1 \_pythonimmediate_line
+			\ifeof \_pythonimmediate_read_file
+				\msg_error:nn {pythonimmediate} {internal-error}
+			\fi
+			\readline \_pythonimmediate_read_file to \_pythonimmediate_line
+		}
+		\tl_build_gend:N #1
+	\endgroup
+}
+\cs_generate_variant:Nn \tl_build_gput_right:Nn {NV}
+
+\cs_new_protected:Npn \_pythonimmediate_read_block:N #1 {
+	\_pythonimmediate_gread_block:N \_pythonimmediate_block
+	\tl_set_eq:NN #1 \_pythonimmediate_block
+}
+
+
+% read one block of \TeX\ code from Python and |\scantokens|-run it
+% the content inside is the actual TeX code to be executed
+\cs_new_protected:Npn \_pythonimmediate_run_block: {
+	\_pythonimmediate_gread_block:N \_pythonimmediate_code
+	\begingroup
+		\newlinechar=10~
+		\expandafter
+	\endgroup
+	\scantokens \expandafter{\_pythonimmediate_code}
+}  % trick described in https://tex.stackexchange.com/q/640274 to scantokens the code with \newlinechar=10
+
+% bootstrap code
+%\bench before~bootstrap.
+\_pythonimmediate_run_block:
+%\bench after~bootstrap.
+
-- 
cgit v1.2.3

