@@ -857,6 +857,99 @@ def __eq__(self, other):
857857 ctx2 .run (var .set , ReentrantHash ())
858858 ctx1 == ctx2
859859
860+ def test_context_depth_increases_on_run (self ):
861+ # Entering a copied context reports a depth one greater than the
862+ # context it was copied from; the depth is restored after the run.
863+ from _contextvars import _current_context_depth as depth
864+ base = depth ()
865+ got = []
866+ contextvars .copy_context ().run (lambda : got .append (depth ()))
867+ self .assertEqual (got , [base + 1 ])
868+ self .assertEqual (depth (), base )
869+
870+ def test_context_depth_nested_run (self ):
871+ # Nested copied contexts increase the depth by one per level.
872+ from _contextvars import _current_context_depth as depth
873+ base = depth ()
874+ got = []
875+
876+ def outer ():
877+ got .append (depth ())
878+ contextvars .copy_context ().run (
879+ lambda : got .append (depth ()))
880+ contextvars .copy_context ().run (outer )
881+ self .assertEqual (got , [base + 1 , base + 2 ])
882+
883+ def test_context_depth_reenter_same_context (self ):
884+ # The depth is fixed when the context is created, so running the
885+ # same context object again reports the same depth.
886+ from _contextvars import _current_context_depth as depth
887+ ctx = contextvars .copy_context ()
888+ got = []
889+ ctx .run (lambda : got .append (depth ()))
890+ ctx .run (lambda : got .append (depth ()))
891+ self .assertEqual (got [0 ], got [1 ])
892+
893+ def test_context_depth_copy_method (self ):
894+ # Context.copy() produces a context one level deeper than its source.
895+ from _contextvars import _current_context_depth as depth
896+ base = depth ()
897+ # copy_context() -> depth base+1; .copy() of that -> depth base+2,
898+ # regardless of where it is entered (depth is a creation property).
899+ ctx = contextvars .copy_context ().copy ()
900+ got = []
901+ ctx .run (lambda : got .append (depth ()))
902+ self .assertEqual (got , [base + 2 ])
903+
904+ def test_context_depth_empty_context_is_zero (self ):
905+ # A freshly created (empty) Context has depth 0 and reports it
906+ # independently of the context it is entered from.
907+ from _contextvars import _current_context_depth as depth
908+ got = []
909+ contextvars .Context ().run (lambda : got .append (depth ()))
910+ # Entered from within a deeper context, still its own depth (0).
911+ contextvars .copy_context ().run (
912+ lambda : contextvars .Context ().run (
913+ lambda : got .append (depth ())))
914+ self .assertEqual (got , [0 , 0 ])
915+
916+ def test_context_depth_inherited_value_is_shared (self ):
917+ # A value set before copying is inherited by the copied context as
918+ # the same object, while the depth differs -- this is the signal a
919+ # mutable value (e.g. a decimal context) uses to copy for isolation.
920+ from _contextvars import _current_context_depth as depth
921+ v = contextvars .ContextVar ('v' )
922+ sentinel = object ()
923+ v .set (sentinel )
924+ base = depth ()
925+ ctx = contextvars .copy_context ()
926+
927+ def check ():
928+ self .assertEqual (depth (), base + 1 )
929+ self .assertIs (v .get (), sentinel )
930+ ctx .run (check )
931+
932+ @threading_helper .requires_working_threading ()
933+ def test_context_depth_with_threads (self ):
934+ # A thread running a copied context sees a deeper context than the
935+ # parent, and each thread's depth is independent.
936+ import threading
937+ from _contextvars import _current_context_depth as depth
938+ base = depth ()
939+ results = {}
940+
941+ def thread_func (name ):
942+ results [name ] = depth ()
943+
944+ t1 = threading .Thread (target = contextvars .copy_context ().run ,
945+ args = (lambda : thread_func ('t1' ),))
946+ t2 = threading .Thread (target = contextvars .copy_context ().run ,
947+ args = (lambda : thread_func ('t2' ),))
948+ t1 .start (); t2 .start ()
949+ t1 .join (); t2 .join ()
950+ self .assertEqual (results ['t1' ], base + 1 )
951+ self .assertEqual (results ['t2' ], base + 1 )
952+
860953
861954@threading_helper .requires_working_threading ()
862955class ThreadInheritableVarTest (unittest .TestCase ):
0 commit comments