When experimenting with any of the supported file formats, you can specify an alternate location where you store your modified format description by means of an environment variable. For instance, to tell the library to use your version of cgf.xml, set the CGFXMLPATH environment variable to the directory where cgf.xml can be found. The environment variables NIFXMLPATH, KFMXMLPATH, DDSXMLPATH, and TGAXMLPATH work similarly.
This section tries to explain how you can implement your own format in pyffi.
Note that the files which make up the following example can all be found in the examples/simple directory of the source distribution of pyffi.
Suppose you have a simple file format, which consists of an integer, followed by a list of integers as many as described by the first integer. We start by creating an XML file, call it simple.xml, which describes this format in a way that pyffi can understand:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE fileformat> <fileformat version="1.0"> <basic name="Int">A signed 32-bit integer.</basic> <struct name="Example"> <add name="Num Integers" type="Int"> Number of integers that follow. </add> <add name="Integers" type="Int" arr1="Num Integers"> A list of integers. </add> </struct> </fileformat>
What pyffi does is convert this simple XML description into Python classes which automatically can read and write the structure you’ve just described. Say this is the contents of simple.py:
import os import pyffi.object_models.xml import pyffi.object_models.common class SimpleFormat(pyffi.object_models.xml.FileFormat): xml_file_name = 'simple.xml' xml_file_path = [ os.path.dirname(__file__) ] # basic types Int = pyffi.object_models.common.Int # extensions of generated types class Data(pyffi.object_models.xml.FileFormat.Data): def __init__(self): self.example = SimpleFormat.Example() def read(self, stream): self.example.read(stream, self) def write(self, stream): self.example.write(stream, self) class Example: def addInteger(self, x): self.numIntegers += 1 self.integers.update_size() self.integers[self.numIntegers-1] = x
What happens in this piece of code?
- The pyffi.object_models.xml.FileFormat base class triggers the transformation of xml into Python classes; how these classes can be used will be explained further.
- The xml_file_name class attribute provides the name of the xml file that describes the structures we wish to generate. The xml_file_path attribute gives a list of locations of where to look for this file; in our case we have simply chosen to put simple.xml in the same directory as simple.py.
- The SimpleFormat.Example class provides the generated class with a function addInteger() in addition to the attributes numIntegers and integers which have been created from the XML.
- Finally, the pyffi.object_models.common module implements the most common basic types, such as integers, characters, and floats. In the above example we have taken advantage of pyffi.object_models.common.Int, which defines a signed 32-bit integer, exactly the type we need.
To read the contents of a file of the format described by simple.xml:
from simple import SimpleFormat x = SimpleFormat.Data() f = open('somefile.simple', 'rb') x.read(f) f.close() print x.example
Or, to create a new file in this format:
from simple import SimpleFormat x = SimpleFormat.Data() x.example.num_integers = 5 x.example.integers.update_size() x.example.integers = 3 x.example.integers = 1 x.example.integers = 4 x.example.integers = 1 x.example.integers = 5 f = open('pi.simple', 'wb') x.write(f) f.close()