usage: generate_python_backend.py [-h] [--input INPUT] [--output OUTPUT] [--custom-type-location CUSTOM_TYPE_LOCATION]
options:
-h, --help show this help message and exit
--input INPUT Input PDL-JSON source
--output OUTPUT Output Python file
--custom-type-location CUSTOM_TYPE_LOCATION
Module of declaration of custom types
Example invocation:
cargo run my-protocol.pdl --output-format json | \
./pdl-compiler/scripts/generate_python_backend.py > my-protocol.py
The generator produces a pure python implementation of the parser and serializer for the selected grammar, using only builtin features of the Python language. The generated constructs are all type annotated and _should_ pass the type validation.
All packets inherit either from their parent declaration or at the root a blanket Packet class implementation.
@dataclass
class Packet:
payload: Optional[bytes] = field(repr=False, default_factory=bytes, compare=False)
enum TestEnum : 8 { A = 1, B = 2..3, C = 4, OTHER = .., } |
class TestEnum(enum.IntEnum):
A = 1
C = 4
@staticmethod
def from_int(v: int) -> typing.Union[TestEnum, int]:
pass |
Note
Python enums are closed by construction, default cases in enum declarations are ignored. The static method from_int provides validation for enum tag ranges.
packet TestPacket { a: 8, b: TestEnum, } |
@dataclass
packet TestPacket(Packet):
a: int = field(kw_only=True, default=0)
b: TestEnum = field(kw_only=True, default=TestEnum.A)
@staticmethod
def parse(span: bytes) -> Tuple['TestPacket', bytes]:
pass
def serialize(self, payload: bytes = None) -> bytes:
pass
@property
def size(self) -> int:
pass |
packet TestPacket: ParentPacket { a: 8, b: TestEnum, } |
@dataclass
packet TestPacket(ParentPacket):
a: int = field(kw_only=True, default=0)
b: TestEnum = field(kw_only=True, default=TestEnum.A)
@staticmethod
def parse(span: bytes) -> Tuple['TestPacket', bytes]:
pass
def serialize(self, payload: bytes = None) -> bytes:
pass
@property
def size(self) -> int:
pass |
Fields without a binding name do not have a concrete representation in the generated class, but are nonetheless validated during parsing or implicitely generated during serialization.
a: 8 |
a: int = field(kw_only=True, default=0) |
a: TestEnum, b: TestStruct |
a: TestEnum = field(kw_only=True, default=TestEnum.A)
b: TestStruct = field(kw_only=True,
default_factory=TestStruct) |
a: 8[], b: 16[128], c: TestEnum[], d: TestStruct[] |
a: List[int] = field(kw_only=True, default_factory=list)
b: List[int] = field(kw_only=True, default_factory=list)
c: List[TestEnum] = field(kw_only=True,
default_factory=list)
d: List[TestStruct] = field(kw_only=True,
default_factory=list) |
a: 8 if c_a = 1, b: TestEnum if c_b = 1, c: TestStruct if c_c = 1, |
a: Optional[int] = field(kw_only=True, default=None)
b: Optional[TestEnum] = field(kw_only=True, default=None)
c: Optional[TestStruct] = field(kw_only=True,
default=None) |