From 5bc193bc2d9c936b26a9e91693e1c6e35c7da4a7 Mon Sep 17 00:00:00 2001 From: Marty Alchin Date: Fri, 22 Jul 2011 21:39:04 -0700 Subject: [PATCH] Store fields in an OrderedDict to better support Structure subclassing --- biwako/bit/base.py | 8 ++++---- biwako/byte/base.py | 36 ++++++++++++++++----------------- biwako/byte/compression.py | 4 ++-- biwako/byte/fields/integrity.py | 2 +- biwako/chunks/base.py | 18 ++++++++--------- biwako/common/fields.py | 4 ++-- biwako/common/meta.py | 13 +++++++++++- 7 files changed, 48 insertions(+), 37 deletions(-) diff --git a/biwako/bit/base.py b/biwako/bit/base.py index 9e5cc04..b38b3b9 100644 --- a/biwako/bit/base.py +++ b/biwako/bit/base.py @@ -31,11 +31,11 @@ def get_raw_bytes(self): output = bytearray() bits_read = 0 byte = 0 - for field in self.__class__._fields: - if field.name not in self._raw_values: - setattr(self, field.name, getattr(self, field.name)) + for name, field in self.__class__._fields.items(): + if name not in self._raw_values: + setattr(self, name, getattr(self, name)) bits_read += field.size - bits = self._raw_values[field.name] + bits = self._raw_values[name] byte = (byte << field.size) + bits while bits_read >= 8: byte >>= 8 diff --git a/biwako/byte/base.py b/biwako/byte/base.py index f4b2bcf..02f603e 100644 --- a/biwako/byte/base.py +++ b/biwako/byte/base.py @@ -34,8 +34,8 @@ def write(self, data): raise IOError("not writable") file = EOFBytesIO(self._write_buffer + data) last_position = 0 - for field in self.__class__._fields: - if field.name not in self.__dict__: + for name, field in self.__class__._fields.items(): + if name not in self.__dict__: try: try: bytes = field.read(file) @@ -43,8 +43,8 @@ def write(self, data): except fields.FullyDecoded as obj: bytes = obj.bytes value = obj.value - self._raw_values[field.name] = bytes - self.__dict__[field.name] = value + self._raw_values[name] = bytes + self.__dict__[name] = value last_position = file.tell() except EOFError: file.seek(last_position) @@ -58,26 +58,26 @@ def tell(self): def _extract(self, field): if field.name not in self._raw_values: - for other_field in self._fields: - if other_field.name not in self._raw_values: + for name, other_field in self._fields.items(): + if name not in self._raw_values: with other_field.for_instance(self): try: bytes = other_field.read(self) except fields.FullyDecoded as obj: bytes = obj.bytes - self.__dict__[other_field.name] = obj.value - self._raw_values[other_field.name] = bytes - if other_field.name == field.name: + self.__dict__[name] = obj.value + self._raw_values[name] = bytes + if name == field.name: break return self._raw_values[field.name] def get_raw_bytes(self): output = b'' - for field in self.__class__._fields: - value = getattr(self, field.name) - if field.name not in self._raw_values: - setattr(self, field.name, getattr(self, field.name)) - output += self._raw_values[field.name] + for name, field in self.__class__._fields.items(): + value = getattr(self, name) + if name not in self._raw_values: + setattr(self, name, getattr(self, name)) + output += self._raw_values[name] return output def get_parent(self): @@ -90,9 +90,9 @@ def save(self, file): def validate(self): errors = [] - for field in self._fields: + for name, field in self._fields.items(): try: - field.validate(self, getattr(self, field.name)) + field.validate(self, getattr(self, name)) except ValueError as error: errors.append(str(error)) return errors @@ -125,8 +125,8 @@ def parse(self, file): position = file.tell() try: value = self.structure(file) - for field in self.structure._fields: - getattr(value, field.name) + for name, field in self.structure._fields.items(): + getattr(value, name) except Exception as e: if file.tell() == position: # The file didn't move, so it must be at the end diff --git a/biwako/byte/compression.py b/biwako/byte/compression.py index 39917f9..c078068 100644 --- a/biwako/byte/compression.py +++ b/biwako/byte/compression.py @@ -24,8 +24,8 @@ def __init__(self, *args, decompress=True, **kwargs): if process_chunk: super(ChunkMixin, self).__init__(chunk.payload) payload = self._compressor.compress(data) - for field in chunk._fields: - getattr(chunk, field.name) + for name in chunk._fields: + getattr(chunk, name) id = chunk.id id = self._chunk.id if chunk.id != self._chunk.id: diff --git a/biwako/byte/fields/integrity.py b/biwako/byte/fields/integrity.py index 24257f7..52da5d3 100644 --- a/biwako/byte/fields/integrity.py +++ b/biwako/byte/fields/integrity.py @@ -21,7 +21,7 @@ def attach_to_class(self, cls): self.fields = [] in_range = False - for field in cls._fields: + for field in cls._fields.values(): if field is self: # Can't go beyond the integrity field itself break diff --git a/biwako/chunks/base.py b/biwako/chunks/base.py index b80c34e..bcbc828 100644 --- a/biwako/chunks/base.py +++ b/biwako/chunks/base.py @@ -33,9 +33,9 @@ def read(cls, file): # Force the evaluation of the entire structure in # order to make sure other fields work properly value_bytes = b'' - for field in cls.structure._fields: - getattr(value, field.name) - value_bytes += value._raw_values[field.name] + for name in cls.structure._fields: + getattr(value, name) + value_bytes += value._raw_values[name] return value_bytes, value @@ -47,8 +47,8 @@ class ChunkMixin: def __init__(self, *args, process_chunk=True, **kwargs): if process_chunk: chunk = self._chunk.structure(*args, **kwargs) - for field in chunk._fields: - getattr(chunk, field.name) + for name in chunk._fields: + getattr(chunk, name) id = chunk.id id = self._chunk.id if chunk.id != self._chunk.id: @@ -123,16 +123,16 @@ def parse(self, file): while 1: chunk = self.base_chunk.structure(file) if chunk.id in self.parsers: - for field in chunk._fields: - getattr(chunk, field.name) + for name in chunk._fields: + getattr(chunk, name) value = self.parsers[chunk.id](chunk.payload, process_chunk=False) if self.terminator and isinstance(chunk, self.terminator): break yield value elif chunk.id: # This is a valid chunk, just not a recognized type - for field in chunk._fields: - getattr(chunk, field.name) + for name in chunk._fields: + getattr(chunk, name) yield chunk else: # This is not a valid chunk, which is probably the end of the file diff --git a/biwako/common/fields.py b/biwako/common/fields.py index 0365fe9..f3132e5 100644 --- a/biwako/common/fields.py +++ b/biwako/common/fields.py @@ -117,7 +117,7 @@ def set_name(self, name): self.label = label.title() def attach_to_class(self, cls): - cls._fields.append(self) + cls._fields[self.name] = self def validate(self, obj, value): # This should raise a ValueError if the value is invalid @@ -217,7 +217,7 @@ def encode(self, value): def __getattr__(self, name): if 'structure' in self.__dict__: field = getattr(self.structure, name) - if field in self.structure._fields: + if field in self.structure._fields.values(): field = copy.copy(field) field._parent = self return field diff --git a/biwako/common/meta.py b/biwako/common/meta.py index 2e7457d..d4e6b76 100644 --- a/biwako/common/meta.py +++ b/biwako/common/meta.py @@ -33,10 +33,21 @@ def __new__(cls, name, bases, attrs, **options): return type.__new__(cls, name, bases, attrs) def __init__(cls, name, bases, attrs, **options): - cls._fields = [] + cls._fields = collections.OrderedDict() + # Go backwards so that the left-most classes take priority + print('%s: %s (%s)' % (name, ', '.join(b.__name__ for b in bases), cls)) + for base in bases: + if hasattr(base, '_fields'): + print('%s %s' % (base, base._fields)) + cls._fields.update(base._fields) + for name, attr in attrs.items(): + if name in cls._fields and attr is None: + del cls._fields[name] if hasattr(attr, 'attach_to_class'): attr.attach_to_class(cls) + + print('%s %s' % (cls, cls._fields)) data.field_options = {} data.field_stack = [[]]