diff options
author | Norbert Preining <norbert@preining.info> | 2023-01-12 03:01:29 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2023-01-12 03:01:29 +0000 |
commit | 6a7900c93acc16d1bbd2f1e26286c7dd7387e6c0 (patch) | |
tree | 76196287351cba68a51934c49468bf1a7846aa76 /macros/latex/contrib/pythonimmediate/pythonimmediate.sty | |
parent | c1c19023b4b1d43cb874f84df5102b485853e672 (diff) |
CTAN sync 202301120301
Diffstat (limited to 'macros/latex/contrib/pythonimmediate/pythonimmediate.sty')
-rw-r--r-- | macros/latex/contrib/pythonimmediate/pythonimmediate.sty | 157 |
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. |