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()