Пример #1
0
class CheckSum(Integer):
    first = args.Argument(default=None)
    last = args.Argument(default=None)

    def attach_to_class(self, cls):
        super(CheckSum, self).attach_to_class(cls)

        self.fields = []
        in_range = False
        for field in cls._fields:
            if field is self:
                # Can't go beyond the integrity field itself
                break

            if self.first is None:
                self.first = field

            if field is self.first:
                in_range = True

            if in_range:
                self.fields.append(field)
                field.after_encode(self.update_encoded_value)

            if field is self.last:
                # End of the line
                break

    def build_cache(self, instance):
        for field in self.fields:
            if field.name not in instance._raw_values:
                # Set the value to itself just to update the encoded value
                setattr(instance, field.name, getattr(instance, field.name))

    def read(self, file):
        try:
            given_bytes = super(CheckSum, self).read(file)
            given_value = super(CheckSum, self).decode(given_bytes)
        except FullyDecoded as obj:
            given_bytes = obj.bytes
            given_value = obj.value
        self.build_cache(file)
        if given_value != self.get_calculated_value(file):
            raise IntegrityError('%s does not match calculated value' %
                                 self.name)
        raise FullyDecoded(given_bytes, given_value)

    def get_calculated_value(self, instance):
        data = b''.join(instance._extract(field) for field in self.fields)
        return self.calculate(data)

    def update_encoded_value(self, instance, value):
        setattr(instance, self.name, self.get_calculated_value(instance))

    def calculate(self, data):
        return sum(data)
Пример #2
0
class Date(Field):
    """
    A field that contains data in the form of dates,
    represented in Python by datetime.date.

    format
        A strptime()-style format string.
        See http://docs.python.org/library/datetime.html for details
    """
    format = args.Argument(default='%Y-%m-%d')

    def decode(self, value):
        """
        Parse a string value according to self.format
        and return only the date portion.
        """
        if isinstance(value, datetime.date):
            return value
        return datetime.datetime.strptime(value, self.format).date()

    def encode(self, value):
        """
        Format a date according to self.format and return that as a string.
        """
        return value.strftime(self.format)
Пример #3
0
class Integer(bin.Integer):
    size = args.Override(resolve_field=False)
    signed = args.Argument(default=False)

    def encode(self, value):
        if value > (1 << self.size) - 1:
            raise ValueError("Value is large for this field.")
        return value & ((1 << self.size) - 1)

    def decode(self, value):
        if value > (1 << self.size) - 1:
            raise ValueError("Value is large for this field.")
        return value & ((1 << self.size) - 1)
Пример #4
0
class Field(bin.Field):
    size = args.Argument(resolve_field=True)
    choices = args.Argument(default=())
    default = args.Argument(default=args.NotProvided)

    def __init__(self, label='', **kwargs):
        self.label = label

        for name, arg in self.arguments.items():
            if name in kwargs:
                value = kwargs.pop(name)
            elif arg.has_default:
                value = arg.default
            else:
                raise TypeError("The %s argument is required for %s fields" % (arg.name, self.__class__.__name__))
            setattr(self, name, value)
        if kwargs:
            raise TypeError("%s is not a valid argument for %s fields" % (list(kwargs.keys())[0], self.__class__.__name__))

        # Once the base values are all in place, arguments can be initialized properly
        for name, arg in self.arguments.items():
            setattr(self, name, arg.initialize(self, getattr(self, name)))
Пример #5
0
class String(Field):
    size = args.Override(default=None)
    encoding = args.Argument(resolve_field=True)
    padding = args.Argument(default=b'\x00')
    terminator = args.Argument(default=b'\x00')

    def read(self, file):
        if self.size is not None:
            return file.read(self.size)

        else:
            # TODO: There's gotta be a better way, but it works for now
            value = b''
            while True:
                data = file.read(1)
                value += data
                if data == self.terminator:
                    break

        return value

    def decode(self, value):
        return value.rstrip(self.terminator).rstrip(self.padding).decode(
            self.encoding)

    def encode(self, value):
        value = value.encode(self.encoding)
        if self.size is not None:
            value = value.ljust(self.size, self.padding)
        else:
            value += self.terminator
        return value

    def validate(self, obj, value):
        value = value.encode(self.encoding)
        if self.size is not None:
            if len(value) > self.size:
                raise ValueError("String %r is longer than %r bytes." %
                                 (value, self.size))
Пример #6
0
class Field(metaclass=common.DeclarativeFieldMetaclass):
    size = args.Argument(resolve_field=True)
    offset = args.Argument(default=None, resolve_field=True)
    choices = args.Argument(default=())
    default = args.Argument(default=args.NotProvided)

    after_encode = Trigger()
    after_decode = Trigger()

    @after_encode
    def update_size(self, obj, value):
        if isinstance(self.size, Field):
            setattr(obj, self.size.name, len(value))

    def __init__(self, label='', **kwargs):
        self.label = label
        self._parent = None

        for name, arg in self.arguments.items():
            if name in kwargs:
                value = kwargs.pop(name)
            elif arg.has_default:
                value = arg.default
            else:
                raise TypeError("The %s argument is required for %s fields" %
                                (arg.name, self.__class__.__name__))
            setattr(self, name, value)
        if kwargs:
            raise TypeError("%s is not a valid argument for %s fields" %
                            (list(kwargs.keys())[0], self.__class__.__name__))

        # Once the base values are all in place, arguments can be initialized properly
        for name, arg in self.arguments.items():
            setattr(self, name, arg.initialize(self, getattr(self, name)))
        self.instance = None

    def for_instance(self, instance):
        if instance is None:
            return self
        field = copy.copy(self)
        for name, attr in self.arguments.items():
            value = getattr(self, name)
            if attr.resolve_field and hasattr(value, 'resolve'):
                resolved = value.resolve(instance)
                setattr(field, name, resolved)
            elif hasattr(value, '__call__'):
                setattr(field, name, value(instance))
        field.instance = instance
        return field

    def resolve(self, instance):
        if self._parent is not None:
            instance = self._parent.resolve(instance)
        return getattr(instance, self.name)

    def read(self, obj):
        # If the size can be determined easily, read
        # that number of bytes and return it directly.
        if self.size is not None:
            return obj.read(self.size)

        # Otherwise, the field needs to supply its own
        # technique for determining how much data to read.
        raise NotImplementedError()

    def write(self, obj, value):
        # By default, this doesn't do much, but individual
        # fields can/should override it if necessary
        obj.write(value)

    def set_name(self, name):
        self.name = name
        label = self.label or name.replace('_', ' ')
        self.label = label.title()

    def attach_to_class(self, cls):
        cls._fields.append(self)

    def validate(self, obj, value):
        # This should raise a ValueError if the value is invalid
        # It should simply return without an error if it's valid
        field = self.for_instance(obj)

        # First, make sure the value can be encoded
        field.encode(value)

        # Then make sure it's a valid option, if applicable
        if self.choices and value not in set(v for v, desc in self.choices):
            raise ValueError("%r is not a valid choice" % value)

    def _extract(self, instance):
        field = self.for_instance(instance)
        try:
            return field.read(instance), None
        except FullyDecoded as obj:
            return obj.bytes, obj.value

    def read_value(self, file):
        try:
            bytes = self.read(file)
            value = self.decode(bytes)
            return bytes, value
        except FullyDecoded as obj:
            return obj.bytes, obj.value

    def __get__(self, instance, owner):
        if not instance:
            return self

        # Customizes the field for this particular instance
        # Use field instead of self for the rest of the method
        field = self.for_instance(instance)

        try:
            value = instance._extract(field)
        except IOError:
            if field.default is not args.NotProvided:
                return field.default
            raise AttributeError("Attribute %r has no data" % field.name)

        if field.name not in instance.__dict__:
            instance.__dict__[field.name] = field.decode(value)
            field.after_decode.apply(instance, value)
        return instance.__dict__[field.name]

    def __set__(self, instance, value):
        field = self.for_instance(instance)
        instance.__dict__[self.name] = value
        instance._raw_values[self.name] = field.encode(value)
        self.after_encode.apply(instance, value)

    def __repr__(self):
        return '<%s: %s>' % (self.name, type(self).__name__)

    def __hash__(self):
        return id(self)

    def __eq__(self, other):
        return Condition(self, other, lambda a, b: a == b)

    def __ne__(self, other):
        return Condition(self, other, lambda a, b: a != b)
Пример #7
0
class Integer(Field):
    size = args.Override(resolve_field=False)

    signed = args.Argument(default=False)
    endianness = args.Argument(default=BigEndian)
    signing = args.Argument(default=TwosComplement)

    @signing.init
    @endianness.init
    def init_size(self, value):
        return value(self.size)

    def encode(self, value):
        if self.signed:
            value = self.signing.encode(value)
        elif value < 0:
            raise ValueError("Value cannot be negative.")
        if value > (1 << (self.size * 8)) - 1:
            raise ValueError("Value is large for this field.")
        return self.endianness.encode(value)

    def decode(self, value):
        value = self.endianness.decode(value)
        if self.signed:
            value = self.signing.decode(value)
        return value

    def __add__(self, other):
        return CalculatedValue(self, other, lambda a, b: a + b)

    __radd__ = __add__

    def __sub__(self, other):
        return CalculatedValue(self, other, lambda a, b: a - b)

    def __rsub__(self, other):
        return CalculatedValue(self, other, lambda a, b: b - a)

    def __mul__(self, other):
        return CalculatedValue(self, other, lambda a, b: a * b)

    __rmul__ = __mul__

    def __pow__(self, other):
        return CalculatedValue(self, other, lambda a, b: a**b)

    def __rpow__(self, other):
        return CalculatedValue(self, other, lambda a, b: b**a)

    def __truediv__(self, other):
        return CalculatedValue(self, other, lambda a, b: a / b)

    def __rtruediv__(self, other):
        return CalculatedValue(self, other, lambda a, b: b / a)

    def __floordiv__(self, other):
        return CalculatedValue(self, other, lambda a, b: a // b)

    def __rfloordiv__(self, other):
        return CalculatedValue(self, other, lambda a, b: b // a)

    def __divmod__(self, other):
        return CalculatedValue(self, other, lambda a, b: divmod(a, b))

    def __rdivmod__(self, other):
        return CalculatedValue(self, other, lambda a, b: divmod(b, a))

    def __and__(self, other):
        return CalculatedValue(self, other, lambda a, b: a & b)

    __rand__ = __and__

    def __or__(self, other):
        return CalculatedValue(self, other, lambda a, b: a | b)

    __ror__ = __or__

    def __xor__(self, other):
        return CalculatedValue(self, other, lambda a, b: a ^ b)

    __rxor__ = __xor__

    def __lt__(self, other):
        return Condition(self, other, lambda a, b: a < b)

    def __lte__(self, other):
        return Condition(self, other, lambda a, b: a <= b)

    def __gte__(self, other):
        return Condition(self, other, lambda a, b: a >= b)

    def __gt__(self, other):
        return Condition(self, other, lambda a, b: a > b)