pythonimmediate.multiengine module

class pythonimmediate.multiengine.MultiChildProcessEngine(engine_name: Literal['pdftex', 'xetex', 'luatex'], count: int = 2, *args: Any, **kwargs: Any)[source]

Bases: Engine

An engine that can be used to run multiple identical child processes. This is useful for terminating one in order to observe its output.


There must be no randomization in the child process.


>>> from pythonimmediate.util import pdftotext
>>> from pythonimmediate import execute, default_engine
>>> with MultiChildProcessEngine("pdftex", 2) as engine, default_engine.set_engine(engine):
...     execute(r"\documentclass{article} \pagenumbering{gobble} \begin{document} \begin{center} Hello")
...     with engine.extract_one() as child1:
...         execute(r"\end{center} \end{document}", expecting_exit=True) # only execute on child1
...         output1=child1.read_output_file()
...     execute(r"world")
...     with engine.extract_one() as child2:
...         execute(r"\end{center} \end{document}", expecting_exit=True)
...         output2=child2.read_output_file()
>>> pdftotext(output1, ["-nopgbrk"]).strip()
>>> pdftotext(output2, ["-nopgbrk"]).strip()
b'Hello world'

Basically you need to:

  • Start an engine. See explanation of parameters below.

  • Note that it is mandatory to call __enter__().

  • Execute commands on the engine.

  • In other to observe the output, use extract_one().


This is not thread-safe.

  • count – the number of child processes to start by at initialization. You can start more or less later with start_child_process() and extract_one().

  • args – arguments to pass to the child process constructor. Refer to ChildProcessEngine.

extract_one(do_replace: bool = True) Generator[ChildProcessEngine, None, None][source]

Extract one child process from the engine. See MultiChildProcessEngine for an example.


do_replace – whether to replace the extracted child process with a new one.

transient_context() Generator[None, None, None][source]

A context that can be used to mark the code being executed as transient.

This is useful to reduce overhead of restarting the engine when the code does not mutate the state of the engine.

The actual explanation is a bit complicated, and depends on the implementation detail. When a child process is restarted, everything is replayed from the beginning.

In addition, in an transient context, only the first child process is used.

>>> from pythonimmediate import T, execute, default_engine
>>> with MultiChildProcessEngine("pdftex", 2) as engine, default_engine.set_engine(engine):
...     T.l_tmpa_tl.str("Hello world") # mutates the state, cannot be put in transient context
...     with engine.transient_context():
...         with engine.transient_context():
...             T.l_tmpa_tl.str() # does not mutate the state, can be put in transient context
'Hello world'
'Hello world'

As seen above, this context manager can be nested as well.