Skip to content

Support listener interface in python plugins #4698

@Lakitna

Description

@Lakitna

Is your feature request related to a problem? Please describe.

I've been playing with plugins recently. As part of polishing everything to a nice, robust thing, I wanted to add a listener hook to a Brower Python plugin. I have found a few ways, but they range from very hacky to somewhat hacky.

Describe the solution you'd like

Document or build an official way of adding listeners to Python plugins.

Describe alternatives you've considered

Keep it hacky, with the chance that the next change breaks it.

Additional context

Slack discussion: https://robotframework.slack.com/archives/C015KB1QSDN/p1772463092653739

My hacky solutions:

1 - Wrap Browser._end_test in a function.

Hacky because: Accessing and overwriting a private class method.
Hackyness scale: High

class BrowerPlugin(LibraryComponent):
    def __init__(self, library: Browser):
        super().__init__(library)

        fn = self.library._end_test
        self.library._end_test = lambda *args, **kwargs: self.end_text(fn, *args, **kwargs)

    def end_test(self, super_end_test, *args, **kwargs):
        # Do custom stuff
        
        return super_end_test(*args, **kwargs)

2 - Forcefully update Browser.ROBOT_LIBRARY_LISTENER to be a list that includes both the original value and self.

This causes type issues as Browser.ROBOT_LIBRARY_LISTENER has type Browser.

This means we're stuck at the same listener interface that Browser uses (v2 at the time of writing).

Hacky because: Accessing and changing the type of a class variable.
Hackyness scale: Medium

class BrowerPlugin(LibraryComponent):
    def __init__(self, library: Browser):
        super().__init__(library)

        self.library.ROBOT_LIBRARY_LISTENER = [
            self.library.ROBOT_LIBRARY_LISTENER,
            self,
        ]

    def end_test(self, *args, **kwargs):
        # Do custom stuff

2 - Set ROBOT_LISTENER_API_VERSION

I have no idea why this works. As far as I understand listeners, this should not work. But it does!

Hacky because: Relies on unclear and undocumented behavior
Hackyness scale: High

class BrowerPlugin(LibraryComponent):
    ROBOT_LISTENER_API_VERSION = 3

    def __init__(self, library: Browser):
        super().__init__(library)

    def end_test(self, *args, **kwargs):
        # Do custom stuff

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions