0

Problem Statement

I'm subclassing Gtk4 container widgets so I can add functionality to various property changes. For this question I'll use set_orientation from Gtk.Box and the Gtk.Orientable interface.

I've gotten various different signatures to work, based on different documentation sources, but all of them bother pylint, and none seem ideal. Is there a way to write the code so there are no linting errors? If that's not possible, what approach should I use?

Attempt #1

When I base the method signature on the Gnome Python APIs for the Orientable interface:

    @override
    def set_orientation(self, orientation: Gtk.Orientation) -> None:
        if not self.get_orientation() == orientation:
            super().set_orientation(orientation)
            logger.info("Do extra orientation stuff.")

I get these pylint errors:

    minimal.py:24: [W0221(arguments-differ), MyBox.set_orientation] Number of \
    parameters was 2 in 'Box.set_orientation' and is now 2 in overriding \
    'MyBox.set_orientation' method

    minimal.py:24: [W0221(arguments-differ), MyBox.set_orientation] Variadics \
    removed in overriding 'MyBox.set_orientation' method

Attempt #3

When I base the signature on inspect.signature(MyBox.set_orientation):

    @override
    def set_orientation(*args, **kwargs) -> None:
        # Handle positional and keyword arguments
        if "orientation" in kwargs:
            logger.info("Using kwargs for orientation argument")
            orientation = kwargs["orientation"]
        else:
            logger.info("Using args[1] for orientation argument")
            orientation = args[1]

        logger.info(f"\nself:        {args[0]=}"
                    f"\norientation: {orientation=}"
                    f"\nno kwargs:   {kwargs=}")
        super(MyBox, args[0]).set_orientation(orientation)
        logger.info("Do extra orientation stuff.")

I just get the obvious pylint error:

   minimal.py:45: [E0213(no-self-argument), MyBox.set_orientation] Method \
    'set_orientation' should have "self" as first argument

Attempt # 2

If I try combing the two signatures:

    @override
    def set_orientation(
                        self,
                        *args,
                        orientation: Gtk.Orientation | None = None,
                        **kwargs,
                        ) -> None:
        # Handle positional and keyword arguments
        if orientation is None:
            orientation = args[0]

        if not self.get_orientation() == orientation:
            super().set_orientation(orientation)
            logger.info("Do extra orientation stuff.")

It avoids the variadicts error but still throws a parameter error:

    minimal.py:25: [W0221(arguments-differ), MyBox.set_orientation] Number of \
    parameters was 2 in 'Box.set_orientation' and is now 4 in overriding \
    'MyBox.set_orientation' method

Final Thoughts

I also tried just self and *args, but that can't handle keyword arguments, so I won't bother including it.

I keep going back and forth about matching the API signature with Attempt 1, and matching the apparent implementation signature with attempt 3. I can obviously add a pylint exception for the signature once I decide how it should be written, but can anyone help me understand how to make that decision?

Working Code example

#!/usr/bin/env python3
"""
Minimal Gtk4 Orientation Override Test
"""
import sys
from typing import override
from loguru import logger
import gi
gi.require_version("Gtk", "4.0")
# pylint: disable=wrong-import-position
from gi.repository import Gtk  # noqa: E402

logger.remove(0)
logger.add(sys.stderr, level="INFO", colorize=True)


class MyBox(Gtk.Box):
    """Basic box subclass example."""
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        logger.info("Do extra orientation initialization.")

    # Attempt 1
    @override
    def set_orientation(self, orientation: Gtk.Orientation) -> None:
        if not self.get_orientation() == orientation:
            super().set_orientation(orientation)
            logger.info("Do extra orientation stuff.")


def rotate_box(the_box: MyBox):
    """Toggle the_box between vertical and horizontal orientations."""
    if the_box.get_orientation() == Gtk.Orientation.VERTICAL:
        # test positional argument
        the_box.set_orientation(Gtk.Orientation.HORIZONTAL)
    else:
        # test keyword argument
        the_box.set_orientation(orientation=Gtk.Orientation.VERTICAL)


def on_activate(app):
    """Build and show the window."""
    win = Gtk.ApplicationWindow(application=app)

    box = MyBox(orientation=Gtk.Orientation.VERTICAL)
    button = Gtk.Button(label="Rotate Box Orientation")
    button.connect('clicked', lambda _x: rotate_box(box))
    dummy = Gtk.Button(label="A dummy button to make orientation matter.")

    box.append(button)
    box.append(dummy)
    win.set_child(box)
    win.present()


def main():
    """Build and run the application."""
    main_app = Gtk.Application(application_id="com.coldnight.overridetest")
    main_app.connect('activate', on_activate)
    main_app.run(sys.argv)


if __name__ == "__main__":
    main()
2

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.