Skip to content

Commit 9b25626

Browse files
gh-157056: Remint NEWS and fold STRING-format tests into TestStringFormat
1 parent f0ef87f commit 9b25626

1 file changed

Lines changed: 152 additions & 0 deletions

File tree

Lib/test/test_annotationlib.py

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,152 @@ def f(x: x | (1).__class__, y: (1).__class__):
545545
{"x": "x | <class 'int'>", "y": "<class 'int'>"},
546546
)
547547

548+
def test_comprehensions(self):
549+
# gh-157056: pair-unpacking comprehensions raised ValueError, and
550+
# generator expressions were stringified with a memory address.
551+
def f(
552+
dictcomp: {k: v for k, v in items},
553+
listcomp: [k for k, v in items],
554+
setcomp: {k for k, v in items},
555+
genexpr: (w for w in seq),
556+
):
557+
pass
558+
559+
anno = get_annotations(f, format=Format.STRING)
560+
self.assertEqual(
561+
anno,
562+
{
563+
"dictcomp": "{k: v for k, v in items}",
564+
"listcomp": "[k for k, v in items]",
565+
"setcomp": "{k for k, v in items}",
566+
"genexpr": "(w for w in seq)",
567+
},
568+
)
569+
self.assertNotIn("0x", anno["genexpr"].lower())
570+
571+
def test_lambda(self):
572+
def f(
573+
lam: lambda q: q,
574+
nested_list: [lambda q: q],
575+
nested_dict: {"h": lambda q: q},
576+
nested_frozenset: frozenset({lambda q: q}),
577+
):
578+
pass
579+
580+
anno = get_annotations(f, format=Format.STRING)
581+
self.assertEqual(
582+
anno,
583+
{
584+
"lam": "lambda q: q",
585+
"nested_list": "[lambda q: q]",
586+
"nested_dict": "{'h': lambda q: q}",
587+
"nested_frozenset": "frozenset({lambda q: q})",
588+
},
589+
)
590+
for value in anno.values():
591+
self.assertNotIn("0x", value.lower())
592+
593+
def test_quoted_string_with_unpack_fallback(self):
594+
def f(a: "int", b: {k: v for k, v in items}):
595+
pass
596+
597+
anno = get_annotations(f, format=Format.STRING)
598+
self.assertEqual(anno, {"a": "int", "b": "{k: v for k, v in items}"})
599+
600+
def test_return_varargs_kwargs(self):
601+
def f(
602+
*xs: {k: v for k, v in items},
603+
**kw: lambda q: q,
604+
) -> {k: v for k, v in items}:
605+
pass
606+
607+
anno = get_annotations(f, format=Format.STRING)
608+
self.assertEqual(
609+
anno,
610+
{
611+
"xs": "{k: v for k, v in items}",
612+
"kw": "lambda q: q",
613+
"return": "{k: v for k, v in items}",
614+
},
615+
)
616+
617+
def test_nested_and_decorated(self):
618+
def deco(fn):
619+
return fn
620+
621+
def outer():
622+
@deco
623+
def inner(x: {k: v for k, v in items}, y: lambda q: q):
624+
pass
625+
return inner
626+
627+
anno = get_annotations(outer(), format=Format.STRING)
628+
self.assertEqual(
629+
anno,
630+
{"x": "{k: v for k, v in items}", "y": "lambda q: q"},
631+
)
632+
633+
def test_async_function(self):
634+
async def f(x: {k: v for k, v in items}) -> int:
635+
pass
636+
637+
anno = get_annotations(f, format=Format.STRING)
638+
self.assertEqual(anno, {"x": "{k: v for k, v in items}", "return": "int"})
639+
640+
def test_method_and_staticmethod(self):
641+
class C:
642+
def meth(self, x: {k: v for k, v in items}) -> str:
643+
pass
644+
645+
@staticmethod
646+
def sm(x: lambda q: q):
647+
pass
648+
649+
self.assertEqual(
650+
get_annotations(C.meth, format=Format.STRING),
651+
{"x": "{k: v for k, v in items}", "return": "str"},
652+
)
653+
self.assertEqual(
654+
get_annotations(C.sm, format=Format.STRING),
655+
{"x": "lambda q: q"},
656+
)
657+
658+
def test_class_with_conditional_annotation(self):
659+
class C:
660+
a: int
661+
b: {k: v for k, v in items}
662+
if True:
663+
c: str
664+
665+
anno = get_annotations(C, format=Format.STRING)
666+
self.assertEqual(
667+
anno,
668+
{
669+
"a": "int",
670+
"b": "{k: v for k, v in items}",
671+
"c": "str",
672+
},
673+
)
674+
675+
def test_string_format_does_not_read_source(self):
676+
import inspect
677+
678+
def f(x: int) -> str:
679+
pass
680+
681+
def boom(*args, **kwargs):
682+
raise AssertionError("inspect.getsource should not be consulted")
683+
684+
with support.swap_attr(inspect, "getsource", boom):
685+
anno = get_annotations(f, format=Format.STRING)
686+
self.assertEqual(anno, {"x": "int", "return": "str"})
687+
688+
def test_exec_without_source_still_raises(self):
689+
ns = {}
690+
exec("def f(x: {k: v for k, v in items}): pass", ns)
691+
with self.assertRaisesRegex(ValueError, "values to unpack"):
692+
get_annotations(ns["f"], format=Format.STRING)
693+
548694

549695
class TestGetAnnotations(unittest.TestCase):
550696
def test_builtin_type(self):
@@ -1859,6 +2005,12 @@ def nested():
18592005
self.assertEqual(type_repr(len), "len")
18602006
self.assertEqual(type_repr(type_repr), "annotationlib.type_repr")
18612007
self.assertEqual(type_repr(times_three), f"{__name__}.times_three")
2008+
lam = (lambda q: q)
2009+
self.assertTrue(type_repr(lam).endswith("<lambda>"))
2010+
self.assertNotIn("0x", type_repr(lam).lower())
2011+
gen = (w for w in ())
2012+
self.assertTrue(type_repr(gen).endswith("<genexpr>"))
2013+
self.assertNotIn("0x", type_repr(gen).lower())
18622014
self.assertEqual(type_repr(...), "...")
18632015
self.assertEqual(type_repr(None), "None")
18642016
self.assertEqual(type_repr(1), "1")

0 commit comments

Comments
 (0)