-
-
Notifications
You must be signed in to change notification settings - Fork 3k
mypy 1.16 hates PEP 747 typing.TypeForm
and thus @beartype
#19227
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
Comments
This is actually due to a typeshed change (mypy has never had any support for TypeForm and hasn't changed anything, nor is the PEP accepted so there isn't really any guarantee of behaviour to the extent of blocking releases) The copy of typeshed in mypy 1.15 simply didn't have TypeForm in its stubs. So when you did The copy of typeshed in mypy 1.16 added a TypeForm symbol:
So now mypy resolves the symbol to something (so you no longer get the attribute not found), but then it doesn't treat it specially. |
If David's PR isn't ready yet, I'd be happy to take a PR that special cases |
If you need an emergency workaround, or this affects your users, the following might work:
|
typing.TypeForm
and thus @beartypetyping.TypeForm
and thus @beartype
...gulp. Indeed, I see that PEP 747 has now been deferred to Python 3.15. That's probably the right move. Without mypy support in time for Python 3.14, it's not quite ready for "prime-time." Thanks so much for the clarification, @hauntsaninja. You're breathtaking. Also, what a username. I should admit that I'm doing this entirely at the behest of @davidfstr, who authored a @beartype PR annotating the entire @beartype codebase with PEP 747 That PR no longer seems like a reasonable idea. @beartype users definitely expect @beartype to pass Today I learned about |
This commit is the first (and hopefully last) in a commit chain permonantly restoring `mypy`-based static type-checking when run both locally under either `pytest` or `tox` *or* remotely under GitHub Actions-hosted continuous integration (CI) workflows. Specifically, this commit resolves upstream issue python/mypy#19227 by employing a clever hack perpetrated by the glorious mypy maintainer @hauntsaninja (Shantanu). (*Untimely temerity, ultimately!*)
This just means that the
Also a TIL for me, which is funny because I work on mypy!
And I'll try to remember to temporarily undo the hack when I retest mypy's TypeForm support against beartype again. 😉 @leycec I'm glad you got unblocked! Now I just need to find enough bandwidth to finish getting TypeForm support into mypy development branch. Probably a few weeks out. |
Oh, ho. Indeed, this is sweet music. PEP 747 currently reads "Status: Draft." I wasn't quite sure whether that meant that If the latter, this is good. I now understand that @beartype should support this monolith of human ingenuity as soon as feasible. Indeed, users now want @beartype to type-check ...or maybe not.Now that I read PEP 747 closer, it looks like the static type-checking decision problem of "Does the user-defined type hint ok1: TypeForm[str | None] = str | None # OK
ok2: TypeForm[str | None] = str # OK
ok3: TypeForm[str | None] = None # OK
ok4: TypeForm[str | None] = Literal[None] # OK
ok5: TypeForm[str | None] = Optional[str] # OK
ok6: TypeForm[str | None] = "str | None" # OK
ok7: TypeForm[str | None] = Any # OK ...is equivalent to the runtime type-checking decision problem of subhint relations as implemented by the >>> from beartype.door import is_subhint
>>> from typing import Any, Literal, Optional
>>> is_subhint(str | None, str | None)
True # OK
>>> is_subhint(str, str | None)
True # OK
>>> is_subhint(None, str | None)
True # OK
>>> is_subhint(Literal[None], str | None)
True # OK
>>> is_subhint(Optional[str], str | None)
True # OK
>>> is_subhint("str | None", str | None)
beartype.roar.BeartypeDoorNonpepException: Type hint 'str | None' currently
unsupported by "beartype.door.TypeHint". # OHNO
>>> is_subhint(Any, str | None)
False # OHNOES SO. CLOSE. Man. @beartype almost got to the finish line. As for Consider set theory. Specifically, consider a type hint to be uniquely described by the set of all objects satisfied by that hint. Under this conception:
Clearly, the set describing # Any type hint "foo" should be a subhint of "Any".
>>> is_subhint(foo, Any)
True # <-- this should always be the case
# "Any" should *NEVER* be a subhint of any type hint "foo".
>>> is_subhint(Any, foo)
False # <-- this should always be the case, too After all, how could any type hint describe a set larger than I stroke my chin thoughtfully and squint my eyes.
|
Stringified type hints are just plain hard.
|
I'll also note that the term "subhint" is not defined in the typing spec, and I don't think I've seen it used before. The term subtype is defined by the typing spec, but it applies only to fully-static types. As soon as an |
Indeed, @beartype invented the subhint relation. It's a well-defined partial ordering over the universe of type hints. This partial ordering enables type hints to be richly compared via the standard comparison operators, which then enables developers to implement novel typing algorithms by simply "porting" existing algorithms that require a partial ordering like topology sort. Sorting type hints then enables developers to implement even more novel typing algorithms on top of efficiently sorted collections of type hints. Pragmatically, the subhint relation is also the theoretical basis for I'm confident that the subhint relation is, in fact, the theoretical basis for PEP 747. I was unaware that Repairing tl;dr:
Truly, there is a first time for everything. Today, I learned how to make tofu at home. The texture was disgusting but the taste was exquisite. Someday, I will make tofu that is worthy of the name of Japan. 😅 |
Uh oh!
There was an error while loading. Please reload this page.
Bug Report
Mypy began publicly breaking PEP 747 support in @beartype four days ago. Unfortunately for both mypy and @beartype, @beartype leverages PEP 747 literally everywhere. Hundreds upon hundreds of lines of labyrinthine spaghetti code across the @beartype codebase have already been annotated with
typing_extensions.TypeForm
. Breaking PEP 747 thus breaks the entirety of @beartype (with respect to mypy-driven static type-checking, anyway).Mypy now emits 169 false positives against the @beartype codebase – all duplicates on the same tired theme of:
Moreover,
mypy_primer
tests against @beartype! Since GitHub Actions-based continuous integration workflows on this repository leveragemypy_primer
, PRs on this repo like #18690 are now littered with red badness suggestive of critical failure. Red means bad.Interestingly,
pyright
has shipped experimental support for PEP 747 via:When enabled in this way,
pyright
accepts @beartype without complaint. Pretty surepyright
has supported PEP 747 for... wait. Has it really been over a year now? Oh, Gods. No! My time just up and dilated again. 😅I'm at a bit of a loss as to what to do. The false positives are pretty intense. I should probably pin the @beartype test suite to
mypy < 1.16.0
for the time being. Instead, I've taken the easy way out. I've temporarily disabled mypy integration in our test suite. That doesn't particularly helpmypy_primer
, but at least @beartype CI now passes. And isn't that what truly matters?This issue fills me with deep regret. I've come to love and adore mypy. Mypy keeps @beartype honest. Sadly, @beartype no longer knows how to satisfy mypy's unbidden desires.
Advice Nobody Wanted or Asked For
This is the section where @leycec says stuff nobody wants to hear.
pyright
has the right approach here. I note that mypy does have a comparable--enable-incomplete-feature
command-line option. That option doesn't appear to support PEP 747 at the moment, though. That's a shame. @davidfstr's preliminary PR supporting PEP 747 at #18690 cleverly adds--enable-incomplete-feature=TypeForm
, which would then have allowed @beartype to continue satisfying mypy.Tragically, mypy 1.16.0 shipped without that PR. #18690 may not have been perfect, but the perfect is the enemy of the good. That PR was probably good enough for
--enable-incomplete-feature=TypeForm
. Nobody except @beartype was going to enable that option. Instead, the current approach of rejecting PEP 747 altogether is the enemy of the good. Something is better than nothing.mypy 1.16.0 leaves @beartype with nothing. This is why bears cry. 😿 <-- pretend this is a bear
To Reproduce
mypy_primer
. Pretty sure that's always the answer.Allow me to now publicly thank and congratulate @hauntsaninja both for
mypy_primer
and for including @beartype inmypy_primer
. Python Gods above! What a wonderful regression tester that is. 🥰Expected Behavior
Mypy used to just superficially ignore PEP 747. While (of course) non-ideal, that was still a lot better than blowing hot chunks everywhere. If
--enable-incomplete-feature=TypeForm
support isn't quite ready yet, perhaps mypy could simply revert back to superficially ignoring PEP 747?Sometimes, the old times really were the good times.
Actual Behavior
169 errors on the theme of:
This depresses me. I'm getting teary-eyed just thinking about all this.
Your Environment
mypy.ini
(and other config files):People Interested
I now summon everyone who cares about PEP 747 and then some! My home boys @davidfstr, @JelleZijlstra, @JukkaL, and @erictraut are the main gotos. Please help solve @beartype's many problems.
The text was updated successfully, but these errors were encountered: