Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot guess why Overloaded function implementation does not accept all possible arguments

Tags:

python

mypy

I want to fully type my Python project. But I'm stuck with a constructor that can be called with different parameters.

I've tried to remove the type from the final constructor, I've tried to remove some constructor... but still, get the same issue.

class PageObject(ABC):
    logger = logging.getLogger(__name__)

    @overload
    def __init__(self, driver: Driver) -> None:
        ...

    @overload
    def __init__(self, by: Tuple[By, str], driver: Driver) -> None:
        ...

    @overload
    def __init__(self, context: WebElement, driver: Driver) -> None:
        ...

    @overload
    def __init__(self, by: Tuple[By, str], parent: "PageObject") -> None:
        ...

    @overload
    def __init__(self, parent: "PageObject") -> None:
        ...

    def __init__(
        self,
        by: Optional[Tuple[By, str]] = None,
        context: Optional[WebElement] = None,
        parent: Optional["PageObject"] = None,
        driver: Optional[Driver] = None,
    ) -> None:

        if by and context:
            raise ValueError("You cannot provide a locator AND a context.")
        # ...

When I run mypy I got the following errors:

base/page_object.py:36: error: Overloaded function implementation does not accept all possible arguments of signature 1

base/page_object.py:36: error: Overloaded function implementation does not accept all possible arguments of signature 2

base/page_object.py:36: error: Overloaded function implementation does not accept all possible arguments of signature 3

base/page_object.py:36: error: Overloaded function implementation does not accept all possible arguments of signature 4

base/page_object.py:36: error: Overloaded function implementation does not accept all possible arguments of signature 5

like image 915
tetienne Avatar asked Jun 17 '26 04:06

tetienne


1 Answers

Here is the problem. Suppose somebody tries running PageObject(Driver()) -- that is, we pass in a Driver object as the first argument.

This matches your first overload and so would be type-checked by mypy. But what actually happens at runtime? The first runtime parameter is by, so your Driver object gets assigned to by, not driver. So now there's a mismatch between your types, since by is supposed to be of type Optional[Tuple[By, str]].

Probably the easiest workaround is to just forbid your users from using positional arguments altogether and mandate that they use only keyword arguments. You can do this like so:

class PageObject:
    @overload
    def __init__(self, *, driver: Driver) -> None:
        ...

    @overload
    def __init__(self, *, by: Tuple[By, str], driver: Driver) -> None:
        ...

    @overload
    def __init__(self, *, context: WebElement, driver: Driver) -> None:
        ...

    @overload
    def __init__(self, *, by: Tuple[By, str], parent: "PageObject") -> None:
        ...

    @overload
    def __init__(self, *, parent: "PageObject") -> None:
        ...

    def __init__(
        self,
        *,
        by: Optional[Tuple[By, str]] = None,
        context: Optional[WebElement] = None,
        parent: Optional["PageObject"] = None,
        driver: Optional[Driver] = None,
    ) -> None:
        ...

Now, mypy typechecks this without an error, and doing PageObject(Driver()) is treated as an error both by mypy and by Python. Instead, you now need to do PageObject(driver=Driver()).

If you do want to allow positional arguments, I'm afraid you'll need to redesign your code. Perhaps you can look into using staticmethods or classmethods or such so you can have different "flavors" of constructors -- basically, the factory pattern as suggested in the comments.

like image 193
Michael0x2a Avatar answered Jun 18 '26 18:06

Michael0x2a



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!