Module latexpp.fix — the base fix class¶
This module provides the base class for fixes.
See Writing a custom fix for a tutorial and reference on writing a custom fix class.
Base fix class¶
- class latexpp.fix.BaseFix¶
Base class for defining specific latexpp fixes.
A fix should be defined by defining a subclass of this class and overriding the methods that will allow to transform nodes.
The methods you should consider overriding are:
fix_node()— this method will be called once for each node in the document. You can then “fix” the node however you like.specs()— provide additional macro, environment and specials definitions to the LaTeX parser.initialize()andfinalize()— these will be called before all fixes and after all fixes have run, respectively. You can do stuff here like scanning an aux file, or other tasks that need to be performed once only.add_preamble()— include additional definitions to the preamble of the document.
- lpp¶
The lpp attribute holds a reference to the preprocessor object (a
latexpp.preprocessor.LatexPreprocessorinstance). You can use this to access some additional functions such asself.lpp.copy_file().Note, if you create an instance of the fix manually, you need to call
set_lpp()to set the lpp attribute.
- add_preamble()¶
Fixes can add arbitrary code to the LaTeX preamble by subclassing this method and returning the required preamble definitions as a string. The default implementation returns None, which means that no additional preamble definitions are requested.
Currently, the preamble definitions are included from the start, before running all the fixes and they are processed as part of the document with all the relevant fixes. [Don’t count on this behavior, it doesn’t sound very logical to me in hindsight, so I might change it in the future.]
- finalize()¶
Method that is called after all fixes have finished processing their transformations (“fixes”). This might be a good time to do any finish-up work, generate a log file with a report, or whatever one-off task you have to do at the end of your processing.
The default implementation does nothing, reimplement to do something useful for your fix.
- fix_name()¶
Returns the full name/identifier of the fix, like ‘latexpp.fixes.ref.ExpandRefs’. No need to reimplement this.
- fix_node(node, *, is_single_token_arg=False, prev_node=None, next_node=None)¶
Transforms a given node to implement the fixes provided by this fix class.
In most cases, your fix class only needs to reimplement this method
fix_node().Subclasses should inspect node and return one of either:
return None: If the present node does not need to be transformed in any way. (In this case, the reimplemented method does not need to inspect child nodes, as they will automatically be visited separately.)
return a string: The string is parsed into a node list, and the resulting nodes are used as a replacement of the original node.
return a node instance or a node list: The node(s) are used in the place of the original node.
If this method returns a valid replacement for the given node, i.e., anything that is not None, then this method is responsible for recursing into child nodes. This can be done by using the methods
preprocess(),preprocess_child_nodes(),preprocess_latex(), andpreprocess_contents_latex()on the new nodes.If this method returns None, then child nodes will be preprocessed automatically.
This method may raise
DontFixThisNode, which has exactly the same effect as returning None.With this method the nodes are considered one by one. If you need to act globally on the full node list, you will have to override
fix_nodelist(). You should not override bothfix_nodelist()andfix_nodes().This method gets some keyword arguments that provide some contextual hints:
If is_single_token_arg=True, this means that the node is in fact a single token used as an argument to a macro, environment or specials, a for the first argument in
\newcommand\Hmax{...}. Such nodes will not have any arguments, e.g., this is not an invocation of\Hmaxwith arguments.The previous node in the list of nodes that is being processed is provided as prev_node (when available). It is None if the node is first in the list of nodes.
Note
Subclasses are strongly advised to accept **kwargs to accommodate future hints that might be introduced.
- fix_nodelist(nodelist)¶
This method is one of two methods responsible for implementing the node transformations for this fix class.
In most cases, you should only override
fix_node(). In advanced cases where you need to act on the whole list globally (perhaps to detect specific sequences of nodes, etc.), then you need to reimplementfix_nodelist().You should not reimplement both
fix_nodelist()andfix_node().This method should not modify nodelist itself, rather, it should return a representation of the transformed node list.
This method should return None, a node list, or a string. A None return value signals that no transformation is to be carried out at the root level. If a node list is returned, it is used as the transformed list. If a string is returned, it is parsed into a node list again and that node list is used.
If this method returns None, then we will automatically call
fix_node()for each node of the nodelist and concatenate the results into a new node list.If this method returns a list or a string, it is also responsible for recursing and preprocessing all relevant child nodes in that list or string. Use the methods
preprocess(),preprocess_child_nodes(),preprocess_latex(), andpreprocess_contents_latex()for that purpose.By default,
fix_nodelist()returns None.
- initialize()¶
This method is called at the beginning, after having parsed the document, but before any other fixes actually process the document.
This is a good opportunity to parse an AUX file, to create a sub-preprocessor, or perform some other task that needs to be done once before applying transformations throughout the document.
The default implementation does nothing, reimplement to do something useful for your fix.
- node_get_arg(node, argn)¶
Return the node that corresponds to the argn-th argument (starting at zero) of the given node.
If node is not a macro, environment, or specials node, an error is raised to help detect bugs.
If node does not have an argument list (node.nodeargd is None), then
DontFixThisNodeis raised. This can happen if the bare macro is given as a single token argument e.g. to another macro. In these cases you’ll usually want to abort the fix; raisingDontFixThisNodeis equivalent to returning None infix_node().If node has an argument list (node.nodeargd.argnlist) that does not have enough arguments, then an error is raised to help detect bugs.
- parse_nodes(s, parsing_state)¶
Parses the given string s with an appropriate LatexWalker to get a node list again, using the given parsing_state.
Returns the node list. Raises
pylatexenc.latexwalker.LatexWalkerParseErrorif there was a parse error.
- preprocess(nodelist)¶
Process the nodelist and apply all relevant transformations that this fix is meant to carry out. Return a new node list that corresponds to the transformed version of nodelist.
This function is what someone should call to get a full processed version of nodelist. This method takes care of descending into child nodes and applying the fixes recursively by calling preprocess() on children nodes.
Don’t subclass this, rather, you should subclass
fix_nodelist()orfix_node().
- preprocess_arg_latex(n, argn)¶
Same as
self.preprocess_contents_latex(self.node_get_arg(n, argn)).
- preprocess_child_nodes(node)¶
Call self.preprocess() on all children of the given node node and modifies the node attributes in place. Children include group contents (if a latex group), argument nodes (if a macro, environment, or latex specials), etc.
This method does not return anything interesting.
- preprocess_contents_latex(n)¶
Same as
preprocess_latex(), except that if n is a group node, then its contents is returned without the delimiters. This is useful for expanding macro arguments, for instance:# transform \textbf{...} -> {\bfseries ...} class MyFix(fixes.BaseFix): def fix_node(self, n, **kwargs): if n.isNodeType(LatexMacroNode) and n.macroname == 'textbf': # recursively apply fixes to macro argument: arg_node = self.node_get_arg(n, 0) return r'{\bfseries ' \ + self.preprocess_contents_latex(arg_node) \ + r'}'
Had we used
self.preprocess_latex()instead, we would have obtained the replacement string{\bfseries {...}}with an extra pair of inner braces.Note that you can also use
preprocess_arg_latex()which is equivalent toself.preprocess_contents_latex(self.node_get_arg(n, 0)).
- preprocess_latex(n)¶
Collects the latex representation of the given node(s) after having recursively preprocessed them with the present fix.
The argument n may be None, a single node instance, or a node list.
You may use this in your
fix_node()implementations to ensure that preprocessing acts recursively in your replacement strings. For instance:# transform \begin{equation*} .. \end{equation*} -> \[ .. \] class MyFix(fixes.BaseFix): def fix_node(self, n, **kwargs): if n.isNodeType(LatexEnvironmentNode) \ and n.environmentname == 'equation*': # recursively apply fixes to body: return r'\[' + self.preprocess_latex(n.nodelist) + r'\]'
- set_lpp(lpp)¶
Set the
lppattribute to lpp. If you create a fix instance you should call this so that the fix can call utility methods on the preprocessor object.You don’t have to call set_lpp() on fixes that are loaded via the preprocessor’s
install_fix()method.
- specs()¶
Return any custom macro, environment and specials specifications that might be needed for the latex walker’s context.
Return None if no definitions are needed, or return a dict with keys ‘macros’, ‘environments’, and ‘specials’ with lists of corresponding definitions. The return value of specs() should be suitable for use as keyword arguments to
pylatexenc.macrospec.LatexContextDb.add_context_category().Specify a
pylatexenc.macrospec.MacroSpecfor each macro you want to define. The second argument is a string that describes what arguments the macro expects. Each character in the string may be ‘*’, ‘[’ or ‘{’ indicating an optional star (‘*’), an optional argument delimited by square brackets, or a mandatory argument (single token or braced group). The arguments are stored in the node.nodeargd.argnlist array in the parsed node (seepylatexenc.latexwalker.LatexMacroNode) with their order being exactly the one given in the argument specification string where a value None indicates that an optional star or optional argument was not specified.See
pylatexenc.macrospec.MacroSpec,pylatexenc.macrospec.EnvironmentSpec, andpylatexenc.macrospec.SpecialsSpecfor more detailed information on specifying macro, environment, and “latex specials” argument signatures.The default implementation returns None. Reimplement this method to provide additional macro/environment/latex-specials for the parser’s consideration.
- class latexpp.fix.DontFixThisNode¶
Bases:
ExceptionCan be raised in
BaseFix.fix_node()to indicate that the given node should not be “fixed”.
Base class for multi-stage fixes¶
- class latexpp.fix.BaseMultiStageFix¶
Bases:
BaseFixImplement a fix that requires multiple passes through the document.
On some occasions you want to implement a fix that requires multiple stages of processing. For instance, the
latexpp.fixes.labels.RenameLabelsfix first needs to run through the document to detect all\label{eq:xyz}commands, and then re-run through the document to replace labels in commands like\ref{eq:xyz}.To implement a multi-stage fix, you simply inherit this class. If you inherit this class DO NOT REIMPLEMENT ANY OF THE `fix_node()`, `fix_nodelist()`, `preprocess()`, OR OTHER METHODS OF THAT FAMILY. The entire processing is left to the individual
Stageobjects, which are themselves Fix objects that support fix_node(), preprocess(), etc., which you can use normally.The BaseMultiStageFix is essentially a thin wrapper that calls each stage’s preprocess() function in sequence (again, each stage is a full “fix” object).
Minimal example:
class CountMeStageFix(BaseMultiStageFix): def __init__(self): super().__init__() self.number_of_countmes = 0 self.add_stage(self.CountMacros(self)) self.add_stage(self.ReplaceMacros(self)) class CountMacros(BaseMultiStageFix.Stage): # silly example: count number of "\countme" macros in document def fix_node(self, n, **kwargs): if n.isNodeType(latexwalker.LatexMacroNode) and n.macroname == 'countme': self.parent_fix.number_of_countmes += 1 return None class ReplaceMacros(BaseMultiStageFix.Stage): # silly example: change "\numberofcountme" macro into the actual number of # "\countme" macros encountered in document def fix_node(self, n, **kwargs): if n.isNodeType(latexwalker.LatexMacroNode) \ and n.macroname == 'numberofcountme': return str(self.parent_fix.number_of_countmes) return None
- class Stage(parent_fix)¶
Bases:
BaseFixA specific stage in a multi-stage fix. A “stage” is itself a fix object (this class inherits
BaseFix) so you can reimplement the usual fix_node() or fix_nodelist() (seeBaseFix).- parent_fix¶
You can use the attribute self.parent_fix to refer to the parent fix object (the one that inherits
BaseMultiStageFix) and access its properties when implementing your Stage object.
You can also use the usual attributes provided by
BaseFix(for instance, you can use self.lpp when implementing your Stage).The
initialize()andfinalize()methods are honored, but the are called simultaneously for all stages before any stage is run and after all stages have run, respectively. The methodsstage_start()andstage_finish(), in contrast, are called immediately before and after the present stage is run.Warning
The methods specs() and add_preamble() do not work if you try to implement them here. (You should implement them on the parent fix instead).
- stage_finish()¶
This method is called immediately after this stage is run, before any succeeding stages are run. (In constrast, all stages’ finalize() method are run after all stages are run.)
- stage_name()¶
Return a short name that describes this stage within this fix (by default, this is the stage’s simple class name).
- stage_start()¶
This method is called immediately before this stage is run, after any preceding stages have been run. (In constrast, all stages’ initialize() method are run before any stage is run.)
- finalize()¶
Calls all stages’ finalize() members in stage sequence. Don’t forget to call the base class’ implementation if you reimplement this method.
Note that this calls the stages’ finalize method at once only after all the stages have finished running. See also
Stage.stage_start()andStage.stage_finish().
- initialize()¶
Calls all stages’ initialize() members in stage sequence. Don’t forget to call the base class’ implementation if you reimplement this method.
Note that this calls the stages’ initialize method at once, before any of the stages are actually run. See also
Stage.stage_start()andStage.stage_finish().
- preprocess(nodelist)¶
Process the nodelist and apply all relevant transformations that this fix is meant to carry out. Return a new node list that corresponds to the transformed version of nodelist.
This function is what someone should call to get a full processed version of nodelist. This method takes care of descending into child nodes and applying the fixes recursively by calling preprocess() on children nodes.
Don’t subclass this, rather, you should subclass
fix_nodelist()orfix_node().
- set_lpp(lpp)¶
Set the
lppattribute to lpp. If you create a fix instance you should call this so that the fix can call utility methods on the preprocessor object.You don’t have to call set_lpp() on fixes that are loaded via the preprocessor’s
install_fix()method.