I have a Python script, part of a test system that calls many third party tools/processes on multiple [Windows] machines, and hence has been designed to clean up comprehensively/carefully when aborted with CTRL-C; the clean-up can take many seconds, depending on what's going on.  This clean-up process works fine from a [Windows] command prompt.
I run that Python script from [a scripted pipeline] Jenkinsfile, using return_value = bat("python my_script.py params", returnStatus: true), which also works fine.
However I need to be able to perform the abort/clean-up during a Jenkins [v2.263.4] run, i.e. when someone presses the little red X, and that bit I can't fathom.  I understand that Jenkins sends SIGTERM when the abort button is pressed so I am trapping that in my_script.py:
SAVED_SIGTERM_HANDLER = signal(SIGTERM, sigterm_handler)
...and running the processes I would normally call from a KeyboardInterrupt in sigterm_handler() as well, but they aren't being called.  I understand that the IO stream to the Jenkins console stops the moment the abort button is pressed; I can see that the clean-up functions aren't being called by looking at the behaviour of my script(s) from the "other side": it appears as though my_script.py is simply stopping dead, all connections from it drop the moment the abort button is pressed, there is no clean-up.
Can anyone suggest a way of making the abort button in Jenkins give my bat()ed Python script time to clean-up?  Or am I just doing something wrong?  Or is there some other approach to this within Jenkins that I'm missing?
After much figuring out, and kudos to our tools people who found the critical "cookie" implementation detail in Jenkins, the workaround to take control of the abort process [on Windows] is as follows:

have Jenkins call a wrapper, let's call it (a), and open a socket or a named-pipe (socket would work on both Linux and Windows),
(a) then launches (b), via "start" so that (b) runs as a separate process but, CRITICALLY, the environment that (a) passes to (b) MUST have JENKINS_SERVER_COOKIE="ignore" added to it; Jenkins uses this flag to find the processes it has launched in order to kill them, so you must set this "cookie" to "ignore" to stop Jenkins killing (b),
(b) connects back to (a) via the socket or pipe,
(a) remains running for as long as (b) is connected to the socket or pipe but also lets itself be killed by CTRL-C/SIGTERM,
(b) then launches the thing you actually want to run,
when (a) is terminated by a Jenkins abort (b) notices (because the socket or pipe will close) and performs a controlled shut-down of the thing you wanted to run before (b) exits,
separately, make a thing, let's call it (c), which checks whether the socket/named-pipe is present: if it is then (b) hasn't terminated yet,
in Jenkinsfile, wrap the calling of (a) in a try()/catch()/finally() and call (c) from the finally(), hence ensuring that the Jenkins pipeline only finishes when (b) has terminated (you might want to add a guard timer for safety).

Quite a thing, and all for the lack of what would be a relatively simple API in Jenkins.
Jenkinsfile and allowing an aborted `bat()` command time to clean up