import struct class Struct(object): __slots__ = ('__attrs__', '__baked__', '__defs__', '__endian__', '__next__', '__sizes__', '__values__') int8 = ('b', 1) uint8 = ('B', 1) int16 = ('h', 2) uint16 = ('H', 2) int32 = ('l', 4) uint32 = ('L', 4) float = ('f', 4) @classmethod def string(cls, len): return ('s', len) LE = '<' BE = '>' __endian__ = '<' def __init__(self): self.__defs__ = [] self.__sizes__ = [] self.__attrs__ = [] self.__values__ = {} self.__next__ = True self.__baked__ = False self.__format__() self.__baked__ = True def __setattr__(self, name, value): if name in self.__slots__: return object.__setattr__(self, name, value) if self.__baked__ == False: type, size = value if type == 's': self.__defs__.append(Struct.string) self.__sizes__.append(size) self.__attrs__.append(name) self.__values__[name] = '' self.__next__ = True else: if self.__next__: self.__defs__.append('') self.__sizes__.append(0) self.__attrs__.append([]) self.__next__ = False self.__defs__[-1] += type self.__sizes__[-1] += size self.__attrs__[-1].append(name) self.__values__[name] = 0 else: self.__values__[name] = value def __getattr__(self, name): if self.__baked__ == False: return name else: try: return self.__values__[name] except KeyError: raise AttributeError(name) def unpack(self, data): pos = 0 for i in range(len(self.__defs__)): sdef, size, attrs = self.__defs__[i], self.__sizes__[i], self.__attrs__[i] if sdef == Struct.string: if isinstance(size, str): size = self.__values__[size] self.__values__[attrs] = data[pos:pos+size] pos += size else: values = struct.unpack(self.__endian__+sdef, data[pos:pos+size]) pos += size j = 0 for name in attrs: self.__values__[name] = values[j] j += 1 return self def pack(self): ret = '' for i in range(len(self.__defs__)): sdef, size, attrs = self.__defs__[i], self.__sizes__[i], self.__attrs__[i] if sdef == Struct.string: if isinstance(size, str): size = self.__values__[size] temp = self.__values__[attrs][:size] ret += temp + ('\0' * (size - len(temp))) else: values = [self.__values__[name] for name in attrs] ret += struct.pack(self.__endian__+sdef, *values) return ret if __name__=='__main__': class TestStruct(Struct): __endian__ = Struct.LE def __format__(self): self.foo, self.bar = Struct.uint32, Struct.float self.baz = Struct.string(8) self.omg = Struct.uint32 self.wtf = Struct.string(self.omg) test = TestStruct() test.unpack('\xEF\xBE\xAD\xDE\x00\x00\x80\x3Fdeadbeef\x04\x00\x00\x00test') print test.bar assert test.foo == 0xDEADBEEF assert test.bar == 1.0 assert test.baz == 'deadbeef' assert test.omg == 4 assert test.wtf == 'test' test.foo = 0xCAFEBABE test.bar = 0.0 assert test.pack() == '\xBE\xBA\xFE\xCA\0\0\0\0deadbeef\x04\x00\x00\x00test'