pyffi.spells — High level file operations

Note

This module is based on wz’s NifTester module, although nothing of wz’s original code is left in this module.

A toaster, implemented by subclasses of the abstract Toaster class, walks over all files in a folder, and applies one or more transformations on each file. Such transformations are called spells, and are implemented by subclasses of the abstract Spell class.

A spell can also run independently of a toaster and be applied on a branch directly. The recommended way of doing this is via the Spell.recurse() method.

Supported spells

For format specific spells, refer to the corresponding module.

Some spells are applicable on every file format, and those are documented here.

class pyffi.spells.SpellApplyPatch(toaster=None, data=None, stream=None)

Bases: pyffi.spells.Spell

A spell for applying a patch on files.

datainspect()

There is no need to read the whole file, so we apply the patch already at inspection stage, and stop the spell process by returning False.

Returns:False
Return type:bool

Adding new spells

To create new spells, derive your custom spells from the Spell class, and include them in the Toaster.SPELLS attribute of your toaster.

class pyffi.spells.Spell(toaster=None, data=None, stream=None)

Bases: object

Spell base class. A spell takes a data file and then does something useful with it. The main entry point for spells is recurse(), so if you are writing new spells, start with reading the documentation with recurse().

READONLY
A bool which determines whether the spell is read only or not. Default value is True. Override this class attribute, and set to False, when subclassing a spell that must write files back to the disk.
SPELLNAME
A str describing how to refer to the spell from the command line. Override this class attribute when subclassing.
data
The Data instance this spell acts on.
stream
The current file being processed.
toaster
The Toaster instance this spell is called from.
__init__(toaster=None, data=None, stream=None)

Initialize the spell data.

Parameters:
recurse(branch=None)

Helper function which calls _branchinspect() and branchinspect() on the branch, if both successful then branchentry() on the branch, and if this is succesful it calls recurse() on the branch’s children, and once all children are done, it calls branchexit().

Note that _branchinspect() and branchinspect() are not called upon first entry of this function, that is, when called with data as branch argument. Use datainspect() to stop recursion into this branch.

Do not override this function.

Parameter:branch (GlobalNode) – The branch to start the recursion from, or None to recurse the whole tree.
_datainspect()

This is called after pyffi.object_models.FileFormat.Data.inspect() has been called, and before pyffi.object_models.FileFormat.Data.read() is called.

Returns:True if the file must be processed, False otherwise.
Return type:bool
datainspect()

This is called after pyffi.object_models.FileFormat.Data.inspect() has been called, and before pyffi.object_models.FileFormat.Data.read() is called. Override this function for customization.

Returns:True if the file must be processed, False otherwise.
Return type:bool
_branchinspect(branch)

Check if spell should be cast on this branch or not, based on exclude and include options passed on the command line. You should not need to override this function: if you need additional checks on whether a branch must be parsed or not, override the branchinspect() method.

Parameter:branch (GlobalNode) – The branch to check.
Returns:True if the branch must be processed, False otherwise.
Return type:bool
branchinspect(branch)

Like _branchinspect(), but for customization: can be overridden to perform an extra inspection (the default implementation always returns True).

Parameter:branch (GlobalNode) – The branch to check.
Returns:True if the branch must be processed, False otherwise.
Return type:bool
dataentry()

Called before all blocks are recursed. The default implementation simply returns True. You can access the data via data, and unlike in the datainspect() method, the full file has been processed at this stage.

Typically, you will override this function to perform a global operation on the file data.

Returns:True if the children must be processed, False otherwise.
Return type:bool
dataexit()

Called after all blocks have been processed, if dataentry() returned True.

Typically, you will override this function to perform a final spell operation, such as writing back the file in a special way, or making a summary log.

branchentry(branch)

Cast the spell on the given branch. First called with branch equal to data‘s children, then the grandchildren, and so on. The default implementation simply returns True.

Typically, you will override this function to perform an operation on a particular block type and/or to stop recursion at particular block types.

Parameter:branch (GlobalNode) – The branch to cast the spell on.
Returns:True if the children must be processed, False otherwise.
Return type:bool
branchexit(branch)

Cast a spell on the given branch, after all its children, grandchildren, have been processed, if branchentry() returned True on the given branch.

Typically, you will override this function to perform a particular operation on a block type, but you rely on the fact that the children must have been processed first.

Parameter:branch (GlobalNode) – The branch to cast the spell on.
classmethod toastentry(toaster)

Called just before the toaster starts processing all files. If it returns False, then the spell is not used. The default implementation simply returns True.

For example, if the spell only acts on a particular block type, but that block type is excluded, then you can use this function to flag that this spell can be skipped. You can also use this function to initialize statistics data to be aggregated from files, to initialize a log file, and so.

Parameter:toaster (Toaster) – The toaster this spell is called from.
Returns:True if the spell applies, False otherwise.
Return type:bool
classmethod toastexit(toaster)

Called when the toaster has finished processing all files.

Parameter:toaster (Toaster) – The toaster this spell is called from.

Grouping spells together

It is also possible to create composite spells, that is, spells that simply execute other spells. The following functions and classes can be used for this purpose.

pyffi.spells.SpellGroupParallel(*args)
Class factory for grouping spells in parallel.
pyffi.spells.SpellGroupSeries(*args)
Class factory for grouping spells in series.
class pyffi.spells.SpellGroupBase(toaster, data, stream)

Bases: pyffi.spells.Spell

Base class for grouping spells. This implements all the spell grouping functions that fall outside of the actual recursing (__init__(), toastentry(), _datainspect(), datainspect(), and toastexit()).

ACTIVESPELLCLASSES
List of active spells of this group (not instantiated). This list is automatically built when toastentry() is called.
SPELLCLASSES
List of Spells of this group (not instantiated).
datainspect()
Inspect every spell with L{Spell.datainspect} and keep those spells that must be cast.
spells
List of active spell instances.
classmethod toastentry(toaster)
classmethod toastexit(toaster)
class pyffi.spells.SpellGroupParallelBase(toaster, data, stream)

Bases: pyffi.spells.SpellGroupBase

Base class for running spells in parallel (that is, with only a single recursion in the tree).

branchentry(branch)
Run all spells.
branchexit(branch)
branchinspect(branch)
Inspect spells with Spell.branchinspect() (not all checks are executed, only keeps going until a spell inspection returns True).
dataentry()
Look into every spell with Spell.dataentry().
dataexit()
Look into every spell with Spell.dataexit().
class pyffi.spells.SpellGroupSeriesBase(toaster, data, stream)

Bases: pyffi.spells.SpellGroupBase

Base class for running spells in series.

branchentry(branch)
branchinspect(branch)
dataentry()
dataexit()
recurse(branch=None)
Recurse spells in series.

Creating toaster scripts

To create a new toaster script, derive your toaster from the Toaster class, and set the Toaster.FILEFORMAT attribute of your toaster to the file format class of the files it can toast.

class pyffi.spells.Toaster(spellclass=None, options=None)

Bases: object

Toaster base class. Toasters run spells on large quantities of files. They load each file and pass the data structure to any number of spells.

ALIASDICT
Dictionary with aliases for spells.
EXAMPLES
Some examples which describe typical use of the toaster.
FILEFORMAT

The file format class (a subclass of FileFormat).

alias of FileFormat

SPELLS
List of all available Spell classes.
cli()
Command line interface: initializes spell classes and options from the command line, and run the toast() method.
exclude_types
Tuple of types corresponding to the exclude key of options.
include_types
Tuple of types corresponding to the include key of options.
indent
An int which describes the current indentation level for messages.
isadmissiblebranchtype(branchtype)

Helper function which checks whether a given branch type should have spells cast on it or not, based in exclude and include options.

>>> from pyffi.formats.nif import NifFormat
>>> class MyToaster(Toaster):
...     FILEFORMAT = NifFormat
>>> toaster = MyToaster() # no include or exclude: all admissible
>>> toaster.isadmissiblebranchtype(NifFormat.NiProperty)
True
>>> toaster.isadmissiblebranchtype(NifFormat.NiNode)
True
>>> toaster.isadmissiblebranchtype(NifFormat.NiAVObject)
True
>>> toaster.isadmissiblebranchtype(NifFormat.NiLODNode)
True
>>> toaster.isadmissiblebranchtype(NifFormat.NiMaterialProperty)
True
>>> toaster = MyToaster(options={"exclude": ["NiProperty", "NiNode"]})
>>> toaster.isadmissiblebranchtype(NifFormat.NiProperty)
False
>>> toaster.isadmissiblebranchtype(NifFormat.NiNode)
False
>>> toaster.isadmissiblebranchtype(NifFormat.NiAVObject)
True
>>> toaster.isadmissiblebranchtype(NifFormat.NiLODNode)
False
>>> toaster.isadmissiblebranchtype(NifFormat.NiMaterialProperty)
False
>>> toaster = MyToaster(options={"include": ["NiProperty", "NiNode"]})
>>> toaster.isadmissiblebranchtype(NifFormat.NiProperty)
True
>>> toaster.isadmissiblebranchtype(NifFormat.NiNode)
True
>>> toaster.isadmissiblebranchtype(NifFormat.NiAVObject)
False
>>> toaster.isadmissiblebranchtype(NifFormat.NiLODNode) # NiNodes are!
True
>>> toaster.isadmissiblebranchtype(NifFormat.NiMaterialProperty) # NiProperties are!
True
>>> toaster = MyToaster(options={"include": ["NiProperty", "NiNode"], "exclude": ["NiMaterialProperty", "NiLODNode"]})
>>> toaster.isadmissiblebranchtype(NifFormat.NiProperty)
True
>>> toaster.isadmissiblebranchtype(NifFormat.NiNode)
True
>>> toaster.isadmissiblebranchtype(NifFormat.NiAVObject)
False
>>> toaster.isadmissiblebranchtype(NifFormat.NiLODNode)
False
>>> toaster.isadmissiblebranchtype(NifFormat.NiSwitchNode)
True
>>> toaster.isadmissiblebranchtype(NifFormat.NiMaterialProperty)
False
>>> toaster.isadmissiblebranchtype(NifFormat.NiAlphaProperty)
True
logger
A logging.Logger for toaster log messages.
msg(message)

Write log message with logger.info(), taking into account indent.

Parameter:message (str) – The message to write.
msgblockbegin(message)
Acts like msg(), but also increases indent after writing the message.
msgblockend(message=None)
Acts like msg(), but also decreases indent before writing the message, but if the message argument is None, then no message is printed.
options
The options of the toaster, as dict.
spellclasses
List of spell classes of the particular Toaster instance.
toast(top)

Walk over all files in a directory tree and cast spells on every file.

Parameter:top (str) – The directory or file to toast.
writeover(stream, data)
Overwrites original file with data, but restores file if fails.
writepatch(stream, data)
Creates a binary patch for the updated file.
writeprefix(stream, data)
Writes the data to a file, appending a prefix to the original file name.
writetemp(stream, data)
Writes the data to a temporary file and raises an exception if the write fails.