Skip to content

Commit

Permalink
Add tests for metaclasses and typing_extensions.get_annotations (#440)
Browse files Browse the repository at this point in the history
Tests from python/cpython#122074. We don't have to use the base descriptor
approach here because we find the annotations directly in the `__dict__`
for the class, which avoids metaclass problems.
  • Loading branch information
JelleZijlstra authored Sep 23, 2024
1 parent 2849332 commit b6c0558
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 3 deletions.
16 changes: 13 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,19 @@ select = [
"W",
]

# Ignore various "modernization" rules that tell you off for importing/using
# deprecated things from the typing module, etc.
ignore = ["UP006", "UP007", "UP013", "UP014", "UP019", "UP035", "UP038"]
ignore = [
# Ignore various "modernization" rules that tell you off for importing/using
# deprecated things from the typing module, etc.
"UP006",
"UP007",
"UP013",
"UP014",
"UP019",
"UP035",
"UP038",
# Not relevant here
"RUF012",
]

[tool.ruff.lint.per-file-ignores]
"!src/typing_extensions.py" = [
Expand Down
66 changes: 66 additions & 0 deletions src/test_typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import importlib
import inspect
import io
import itertools
import pickle
import re
import subprocess
Expand Down Expand Up @@ -7685,6 +7686,71 @@ def f(x: int):
self.assertEqual(get_annotations(f), {"x": str})


class TestGetAnnotationsMetaclasses(BaseTestCase):
def test_annotated_meta(self):
class Meta(type):
a: int

class X(metaclass=Meta):
pass

class Y(metaclass=Meta):
b: float

self.assertEqual(get_annotations(Meta), {"a": int})
self.assertEqual(get_annotations(X), {})
self.assertEqual(get_annotations(Y), {"b": float})

def test_unannotated_meta(self):
class Meta(type): pass

class X(metaclass=Meta):
a: str

class Y(X): pass

self.assertEqual(get_annotations(Meta), {})
self.assertEqual(get_annotations(Y), {})
self.assertEqual(get_annotations(X), {"a": str})

def test_ordering(self):
# Based on a sample by David Ellis
# https://discuss.python.org/t/pep-749-implementing-pep-649/54974/38

def make_classes():
class Meta(type):
a: int
expected_annotations = {"a": int}

class A(type, metaclass=Meta):
b: float
expected_annotations = {"b": float}

class B(metaclass=A):
c: str
expected_annotations = {"c": str}

class C(B):
expected_annotations = {}

class D(metaclass=Meta):
expected_annotations = {}

return Meta, A, B, C, D

classes = make_classes()
class_count = len(classes)
for order in itertools.permutations(range(class_count), class_count):
names = ", ".join(classes[i].__name__ for i in order)
with self.subTest(names=names):
classes = make_classes() # Regenerate classes
for i in order:
get_annotations(classes[i])
for c in classes:
with self.subTest(c=c):
self.assertEqual(get_annotations(c), c.expected_annotations)


@skipIf(STRINGIZED_ANNOTATIONS_PEP_695 is None, "PEP 695 has yet to be")
class TestGetAnnotationsWithPEP695(BaseTestCase):
@classmethod
Expand Down

0 comments on commit b6c0558

Please sign in to comment.