Skip to content

Commit aa651c3

Browse files
henrykraphaelm
authored andcommitted
Fix logic error in counted elements parser, can now parse HIRMG2
1 parent 2922381 commit aa651c3

File tree

3 files changed

+71
-47
lines changed

3 files changed

+71
-47
lines changed

fints/formals.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ def _check_value_length(self, value):
134134

135135
class TypedField(Field, SubclassesMixin):
136136
flat_length = 1
137+
flat_length_max = 1
137138

138139
def __new__(cls, *args, **kwargs):
139140
target_cls = None
@@ -186,6 +187,19 @@ def flat_length(self):
186187
raise TypeError("Cannot compute flat length of field {}.{} with variable count".format(self.__class__.__name__, name))
187188
result = result + field.count * field.flat_length
188189
return result
190+
191+
@property
192+
def flat_length_max(self):
193+
result = 0
194+
for name, field in self.type._fields.items():
195+
# Note: We're *not* recursing into flat_length_max, because we don't want variable count fields at deeper levels
196+
if field.count is not None:
197+
result = result + field.count * field.flat_length
198+
elif field.max_count is not None:
199+
result = result + field.max_count * field.flat_length
200+
else:
201+
raise TypeError("Cannot compute max flat length of field {}.{} without count and max_count".format(self.__class__.__name__, name))
202+
return result
189203

190204

191205
class DataElementGroupField(ContainerField):

fints/parser.py

Lines changed: 51 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -116,60 +116,70 @@ def parse_segment(self, segment):
116116
seg = clazz()
117117

118118
data = iter(segment)
119-
for name, field in seg._fields.items():
120-
try:
121-
val = next(data)
122-
except StopIteration:
123-
if field.required:
124-
raise ValueError("Required field {}.{} was not present".format(clazz.__name__, name))
119+
for number, (name, field) in enumerate(seg._fields.items()):
120+
vals = self.parse_repeat(field, data, number == len(seg._fields)-1)
121+
if field.count == 1:
122+
if len(vals):
123+
setattr(seg, name, vals[0])
124+
else:
125+
if field.required:
126+
raise ValueError("Required field {}.{} was not present".format(seg.__class__.__name__, name))
125127
else:
126-
deg = self.parse_n_deg(field, val)
127-
setattr(seg, name, deg)
128+
setattr(seg, name, vals)
129+
128130
seg._additional_data = list(data)
129131

130132
return seg
131133

132-
def parse_n_deg(self, field, data):
133-
if not isinstance(data, Iterable) or isinstance(data, (str, bytes)):
134-
data = [data]
135-
136-
data_i = iter(data)
137-
field_index = 0
138-
field_length = field.flat_length
139-
134+
def parse_repeat(self, field, data_i, is_last):
140135
retval = []
141-
eod = False
142136

143-
while not eod:
144-
vals = []
137+
if field.count == 1:
145138
try:
146-
for x in range(field_length):
147-
vals.append(next(data_i))
139+
val = next(data_i)
148140
except StopIteration:
149-
eod = True
150-
151-
if field.count == 1:
152-
if isinstance(field, DataElementField):
153-
if not len(vals):
154-
return
155-
return vals[0]
156-
elif isinstance(field, DataElementGroupField):
157-
return self.parse_deg(field.type, vals)
141+
pass
142+
else:
143+
retval.append( self.parse_n_deg(field, val) )
144+
else:
145+
for i in range(field.count if field.count is not None else field.max_count):
146+
try:
147+
val = next(data_i)
148+
except StopIteration:
149+
break
158150
else:
159-
raise Error("Internal error")
160-
break
151+
retval.append(self.parse_n_deg(field, val, is_last))
161152

162-
if field_index >= (field.count if field.count is not None else len(data) // field_length):
163-
break
153+
return retval
164154

165-
if isinstance(field, DataElementField):
166-
retval.append(vals[0] if len(vals) else None)
167-
elif isinstance(field, DataElementGroupField):
168-
retval.append(self.parse_deg(field.type, vals))
169-
else:
170-
raise Error("Internal error")
171155

172-
return retval
156+
def parse_n_deg(self, field, data, is_last=False):
157+
if not isinstance(data, Iterable) or isinstance(data, (str, bytes)):
158+
data = [data]
159+
160+
data_i = iter(data)
161+
if is_last:
162+
field_length = field.flat_length_max
163+
else:
164+
field_length = field.flat_length
165+
166+
eod = False
167+
168+
vals = []
169+
try:
170+
for x in range(field_length):
171+
vals.append(next(data_i))
172+
except StopIteration:
173+
pass
174+
175+
if isinstance(field, DataElementField):
176+
if not len(vals):
177+
return
178+
return vals[0]
179+
elif isinstance(field, DataElementGroupField):
180+
return self.parse_deg(field.type, vals)
181+
else:
182+
raise Error("Internal error")
173183

174184
def parse_deg(self, clazz, vals):
175185
retval = clazz()

tests/test_message_parser.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,14 @@ class ITST2(FinTS3Segment):
4848
a = NumericField(max_count=3)
4949

5050
m2 = FinTS3Parser().parse_message(b"ITST:1:2+1+2+3'")
51-
assert m1.segments[0].a[2] == 3
51+
assert m2.segments[0].a[2] == 3
5252

53-
with pytest.raises(IndexError):
54-
FinTS3Parser().parse_message(b"ITST:1:2+1+2+3+4'")
53+
m3 = FinTS3Parser().parse_message(b"ITST:1:2+1+2+3+4'")
54+
assert m3.segments[0]._additional_data == ['4']
5555

56-
m = FinTS3Parser().parse_message(b"ITST:1:2+1+2'")
57-
assert len(m2.segments[0].a) == 2
58-
assert m2.segments[0].a[1] == 2
56+
m4 = FinTS3Parser().parse_message(b"ITST:1:2+1+2'")
57+
assert len(m4.segments[0].a) == 2
58+
assert m4.segments[0].a[1] == 2
5959

6060

6161
def test_parse_HIRMG2():

0 commit comments

Comments
 (0)