mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
gh-133684: Fix get_annotations() where PEP 563 is involved (#133795)
This commit is contained in:
parent
4443110c34
commit
3e562b3942
3 changed files with 84 additions and 4 deletions
|
@ -1042,14 +1042,27 @@ def _get_and_call_annotate(obj, format):
|
|||
return None
|
||||
|
||||
|
||||
_BASE_GET_ANNOTATIONS = type.__dict__["__annotations__"].__get__
|
||||
|
||||
|
||||
def _get_dunder_annotations(obj):
|
||||
"""Return the annotations for an object, checking that it is a dictionary.
|
||||
|
||||
Does not return a fresh dictionary.
|
||||
"""
|
||||
ann = getattr(obj, "__annotations__", None)
|
||||
if ann is None:
|
||||
return None
|
||||
# This special case is needed to support types defined under
|
||||
# from __future__ import annotations, where accessing the __annotations__
|
||||
# attribute directly might return annotations for the wrong class.
|
||||
if isinstance(obj, type):
|
||||
try:
|
||||
ann = _BASE_GET_ANNOTATIONS(obj)
|
||||
except AttributeError:
|
||||
# For static types, the descriptor raises AttributeError.
|
||||
return None
|
||||
else:
|
||||
ann = getattr(obj, "__annotations__", None)
|
||||
if ann is None:
|
||||
return None
|
||||
|
||||
if not isinstance(ann, dict):
|
||||
raise ValueError(f"{obj!r}.__annotations__ is neither a dict nor None")
|
||||
|
|
|
@ -7,7 +7,7 @@ import collections
|
|||
import functools
|
||||
import itertools
|
||||
import pickle
|
||||
from string.templatelib import Interpolation, Template
|
||||
from string.templatelib import Template
|
||||
import typing
|
||||
import unittest
|
||||
from annotationlib import (
|
||||
|
@ -815,6 +815,70 @@ class TestGetAnnotations(unittest.TestCase):
|
|||
{"x": int},
|
||||
)
|
||||
|
||||
def test_stringized_annotation_permutations(self):
|
||||
def define_class(name, has_future, has_annos, base_text, extra_names=None):
|
||||
lines = []
|
||||
if has_future:
|
||||
lines.append("from __future__ import annotations")
|
||||
lines.append(f"class {name}({base_text}):")
|
||||
if has_annos:
|
||||
lines.append(f" {name}_attr: int")
|
||||
else:
|
||||
lines.append(" pass")
|
||||
code = "\n".join(lines)
|
||||
ns = support.run_code(code, extra_names=extra_names)
|
||||
return ns[name]
|
||||
|
||||
def check_annotations(cls, has_future, has_annos):
|
||||
if has_annos:
|
||||
if has_future:
|
||||
anno = "int"
|
||||
else:
|
||||
anno = int
|
||||
self.assertEqual(get_annotations(cls), {f"{cls.__name__}_attr": anno})
|
||||
else:
|
||||
self.assertEqual(get_annotations(cls), {})
|
||||
|
||||
for meta_future, base_future, child_future, meta_has_annos, base_has_annos, child_has_annos in itertools.product(
|
||||
(False, True),
|
||||
(False, True),
|
||||
(False, True),
|
||||
(False, True),
|
||||
(False, True),
|
||||
(False, True),
|
||||
):
|
||||
with self.subTest(
|
||||
meta_future=meta_future,
|
||||
base_future=base_future,
|
||||
child_future=child_future,
|
||||
meta_has_annos=meta_has_annos,
|
||||
base_has_annos=base_has_annos,
|
||||
child_has_annos=child_has_annos,
|
||||
):
|
||||
meta = define_class(
|
||||
"Meta",
|
||||
has_future=meta_future,
|
||||
has_annos=meta_has_annos,
|
||||
base_text="type",
|
||||
)
|
||||
base = define_class(
|
||||
"Base",
|
||||
has_future=base_future,
|
||||
has_annos=base_has_annos,
|
||||
base_text="metaclass=Meta",
|
||||
extra_names={"Meta": meta},
|
||||
)
|
||||
child = define_class(
|
||||
"Child",
|
||||
has_future=child_future,
|
||||
has_annos=child_has_annos,
|
||||
base_text="Base",
|
||||
extra_names={"Base": base},
|
||||
)
|
||||
check_annotations(meta, meta_future, meta_has_annos)
|
||||
check_annotations(base, base_future, base_has_annos)
|
||||
check_annotations(child, child_future, child_has_annos)
|
||||
|
||||
def test_modify_annotations(self):
|
||||
def f(x: int):
|
||||
pass
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Fix bug where :func:`annotationlib.get_annotations` would return the wrong
|
||||
result for certain classes that are part of a class hierarchy where ``from
|
||||
__future__ import annotations`` is used.
|
Loading…
Add table
Add a link
Reference in a new issue