beartype / beartype

Unbearably fast near-real-time hybrid runtime-static type-checking in pure Python.
https://beartype.readthedocs.io
MIT License
2.69k stars 57 forks source link

[Feature Request] Support for PEP695 type aliases. #297

Open EtaoinWu opened 12 months ago

EtaoinWu commented 12 months ago

The latest release of beartype does not currently support typing.TypeAliasType, introduced in PEP 695 and released with 3.12, with the following syntax:

from beartype import beartype

Point1 = tuple[int, int]
type Point2 = tuple[int, int]
print(type(Point2)) # <class 'typing.TypeAliasType'>

def f(x: Point1):
  return x[0] + x[1]
def g(x: Point2):
  return x[0] + x[1]

beartype(f) # Success. The bear sleeps cozily.
beartype(g) # Error! The bear roars!
# beartype.roar.BeartypeDecorHintNonpepException: 
# Function testfile.g() parameter "x" type hint Point2 either 
# PEP-noncompliant or currently unsupported by @beartype.

I am not 100% sure I've understood the documented behavior on the lazy evaluation, but I think beartype can just read its .__value__ and get back the aliasee (in this case tuple[int, int]).

leycec commented 12 months ago

100%. Thanks so much for the gentle reminder, sparkly-eyed unicorn. You're absolutely right. As luck would have it, this is exactly what I'm working on at the moment. I just finalized official support for Python 3.12 two days ago. Now, I'm (in order):

  1. Refactoring our test suite for improved maintainability. Booooring.
  2. Banging hard on PEP 695. Exciting!

Thankfully, PEP 695 appears to be trivial for @beartype to support at runtime – exactly as you suggest. Let's do this, fam. :partying_face:

leycec commented 10 months ago

@EtaoinWu: Thanks so much for your formidable patience. You are a testament to the strength within the human spirit.

At long last, we reward your tenacity by actually doing something. There are three use cases here:

  1. Fun PEP 695 type aliases that contain neither forward references nor recursion. Commit 4c8921bba1f now fully handles this common case. :partying_face:
  2. Brutal PEP 695 type aliases that contain one or more forward references but no recursion. I haven't quite implemented support for this, but I promise your beautiful app this: @beartype 0.17.0 will support this no matter how many dangerous and ill-advised hacks I have to force down CPython's throat. Prepare yourself, CPython. :grimacing:
  3. Hellish PEP 695 type aliases that contain recursion. Hell is a place in typing. Who new? @beartype 0.17.0 will probably not support this – mostly because @beartype 0.17.0 is now weeks overdue and I'm sweating rainbow-flavored tears. That's as uncomfortable as it sounds. So, I'm reluctantly pushing support for recursive PEP 695 type aliases up to @beartype 0.18.0. ...to be released Guido knows when. :sob:

May unicorns be our guide to a better world.

leycec commented 8 months ago

Dear delightful reader: @beartype ≥ 0.17.0 now fully supports all PEP 695 type aliases except these two edge cases – one of which we can (and should) resolve under a future version of @beartype and the other of which we can never resolve. Why? Because PEP 695 is fundamentally broken. These edge cases are:

def some_func():
   type muh_local_forward_refs = list[SomeUndefinedType]

PEP 695 is busted. So, @beartype will probably never support forward references in local type aliases. Sob, sad emoji! :sob: