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

Union types and generics #4432

Closed
mitar opened this issue Jan 6, 2018 · 10 comments
Closed

Union types and generics #4432

mitar opened this issue Jan 6, 2018 · 10 comments

Comments

@mitar
Copy link

mitar commented Jan 6, 2018

I have this example:

import typing

A = typing.TypeVar('A')

class Base(typing.Generic[A]):
    def do(self, input: A) -> A:
        return input

Inputs1 = typing.Union[int, str]

class Numbers1(Base[Inputs1]):
    pass

Inputs2 = typing.TypeVar('Inputs2', bound=typing.Union[int, str])

class Numbers2(Base[Inputs2]):
    pass

class Numbers3(Base[Inputs1]):
    def do(self, input: Inputs2) -> Inputs2:
        return input

reveal_type(1)  # This is line 23
reveal_type(Numbers1().do(1))
reveal_type(Numbers1().do('foobar'))
reveal_type(Numbers2().do(1))
reveal_type(Numbers2().do('foobar'))
reveal_type(Numbers3().do(1))
reveal_type(Numbers3().do('foobar'))

When I run mypy (mypy===0.570-dev-1aba774cc00b49627b86f95c8172adbb12b52891), I get:

test.py:23: error: Revealed type is 'builtins.int'
test.py:24: error: Revealed type is 'Union[builtins.int, builtins.str]'
test.py:25: error: Revealed type is 'Union[builtins.int, builtins.str]'
test.py:26: error: Revealed type is '<nothing>'
test.py:26: error: Argument 1 to "do" of "Base" has incompatible type "int"; expected <nothing>
test.py:27: error: Revealed type is '<nothing>'
test.py:27: error: Argument 1 to "do" of "Base" has incompatible type "str"; expected <nothing>
test.py:28: error: Revealed type is 'builtins.int*'
test.py:29: error: Revealed type is 'builtins.str*'

Ideally, Numbers1 should work like Numbers3, or at least Numbers2 should. But currently it seems like only Numbers3 make a strict return type and not union type. It is sad that I have to reimplement do method just to get correct return types. This is really going against DRY principle.

@gvanrossum
Copy link
Member

That seems wishful thinking. If you expand the generics in Numbers1, you end up with a do() method that takes an arg of type Union[int, str] and returns Union[int, str], so the behavior is as it should be.

The problem with Numbers2 seems to be that you've made Numbers2 a generic class. Now instantiating a generic class without giving it a specific type typically defaults to Any, but that doesn't fly since the generic var has a union bound. There's some bug here that causes this to come out as <nothing> (probably already in the tracker somewhere else) but at best it would behave the same as Numbers1, not Numbers3.

@mitar
Copy link
Author

mitar commented Jan 6, 2018

Yea, I think I understand why it is not working at the moment. But hopefully it will could work. So how I see is that A is a placeholder and that then a subclass can specify what it is. And it seems there is no way to tell currently that it can be or type A or type B, but whatever it will be, it will be the same type. So it seems you cannot mix generic class with methods using type variables? Maybe there could be some syntax for this?

class Numbers2(Base.params(Inputs2)):
    pass

Less fancy than [...] but could tell to do really replace A with Inputs and not create a new generic?

@ilevkivskyi
Copy link
Member

TBH, I don't see an issue here (apart from Uninhabited instead of Any for an unconstrained TypeVar, but we have an issue for this). @mitar could you please provide some more details, what is the code you have, mypy output, and expected output?

As a side note, maybe we should add an issue template? (GitHub supports this.) For me personally it would be easier to see these points:

  • The code (or a simplified repro if the source is private)
  • Actual behavior
  • Expected behavior
  • mypy/Python version
  • Has a user tried running mypy from master
  • Full command line with all flags used
  • If there was a traceback - paste full traceback

@gvanrossum @JukkaL What do you think?

@gvanrossum
Copy link
Member

gvanrossum commented Jan 6, 2018 via email

@ilevkivskyi
Copy link
Member

But feel free to set up a template, as long as it's also easy to ignore when people don't need the guidance.

It is jut plain text placed initially in the issue, one can edit it or remove completely. I will make one now, and we will see how it goes.

@ilevkivskyi
Copy link
Member

OK, here is the PR #4433

@mitar
Copy link
Author

mitar commented Jan 6, 2018

could you please provide some more details, what is the code you have, mypy output, and expected output?

I provided that in the initial issue text? There is code, which mypy I ran, current output, and expected output is the last two lines of the mypy output.

Well, Mitar has a feature request formulated as a bug report.

Oh, sorry, I thought it was clear that is a feature request. Is there some other/better way to submit features requests? In README it says that issues are for feature requests as well:

Please report any bugs and enhancement ideas using the mypy issue tracker:

@emmatyping
Copy link
Collaborator

Is there some other/better way to submit features requests?

You don't explicitly say whether or not this is a feature request or bug report, so being explicit about that at the beginning would be helpful.

@mitar
Copy link
Author

mitar commented Jan 6, 2018

Sorry about that. Yea, it is a feature request.

@mitar
Copy link
Author

mitar commented Apr 30, 2019

I am closing this issue in favor of #6746 where I tried to describe in more detail what I am doing, what I tried, and how it does not work.

@mitar mitar closed this as completed Apr 30, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants