]> git.ipfire.org Git - thirdparty/python-fints.git/commitdiff
Implement rendering for most fields
authorHenryk Plötz <henryk@ploetzli.ch>
Tue, 7 Aug 2018 22:15:09 +0000 (00:15 +0200)
committerRaphael Michel <mail@raphaelmichel.de>
Mon, 3 Dec 2018 18:34:17 +0000 (19:34 +0100)
fints/formals.py
tests/test_formals.py

index 6ad2023835f8626f18714836b692c42f2c78f375..5568023535b8592f11ab94c3169fc0de3bcda52a 100644 (file)
@@ -118,6 +118,17 @@ class Field:
         with suppress(NotImplementedError):
             self._render_value(value)
 
+    def _check_value_length(self, value):
+        if self.max_length is not None and len(value) > self.max_length:
+            raise ValueError("Value {!r} cannot be rendered: max_length={} exceeded".format(value, self.max_length))
+
+        if self.min_length is not None and len(value) < self.min_length:
+            raise ValueError("Value {!r} cannot be rendered: min_length={} not reached".format(value, self.min_length))
+
+        if self.length is not None and len(value) != self.length:
+            raise ValueError("Value {!r} cannot be rendered: length={} not satisfied".format(value, self.length))
+
+
 class TypedField(Field, SubclassesMixin):
     flat_length = 1
 
@@ -145,6 +156,15 @@ class TypedField(Field, SubclassesMixin):
 class DataElementField(TypedField):
     pass
 
+class FieldRenderFormatStringMixin:
+    FORMAT_STRING = None
+
+    def _render_value(self, value):
+        retval = self.FORMAT_STRING.format(value)
+        self._check_value_length(retval)
+
+        return retval
+
 class ContainerField(TypedField):
     def _check_value(self, value):
         if self.type:
@@ -168,8 +188,10 @@ class ContainerField(TypedField):
 class DataElementGroupField(ContainerField):
     pass
 
-class GenericField(DataElementField):
+class GenericField(FieldRenderFormatStringMixin, DataElementField):
     type = None
+    FORMAT_STRING = "{}"
+
     def _parse_value(self, value):
         warnings.warn("Generic field used for type {!r} value {!r}".format(self.type, value))
         return value
@@ -188,8 +210,9 @@ class GenericGroupField(DataElementGroupField):
             warnings.warn("Generic field used for type {!r} value {!r}".format(self.type, value))
         return value
 
-class TextField(DataElementField):
+class TextField(FieldRenderFormatStringMixin, DataElementField):
     type = 'txt'
+    FORMAT_STRING = "{}"  ## FIXME Restrict CRLF
 
     def _parse_value(self, value): return str(value)
 
@@ -199,8 +222,9 @@ class AlphanumericField(TextField):
 class DTAUSField(DataElementField):
     type = 'dta'
 
-class NumericField(DataElementField):
+class NumericField(FieldRenderFormatStringMixin, DataElementField):
     type = 'num'
+    FORMAT_STRING = "{:d}"
 
     def _parse_value(self, value): 
         _value = str(value)
@@ -208,8 +232,9 @@ class NumericField(DataElementField):
             raise TypeError("Leading zeroes not allowed for value of type 'num': {!r}".format(value))
         return int(_value, 10)
 
-class DigitsField(DataElementField):
+class DigitsField(FieldRenderFormatStringMixin, DataElementField):
     type = 'dig'
+    FORMAT_STRING = "{}"
 
     def _parse_value(self, value): 
         _value = str(value)
@@ -217,12 +242,18 @@ class DigitsField(DataElementField):
             raise TypeError("Only digits allowed for value of type 'dig': {!r}".format(value))
         return _value
 
-class FloatField(DataElementField):
+class FloatField(FieldRenderFormatStringMixin, DataElementField):
     type = 'float'
 
 class BinaryField(DataElementField):
     type = 'bin'
 
+    def _render_value(self, value):
+        retval = bytes(value)
+        self._check_value_length(retval)
+
+        return retval
+
     def _parse_value(self, value): return bytes(value)
 
 
index 556f9e4793dd664ff68ce8a40c391afcc2b9445f..3afa7129cac21b41b461557cfc42fb16b48982d5 100644 (file)
@@ -1,5 +1,5 @@
 import pytest
-from fints.formals import Container, ContainerField, DataElementField, DataElementGroupField, DigitsField, NumericField, Field, SegmentSequence, SegmentHeader
+from fints.formals import Container, ContainerField, DataElementField, DataElementGroupField, DigitsField, NumericField, Field, SegmentSequence, SegmentHeader, AlphanumericField
 
 def test_container_simple():
     class A(Container):
@@ -258,6 +258,26 @@ def test_invalid_spec():
 
         A(a=123)
 
+def test_parse_restrictions():
+    class A(Container):
+        a = NumericField(min_length=2, max_length=3)
+        b = DigitsField(length=3)
+
+    i1 = A()
+    with pytest.raises(ValueError, match='max_length=3 exceeded'):
+        i1.a = '1234'
+
+    i2 = A(a=123)
+    with pytest.raises(ValueError, match='max_length=3 exceeded'):
+        i2.a = 1234
+
+    with pytest.raises(ValueError, match='length=3 not satisfied'):
+        A(b='01')
+
+    with pytest.raises(ValueError, match='min_length=2 not reached'):
+        A(a=1)
+
+
 def test_sequence_repr():
     s = SegmentSequence()