Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

__getnewargs__ for a subclass incompatible with return type of supertype #13309

Open
eli-schwartz opened this issue Aug 2, 2022 · 2 comments
Labels
bug mypy got something wrong false-positive mypy gave an error on correct code topic-inheritance Inheritance and incompatible overrides

Comments

@eli-schwartz
Copy link
Contributor

Here is an example commit with the issue: eli-schwartz/meson@4d204bb

mypy.ini: https://github.com/mesonbuild/meson/blob/27626124d0bdd89f5f276f70a73b4713aa3aac3d/.mypy.ini

$ mypy --version
mypy 0.961 (compiled: yes)

$ mypy mesonbuild/interpreter/primitives/string.py
mesonbuild/interpreter/primitives/string.py:211:5: error: Return type "Tuple[str, str]" of "__getnewargs__" incompatible with return type "Tuple[str]" in supertype "str"

I subclassed str and treat it specially with its own incompatible __new__() to enforce a specific creation pattern. After discovering I need to pickle it as well, I added a __getnewargs__() which is also incompatible. mypy is happy with my incompatible new, but not with the getnewargs that needs to match the argument contract of new.

@eli-schwartz eli-schwartz added the bug mypy got something wrong label Aug 2, 2022
@AlexWaygood AlexWaygood added false-positive mypy gave an error on correct code topic-inheritance Inheritance and incompatible overrides labels Aug 2, 2022
@erictraut
Copy link

Could you provide a minimal self-contained example that demonstrates the problem?

@eli-schwartz
Copy link
Contributor Author

If I just extract that class on its lonesome, and add in a primitive version of the join_paths() function from elsewhere in the project that makes this whole thing useful:

# foo.py
import os
import typing as T

class OptionString(str):
    optname: str

    def __new__(cls, value: str, name: str) -> 'OptionString':
        obj = str.__new__(cls, value)
        obj.optname = name
        return obj

    def __getnewargs__(self) -> T.Tuple[str, str]:
        return (str(self), self.optname)


def join_paths(s1: str, s2: str) -> str:
    val = os.path.join(s1, s2)

    if isinstance(s1, OptionString):
        name = os.path.join(s1.optname, s2)
        return OptionString(val, name)
    else:
        return val

Expected usage:

>>> foo.OptionString('/usr', '{prefix}')
'/usr'
>>> s = foo.OptionString('/usr', '{prefix}')
>>> print(s)
/usr
>>> s + '/lib'
'/usr/lib'
>>> type(s + '/lib')
<class 'str'>
>>> type(s)
<class 'foo.OptionString'>
>>> foo.join_paths(s, 'lib')
'/usr/lib'
>>> type(foo.join_paths(s, 'lib'))
<class 'foo.OptionString'>
>>> foo.join_paths(s, 'lib').optname
'{prefix}/lib'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong false-positive mypy gave an error on correct code topic-inheritance Inheritance and incompatible overrides
Projects
None yet
Development

No branches or pull requests

3 participants