'''
TODO Write doc
'''
import sys
import itertools
import six
if six.PY2:
STRSUM = lambda x: sum(bytearray(x))
else:
STRSUM = sum
[docs]class RepeatingGroup(list):
""" Implementation of repeating groups for pyfixmsg.FixMessage.
The repeating group will look like {opening_tag:[FixMessage,FixMessage]} in the fix message
a repeating group behaves like a list. You can add two repeating groups, or append a FixMessage to one.
"""
[docs] def __init__(self, *args, **kwargs):
"""Maintains ``list``'s signature unchanged.
Sets
* self.number_tag (the tag that contains the number of elements in the group)
* self.first_tag (the first repeated tag)
* self.standard (reserved)
"""
super(RepeatingGroup, self).__init__(*args, **kwargs)
self.number_tag = None
self.standard = True
self.first_tag = None
@property
def entry_tag(self):
""" returns the entry tag for the group and its value as a tuple"""
return self.number_tag, len(self)
[docs] @classmethod
def create_repeating_group(cls, tag, standard=True, first_tag=None):
""" creates a group with member. Can't use __init__ as it would mean overriding the list __init__ which sounds
dangerous"""
group = cls()
group.number_tag = tag
group.standard = standard
group.first_tag = first_tag
return group
def __add__(self, other):
""" addition of groups"""
result = RepeatingGroupFactory(self.number_tag, self.standard,
self.first_tag).get_r_group(*self)
for group in other:
result.append(group)
return result
[docs] def find_all(self, tag):
"""
Generator.
Find all instances of the tag in the message or inside repeating groups and returns the path to
them one at a time.
Example, navigate all paths for a given tag:
>>> for path in msg.find_all(self, tag):
... # path here is a list of ints or str keys
... path_msg = msg
... for key in path:
... path_msg = path_msg[key]
... # [...] do something at each level in the path
... path_msg[tag] = # [...] do something with the last level of the path
@return: a generator of paths where each path is a list of string or integer indices into the message
@rtype: Generator of C{list} of C{int} or C{str}
"""
for index, msg in enumerate(self):
if msg.anywhere(tag):
for path in msg.find_all(tag):
result = [index]
result.extend(path)
yield result
[docs] def length(self):
"""
Length of the body of the message in bytes
"""
return sum(len_and_chsum(member)[0] for member in self)
[docs]class RepeatingGroupFactory(object):
""" An easy way to create a repeating group for a given tag, without having to define all the tags yourself, takes
the standard ones"""
[docs] def __init__(self, tag, standard=True, first_tag=None):
self.tag = tag
self.standard = standard
self.first_tag = first_tag
[docs] def get_r_group(self, *fix_messages):
""" factory method. I'm not familiar with the factory design pattern, it shows ;-)"""
r_group = RepeatingGroup.create_repeating_group(self.tag, self.standard, self.first_tag)
for fixmsg in fix_messages:
r_group.append(fixmsg)
return r_group
def len_and_chsum(msg, group=False):
"""Calculate length and checksum. Note that the checksum is not moduloed with 256 or formatted,
it's just the sum part of the checksum."""
count = 0
chsum_count = 0
for tag, value in list(msg.items()):
if not isinstance(tag, bytes):
tag = str(tag).encode('ascii')
if not isinstance(value, bytes) and not isinstance(value, RepeatingGroup):
if isinstance(value, six.text_type):
value = value.encode('UTF-8')
else:
value = str(value).encode('UTF-8')
if tag == b'8':
chsum_count += STRSUM(tag)
chsum_count += STRSUM(value)
chsum_count += 1
chsum_count += 61
continue
if tag in (b'9', b'10'):
continue
# For being consistent with fixmessage.py use RepeatingGroup. Both works anyway.
# if isinstance(value, list):
if isinstance(value, RepeatingGroup):
# repeating groups
g_tag = str(value.entry_tag[0]).encode('ascii')
g_val = str(value.entry_tag[1]).encode('UTF-8')
chsum_count += STRSUM(g_tag)
count += len(g_tag)
count += 1 # separator
chsum_count += 1 # separator
count += len(g_val)
chsum_count += STRSUM(g_val)
count += 1 # delimiter
chsum_count += 61 # delimiter
for member in value:
member_len, member_chsum = len_and_chsum(member, True)
count += member_len
chsum_count += member_chsum
else:
# normal tags
chsum_count += STRSUM(tag)
count += len(tag)
count += 1 # separator
chsum_count += 1 # separator
count += len(value)
chsum_count += STRSUM(value)
count += 1 # delimiter
chsum_count += 61 # delimiter
# no need to add delimiter here as it is counted in the fragment
if not group:
chsum_count += 119 # <SOH>9=
chsum_count += STRSUM(str(count).encode('ascii'))
return count, chsum_count