summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/pythonimmediate/pythonimmediate.sty
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/contrib/pythonimmediate/pythonimmediate.sty')
-rw-r--r--macros/latex/contrib/pythonimmediate/pythonimmediate.sty157
1 files changed, 73 insertions, 84 deletions
diff --git a/macros/latex/contrib/pythonimmediate/pythonimmediate.sty b/macros/latex/contrib/pythonimmediate/pythonimmediate.sty
index 6ed72780bf..5a92dd88a6 100644
--- a/macros/latex/contrib/pythonimmediate/pythonimmediate.sty
+++ b/macros/latex/contrib/pythonimmediate/pythonimmediate.sty
@@ -1,5 +1,5 @@
% File: pythonimmediate.sty
-% Copyright 2022 user202729
+% Copyright 2022-2023 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
@@ -12,7 +12,7 @@
%
% The Current Maintainer of this work is user202729.
-\ProvidesExplPackage{pythonimmediate}{2022/12/23}{0.0.0}{Library to run Python code}
+\ProvidesExplPackage{pythonimmediate}{2023/01/09}{0.1.0}{Library to run Python code}
\RequirePackage{saveenv}
\RequirePackage{currfile}
@@ -29,110 +29,104 @@
\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.
+% some old commands e.g. \$, \^, \_, \~ require \set@display@protect to be robust.
+% ~ needs to be redefined directly.
+\precattl_exec:n {
+ \cs_new_protected:Npn \_pythonimmediate_begingroup_setup_estr: {
+ \begingroup
+ \escapechar=-1~
+ \cC{set@display@protect}
+ \let \cA\~ \relax
+ }
+}
-%\GenerateVariantsFile:n{\rescansynclastfilename}
+% use like this \__begingroup_setup_estr: ⟨some command⟩ \endgroup
+% e.g. \__begingroup_setup_estr: \exp_args:NNx \endgroup \__function:n {⟨text⟩}
+% will eventually execute to \__function:n {⟨estr-expansion of text⟩}
-\str_set:Nn \_pythonimmediate_outputdir{.}
-\tl_set:Nn \_pythonimmediate_textopy_script_path{}
-\str_set:Nn \_pythonimmediate_mode{multiprocessing-network}
+\str_set:Nn \_pythonimmediate_args{--mode=multiprocessing-network}
\str_set:Nn \_pythonimmediate_python_executable{python3}
+\str_set:Nn \_pythonimmediate_python_flags{}
\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,
+ args.tl_gset:N =\_pythonimmediate_args,
+ python-executable.tl_gset:N=\_pythonimmediate_python_executable,
+ python-flags.tl_gset:N =\_pythonimmediate_python_flags,
+ child-process.bool_gset:N =\_pythonimmediate_child_process,
}
\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
+\_pythonimmediate_begingroup_setup_estr:
+ \str_gset:Nx \_pythonimmediate_args { \_pythonimmediate_args }
+ \str_gset:Nx \_pythonimmediate_python_executable{ \_pythonimmediate_python_executable }
+ \str_gset:Nx \_pythonimmediate_python_flags { \_pythonimmediate_python_flags }
+\endgroup
-\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} {shell-fail} {Please~enable~unrestricted~shell~scape!}
\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
+\sys_if_shell_unrestricted:F {
+ \msg_error:nn {pythonimmediate} {shell-fail}
}
-% we need to persistently open the file anyway, so using LaTeX3 stream reference counting doesn't help
-\newread \_pythonimmediate_read_file
-
+% note on \newread: we need to persistently open the file anyway, so using LaTeX3 stream reference counting doesn't help
+% ======== setup read file ========
-%\bench before~setup.
+\bool_if:NTF \_pythonimmediate_child_process {
+ % make read file read from stdin
+ % note that \ior_str_get:NN cannot be used here as it checks for \ifeof which breaks with terminal for some reason
+ \int_const:Nn \_pythonimmediate_read_file {-1}
+} {
+ \newread \_pythonimmediate_read_file
+ \openin \_pythonimmediate_read_file=|"\_pythonimmediate_python_executable \space \_pythonimmediate_python_flags \space -m ~ pythonimmediate.pytotex ~ \_pythonimmediate_args"~
+}
-\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
+% ======== copy of ior_str_get but does not check for file end ========
+\cs_generate_variant:Nn \use_ii_i:nn {o}
+\cs_new_protected:Npn \_pythonimmediate_str_get:N #1 {
+ \use_ii_i:on {\the\endlinechar\relax} {
+ \endlinechar=-1~
+ \readline \_pythonimmediate_read_file to #1
+ \endlinechar=
}
+}
+
- \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
+% ======== setup write file ========
+\newwrite \_pythonimmediate_write_file
+\bool_if:NTF \_pythonimmediate_child_process {
+ %\immediate\openout \_pythonimmediate_write_file=symlink-to-stderr.txt~
+ % tried this method, it seems to work except that it's buffered when stderr is redirected to a file...
+ \immediate\openout \_pythonimmediate_write_file=|"\_pythonimmediate_python_executable \space \_pythonimmediate_python_flags \space -m ~ pythonimmediate.copy_to_stderr"~
+} {
+ \immediate\openout \_pythonimmediate_write_file=|"\_pythonimmediate_python_executable \space \_pythonimmediate_python_flags \space -m ~ pythonimmediate.textopy"~
+ % note that openout, even to shell, will append .tex if there's no dot in the command
+ % so we artificially add a dot here in `.textopy`
- %\bench before~openin.
+ % both processes must be before the \readline below so that the 2 Python processes are started "in parallel"
+ \_pythonimmediate_str_get:N \_pythonimmediate_line % read one line from pytotex half, forward to textopy half
- % ======== 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
+ \str_if_eq:VnT \_pythonimmediate_line {} {
+ \msg_error:nn {pythonimmediate} {cannot-read-line}
}
- %\bench after~openin.
+ \str_const:Nn \_pythonimmediate_engine_mark_pdftex {p}
+ \str_const:Nn \_pythonimmediate_engine_mark_ptex {P}
+ \str_const:Nn \_pythonimmediate_engine_mark_uptex {u}
- %\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.
+ \str_const:Nn \_pythonimmediate_engine_mark_xetex {x}
+ \str_const:Nn \_pythonimmediate_engine_mark_luatex {l}
- \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}
- }
+ \immediate\write\_pythonimmediate_write_file {
+ \use:c {_pythonimmediate_engine_mark_ \c_sys_engine_str}
+ \_pythonimmediate_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.
+}
+% ======== bootstrap utility functions
% 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
@@ -149,9 +143,6 @@
\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
@@ -176,8 +167,6 @@
\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.
+% ======== bootstrap code
\_pythonimmediate_run_block:
-%\bench after~bootstrap.