Skip to content

Commit 000d4a3

Browse files
martindemellorchen152
authored andcommitted
Defer checking of Final so that it can be used with Annotated.
Also tweaks a couple of initializers to let __repr__ work during construction. Fixes #1110 PiperOrigin-RevId: 424194421
1 parent 2ae395c commit 000d4a3

File tree

4 files changed

+21
-7
lines changed

4 files changed

+21
-7
lines changed

pytype/abstract/_instances.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,8 +581,8 @@ class AnnotationsDict(Dict):
581581
"""__annotations__ dict."""
582582

583583
def __init__(self, annotated_locals, ctx):
584-
super().__init__(ctx)
585584
self.annotated_locals = annotated_locals
585+
super().__init__(ctx)
586586

587587
def get_type(self, node, name):
588588
if name not in self.annotated_locals:

pytype/abstract/_interpreter_function.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ def __init__(self, name, def_opcode, code, f_locals, f_globals, defaults,
448448
self.nonstararg_count += self.code.co_kwonlyargcount
449449
signature = self._build_signature(name, annotations)
450450
super().__init__(signature, ctx)
451+
self._check_annotations(name, annotations)
451452
self._update_signature_scope()
452453
self.last_frame = None # for BuildClass
453454
self._store_call_records = False
@@ -464,6 +465,13 @@ def record_calls(self):
464465
yield
465466
self._store_call_records = old
466467

468+
def _check_annotations(self, name, annotations):
469+
"""Validate function annotations."""
470+
for ann in annotations.values():
471+
if isinstance(ann, _typing.FinalAnnotation):
472+
self.ctx.errorlog.invalid_final_type(
473+
self.ctx.vm.simple_stack(self.def_opcode))
474+
467475
def _build_signature(self, name, annotations):
468476
"""Build a function.Signature object representing this function."""
469477
vararg_name = None

pytype/abstract/_typing.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -422,10 +422,10 @@ def __init__(self, options, ctx):
422422
self.options = list(options)
423423
self.cls = self._get_class()
424424
self.formal = any(t.formal for t in self.options)
425+
self._printing = False
425426
mixin.NestedAnnotation.init_mixin(self)
426427
mixin.HasSlots.init_mixin(self)
427428
self.set_slot("__getitem__", self.getitem_slot)
428-
self._printing = False
429429

430430
def __repr__(self):
431431
if self._printing: # recursion detected
@@ -672,5 +672,4 @@ def __repr__(self):
672672
return f"Final[{self.annotation}]"
673673

674674
def instantiate(self, node, container=None):
675-
self.ctx.errorlog.invalid_final_type(self.ctx.vm.frames)
676-
return self.annotation.to_variable(node)
675+
return self.to_variable(node)

pytype/tests/test_final.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,10 @@ def f(x: Final[int]): # final-error[e]
247247
pass
248248
def g(x: Final): # final-error
249249
pass
250-
def h(x) -> Final[int]:
251-
pass # bad-return-type # final-error
250+
def h(x) -> Final[int]: # final-error
251+
pass # bad-return-type
252252
def i(x) -> Final:
253-
pass # bad-return-type # final-error
253+
pass # bad-return-type # final-error
254254
""")
255255
self.assertErrorSequences(
256256
err, {"e": ["only be used", "assignments", "variable annotations"]})
@@ -262,6 +262,13 @@ def test_cannot_use_in_type_params(self):
262262
y: Tuple[int, Final[int]] = (1, 2) # invalid-annotation # final-error
263263
""")
264264

265+
def test_can_use_in_annotated(self):
266+
self.CheckWithErrors("""
267+
from typing import Annotated, Final, List
268+
x: Annotated[Final[List[int]], 'valid'] = [10]
269+
y: Annotated[List[Final[int]], 'invalid'] = [10] # invalid-annotation # final-error
270+
""")
271+
265272

266273
class TestFinalDecoratorInPyi(test_base.BaseTest):
267274
"""Test @final in pyi files."""

0 commit comments

Comments
 (0)