vc2_conformance.fixeddict: Fixed-key dictionaries

The vc2_conformance.fixeddict module provides the fixeddict() function for creating new dict subclasses which permit only certain keys to be used. These new types may be used like ordinary Python dictionaries but add three main features:

  • Explicitness – The VC-2 pseudocode creates and uses many dictionaries (called ‘maps’ in the specification) in place of struct-like objects. fixeddicts provide a way to give these clear names.

  • Avoidance of typos – Misspelt key names will result in a FixedDictKeyError.

  • Better pretty printing – See more below…

Tutorial

Using fixeddict(), dictionary-like types with well defined fields can be described like so:

>>> from vc2_conformance.fixeddict import fixeddict

>>> FrameSize = fixeddict(
...     "FrameSize",
...     "custom_dimensions_flag",
...     "frame_width",
...     "frame_height",
... )

This produces a ‘dict’ subclass called FrameSize with all of the usual dictionary behaviour but which only allows the specified keys to be used:

>>> f = FrameSize()
>>> f["custom_dimensions_flag"] = True
>>> f["frame_width"] = 1920
>>> f["frame_height"] = 1080

>>> f["frame_width"]
1920

>>> f["not_in_fixeddict"] = 123
Traceback (most recent call last):
  ...
FixedDictKeyError: 'not_in_fixeddict'

To improve readability when producing string representations of VC-2 data structures, the generated dictionary types have a ‘pretty’ string representation.

>>> print(f)
FrameSize:
  custom_dimensions_flag: True
  frame_width: 1920
  frame_height: 1080

To further improve the readability of this output, custom string formatting functions may be provided for each entry in the dictionary. To define these, Entry instances must be used in place of key name strings like so:

>>> from vc2_conformance.string_formatters import Hex
>>> from vc2_data_tables import ParseCodes  # An IntEnum
>>> ParseInfo = fixeddict(
...     "ParseInfo",
...     Entry("parse_info_prefix", formatter=Hex(8)),
...     Entry("parse_code", enum=ParseCodes, formatter=Hex(2)),
...     Entry("next_parse_offset"),
...     Entry("previous_parse_offset"),

>>> pi = ParseInfo(
...     parse_info_prefix=0x42424344,
...     parse_code=0x10,
...     next_parse_offset=0,
...     previous_parse_offset=0,
... )
>>> str(pi)
ParseInfo:
   parse_info_prefix: 0x42424344
   parse_code: end_of_sequence (0x10)
   next_parse_offset: 0
   previous_parse_offset: 0

See the vc2_conformance.string_formatters module for a set of useful string formatting functions.

Finally, documentation can optionally be added in the form of help and help_type arguments which will combined into the generated type’s docstring:

>>> ParseInfo = fixeddict(
...     "ParseInfo",
...     Entry("parse_info_prefix", formatter=Hex(8), help_type="int", help="Always 0x42424344"),
...     Entry("parse_code", enum=ParseCodes, formatter=Hex(2), help_type="int"),
...     Entry("next_parse_offset", help_type="int"),
...     Entry("previous_parse_offset", help_type="int"),
...     help="A deserialised parse info block.",
... )

>>> print(ParseInfo.__doc__)
A deserialised parse info block.

Parameters
==========
parse_info_prefix : int
    Always 0x42424344
parse_code : int
next_parse_offset : int
previous_parse_offset : int

API

fixeddict(name, *entries, **kwargs)

Create a fixed-entry dictionary.

A fixed-entry dictionary is a dict subclass which permits only a preset list of key names.

The first argument is the name of the created class, the remaining arguments may be strings or Entry instances describing the allowed entries in the dictionary.

Example usage:

>>> ExampleDict = fixeddict(
...     "ExampleDict",
...     "attr",
...     Entry("attr_with_default"),
... )

Instances of the dictionary can be created like an ordinary dictionary:

>>> d = ExampleDict(attr=10, attr_with_default=20)
>>> d["attr"]
10
>>> d["attr_with_default"]
20

The string format of generated dictionaries includes certain pretty-printing behaviour (see Entry) and will also omit any entries whose name is prefixed with an underscore (_).

The class itself will have a static (and read-only) attribute entry_objs which is a :py;class:collections.OrderedDict mapping from entry name to Entry object in the dictionary.

The keyword-only argument, ‘module’ may be provided which overrides the __module__ value of the returned fixeddict type. (By default the module name is inferred using runtime stack inspection, if possible). This must be set correctly for this type to be picklable.

The keyword-only argument ‘help’ may be used to set the docstring of the returned class. This will automatically be appended with the list of entries allowed (and their help strings).

class Entry(name, **kwargs)

Defines advanced properties of of an entry in a fixeddict() dictionary.

All constructor arguments, except name, are keyword-only.

Parameters
namestr

The name of this entry in the dictionary.

formatterfunction(value) -> string

A function which takes a value and returns a string representation to use when printing this value as a string. Defaults to ‘str’.

friendly_formatterfunction(value) -> string

If provided, when converting this value to a string, this function will be used to generate a ‘friendly’ name for this value. This will be followed by the actual value in brackets. If this function returns None, only the actual value will be shown (without brackets).

enumEnum

A convenience interface which is equivalent to the following formatter argument:

def enum_formatter(value):
    try:
        return str(MyEnum(value).value)
    except ValueError:
        return str(value)

And the following friendly_formatter argument:

def friendly_enum_formatter(value):
    try:
        return MyEnum(value).name
    except ValueError:
        return None

If formatter or friendly_formatter are provided in addition to enum, they will override the functions implicitly defined by enum.

helpstr

Optional documentation string.

help_typestr

Optional string describing the type of the entry.

exception FixedDictKeyError(key, fixeddict_class)

A KeyError which also includes information about which fixeddict dictionary it was produced by.

Attributes
key

The key which was accessed.

fixeddict_class

The fixeddict type of the dictionary used.