# HG changeset patch # User Sebastien Jodogne # Date 1623397095 -7200 # Node ID 6e02cd89eb6ae64a4990e719fe862fd29bfd0234 # Parent f093160dd7f453232f55c9ec77475e49b5bf8f26 moving python samples in separate files diff -r f093160dd7f4 -r 6e02cd89eb6a Sphinx/source/plugins/python.rst --- a/Sphinx/source/plugins/python.rst Fri Jun 11 09:30:28 2021 +0200 +++ b/Sphinx/source/plugins/python.rst Fri Jun 11 09:38:15 2021 +0200 @@ -1018,31 +1018,11 @@ Using slave processes ..................... -.. highlight:: python - Let us consider the following sample Python script that makes a -CPU-intensive computation on a REST callback:: - - import math - import orthanc - import time +CPU-intensive computation on a REST callback: - # CPU-intensive computation taking about 4 seconds - def SlowComputation(): - start = time.time() - for i in range(1000): - for j in range(30000): - math.sqrt(float(j)) - end = time.time() - duration = (end - start) - return 'computation done in %.03f seconds\n' % duration - - def OnRest(output, uri, **request): - answer = SlowComputation() - output.AnswerBuffer(answer, 'text/plain') - - orthanc.RegisterRestCallback('/computation', OnRest) - +.. literalinclude:: python/multiprocessing-1.py + :language: python .. highlight:: text @@ -1074,8 +1054,6 @@ the GIL only applies to the Python script: The baseline REST API of Orthanc is not affected by the GIL. -.. highlight:: python - The solution is to use the `multiprocessing primitives `__ of Python. The "master" Python interpreter that is initially started by the @@ -1084,39 +1062,10 @@ processes running a separate Python interpreter. This allows to offload intensive computations from the "master" Python interpreter of Orthanc onto those "slave" interpreters. The ``multiprocessing`` -library is actually quite straightforward to use:: - - import math - import multiprocessing - import orthanc - import signal - import time +library is actually quite straightforward to use: - # CPU-intensive computation taking about 4 seconds - # (same code as above) - def SlowComputation(): - start = time.time() - for i in range(1000): - for j in range(30000): - math.sqrt(float(j)) - end = time.time() - duration = (end - start) - return 'computation done in %.03f seconds\n' % duration - - # Ignore CTRL+C in the slave processes - def Initializer(): - signal.signal(signal.SIGINT, signal.SIG_IGN) - - # Create a pool of 4 slave Python interpreters - POOL = multiprocessing.Pool(4, initializer = Initializer) - - def OnRest(output, uri, **request): - # Offload the call to "SlowComputation" onto one slave process. - # The GIL is unlocked until the slave sends its answer back. - answer = POOL.apply(SlowComputation) - output.AnswerBuffer(answer, 'text/plain') - - orthanc.RegisterRestCallback('/computation', OnRest) +.. literalinclude:: python/multiprocessing-2.py + :language: python .. highlight:: text @@ -1157,22 +1106,11 @@ You must write your Python plugin so as that all the calls to ``orthanc`` are moved from the slaves process to the master process. For instance, here is how you would parse a DICOM file in a -slave process:: - - import pydicom - import io +slave process: - def OffloadedDicomParsing(dicom): - # No access to the "orthanc" library here, as we are in the slave process - dataset = pydicom.dcmread(io.BytesIO(dicom)) - return str(dataset) +.. literalinclude:: python/multiprocessing-3.py + :language: python - def OnRest(output, uri, **request): - # The call to "orthanc.RestApiGet()" is only possible in the master process - dicom = orthanc.RestApiGet('/instances/19816330-cb02e1cf-df3a8fe8-bf510623-ccefe9f5/file') - answer = POOL.apply(OffloadedDicomParsing, args = (dicom, )) - output.AnswerBuffer(answer, 'text/plain') - Communication primitives such as ``multiprocessing.Queue`` are available to exchange messages from the "slave" Python interpreters to the "master" Python interpreter for more advanced scenarios. @@ -1184,28 +1122,7 @@ the REST API of Orthanc (without have to set credentials in your plugin). Any HTTP client library for Python, such as `requests `__, can then be used to -access the REST API of Orthanc. Here is a minimal example:: +access the REST API of Orthanc. Here is a minimal example: - import json - import multiprocessing - import orthanc - import requests - import signal - - TOKEN = orthanc.GenerateRestApiAuthorizationToken() - - def SlaveProcess(): - r = requests.get('http://localhost:8042/instances', - headers = { 'Authorization' : TOKEN }) - return json.dumps(r.json()) - - def Initializer(): - signal.signal(signal.SIGINT, signal.SIG_IGN) - - POOL = multiprocessing.Pool(4, initializer = Initializer) - - def OnRest(output, uri, **request): - answer = POOL.apply(SlaveProcess) - output.AnswerBuffer(answer, 'text/plain') - - orthanc.RegisterRestCallback('/computation', OnRest) +.. literalinclude:: python/multiprocessing-4.py + :language: python diff -r f093160dd7f4 -r 6e02cd89eb6a Sphinx/source/plugins/python/multiprocessing-1.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sphinx/source/plugins/python/multiprocessing-1.py Fri Jun 11 09:38:15 2021 +0200 @@ -0,0 +1,19 @@ +import math +import orthanc +import time + +# CPU-intensive computation taking about 4 seconds +def SlowComputation(): + start = time.time() + for i in range(1000): + for j in range(30000): + math.sqrt(float(j)) + end = time.time() + duration = (end - start) + return 'computation done in %.03f seconds\n' % duration + +def OnRest(output, uri, **request): + answer = SlowComputation() + output.AnswerBuffer(answer, 'text/plain') + +orthanc.RegisterRestCallback('/computation', OnRest) diff -r f093160dd7f4 -r 6e02cd89eb6a Sphinx/source/plugins/python/multiprocessing-2.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sphinx/source/plugins/python/multiprocessing-2.py Fri Jun 11 09:38:15 2021 +0200 @@ -0,0 +1,31 @@ +import math +import multiprocessing +import orthanc +import signal +import time + +# CPU-intensive computation taking about 4 seconds +# (same code as above) +def SlowComputation(): + start = time.time() + for i in range(1000): + for j in range(30000): + math.sqrt(float(j)) + end = time.time() + duration = (end - start) + return 'computation done in %.03f seconds\n' % duration + +# Ignore CTRL+C in the slave processes +def Initializer(): + signal.signal(signal.SIGINT, signal.SIG_IGN) + +# Create a pool of 4 slave Python interpreters +POOL = multiprocessing.Pool(4, initializer = Initializer) + +def OnRest(output, uri, **request): + # Offload the call to "SlowComputation" onto one slave process. + # The GIL is unlocked until the slave sends its answer back. + answer = POOL.apply(SlowComputation) + output.AnswerBuffer(answer, 'text/plain') + +orthanc.RegisterRestCallback('/computation', OnRest) diff -r f093160dd7f4 -r 6e02cd89eb6a Sphinx/source/plugins/python/multiprocessing-3.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sphinx/source/plugins/python/multiprocessing-3.py Fri Jun 11 09:38:15 2021 +0200 @@ -0,0 +1,13 @@ +import pydicom +import io + +def OffloadedDicomParsing(dicom): + # No access to the "orthanc" library here, as we are in the slave process + dataset = pydicom.dcmread(io.BytesIO(dicom)) + return str(dataset) + +def OnRest(output, uri, **request): + # The call to "orthanc.RestApiGet()" is only possible in the master process + dicom = orthanc.RestApiGet('/instances/19816330-cb02e1cf-df3a8fe8-bf510623-ccefe9f5/file') + answer = POOL.apply(OffloadedDicomParsing, args = (dicom, )) + output.AnswerBuffer(answer, 'text/plain') diff -r f093160dd7f4 -r 6e02cd89eb6a Sphinx/source/plugins/python/multiprocessing-4.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sphinx/source/plugins/python/multiprocessing-4.py Fri Jun 11 09:38:15 2021 +0200 @@ -0,0 +1,23 @@ +import json +import multiprocessing +import orthanc +import requests +import signal + +TOKEN = orthanc.GenerateRestApiAuthorizationToken() + +def SlaveProcess(): + r = requests.get('http://localhost:8042/instances', + headers = { 'Authorization' : TOKEN }) + return json.dumps(r.json()) + +def Initializer(): + signal.signal(signal.SIGINT, signal.SIG_IGN) + +POOL = multiprocessing.Pool(4, initializer = Initializer) + +def OnRest(output, uri, **request): + answer = POOL.apply(SlaveProcess) + output.AnswerBuffer(answer, 'text/plain') + +orthanc.RegisterRestCallback('/computation', OnRest)