Skip to content

Commit ec9ac5b

Browse files
henrykraphaelm
authored andcommitted
Implement rendering for most fields
1 parent 04b5fdb commit ec9ac5b

File tree

2 files changed

+57
-6
lines changed

2 files changed

+57
-6
lines changed

fints/formals.py

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,17 @@ def _check_value(self, value):
118118
with suppress(NotImplementedError):
119119
self._render_value(value)
120120

121+
def _check_value_length(self, value):
122+
if self.max_length is not None and len(value) > self.max_length:
123+
raise ValueError("Value {!r} cannot be rendered: max_length={} exceeded".format(value, self.max_length))
124+
125+
if self.min_length is not None and len(value) < self.min_length:
126+
raise ValueError("Value {!r} cannot be rendered: min_length={} not reached".format(value, self.min_length))
127+
128+
if self.length is not None and len(value) != self.length:
129+
raise ValueError("Value {!r} cannot be rendered: length={} not satisfied".format(value, self.length))
130+
131+
121132
class TypedField(Field, SubclassesMixin):
122133
flat_length = 1
123134

@@ -145,6 +156,15 @@ def __init__(self, type=None, *args, **kwargs):
145156
class DataElementField(TypedField):
146157
pass
147158

159+
class FieldRenderFormatStringMixin:
160+
FORMAT_STRING = None
161+
162+
def _render_value(self, value):
163+
retval = self.FORMAT_STRING.format(value)
164+
self._check_value_length(retval)
165+
166+
return retval
167+
148168
class ContainerField(TypedField):
149169
def _check_value(self, value):
150170
if self.type:
@@ -168,8 +188,10 @@ def flat_length(self):
168188
class DataElementGroupField(ContainerField):
169189
pass
170190

171-
class GenericField(DataElementField):
191+
class GenericField(FieldRenderFormatStringMixin, DataElementField):
172192
type = None
193+
FORMAT_STRING = "{}"
194+
173195
def _parse_value(self, value):
174196
warnings.warn("Generic field used for type {!r} value {!r}".format(self.type, value))
175197
return value
@@ -188,8 +210,9 @@ def _parse_value(self, value):
188210
warnings.warn("Generic field used for type {!r} value {!r}".format(self.type, value))
189211
return value
190212

191-
class TextField(DataElementField):
213+
class TextField(FieldRenderFormatStringMixin, DataElementField):
192214
type = 'txt'
215+
FORMAT_STRING = "{}" ## FIXME Restrict CRLF
193216

194217
def _parse_value(self, value): return str(value)
195218

@@ -199,30 +222,38 @@ class AlphanumericField(TextField):
199222
class DTAUSField(DataElementField):
200223
type = 'dta'
201224

202-
class NumericField(DataElementField):
225+
class NumericField(FieldRenderFormatStringMixin, DataElementField):
203226
type = 'num'
227+
FORMAT_STRING = "{:d}"
204228

205229
def _parse_value(self, value):
206230
_value = str(value)
207231
if len(_value) > 1 and _value[0] == '0':
208232
raise TypeError("Leading zeroes not allowed for value of type 'num': {!r}".format(value))
209233
return int(_value, 10)
210234

211-
class DigitsField(DataElementField):
235+
class DigitsField(FieldRenderFormatStringMixin, DataElementField):
212236
type = 'dig'
237+
FORMAT_STRING = "{}"
213238

214239
def _parse_value(self, value):
215240
_value = str(value)
216241
if not re.match(r'^\d*$', _value):
217242
raise TypeError("Only digits allowed for value of type 'dig': {!r}".format(value))
218243
return _value
219244

220-
class FloatField(DataElementField):
245+
class FloatField(FieldRenderFormatStringMixin, DataElementField):
221246
type = 'float'
222247

223248
class BinaryField(DataElementField):
224249
type = 'bin'
225250

251+
def _render_value(self, value):
252+
retval = bytes(value)
253+
self._check_value_length(retval)
254+
255+
return retval
256+
226257
def _parse_value(self, value): return bytes(value)
227258

228259

tests/test_formals.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import pytest
2-
from fints.formals import Container, ContainerField, DataElementField, DataElementGroupField, DigitsField, NumericField, Field, SegmentSequence, SegmentHeader
2+
from fints.formals import Container, ContainerField, DataElementField, DataElementGroupField, DigitsField, NumericField, Field, SegmentSequence, SegmentHeader, AlphanumericField
33

44
def test_container_simple():
55
class A(Container):
@@ -258,6 +258,26 @@ class A(Container):
258258

259259
A(a=123)
260260

261+
def test_parse_restrictions():
262+
class A(Container):
263+
a = NumericField(min_length=2, max_length=3)
264+
b = DigitsField(length=3)
265+
266+
i1 = A()
267+
with pytest.raises(ValueError, match='max_length=3 exceeded'):
268+
i1.a = '1234'
269+
270+
i2 = A(a=123)
271+
with pytest.raises(ValueError, match='max_length=3 exceeded'):
272+
i2.a = 1234
273+
274+
with pytest.raises(ValueError, match='length=3 not satisfied'):
275+
A(b='01')
276+
277+
with pytest.raises(ValueError, match='min_length=2 not reached'):
278+
A(a=1)
279+
280+
261281
def test_sequence_repr():
262282
s = SegmentSequence()
263283

0 commit comments

Comments
 (0)