Skip to content

Commit

Permalink
Store fields in an OrderedDict to better support Structure subclassing
Browse files Browse the repository at this point in the history
  • Loading branch information
gulopine committed Jul 23, 2011
1 parent fad92ca commit 5bc193b
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 37 deletions.
8 changes: 4 additions & 4 deletions biwako/bit/base.py
Expand Up @@ -31,11 +31,11 @@ def get_raw_bytes(self):
output = bytearray() output = bytearray()
bits_read = 0 bits_read = 0
byte = 0 byte = 0
for field in self.__class__._fields: for name, field in self.__class__._fields.items():
if field.name not in self._raw_values: if name not in self._raw_values:
setattr(self, field.name, getattr(self, field.name)) setattr(self, name, getattr(self, name))
bits_read += field.size bits_read += field.size
bits = self._raw_values[field.name] bits = self._raw_values[name]
byte = (byte << field.size) + bits byte = (byte << field.size) + bits
while bits_read >= 8: while bits_read >= 8:
byte >>= 8 byte >>= 8
Expand Down
36 changes: 18 additions & 18 deletions biwako/byte/base.py
Expand Up @@ -34,17 +34,17 @@ def write(self, data):
raise IOError("not writable") raise IOError("not writable")
file = EOFBytesIO(self._write_buffer + data) file = EOFBytesIO(self._write_buffer + data)
last_position = 0 last_position = 0
for field in self.__class__._fields: for name, field in self.__class__._fields.items():
if field.name not in self.__dict__: if name not in self.__dict__:
try: try:
try: try:
bytes = field.read(file) bytes = field.read(file)
value = field.decode(bytes) value = field.decode(bytes)
except fields.FullyDecoded as obj: except fields.FullyDecoded as obj:
bytes = obj.bytes bytes = obj.bytes
value = obj.value value = obj.value
self._raw_values[field.name] = bytes self._raw_values[name] = bytes
self.__dict__[field.name] = value self.__dict__[name] = value
last_position = file.tell() last_position = file.tell()
except EOFError: except EOFError:
file.seek(last_position) file.seek(last_position)
Expand All @@ -58,26 +58,26 @@ def tell(self):


def _extract(self, field): def _extract(self, field):
if field.name not in self._raw_values: if field.name not in self._raw_values:
for other_field in self._fields: for name, other_field in self._fields.items():
if other_field.name not in self._raw_values: if name not in self._raw_values:
with other_field.for_instance(self): with other_field.for_instance(self):
try: try:
bytes = other_field.read(self) bytes = other_field.read(self)
except fields.FullyDecoded as obj: except fields.FullyDecoded as obj:
bytes = obj.bytes bytes = obj.bytes
self.__dict__[other_field.name] = obj.value self.__dict__[name] = obj.value
self._raw_values[other_field.name] = bytes self._raw_values[name] = bytes
if other_field.name == field.name: if name == field.name:
break break
return self._raw_values[field.name] return self._raw_values[field.name]


def get_raw_bytes(self): def get_raw_bytes(self):
output = b'' output = b''
for field in self.__class__._fields: for name, field in self.__class__._fields.items():
value = getattr(self, field.name) value = getattr(self, name)
if field.name not in self._raw_values: if name not in self._raw_values:
setattr(self, field.name, getattr(self, field.name)) setattr(self, name, getattr(self, name))
output += self._raw_values[field.name] output += self._raw_values[name]
return output return output


def get_parent(self): def get_parent(self):
Expand All @@ -90,9 +90,9 @@ def save(self, file):


def validate(self): def validate(self):
errors = [] errors = []
for field in self._fields: for name, field in self._fields.items():
try: try:
field.validate(self, getattr(self, field.name)) field.validate(self, getattr(self, name))
except ValueError as error: except ValueError as error:
errors.append(str(error)) errors.append(str(error))
return errors return errors
Expand Down Expand Up @@ -125,8 +125,8 @@ def parse(self, file):
position = file.tell() position = file.tell()
try: try:
value = self.structure(file) value = self.structure(file)
for field in self.structure._fields: for name, field in self.structure._fields.items():
getattr(value, field.name) getattr(value, name)
except Exception as e: except Exception as e:
if file.tell() == position: if file.tell() == position:
# The file didn't move, so it must be at the end # The file didn't move, so it must be at the end
Expand Down
4 changes: 2 additions & 2 deletions biwako/byte/compression.py
Expand Up @@ -24,8 +24,8 @@ def __init__(self, *args, decompress=True, **kwargs):
if process_chunk: if process_chunk:
super(ChunkMixin, self).__init__(chunk.payload) super(ChunkMixin, self).__init__(chunk.payload)
payload = self._compressor.compress(data) payload = self._compressor.compress(data)
for field in chunk._fields: for name in chunk._fields:
getattr(chunk, field.name) getattr(chunk, name)
id = chunk.id id = chunk.id
id = self._chunk.id id = self._chunk.id
if chunk.id != self._chunk.id: if chunk.id != self._chunk.id:
Expand Down
2 changes: 1 addition & 1 deletion biwako/byte/fields/integrity.py
Expand Up @@ -21,7 +21,7 @@ def attach_to_class(self, cls):


self.fields = [] self.fields = []
in_range = False in_range = False
for field in cls._fields: for field in cls._fields.values():
if field is self: if field is self:
# Can't go beyond the integrity field itself # Can't go beyond the integrity field itself
break break
Expand Down
18 changes: 9 additions & 9 deletions biwako/chunks/base.py
Expand Up @@ -33,9 +33,9 @@ def read(cls, file):
# Force the evaluation of the entire structure in # Force the evaluation of the entire structure in
# order to make sure other fields work properly # order to make sure other fields work properly
value_bytes = b'' value_bytes = b''
for field in cls.structure._fields: for name in cls.structure._fields:
getattr(value, field.name) getattr(value, name)
value_bytes += value._raw_values[field.name] value_bytes += value._raw_values[name]


return value_bytes, value return value_bytes, value


Expand All @@ -47,8 +47,8 @@ class ChunkMixin:
def __init__(self, *args, process_chunk=True, **kwargs): def __init__(self, *args, process_chunk=True, **kwargs):
if process_chunk: if process_chunk:
chunk = self._chunk.structure(*args, **kwargs) chunk = self._chunk.structure(*args, **kwargs)
for field in chunk._fields: for name in chunk._fields:
getattr(chunk, field.name) getattr(chunk, name)
id = chunk.id id = chunk.id
id = self._chunk.id id = self._chunk.id
if chunk.id != self._chunk.id: if chunk.id != self._chunk.id:
Expand Down Expand Up @@ -123,16 +123,16 @@ def parse(self, file):
while 1: while 1:
chunk = self.base_chunk.structure(file) chunk = self.base_chunk.structure(file)
if chunk.id in self.parsers: if chunk.id in self.parsers:
for field in chunk._fields: for name in chunk._fields:
getattr(chunk, field.name) getattr(chunk, name)
value = self.parsers[chunk.id](chunk.payload, process_chunk=False) value = self.parsers[chunk.id](chunk.payload, process_chunk=False)
if self.terminator and isinstance(chunk, self.terminator): if self.terminator and isinstance(chunk, self.terminator):
break break
yield value yield value
elif chunk.id: elif chunk.id:
# This is a valid chunk, just not a recognized type # This is a valid chunk, just not a recognized type
for field in chunk._fields: for name in chunk._fields:
getattr(chunk, field.name) getattr(chunk, name)
yield chunk yield chunk
else: else:
# This is not a valid chunk, which is probably the end of the file # This is not a valid chunk, which is probably the end of the file
Expand Down
4 changes: 2 additions & 2 deletions biwako/common/fields.py
Expand Up @@ -117,7 +117,7 @@ def set_name(self, name):
self.label = label.title() self.label = label.title()


def attach_to_class(self, cls): def attach_to_class(self, cls):
cls._fields.append(self) cls._fields[self.name] = self


def validate(self, obj, value): def validate(self, obj, value):
# This should raise a ValueError if the value is invalid # This should raise a ValueError if the value is invalid
Expand Down Expand Up @@ -217,7 +217,7 @@ def encode(self, value):
def __getattr__(self, name): def __getattr__(self, name):
if 'structure' in self.__dict__: if 'structure' in self.__dict__:
field = getattr(self.structure, name) field = getattr(self.structure, name)
if field in self.structure._fields: if field in self.structure._fields.values():
field = copy.copy(field) field = copy.copy(field)
field._parent = self field._parent = self
return field return field
Expand Down
13 changes: 12 additions & 1 deletion biwako/common/meta.py
Expand Up @@ -33,10 +33,21 @@ def __new__(cls, name, bases, attrs, **options):
return type.__new__(cls, name, bases, attrs) return type.__new__(cls, name, bases, attrs)


def __init__(cls, name, bases, attrs, **options): 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(): 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'): if hasattr(attr, 'attach_to_class'):
attr.attach_to_class(cls) attr.attach_to_class(cls)

print('%s %s' % (cls, cls._fields))
data.field_options = {} data.field_options = {}
data.field_stack = [[]] data.field_stack = [[]]


Expand Down

0 comments on commit 5bc193b

Please sign in to comment.