Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheriting all __init__ arguments' type hints from parent class

I'm working with inheritance from other modules and classes made by others. And so whenever I make a subclass that inherits from an existing class like tkinter.Frame, pandas.Dataframe or sqlite3.Connection I get all the arguments with their type-hints (I'm using VS Code and by pressing Tab it autocompletes def __init__ and generates them). But the type hints are always broken, especially the Ellipsis ....

I know it can be solved by importing the type hints if there exists TypeVar, or TypeAlias for it, or remove them altogether, or even remove all the extra arguments that I don't really need and use *args, **kwargs instead, but I don't want that.

I want my subclass to function the same way as the superclass did, and only extend its functionality. I found that explicate arguments with their type hints makes it better to work with class and what it can do.

Example:

import tkinter
from tkinter import ttk

class MyFrame(ttk.Frame):
    def __init__(self, master: tkinter.Misc | None = ..., *, border: tkinter._ScreenUnits = ...,
        borderwidth: tkinter._ScreenUnits = ..., class_: str = ..., cursor: tkinter._Cursor = ...,
        height: tkinter._ScreenUnits = ..., name: str = ..., padding: _Padding = ...,
        relief: tkinter._Relief = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ...,
        width: tkinter._ScreenUnits = ...) -> None:
    
        super().__init__(master, border, borderwidth, class_, cursor, height, name,
                        padding, relief, style, takefocus, width)

In this example all ellipsis cause problems and some type hints don't show up, _Padding for example.

And running the code with this class raises this error:

AttributeError: module 'tkinter' has no attribute '_ScreenUnits'

or

AttributeError: module 'tkinter' has no attribute '_Cursor'

And if I decided to remove all type hints, I would get this error because of the ellipsis:

TypeError: can only concatenate str (not "ellipsis") to str

meaning that ... is getting passed as str value.

Is there an easy way to inherit the type hints from the superclass, without breaking my code?

Or am I doing something wrong?

like image 217
Abdulrahman Sheikho Avatar asked Oct 20 '25 19:10

Abdulrahman Sheikho


1 Answers

This should do the trick for you:

from typing import ParamSpec, TypeVar, Callable
from functools import update_wrapper

from tkinter import ttk

P = ParamSpec("P")
T = TypeVar("T")


def inherit_signature_from(
    original: Callable[P, T]
) -> Callable[[Callable], Callable[P, T]]:
    """Set the signature of one function to the signature of another."""
    def wrapper(f: Callable) -> Callable[P, T]:
        return update_wrapper(f, original)
    return wrapper


class MyFrame(ttk.Frame):
    @inherit_signature_from(ttk.Frame.__init__)
    def __init__(self, *args, **kwargs):
        # do stuff?
        super().__init__(*args, **kwargs)

VSCode/Pycharm/mypy will now think that MyFrame.__init__ has the same signature as ttk.Frame.__init__ and will check them accordingly.


If you're on python3.12 and your type-checker of choice has adopted PEP 695 already, you can also use the new parameter syntax, which avoids importing TypeVar and ParamSpec by defining them on the fly within the scope of the function:

from typing import Callable
from functools import update_wrapper


def inherit_signature_from[T, **P](
    original: Callable[P, T]
) -> Callable[[Callable], Callable[P, T]]:
    """Set the signature of one function to the signature of another."""
    def wrapper(f: Callable) -> Callable[P, T]:
        return update_wrapper(f, original)
    return wrapper
like image 121
Robin Gugel Avatar answered Oct 22 '25 08:10

Robin Gugel



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!