-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[ty] Improve the display of various special-form types #21775
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
Conversation
Diagnostic diff on typing conformance testsChanges were detected when running ty on typing conformance tests--- old-output.txt 2025-12-03 21:16:41.719454171 +0000
+++ new-output.txt 2025-12-03 21:16:45.404479261 +0000
@@ -502,16 +502,16 @@
generics_scoping.py:75:11: error[invalid-generic-class] Generic class `Bad` must not reference type variables bound in an enclosing scope: `T` referenced in class definition here
generics_self_advanced.py:11:24: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `Self@prop1`
generics_self_advanced.py:28:25: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `Self@method1`
-generics_self_advanced.py:36:9: error[type-assertion-failure] Type `list[Self@method2]` does not match asserted type `list[typing.Self]`
-generics_self_advanced.py:37:9: error[type-assertion-failure] Type `Self@method2` does not match asserted type `typing.Self`
+generics_self_advanced.py:36:9: error[type-assertion-failure] Type `list[Self@method2]` does not match asserted type `list[<special form 'typing.Self'>]`
+generics_self_advanced.py:37:9: error[type-assertion-failure] Type `Self@method2` does not match asserted type `<special form 'typing.Self'>`
generics_self_advanced.py:38:9: error[type-assertion-failure] Type `Self@method2` does not match asserted type `Unknown`
generics_self_advanced.py:42:9: error[type-assertion-failure] Type `type[Self@method3]` does not match asserted type `Unknown`
generics_self_advanced.py:43:9: error[type-assertion-failure] Type `list[Self@method3]` does not match asserted type `Unknown`
generics_self_advanced.py:44:9: error[type-assertion-failure] Type `Self@method3` does not match asserted type `Unknown`
generics_self_advanced.py:45:9: error[type-assertion-failure] Type `Self@method3` does not match asserted type `Unknown`
-generics_self_attributes.py:26:33: error[invalid-argument-type] Argument is incorrect: Expected `typing.Self | None`, found `LinkedList[int]`
-generics_self_attributes.py:29:5: error[invalid-assignment] Object of type `OrdinalLinkedList` is not assignable to attribute `next` of type `typing.Self | None`
-generics_self_attributes.py:32:5: error[invalid-assignment] Object of type `LinkedList[int]` is not assignable to attribute `next` of type `typing.Self | None`
+generics_self_attributes.py:26:33: error[invalid-argument-type] Argument is incorrect: Expected `<special form 'typing.Self'> | None`, found `LinkedList[int]`
+generics_self_attributes.py:29:5: error[invalid-assignment] Object of type `OrdinalLinkedList` is not assignable to attribute `next` of type `<special form 'typing.Self'> | None`
+generics_self_attributes.py:32:5: error[invalid-assignment] Object of type `LinkedList[int]` is not assignable to attribute `next` of type `<special form 'typing.Self'> | None`
generics_self_basic.py:20:16: error[invalid-return-type] Return type does not match returned value: expected `Self@method2`, found `Shape`
generics_self_basic.py:27:9: error[type-assertion-failure] Type `type[Self@from_config]` does not match asserted type `Unknown`
generics_self_basic.py:33:16: error[invalid-return-type] Return type does not match returned value: expected `Self@cls_method2`, found `Shape`
@@ -521,16 +521,16 @@
generics_self_basic.py:67:26: error[invalid-type-form] Special form `typing.Self` expected no type parameter
generics_self_protocols.py:61:19: error[invalid-argument-type] Argument to function `accepts_shape` is incorrect: Expected `ShapeProtocol`, found `BadReturnType`
generics_self_protocols.py:64:19: error[invalid-argument-type] Argument to function `accepts_shape` is incorrect: Expected `ShapeProtocol`, found `ReturnDifferentClass`
-generics_self_usage.py:50:34: error[invalid-assignment] Object of type `def foo(self) -> int` is not assignable to `(typing.Self, /) -> int`
-generics_self_usage.py:73:14: error[invalid-type-form] Variable of type `typing.Self` is not allowed in a type expression
-generics_self_usage.py:73:23: error[invalid-type-form] Variable of type `typing.Self` is not allowed in a type expression
-generics_self_usage.py:76:6: error[invalid-type-form] Variable of type `typing.Self` is not allowed in a type expression
+generics_self_usage.py:50:34: error[invalid-assignment] Object of type `def foo(self) -> int` is not assignable to `(<special form 'typing.Self'>, /) -> int`
+generics_self_usage.py:73:14: error[invalid-type-form] Variable of type `<special form 'typing.Self'>` is not allowed in a type expression
+generics_self_usage.py:73:23: error[invalid-type-form] Variable of type `<special form 'typing.Self'>` is not allowed in a type expression
+generics_self_usage.py:76:6: error[invalid-type-form] Variable of type `<special form 'typing.Self'>` is not allowed in a type expression
generics_self_usage.py:82:54: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `Self@has_existing_self_annotation`
generics_self_usage.py:86:16: error[invalid-return-type] Return type does not match returned value: expected `Self@return_concrete_type`, found `Foo3`
generics_self_usage.py:98:22: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `T@Bar`
-generics_self_usage.py:101:15: error[invalid-type-form] Variable of type `typing.Self` is not allowed in a type expression
-generics_self_usage.py:103:12: error[invalid-base] Invalid class base with type `typing.Self`
-generics_self_usage.py:106:30: error[invalid-type-form] Variable of type `typing.Self` is not allowed in a type expression
+generics_self_usage.py:101:15: error[invalid-type-form] Variable of type `<special form 'typing.Self'>` is not allowed in a type expression
+generics_self_usage.py:103:12: error[invalid-base] Invalid class base with type `<special form 'typing.Self'>`
+generics_self_usage.py:106:30: error[invalid-type-form] Variable of type `<special form 'typing.Self'>` is not allowed in a type expression
generics_self_usage.py:111:19: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `Self@make`
generics_self_usage.py:116:40: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `Self@return_parameter`
generics_self_usage.py:121:37: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `Self@__new__`
@@ -861,11 +861,11 @@
qualifiers_annotated.py:48:18: error[invalid-type-form] Boolean operations are not allowed in type expressions
qualifiers_annotated.py:49:18: error[fstring-type-annotation] Type expressions cannot use f-strings
qualifiers_annotated.py:59:8: error[invalid-type-form] Special form `typing.Annotated` expected at least 2 arguments (one type and at least one metadata element)
-qualifiers_annotated.py:71:24: error[invalid-assignment] Object of type `<typing.Annotated special form>` is not assignable to `type[Any]`
-qualifiers_annotated.py:72:24: error[invalid-assignment] Object of type `<typing.Annotated special form>` is not assignable to `type[Any]`
-qualifiers_annotated.py:79:7: error[invalid-argument-type] Argument to function `func4` is incorrect: Expected `type[Unknown]`, found `<typing.Annotated special form>`
-qualifiers_annotated.py:80:7: error[invalid-argument-type] Argument to function `func4` is incorrect: Expected `type[Unknown]`, found `<typing.Annotated special form>`
-qualifiers_annotated.py:86:1: error[call-non-callable] Object of type `typing.Annotated` is not callable
+qualifiers_annotated.py:71:24: error[invalid-assignment] Object of type `<special form 'typing.Annotated[int, <metadata>]'>` is not assignable to `type[Any]`
+qualifiers_annotated.py:72:24: error[invalid-assignment] Object of type `<special form 'typing.Annotated[int, <metadata>]'>` is not assignable to `type[Any]`
+qualifiers_annotated.py:79:7: error[invalid-argument-type] Argument to function `func4` is incorrect: Expected `type[Unknown]`, found `<special form 'typing.Annotated[str, <metadata>]'>`
+qualifiers_annotated.py:80:7: error[invalid-argument-type] Argument to function `func4` is incorrect: Expected `type[Unknown]`, found `<special form 'typing.Annotated[int, <metadata>]'>`
+qualifiers_annotated.py:86:1: error[call-non-callable] Object of type `<special form 'typing.Annotated'>` is not callable
qualifiers_annotated.py:87:1: error[call-non-callable] Object of type `GenericAlias` is not callable
qualifiers_annotated.py:88:1: error[call-non-callable] Object of type `GenericAlias` is not callable
qualifiers_final_annotation.py:18:7: error[invalid-type-form] Type qualifier `typing.Final` expected exactly 1 argument, got 2
@@ -902,7 +902,7 @@
specialtypes_none.py:32:1: error[missing-argument] No argument provided for required parameter `value` of function `__eq__`
specialtypes_promotions.py:13:5: warning[possibly-missing-attribute] Attribute `numerator` may be missing on object of type `int | float`
specialtypes_type.py:56:7: error[invalid-argument-type] Argument to function `func4` is incorrect: Expected `type[BasicUser] | type[ProUser]`, found `<class 'TeamUser'>`
-specialtypes_type.py:70:7: error[invalid-argument-type] Argument to function `func5` is incorrect: Expected `type[Unknown]`, found `typing.Callable`
+specialtypes_type.py:70:7: error[invalid-argument-type] Argument to function `func5` is incorrect: Expected `type[Unknown]`, found `<special form 'typing.Callable'>`
specialtypes_type.py:76:17: error[invalid-type-form] type[...] must have exactly one type argument
specialtypes_type.py:84:5: error[type-assertion-failure] Type `type[Any]` does not match asserted type `type`
specialtypes_type.py:99:17: error[unresolved-attribute] Object of type `type` has no attribute `unknown`
|
|
0ab1c4d to
4fc1968
Compare
|
(I also made some changes to make clear that the objects |
sharkdp
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, thank you!
Do we need to worry about the fact that things like <special form typing.Never> are not valid Python syntax? Do we show the display types syntax-highlighted somewhere?
crates/ty_python_semantic/resources/mdtest/generics/pep695/aliases.md
Outdated
Show resolved
Hide resolved
| reveal_type(IntOrStr) # revealed: <types.UnionType special form (`int | str`)> | ||
| reveal_type(IntOrStrOrBytes1) # revealed: <types.UnionType special form (`int | str | bytes`)> | ||
| reveal_type(IntOrStrOrBytes2) # revealed: <types.UnionType special form (`int | str | bytes`)> | ||
| reveal_type(IntOrStrOrBytes3) # revealed: <types.UnionType special form (`int | str | bytes`)> | ||
| reveal_type(IntOrStrOrBytes4) # revealed: <types.UnionType special form (`int | str | bytes`)> | ||
| reveal_type(IntOrStrOrBytes5) # revealed: <types.UnionType special form (`int | str | bytes`)> | ||
| reveal_type(IntOrStrOrBytes6) # revealed: <types.UnionType special form (`int | str | bytes`)> | ||
| reveal_type(BytesOrIntOrStr) # revealed: <types.UnionType special form (`bytes | int | str`)> | ||
| reveal_type(IntOrNone) # revealed: <types.UnionType special form (`int | None`)> | ||
| reveal_type(NoneOrInt) # revealed: <types.UnionType special form (`None | int`)> | ||
| reveal_type(IntOrStrOrNone) # revealed: <types.UnionType special form (`int | str | None`)> | ||
| reveal_type(NoneOrIntOrStr) # revealed: <types.UnionType special form (`None | int | str`)> | ||
| reveal_type(IntOrAny) # revealed: <types.UnionType special form (`int | Any`)> | ||
| reveal_type(AnyOrInt) # revealed: <types.UnionType special form (`Any | int`)> | ||
| reveal_type(NoneOrAny) # revealed: <types.UnionType special form (`None | Any`)> | ||
| reveal_type(AnyOrNone) # revealed: <types.UnionType special form (`Any | None`)> | ||
| reveal_type(NeverOrAny) # revealed: <types.UnionType special form (`Any`)> | ||
| reveal_type(AnyOrNever) # revealed: <types.UnionType special form (`Any`)> | ||
| reveal_type(UnknownOrInt) # revealed: <types.UnionType special form (`Unknown | int`)> | ||
| reveal_type(IntOrUnknown) # revealed: <types.UnionType special form (`int | Unknown`)> | ||
| reveal_type(StrOrZero) # revealed: <types.UnionType special form (`str | Literal[0]`)> | ||
| reveal_type(ZeroOrStr) # revealed: <types.UnionType special form (`Literal[0] | str`)> | ||
| reveal_type(IntOrLiteralString) # revealed: <types.UnionType special form (`int | LiteralString`)> | ||
| reveal_type(LiteralStringOrInt) # revealed: <types.UnionType special form (`LiteralString | int`)> | ||
| reveal_type(NoneOrTuple) # revealed: <types.UnionType special form (`None | tuple[int, str]`)> | ||
| reveal_type(TupleOrNone) # revealed: <types.UnionType special form (`tuple[int, str] | None`)> | ||
| reveal_type(IntOrAnnotated) # revealed: <types.UnionType special form (`int | str`)> | ||
| reveal_type(AnnotatedOrInt) # revealed: <types.UnionType special form (`str | int`)> | ||
| reveal_type(IntOrOptional) # revealed: <types.UnionType special form (`int | str | None`)> | ||
| reveal_type(OptionalOrInt) # revealed: <types.UnionType special form (`str | None | int`)> | ||
| reveal_type(IntOrTypeOfStr) # revealed: <types.UnionType special form (`int | type[str]`)> | ||
| reveal_type(TypeOfStrOrInt) # revealed: <types.UnionType special form (`type[str] | int`)> | ||
| reveal_type(IntOrCallable) # revealed: <types.UnionType special form (`int | ((str, /) -> bytes)`)> | ||
| reveal_type(CallableOrInt) # revealed: <types.UnionType special form (`((str, /) -> bytes) | int`)> | ||
| reveal_type(TypeVarOrInt) # revealed: <types.UnionType special form (`T@TypeVarOrInt | int`)> | ||
| reveal_type(IntOrTypeVar) # revealed: <types.UnionType special form (`int | T@IntOrTypeVar`)> | ||
| reveal_type(TypeVarOrNone) # revealed: <types.UnionType special form (`T@TypeVarOrNone | None`)> | ||
| reveal_type(NoneOrTypeVar) # revealed: <types.UnionType special form (`None | T@NoneOrTypeVar`)> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about doing this. I like it personally, but it goes a bit against the goal you're trying to achieve here? When users see the type of IntOrStr somewhere in a diagnostic, there's a high likelihood that they've done something wrong (?), so maybe displaying <special form types.UnionType> makes that even clearer, given that int | str is probably what they really wanted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's true, but i worry that that display implies that all UnionType value expressions inhabit the same type? When in fact, in our model, each has its own bespoke type. I would say part of the aim of this PR is to more clearly differentiate different types by giving them clearly different displays!
I've done |
7552a50 to
31f5f9e
Compare
Summary
The type defined by
typing.Any, and the type inhabited bytyping.Anyare two very different types in our model. However, we currently display them almost exactly the same way! The former is displayed asAnyin our diagnostics, and the latter is displayed astyping.Any, but we currently expect users to somehow psychically divine that these refer to two different types. This, understandably, causes confusion (see astral-sh/ty#1744).This PR improves our type display for the various strange, highly special-cased singleton types that we have in our model for tracking special forms in value positions. It should now be much more obvious that symbols inhabit very different types to the types they define.
Test Plan
Mdtests