Skip to content
Open
4 changes: 2 additions & 2 deletions bdec/choice.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ class Choice(bdec.entry.Entry):
The first entry to decode correctly will be used.
"""

def __init__(self, name, children, length=None):
bdec.entry.Entry.__init__(self, name, length, children)
def __init__(self, name, children, length=None, attributes=None):
bdec.entry.Entry.__init__(self, name, length, children, attributes=attributes)

assert len(children) > 0

Expand Down
2 changes: 1 addition & 1 deletion bdec/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def __init__(self, data, encoding):
self.encoding = encoding

def __str__(self):
return "'%s' can't convert %s" % (self.encoding, self.data)
return "'%s' can't convert '%s'" % (self.encoding, self.data)

class _OutOfDataError(Exception):
"""Not derived from DataError as this is an internal error."""
Expand Down
3 changes: 2 additions & 1 deletion bdec/entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class Entry(object):
directly).
"""

def __init__(self, name, length, children, constraints=[]):
def __init__(self, name, length, children, constraints=[], attributes=None):
"""Construct an Entry instance.

children -- A list of Entry or Child instances.
Expand All @@ -119,6 +119,7 @@ def __init__(self, name, length, children, constraints=[]):
self.constraints = list(constraints)
for constraint in self.constraints:
assert getattr(constraint, 'check') is not None
self.attributes = attributes if attributes!=None else {}

def _get_children(self):
return self._children
Expand Down
4 changes: 2 additions & 2 deletions bdec/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ class Field(bdec.entry.Entry):
LITTLE_ENDIAN = "little endian"
BIG_ENDIAN = "big endian"

def __init__(self, name, length, format=BINARY, encoding=None, constraints=[]):
bdec.entry.Entry.__init__(self, name, length, [], constraints)
def __init__(self, name, length, format=BINARY, encoding=None, constraints=[], attributes=None):
bdec.entry.Entry.__init__(self, name, length, [], constraints, attributes=attributes)
assert format in self._formats

if encoding is None:
Expand Down
4 changes: 2 additions & 2 deletions bdec/sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ class Sequence(bdec.entry.Entry):
lookup tables, and alternate integer encoding methods.
"""

def __init__(self, name, children, value=None, length=None, constraints=[]):
bdec.entry.Entry.__init__(self, name, length, children, constraints)
def __init__(self, name, children, value=None, length=None, constraints=[], attributes=None):
bdec.entry.Entry.__init__(self, name, length, children, constraints, attributes=attributes)
self.value = value

def _range(self, ignore_entries):
Expand Down
4 changes: 2 additions & 2 deletions bdec/sequenceof.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class SequenceOf(bdec.entry.Entry):
ITERATING = "iterating"
STOPPING = "stopping"

def __init__(self, name, child, count, length=None, end_entries=[]):
def __init__(self, name, child, count, length=None, end_entries=[], attributes=None):
"""
count -- The number of times the child will repeat. If this value is
None, the count will not be used.
Expand All @@ -67,7 +67,7 @@ def __init__(self, name, child, count, length=None, end_entries=[]):
If neither count, length, or end_entries are used, the SequenceOf will
fail to decode after using all of the available buffer.
"""
bdec.entry.Entry.__init__(self, name, length, [child])
bdec.entry.Entry.__init__(self, name, length, [child], attributes=attributes)
if isinstance(count, int):
count = Constant(count)
assert count is None or isinstance(count, Expression)
Expand Down
26 changes: 13 additions & 13 deletions bdec/spec/integer.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class Integers:
def __init__(self):
self.common = {}

def signed_big_endian(self, length_expr):
def signed_big_endian(self, length_expr, attributes=None):
try:
# We try to choose the name based on the length value. If we
# cannot evaluate the length, we'll use the length name.
Expand All @@ -51,14 +51,14 @@ def signed_big_endian(self, length_expr):
try:
result = self.common[name]
except KeyError:
is_signed = Field('signed:', 1)
value = Field('integer value:', ArithmeticExpression(operator.sub, length_expr, Constant(1)))
is_signed = Field('signed:', 1, attributes=attributes)
value = Field('integer value:', ArithmeticExpression(operator.sub, length_expr, Constant(1)), attributes=attributes)
expression = compile('${signed:} * ((0 - 1) << (%s - 1)) + ${integer value:}' % (length_expr))
result = Sequence(name, [is_signed, value], value=expression)
result = Sequence(name, [is_signed, value], value=expression, attributes=attributes)
self.common[name] = result
return result

def _variable_length_signed_little_endian(self, length_expr):
def _variable_length_signed_little_endian(self, length_expr, attributes=None):
name = 'variable length integer'
try:
result = self.common[name]
Expand All @@ -72,16 +72,16 @@ def _variable_length_signed_little_endian(self, length_expr):
# We wrap the choice inside a sequence, as choices don't currently
# 'compile' to integers (and sequences do).
var_name = 'variable integer types:'
result = Sequence(name, [Choice(var_name, options)],
value=compile('${%s.value}' % var_name))
result = Sequence(name, [Choice(var_name, options, attributes=attributes)],
value=compile('${%s}' % var_name), attributes=attributes)
self.common[name] = result
return result

def signed_litte_endian(self, length_expr):
def signed_litte_endian(self, length_expr, attributes=None):
try:
length = length_expr.evaluate({})
except UndecodedReferenceError:
return self._variable_length_signed_little_endian(length_expr)
return self._variable_length_signed_little_endian(length_expr, attributes=attributes)

if length % 8 != 0:
raise IntegerError('The length of little endian fields must be a multiple of 8.')
Expand All @@ -93,9 +93,9 @@ def signed_litte_endian(self, length_expr):
children = []
num_bytes = length / 8
for i in range(num_bytes - 1):
children.append(Field('byte %i:' % i, 8))
children.append(Field('signed:', 1))
children.append(Field('byte %i:' % (num_bytes - 1), 7))
children.append(Field('byte %i:' % i, 8, attributes=attributes))
children.append(Field('signed:', 1, attributes=attributes))
children.append(Field('byte %i:' % (num_bytes - 1), 7, attributes=attributes))
# We define the minimum as being '-number - 1' to avoid compiler
# warnings in C, where there are no negative constants, just
# constants that are then negated (and the positive version of
Expand All @@ -105,7 +105,7 @@ def signed_litte_endian(self, length_expr):
names.reverse()
reference = reduce(lambda left, right: '(%s) * 256 + %s' % (left, right), names)
value_text = '${signed:} * (0 - %i - 1) + %s' % (maximum - 1, reference)
result = Sequence(name, children, value=compile(value_text))
result = Sequence(name, children, value=compile(value_text), attributes=attributes)
self.common[name] = result
return result

14 changes: 7 additions & 7 deletions bdec/spec/xmlspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ def endElement(self, name):
self.lookup[entry] = (self._filename, lineno, colno)

name = 'optional %s' % entry_name if entry_name else 'optional:'
optional = chc.Choice(name, [not_present, entry])
optional = chc.Choice(name, [not_present, entry], attributes=attrs)
entry = optional

if entry is not None:
Expand Down Expand Up @@ -278,16 +278,16 @@ def _field(self, attributes, children, name, length, breaks):
# have to know about these types.
try:
if encoding in [None, fld.Field.BIG_ENDIAN]:
integer = self._integers.signed_big_endian(length)
integer = self._integers.signed_big_endian(length, attributes=attributes)
else:
assert encoding == fld.Field.LITTLE_ENDIAN
integer = self._integers.signed_litte_endian(length)
integer = self._integers.signed_litte_endian(length, attributes=attributes)
except IntegerError, error:
raise self._error(str(error))
return self._references.get_common(name, integer.name)

# We'll create the field, then use it to create the expected value.
result = fld.Field(name, length, format, encoding)
result = fld.Field(name, length, format, encoding, attributes=attributes)
if attributes.has_key('value'):
# Get the correct object type by encoding, then decoding, the text
# value.
Expand Down Expand Up @@ -325,12 +325,12 @@ def _sequence(self, attributes, children, name, length, breaks):
if attributes.has_key('value'):
# A sequence can have a value derived from its children...
value = self._parse_expression(attributes['value'])
return seq.Sequence(name, children, value, length)
return seq.Sequence(name, children, value, length, attributes=attributes)

def _choice(self, attributes, children, name, length, breaks):
if len(children) == 0:
raise self._error("Choice '%s' must have children! Should this be a 'reference' entry?" % attributes['name'])
return chc.Choice(name, children, length)
return chc.Choice(name, children, length, attributes=attributes)

def _sequenceof(self, attributes, children, name, length, breaks):
if len(children) == 0:
Expand All @@ -342,7 +342,7 @@ def _sequenceof(self, attributes, children, name, length, breaks):
count = None
if attributes.has_key('count'):
count = self._parse_expression(attributes['count'])
result = sof.SequenceOf(name, children[0], count, length, breaks)
result = sof.SequenceOf(name, children[0], count, length, breaks, attributes=attributes)
return result


Expand Down