Python codec

This page describes how to encode, decode and manipulate prophy messages in Python.

Compilation

Prophy Compiler can be used to generate Python codec source code from .prophy files. This generated code together with Python prophy library forms a fully functional codec.

Example compiler invocation:

prophyc --python_out . test.prophy

will result in creating test.py.

Generated code

File generated by Prophy Compiler defines classes representing enums, structs and unions.

Enumerators can be accessed:

enum Test1
{
    Test1_1 = 1,
    Test1_2 = 2,
    Test1_3 = 3
};
>>> import test
>>> test.Test1_1
1

Structs can be instantiated, written or read and encoded or decoded:

struct Test2
{
    u32 a;
};
>>> import test
>>> x = test.Test2()
>>> x.a = 42
>>> x.a
42
>>> print x
a: 42
>>> x.encode('>')
'\x00\x00\x00*'
>>> x.decode('\x00\x00\x00*', '>')
4

Struct enum field can be set by value or name, name can be extracted from it:

struct Test3
{
    Test1 a;
};
>>> import test
>>> x = test.Test3()
>>> x.a = 2
>>> x.a = 'Test1_2'
>>> x.a
2L
>>> x.a.name
'Test1_2'

Arrays (all kinds) may be indexed, sliced and iterated:

struct Test4
{
    i32 a[3];
};
>>> import test
>>> x = test.Test4()
>>> x.a[0] = 42
>>> x.a
[42, 0, 0]
>>> x.a[:] = [1, 2, 3]
>>> for value in x.a:
...     print value
...
1
2
3

Arrays of structs or unions can add new elements or be extended by iterables of them:

struct Test5
{
    Test2 a<>;
};
>>> import test
>>> x = test.Test5()
>>> y = x.a.add()
>>> y.a = 42
>>> print x
a {
  a: 42
}

>>> x.a.extend([y])
>>> print x
a {
  a: 42
}
a {
  a: 42
}

Optional struct fields may be set or cleared by setting with True and None:

struct Test6
{
    u32* a;
    Test2* b;
};
>>> import test
>>> x = test.Test6()
>>> x.a = 42
>>> x.a
42
>>> x.a = None
>>> x.a
>>> x.b = True
>>> print x.b
a: 0

>>> x.b = None
>>> print x.b
None

Union arm is chosen by setting discriminator with arm number or name:

union Test7
{
    0: u32 a;
    1: Test2 b;
};
>>> import test
>>> x = test.Test7()
>>> x.discriminator = 0
>>> x.a = 42
>>> x.discriminator = 'b'
>>> x.b.a = 42

Introspection

Struct and union can be introspected by get_descriptor and get_discriminated methods:

struct Struct
{
    u32 a;
    u8 b[4];
};

union Union
{
    0: u32 a;
    1: u16 b;
};
>>> import test
>>> test.Struct.get_descriptor()
[<a, ('INT', 0), <class 'prophy.scalar.u32'>>, <b, ('ARRAY', 3), <class 'prophy.container._array'>>]
>>> test.Struct().get_descriptor()
[<a, ('INT', 0), <class 'prophy.scalar.u32'>>, <b, ('ARRAY', 3), <class 'prophy.container._array'>>]
>>> test.Union.get_descriptor()
[<a, ('INT', 0), <class 'prophy.scalar.u32'>>, <b, ('INT', 0), <class 'prophy.scalar.u16'>>]
>>> test.Union().get_descriptor()
[<a, ('INT', 0), <class 'prophy.scalar.u32'>>, <b, ('INT', 0), <class 'prophy.scalar.u16'>>]
>>> test.Union().get_discriminated()
<a, ('INT', 0), <class 'prophy.scalar.u32'>>

>>> field_desc = test.Struct.get_descriptor()[0]
>>> field_desc.name
'a'
>>> field_desc.kind
('INT', 0)
>>> field_desc.type
<class 'prophy.scalar.u32'>
>>> import prophy
>>> field_desc.kind == prophy.kind.INT
True

Packed mode

Python generated message descriptors may be altered to inhibit padding by inheriting from struct_packed instead of struct. Following message would be encoded as 6 bytes:

class PackedMessage(prophy.struct_packed):
    __metaclass__ = prophy.struct_generator
    _descriptor = [('x', prophy.u8),
                   ('y', prophy.u32),
                   ('z', prophy.u8)]

Warning

Mixing struct_packed with nested struct and otherwise yields undefined behavior.