Source code for usb_protocol.emitters.construct_interop

# -*- coding: utf-8 -*-
#
# This file is part of usb-protocol.
#
""" Helpers for creating construct-related emitters. """

import construct


[docs] def emitter_for_format(construct_format): """ Creates a factory method for the relevant construct format. """ def _factory(): return ConstructEmitter(construct_format) return _factory
[docs] 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): """ :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. :param format_object: The Construct format to work with. This includes e.g. most descriptor types. :param 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
[docs] 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}")