Skip to content

Conversation

cknd
Copy link
Contributor

@cknd cknd commented May 23, 2017

PR Summary

FigureCanvasWebAggCore.get_diff_image() can crash with an UnboundLocalError on its return value buff. This is because buff is only created when self._png_is_old == True, but get_diff_image tries to return it in either case.

We presumably ran into this because tornado likes to call FigureManagerWebAgg.refresh_all multiple times in between calls to draw() in certain situations -- then, even if draw() did set the _png_is_old flag, refresh_all resets it through its call to get_diff_image, and so the next refresh will fail.

This change keeps the buffer around as an attribute of the Canvas, so get_diff_image can return it regardless of whether it has been refreshed.

This change lets get_diff_image return None when the png isn't outdated and adds a condition to refresh_all to only send diffs that are non-None.


return buff
self.buff = buff
return self.buff
Copy link
Member

Choose a reason for hiding this comment

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

Would it be better to return buff inside the if block and return None if there is no changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

semantically yes, but the consumer of that return value in refresh_all then complains that None is not a buffer. (send_binary, that is)

    def refresh_all(self):
        if self.web_sockets:
            diff = self.canvas.get_diff_image()
            for s in self.web_sockets:
                s.send_binary(diff)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess refresh_all could inspect self.canvas._png_is_old to see whether there is a need to refresh, and otherwise just do nothing (no sending of binaries). but that's not entirely clean in terms of attribute access

Copy link
Member

Choose a reason for hiding this comment

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

Maybe in refresh_all it should only send the diff if it is non None? What does the consumer side do if it receives the same diff multiple times?

👎 on looking at private attributes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That sounds like the sanest way to do it. I've updated the PR

@tacaswell tacaswell added this to the 2.1 (next point release) milestone May 23, 2017
@QuLogic QuLogic merged commit 459f185 into matplotlib:master May 23, 2017
@cknd cknd deleted the feature_keep_buffer branch May 23, 2017 21:54
@cknd
Copy link
Contributor Author

cknd commented May 23, 2017

Thx for the quick reaction everyone!
I'm not familiar with the project's release schedule, but would it maybe make sense to include this already in the next bug fix release (2.0.3) instead of the 2.1 milestone?

@tacaswell
Copy link
Member

Congratulations on your first contribution to Matplotlib 🎉 ! Hopefully we will hear from you again.

It is unlikely that there will be a 2.0.3, but 2.1 is targeted for July.

@cknd
Copy link
Contributor Author

cknd commented May 24, 2017

Cheers for running a welcoming project! July-ish sounds perfectly reasonable for us. Thanks again

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants