|
- #
- # This file is part of usb-protocol.
- #
- """ Helpers for creating construct-related emitters. """
-
- import unittest
- import construct
-
-
- class ConstructEmitter:
- """ Class that creates a simple emitter based on a construct struct.
-
- For example, if we have a construct format that looks like the following:
- MyStruct = struct(
- "a" / Int8
- "b" / Int8
- )
-
- We could create emit an object like follows:
- emitter = ConstructEmitter(MyStruct)
- emitter.a = 0xab
- emitter.b = 0xcd
- my_bytes = emitter.emit() # "\xab\xcd"
- """
-
- def __init__(self, struct):
- """
- Parmeters:
- construct_format -- The format for which to create an emitter.
- """
- self.__dict__['format'] = struct
- self.__dict__['fields'] = {}
-
-
- def _format_contains_field(self, field_name):
- """ Returns True iff the given format has a field with the provided name.
-
- Parameters:
- format_object -- The Construct format to work with. This includes e.g. most descriptor types.
- field_name -- The field name to query.
- """
- return any(f.name == field_name for f in self.format.subcons)
-
-
- def __setattr__(self, name, value):
- """ Hook that we used to set our fields. """
-
- # If the field starts with a '_', don't handle it, as it's an internal field.
- if name.startswith('_'):
- super().__setattr__(name, value)
- return
-
- if not self._format_contains_field(name):
- raise AttributeError(f"emitter specification contains no field {name}")
-
- self.fields[name] = value
-
-
- def emit(self):
- """ Emits the stream of bytes associated with this object. """
-
- try:
- return self.format.build(self.fields)
- except KeyError as e:
- raise KeyError(f"missing necessary field: {e}")
-
-
- def __getattr__(self, name):
- """ Retrieves an emitter field, if possible. """
-
- if name in self.fields:
- return self.fields[name]
- else:
- raise AttributeError(f"descriptor emitter has no property {name}")
-
-
- class ConstructEmitterTest(unittest.TestCase):
-
- def test_simple_emitter(self):
-
- test_struct = construct.Struct(
- "a" / construct.Int8ul,
- "b" / construct.Int8ul
- )
-
- emitter = ConstructEmitter(test_struct)
- emitter.a = 0xab
- emitter.b = 0xcd
-
- self.assertEqual(emitter.emit(), b"\xab\xcd")
-
-
- def emitter_for_format(construct_format):
- """ Creates a factory method for the relevant construct format. """
-
- def _factory():
- return ConstructEmitter(construct_format)
-
- return _factory
-
-
- if __name__ == "__main__":
- unittest.main()
|