pythonimmediate package¶
Submodules¶
pythonimmediate.communicate module¶
- class pythonimmediate.communicate.Communicator[source]¶
Bases:
ABC
- character = None¶
- abstract static setup() tuple[Communicator, ListenForwarder] [source]¶
Constructs an communicator object, that can be used to send information to this process.
The Communicator should be sent to the other process as part of the GlobalConfiguration object.
The ListenForwarder should be called in this process.
- class pythonimmediate.communicate.GlobalConfiguration(debug: 'int' = 0, communicator: 'Communicator' = None, sanity_check_extra_line: 'bool' = False, debug_force_buffered: 'bool' = False, naive_flush: 'bool' = False)[source]¶
Bases:
object
- communicator: Communicator = None¶
- debug: int = 0¶
- debug_force_buffered: bool = False¶
- static from_args(args: Namespace, communicator: Communicator) GlobalConfiguration [source]¶
- naive_flush: bool = False¶
- sanity_check_extra_line: bool = False¶
- class pythonimmediate.communicate.MultiprocessingNetworkCommunicator(port: 'int', connection: 'Optional[Connection]' = None)[source]¶
Bases:
Communicator
- connection: Optional[Connection] = None¶
- port: int¶
- static setup() tuple[MultiprocessingNetworkCommunicator, ListenForwarder] [source]¶
Constructs an communicator object, that can be used to send information to this process.
The Communicator should be sent to the other process as part of the GlobalConfiguration object.
The ListenForwarder should be called in this process.
- class pythonimmediate.communicate.UnnamedPipeCommunicator(pid: 'int', fileno: 'int', connection: 'Optional[IO[bytes]]' = None)[source]¶
Bases:
Communicator
- connection: Optional[IO[bytes]] = None¶
- fileno: int¶
- pid: int¶
- static setup() tuple[UnnamedPipeCommunicator, ListenForwarder] [source]¶
Constructs an communicator object, that can be used to send information to this process.
The Communicator should be sent to the other process as part of the GlobalConfiguration object.
The ListenForwarder should be called in this process.
pythonimmediate.copy_to_stderr module¶
module to copy everything from stdin to stderr.
needed to allow TeX to write to stderr.
pythonimmediate.decode_8bit module¶
pythonimmediate.engine module¶
Abstract engine class.
- class pythonimmediate.engine.ChildProcessEngine(engine_name: Literal['pdftex', 'xetex', 'luatex'], args: Iterable[str] = ())[source]¶
Bases:
Engine
An object that represents a \(\TeX\) engine that runs as a subprocess of this process.
Can be used as a context manager to automatically close the subprocess when the context is exited.
For example, the following Python code, if run alone, will spawn a \(\TeX\) process and use it to write “Hello world” to a file named
a.txt
in the temporary directory:from pythonimmediate.engine import ChildProcessEngine from pythonimmediate import execute with ChildProcessEngine("pdftex") as engine: # do something with the engine, for example: execute(r''' \immediate\openout9=a.txt \immediate\write9{Hello world} \immediate\closeout9 ''', engine=engine) # now the engine is closed.
Note that explicit
engine
argument must be passed in most functions.See
DefaultEngine
for a way to bypass that.
- class pythonimmediate.engine.DefaultEngine[source]¶
Bases:
Engine
A convenience class that can be used to avoid passing explicit
engine
argument to functions.This is not thread-safe.
Users should not instantiate this class directly. Instead, use
default_engine
.Usage example:
default_engine.set_engine(engine) execute("hello world") # this is executed on engine=engine
See also
- property config: GlobalConfiguration¶
Self-explanatory.
- engine: Optional[Engine]¶
Stores the engine being set internally.
Normally there’s no reason to access the internal engine directly, as
self
can be used like the engine inside.
- get_engine() Engine [source]¶
Convenience helper function, return the engine.
All the other functions that use this one (those that make use of the engine) will raise RuntimeError if the engine is None.
- property name: Literal['pdftex', 'xetex', 'luatex']¶
Self-explanatory.
- set_engine(engine: Optional[Engine]) SetDefaultEngineContextManager [source]¶
Set the default engine to another engine.
Can also be used as a context manager to revert to the original engine. Example:
with default_engine.set_engine(...): pass # do something # now the original engine is restored
- class pythonimmediate.engine.Engine[source]¶
Bases:
ABC
- property config: GlobalConfiguration¶
Self-explanatory.
- property is_unicode: bool¶
Self-explanatory.
- property name: Literal['pdftex', 'xetex', 'luatex']¶
Self-explanatory.
- class pythonimmediate.engine.ParentProcessEngine(pseudo_config: GlobalConfiguration)[source]¶
Bases:
Engine
Represent the engine if this process is started by the TeX’s pythonimmediate library.
This should not be used directly. Only pythonimmediate.main module should use this.
- class pythonimmediate.engine.SetDefaultEngineContextManager(old_engine: Optional[Engine])[source]¶
Bases:
object
Context manager, used in conjunction with default_engine.set_engine(…) to revert to the original engine.
- pythonimmediate.engine.default_engine = <pythonimmediate.engine.DefaultEngine object>¶
A constant that can be used to avoid passing explicit
engine
argument to functions.See documentation of
DefaultEngine
for more details.For Python running inside a TeX process, useful attributes are
name
andis_unicode
.
pythonimmediate.simple module¶
Simple interface, suitable for users who may not be aware of \(\TeX\) subtleties, such as category codes.
This is only guaranteed to work properly in normal category code situations
(in other words, \makeatletter
or \ExplSyntaxOn
are not officially supported).
In all other cases, use the advanced API.
Nevertheless, there are still some parts that you must be aware of:
Any “output”, resulted from any command related to Python, can only be used to typeset text, it must not be used to pass “values” to other \(\TeX\) commands.
This is already mentioned in the documentation of the \(\TeX\) file in
\py
command, just repeated here.For example:
\py{1+1} % legal, typeset 2 \setcounter{abc}{\py{1+1}} % illegal % the following is still illegal no many how many ``\expanded`` and such are used \edef\tmp{\py{1+1}} \setcounter{abc}{\tmp} \py{ r'\\setcounter{abc}{' + str(1+1) + '}' } % legal workaround
Similar for commands defined with
newcommand()
:@newcommand def test(): print_TeX("123", end="")
Then the \(\TeX\) code:
The result is \test. % legal, typesets "123" \setcounter{abc}{\test} % illegal
There’s the function
fully_expand()
, but some \(\TeX\) commands cannot be used inside this.Refer to the Note on unexpandable and fragile macros section for more details.
Regarding values being passed from \(\TeX\) to Python, you can do one of the following:
Either simply do
\py{value = r'''#1'''}
, or\py{value = get_arg_str()}{#1}
.
You can easily see both forms are equivalent, except that in the first form,
#1
cannot end with unbalanced\
or contain triple single quotes.
Start with reading newcommand()
and execute()
. Then read the rest of the functions in this module.
- pythonimmediate.simple.define_char(char: str, engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) Callable[[T2], T2] [source]¶
Define a character to do some specific action.
Can be used as a decorator:
@define_char("×") def multiplication_sign(): print_TeX(end=r"\times")
Note
It’s not recommended to define commonly-used characters, for example if you define
n
then commands whose name containsn
cannot be used anymore.As another example, if you define
-
, then commands like\draw (1, 2) -- (3, 4);
in TikZ cannot be used.undefine_char()
can be used to undo the effect.
- pythonimmediate.simple.execute(block: str, engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) None [source]¶
Run a block of \(\TeX\)-code (might consist of multiple lines).
A simple example is
execute('123')
which simply typesets123
(with a trailing newline, see the note innewcommand()
documentation).This is similar to
print_TeX()
but it’s executed immediately, and any error is immediately reported with Python traceback points to the caller.On the other hand, because this is executed immediately, each part must be “self-contained”. With
print_TeX()
you can do the following:print_TeX(r"\abc", end="") print_TeX(r"def", end="")
and a single command
\abcdef
will be executed, but it does not work with this method.As another consequence, all
execute()
are done before allprint_TeX()
in the same block. For example:print_TeX(r"3") execute(r"1") print_TeX(r"4") execute(r"2")
will typeset
1 2 3 4
.Note
For advanced users: it’s implemented with
\scantokens
, so catcode-changing commands are allowed inside.
- pythonimmediate.simple.f1(s: str, globals: Optional[dict] = None, locals: Optional[dict] = None, escape: Optional[Union[Tuple[str, ...], str]] = None) str [source]¶
Helper function to construct a string from Python expression parts, similar to f-strings, but allow using arbitrary delimiters.
This is useful for constructing \(\TeX\) code, because the
{
and}
characters are frequently used in \(\TeX\).Example:
>>> a = 1 >>> b = 2 >>> f1("a=`a`, b=`b`") 'a=1, b=2' >>> f1("`") Traceback (most recent call last): ... ValueError: Unbalanced delimiters!
The escape/delimiter character can be escaped by doubling it:
>>> f1("a``b") 'a`b' >>> f1("a ` '````' ` b") 'a `` b'
It’s possible to customize the delimiters:
>>> f1("a=!a!", escape="!") 'a=1'
It’s possible to use different delimiters for the opening and the closing:
>>> f1("a=⟨a⟩, b=⟨b⟩", escape="⟨⟩") 'a=1, b=2' >>> f1("<", escape="<>") Traceback (most recent call last): ... ValueError: Unbalanced delimiters!
There’s no way to escape them, they must be balanced in the string:
>>> f1("a=⟨ '⟨'+str(a)+'⟩' ⟩", escape="⟨⟩") 'a=⟨1⟩'
The default value of escape is stored in
f1.default_escape
, it can be reassigned:>>> f1.default_escape="!" >>> f1("a=!a!") 'a=1' >>> f1.default_escape="`"
It’s even possible to use multiple characters as the delimiter:
>>> f1("a=**a**, b=**b**", escape=["**"]) 'a=1, b=2' >>> f1("a=[[a]], b=[[b]]", escape=["[[", "]]"]) 'a=1, b=2'
In the first case above, remember to put the delimiter in a list because otherwise it will be interpreted as an opening and a closing delimiter.
Currently, format specifiers such as
:d
are not supported.
- pythonimmediate.simple.fully_expand(content: str, engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) str [source]¶
Expand all macros in the given string.
Note on unexpandable and fragile macros¶
Not all \(\TeX\) commands/macros can be used inside this function. If you try to, they will either return the input unchanged, or error out.
In such cases, the problem is impossible to solve, so just look for workarounds.
An example is the
\py
command, which is already mentioned in the initial introduction to thesimple
module:>> fully_expand(r"\py{1+1}") r"\py{1+1}"
Some other examples (assuming
\usepackage{ifthen}
is used):>> execute(r'\ifthenelse{1=2}{equal}{not equal}') # legal, typeset "not equal" >> fully_expand(r'\ifthenelse{1=2}{equal}{not equal}') # error out >> execute(r'\uppercase{abc}') # legal, typeset "ABC" >> fully_expand(r'\uppercase{abc}') # just returned verbatim r'\uppercase {abc}'
There might be a few workarounds, but they’re all dependent on the exact macros involved.
For if-style commands, assign the desired result to a temporary variable:
>> execute(r'\ifthenelse{1=2}{\pyc{result = "equal"}}{\pyc{result = "not equal"}}') >> result "not equal"
For for-loop-style commands, you can do something similar (demonstrated here with
tikz
’s\foreach
command):>> result = [] >> execute(r'\foreach \x in {1, 2, ..., 10} {\pyc{result.append(var["x"])}}') >> result ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
For macros such as
\uppercase{...}
, there’s no simple workaround. Reimplement it yourself (such as with.upper()
in Python).
- pythonimmediate.simple.get_arg_estr(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) str [source]¶
Get a mandatory argument from the input stream, then process it as described in Note on argument expansion of estr-type functions.
Note on argument expansion of
estr
-type functions¶These functions return a string, however they’re meant for strings other than \(\TeX\) code.
The argument is fully expanded, and any “escaped character” are passed as the character itself to Python.
The behavior is similar to that of the
\py
command argument processing, see also the \(\TeX\) package documentation.Some examples: After the \(\TeX\) code
\def\myvalue{123abc}
is executed, then:if the argument in \(\TeX\) code is
{abc}
, then the Python function will return"abc"
(similar to as ifget_arg_str()
is used),if the argument in \(\TeX\) code is
{\%a\#b\\\~}
, then the Python function will returnr"%a#b\~"
,if the argument in \(\TeX\) code is
{\myvalue}
, then the Python function will return"123abc"
.
- pythonimmediate.simple.get_arg_str(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) str [source]¶
Get a mandatory argument from the input stream.
It’s slightly difficult to explain what this does, so here’s an example:
@newcommand def foo(): a = get_arg_str() b = get_arg_str() c = get_arg_str()
The above defines a \(\TeX\) function
\foo
, if it’s called in \(\TeX\) code as:\foo{xx}{yy}{zz}
then the variable
a
will be the string"xx"
,b
will be"yy"
, andc
will be"zz"
.Note on parameter tokenization¶
Note
This function, as well as all the _str functions, return the argument that might be mangled in the following ways:
A space might be added after each command that consist of multiple characters
the
^^xx
or^^xxxx
etc. notation is replaced by the character itself, or vice versa – literal tab character might get replaced with^^I
multiple space characters may be collapsed into one
newline character may become space character
double-newline character may become
\par
For example,
\*\hell^^6F{ }
may become the stringr"\*\hello { }"
in Python.As such, verbatim-like commands in the argument are not supported. See
get_verb_arg()
for a workaround.Nevertheless, if the argument consist of only “normal” \(\TeX\) commands, re-executing the string should do the same thing as the original code.
In case of doubt, print out the string and check it manually.
Note
For advanced users:
This function corresponds to the
m
-type argument inxparse
package.It gets the argument, detokenize it, pass it through
_replace_double_hash()
, and return the result.This is the simple API, as such it assumes normal category code values. Refer to
BalancedTokenList.get_next()
for a more advanced API.
- pythonimmediate.simple.get_multiline_verb_arg(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) str [source]¶
Get a multi-line verbatim argument. Usage is identical to
get_verb_arg()
, except newline characters in the argument is supported.Note
in unusual category regime (such as that in
\ExplSyntaxOn
), it may return wrong result.
- pythonimmediate.simple.get_next_char(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) str [source]¶
Return the character of the following token as with
peek_next_char()
, but also removes it from the input stream.
- pythonimmediate.simple.get_optional_arg_estr(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) Optional[str] [source]¶
Get an optional argument. See also Note on argument expansion of estr-type functions.
- pythonimmediate.simple.get_optional_arg_str(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) Optional[str] [source]¶
Get an optional argument. See also Note on parameter tokenization.
Note
For advanced users: This function corresponds to the
o
-type argument inxparse
package.
- pythonimmediate.simple.get_verb_arg(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) str [source]¶
Get a verbatim argument.
Similar to
\verb
, defined \(\TeX\) commands that use this function can only be used at top level i.e. not inside any arguments.This function behavior is identical to
v
-type argument inxparse
package, you can use it like this:\foo{xx%^\?} % delimited with braces \foo|xx%^\?| % delimited with another symbol
See also
get_multiline_verb_arg()
to support newline character in the argument.Note
Hard TAB character in the argument gives an error until the corresponding LaTeX3 bug is fixed, see https://tex.stackexchange.com/q/508001/250119.
- pythonimmediate.simple.is_balanced(content: str) bool [source]¶
Check if the given string is balanced, i.e. if all opening and closing braces match.
This is a bit tricky to implement, it’s recommended to use the library function.
For example:
>>> is_balanced("a{b}c") True >>> is_balanced("a{b}c}") False >>> is_balanced(r"\{") True
- pythonimmediate.simple.newcommand(name: str, f: Optional[Callable], engine: Engine) Callable [source]¶
Define a new \(\TeX\)-command.
Example:
@newcommand def myfunction(): print_TeX("Hello world!", end="")
The above is mostly equivalent to
\newcommand{\myfunction}{Hello world!}
, it defines a \(\TeX\) command\myfunction
that printsHello world!
.See also documentation of
print_TeX()
to understand the example above.It can be used as either a decorator or a function:
def myfunction(): print_TeX("Hello world!", end="") newcommand("myfunction", myfunction)
An explicit command name can also be provided:
@newcommand("hello") def myfunction(): print_TeX("Hello world!", end="")
The above defines a \(\TeX\) command
\hello
that printsHello world!
.If name is not provided, it’s automatically deduced from the Python function name.
The above is not by itself very useful. Read the documentation of the following functions sequentially for a guide on how to define commands that take arguments:
Then see also (as natural extensions of the above):
Note on trailing newline¶
Note
Regarding the
end=""
part in the example above, it’s used to prevent a spurious space, otherwise if you run the \(\TeX\) code:123\myfunction 456
it will be “equivalent” to:
123Hello world! 456
and the newline inserts a space.
Internally, the feature is implemented by appending
%
to the last line, as most of the time the code:123Hello world!% 456
will be equivalent to:
123Hello world!456
but in unusual category code cases or cases of malformed \(\TeX\) code (such as a trailing backslash at the end of the line), it may not be equivalent. Use the advanced API, or always include a final newline and manually add the comment character, if these cases happen.
- pythonimmediate.simple.newenvironment(name: str, f: Optional[Callable], engine: Engine) Callable [source]¶
Define a new \(\TeX\) normal environment.
Note that the environment will normally not have access to the body of the environment, see
newenvironment_verb()
for some alternatives.- Parameters
name – the name of the environment, e.g.
"myenv"
or"myenv*"
.f – a function that should execute the code for the begin part of the environment,
yield
, then execute the code for the end part of the environment.engine – the engine to use.
Usage example:
@newenvironment("myenv") def myenv(): x=random.randint(1, 100) print_TeX(f"begin {x}") yield print_TeX(f"end {x}")
then the following \(\TeX\) code:
\begin{myenv} \begin{myenv} Hello world! \end{myenv} \end{myenv}
might typeset the following content:
begin 42 begin 24 Hello world! end 24 end 42
Functions such as
get_arg_str()
etc. can also be used in the first part of the function to take arguments.If the name is omitted, the function name is used as the environment name.
It can be used either as a decorator or a function, see
newcommand()
for details.
- pythonimmediate.simple.newenvironment_verb(name: str, f: Optional[Callable], engine: Engine) Callable [source]¶
Define a new \(\TeX\) environment that reads its body verbatim.
Note that space characters at the end of each line are removed.
The environment must not take any argument. For example the following built-in
tabular
environment takes some argument, so cannot be implemented with this function:\begin{tabular}[t] ... \end{tabular}
It can be used either as a decorator or a function, see
newenvironment()
for details.Some usage example:
@newenvironment_verb("myenv") def myenv(body: str): execute(body.replace("a", "b"))
If later the following \(\TeX\) code is executed:
\begin{myenv} aaa \end{myenv}
then the value of the variable
body
will be"aaa\n"
, and the following content will be typeset:bbb
Note
For advanced users: unlike a typical environment, the code will not be executed in a new \(\TeX\) group.
- pythonimmediate.simple.peek_next_char(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) str [source]¶
Get the character of the following token, or empty string if it’s not a character.
This function can be used as follows to simulate
e
-type argument specifiers ofxparse
:if peek_next_char()=="^": get_next_char() result = get_arg_str() # the following content in the input stream is ``^{...}`` and we stored the content inside to ``result`` else: pass # there's no ``^...`` following in the input stream
It can also simulate
s
-type argument specifier (optional star):if peek_next_char()=="*": get_next_char() # there's a star else: pass # there's no star
It can even simulate
o
-type argument specifier (optional argument delimited by[...]
):if peek_next_char()=="[": get_next_char() # skip the `[` result="" while True: if peek_next_char(): c=get_next_char() if c=="]": break result+=c else: # following in the input stream must be a control sequence, such as `\relax` result+=get_arg_str() # now result contains the content inside the `[...]` else: pass # there's no optional argument
Note that the above does not take into account the balance of braces or brackets, so:
If the following content in the input is
[ab{cd]ef}gh]
then the result will beab{cd
.If the following content in the input is
[ab[cd]ef]
then the result will beab[cd
.
Note
For advanced users:
This function uses
peek_next_meaning()
under the hood to get the meaning of the following token. See that function documentation for a warning on undefined behavior.Will also return nonempty if the next token is an implicit character token. This case is not supported and might give random error.
- pythonimmediate.simple.print_TeX(*args, **kwargs) None [source]¶
Unlike other packages, the normal
print()
function prints to the console.This function can be used to print \(\TeX\) code to be executed. For example:
\begin{pycode} print_TeX("Hello world!") \end{pycode}
It can also be used inside a custom-defined command, see the documentation of
newcommand()
for an example.The signature is identical to that of
print()
. Passing explicitfile=
argument is not allowed.Remark: normally the
print()
function prints out a newline with each invocation. A newline will appear as a spurious space in the output Useprint_TeX(end="")
to prevent this, see Note on trailing newline.
- pythonimmediate.simple.put_next(arg: str, engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) None [source]¶
Put some content forward in the input stream.
- Parameters
arg –
The content, must be a single line.
Note that there must not be any verbatim-like commands in the argument, so for example
put_next(r"\verb|a|")
is not allowed.If there might be verbatim-like arguments, the problem is (almost) unsolvable. Refer to
print_TeX()
orexecute()
for workarounds, or use the advanced interface such aspythonimmediate.BalancedTokenList.put_next()
.
For example, if the following content in the input stream are
{abc}{def}
:s = get_arg_str() # s = "abc" t = get_arg_str() # t = "def" put_next("{" + t + "}") put_next("{" + s + "}")
After the above code, the content in the input stream is “mostly unchanged”.
Note
For advanced users: the argument is tokenized in the current category regime.
- pythonimmediate.simple.renewcommand(name: str, f: Optional[Callable], engine: Engine) Callable [source]¶
Redefine a \(\TeX\)-command. Usage is similar to
newcommand()
.
- pythonimmediate.simple.run_tokenized_line_local(line: str, *, check_braces: bool = True, check_newline: bool = True, check_continue: bool = True, engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) None [source]¶
- pythonimmediate.simple.split_balanced(content: str, separator: str) List[str] [source]¶
Split the given string at the given substring, but only if the parts are balanced.
This is a bit tricky to implement, it’s recommended to use the library function.
If either content or separator is unbalanced, the function will raise ValueError.
For example:
>>> split_balanced("a{b,c},c{d}", ",") ['a{b,c}', 'c{d}'] >>> split_balanced(r"\{,\}", ",") ['\\{', '\\}'] >>> split_balanced("{", "{") Traceback (most recent call last): ... ValueError: Content is not balanced!
- pythonimmediate.simple.strip_optional_braces(content: str) str [source]¶
Strip the optional braces from the given string, if the whole string is wrapped in braces.
For example:
>>> strip_optional_braces("{a}") 'a' >>> strip_optional_braces("a") 'a' >>> strip_optional_braces("{a},{b}") '{a},{b}'
- pythonimmediate.simple.undefine_char(char: str, engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) None [source]¶
The opposite of
define_char()
.
- pythonimmediate.simple.var = VarManager(engine=<pythonimmediate.engine.DefaultEngine object>)¶
Get and set value of “variables” in \(\TeX\).
Can be used like this:
var["myvar"]="myvalue" # "equivalent" to \def\myvar{myvalue} var["myvar"]=r"\textbf{123}" # "equivalent" to \def\myvar{\textbf{123}} var["myvar"]=r"\def\test#1{#1}" # "equivalent" to \tl_set:Nn \myvar {\def\test#1{#1}} (note that `#` doesn't need to be doubled here unlike in `\def`) var(engine)["myvar"]="myvalue" # pass explicit engine # after ``\def\myvar{myvalue}`` (or ``\edef``, etc.) on TeX you can do: print(var["myvar"]) # get the value of the variable, return a string
It’s currently undefined behavior if the passed-in value is not a “variable”. (in some future version additional checking might be performed in debug run)
Notes in Note on parameter tokenization apply – in other words, after you set a value it may become slightly different when read back.
Module contents¶
- class pythonimmediate.BalancedTokenList(a: Iterable = ())[source]¶
Bases:
TokenList
Represents a balanced token list.
Some useful methods to interact with \(\TeX\) include
expand_o()
,expand_x()
,get_next()
andput_next()
. See the corresponding methods’ documentation for usage examples.See also Token list construction for shorthands to construct token lists in Python code.
Note
Runtime checking is not strictly enforced, use
is_balanced()
method explicitly if you need to check.- detokenize(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) str [source]¶
- Returns
a string, equal to the result of
\detokenize
applied to this token list.
- execute(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) None [source]¶
Execute this token list. It must not “peek ahead” in the input stream.
For example the token list
\catcode1=2\relax
can be executed safely (and sets the corresponding category code), but there’s no guarantee what will be assigned to\tmp
when\futurelet\tmp
is executed.
- expand_o(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) BalancedTokenList [source]¶
Return the
o
-expansion of this token list.The result must be balanced, otherwise the behavior is undefined.
- expand_x(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) BalancedTokenList [source]¶
Return the
x
-expansion of this token list.The result must be balanced, otherwise the behavior is undefined.
- static get_next(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) BalancedTokenList [source]¶
Get an (undelimited) argument from the \(\TeX\) input stream.
- class pythonimmediate.BlueToken(token: Token)[source]¶
Bases:
NToken
Represents a blue token (see documentation of
NToken
).- property no_blue: Token¶
Return the result of this token after being “touched”, which drops its blue status if any.
- class pythonimmediate.Catcode(value)[source]¶
Bases:
Enum
This class contains a shorthand to allow creating a token with little Python code. The individual
Catcode
objects can be called with either a character or a character code to create the object:Catcode.letter("a") # creates a token with category code letter and character code "a"=chr(97) Catcode.letter(97) # same as above
Both of the above forms are equivalent to
CharacterToken(index=97, catcode=Catcode.letter)
.See also Token list construction for more ways of constructing token lists.
- active = 13¶
- alignment = 4¶
- begin_group = 1¶
- bgroup = 1¶
- comment = 14¶
- egroup = 2¶
- end_group = 2¶
- end_of_line = 5¶
- escape = 0¶
- property for_token: bool¶
Return whether a
CharacterToken
may have this catcode.
- ignored = 9¶
- invalid = 15¶
- letter = 11¶
- line = 5¶
- math = 3¶
- math_subscript = 8¶
- math_superscript = 7¶
- math_toggle = 3¶
- other = 12¶
- paragraph = 5¶
- param = 6¶
- parameter = 6¶
- space = 10¶
- subscript = 8¶
- superscript = 7¶
- class pythonimmediate.CharacterToken(index: 'int', catcode: 'Catcode')[source]¶
Bases:
Token
- property assignable: bool¶
Whether this token can be assigned to i.e. it’s control sequence or active character.
- property can_blue: bool¶
Return whether this token can possibly be blue i.e. expandable.
- property chr: str¶
The character of this token. For example
Catcode.letter("a").chr=="a"
.
- degree() int [source]¶
return the imbalance degree for this token (
{
-> 1,}
-> -1, everything else -> 0)
- index: int¶
The character code of this token. For example
Catcode.letter("a").index==97
.
- simple_detokenize(get_catcode: Callable[[int], Catcode]) str [source]¶
Simple approximate detokenizer, implemented in Python.
- class pythonimmediate.ControlSequenceToken(csname: str)[source]¶
Bases:
Token
Represents a control sequence.
Note that currently, on non-Unicode engines, the
csname
field is represented in a particular way: each character represents a byte in the TokenList, and thus it has character code no more than 255.So for example, the control sequence obtained by expanding
\csname ℝ\endcsname
once has.csname
field equal to"\xe2\x84\x9d"
(which haslen=3
).- property assignable: bool¶
Whether this token can be assigned to i.e. it’s control sequence or active character.
- can_blue = True¶
- csname: str¶
- make = <pythonimmediate.ControlSequenceTokenMaker object>¶
Refer to the documentation of
ControlSequenceTokenMaker
.
- class pythonimmediate.ControlSequenceTokenMaker(prefix: str)[source]¶
Bases:
object
Shorthand to create control sequence objects in Python easier.
There’s a default one that can be used as, if you assign
T=ControlSequenceToken.make
, thenT.hello
returns the token\hello
.
- class pythonimmediate.NToken[source]¶
Bases:
ABC
Represent a possibly-notexpanded token. For convenience, a notexpanded token is called a blue token. It’s not always possible to determine the notexpanded status of a following token in the input stream.
Implementation note: Token objects must be frozen.
- degree() int [source]¶
return the imbalance degree for this token (
{
-> 1,}
-> -1, everything else -> 0)
- meaning_equal(other: NToken, engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) bool [source]¶
Whether this token is the same in meaning as the token specified in the parameter other.
Note that two tokens might have different meaning despite having equal
meaning_str()
.
- meaning_str(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) str [source]¶
get the meaning of this token as a string.
Note that all blue tokens have the meaning equal to
\relax
(or[unknown command code! (0, 1)]
in a buggy LuaTeX implementation) with the backslash replaced by the currentescapechar
.
- abstract property no_blue: Token¶
Return the result of this token after being “touched”, which drops its blue status if any.
- abstract put_next(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>)[source]¶
Put this token forward in the input stream.
- class pythonimmediate.NTokenList(a: Iterable = ())[source]¶
Bases:
UserList
Similar to
TokenList
, but can contain blue tokens.The class can be used identical to a Python list consist of
NToken
objects, plus some additional methods to operate on token lists.Refer to the documentation of
TokenList
for some usage example.- bool() bool [source]¶
Internal function.
self
must represent a \(\TeX\) string either equal to “0” or “1”.- Returns
the boolean represented by the string.
- execute(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) None [source]¶
- expand_x(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) BalancedTokenList [source]¶
- put_next(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) None [source]¶
- simple_parts() List[Union[BalancedTokenList, Token, BlueToken]] [source]¶
Internal function.
Split this
NTokenList
into a list of balanced non-blue parts, unbalanced{
/}
tokens, and blue tokens.
- str(engine: Engine) str [source]¶
self
must represent a \(\TeX\) string. (i.e. equal to itself when detokenized)- Returns
the string content.
- str_unicode() str [source]¶
self
must represent a \(\TeX\) string. (i.e. equal to itself when detokenized)- Returns
the string content.
Note
In non-Unicode engines, each token will be replaced with a character with character code equal to the character code of that token. UTF-8 characters with character code
>=0x80
will be represented by multiple characters in the returned string.
- class pythonimmediate.PTTBalancedTokenList(data: 'BalancedTokenList')[source]¶
Bases:
PyToTeXData
- data: BalancedTokenList¶
- class pythonimmediate.PTTBlock(data: 'str')[source]¶
Bases:
PyToTeXData
- data: str¶
- class pythonimmediate.PTTInt(data: 'int')[source]¶
Bases:
PyToTeXData
- data: int¶
- class pythonimmediate.PTTTeXLine(data: str)[source]¶
Bases:
PyToTeXData
Represents a line to be tokenized in TeX’s current catcode regime. The trailing newline is not included, i.e. it’s tokenized under
\endlinechar=-1
.- data: str¶
- class pythonimmediate.PTTVerbatimLine(data: 'str')[source]¶
Bases:
PyToTeXData
- data: str¶
- class pythonimmediate.PTTVerbatimRawLine(data: bytes)[source]¶
Bases:
PyToTeXData
Represents a line to be tokenized verbatim. Internally the
\readline
primitive is used, as such, any trailing spaces are stripped. The trailing newline is not included, i.e. it’s read under\endlinechar=-1
.- data: bytes¶
- class pythonimmediate.PyToTeXData[source]¶
Bases:
ABC
Internal class (for now). Represent a data type that can be sent from Python to \(\TeX\).
- class pythonimmediate.PythonCallTeXSyncFunctionType(*args, **kwargs)[source]¶
Bases:
PythonCallTeXFunctionType
,Protocol
- class pythonimmediate.Python_call_TeX_data(TeX_code: 'str', recursive: 'bool', finish: 'bool', sync: 'Optional[bool]')[source]¶
Bases:
object
- TeX_code: str¶
- finish: bool¶
- recursive: bool¶
- sync: Optional[bool]¶
- class pythonimmediate.Python_call_TeX_extra(ptt_argtypes: 'Tuple[Type[PyToTeXData], ...]', ttp_argtypes: 'Union[Type[TeXToPyData], Tuple[Type[TeXToPyData], ...]]')[source]¶
Bases:
object
- ptt_argtypes: Tuple[Type[PyToTeXData], ...]¶
- ttp_argtypes: Union[Type[TeXToPyData], Tuple[Type[TeXToPyData], ...]]¶
- pythonimmediate.Python_call_TeX_local(TeX_code: str, *, recursive: bool = True, sync: Optional[bool] = None, finish: bool = False) Callable [source]¶
Internal function. See
scan_Python_call_TeX()
.
- class pythonimmediate.TTPBalancedTokenList(a: Iterable = ())[source]¶
Bases:
TeXToPyData
,BalancedTokenList
- static read(engine: Engine) TTPBalancedTokenList [source]¶
Given that \(\TeX\) has just sent the data, read into a Python object.
- class pythonimmediate.TTPBlock[source]¶
Bases:
TeXToPyData
,str
- static read(engine: Engine) TTPBlock [source]¶
Given that \(\TeX\) has just sent the data, read into a Python object.
- class pythonimmediate.TTPEBlock[source]¶
Bases:
TeXToPyData
,str
A kind of argument that interprets “escaped string” and fully expand anything inside. For example, {\} sends a single backslash to Python, {{} sends a single ‘{‘ to Python. Done by fully expand the argument in escapechar=-1 and convert it to a string. Additional precaution is needed, see the note above.
- static read(engine: Engine) TTPEBlock [source]¶
Given that \(\TeX\) has just sent the data, read into a Python object.
- class pythonimmediate.TTPELine[source]¶
Bases:
TeXToPyData
,str
Same as TTPEBlock, but for a single line only.
- static read(engine: Engine) TTPELine [source]¶
Given that \(\TeX\) has just sent the data, read into a Python object.
- class pythonimmediate.TTPEmbeddedLine[source]¶
Bases:
TeXToPyData
,str
- static read(engine: Engine) TTPEmbeddedLine [source]¶
Given that \(\TeX\) has just sent the data, read into a Python object.
- class pythonimmediate.TTPLine[source]¶
Bases:
TeXToPyData
,str
- static read(engine: Engine) TTPLine [source]¶
Given that \(\TeX\) has just sent the data, read into a Python object.
- class pythonimmediate.TTPRawLine[source]¶
Bases:
TeXToPyData
,bytes
- static read(engine: Engine) TTPRawLine [source]¶
Given that \(\TeX\) has just sent the data, read into a Python object.
- class pythonimmediate.TeXToPyData[source]¶
Bases:
ABC
Internal class (for now). Represent a data type that can be sent from \(\TeX\) to Python.
- abstract static read(engine: Engine) TeXToPyData [source]¶
Given that \(\TeX\) has just sent the data, read into a Python object.
- class pythonimmediate.Token[source]¶
Bases:
NToken
Represent a \(\TeX\) token, excluding the notexpanded possibility. See also documentation of
NToken
.- assign(other: NToken, engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) None [source]¶
Assign the meaning of this token to be equivalent to that of the other token.
- assign_future(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) None [source]¶
Assign the meaning of this token to be equivalent to that of the following token in the input stream.
For example if this token is
\a
, and the input stream starts withbcde
, then\a
’s meaning will be assigned to that of the explicit characterb
.Note
Tokenizes one more token in the input stream, and remove its blue status if any.
- assign_futurenext(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) None [source]¶
Assign the meaning of this token to be equivalent to that of the second-next token in the input stream.
For example if this token is
\a
, and the input stream starts withbcde
, then\a
’s meaning will be assigned to that of the explicit characterc
.Note
Tokenizes two more tokens in the input stream, and remove their blue status if any.
- assign_value(content: BalancedTokenList) None [source]¶
Given
self
is an expl3tl
-variable, assign content to it locally.
- abstract property assignable: bool¶
Whether this token can be assigned to i.e. it’s control sequence or active character.
- abstract property can_blue: bool¶
Return whether this token can possibly be blue i.e. expandable.
- static deserialize(s: str | bytes) Token [source]¶
See documentation of
TokenList.deserialize()
.Always return a single token.
- static deserialize_bytes(data: bytes, engine: Engine) Token [source]¶
See documentation of
TokenList.deserialize_bytes()
.Always return a single token.
- static get_next(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) Token [source]¶
Get the following token.
Note
in LaTeX3 versions without the commit https://github.com/latex3/latex3/commit/24f7188904d6 sometimes this may error out.
Note
because of the internal implementation of
\peek_analysis_map_inline:n
, this may tokenize up to 2 tokens ahead (including the returned token), as well as occasionally return the wrong token in unavoidable cases.
- property no_blue: Token¶
Return the result of this token after being “touched”, which drops its blue status if any.
- static peek_next(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) Token [source]¶
Get the following token without removing it from the input stream.
Equivalent to
get_next()
thenput_next()
immediately. See documentation ofget_next()
for some notes.
- put_next(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) None [source]¶
Put this token forward in the input stream.
- abstract serialize() str [source]¶
Internal function, serialize this token to be able to pass to \(\TeX\).
- abstract simple_detokenize(get_catcode: Callable[[int], Catcode]) str [source]¶
Simple approximate detokenizer, implemented in Python.
- value(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) BalancedTokenList [source]¶
given
self
is a expl3tl
-variable, return the content.
- class pythonimmediate.TokenList(a: Iterable = ())[source]¶
Bases:
UserList
Represent a \(\TeX\) token list, none of which can contain a blue token.
The class can be used identical to a Python list consist of
Token
objects, plus some additional methods to operate on token lists.The list of tokens represented by this class does not need to be balanced. Usually you would want to use
BalancedTokenList
instead.Token list construction¶
There are some functions to quickly construct token lists, see
from_string()
and__init__()
.__init__()
is the constructor of the class, for aTokenList
it can be used as follows:Given an existing token list, construct a copy (this usage is identical to that of the Python list constructor):
a=TokenList.doc("hello world") b=TokenList(a)
Construct a token list from a list of tokens:
a=TokenList([Catcode.letter("a"), Catcode.other("b"), T.test])
The above will define
a
to beab\test
, providedT
is the object referred to inControlSequenceTokenMaker
.See also
Catcode
for the explanation of theCatcode.letter("a")
form.Construct a token list, using lists to represent nesting levels:
a=TokenList([T.edef, T.a, [T.x, T.y]])
The above creates a token list with content
\edef\a{\x\y}
.
The constructor of other classes such as
BalancedTokenList
andNTokenList
works the same way.- property balanced: BalancedTokenList¶
self
must be balanced.- Returns
a
BalancedTokenList
containing the content of this object.
- balanced_parts() List[Union[BalancedTokenList, Token]] [source]¶
Internal function, used for serialization and sending to \(\TeX\).
Split this
TokenList
into a list of balanced parts and unbalanced{
/}
tokens.
- bool() bool [source]¶
See
NTokenList.bool()
.
- check_balanced() None [source]¶
ensure that this is balanced.
- Raises
ValueError – if this is not balanced.
- classmethod deserialize_bytes(data: bytes, engine: Engine) TokenListType [source]¶
Internal function.
Given a bytes object read directly from the engine, deserialize it.
- classmethod doc(s: str) TokenListType [source]¶
Approximate tokenizer in document (normal) catcode regime.
Refer to documentation of
from_string()
for details.Usage example:
BalancedTokenList.doc(r'\def\a{b}') # returns an instance of BalancedTokenList with the expected content BalancedTokenList.doc('}') # raises an error TokenList.doc('}') # returns an instance of TokenList with the expected content
- classmethod e3(s: str) TokenListType [source]¶
Approximate tokenizer in expl3 (
\ExplSyntaxOn
) catcode regime.Refer to documentation of
from_string()
for details.Usage example:
BalancedTokenList.e3(r'\cs_new_protected:Npn \__mymodule_myfunction:n #1 { #1 #1 }') # returns an instance of BalancedTokenList with the expected content
- execute(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) None [source]¶
Execute this token list. It must not “peek ahead” in the input stream.
For example the token list
\catcode1=2\relax
can be executed safely (and sets the corresponding category code), but there’s no guarantee what will be assigned to\tmp
when\futurelet\tmp
is executed.
- expand_x(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) BalancedTokenList [source]¶
Return the
x
-expansion of this token list.The result must be balanced, otherwise the behavior is undefined.
- classmethod from_string(s: str, get_catcode: Callable[[int], Catcode]) TokenListType [source]¶
Approximate tokenizer implemented in Python.
Convert a string to a
TokenList
(or some subclass of it such asBalancedTokenList
approximately.This is an internal function and should not be used directly. Use one of
e3()
ordoc()
instead.These are used to allow constructing a
TokenList
object in Python without being too verbose. Refer to Token list construction for alternatives.The tokenization algorithm is slightly different from \(\TeX\)’s in the following respect:
multiple spaces are collapsed to one space, but only if it has character code space (32). i.e. in expl3 catcode,
~~
get tokenized to two spaces.spaces with character code different from space (32) after a control sequence is not ignored. i.e. in expl3 catcode,
~
always become a space.^^
syntax are not supported. Use Python’s escape syntax (e.g.) as usual (of course that does not work in raw Python strings
r"..."
).
- Parameters
get_catcode – A function that given a character code, return its desired category code.
- static iterable_from_string(s: str, get_catcode: Callable[[int], Catcode]) Iterable[Token] [source]¶
refer to documentation of from_string() for details.
- put_next(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) None [source]¶
Put this token list forward in the input stream.
- serialize_bytes(engine: Engine) bytes [source]¶
Internal function.
Given an engine, serialize it in a form that is suitable for writing directly to the engine.
- str(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) str [source]¶
See
NTokenList.str()
.
- pythonimmediate.bootstrap_code_functions: list[EngineDependentCode] = [<function _naive_flush_data_define>, functools.partial(<function naive_replace>, '\n\\cs_new_protected:Npn \\__read_do_one_command: {\n\t\\begingroup\n\t\t\\endlinechar=-1~\n\t\t\\readline \\__read_file to \\__line\n\t\t\\expandafter\n\t\\endgroup % also this will give an error instead of silently do nothing when command is invalid\n\t\t\\csname __run_ \\__line :\\endcsname\n}\n\n\n% read documentation of ``_peek`` commands for details what this command does.\n\\cs_new_protected:Npn \\pythonimmediatecontinue #1 {\n\t\\__send_content:e {r #1 %naive_inline% }\n\t\\__read_do_one_command:\n}\n\n\\cs_new_protected:Npn \\pythonimmediatecontinuenoarg {\n\t\\pythonimmediatecontinue {}\n}\n\n\n\\cs_new_protected:Npn \\__send_content:n #1 {\n\t\\__send_content:e { \\unexpanded{#1} }\n}\n\n\\cs_new_protected:Npn \\__send_content_naive_flush:e #1 {\n\t\\__send_content:e { #1 %naive_inline% }\n}\n\n\\cs_new_protected:Npn \\__send_content_naive_flush:n #1 {\n\t\\__send_content_naive_flush:e { \\unexpanded{#1} }\n}\n\n% the names are such that \\__send_content%naive_send%:n {something} results in the correct content\n\n% internal function. Just send an arbitrary block of data to Python.\n% this function only works properly when newlinechar = 10.\n\\cs_new_protected:Npn \\__send_block:e #1 {\n\t\\__send_content:e {\n\t\t#1 ^^J\n\t\tpythonimm?"""?\'\'\'? % following character will be newline\n\t}\n}\n\n\\cs_new_protected:Npn \\__send_block:n #1 {\n\t\\__send_block:e {\\unexpanded{#1}}\n}\n\n\\cs_new_protected:Npn \\__send_block_naive_flush:e #1 {\n\t\\__send_content:e {\n\t\t#1 ^^J\n\t\tpythonimm?"""?\'\'\'? % following character will be newline\n\t\t%naive_inline%\n\t}\n}\n\n\\cs_new_protected:Npn \\__send_block_naive_flush:n #1 {\n\t\\__send_block_naive_flush:e {\\unexpanded{#1}}\n}\n\n\\cs_generate_variant:Nn \\__send_block:n {V}\n\\cs_generate_variant:Nn \\__send_block_naive_flush:n {V}\n\n\\AtEndDocument{\n\t\\__send_content:e {r %naive_inline%}\n\t\\__close_write:\n}\n'), <function mark_bootstrap.<locals>.<lambda>>, functools.partial(<function naive_replace>, '\n\t\\cs_new_protected:Npn \\py#1 {\n\t\t\\__send_content:e { i a }\n\t\t\\__begingroup_setup_estr: \\__send_block%naive_send%:e { #1 } \\endgroup\n\t\t\\__read_do_one_command:\n\t}\n\t'), functools.partial(<function naive_replace>, '\n\t\\cs_new_protected:Npn \\pyfile#1 {\n\t\t\\__send_content:e { i b }\n\t\t\\__begingroup_setup_estr: \\__send_content%naive_send%:e { #1 } \\endgroup\n\t\t\\__read_do_one_command:\n\t}\n\t'), functools.partial(<function naive_replace>, '\n\t\\cs_new_protected:Npn \\pyc#1 {\n\t\t\\__send_content:e { i c }\n\t\t\\__begingroup_setup_estr: \\__send_block%naive_send%:e { #1 } \\endgroup\n\t\t\\__read_do_one_command:\n\t}\n\t'), functools.partial(<function naive_replace>, '\n\t\\cs_new_protected:Npn \\pycq#1 {\n\t\t\\__send_content:e { i d }\n\t\t\\__begingroup_setup_estr: \\__send_block%naive_send%:e { #1 } \\endgroup\n\t\t\\__read_do_one_command:\n\t}\n\t'), <function mark_bootstrap.<locals>.<lambda>>, <function mark_bootstrap.<locals>.<lambda>>, functools.partial(<function naive_replace>, '\n\t\\cs_new_protected:Npn \\__pycodex#1#2#3#4 {\n\t\t\\__send_content:e { i e }\n\t\t\\__send_block:n { #1 } \\__send_content:n { #2 }\\__send_content:n { #3 }\\__send_content%naive_send%:n { #4 }\n\t\t\\__read_do_one_command:\n\t}\n\t'), functools.partial(<function naive_replace>, '\n\\cs_new_eq:NN \\__run_f: \\relax\n'), functools.partial(<function naive_replace>, '\n\\msg_new:nnn {pythonimmediate} {python-error} {Python~error:~#1.}\n\\cs_new_protected:Npn \\__run_g: {\n\t\\__read_block:N \\__data\n\t\\__read_block:N \\__summary\n\t\\wlog{^^JPython~error~traceback:^^J\\__data^^J}\n \\msg_error:nnx {pythonimmediate} {python-error} {\\__summary}\n}\n'), functools.partial(<function naive_replace>, '\n\t\t\\cs_new_protected:Npn \\__run_h: {\n\t\t\t\\__read_block:N \\__data\n\t\t\t\\begingroup \\newlinechar=10~ \\expandafter \\endgroup\n\t\t\t\\scantokens \\expandafter{\\__data}\n\t\t\t% trick described in https://tex.stackexchange.com/q/640274 to scantokens the code with \\newlinechar=10\n\n\t\t\t\\__send_content%naive_send%:e { r }\n\t\t\t\\__read_do_one_command:\n\t\t}\n\t\t'), functools.partial(<function naive_replace>, '\n\t\t\\cs_new_eq:NN \\__run_i: \\relax\n\t\t'), functools.partial(<function naive_replace>, '\n\t\t\\cs_new_protected:Npn \\__run_j: { \\expandafter \\pythonimmediatecontinuenoarg }\n\t\t'), functools.partial(<function naive_replace>, '\n\t\t\t\\cs_new_protected:Npn \\__peek_next_meaning_callback: {\n\n\t\t\t\t\\edef \\__tmp {\\meaning \\__tmp} % just in case ``\\__tmp`` is outer, ``\\write`` will not be able to handle it\n\t\t\t\t\\__send_content%naive_send%:e { r \\__tmp }\n\n\t\t\t\t\\__read_do_one_command:\n\t\t\t}\n\t\t\t\\cs_new_protected:Npn \\__run_k: {\n\t\t\t\t\\futurelet \\__tmp \\__peek_next_meaning_callback:\n\t\t\t}\n\t\t\t'), functools.partial(<function naive_replace>, '\n\t\t\t\\cs_new_protected:Npn \\__run_l: {\n\t\t\t\t\\__str_get:N \\__data \\__tldeserialize_dot:NV \\__data \\__data\n\t\t\t\t\\expandafter \\futurelet \\__data \\pythonimmediatecontinuenoarg\n\t\t\t}\n\t\t\t'), functools.partial(<function naive_replace>, '\n\t\t\t\\cs_new_protected:Npn \\__run_m: {\n\t\t\t\t\\__str_get:N \\__data \\__tldeserialize_dot:NV \\__data \\__data\n\t\t\t\t\\afterassignment \\pythonimmediatecontinuenoarg \\expandafter \\futurelet \\__data \n\t\t\t}\n\t\t\t'), functools.partial(<function naive_replace>, '\n\t\t\t\\cs_new_protected:Npn \\__put_next_blue_tmp {\n\t\t\t\t\\__send_content%naive_send%:e { r }\n\t\t\t\t\\expandafter \\__read_do_one_command: \\noexpand\n\t\t\t}\n\t\t\t\\cs_new_protected:Npn \\__run_n: {\n\t\t\t\t\\__str_get:N \\__target \\__tldeserialize_dot:NV \\__target \\__target\n\t\t\t\t\\expandafter \\__put_next_blue_tmp \\__target\n\t\t\t}\n\t\t\t'), functools.partial(<function naive_replace>, '\n\t\t\t\\cs_new_protected:Npn \\__run_o: {\n\t\t\t\t\\__str_get:N \\__data \\__tldeserialize_dot:NV \\__data \\__data\n\t\t\t\t\\exp_args:NNV \\tl_set:No \\__data \\__data\n\t\t\t\t\\__send_content:e { r }\n\t\t\t\t\\__tlserialize_nodot:NV \\__tmp \\__data \\__send_content%naive_send%:e {\\unexpanded\\expandafter{ \\__tmp } }\n\t\t\t\t\\__read_do_one_command:\n\t\t\t}\n\t\t\t'), functools.partial(<function naive_replace>, '\n\t\t\\cs_new_protected:Npn \\__run_p: {\n\t\t\t\\__str_get:N \\__data \\__tldeserialize_dot:NV \\__data \\__data\n\t\t\t\\tl_set:Nx \\__data {\\__data}\n\t\t\t\\__send_content:e { r }\n\t\t\t\\__tlserialize_nodot:NV \\__tmp \\__data \\__send_content%naive_send%:e {\\unexpanded\\expandafter{ \\__tmp } }\n\t\t\t\\__read_do_one_command:\n\t\t}\n\t\t'), functools.partial(<function naive_replace>, '\n\t\t\t\\cs_new_protected:Npn \\__run_q: {\n\t\t\t\t\\__str_get:N \\__data \\__tldeserialize_dot:NV \\__data \\__data\n\t\t\t\t\\__data\n\t\t\t\t\\__send_content%naive_send%:e { r }\n\t\t\t\t\\__read_do_one_command:\n\t\t\t}\n\t\t\t'), functools.partial(<function naive_replace>, '\n\t\t\t\\cs_new_protected:Npn \\__put_next_tmp {\n\t\t\t\t\\__send_content%naive_send%:e { r }\n\t\t\t\t\\__read_do_one_command:\n\t\t\t}\n\t\t\t\\cs_new_protected:Npn \\__run_r: {\n\t\t\t\t\\__str_get:N \\__target \\__tldeserialize_dot:NV \\__target \\__target\n\t\t\t\t\\expandafter \\__put_next_tmp \\__target\n\t\t\t}\n\t\t\t'), functools.partial(<function naive_replace>, '\n\t\t\t\\cs_new_protected:Npn \\__run_s: #1 {\n\t\t\t\t\\__send_content:e { r }\n\t\t\t\t\\__tlserialize_nodot:Nn \\__tmp { #1 } \\__send_content%naive_send%:e {\\unexpanded\\expandafter{ \\__tmp } }\n\t\t\t\t\\__read_do_one_command:\n\t\t\t}\n\t\t\t'), functools.partial(<function naive_replace>, '\n\t\t\t\t\\cs_new_protected:Npn \\__run_t: {\n\t\t\t\t\t\\exp_args:Nno \\use:nn { \\endlinechar-1 \\ior_get:NN \\__read_file \\__data \\endlinechar} {\\the\\endlinechar\\relax}\n\t\t\t\t\t\\__data\n\t\t\t\t}\n\t\t\t\t'), functools.partial(<function naive_replace>, '\n\t\t\t\t\\cs_new_protected:Npn \\__get_next_callback #1 {\n\t\t\t\t\t\\peek_analysis_map_break:n { \\pythonimmediatecontinue {^^J#1} }\n\t\t\t\t}\n\t\t\t\t\\cs_new_protected:Npn \\__run_u: {\n\t\t\t\t\t\\peek_analysis_map_inline:n {\n\t\t\t\t\t\t\\__tlserialize_char_unchecked:nNnN {##2}##3{##1} \\__get_next_callback\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t'), functools.partial(<function naive_replace>, '\n\t\t\t\t\t\\cs_new_protected:Npn \\__run_v: {\n\t\t\t\t\t\t\\__str_get:N \\__index \n\t\t\t\t\t\t\\expandafter \\expandafter \\expandafter \\pythonimmediatecontinuenoarg\n\t\t\t\t\t\t\t\\char_generate:nn {\\__index} {1}\n\t\t\t\t\t}\n\t\t\t\t\t'), functools.partial(<function naive_replace>, '\n\\cs_new_protected:Npn \\__run_w: {\n\t\\__str_get:N \\__index \n\t\\expandafter \\expandafter \\expandafter \\pythonimmediatecontinuenoarg\n\t\t\\char_generate:nn {\\__index} {2}\n}\n'), functools.partial(<function naive_replace>, '\n\t\t\\cs_new_protected:Npn \\__run_x: {\n\t\t\t\\exp_args:Nno \\use:nn { \\endlinechar-1 \\ior_get:NN \\__read_file \\__data \\endlinechar} {\\the\\endlinechar\\relax}\n\t\t\t\\__data\n\t\t\t\\__send_content%naive_send%:e { r }\n\t\t\t\\__read_do_one_command:\n\t\t}\n\t\t'), functools.partial(<function naive_replace>, '\n\t\t\\cs_new_protected:Npn \\__put_next_tmpa {\n\t\t\t\\__send_content%naive_send%:e { r }\n\t\t\t\\__read_do_one_command:\n\t\t}\n\t\t\\cs_new_protected:Npn \\__run_y: {\n\t\t\t\\exp_args:Nno \\use:nn { \\endlinechar-1 \\ior_get:NN \\__read_file \\__target \\endlinechar} {\\the\\endlinechar\\relax}\n\t\t\t\\expandafter \\__put_next_tmpa \\__target\n\t\t}\n\t\t'), functools.partial(<function naive_replace>, '\n\n\t\t\\cs_new_protected:Npn \\__run_z: #1 {\n\t\t\t\\__send_content:e { r }\n\t\t\t\\__begingroup_setup_estr: \\__send_block%naive_send%:e { #1 } \\endgroup\n\t\t\t\\__read_do_one_command:\n\t\t}\n\t\t'), functools.partial(<function naive_replace>, '\n\t\t\\NewDocumentCommand \\__run_A: {o} {\n\t\t\t\\__send_content%naive_send%:e {\n\t\t\t\tr ^^J\n\t\t\t\t\\IfNoValueTF {#1} {\n\t\t\t\t\t0\n\t\t\t\t} {\n\t\t\t\t\t\\unexpanded{1 #1}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\\__read_do_one_command:\n\t\t}\n\t\t'), functools.partial(<function naive_replace>, '\n\t\t\\NewDocumentCommand \\__run_B: {o} {\n\t\t\t\\__send_content:e { r }\n\t\t\t\\IfNoValueTF {#1} {\n\t\t\t\t\\__begingroup_setup_estr: \\__send_block%naive_send%:e { 0 } \\endgroup\n\t\t\t} {\n\t\t\t\t\\__begingroup_setup_estr: \\__send_block%naive_send%:e { 1 #1 } \\endgroup\n\t\t\t}\n\t\t\t\\__read_do_one_command:\n\t\t}\n\t\t'), functools.partial(<function naive_replace>, '\n\t\t\\NewDocumentCommand \\__run_C: {v} {\n\t\t\t\\__send_content%naive_send%:n {\n\t\t\t\tr ^^J\n\t\t\t\t#1\n\t\t\t}\n\t\t\t\\__read_do_one_command:\n\t\t}\n\t\t'), functools.partial(<function naive_replace>, '\n\t\t\\precattl_exec:n {\n\t\t\t\\NewDocumentCommand \\__run_D: {+v} {\n\t\t\t\t\\__send_content:e { r }\n\t\t\t\t\\str_set:Nn \\l_tmpa_tl { #1 }\n\t\t\t\t\\str_replace_all:Nnn \\l_tmpa_tl { \\cO\\^^M } { ^^J }\n\t\t\t\t\\__send_block%naive_send%:e { \\l_tmpa_tl }\n\t\t\t\t\\__read_do_one_command:\n\t\t\t}\n\t\t}\n\t\t'), functools.partial(<function naive_replace>, '\n\t\t\\cs_new_protected:Npn \\__run_E: {\n\t\t\t\\begingroup\n\t\t\t\t\\endlinechar=-1~\n\t\t\t\t\\__str_get:N \\__line \n\t\t\t\t\\__str_get:N \\__identifier \n\t\t\t\t\\cs_new_protected:cpx {\\__line} {\n\t\t\t\t\t\\__send_content%naive_send%:e { i \\__identifier }\n\t\t\t\t\t\\unexpanded{\\__read_do_one_command:}\n\t\t\t\t}\n\t\t\t\\endgroup\n\t\t\t\\__send_content%naive_send%:e { r }\n\t\t\t\\__read_do_one_command:\n\t\t}\n\t\t'), functools.partial(<function naive_replace>, '\n\t\t\\cs_new_protected:Npn \\__run_F: {\n\t\t\t\\begingroup\n\t\t\t\t\\endlinechar=-1~\n\t\t\t\t\\readline \\__read_file to \\__line\n\t\t\t\t\\readline \\__read_file to \\__identifier\n\t\t\t\t\\exp_args:Ncx \\renewcommand {\\__line} {\n\t\t\t\t\t\\__send_content%naive_send%:e { i \\__identifier }\n\t\t\t\t\t\\unexpanded{\\__read_do_one_command:}\n\t\t\t\t}\n\t\t\t\t\\exp_args:Nc \\MakeRobust {\\__line} % also make the command global\n\t\t\t\\endgroup\n\t\t\t\\__send_content%naive_send%:e { r }\n\t\t\t\\__read_do_one_command:\n\t\t}\n\t\t'), functools.partial(<function naive_replace>, '\n\t\t\\cs_new_protected:Npn \\__run_G: {\n\t\t\t\\__str_get:N \\__line \n\t\t\t\\__str_get:N \\__begin_identifier \n\t\t\t\\__str_get:N \\__end_identifier \n\t\t\t\\use:x {\n\t\t\t\t\\noexpand\\newenvironment\n\t\t\t\t\t{\\__line}\n\t\t\t\t\t{\n\t\t\t\t\t\t\\__send_content%naive_send%:e { i \\__begin_identifier }\n\t\t\t\t\t\t\\unexpanded{\\__read_do_one_command:}\n\t\t\t\t\t}\n\t\t\t\t\t{\n\t\t\t\t\t\t\\__send_content%naive_send%:e { i \\__end_identifier }\n\t\t\t\t\t\t\\unexpanded{\\__read_do_one_command:}\n\t\t\t\t\t}\n\t\t\t}\n\t\t\t\\__send_content%naive_send%:e { r }\n\t\t\t\\__read_do_one_command:\n\t\t}\n\t\t'), functools.partial(<function naive_replace>, '\n\t\t\\cs_new_protected:Npn \\__run_H: {\n\t\t\t\\__str_get:N \\__line \n\t\t\t\\__str_get:N \\__identifier \n\t\t\t\\use:x {\n\t\t\t\t\\noexpand\\newenvironment\n\t\t\t\t\t{\\__line}\n\t\t\t\t\t{\n\t\t\t\t\t\t\\__send_content%naive_send%:e { i \\__identifier }\n\t\t\t\t\t\t\\__read_do_one_command:\n\t\t\t\t\t}\n\t\t\t\t\t{}\n\t\t\t}\n\t\t\t\\__send_content%naive_send%:e { r }\n\t\t\t\\__read_do_one_command:\n\t\t}\n\t\t'), functools.partial(<function naive_replace>, '\n\t\t\\cs_new_protected:Npn \\__run_I: {\n\t\t\t\\exp_args:Nno \\use:nn { \\endlinechar-1 \\ior_get:NN \\__read_file \\__content \\endlinechar} {\\the\\endlinechar\\relax}\n\t\t\t\\exp_args:Nx \\pythonimmediatecontinue { \\__content }\n\t\t}\n\t\t'), functools.partial(<function naive_replace>, '\n\t\t\\cs_new_protected:Npn \\__run_J: #1 {\n\t\t\t\\__send_content%naive_send%:n {\n\t\t\t\tr #1\n\t\t\t}\n\t\t\t\\__read_do_one_command:\n\t\t}\n\t\t'), functools.partial(<function naive_replace>, '\n\t\t\t\\cs_new_protected:Npn \\__run_K: {\n\t\t\t\t\\begingroup\n\t\t\t\t\t\\endlinechar=-1~\n\t\t\t\t\t\\readline \\__read_file to \\__line\n\t\t\t\t\t\\cs_undefine:c {u8:\\__line}\n\t\t\t\t\\endgroup\n\t\t\t\t\\__send_content%naive_send%:e { r }\n\t\t\t\t\\__read_do_one_command:\n\t\t\t}\n\t\t\t'), functools.partial(<function naive_replace>, "\n\t\t\t\\precattl_exec:n {\n\t\t\t\t\\cs_new_protected:Npn \\__run_L: {\n\t\t\t\t\t\\__str_get:N \\__line \n\t\t\t\t\t\\global \\catcode \\expandafter`\\__line = \\cctab_item:Nn \\c_document_cctab {\\expandafter`\\__line} \\relax\n\t\t\t\t\t\\int_compare:nNnT {\\catcode \\expandafter`\\__line} = {13} {\n\t\t\t\t\t\t\\expandafter \\token_if_eq_charcode:NNTF \\__line \\cO\\~ {\n\t\t\t\t\t\t\t% restore ~'s original meaning\n\t\t\t\t\t\t\t\\global \\def \\cA\\~ { \\nobreakspace {} }\n\t\t\t\t\t\t} {\n\t\t\t\t\t\t\t% not sure what to do here\n\t\t\t\t\t\t\t\\msg_error:nn {pythonimmediate} {internal-error}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\\__send_content%naive_send%:e { r }\n\t\t\t\t\t\\__read_do_one_command:\n\t\t\t\t}\n\t\t\t}\n\t\t\t"), functools.partial(<function naive_replace>, '\n\t\t\t\\cs_new_protected:Npn \\__run_M: {\n\t\t\t\t\\saveenvreinsert \\__tmp {\n\t\t\t\t\t\\__send_content:e { r }\n\t\t\t\t\t\\__send_block:V \\__tmp %naive_flush%\n\t\t\t\t\t\\__read_do_one_command:\n\t\t\t\t}\n\t\t\t}\n\t\t\t'), functools.partial(<function naive_replace>, '\n\t\t\t\\cs_new_protected:Npn \\__run_N: {\n\t\t\t\t\\__str_get:N \\__line \n\t\t\t\t\\exp_args:Nno \\use:nn { \\endlinechar-1 \\ior_get:NN \\__read_file \\__value \\endlinechar} {\\the\\endlinechar\\relax}\n\t\t\t\t\\tl_set_eq:cN {\\__line} \\__value\n\t\t\t\t\\__send_content%naive_send%:e { r }\n\t\t\t\t\\__read_do_one_command:\n\t\t\t}\n\t\t\t'), functools.partial(<function naive_replace>, '\n\t\t\t\t\\cs_new_protected:Npn \\__run_O: {\n\t\t\t\t\t\\begingroup\n\t\t\t\t\t\t\\endlinechar=-1~\n\t\t\t\t\t\t\\readline \\__read_file to \\__line\n\t\t\t\t\t\t\\readline \\__read_file to \\__identifier\n\t\t\t\t\t\t\\cs_gset_protected:cpx {u8:\\__line} {\n\t\t\t\t\t\t\t\\__send_content%naive_send%:e { i \\__identifier }\n\t\t\t\t\t\t\t\\unexpanded{\\__read_do_one_command:}\n\t\t\t\t\t\t}\n\t\t\t\t\t\\endgroup\n\t\t\t\t\t\\__send_content%naive_send%:e { r }\n\t\t\t\t\t\\__read_do_one_command:\n\t\t\t\t}\n\t\t\t\t'), functools.partial(<function naive_replace>, '\n\t\t\t\t\\cs_new_protected:Npn \\__run_P: {\n\t\t\t\t\t\\begingroup\n\t\t\t\t\t\t\\endlinechar=-1~\n\t\t\t\t\t\t\\readline \\__read_file to \\__line\n\t\t\t\t\t\t\\readline \\__read_file to \\__identifier\n\t\t\t\t\t\t\\global \\catcode \\expandafter`\\__line \\active\n\t\t\t\t\t\t\\use:x {\n\t\t\t\t\t\t\t\\protected \\gdef\n\t\t\t\t\t\t\t\\expandafter \\expandafter \\expandafter \\noexpand \\char_generate:nn{\\expandafter`\\__line}{13}\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\\__send_content%naive_send%:e { i \\__identifier }\n\t\t\t\t\t\t\t\t\\unexpanded{\\__read_do_one_command:}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\\endgroup\n\t\t\t\t\t\\__send_content%naive_send%:e { r }\n\t\t\t\t\t\\__read_do_one_command:\n\t\t\t\t}\n\t\t\t\t')]¶
Internal constant. Contains functions that takes an engine object and returns some code before
substitute_private()
is applied on it.
- pythonimmediate.build_Python_call_TeX(T: Type, TeX_code: str, *, recursive: bool = True, sync: Optional[bool] = None, finish: bool = False) None [source]¶
Internal function. See
scan_Python_call_TeX()
.T has the form Callable[[T1, T2], Tuple[U1, U2]] where the Tx are subclasses of PyToTeXData and the Ux are subclasses of TeXToPyData
The Tuple[…] can optionally be a single type, then it is almost equivalent to a tuple of one element It can also be None
- pythonimmediate.can_be_mangled_to(original: str, mangled: str) bool [source]¶
If original is put in a \(\TeX\) file, read in other catcode regime (possibly drop trailing spaces/tabs), and then sent through
\write
(possibly convert control characters to^^
-notation), is it possible that the written content is equal to mangled?The function is somewhat tolerant (might return
True
in some cases whereFalse
should be returned), but not too tolerant.Example:
>>> can_be_mangled_to("a\n", "a\n") True >>> can_be_mangled_to("\n", "\n") True >>> can_be_mangled_to("\t\n", "\n") True >>> can_be_mangled_to("\t\n", "\t\n") True >>> can_be_mangled_to("\t\n", "^^I\n") True >>> can_be_mangled_to("\ta\n", "^^Ia\n") True >>> can_be_mangled_to("a b\n", "a b\n") True >>> can_be_mangled_to("a b \n", "a b\n") True >>> can_be_mangled_to("a\n", "b\n") False
- pythonimmediate.check_line(line: str, *, braces: bool, newline: bool, continue_: Optional[bool]) None [source]¶
check user-provided line before sending to TeX for execution
- pythonimmediate.continue_until_passed_back(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) None [source]¶
Same as
continue_until_passed_back_str()
but nothing can be returned from TeX to Python.
- pythonimmediate.continue_until_passed_back_str(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) str [source]¶
Usage:
First put some tokens in the input stream that includes
\pythonimmediatecontinue{...}
(or%sync% \__read_do_one_command:
), then callcontinue_until_passed_back()
.The function will only return when the
\pythonimmediatecontinue
is called.
- pythonimmediate.debug(*args, **kwargs)¶
- pythonimmediate.define_Python_call_TeX(TeX_code: str, ptt_argtypes: List[Type[PyToTeXData]], ttp_argtypes: List[Type[TeXToPyData]], *, recursive: bool = True, sync: Optional[bool] = None, finish: bool = False) Tuple[Callable[[Engine], str], PythonCallTeXFunctionType] [source]¶
Internal function.
TeX_code
should be some expl3 code that defines a function with name%name%
that when called should:run some \(\TeX\)-code (which includes reading the arguments, if any)
- do the following if
sync
: send
r
to Python (equivalently write %sync%)send whatever needed for the output (as in
ttp_argtypes
)
- do the following if
call
\__read_do_one_command:
iff notfinish
.
This is allowed to contain the following: * %name%: the name of the function to be defined as explained above. * %read_arg0(var_name)%, %read_arg1(…)%: will be expanded to code that reads the input. * %send_arg0(…)%, %send_arg1(…)%: will be expanded to code that sends the content. * %send_arg0_var(var_name)%, %send_arg1_var(…)%: will be expanded to code that sends the content in the variable. * %optional_sync%: expanded to code that writes
r
(to sync), ifsync
is True. * %naive_flush% and %naive_inline%: as explained inmark_bootstrap_naive_replace()
.(although usually you don’t need to explicitly write this, it’s embedded in the
send*()
command of the last argument, or%sync%
)
ptt_argtypes: list of argument types to be sent from Python to TeX (i.e. input of the TeX function)
ttp_argtypes: list of argument types to be sent from TeX to Python (i.e. output of the TeX function)
- recursive: whether the TeX_code might call another Python function. Default to True.
It does not hurt to always specify True, but performance would be a bit slower.
- sync: whether the Python function need to wait for the TeX function to finish.
Required if
ttp_argtypes
is not empty. This should be left to be the default None most of the time. (which will make it always sync ifdebugging
, otherwise only sync if needed i.e. there’s some output)- finish: Include this if and only if
\__read_do_one_command:
is omitted. Normally this is not needed, but it can be used as a slight optimization; and it’s needed internally to implement
run_none_finish
among others. For each TeX-call-Python layer, emph{exactly one}finish
call can be made. If the function itself doesn’t call anyfinish
call (which happens most of the time), then the wrapper will callrun_none_finish
.
Return some TeX code to be executed, and a Python function object that when called will call the TeX function on the passed-in TeX engine and return the result.
Note that the TeX_code must eventually be executed on the corresponding engine for the program to work correctly.
- Possible optimizations:
- the
r
is not needed if not recursive andttp_argtypes
is nonempty (the output itself tells Python when the \(\TeX\)-code finished)
- the
the first line of the output may be on the same line as the
r
itself (done, useTTPEmbeddedLine
type, although a bit hacky)
- pythonimmediate.define_TeX_call_Python(f: Callable[[...], None], name: Optional[str] = None, argtypes: Optional[List[Type[TeXToPyData]]] = None, identifier: Optional[str] = None) Callable[[Engine], str] [source]¶
This function setups some internal data structure, and returns the \(\TeX\)-code to be executed on the \(\TeX\)-side to define the macro.
f: the Python function to be executed. It should take some arguments plus a keyword argument engine and eventually (optionally) call one of the
_finish
functions.name: the macro name on the \(\TeX\)-side. This should only consist of letter characters in
expl3
catcode regime.argtypes: list of argument types. If it’s None it will be automatically deduced from the function
f
’s signature.Returns: some code (to be executed in
expl3
catcode regime) as explained above.
- pythonimmediate.define_internal_handler(f: Callable) Callable [source]¶
define a TeX function with TeX name = f.__name__ that calls f().
- this does not define the specified function in any particular engine, just add them to the bootstrap_code.
essert self.process is not None, “process is already closed!”
- pythonimmediate.expand_once(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) None [source]¶
Expand the following content in the input stream once.
For example, if the following tokens in the input stream are
\iffalse 1 \else 2 \fi
, then afterexpand_once()
being called once, the tokens in the input stream will be2 \fi
.
- pythonimmediate.export_function_to_module(f: FunctionType) FunctionType [source]¶
the functions decorated with this decorator are accessible from user code with
import pythonimmediate pythonimmediate.⟨function name⟩(…)
- pythonimmediate.get_bootstrap_code(engine: Engine) str [source]¶
Return the bootstrap code for an engine.
This is before the call to
substitute_private()
.
- pythonimmediate.mark_bootstrap_naive_replace(code: str) None [source]¶
Similar to
mark_bootstrap()
, but code may contain one of the following:%naive_inline
: replaced with^^J \__naive_flush_data:
if
Engine.config.naive_flush
isTrue
, else become empty
%naive_flush%
: replaced with\__send_content:e {\__naive_flush_data:}
if
Engine.config.naive_flush
isTrue
- pythonimmediate.naive_replace(code: str, naive_flush: bool, /) str [source]¶
- pythonimmediate.naive_replace(code: str, engine: Engine, /) str
- pythonimmediate.peek_next_meaning(engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) str [source]¶
Get the meaning of the following token, as a string, using the current
\escapechar
.This is recommended over
peek_next_token()
as it will not tokenize an extra token.It’s undefined behavior if there’s a newline (
\newlinechar
or^^J
, the latter is OS-specific) in the meaning string.
- pythonimmediate.read_block(engine: Engine) str [source]¶
Internal function to read one block sent from :math:`TeX`(including the final delimiter line, but the delimiter line is not returned)
- pythonimmediate.run_block_finish(block: str, engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) None [source]¶
- pythonimmediate.run_block_local(block: str, engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) None [source]¶
- pythonimmediate.run_error_finish(*args)¶
- pythonimmediate.run_none_finish(*args)¶
Internal function.
run_error_finish
is fatal to \(\TeX\), so we only run it when it’s fatal to Python.We want to make sure the Python traceback is printed strictly before run_error_finish() is called, so that the Python traceback is not interleaved with \(\TeX\) error messages.
- pythonimmediate.run_tokenized_line_peek(line: str, *, check_braces: bool = True, check_newline: bool = True, check_continue: bool = True, engine: ~pythonimmediate.engine.Engine = <pythonimmediate.engine.DefaultEngine object>) str [source]¶
- pythonimmediate.scan_Python_call_TeX(sourcecode: str) None [source]¶
Internal function.
Scan the file in filename for occurrences of
typing.cast(T, Python_call_TeX_local(...))
, then callbuild_Python_call_TeX(T, ...)
for each occurrence.The way the whole thing work is:
In the Python code, some
typing.cast(T, Python_call_TeX_local(...))
are used.This function is called on all the library source codes to scan for those occurrences, build necessary data structures for the
Python_call_TeX_local()
function calls to work correctly.When
Python_call_TeX_local()
is actually called, it does some magic to return the correct function.
Done this way, the type checking works correctly and it’s not necessary to define global temporary variables.
Don’t use this function on untrusted code.
- pythonimmediate.scan_Python_call_TeX_module(name: str) None [source]¶
Internal function. Can be used as
scan_Python_call_TeX_module(__name__)
to scan the current module.