@@ -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
549695class 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