How shall I resolve a nonlocal name in a language with deep binding?
Roughly put, every time you define a function, as in def B(): ... you define B to be a closure. This is a pair contains a representation of the body of the function (possibly compiled to a simpler language), and a reference to the current environment (possibly restricted to what B actually needs). The current environment is the one we are in when we run def B(): ....

In the example you provide, B is defined inside A, so its associated closure gets a reference the the current call frame of A. This is why, if you pass P=B in the recursive call, after the call running P prints the value of I in the previous call frame.

Note that, in many languages, closures can be returned, which makes the current call frame to be not freed after the return. For example,

def A(I):
  def B():
    print(I)
  def C():
    nonlocal I  # Python for "use A's I, don't create a fresh local I"
    I += 1
  return (B,C)

# main
(b,c) = A(10)
b() # 10
c()
b() # 11
c()
b() # 12


Above, we can see that, even if A returned, its local variable I is still live! It can be accessed through the two returned closures, which contain a reference to the A's call frame, even if A is now finished.