@@ -672,7 +672,7 @@ def __fspath__(self):
672672 compile ("42" , PathLike ("test_compile_pathlike" ), "single" )
673673
674674
675- class TestStackSize (unittest .TestCase ):
675+ class TestExpressionStackSize (unittest .TestCase ):
676676 # These tests check that the computed stack size for a code object
677677 # stays within reasonable bounds (see issue #21523 for an example
678678 # dysfunction).
@@ -710,5 +710,294 @@ def test_func_and(self):
710710 self .check_stack_size (code )
711711
712712
713+ class TestStackSizeStability (unittest .TestCase ):
714+ # Check that repeating certain snippets doesn't increase the stack size
715+ # beyond what a single snippet requires.
716+
717+ def check_stack_size (self , snippet , async_ = False ):
718+ def compile_snippet (i ):
719+ ns = {}
720+ script = """def func():\n """ + i * snippet
721+ if async_ :
722+ script = "async " + script
723+ code = compile (script , "<script>" , "exec" )
724+ exec (code , ns , ns )
725+ return ns ['func' ].__code__
726+
727+ sizes = [compile_snippet (i ).co_stacksize for i in range (2 , 5 )]
728+ if len (set (sizes )) != 1 :
729+ import dis , io
730+ out = io .StringIO ()
731+ dis .dis (compile_snippet (1 ), file = out )
732+ self .fail ("stack sizes diverge with # of consecutive snippets: "
733+ "%s\n %s\n %s" % (sizes , snippet , out .getvalue ()))
734+
735+ def test_if (self ):
736+ snippet = """
737+ if x:
738+ a
739+ """
740+ self .check_stack_size (snippet )
741+
742+ def test_if_else (self ):
743+ snippet = """
744+ if x:
745+ a
746+ elif y:
747+ b
748+ else:
749+ c
750+ """
751+ self .check_stack_size (snippet )
752+
753+ def test_try_except_bare (self ):
754+ snippet = """
755+ try:
756+ a
757+ except:
758+ b
759+ """
760+ self .check_stack_size (snippet )
761+
762+ def test_try_except_qualified (self ):
763+ snippet = """
764+ try:
765+ a
766+ except ImportError:
767+ b
768+ except:
769+ c
770+ else:
771+ d
772+ """
773+ self .check_stack_size (snippet )
774+
775+ def test_try_except_as (self ):
776+ snippet = """
777+ try:
778+ a
779+ except ImportError as e:
780+ b
781+ except:
782+ c
783+ else:
784+ d
785+ """
786+ self .check_stack_size (snippet )
787+
788+ def test_try_finally (self ):
789+ snippet = """
790+ try:
791+ a
792+ finally:
793+ b
794+ """
795+ self .check_stack_size (snippet )
796+
797+ def test_with (self ):
798+ snippet = """
799+ with x as y:
800+ a
801+ """
802+ self .check_stack_size (snippet )
803+
804+ def test_while_else (self ):
805+ snippet = """
806+ while x:
807+ a
808+ else:
809+ b
810+ """
811+ self .check_stack_size (snippet )
812+
813+ def test_for (self ):
814+ snippet = """
815+ for x in y:
816+ a
817+ """
818+ self .check_stack_size (snippet )
819+
820+ def test_for_else (self ):
821+ snippet = """
822+ for x in y:
823+ a
824+ else:
825+ b
826+ """
827+ self .check_stack_size (snippet )
828+
829+ def test_for_break_continue (self ):
830+ snippet = """
831+ for x in y:
832+ if z:
833+ break
834+ elif u:
835+ continue
836+ else:
837+ a
838+ else:
839+ b
840+ """
841+ self .check_stack_size (snippet )
842+
843+ def test_for_break_continue_inside_try_finally_block (self ):
844+ snippet = """
845+ for x in y:
846+ try:
847+ if z:
848+ break
849+ elif u:
850+ continue
851+ else:
852+ a
853+ finally:
854+ f
855+ else:
856+ b
857+ """
858+ self .check_stack_size (snippet )
859+
860+ def test_for_break_inside_finally_block (self ):
861+ snippet = """
862+ for x in y:
863+ try:
864+ t
865+ finally:
866+ if z:
867+ break
868+ else:
869+ a
870+ else:
871+ b
872+ """
873+ self .check_stack_size (snippet )
874+
875+ def test_for_break_continue_inside_except_block (self ):
876+ snippet = """
877+ for x in y:
878+ try:
879+ t
880+ except:
881+ if z:
882+ break
883+ elif u:
884+ continue
885+ else:
886+ a
887+ else:
888+ b
889+ """
890+ self .check_stack_size (snippet )
891+
892+ def test_for_break_continue_inside_with_block (self ):
893+ snippet = """
894+ for x in y:
895+ with c:
896+ if z:
897+ break
898+ elif u:
899+ continue
900+ else:
901+ a
902+ else:
903+ b
904+ """
905+ self .check_stack_size (snippet )
906+
907+ def test_return_inside_try_finally_block (self ):
908+ snippet = """
909+ try:
910+ if z:
911+ return
912+ else:
913+ a
914+ finally:
915+ f
916+ """
917+ self .check_stack_size (snippet )
918+
919+ def test_return_inside_finally_block (self ):
920+ snippet = """
921+ try:
922+ t
923+ finally:
924+ if z:
925+ return
926+ else:
927+ a
928+ """
929+ self .check_stack_size (snippet )
930+
931+ def test_return_inside_except_block (self ):
932+ snippet = """
933+ try:
934+ t
935+ except:
936+ if z:
937+ return
938+ else:
939+ a
940+ """
941+ self .check_stack_size (snippet )
942+
943+ def test_return_inside_with_block (self ):
944+ snippet = """
945+ with c:
946+ if z:
947+ return
948+ else:
949+ a
950+ """
951+ self .check_stack_size (snippet )
952+
953+ def test_async_with (self ):
954+ snippet = """
955+ async with x as y:
956+ a
957+ """
958+ self .check_stack_size (snippet , async_ = True )
959+
960+ def test_async_for (self ):
961+ snippet = """
962+ async for x in y:
963+ a
964+ """
965+ self .check_stack_size (snippet , async_ = True )
966+
967+ def test_async_for_else (self ):
968+ snippet = """
969+ async for x in y:
970+ a
971+ else:
972+ b
973+ """
974+ self .check_stack_size (snippet , async_ = True )
975+
976+ def test_for_break_continue_inside_async_with_block (self ):
977+ snippet = """
978+ for x in y:
979+ async with c:
980+ if z:
981+ break
982+ elif u:
983+ continue
984+ else:
985+ a
986+ else:
987+ b
988+ """
989+ self .check_stack_size (snippet , async_ = True )
990+
991+ def test_return_inside_async_with_block (self ):
992+ snippet = """
993+ async with c:
994+ if z:
995+ return
996+ else:
997+ a
998+ """
999+ self .check_stack_size (snippet , async_ = True )
1000+
1001+
7131002if __name__ == "__main__" :
7141003 unittest .main ()
0 commit comments