Skip to content
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

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

Open
EtaoinWu opened this issue Oct 26, 2023 · 3 comments
Open

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

EtaoinWu opened this issue Oct 26, 2023 · 3 comments

Comments

@EtaoinWu
Copy link

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
Copy link
Member

leycec commented Oct 27, 2023

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. 🥳

leycec added a commit that referenced this issue Nov 26, 2023
This commit is the first in a commit chain adding support for PEP
695-compliant type aliases (i.e., type hints whose evaluation is lazily
deferred under the new `type` statement introduced by Python 3.12.0),
en-route to resolving feature request #297 kindly submitted by the
magical rainbow GitHub unicorn @EtaoinWu (Yue Wu). Specifically, this
commit outlines a pragmatic and defensible plan for tackling the most
problematic and troubling aspect of PEP 695 with respect to hybrid
runtime-static type-checking: PEP 695's horrifying, terrifying, and
frankly shocking lack of runtime support for resolving forward
references. Although PEP 695 repeatedly mic-drops forward references as
a central motivation for its existence, the actual runtime
implementation of PEP 695 lacks any support whatsoever for forward
references and even goes to elaborate and outrageous lengths to prevent
runtime-static type-checkers from resolving forward references embedded
in PEP 695-compliant type aliases. Yet again, a new runtime-hostile PEP
arises. Thankfully, this is @beartype. We do what we want here. And what
we want here is to fully dismantle PEP 695, break it, and then bend it
to our perfidious will until this Accursed Abomination Unto Nuggan does
what it was advertised but failed to do at runtime. So says the Bear.
(*Icky lackies lack sand-blasted ampersands!*)
leycec added a commit that referenced this issue Nov 27, 2023
This commit is the next in a commit chain adding support for PEP
695-compliant type aliases (i.e., type hints whose evaluation is lazily
deferred under the new `type` statement introduced by Python 3.12.0),
en-route to resolving feature request #297 kindly submitted by the
magical rainbow GitHub unicorn @EtaoinWu (Yue Wu). Specifically, this
commit continues outlining a pragmatic and defensible plan for tackling
the most problematic and troubling aspect of PEP 695 with respect to
hybrid runtime-static type-checking: PEP 695's horrifying, terrifying,
and frankly shocking lack of runtime support for resolving forward
references. Although PEP 695 repeatedly mic-drops forward references as
a central motivation for its existence, the actual runtime
implementation of PEP 695 lacks any support whatsoever for forward
references and even goes to elaborate and outrageous lengths to prevent
runtime-static type-checkers from resolving forward references embedded
in PEP 695-compliant type aliases. Yet again, a new runtime-hostile PEP
arises. Thankfully, this is @beartype. We do what we want here. And what
we want here is to fully dismantle PEP 695, break it, and then bend it
to our perfidious will until this Accursed Abomination Unto Nuggan does
what it was advertised but failed to do at runtime. So says the Bear.
(*Kindly humming hummingbird in a summery bind at supper!*)
leycec added a commit that referenced this issue Dec 20, 2023
This commit is the next in a commit chain adding support for PEP
695-compliant type aliases (i.e., type hints instantiated by statements
of the form ``type {alias_name} = {alias_value}``, available under
Python ≥ 3.12), en-route to resolving feature request #297 kindly
submitted by the magical rainbow GitHub unicorn @EtaoinWu (Yue Wu).
Specifically, this commit:

* Defines a new
  `beartype._util.hint.pep.utilpep695.reduce_hint_pep695()` reducer
  extracting the type hint lazily referred to by the passed type alias.
  Naturally, nothing is tested. Trust no one – *not even @leycec*.
* Defines a new
  `beartype._util.error.utilerrorget.get_name_error_attr_name()` getter
  extracting the unqualified basename of the undefined
  attribute described by the passed `NameError` exception.
* Exhaustively unit tests that `get_name_error_attr_name()` getter.
* Continues outlining a pragmatic and defensible plan for tackling the
  most problematic and troubling aspect of PEP 695 with respect to
  hybrid runtime-static type-checking: PEP 695's horrifying, terrifying,
  and frankly shocking lack of runtime support for resolving forward
  references. Although PEP 695 repeatedly mic-drops forward references
  as a central motivation for its existence, the actual runtime
  implementation of PEP 695 lacks any support whatsoever for forward
  references and even goes to elaborate and outrageous lengths to
  prevent runtime-static type-checkers from resolving forward references
  embedded in PEP 695-compliant type aliases. Yet again, a new
  runtime-hostile PEP arises. Thankfully, this is @beartype. We do what
  we want here. And what we want here is to fully dismantle PEP 695,
  break it, and then bend it to our perfidious will until this Accursed
  Abomination Unto Nuggan does what it was advertised but failed to do
  at runtime. So says the Bear.

(*Sassy sassafras!*)
@leycec
Copy link
Member

leycec commented Dec 20, 2023

@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 4c8921b now fully handles this common case. 🥳
  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. 😬
  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. 😭

May unicorns be our guide to a better world.

leycec added a commit that referenced this issue Dec 21, 2023
This commit is the next in a commit chain adding support for PEP
695-compliant type aliases (i.e., type hints instantiated by statements
of the form ``type {alias_name} = {alias_value}``, available under
Python ≥ 3.12), en-route to resolving feature request #297 kindly
submitted by the magical rainbow GitHub unicorn @EtaoinWu (Yue Wu).
Specifically, this commit:

* Finalizes an initial working implementation of our new
  `beartype._util.hint.pep.utilpep695.reduce_hint_pep695()` reducer
  extracting the type hint lazily referred to by the passed type alias.
  Notably, this reducer now implements our pragmatic and defensible plan
  for tackling the most problematic and troubling aspect of PEP 695 with
  respect to hybrid runtime-static type-checking: PEP 695's horrifying,
  terrifying, and frankly shocking lack of runtime support for resolving
  forward references. Although PEP 695 repeatedly mic-drops forward
  references as a central motivation for its existence, the actual
  runtime implementation of PEP 695 lacks any support whatsoever for
  forward references and even goes to elaborate and outrageous lengths
  to prevent runtime-static type-checkers from resolving forward
  references embedded in PEP 695-compliant type aliases. Yet again, a
  new runtime-hostile PEP arises. Thankfully, this is @beartype. We do
  what we want here. And what we want here is to fully dismantle PEP
  695, break it, and then bend it to our perfidious will until this
  Accursed Abomination Unto Nuggan does what it was advertised but
  failed to do at runtime. So says the Bear. To do so:
  * For each PEP 695-compliant type alias containing one or more forward
    references to attributes *not* defined in the external third-party
    module defining that alias, the `@beartype` decorator now forcefully
    defines one global variable for each such reference whose value is a
    forward reference proxy referring to that undefined attribute.
* Begins exhaustively testing that reducer with a new
  `beartype_test.a00_unit.data.hint.pep.proposal._data_hintpep695` test
  submodule.

(*Hulking skulker of a skeletal hurricane: can you hurry it up?*)
leycec added a commit that referenced this issue Dec 22, 2023
This commit is the next in a commit chain adding support for PEP
695-compliant type aliases (i.e., type hints instantiated by statements
of the form ``type {alias_name} = {alias_value}``, available under
Python ≥ 3.12), en-route to resolving feature request #297 kindly
submitted by the magical rainbow GitHub unicorn @EtaoinWu (Yue Wu).
Specifically, this commit generalizes our recently introduced
`beartype._util.error.utilerrorget.get_name_error_attr_name()` getter to
portably extract the unqualified basename of the undefined attribute
described by all passed `NameError` exceptions regardless of the exact
format of those exceptions. Previously, that getter only supported the
most common variant of `NameError` exceptions implicitly raised by
forward references in PEP 695-compliant type aliases.
(*Unassailable asininity in a divine trinity!*)
leycec added a commit that referenced this issue Dec 27, 2023
This commit is the next in a commit chain adding support for PEP
695-compliant type aliases (i.e., type hints instantiated by statements
of the form ``type {alias_name} = {alias_value}``, available under
Python ≥ 3.12), en-route to resolving feature request #297 kindly
submitted by the magical rainbow GitHub unicorn @EtaoinWu (Yue Wu).
Specifically, this commit continues attempting (but mostly failing) to
implement our pragmatic and defensible plan for tackling the most
problematic and troubling aspect of PEP 695 with respect to hybrid
runtime-static type-checking: PEP 695's horrifying, terrifying, and
frankly shocking lack of runtime support for resolving forward
references. Although PEP 695 repeatedly mic-drops forward references as
a central motivation for its existence, the actual runtime
implementation of PEP 695 lacks any support whatsoever for forward
references and even goes to elaborate and outrageous lengths to prevent
runtime-static type-checkers from resolving forward references embedded
in PEP 695-compliant type aliases. Yet again, a new runtime-hostile PEP
arises. Thankfully, this is @beartype. We do what we want here. And what
we want here is to fully dismantle PEP 695, break it, and then bend it
to our perfidious will until this Accursed Abomination Unto Nuggan does
what it was advertised but failed to do at runtime. So says the Bear.

Sadly, it would seem that most of my prior plans were mislaid and that
@beartype must now revert to a brutal application of hardcore AST
transformations just to force PEP 695 to comply with our will. Will you
just stop sucking it already, PEP 695? PEP 695 as if written by Terry
Pratchett:

> "I didn do nuffin."

(*Nuffin muffinburger!*)
leycec added a commit that referenced this issue Dec 29, 2023
This commit is the next in a commit chain adding support for PEP
695-compliant type aliases (i.e., type hints instantiated by statements
of the form ``type {alias_name} = {alias_value}``, available under
Python ≥ 3.12), en-route to resolving feature request #297 kindly
submitted by the magical rainbow GitHub unicorn @EtaoinWu (Yue Wu).
Specifically, this commit begins brutally applying a hardcore AST
transformation just to force PEP 695 to comply with our will. Will you
just stop sucking it already, PEP 695? PEP 695 as if written by Terry
Pratchett:

> "I didn do nuffin."

(*Ridiculous ridicule invites vectorless invective!*)
leycec added a commit that referenced this issue Dec 30, 2023
This commit is the next in a commit chain adding support for PEP
695-compliant type aliases (i.e., type hints instantiated by statements
of the form ``type {alias_name} = {alias_value}``, available under
Python ≥ 3.12), en-route to resolving feature request #297 kindly
submitted by the magical rainbow GitHub unicorn @EtaoinWu (Yue Wu).
Specifically, this commit continues brutally applying a hardcore AST
transformation just to force PEP 695 to comply with our will. Will you
just stop sucking it already, PEP 695? PEP 695 as if written by Terry
Pratchett:

> "I didn do nuffin."

(*Unbreakably limitless limit break!*)
leycec added a commit that referenced this issue Dec 31, 2023
This commit is the next in a commit chain adding support for PEP
695-compliant type aliases (i.e., type hints instantiated by statements
of the form ``type {alias_name} = {alias_value}``, available under
Python ≥ 3.12), en-route to resolving feature request #297 kindly
submitted by the magical rainbow GitHub unicorn @EtaoinWu (Yue Wu).
Specifically, this commit continues brutally applying a hardcore AST
transformation just to force PEP 695 to comply with our will. Will you
just stop sucking it already, PEP 695? PEP 695 as if written by Terry
Pratchett:

> "I didn do nuffin."

(*Bland blemish on a Flemish flamingo!*)
leycec added a commit that referenced this issue Jan 2, 2024
This commit bumps all copyright notices throughout the codebase in a
hopeless bid to remain hip and cool with a younger generation that never
"enjoyed" the endless hell of floating Medusa heads in Castlevania 1.
Unrelatedly, this commit also:

* Resolves a recent rash of false positives from `pyright ≥ 1.1.339`.
  "y u do this 2 us, microsoft..."
* Serves as the next in a commit chain adding support for PEP
  695-compliant type aliases (i.e., type hints instantiated by
  statements of the form ``type {alias_name} = {alias_value}``,
  available under Python ≥ 3.12), en-route to resolving feature request
  #297 kindly submitted by the magical rainbow GitHub unicorn @EtaoinWu
  (Yue Wu). Specifically, this commit continues brutally applying a
  hardcore AST transformation just to force PEP 695 to comply with
  our will. Will you just stop sucking it already, PEP 695? PEP 695
  as if written by Terry Pratchett:

  > "I didn do nuffin."

(*Drag on, Dragon!*)
leycec added a commit that referenced this issue Jan 3, 2024
This commit is the last in a commit chain adding support for PEP
695-compliant type aliases (i.e., type hints instantiated by statements
of the form ``type {alias_name} = {alias_value}``, available under
Python ≥ 3.12), partially resolving feature request #297 kindly
submitted by the magical rainbow GitHub unicorn @EtaoinWu (Yue Wu).
Specifically, this commit finishes brutally applying a hardcore AST
transformation just to force PEP 695 to comply with our will. Will you
just stop sucking it already, PEP 695? PEP 695 as if written by Terry
Pratchett:

> "I didn do nuffin."

@beartype now partially supports various aspects of PEP 695-compliant
type aliases, including:

* **Full support** for type aliases containing neither forward
  references *nor* recursion. Huzzah!
* **Full support** for global type aliases (i.e., defined as global
  attributes at module scope) containing forward references but *not*
  recursion. Huzzah x 2!
* **No support** for local type aliases (i.e., defined as local
  attributes in callables) containing forward references. Sadly, *no*
  subsequent @beartype release is expected to fully support this use
  case. For unknown reasons that would probably bore all of us,
  CPython's runtime implementation of PEP 695-compliant local type
  aliases is fundamentally and profoundly broken. Thus, bears cry.
* **No support** for type aliases containing recursion. Unlike the prior
  bullet point, @beartype can theoretically fully support this use case.
  It simply chooses not to at the moment, because it is very tired and
  must now lie down. A subsequent @beartype release is expected to fully
  support recursive type aliases. Belated huzzah!

(*Sundry sundials wryly dry on Sundays!*)
@leycec
Copy link
Member

leycec commented Feb 18, 2024

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:

  • Recursion (e.g., type muh_recursive_alias = list[muh_recursive_alias]). A future version of @beartype will definitely support this. Sadly, current versions of @beartype do not. Cry, cat emoji! 😿
  • Forward references in local type aliases: e.g.,
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! 😭

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants