[ty] Remove special casing for string-literal-in-tuple __contains__
#19642
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
This PR rips out the special casing for
<string literal> in <tuple>that was added in #18251.We were unsure at the time whether it was worth it to add this special casing, since it added complexity to our type inference machinery but didn't appear to remove any false positives or false negatives when checking user code (the mypy_primer report on that PR is an empty diff). At the time, we thought that the benefits outweighed the costs, however, since the added complexity wasn't very large, and it allowed us to test our new
ide_support::all_membersAPI in an elegant way in mdtests.I think we should reconsider this, however. It's fundamentally inconsistent behaviour for us to infer the expression
"foo" in ("foo",)as evaluating toLiteral[True]if we do not also infer the expression"foo" in SingleElementTupleSubclass(("foo",))as evaluating toLiteral[True](whereSingleElementTupleSubclassis considered by ty to be a subtype oftuple[Literal["foo"]]). We therefore have three options:__contains__overloads, similar to what was done for__getitem__in [ty] Synthesize precise__getitem__overloads for tuple subclasses #19493TypeInferenceBuilder::infer_binary_type_comparisonso that it also applies to tuple subclasses(1) and (2) are both viable paths here, but they would both add significant complexity to the implementation of the special case, and I'm not sure it's worth doing that without evidence of patterns in user code that this enables us to understand better. Additionally, if we went with option (2) we would have to forbid users from overriding
__contains__on tuple subclasses in order for us to make tuple subclasses sound. If we went with option (1) we would not have to forbid overrides of__contains__on user subclasses oftuple; but in practice, we'd make it very difficult for users to do so after our implementation of the Liskov Substitution Principle has landed. Either forbidding__contains__overrides outright, or de-facto forbidding them by requiring the override to duplicate complicated overloads from the base class, feels overly pedantic to me. This PR therefore goes with option (3): ripping out the special casing.The remaining question was, therefore: what to do about our
all_members.mdtest? The solution this PR proposes is to adapt that test by adding a newty_extensions.has_member()function that returnsLiteral[True]if ouride_support_all_membersroutine considers an object to have a particular member, andLiteral[False]if not.Test Plan
Mdtests