Why does the following start()
method of multiprocessing.Process
not work when targeting the method Flask.run()
and an AttributeError
is raised when initialising Flask
:
if __name__ == "__main__":
Process(target=app.run,
kwargs=dict(host="0.0.0.0",
port=5002,
ssl_context=('./cert/cert.pem', './cert/cert-key.pem'),
debug=False)
).start()
app.run(host="0.0.0.0", port=5001)
with the following error/Traceback:
Traceback (most recent call last):
File "projectDir/main.py", line 80, in <module>
Process(target=app.run,
File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/multiprocessing/process.py", line 121, in start
self._popen = self._Popen(self)
File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/multiprocessing/context.py", line 224, in _Popen
return _default_context.get_context().Process._Popen(process_obj)
File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/multiprocessing/context.py", line 284, in _Popen
return Popen(process_obj)
File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/multiprocessing/popen_spawn_posix.py", line 32, in __init__
super().__init__(process_obj)
File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/multiprocessing/popen_fork.py", line 19, in __init__
self._launch(process_obj)
File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/multiprocessing/popen_spawn_posix.py", line 47, in _launch
reduction.dump(process_obj, fp)
File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/multiprocessing/reduction.py", line 60, in dump
ForkingPickler(file, protocol).dump(obj)
AttributeError: Can't pickle local object 'Flask.__init__.<locals>.<lambda>'
But referencing the Flask.run()
in a function, and passing that function under target=
parameter does work:
def run_https(**kwargs):
app.run(**kwargs)
if __name__ == "__main__":
Process(target=run_https,
kwargs=dict(host = "0.0.0.0",
port=5002,
ssl_context=('./cert/cert.pem', './cert/cert-key.pem'),
debug=False)
).start()
app.run(host="0.0.0.0", port=5001)
More context:
I am trying to encrypt the communication with a local SocketServer using flask_restful
, but because certificates management might take a while on all client devices, I have decided to run two Flask()
instances on two processes and two ports, one for http and another for https
Thanks in advance!
Since your child process's target is app.run
, Python needs to send the entire app
object to the new address space by serializing it with pickle
and then de-serializing the result in the new process's address space. Unfortunately, the app
object cannot be serialized. There can be any number of reasons but it comes down to it having one or more attributes that cannot be serialized because the nature of that attribute is such that it only is valid in the main process's address space. For example, the object's implementation might include a reference to an absolute memory location or file descriptor, which would only be valid in the address space that has created the object. However, if this same code were run in a platform that uses OS fork to create new processes, then pickle
would not be used and the program would run. This is because the new child's address space inherits all the memory (read only/copy on write) and open file descriptors. See below as an example:
def worker(f):
print(f.read())
if __name__ == '__main__':
from multiprocessing import Process
with open('test.txt', 'r') as f:
print(type(f))
Process(target=worker, args=(f,)).start()
Prints:
<class '_io.TextIOWrapper'>
Traceback (most recent call last):
File "C:\Booboo\test\test.py", line 8, in <module>
Process(target=worker, args=(f,)).start()
File "C:\Program Files\Python38\lib\multiprocessing\process.py", line 121, in start
self._popen = self._Popen(self)
File "C:\Program Files\Python38\lib\multiprocessing\context.py", line 224, in _Popen
return _default_context.get_context().Process._Popen(process_obj)
File "C:\Program Files\Python38\lib\multiprocessing\context.py", line 327, in _Popen
return Popen(process_obj)
File "C:\Program Files\Python38\lib\multiprocessing\popen_spawn_win32.py", line 93, in __init__
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\Program Files\Python38\lib\multiprocessing\spawn.py", line 116, in spawn_main
reduction.dump(process_obj, to_child)
File "C:\Program Files\Python38\lib\multiprocessing\reduction.py", line 60, in dump
exitcode = _main(fd, parent_sentinel)
File "C:\Program Files\Python38\lib\multiprocessing\spawn.py", line 126, in _main
self = reduction.pickle.load(from_parent)
EOFError: Ran out of input
ForkingPickler(file, protocol).dump(obj)
TypeError: cannot pickle '_io.TextIOWrapper' object