[ty]: special-case comparisons of Generator prior to Python 3.13#23386
[ty]: special-case comparisons of Generator prior to Python 3.13#23386oconnor663 merged 2 commits intomainfrom
Generator prior to Python 3.13#23386Conversation
Typing conformance results regressed ❌The percentage of diagnostics emitted that were expected errors decreased from 84.96% to 84.87%. The percentage of expected errors that received a diagnostic held steady at 75.14%. Summary
False positives addedDetails
|
Memory usage reportSummary
Significant changesClick to expand detailed breakdownsphinx
flake8
trio
prefect
|
|
|
(I need to look through these conformance/ecosystem results tomorrow. Putting this in draft until then.) |
|
| Lint rule | Added | Removed | Changed |
|---|---|---|---|
invalid-return-type |
13 | 0 | 2 |
invalid-argument-type |
9 | 1 | 4 |
unresolved-attribute |
5 | 2 | 0 |
invalid-assignment |
2 | 1 | 0 |
redundant-cast |
0 | 3 | 0 |
call-non-callable |
2 | 0 | 0 |
unused-type-ignore-comment |
1 | 1 | 0 |
| Total | 32 | 8 | 6 |
bb6404e to
fe74b41
Compare
|
Ok, after a long discussion with @carljm, I now understand the "false positive" above. If you run |
|
We had a question about whether #23428 ( |
|
(meant to take this out of Draft yesterday but just forgot to hit the button) |
| // methods (except `__iter__`, but that returns the self type recursively, which gets | ||
| // normalized to `Any`). We don't want generators with different return types to be | ||
| // assignable to each other. In this case we use the result of the nominal check above. | ||
| if Program::get(db).python_version(db) < PythonVersion::PY313 |
There was a problem hiding this comment.
I seem to remember that these Program::get(db).python_version(db) are somewhat expensive? If so, should we consider testing the Python version further down in this && chain?
I could easily be wrong here with that assumption though.
There was a problem hiding this comment.
they can be surprisingly expensive I think, yes
| ## The `Generator` protocol's `_ReturnT_co` needs special casing prior to Python 3.13 | ||
|
|
||
| Prior to Python 3.13, the `_ReturnT_co` type parameter doesn't appear in any `Generator` methods in | ||
| typeshed (except `__iter__`, which returns the self type recursively and gets normalized to `Any`). |
There was a problem hiding this comment.
Sorry I'm slow getting to this -- would prefer to document a bit more thoroughly our understanding here. The need for special-casing here is not just due to Any normalization as an implementation limitation (which is what I thought when you first identified normalization as the culprit). It is in fact correct (given the information present in typeshed) to consider two pre-3.13 Generator types with different _ReturnT_co as equivalent types -- comparing their __iter__ return type is just a recursive comparison, and we should inductively assume "true" there. So our protocol comparison is working correctly here -- this really is a necessary special case due to the "invisible" use of _ReturnT_co also in StopIteration (which is not visible in the type annotations of Generator.)
| // methods (except `__iter__`, but that returns the self type recursively, which gets | ||
| // normalized to `Any`). We don't want generators with different return types to be |
There was a problem hiding this comment.
Similarly here -- I feel like "which gets normalized to Any" suggests that it's just an implementation limitation (because Any normalization of recursive types is not always correct, and is a current implementation limitation), but in this case even if we didn't normalize to Any we would still correctly consider the types equivalent
Fixes astral-sh/ty#2426.
Prior to Python 3.13, the
_ReturnT_cotype parameter doesn't appear in anyGeneratormethods intypeshed (except
__iter__, which returns the self type recursively and gets normalized toAny).We need to special-case
Generatorso that specializations that differ in their return type don'tappear equivalent.