def test_parse_fields_fields_mixed(self, fieldmap_class): fm = fieldmap_class(Field(1, "a"), (2, "b")) assert len(fm) == 2 assert all([tag in fm for tag in [1, 2]]) assert all([field in fm.values() for field in [(1, "a"), (2, "b")]]) assert all(type(field) is Field for field in fm)
def _parse_fields(cls, fields, **kwargs): """ Creates a list of Fields from the provided (tag, value) pairs. :param fields: Any combination of (tag, value) pairs or other Field objects. :return: An list of Fields. """ parsed_fields = [] for field in fields: # For each field in the FieldMap if isinstance(field, Field) or isinstance(field, Group): # Add field as-is parsed_fields.append(field) continue try: # Add new field parsed_fields.append( Field( *field ) # Make sure that this is an actual, well-formed Field. ) except TypeError: raise ParsingError( f"Invalid Field: '{field}' mut be a (tag, value) tuple.") return parsed_fields
def _parse_group_fields(self, fields, group_index, message_type): parsed_fields = [] # Retrieve the template for this repeating group group_identifier = Field(fields[group_index][0], fields[group_index][1]) templates = self.get_group_templates(group_identifier.tag, message_type=message_type) if len(templates) != 1: # Cannot have more than one template defined for a group_identifier / message_type pair raise ParsingError( f"Could not determine template for tag {group_identifier.tag}." ) instance_template = templates[0] idx = group_index + 1 while idx < len(fields): field = fields[idx] if not isinstance(fields[idx], Field): try: field = Field(*fields[idx]) except TypeError: raise ParsingError( f"Invalid Field: '{field}' mut be a (tag, value) tuple." ) if field.tag not in instance_template: # No more group fields to process - done. break if field.tag in self.group_templates: # Tag denotes the start of a new repeating group. group = self._parse_group_fields(fields, idx, message_type) parsed_fields.append(group) # Skip over all of the fields that were processed as part of the group. idx += len(group) continue parsed_fields.append(field) idx += 1 return Group(group_identifier, *parsed_fields, template=instance_template)
def _parse_fields(self, fields, **kwargs): """ Parses the list of field tuples recursively into Field instances. :param fields: A list of (tag, value) tuples :return: A list of parsed Field and repeating Group objects. :raises: DuplicateTags if 'fields' contain repeating Fields for which no group_template has been provided. """ parsed_fields = collections.OrderedDict() idx = 0 tags_seen = set() while idx < len(fields): field = fields[idx] if not isinstance(fields[idx], Field): try: field = Field(*fields[idx]) except TypeError: raise ParsingError( f"Invalid Field: '{field}' mut be a (tag, value) tuple." ) if field.tag in tags_seen: raise DuplicateTags( field.tag, fields[idx], f"No repeating group template defined for duplicate tag {field.tag} in {fields}.", ) else: # Busy parsing a non-group tag. tags_seen.add(field.tag) if field.tag in self.group_templates: # Tag denotes the start of a new repeating group. try: message_type = str( parsed_fields[connection.protocol.Tag.MsgType]) except KeyError: # Message type not yet determined! raise ParsingError( f"Cannot parse repeating group as MsgType tag ({connection.protocol.Tag.MsgType}) has not " "been seen yet!") group = self._parse_group_fields(fields, idx, message_type) parsed_fields[group.tag] = group # Skip over all of the fields that were processed as part of the group. idx += len(group) continue parsed_fields[field.tag] = field idx += 1 return parsed_fields
def __setitem__(self, tag: int, value: any): if isinstance(value, Group): # Also add group templates when a new group is set. self.add_group_templates({tag: {"*": value.template}}) elif not (isinstance(value, Field)): # Create a new Field if value is not a Field or Group already. value = Field(tag, value) self._data[tag] = value
async def on_receive(self, message: RawMessage) -> FIXMessage: fields = ( message.BeginString, message.BodyLength, message.MsgType, message.MsgSeqNum, *Field.fields_frombytes(message.encoded_body), message.CheckSum, ) message = generic_message_factory(*fields, group_templates=self.group_templates) return message
def _parse_instance_fields(self, identifier_tag, fields, template): instances = [] parsed_fields = [] instance_tags_remaining = set(template) for field in fields: # Loop over group instances if not isinstance(field, Field) and not isinstance(field, Group): try: field = Field(*field) except TypeError: raise ParsingError( f"Invalid Field: '{field}' mut be a (tag, value) tuple." ) if field.tag == identifier_tag: continue # Skip over identifier tags if field.tag not in instance_tags_remaining: if field.tag in template: # Tag belongs to the next instance. Append the current instance to this group. instances.append(FieldList(*parsed_fields)) instance_tags_remaining = set( template) # Reset group filter parsed_fields.clear() # Start parsing the next instance else: raise ParsingError( f"Unknown tag {field.tag} found while parsing group fields {template}." ) instance_tags_remaining.remove(field.tag) parsed_fields.append(field) if parsed_fields: # Append final instance that was parsed instances.append(FieldList(*parsed_fields)) return instances
def __setitem__(self, tag: int, value: any): count = self.count(tag) if count > 1: raise DuplicateTags( tag, self.values(), message= f"Cannot set value: FieldMap contains {count} occurrence(s) of '{tag}'.", ) if not (isinstance(value, Field) or isinstance(value, Group)): # Create a new Field if value is not a Field or Group already. value = Field(tag, value) if tag in self: # Update value, retaining the Field's position in the list self._data = [ value if field.tag == tag else field for field in self.data ] else: # Add a new Field self.data.append(value)
def __init__(self, identifier, *fields, template=None, message_type="*"): """ :param identifier: A Field that identifies the repeating Group. The value of the 'identifier' Field indicates the number of times that GroupInstance repeats in this Group. :param fields: A FieldMap or list of (tag, value) tuples. :param template: Optional. The list of tags that this repeating group consists of. If no template is provided then tries to find a template corresponding to identifier.tag in the default GROUP_TEMPLATES setting. :param message_type: Optional. The message type that this repeating group is for (used to retrieve the correct default template). :raises: ParsingError if no template is specified and no template could be found in settings. """ group_identifier = Field( *identifier ) # First, make sure the group identifier is a valid Field. if template is None: templates = self.get_group_templates(group_identifier.tag, message_type=message_type) if len(templates) != 1: # FieldDicts are not message-type aware, so can only handle a single template per identifier tag raise ParsingError( f"Could not determine template for tag {group_identifier.tag}." ) template = templates[0] self.identifier = group_identifier self._instance_template = template self._instances = self._parse_fields(group_identifier, fields, template=template) if len(self._instances) != self.size: raise ParsingError( self.tag, fields, f"Cannot make {self.size} instances of {template} with {fields}.", )
def clear(self): for instance in self.instances: instance.clear() self.identifier = Field(self.identifier.tag, 0)
def __delitem__(self, index: int): del self._instances[index] self.identifier = Field(self.tag, int(self.value) - 1)
def test_eq_list_of_fields(self, fieldmap_class, fieldmap_impl_abc_123): assert fieldmap_impl_abc_123 == [Field(1, "abc"), Field(2, 123)]
def test_add_field(self, routing_id_group): fm = routing_id_group + Field(216, "z") assert ( repr(fm) == "Group(Field(215, '3'), Field(216, 'a'), Field(217, 'b'), Field(216, 'c'), Field(217, 'd'), Field(216, 'z'))" # noqa )
def test_add_sequence_of_fields(self, fieldmap_impl_abc_123): fm = fieldmap_impl_abc_123 + (Field(3, "ccc"), Field(4, "dddd")) assert len(fm) == 4 assert fm[3] == "ccc" assert fm[4] == "dddd"
def test_add_field(self, fieldmap_impl_abc_123): fm = fieldmap_impl_abc_123 + Field(3, "ccc") assert len(fm) == 3 assert fm[3] == "ccc"