Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moved callback execution to separate methods. #490

Merged
merged 3 commits into from May 1, 2024

Conversation

deepadmax
Copy link
Contributor

I've moved the execution loops of callbacks to separate methods. This is so that the behaviour can be containedly redefined without needing to hard-rewrite all surrounding logic. As it is now, subclassing the client and adding functionality on running the callbacks would entail needing to keep an eye out for any changes to these whole methods upstream, lest an update be missed and either break it or simply not benefit from patches or upgrades to the flow.

@PaarthShah
Copy link
Collaborator

Looks like the unit tests weren't too happy with it, but I really do like this concept and I think it's absolutely a move in the right direction

Comment on lines 132 to 153
def execute(self, event, room: Optional[MatrixRoom] = None) -> Optional[Awaitable]:
if self.filter is None or isinstance(event, self.filter):
result = self.func(room, event) if room else self.func(event)
if inspect.isawaitable(result):
await result
if room:
return self.func(room, event)
else:
return self.func(event)

async def async_execute(self, event, room: Optional[MatrixRoom] = None) -> None:
result = self.execute(event, room)
if inspect.isawaitable(result):
await result
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In practice, both the sync and async clients need to attempt to invoke a callback as either sync or async; someone can reasonably pass a sync (and hopefully, nonblocking) callback to an async client and it ought to work

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There wasn't one method for each before. In stead, in places where it was done solely synchronously, it was hard-coded to loop and do the if-statements. All this does is to put that into a method to avoid repetition. And to further avoid repetition, the asynchronous method just calls the synchronous one; if an awaitable is returned, await it.

Everything should work exactly as before. Are you suggesting the synchronous method should have an extra step added for if the function is asynchronous? For example:

def execute(self, event, room: Optional[MatrixRoom] = None) -> Optional[Awaitable]:
    if self.filter is None or isinstance(event, self.filter):
        if room:
            result = self.func(room, event)
        else:
            result =  self.func(event)

        if inspect.isawaitable(result):
            asyncio.run(result)

@PaarthShah PaarthShah marked this pull request as ready for review April 22, 2024 01:54
Comment on lines 146 to 147
if inspect.isawaitable(result):
asyncio.run(result)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like that the main motivation to have a separate sync_execute and async_execute is right here: the ability to support async callbacks in a sync client.

Considering the extra complexity it introduces, and considering that it involves implicitly supporting/adding new features to a soft-deprecated usecase (using matrix-nio without async) I'm tempted to say that we might not care to include this extra complexity, and stick to the original implementation of execute here.

I don't generally expect people who know how to write async def callback functions to still be using the sync client, as well.

Simultaneously, I don't think this is a bad change and it's fairly straightforward. So I'm more tempted to keep it.

Comment on lines 134 to 136
# Checks the filter and executes the function once.
# sync_execute and async_execute will each determine
# how to run an awaitable if one is returned.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use docstring formatting

Comment on lines 146 to 147
if inspect.isawaitable(result):
asyncio.run(result)
Copy link
Collaborator

@PaarthShah PaarthShah May 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also just noticed it as I was making the doc change: asyncio.run has a slightly different requirement than simply taking an awaitable

Suggested change
if inspect.isawaitable(result):
asyncio.run(result)
if inspect.iscoroutine(result):
asyncio.run(result)

@PaarthShah PaarthShah merged commit 939a11d into matrix-nio:main May 1, 2024
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants