generics: specify TypeVar-default inference when parameter default matches by ashishpatel26 · Pull Request #2313 · python/typing · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions conformance/results/mypy/generics_defaults.toml
16 changes: 13 additions & 3 deletions conformance/results/results.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions conformance/results/ty/generics_defaults.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ conformant = "Partial"
notes = """
Does not forbid a `TypeVar` immediately following a `TypeVarTuple` in a parameter list from having a default.
Does not support `TypeVarTuple`.
Incorrectly rejects a valid generic function definition where a TypeVar's
default matches the default value of the corresponding parameter.
"""
errors_diff = """
Line 188: Expected 1 errors
Expand All @@ -13,6 +15,7 @@ Line 204: Unexpected errors ['generics_defaults.py:204:5: error[type-assertion-f
Line 205: Unexpected errors ['generics_defaults.py:205:5: error[type-assertion-failure] Type `@Todo` does not match asserted type `(int | float, bool, /) -> None`']
Line 207: Unexpected errors ['generics_defaults.py:207:5: error[type-assertion-failure] Type `@Todo` does not match asserted type `tuple[int, str]`']
Line 208: Unexpected errors ['generics_defaults.py:208:5: error[type-assertion-failure] Type `@Todo` does not match asserted type `(bytes, /) -> None`']
Line 239: Unexpected errors ['generics_defaults.py:239:19: error[invalid-parameter-default] Default value of type `None` is not assignable to annotated parameter type `S8@get`']
"""
output = """
generics_defaults.py:24:40: error[invalid-generic-class] Type parameter `T` without a default cannot follow earlier parameter `DefaultStrT` with a default
Expand All @@ -27,4 +30,5 @@ generics_defaults.py:204:5: error[type-assertion-failure] Type `@Todo` does not
generics_defaults.py:205:5: error[type-assertion-failure] Type `@Todo` does not match asserted type `(int | float, bool, /) -> None`
generics_defaults.py:207:5: error[type-assertion-failure] Type `@Todo` does not match asserted type `tuple[int, str]`
generics_defaults.py:208:5: error[type-assertion-failure] Type `@Todo` does not match asserted type `(bytes, /) -> None`
generics_defaults.py:239:19: error[invalid-parameter-default] Default value of type `None` is not assignable to annotated parameter type `S8@get`
"""
11 changes: 10 additions & 1 deletion conformance/results/zuban/generics_defaults.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
conformance_automated = "Pass"
conformant = "Partial"
notes = """
Incorrectly rejects a valid generic function definition where a TypeVar's
default matches the default value of the corresponding parameter.
"""
conformance_automated = "Fail"
errors_diff = """
Line 239: Unexpected errors ['generics_defaults.py:239: error: Incompatible default for parameter "default" (default has type "None", parameter has type "S8 = None") [assignment]']
"""
output = """
generics_defaults.py:24: error: "T" cannot appear after "DefaultStrT" in type parameter list because it has no default type [misc]
Expand All @@ -8,4 +14,7 @@ generics_defaults.py:152: error: TypeVar default must be a subtype of the bound
generics_defaults.py:159: error: TypeVar default must be one of the constraint types [misc]
generics_defaults.py:177: error: Expression is of type "int", not "Any" [misc]
generics_defaults.py:188: error: TypeVar defaults are ambiguous after a TypeVarTuple [misc]
generics_defaults.py:239: error: Incompatible default for parameter "default" (default has type "None", parameter has type "S8 = None") [assignment]
generics_defaults.py:239: note: PEP 484 prohibits implicit Optional. Accordingly, mypy has changed its default to no_implicit_optional=True
generics_defaults.py:239: note: Use https://github.com/hauntsaninja/no_implicit_optional to automatically upgrade your codebase
"""
24 changes: 24 additions & 0 deletions conformance/tests/generics_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,27 @@ def meth(self, /) -> Self:
foo7 = Foo7()
assert_type(Foo7.meth(foo7), Foo7[int])
assert_type(Foo7().attr, int)


# > When a type parameter ``S`` with default ``D`` is used as the declared type
# > of exactly one parameter ``p`` and that parameter also has a default
# > argument value whose type is assignable to ``D``:
# > 1. The function definition is valid.
# > 2. A call that omits ``p`` should be type-checked as if ``p`` were passed
# > its default value. Type checkers must infer ``S = D`` in this case.

T8 = TypeVar("T8")
S8 = TypeVar("S8", default=None)


class Getter(Generic[T8]):
def get(self, default: S8 = None) -> T8 | S8: # OK
raise NotImplementedError


class GetterStr(Getter[str]): ...


getter = GetterStr()
assert_type(getter.get(), str | None) # S8 = None (omitted → default value)
assert_type(getter.get(None), str | None) # S8 = None (explicit)
31 changes: 27 additions & 4 deletions docs/spec/generics.rst