Skip to content

Parameterless generic functions are allowed #2885

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

Closed
miedzinski opened this issue Feb 20, 2017 · 13 comments
Closed

Parameterless generic functions are allowed #2885

miedzinski opened this issue Feb 20, 2017 · 13 comments

Comments

@miedzinski
Copy link
Contributor

miedzinski commented Feb 20, 2017

from typing import TypeVar
T = TypeVar('T')
def f() -> T: ...

Mypy is absolutely OK with this code, but it makes no sense (it is not possible to return T without argument).

@ilevkivskyi
Copy link
Member

ilevkivskyi commented Feb 20, 2017

I don't think mypy should necessarily prohibit something that "makes no sense". Could this cause a false positive or false negative (uncaught runtime error)? If yes, then those are bugs, if no, then there is no problem.

@miedzinski
Copy link
Contributor Author

I disagree. You could say the same about usage of ClassVar in function signatures, but I'm pretty sure you think otherwise. :)

@ilevkivskyi
Copy link
Member

These two cases are different. For ClassVar a user might expect some guarantees (like accepting only C.x as an argument of a function), that are in fact not provided, we want to avoid such situations. But what are the guarantees here that could be expected by a user but not actually provided? If you see them, then this might be a reason to "fix" this.

@miedzinski
Copy link
Contributor Author

Okay, that's a point. On the other hand, I could see an user with poor understanding of generics writing something like

T = TypeVar('T')
U = TypeVar('U')
def f(x: T) -> U: ...

but as soon as he would provide some implementation for f (and it'd result in returning something other than None, assuming --strict-optional is off) mypy would throw an error.

If no one else considers this a bug, I can close this.

@JukkaL
Copy link
Collaborator

JukkaL commented Feb 21, 2017

There actually is a valid reason to only use T in a return value type, at least if the return type is generic. A contrived example:

class MyList(List[T]): ...

def make_list() -> List[T]:
    return MyList()  # want to hide the implementation detail that it's actually a MyList

a: List[int] = make_list()

(Also, list() basically behaves like make_list in the above example, though it's a bit more complicated.)

However, I can't think of any real use case where plain T would be a reasonable return type if T is never used in an argument type. Warning about this would probably be a reasonable thing to do, but I agree with @ilevkivskyi -- it's not necessary for correctness. This issue can be left open, but this is a pretty low-priority thing unless it seems that users are confused by the current behavior.

@pkch
Copy link
Contributor

pkch commented Feb 24, 2017

IMO, identifying every possible non-sensical type annotation in mypy is unrealistic. So as with any apparently meaningless code, the question is how likely it is a symptom of a serious bug. If often, it could be reported as an error by mypy. If not too often, it might be something that could be caught by a linter such as pylint (if it becomes aware of mypy). This would be quite similar to how python interpreter accepts unused variables, but pylint complains about them.

@ilevkivskyi
Copy link
Member

While thinking about whether there are possible use cases for returning a plain type variable I discovered that mypy behaves strangely in case of a restricted type variable:

from typing import TypeVar

class A: ...
class B: ...
T = TypeVar('T', A, B)

def f() -> T:
    if bool():
        return A() # E: Incompatible return value type (got "A", expected "B")
    else:
        return B() # E: Incompatible return value type (got "B", expected "A")

Note how two error messages contradict each other.

@sixolet
Copy link
Collaborator

sixolet commented Apr 11, 2017

Regarding @JukkaL's example of returning List[T], I think it would be kind to tell the user their line needs a type annotation rather than returning List[uninhabited] if the relevant line is a = make_list() instead of a: List[int] = make_list()

@ilevkivskyi
Copy link
Member

Raising priority to normal since this appeared couple times again.

@ilevkivskyi
Copy link
Member

Note that mypy actually complains about an invalid return in such functions, see #7702. So I think it makes sense to instead give a dedicated error message explaining why it is bad, or better just with a links to some docs about this.

@Kurt-von-Laven
Copy link

Here is a use case I encountered for the more general notion of generic functions that don't accept generic parameters. Suppose a function calls a specific REST API with the given path and query and returns the result of json.loads(). Perhaps it adds some headers to deal with authentication and the hostname since these may be common to all requests. What should the return type of such a function be? It can certainly be Dict[str, Any] or whatever your preferred arbitrary JSON object type, but it also seems perfectly valid to require that the calling code provide a type hint (since the type can not be inferred) and instead return T, where T = TypeVar("T", bound=Dict[str, Any]). The calling code (or something higher up the stack) often knows the expected structure of the response to a request to a specific path and can hint a much narrower TypedDict and then call isinstance() once on the returned value.

@Kurt-von-Laven
Copy link

Hmm... I take it all back. You could just return Dict[str, Any] and call isinstance() on the whole thing anyways, and there's nothing lost really since the concrete type of the JSON response is only known at runtime anyways.

@JukkaL
Copy link
Collaborator

JukkaL commented Nov 19, 2022

This was fixed by #13166.

@JukkaL JukkaL closed this as completed Nov 19, 2022
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

6 participants