A suggested solution
Tracing the deep call stack of multiprocessing.Process() I found the apparent necessity for the guard comes from a deliberate design choice in the multiprocessing module: it is hard-wired to initially spawn the main script for every process. It doesn't have to! The fix is to monkey-patch the start() method of the mp.Process class so that it simply spawns some other script dedicated to the child process. This avoids the deadly recursion and no functionality is lost, except for that each child process code has to be put into a separate module.
Even though this solution has already been battle-tested by controlling real hardware in several Windows/Linux lab setups for over a year, I will be grateful if you double-checked this approach and gave me your comments. Probably a cleaner solution can be found.
Some code - minimum working example
This is a basic script running a (patched) second process; similar syntax can also be used in a pythonic module.
#!/usr/bin/python3
#-*- coding: utf-8 -*-
import worker_module # All second process code has to be in a separate module
# No guard necessary here.
p = worker_module.PatchedProcess(target=worker_module.worker_func, args=('bob',))
p.start()
p.join() # wait for the process finishing to get printouts right
And here is the worker_module.py with the monkey-patching trick along with all code to be ran in the separate process:
#!/usr/bin/python3
#-*- coding: utf-8 -*-
import multiprocessing as mp
class PatchedProcess(mp.Process):
def start(self, *args):
import sys
bkup_main = sys.modules['__main__'] ## just in case
sys.modules['__main__'] = __import__('worker_module') ## this must match this module's name
super().start(*args)
sys.modules['__main__'] = bkup_main ## just in case
def worker_func(parameter):
print('USER FUNCTION ', parameter*3)
Running python3 hacked_example.py yields as expected:
USER FUNCTION bobbobbob