-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[ty] Improve debuggability of protocol types #19662
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-08-01 14:10:34.764459119 +0000
+++ new-output.txt 2025-08-01 14:10:34.827459530 +0000
@@ -368,8 +368,8 @@
generics_basic.py:34:12: error[unsupported-operator] Operator `+` is unsupported between objects of type `AnyStr` and `AnyStr`
generics_basic.py:139:5: error[type-assertion-failure] Argument does not have asserted type `int`
generics_basic.py:140:5: error[type-assertion-failure] Argument does not have asserted type `int`
-generics_basic.py:157:5: error[invalid-argument-type] Method `__getitem__` of type `bound method MyMap1[str, int].__getitem__(key: str, /) -> int` cannot be called with key of type `Literal[0]` on object of type `MyMap1[str, int]`
-generics_basic.py:158:5: error[invalid-argument-type] Method `__getitem__` of type `bound method MyMap2[int, str].__getitem__(key: str, /) -> int` cannot be called with key of type `Literal[0]` on object of type `MyMap2[int, str]`
+generics_basic.py:157:5: error[call-non-callable] Method `__getitem__` of type `bound method MyMap1[str, int].__getitem__(key: str, /) -> int` is not callable on object of type `MyMap1[str, int]`
+generics_basic.py:158:5: error[call-non-callable] Method `__getitem__` of type `bound method MyMap2[int, str].__getitem__(key: str, /) -> int` is not callable on object of type `MyMap2[int, str]`
generics_basic.py:162:12: error[invalid-argument-type] `<class 'int'>` is not a valid argument to `Generic`
generics_basic.py:163:12: error[invalid-argument-type] `<class 'int'>` is not a valid argument to `Protocol`
generics_basic.py:171:1: error[invalid-generic-class] `Generic` base class must include all type variables used in other base classes
@@ -704,7 +704,6 @@
namedtuples_usage.py:30:1: error[type-assertion-failure] Argument does not have asserted type `str`
namedtuples_usage.py:31:1: error[type-assertion-failure] Argument does not have asserted type `int`
namedtuples_usage.py:32:1: error[type-assertion-failure] Argument does not have asserted type `int`
-namedtuples_usage.py:41:1: error[invalid-assignment] Cannot assign to object of type `Point` with no `__setitem__` method
namedtuples_usage.py:49:1: error[type-assertion-failure] Argument does not have asserted type `int`
namedtuples_usage.py:50:1: error[type-assertion-failure] Argument does not have asserted type `str`
narrowing_typeguard.py:17:9: error[type-assertion-failure] Argument does not have asserted type `tuple[str, str]`
@@ -725,7 +724,7 @@
narrowing_typeis.py:96:9: error[type-assertion-failure] Argument does not have asserted type `B`
narrowing_typeis.py:132:20: error[invalid-argument-type] Argument to function `takes_callable_str` is incorrect: Expected `(object, /) -> str`, found `def simple_typeguard(val: object) -> TypeIs[int]`
narrowing_typeis.py:191:18: error[invalid-argument-type] Argument to function `takes_int_typeis` is incorrect: Expected `(object, /) -> TypeIs[int]`, found `def bool_typeis(val: object) -> TypeIs[bool]`
-overloads_basic.py:39:1: error[invalid-argument-type] Method `__getitem__` of type `Overload[(__i: int) -> int, (__s: slice[Any, Any, Any]) -> bytes]` cannot be called with key of type `Literal[""]` on object of type `Bytes`
+overloads_basic.py:39:1: error[call-non-callable] Method `__getitem__` of type `Overload[(__i: int) -> int, (__s: slice[Any, Any, Any]) -> bytes]` is not callable on object of type `Bytes`
overloads_definitions.py:20:5: error[invalid-overload] Overloaded function `func1` requires at least two overloads
overloads_definitions.py:33:5: error[invalid-overload] Overloaded non-stub function `func2` must have an implementation
overloads_definitions.py:63:9: error[invalid-overload] Overloaded non-stub function `not_abstract` must have an implementation
@@ -889,4 +888,4 @@
tuples_type_form.py:36:1: error[invalid-assignment] Object of type `tuple[Literal[1], Literal[2], Literal[3], Literal[""]]` is not assignable to `tuple[int, ...]`
typeddicts_operations.py:60:1: error[type-assertion-failure] Argument does not have asserted type `str | None`
typeddicts_type_consistency.py:101:1: error[invalid-assignment] Object of type `Unknown | None` is not assignable to `str`
-Found 890 diagnostics
+Found 889 diagnostics |
|
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. I can see how this can be very helpful. I like that you are constructing the "debug" representation manually instead of relying on {:?}.
Still, I'm a little worried that we should maybe be splitting ty_extensions into "public" and "definitely internal" parts (see my comment here), because this definitely belongs in the latter category. But this is also true for other functions in ty_extensions, so not only related to this PR.
|
Thinking a bit more about this... if we do want to eventually add something like |
I think this is a fair concern, although I'm not sure if we should indeed communicate that anything about Either way. I guess the pythonic solution to this problem could be to mark the "more internal" functions as private: |
I think I'd prefer |
Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
|
Yes, it would be nice to expose some parts of
So I agree that we have a number of internal functions in |
It could be. But I think I'd want more information from |
* main: (39 commits) [ty] Initial test suite for `TypedDict` (#19686) [ty] Improve debuggability of protocol types (#19662) [ty] Simplify lifetime requirements for `PySlice` trait (#19687) [ty] Improve `isinstance()` truthiness analysis for generic types (#19668) [`refurb`] Make example error out-of-the-box (`FURB164`) (#19673) Fix link: unused_import.rs (#19648) [ty] Remove `Specialization::display` (full) (#19682) [ty] Remove `KnownModule::is_enum` (#19681) [ty] Support `__setitem__` and improve `__getitem__` related diagnostics (#19578) [ty] Sync vendored typeshed stubs (#19676) [`flake8-use-pathlib`] Expand `PTH201` to check all `PurePath` subclasses (#19440) [`refurb`] Make example error out-of-the-box (`FURB180`) (#19672) [`pyupgrade`] Prevent infinite loop with `I002` (`UP010`, `UP035`) (#19413) [ty] Improve the `Display` for generic `type[]` types (#19667) [ty] Refactor `TypeInferenceBuilder::infer_subscript_expression_types` (#19658) Fix tests on 32-bit architectures (#19652) [ty] Move `pandas-stubs` to bad.txt (#19659) [ty] Remove special casing for string-literal-in-tuple `__contains__` (#19642) Update pre-commit's `ruff` id (#19654) Update salsa (#19449) ...
Summary
As our protocol implementation continues to progress, it's increasingly important for debuggability to know what types are being captured for each member in a protocol's interface and what kind of a member each member is treated as. One way of obtaining this information is to use a
dbg!()call, but this means that you have to recompile ty (which takes a while), and also prints out a lot of detail that you don't need, making it hard to see the information that you do need.This PR adds a
ty_extensions.reveal_protocol_interfacefunction to solve this issue. If you pass a protocol to this function, ty emits a diagnostic that prints a formatted representation of the underlying interface.Test Plan
mdtests