From 88bfa11b8a225aa754e050d10b482b51e432dbe1 Mon Sep 17 00:00:00 2001 From: SamDanielThangarajan <12202554+SamDanielThangarajan@users.noreply.github.com> Date: Tue, 8 Oct 2024 14:59:30 +0200 Subject: [PATCH] fix_parser: parse messages --- .../fix/parser/definitions.py | 10 ++++- src/nasdaq_protocols/fix/parser/parser.py | 20 +++++++++- tests/test_fix_parser.py | 39 +++++++++++++++---- tests/testdata.py | 10 +++++ 4 files changed, 68 insertions(+), 11 deletions(-) diff --git a/src/nasdaq_protocols/fix/parser/definitions.py b/src/nasdaq_protocols/fix/parser/definitions.py index 88b3f24..ec65bce 100644 --- a/src/nasdaq_protocols/fix/parser/definitions.py +++ b/src/nasdaq_protocols/fix/parser/definitions.py @@ -1,6 +1,5 @@ from typing import Any -import attr import attrs from nasdaq_protocols.common import TypeDefinition @@ -13,6 +12,7 @@ 'Field', 'Group', 'Component', + 'Message', 'Definitions' ] @@ -51,9 +51,17 @@ class Component(EntryContainer): name: str = attrs.field(kw_only=True) +@attrs.define +class Message(EntryContainer): + tag: str + name: str + category: str + + @attrs.define class Definitions: fields: dict[str, FieldDef] = attrs.field(init=False, factory=dict) components: dict[str, Component] = attrs.field(kw_only=True, factory=dict) header: EntryContainer = attrs.field(kw_only=True, factory=EntryContainer) trailer: EntryContainer = attrs.field(kw_only=True, factory=EntryContainer) + messages: list[Message] = attrs.field(kw_only=True, factory=list) diff --git a/src/nasdaq_protocols/fix/parser/parser.py b/src/nasdaq_protocols/fix/parser/parser.py index 54d882a..c1ed4b8 100644 --- a/src/nasdaq_protocols/fix/parser/parser.py +++ b/src/nasdaq_protocols/fix/parser/parser.py @@ -7,7 +7,9 @@ FieldDef, Component, Field, - EntryContainer, Group + EntryContainer, + Group, + Message ) from .version_types import ( get_supported_types, @@ -36,7 +38,8 @@ def parse(file: str) -> Definitions: 'fields': partial(_handle_fields, get_supported_types(version)), 'components': _handle_components, 'header': _handle_header, - 'trailer': _handle_trailer + 'trailer': _handle_trailer, + 'messages': _handle_messages } definitions = Definitions() @@ -58,6 +61,19 @@ def _handle_trailer(definitions: Definitions, root, element) -> None: _handle_entry(definitions, definitions.trailer, root, entry) +def _handle_messages(definitions: Definitions, root, element) -> None: + LOG.debug('parsing ') + for msg in element: + message = Message( + tag=msg.get('msgtype'), + name=msg.get('name'), + category=msg.get('msgcat') + ) + for entry in msg: + _handle_entry(definitions, message, root, entry) + definitions.messages.append(message) + + def _handle_fields(types: SupportedTypes, definitions: Definitions, _root, diff --git a/tests/test_fix_parser.py b/tests/test_fix_parser.py index d53dc24..f28b689 100644 --- a/tests/test_fix_parser.py +++ b/tests/test_fix_parser.py @@ -120,14 +120,6 @@ def assert_component(name, *fields_defn): assert field.required == field_defn[1] assert field.field.name == field_defn[0] - def assert_group(name, group, *fields_defn): - assert name == group.name - assert len(group.entries) == len(fields_defn) - for i, field_defn in enumerate(fields_defn): - field = group.entries[i] - assert field.field.name == field_defn[0] - assert field.required == field_defn[1] - assert_component('InstrmtLegGrp', ('PossResend', False), ('QuoteStatus', False)) assert_component('Instrument', ('PossResend', True), ('QuoteStatus', True)) assert_component('InstrumentExtensionNoInstrAttribSubGroup', ('PossResend', True), ('QuoteStatus', False)) @@ -162,3 +154,34 @@ def test__fix_parser__trailer_is_parsed(fix_44_definitions): assert fix_44_definitions.trailer.entries[0].field.tag == '10' assert fix_44_definitions.trailer.entries[0].field.type == FixString assert fix_44_definitions.trailer.entries[0].required + + +def test__fix_parser__message_is_parsed(fix_44_definitions): + assert len(fix_44_definitions.messages) == 1 + message = fix_44_definitions.messages[0] + + assert message.tag == 'A' + assert message.name == 'Logon' + assert message.category == 'Session' + + assert len(message.entries) == 4 + assert message.entries[0].field.name == 'HeartBtInt' + assert message.entries[0].required + assert message.entries[1].field.name == 'PossResend' + assert not message.entries[1].required + assert message.entries[2].field.name == 'QuoteStatus' + assert not message.entries[2].required + assert_group( + 'Instrument_Group', + message.entries[3], + ('PossResend', True), ('QuoteStatus', True) + ) + + +def assert_group(name, group, *fields_defn): + assert name == group.name + assert len(group.entries) == len(fields_defn) + for i, field_defn in enumerate(fields_defn): + field = group.entries[i] + assert field.field.name == field_defn[0] + assert field.required == field_defn[1] diff --git a/tests/testdata.py b/tests/testdata.py index 1a6711d..f4309eb 100644 --- a/tests/testdata.py +++ b/tests/testdata.py @@ -200,6 +200,15 @@ + + + + + + + + + @@ -223,6 +232,7 @@ +