PyPubSub with function currying?

I’m trying to add a Listener to a topic, where the Listener is a curried function (has one parameter), and it seems like it should work - but it doesn’t. :slight_smile:

from functools import partial

def new_game_msg(*args):
    pub.sendMessage(topicName='game.new', some_data='this is a test message')

def MakeStatusBar(top_frame):

    statusbar = top_frame.CreateStatusBar()
    statusbar.SetFieldsCount(3)
    statusbar.SetStatusWidths([200, 200, -2])

    status_func = partial(got_status_msg, statusbar)

    status_func(some_data='Directly testing function currying')  # Works

    pub.subscribe(listener=status_func, topicName='game.new')  # Breaks

    # pub.subscribe(listener=got_msg, topicName='game.new')  # Works, when alone

    return statusbar

def got_status_msg(status_bar, some_data):
    print(f"*** got_status_msg, args = {some_data}\n")
    status_bar.SetStatusText(some_data, 0)

def got_msg(some_data):
    print(f"*** got_msg: data = {some_data}\n")

I’d like the Listener to have a pointer to a StatusBar, for posting messages. (Note this is just experimentation-code, learning WxPython.) The error is:

Traceback (most recent call last):
  File "/Code/wxpy/hex_test/src/wx/menu_bar.py", line 11, in new_game_msg
    pub.sendMessage(topicName='game.new', some_data='this is a test message')
    raise SenderUnknownMsgDataError(self.topicNameTuple,
pubsub.core.topicargspec.SenderUnknownMsgDataError: 
Some optional args unknown in call to sendMessage('('game', 'new')', some_data): some_data

The Listener, as I understood it, can be any Callable - so is there a way of making this work? Thanks.

I’m guessing you can’t re-edit posts? Not finding it, if so. Anyway, I updated the code to be a fully working example, partly as an attempt at rubber-ducking. It didn’t work, the problem is still there.

from functools import partial
import time

import wx
from pubsub import pub

def MakeStatusBar(top_frame):
    statusbar = top_frame.CreateStatusBar()
    statusbar.SetFieldsCount(2)
    statusbar.SetStatusWidths([400, 400])

    # pub.subscribe(listener=got_msg, topicName='game.new')  # Works, when alone

    status_func = partial(got_status_msg, statusbar)
    print(f"status_func[callable={callable(status_func)}] = {status_func}")
    status_func(some_data='Directly calling the curried-function')  # Calling the function directly - works
    pub.subscribe(listener=status_func, topicName='game.new')  # Breaks

    return statusbar

def got_msg(some_data):
    print(f"\n*** got_msg: data = {some_data}\n")

def got_status_msg(status_bar, some_data):
    print(f"\n*** got_status_msg, args = {some_data}\n")
    status_bar.SetStatusText(some_data, 0)

class topFrame(wx.Frame):
    def __init__(self, parent):
        super().__init__(parent, wx.ID_ANY, title='testing', size=(500, 500))
        self.status_bar = (MakeStatusBar(self))

class myApp(wx.App):
    def __init__(self):
        wx.App.__init__(self, redirect=True, filename='errlog.txt')  # To log error to a file
        # super().__init__(self)  # Doesn't take redirect? Weird.

        self.topFrame = topFrame(None)
        self.SetTopWindow(self.topFrame)
        self.topFrame.Show()
        print(f"in 2 seconds, will send a message to the Statusbar")
        time.sleep(2)
        print(f"sending...\n")

        try:
            pub.sendMessage(topicName='game.new', some_data='publishing a new-game message')
        except Exception as ex:
            print(f"WTF? got {str(ex)}\n")
        print(f"message has been sent.\n")

if __name__ == '__main__':
    my_app = myApp()
    my_app.MainLoop()

I don’t know if it’s a problem with my code, or understanding of the libraries, but it certainly feels like a bug, or some kind of “magic” going on under-the-hood of PyPubSub, to find the callable, instead of just calling it.

Imagine my surprise, to find out there is a solution, baked into PyPubSub. :slight_smile: The listener itself already allows for curried arguments. I can just directly pass in the StatusBar parameter I want.

# status_func = partial(got_status_msg, statusbar)
# print(f"status_func[callable={callable(status_func)}] = {status_func}")
# status_func(some_data='Directly calling the curried-function')  # Calling the function directly - works
pub.subscribe(listener=got_status_msg, topicName='game.new', status_bar=statusbar)  # Now works

Clearly the developers of PyPubSub, know what they’re doing. :slight_smile: Good work.

I thought the current version of PyPubSub did away with *args and now requires named keywords or no arguments.

Yeah, but I just tried the curried-function with a keyword, and it didn’t work either.

status_func = partial(got_status_msg, status_bar=statusbar)

So using the built-in function-currying is the only solution.