bpo-46771: Implement task cancel requests by Tinche · Pull Request #31513 · python/cpython

So this needs to be reviewed carefully by @agronholm and @Tinche -- not so much regarding "code review" but regarding "does this address our concerns, does it fix the edge cases, can we implement Trio-like behavior in 3rd party libraries when we need it". I'd also like @cjerdonek to think about this.

We also, separately, need to debate whether the lines

        if self._num_cancels_requested > 1:
            return False

should stay in or not. IIRC @agronholm has claimed that this (or the previous incarnation) broke several AnyIO tests.

There are two aspects that would change if we removed these two lines:

  1. The cancel message. See https://bugs.python.org/issue46829. In 3.10, after t.cancel("first"); t.cancel("2nd") the cancel message ended up being set to "2nd"; with 3.11 main it ends up "first". If we remove these two lines it would become "2nd" again.
  2. How many CancelledError exceptions will the task see? In 3.10, the task may see multiple cancellations -- but it may not. In particular, if the second cancel() call finds the must-cancel flag already set, only one cancellation will be delivered. However, if the first cancellation is delivered, and the task catches the CancelledError and then uses await to do some cleanup, in 3.10 (and again if we remove those two lines) that cleanup await will be cancelled; but if we keep those two lines, that await will be shielded.