Skip to content

Latest commit

 

History

History
 
 

codegen

Generated code design

This document lays out the required (and optional) functions of the code generated by codegen.py, and their signatures. The code is generated based on the format described on the nif.xml wiki, with some additional flexibility for the names of tags and attributes. The generated classes fall into one of 4 cases:

  1. Basic. This corresponds to the basic tag in the xml. These classes are not generated, merely copied from the source for that format. They do not have a shared class unless they had one in the source.
  2. Enum. This corresponds to the enum tag in the xml. These classes all inherit from the BaseEnum class defined in base_enum.py.
  3. Bitfield. This corresponds to the bitfield, bitflags and bitstruct tags in the xml. These classes all inherit from the BasicBitfield class defined in bitfield.py.
  4. Struct. This corresponds to the struct, compound and niobject tags in the xml. These classes all inherit from the BaseStruct class defined in base_struct.py.

This also means that, in the generated python objects, every field in a struct falls into one of those four categories (the associated type, not necessarily the value). There is one exception:

  1. Array. Any field which has the arr1 (or length) field specified will have the Array class , defined in array.py, as its type. The actual type of the field is passed via an extra argument.
    • There is also the RaggedArray class. This class is used for arrays where shape[1] is not an integer, but instead an iterable of integers. However, this is an implementation detail, as access to the RaggedArray class can be handled solely through the Array class, which will defer functions to the RaggedArray class if necessary.

There are a number of shared functions across every class. Strict adherence to this is necessary/desirable for the following reasons:

  • Implementation of type="#T#". Standardized functions means that functions can be called on self.template without knowing beforehand what class it is.
  • Code simplification. By standardizing the functions and introducing iteration over the fields, the generated code for structs is much less, and Array code is simplified.

It should be noted that most shared functions are class or static methods instead of instance methods. This is because any class is not guaranteed to return an instance of itself when reading or instancing. A typical example of this would be basic classes which are ints or floats, or SizedString, which in an xml may be described as a struct with an integer for the length and an array of characters, but may return a python str.

Shared across all classes

Required

Every class must have three basic functions. These functions are necessary for reading and writing files for the generated format, as well as instantiation of the defined objects without a file to read from:

  • Instantiation of the class object (either through __new__ or __init__ function). This does not necessarily have to return an actual instance of the class.
    • Function signature: class(context, arg, template)
      • context: object which stores file-specific variables.
      • arg: corresponds to the arg attribute specified in a field.
      • template: corresponds to the template attribute specified in a field.
  • Class- or static method from_stream which reads from the stream and returns an instance of the same type as instantiation does.
    • Function signature: class.from_stream(stream, context, arg, template)
      • stream: binary stream from which to read the object.
      • context: object which stores file-specific variables.
      • arg: corresponds to the arg attribute specified in a field.
      • template: corresponds to the template attribute specified in a field.
  • Class- or static method to_stream which writes an instance to the stream.
    • Function signature: class.to_stream(instance, stream, context, arg, template)
      • instance: object that quacks like the object returned by instantiation/reading of this class.
      • stream: binary stream to which to write the object.
      • context: object which stores file-specific variables.
      • arg: corresponds to the arg attribute specified in a field.
      • template: corresponds to the template attribute specified in a field.

Optional

Optional interfaces

The following functions are implemented on the 4 classes included by default in source (BaseEnum, BasicBitfield, BaseStruct and Array). They are not integral to essential functionality (reading, writing and instantation) but may prove useful in various cases. If you make use of these, they must be implemented on all basics.

  • Class- or static method format_indented, which takes in an object and returns a string. Useful for printing.
    • Function signature: class.format_indented(member, indent)
      • member: object that quacks like the object returned by instantiation/reading of this class.
      • indent: used for pretty-printing, mostly to allow for a tree-like representation of structs within structs.
  • Class- or static method get_size, used for getting the size (in bytes) of the associated instance.
    • Function signature: class.get_size(instance, context, arg, template)
      • context: object which stores file-specific variables.
      • instance: object that quacks like the object returned by instantiation/reading of this class.
      • arg: corresponds to the arg attribute specified in a field.
      • template: corresponds to the template attribute specified in a field.
  • Class- or static method from_xml, used for conversion from xml file to python objects in much the same way as from_stream does from binary.
    • Function signature: class.from_xml(target, elem, prop, arg, template)
      • target: parent object.
      • elem: xml element associated with the parent object.
      • prop: name of the field of the parent where the instance should be.
      • arg: corresponds to the arg attribute specified in a field.
      • template: corresponds to the template attribute specified in a field.
  • Class- or static method to_xml, used for conversion from python objects to xml file in much the same way as to_stream does to binary.
    • Function signature: class.to_xml(elem, prop, instance, arg, template, debug)
      • elem: xml element associated with the parent object.
      • prop: name of the field of the parent where the instance should be.
      • instance: object that quacks like the object returned by instantiation/reading of this class.
      • arg: corresponds to the arg attribute specified in a field.
      • template: corresponds to the template attribute specified in a field.
      • debug: whether to write in debugging mode.
  • Class- or static method validate_instance, used to check whether the instance matches the given arguments. Can be used before file writing to find incorrect fields.
    • Function signature: class.validate_instance(instance, context, arg, template)
      • instance: object that quacks like the object returned by instantiation/reading of this class.
      • context: object which stores file-specific variables.
      • arg: corresponds to the arg attribute specified in a field.
      • template: corresponds to the template attribute specified in a field.

Optional per-class

The following functions don't have to be implemented on any classes, but may be implemented on some.

  • Class- or static method from_value. Used to convert a value (typically an int, float or tuple, but isn't limited to that) to an instance of the same type as returned by instantiation/reading of this class. Bitfields and enums already have this implemented, any other classes will need to have it manually defined in source.
    • Function signature: class.from_value(value)
      • value: any object which the class can use to instance itself.
  • Class- or static method create_array. If present, is used instead to instantiate arrays for this class, instead of an Array with this class as its dtype.
    • Function signature: class.create_array(shape, default, context, arg, template)
      • shape: shape of the array.
      • default: the value with which to fill every point of the array (as if filling the array with from_value(default).
      • context: object which stores file-specific variables.
      • arg: corresponds to the arg attribute specified in a field.
      • template: corresponds to the template attribute specified in a field.
  • Class- or static method read_array. If present, is used to read arrays from the stream instead of the Array class.
    • Function signature: class.read_array(stream, shape, context, arg, template)
      • stream: binary stream from which to read the array.
      • shape: shape of the array.
      • context: object which stores file-specific variables.
  • Class- or static method write_array. If present, is used to write arrays to the stream instead of the Array class.
    • Function signature: class.write_array(stream, instance)
      • stream: binary stream to which to write the array.
      • instance: array that quacks like the array returned by instantiation/reading of this dtype's array.
  • Class- or static method validate_array. If present, is used to validate arrays instead of the Array class.
    • Function signature: class.validate_array(instance, context, arg, template, shape)
      • instance: array
      • context: array that quacks like the array returned by instantiation/reading of this dtype's array.
      • arg: corresponds to the arg attribute specified in a field.
      • template: corresponds to the template attribute specified in a field.
      • shape: corresponds to the arr1/length and arr2/width attribute specified in a field.

Class specific

Structs and Arrays

Structs and Arrays share some similarity because they both can contain the other classes and themselves. To iterate over these fields and access them, the following functions are used. Note that these functions do not have to exist on, for example, a struct class, it merely indicates that the fields do not exist or are not accessible on the object resulting from instantiation/reading.

  • Class- or static method _get_filtered_attribute_list, which returns an iterator over the fields whose conditions currently evaluate to false. These field definitions do not return the fields value.
    • Function signature: _get_filtered_attribute_list(instance, include_abstract).
      • instance: object that quacks like the object returned by instantiation/reading of this class.
      • include_abstract: whether to include fields with the abstract="true" attribute. These fields are not found in the file, but are found on the resulting objects.
    • Returns: for every field, field_name, field_type, arguments, (optional, default). If the field has no arr1 or length attribute defined in the xml, field_type corresponds to the type attribute and arguments is (arg, template) corresponding to the attributes. If the field does have arr1 or length attribute defined in the xml, then field_type is Array, and arguments is (arg, template, shape, dtype), with shape being the shape tuple of the array and dtype corresponding to the type attribute of the field.
  • Class- or static method get_field, which is used to access the fields returned by _get_filtered_attribute_list through their name.