da4089 / simplefix

Simple FIX protocol implementation for Python
MIT License
229 stars 63 forks source link

Mismatch method call and method displayed. #31

Open Scrier opened 3 years ago

Scrier commented 3 years ago

I am using simplefix version 1.0.14. And it gives me error calling get with the nth parameter.

    Traceback (most recent call last):
      File "/usr/local/Cellar/python@3.9/3.9.5/Frameworks/Python.framework/Versions/3.9/lib/python3.9/threading.py", line 954, in _bootstrap_inner
        self.run()
      File "/usr/local/Cellar/python@3.9/3.9.5/Frameworks/Python.framework/Versions/3.9/lib/python3.9/threading.py", line 892, in run
        self._target(*self._args, **self._kwargs)
      File "/Users/recapital/Git/tt-fix/client.py", line 59, in run
        self._reader.on_message(msg)
      File "/Users/recapital/Git/tt-fix/message_handler.py", line 31, in on_message
        self.handle_logon(message)
      File "/Users/recapital/Git/tt-fix/message_handler.py", line 39, in handle_logon
        logon.decode(message)
      File "/Users/recapital/Git/tt-fix/tt_messages.py", line 9517, in decode
        if (val := message.get(FIELD_ENCRYPT_METHOD, nth)) is not None:
    TypeError: <lambda>() takes 1 positional argument but 2 were given

Here is the code called:

    def decode(self, message: FixMessage, nth: int = 1):
        if (val := message.get(FIELD_ENCRYPT_METHOD, nth)) is not None:
            self.encrypt_method = EncryptMethod(int(val))

Here is the field constant:

FIELD_ENCRYPT_METHOD = b'98'

And here is the get method in simplefix:

    def get(self, tag, nth=1):
        """Return n-th value for tag.

        :param tag: FIX field tag number.
        :param nth: Index of tag if repeating, first is 1.
        :return: None if nothing found, otherwise value matching tag.

        Defaults to returning the first matching value of 'tag', but if
        the 'nth' parameter is overridden, can get repeated fields."""

        tag = fix_tag(tag)
        nth = int(nth)

        for t, v in self.pairs:
            if t == tag:
                nth -= 1
                if nth == 0:
                    return v

        return None

It works if I don't pass the nth parameter, but providing this (that I have to due to repetitions) gives this weird error.

Message that I received before the decode error. 8=FIX.4.4|9=00080|35=A|49=NOTHING|56=CAPITALAS_MD|34=1|52=20210622-13:59:02.752|98=0|108=30|141=Y|10=085

da4089 commented 3 years ago

That's curious. It appears as if message.get is a lambda?

In your decode() method, can you add a first line something like:

print(message.get, type(message.get))

In a quick test, mine shows:

<bound method FixMessage.get of <simplefix.message.FixMessage object at 0x10c72ddc0>> <class 'method'>

Scrier commented 3 years ago

2021-06-24 15:52:07,803 INFO connection send: b'8=FIX.4.4|9=92|35=A|49=CAPITALAS_MD|56=NOTHING|34=1|98=0|108=30|96=12345678|141=Y|52=20210624-13:52:07.802|10=021|' <function Connection._validate_message.<locals>.<lambda> at 0x10581d1f0> <class 'function'>

da4089 commented 3 years ago

That looks like the 'message' parameter to your decode() method is not actually a FixMessage instance?

Scrier commented 3 years ago

I am using a static method for that message now that makes it work:

def get_field(message: FixMessage, tag, nth=1):
    tag = fix_tag(tag)
    nth = int(nth)
    for t, v in message.pairs:
        if t == tag:
            nth -= 1
            if nth == 0:
                return v
    return None

So the decode now is:

    def decode(self, message: FixMessage, nth: int = 1):
        if (val := get_field(message, FIELD_ENCRYPT_METHOD, nth)) is not None:

And that works. And I can read the message type from it before. As I do a assert message.message_type == MESSAGE_LOGON right before.

da4089 commented 3 years ago

Glad it's working now, at least.

I appreciate it might not be possible, but if it is, could you send me the source code file? I'd like to investigate this a little more to see if I can understand what was happening ...

Scrier commented 3 years ago

https://gitlab.com/recapital/tasks/-/snippets/2140207 I can give you this, the Client is one of the example applications out there that I built from. Enums is more or less the standard types and constants. Its the path from your decode more or less.