From d45e5d2c4ee96a3d085dc612469060467bf90eae Mon Sep 17 00:00:00 2001 From: Nipunn Koorapati Date: Tue, 16 Nov 2021 18:00:38 -0800 Subject: [PATCH 1/2] PEP 613: Eliminate scope restriction of explicit TypeAlias Per discussion on typing-sig https://mail.python.org/archives/list/typing-sig@python.org/thread/CGOO7GPPECGMLFDUDXSSXTRADI4BXYCS/ --- pep-0613.rst | 52 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/pep-0613.rst b/pep-0613.rst index f11c5625921..5873a94b7bc 100644 --- a/pep-0613.rst +++ b/pep-0613.rst @@ -92,34 +92,38 @@ and ``InvalidType`` are not valid types. When the value expression is no longer evaluated as a global value, unactionable type errors on all usages of ``MyType`` across the codebase can be suppressed. -Scope Restrictions: -******************* +Scoping: +******** + +Implicit and explicit type aliases can be made at any scope as long as they obey +the Type Alias vs Variable inference rules specified in PEP 484 :: - x = ClassName - def foo() -> None: - x = ClassName + class Foo: + pass -The outer ``x`` is a valid type alias, but type checkers must error if the -inner ``x`` is ever used as a type because type aliases cannot be defined -inside a nested scope. -This is confusing because the alias declaration rule is not explicit, and because -a type error will not be thrown on the location of the inner type alias declaration -but rather on every one of its subsequent use cases. + F1 = Foo + class Bar: + F2 = Foo + F3: TypeAlias = Foo + +On explicit type aliases made on assignments that break the inference rules specified +in PEP 484, type checkers should raise a clear error, communicating to the author +why the TypeAlias is invalid. :: - x: TypeAlias = ClassName - def foo() -> None: - x = ClassName - def bar() -> None: - x: TypeAlias = ClassName + class Foo: + pass + + F1: TypeAlias = [f for f in Foo] # RHS not a valid type + F2: TypeAlias = Foo # Multiple assignments to F2 + F2 = None -With explicit aliases, the outer assignment is still a valid type variable, -and the inner assignment can either be a valid local variable or a clear error, -communicating to the author that type aliases cannot be defined inside a nested -scope. + class Bar: + def __init__(self): + self.F2: TypeAlias = Foo # LHS not a simple identifier Specification @@ -200,6 +204,14 @@ appealing because it still sticks with the ``MyType = int`` assignment syntax, and adds some information for the type checker purely as an annotation. +Version History +=============== + +* 2021-11-16 + + * Allow TypeAlias inside class/function scope + + Copyright ========= From c37d860c6f436b021f01aa4d5b2d55aa12f94d36 Mon Sep 17 00:00:00 2001 From: Nipunn Koorapati Date: Tue, 23 Nov 2021 15:48:15 -0800 Subject: [PATCH 2/2] PEP 484: Specify inference rules for type alias vs variables --- pep-0484.txt | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/pep-0484.txt b/pep-0484.txt index 1980e661223..b8003b9645d 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -236,6 +236,37 @@ This is equivalent to:: return ((x * scale, y * scale) for x, y in v) vec = [] # type: Iterable[Tuple[float, float]] +Simple variable assignments may be either type aliases or variables. PEP 613 +provides a way to explicitly annotate a type alias. Otherwise, the type checker +should infer a type alias if all of the following conditions apply. + +- There is no type annotation provided +- The LHS is a simple identifier symbol +- There is only one assignment to the LHS symbol +- The expression on the RHS of the assignment does not contain any syntactic form that would be considered illegal for a type annotation (call expressions, lambdas, comprehensions, etc.) +- The type evaluation of the RHS evaluates to an instantiable type or a union of instantiable types + +Type Alias vs Variable Examples:: + + class Foo: + pass + + F1: TypeAlias = Foo # explicit PEP 613 type alias + F2: Type[Foo] = Foo # explicit variable + F3 = Foo # inferred as type alias + + F4 = None + F4 = Foo # inferred as variable + + F5 = x() # inferred as variable + F6 = Union[Foo, str] # inferred as type alias + + class Bar: + F7 = Foo # inferred as type alias + def __init__(self): + F8 = Foo # inferred as type alias + self.F9 = Foo # inferred as variable + Callable -------- @@ -2494,6 +2525,14 @@ References https://docs.python.org/3/reference/import.html#submodules +Version History +=============== + +* 2021-11-23 + + * Specify Type Alias vs Variable inference rules + + Copyright =========