Repository: scipy/scipy

Issue:
DOC: scipy.integrate.solve_ivp
> You can assign attributes like event.terminal = True to any function in Python. If None (default), events won’t be tracked.

That's very confusing.    It's not the attribute **event.terminal** which is set to None for no tracking, but the argument **events**.


Discussion:
I don't know what that sentence is trying to say either.  Maybe, it is supposed to mean that events won't be tracked for terminal status, i.e. a type of zero crossing.
The last sentence should have been near the top, like this:

    Types of events to track. **If None (default), events won’t be tracked**. Each is defined by a continuous function of time and state that becomes zero value in case of an event. Each function must have the signature event(t, y) and return a float. The solver will find an accurate value of t at which event(t, y(t)) = 0 using a root-finding algorithm. Additionally each event function might have the following attributes:

            terminal: bool, whether to terminate integration if this event occurs. Implicitly False if not assigned.
            direction: float, direction of a zero crossing. If direction is positive, event must go from negative to positive, and vice versa if direction is negative. If 0, then either direction will count. Implicitly 0 if not assigned.

    You can assign attributes like event.terminal = True to any function in Python. 


Resolving patch:
diff --git a/scipy/integrate/_ivp/ivp.py b/scipy/integrate/_ivp/ivp.py
--- a/scipy/integrate/_ivp/ivp.py
+++ b/scipy/integrate/_ivp/ivp.py
@@ -179,11 +179,11 @@ def solve_ivp(fun, t_span, y0, method='RK45', t_eval=None, dense_output=False,
     ----------
     fun : callable
         Right-hand side of the system. The calling signature is ``fun(t, y)``.
-        Here ``t`` is a scalar, and there are two options for the ndarray ``y``:
-        It can either have shape (n,); then ``fun`` must return array_like with
-        shape (n,). Alternatively it can have shape (n, k); then ``fun``
+        Here `t` is a scalar, and there are two options for the ndarray `y`:
+        It can either have shape (n,); then `fun` must return array_like with
+        shape (n,). Alternatively it can have shape (n, k); then `fun`
         must return an array_like with shape (n, k), i.e. each column
-        corresponds to a single column in ``y``. The choice between the two
+        corresponds to a single column in `y`. The choice between the two
         options is determined by `vectorized` argument (see below). The
         vectorized implementation allows a faster approximation of the Jacobian
         by finite differences (required for stiff solvers).
@@ -234,30 +234,35 @@ def solve_ivp(fun, t_span, y0, method='RK45', t_eval=None, dense_output=False,
     t_eval : array_like or None, optional
         Times at which to store the computed solution, must be sorted and lie
         within `t_span`. If None (default), use points selected by the solver.
-    events : callable, list of callables or None, optional
-        Types of events to track. Each is defined by a continuous function of
-        time and state that becomes zero value in case of an event. Each function
-        must have the signature ``event(t, y)`` and return a float. The solver will
-        find an accurate value of ``t`` at which ``event(t, y(t)) = 0`` using a
-        root-finding algorithm. Additionally each ``event`` function might have
-        the following attributes:
-
-            * terminal: bool, whether to terminate integration if this
-              event occurs. Implicitly False if not assigned.
-            * direction: float, direction of a zero crossing. If `direction`
-              is positive, `event` must go from negative to positive, and
-              vice versa if `direction` is negative. If 0, then either direction
-              will count. Implicitly 0 if not assigned.
+    events : callable, or list of callables, optional
+        Events to track. If None (default), no events will be tracked.
+        Each event occurs at the zeros of a continuous function of time and
+        state. Each function must have the signature ``event(t, y)`` and return
+        a float. The solver will find an accurate value of `t` at which
+        ``event(t, y(t)) = 0`` using a root-finding algorithm. By default, all
+        zeros will be found. The solver looks for a sign change over each step,
+        so if multiple zero crossings occur within one step, events may be
+        missed. Additionally each `event` function might have the following
+        attributes:
+
+            terminal: bool, optional
+                Whether to terminate integration if this event occurs.
+                Implicitly False if not assigned.
+            direction: float, optional
+                Direction of a zero crossing. If `direction` is positive,
+                `event` will only trigger when going from negative to positive,
+                and vice versa if `direction` is negative. If 0, then either
+                direction will trigger event. Implicitly 0 if not assigned.
 
         You can assign attributes like ``event.terminal = True`` to any
-        function in Python. If None (default), events won't be tracked.
+        function in Python. 
     vectorized : bool, optional
         Whether `fun` is implemented in a vectorized fashion. Default is False.
     options
         Options passed to a chosen solver. All options available for already
         implemented solvers are listed below.
     first_step : float or None, optional
-        Initial step size. Default is ``None`` which means that the algorithm
+        Initial step size. Default is `None` which means that the algorithm
         should choose.
     max_step : float, optional
         Maximum allowed step size. Default is np.inf, i.e. the step size is not


Test patch: