isnt this the opposite of how a decorator works?

Yesterday we upgraded our slackclient module in python and needed to change some code based on this migration guide for the RTM API.

https://github.com/slackapi/python-slackclient/wiki/Migrating-to-2.x

Here is my code...

import slack

slack_token = os.environ['SLACK_BOT_TOKEN']

rtmclient = slack.RTMClient(token=slack_token)

@slack.RTMClient.run_on(event='message')
def parse_message(**payload):
    data = payload['data']
    channel_id = data['channel']
    print(data)
    print(channel_id)

rtmclient.start()

I had never worked with a Python decorator before this.

After reading up on decorators, it was my understanding that the decorator slack.RTMClient.run_on would be called if I were to call the function parse_message.

This code appears to be doing the opposite.

Why/how does this code work when rtmclient.start() is called?

1 answer

  • answered 2019-07-18 15:59 tobias_k

    Not familiar with that particular API, but

    @slack.RTMClient.run_on(event='message')
    def parse_message(**payload):
        ...
    

    is essentially the same as

    def parse_message(**payload):
        ...
    
    parse_message = slack.RTMClient.run_on(event='message')(parse_message)
    

    The decorator is not called when the function is called, but when it is declared. It will then usually define and return a new function that will be called instead of the original function, e.g. with additional logging, memoization, and the like.

    But it is also perfectly possible for a decorator to just return the original version of the function,1 but e.g. register that function as a callback for some events.

    A very much simplified example:

    my_callbacks = []
    def register(f):
        my_callbacks.append(f)
        return f
    
    @register
    def foo():
        print("calling foo")
    
    
    for f in my_callbacks:
        f()
    

    1) As noted in comments, this decorator seems to not even return the original function but None, meaning that the function will no longer be directly callable after being decorated, only via the callback.