Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

PEP 695 TypeAliasType.__value__ not supporting recursion intentional? #1816

Unanswered
randolf-scholz asked this question in Q&A
Discussion options

I was moving some code to PEP695 and just realized an issue. Consider the following example:

from typing import TypeAlias
StringScalar: TypeAlias = str | bytes
NumericalScalar: TypeAlias = bool | int | float | complex
Scalar: TypeAlias = StringScalar | NumericalScalar
assert isinstance("foo", StringScalar) # ✅
assert isinstance(3.14, NumericalScalar) # ✅
assert isinstance(42, Scalar) # ✅

and the supposed PEP 695 analogue fails:

type StringScalar = str | bytes
type NumericalScalar = bool | int | float | complex
type Scalar = StringScalar | NumericalScalar
assert isinstance("foo", StringScalar.__value__) # ✅
assert isinstance(3.14, NumericalScalar.__value__) # ✅
assert isinstance(42, Scalar.__value__) # ❌ arg 2 must be a type, a tuple of types, or a union

The issue is that Scalar.__value__ is the Union of two TypeAliasType members. PEP 695 only states that "__value__ is the evaluated value of the type alias", which under certain interpretations could be considered to mean that the result shouldn't contain any TypeAliasType. The PEP also mentions __evaluate_value__ in the context of PEP649, i.e. the resolution of Forward References.

What's the recommended way of getting the value of the TypeAliasType recursively? I guess for the moment, I could expand the unions manually. Will this be covered by __evaluate_value__ in the future?

You must be logged in to vote

Replies: 1 comment 4 replies

Comment options

I would recommend against using the type statement when you need to use the "type alias" in a runtime context.

I am not planning to change any of this behavior in the future (but that decision is not solely up to me).

You must be logged in to vote
4 replies
Comment options

@JelleZijlstra I found this issue based on the same assumption, that PEP 695 indicates a runtime-valid RHS of type parameters (even if not recursively expanded). A Generic example:

type GenericMetadata[T] = Annotated[T, list[T]]
type HardCodedMetadata = Annotated[int, list[int]]
print(HardCodedMetadata.__value__) # Annotated[int, list[int]]
print(GenericMetadata[int].__value__) # Annotated[T, list[T]] (not int??)

It seems this behavior violates the evaluatedvalue of the type alias statement in the PEP... Given TypeAlias is officially deprecated in favor of type statements, what is the recommended alternative if __value__ support is not planned?

Comment options

I don't know what to recommend to you because I don't know what you are trying to achieve. In general, I aim for the standard library to provide simple building blocks, and user code that knows what it wants can implement more complex operations (such as walking over a type and substituting type variables).

Comment options

Thanks for the reply. My use case is to annotate IDs with information about what object they reference:

type IdOf[T] = Annotated[UUID, T]

This can be hooked by e.g. JSON schema generators in pydantic models to provide more schema information/typing to endpoints accepting UUID strings.

As for how it relates to typing, my hope was that __value__ would supply runtime-specific generic values, as opposed to user-created custom traversals. The question was mostly about how to correctly interpret the PEP 695 spec if runtime type alias values aren't resolved within __value__

Comment options

Tools interpreting such metadata should recognize GenericAlias instances wrapping TypeAliasType instances and resolve the TypeVar accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Q&A
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /