The scipy.integrate.ode interface to integration routines provides a method for stopping the integration if a constraint is violated at any step, set_solo
It turns out you need to call set_solout before calling set_initial_value. (I figured this out by studying the set_solout tests in the scipy test suite.) So, reversing the order of the two calls in my question code produces the correct result.
Even if this behavior is correct, it ought to be mentioned in the documentation for set_solout. I've posted an issue with SciPy on GitHub.
UPDATE: This issue is fixed in SciPy 0.17.0; set_solout will work even if called after set_initial_value, and the question code will produce the correct result.