We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Just importing gammapy raises a warning about ray support being experimental
**Gammapy version**

```
Gammapy support for parallelisation with ray is still a prototype and is not fully functional.

System:

	python_executable      : /home/maxnoe/.local/conda/envs/gammapy-dev/bin/python3.9 
	python_version         : 3.9.16     
	machine                : x86_64     
	system                 : Linux      


Gammapy package:

	version                : 1.2.dev201+g514451881.d20230627 
	path                   : /home/maxnoe/Projects/gammapy/gammapy 


Other packages:

	numpy                  : 1.25.0     
	scipy                  : 1.11.0     
	astropy                : 5.3        
	regions                : 0.7        
	click                  : 8.1.3      
	yaml                   : 6.0        
	IPython                : 8.14.0     
	jupyterlab             : 3.5.3      
	matplotlib             : 3.7.1      
	pandas                 : 2.0.2      
	healpy                 : 1.16.2     
	iminuit                : 2.22.0     
	sherpa                 : 4.15.1     
	naima                  : 0.10.0     
	emcee                  : 3.1.4      
	corner                 : 2.2.2      
	ray                    : 2.5.1      


Gammapy environment variables:

	GAMMAPY_DATA           : /home/maxnoe/Projects/gammapy/gammapy-datasets/dev 
```

**Bug description**

Just importing a subpackage of gammapy, without doing anything else, raises a warning about ray support being experimental.

I am not doing anything with ray, I just setup the dev environment and imported things:

```
❯ python -c 'import gammapy.datasets'
Gammapy support for parallelisation with ray is still a prototype and is not fully functional.
❯ python -c 'import gammapy.makers'
Gammapy support for parallelisation with ray is still a prototype and is not fully functional.
```

**Expected behavior**

No warnings about things I don't actually use.


**To Reproduce**
See above, dev environment and the imports.

**Other information**



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `gammapy/utils/parallel.py`
Content:
```
1 # Licensed under a 3-clause BSD style license - see LICENSE.rst
2 """Multiprocessing and multithreading setup"""
3 import importlib
4 import logging
5 import multiprocessing
6 from enum import Enum
7 from gammapy.utils.pbar import progress_bar
8 
9 log = logging.getLogger(__name__)
10 
11 
12 class ParallelBackendEnum(Enum):
13     """Enum for parallel backend"""
14 
15     multiprocessing = "multiprocessing"
16     ray = "ray"
17 
18     @classmethod
19     def from_str(cls, value):
20         """Get enum from string"""
21         if value is None:
22             value = BACKEND_DEFAULT
23 
24         if value == "ray" and not is_ray_available():
25             log.warning("Ray is not installed, falling back to multiprocessing backend")
26             value = "multiprocessing"
27 
28         return cls(value)
29 
30 
31 class PoolMethodEnum(Enum):
32     """Enum for pool method"""
33 
34     starmap = "starmap"
35     apply_async = "apply_async"
36 
37 
38 BACKEND_DEFAULT = ParallelBackendEnum.multiprocessing
39 N_JOBS_DEFAULT = 1
40 
41 
42 def get_multiprocessing_ray():
43     """Get multiprocessing module for ray backend"""
44     import ray.util.multiprocessing as multiprocessing
45 
46     log.warning(
47         "Gammapy support for parallelisation with ray is still a prototype and is not fully functional."
48     )
49     return multiprocessing
50 
51 
52 def is_ray_initialized():
53     """Check if ray is initialized"""
54     try:
55         from ray import is_initialized
56 
57         return is_initialized()
58     except ModuleNotFoundError:
59         return False
60 
61 
62 def is_ray_available():
63     """Check if ray is available"""
64     try:
65         importlib.import_module("ray")
66         return True
67     except ModuleNotFoundError:
68         return False
69 
70 
71 class ParallelMixin:
72     """Mixin class to handle parallel processing"""
73 
74     @property
75     def n_jobs(self):
76         """Number of jobs (int)"""
77         # TODO: this is somewhat unusual behaviour. It deviates from a normal default value handling
78         if self._n_jobs is None:
79             return N_JOBS_DEFAULT
80 
81         return self._n_jobs
82 
83     @n_jobs.setter
84     def n_jobs(self, value):
85         """Number of jobs setter (int)"""
86         if not isinstance(value, (int, type(None))):
87             raise ValueError(
88                 f"Invalid type: {value!r}, and integer or None is expected."
89             )
90 
91         self._n_jobs = value
92 
93     @property
94     def parallel_backend(self):
95         """Parallel backend (str)"""
96         if self._parallel_backend is None:
97             return BACKEND_DEFAULT
98 
99         return self._parallel_backend
100 
101     @parallel_backend.setter
102     def parallel_backend(self, value):
103         """Parallel backend setter (str)"""
104         self._parallel_backend = ParallelBackendEnum.from_str(value).value
105 
106 
107 def run_multiprocessing(
108     func,
109     inputs,
110     backend=None,
111     pool_kwargs=None,
112     method="starmap",
113     method_kwargs=None,
114     task_name="",
115 ):
116     """Run function in a loop or in Parallel
117 
118     Notes
119     -----
120     The progress bar can be displayed for this function.
121 
122     Parameters
123     ----------
124     func : function
125         Function to run
126     inputs : list
127         List of arguments to pass to the function
128     backend : {'multiprocessing', 'ray'}
129         Backend to use.
130     pool_kwargs : dict
131         Keyword arguments passed to the pool. The number of processes is limited
132         to the number of physical CPUs.
133     method : {'starmap', 'apply_async'}
134         Pool method to use.
135     method_kwargs : dict
136         Keyword arguments passed to the method
137     task_name : str
138         Name of the task to display in the progress bar
139     """
140     backend = ParallelBackendEnum.from_str(backend)
141 
142     if method_kwargs is None:
143         method_kwargs = {}
144 
145     if pool_kwargs is None:
146         pool_kwargs = {}
147 
148     processes = pool_kwargs.get("processes", N_JOBS_DEFAULT)
149 
150     multiprocessing = PARALLEL_BACKEND_MODULES[backend]
151 
152     if backend == ParallelBackendEnum.multiprocessing:
153         cpu_count = multiprocessing.cpu_count()
154 
155         if processes > cpu_count:
156             log.info(f"Limiting number of processes from {processes} to {cpu_count}")
157             processes = cpu_count
158 
159         if multiprocessing.current_process().name != "MainProcess":
160             # subprocesses cannot have childs
161             processes = 1
162     # TODO: check for ray
163 
164     if processes == 1:
165         return run_loop(
166             func=func, inputs=inputs, method_kwargs=method_kwargs, task_name=task_name
167         )
168 
169     if backend == ParallelBackendEnum.ray:
170         address = "auto" if is_ray_initialized() else None
171         pool_kwargs.setdefault("ray_address", address)
172 
173     log.info(f"Using {processes} processes to compute {task_name}")
174 
175     with multiprocessing.Pool(**pool_kwargs) as pool:
176         pool_func = POOL_METHODS[PoolMethodEnum(method)]
177         results = pool_func(
178             pool=pool,
179             func=func,
180             inputs=inputs,
181             method_kwargs=method_kwargs,
182             task_name=task_name,
183         )
184 
185     return results
186 
187 
188 def run_loop(func, inputs, method_kwargs=None, task_name=""):
189     """Loop over inputs an run function"""
190     results = []
191 
192     callback = method_kwargs.get("callback", None)
193 
194     for arguments in progress_bar(inputs, desc=task_name):
195         result = func(*arguments)
196 
197         if callback is not None:
198             result = callback(result)
199 
200         results.append(result)
201 
202     return results
203 
204 
205 def run_pool_star_map(pool, func, inputs, method_kwargs=None, task_name=""):
206     """Run function in parallel"""
207     return pool.starmap(func, progress_bar(inputs, desc=task_name), **method_kwargs)
208 
209 
210 def run_pool_async(pool, func, inputs, method_kwargs=None, task_name=""):
211     """Run function in parallel async"""
212     results = []
213 
214     for arguments in progress_bar(inputs, desc=task_name):
215         result = pool.apply_async(func, arguments, **method_kwargs)
216         results.append(result)
217     # wait async run is done
218     [result.wait() for result in results]
219     return results
220 
221 
222 POOL_METHODS = {
223     PoolMethodEnum.starmap: run_pool_star_map,
224     PoolMethodEnum.apply_async: run_pool_async,
225 }
226 
227 PARALLEL_BACKEND_MODULES = {
228     ParallelBackendEnum.multiprocessing: multiprocessing,
229 }
230 
231 if is_ray_available():
232     PARALLEL_BACKEND_MODULES[ParallelBackendEnum.ray] = get_multiprocessing_ray()
233
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/gammapy/utils/parallel.py b/gammapy/utils/parallel.py
--- a/gammapy/utils/parallel.py
+++ b/gammapy/utils/parallel.py
@@ -2,7 +2,6 @@
 """Multiprocessing and multithreading setup"""
 import importlib
 import logging
-import multiprocessing
 from enum import Enum
 from gammapy.utils.pbar import progress_bar
 
@@ -39,6 +38,13 @@
 N_JOBS_DEFAULT = 1
 
 
+def get_multiprocessing():
+    """Get multiprocessing module"""
+    import multiprocessing
+
+    return multiprocessing
+
+
 def get_multiprocessing_ray():
     """Get multiprocessing module for ray backend"""
     import ray.util.multiprocessing as multiprocessing
@@ -147,7 +153,7 @@
 
     processes = pool_kwargs.get("processes", N_JOBS_DEFAULT)
 
-    multiprocessing = PARALLEL_BACKEND_MODULES[backend]
+    multiprocessing = PARALLEL_BACKEND_MODULES[backend]()
 
     if backend == ParallelBackendEnum.multiprocessing:
         cpu_count = multiprocessing.cpu_count()
@@ -225,8 +231,6 @@
 }
 
 PARALLEL_BACKEND_MODULES = {
-    ParallelBackendEnum.multiprocessing: multiprocessing,
+    ParallelBackendEnum.multiprocessing: get_multiprocessing,
+    ParallelBackendEnum.ray: get_multiprocessing_ray,
 }
-
-if is_ray_available():
-    PARALLEL_BACKEND_MODULES[ParallelBackendEnum.ray] = get_multiprocessing_ray()

{"golden_diff": "diff --git a/gammapy/utils/parallel.py b/gammapy/utils/parallel.py\n--- a/gammapy/utils/parallel.py\n+++ b/gammapy/utils/parallel.py\n@@ -2,7 +2,6 @@\n \"\"\"Multiprocessing and multithreading setup\"\"\"\n import importlib\n import logging\n-import multiprocessing\n from enum import Enum\n from gammapy.utils.pbar import progress_bar\n \n@@ -39,6 +38,13 @@\n N_JOBS_DEFAULT = 1\n \n \n+def get_multiprocessing():\n+    \"\"\"Get multiprocessing module\"\"\"\n+    import multiprocessing\n+\n+    return multiprocessing\n+\n+\n def get_multiprocessing_ray():\n     \"\"\"Get multiprocessing module for ray backend\"\"\"\n     import ray.util.multiprocessing as multiprocessing\n@@ -147,7 +153,7 @@\n \n     processes = pool_kwargs.get(\"processes\", N_JOBS_DEFAULT)\n \n-    multiprocessing = PARALLEL_BACKEND_MODULES[backend]\n+    multiprocessing = PARALLEL_BACKEND_MODULES[backend]()\n \n     if backend == ParallelBackendEnum.multiprocessing:\n         cpu_count = multiprocessing.cpu_count()\n@@ -225,8 +231,6 @@\n }\n \n PARALLEL_BACKEND_MODULES = {\n-    ParallelBackendEnum.multiprocessing: multiprocessing,\n+    ParallelBackendEnum.multiprocessing: get_multiprocessing,\n+    ParallelBackendEnum.ray: get_multiprocessing_ray,\n }\n-\n-if is_ray_available():\n-    PARALLEL_BACKEND_MODULES[ParallelBackendEnum.ray] = get_multiprocessing_ray()\n", "issue": "Just importing gammapy raises a warning about ray support being experimental\n**Gammapy version**\r\n\r\n```\r\nGammapy support for parallelisation with ray is still a prototype and is not fully functional.\r\n\r\nSystem:\r\n\r\n\tpython_executable      : /home/maxnoe/.local/conda/envs/gammapy-dev/bin/python3.9 \r\n\tpython_version         : 3.9.16     \r\n\tmachine                : x86_64     \r\n\tsystem                 : Linux      \r\n\r\n\r\nGammapy package:\r\n\r\n\tversion                : 1.2.dev201+g514451881.d20230627 \r\n\tpath                   : /home/maxnoe/Projects/gammapy/gammapy \r\n\r\n\r\nOther packages:\r\n\r\n\tnumpy                  : 1.25.0     \r\n\tscipy                  : 1.11.0     \r\n\tastropy                : 5.3        \r\n\tregions                : 0.7        \r\n\tclick                  : 8.1.3      \r\n\tyaml                   : 6.0        \r\n\tIPython                : 8.14.0     \r\n\tjupyterlab             : 3.5.3      \r\n\tmatplotlib             : 3.7.1      \r\n\tpandas                 : 2.0.2      \r\n\thealpy                 : 1.16.2     \r\n\timinuit                : 2.22.0     \r\n\tsherpa                 : 4.15.1     \r\n\tnaima                  : 0.10.0     \r\n\temcee                  : 3.1.4      \r\n\tcorner                 : 2.2.2      \r\n\tray                    : 2.5.1      \r\n\r\n\r\nGammapy environment variables:\r\n\r\n\tGAMMAPY_DATA           : /home/maxnoe/Projects/gammapy/gammapy-datasets/dev \r\n```\r\n\r\n**Bug description**\r\n\r\nJust importing a subpackage of gammapy, without doing anything else, raises a warning about ray support being experimental.\r\n\r\nI am not doing anything with ray, I just setup the dev environment and imported things:\r\n\r\n```\r\n\u276f python -c 'import gammapy.datasets'\r\nGammapy support for parallelisation with ray is still a prototype and is not fully functional.\r\n\u276f python -c 'import gammapy.makers'\r\nGammapy support for parallelisation with ray is still a prototype and is not fully functional.\r\n```\r\n\r\n**Expected behavior**\r\n\r\nNo warnings about things I don't actually use.\r\n\r\n\r\n**To Reproduce**\r\nSee above, dev environment and the imports.\r\n\r\n**Other information**\r\n\r\n\n", "before_files": [{"content": "# Licensed under a 3-clause BSD style license - see LICENSE.rst\n\"\"\"Multiprocessing and multithreading setup\"\"\"\nimport importlib\nimport logging\nimport multiprocessing\nfrom enum import Enum\nfrom gammapy.utils.pbar import progress_bar\n\nlog = logging.getLogger(__name__)\n\n\nclass ParallelBackendEnum(Enum):\n    \"\"\"Enum for parallel backend\"\"\"\n\n    multiprocessing = \"multiprocessing\"\n    ray = \"ray\"\n\n    @classmethod\n    def from_str(cls, value):\n        \"\"\"Get enum from string\"\"\"\n        if value is None:\n            value = BACKEND_DEFAULT\n\n        if value == \"ray\" and not is_ray_available():\n            log.warning(\"Ray is not installed, falling back to multiprocessing backend\")\n            value = \"multiprocessing\"\n\n        return cls(value)\n\n\nclass PoolMethodEnum(Enum):\n    \"\"\"Enum for pool method\"\"\"\n\n    starmap = \"starmap\"\n    apply_async = \"apply_async\"\n\n\nBACKEND_DEFAULT = ParallelBackendEnum.multiprocessing\nN_JOBS_DEFAULT = 1\n\n\ndef get_multiprocessing_ray():\n    \"\"\"Get multiprocessing module for ray backend\"\"\"\n    import ray.util.multiprocessing as multiprocessing\n\n    log.warning(\n        \"Gammapy support for parallelisation with ray is still a prototype and is not fully functional.\"\n    )\n    return multiprocessing\n\n\ndef is_ray_initialized():\n    \"\"\"Check if ray is initialized\"\"\"\n    try:\n        from ray import is_initialized\n\n        return is_initialized()\n    except ModuleNotFoundError:\n        return False\n\n\ndef is_ray_available():\n    \"\"\"Check if ray is available\"\"\"\n    try:\n        importlib.import_module(\"ray\")\n        return True\n    except ModuleNotFoundError:\n        return False\n\n\nclass ParallelMixin:\n    \"\"\"Mixin class to handle parallel processing\"\"\"\n\n    @property\n    def n_jobs(self):\n        \"\"\"Number of jobs (int)\"\"\"\n        # TODO: this is somewhat unusual behaviour. It deviates from a normal default value handling\n        if self._n_jobs is None:\n            return N_JOBS_DEFAULT\n\n        return self._n_jobs\n\n    @n_jobs.setter\n    def n_jobs(self, value):\n        \"\"\"Number of jobs setter (int)\"\"\"\n        if not isinstance(value, (int, type(None))):\n            raise ValueError(\n                f\"Invalid type: {value!r}, and integer or None is expected.\"\n            )\n\n        self._n_jobs = value\n\n    @property\n    def parallel_backend(self):\n        \"\"\"Parallel backend (str)\"\"\"\n        if self._parallel_backend is None:\n            return BACKEND_DEFAULT\n\n        return self._parallel_backend\n\n    @parallel_backend.setter\n    def parallel_backend(self, value):\n        \"\"\"Parallel backend setter (str)\"\"\"\n        self._parallel_backend = ParallelBackendEnum.from_str(value).value\n\n\ndef run_multiprocessing(\n    func,\n    inputs,\n    backend=None,\n    pool_kwargs=None,\n    method=\"starmap\",\n    method_kwargs=None,\n    task_name=\"\",\n):\n    \"\"\"Run function in a loop or in Parallel\n\n    Notes\n    -----\n    The progress bar can be displayed for this function.\n\n    Parameters\n    ----------\n    func : function\n        Function to run\n    inputs : list\n        List of arguments to pass to the function\n    backend : {'multiprocessing', 'ray'}\n        Backend to use.\n    pool_kwargs : dict\n        Keyword arguments passed to the pool. The number of processes is limited\n        to the number of physical CPUs.\n    method : {'starmap', 'apply_async'}\n        Pool method to use.\n    method_kwargs : dict\n        Keyword arguments passed to the method\n    task_name : str\n        Name of the task to display in the progress bar\n    \"\"\"\n    backend = ParallelBackendEnum.from_str(backend)\n\n    if method_kwargs is None:\n        method_kwargs = {}\n\n    if pool_kwargs is None:\n        pool_kwargs = {}\n\n    processes = pool_kwargs.get(\"processes\", N_JOBS_DEFAULT)\n\n    multiprocessing = PARALLEL_BACKEND_MODULES[backend]\n\n    if backend == ParallelBackendEnum.multiprocessing:\n        cpu_count = multiprocessing.cpu_count()\n\n        if processes > cpu_count:\n            log.info(f\"Limiting number of processes from {processes} to {cpu_count}\")\n            processes = cpu_count\n\n        if multiprocessing.current_process().name != \"MainProcess\":\n            # subprocesses cannot have childs\n            processes = 1\n    # TODO: check for ray\n\n    if processes == 1:\n        return run_loop(\n            func=func, inputs=inputs, method_kwargs=method_kwargs, task_name=task_name\n        )\n\n    if backend == ParallelBackendEnum.ray:\n        address = \"auto\" if is_ray_initialized() else None\n        pool_kwargs.setdefault(\"ray_address\", address)\n\n    log.info(f\"Using {processes} processes to compute {task_name}\")\n\n    with multiprocessing.Pool(**pool_kwargs) as pool:\n        pool_func = POOL_METHODS[PoolMethodEnum(method)]\n        results = pool_func(\n            pool=pool,\n            func=func,\n            inputs=inputs,\n            method_kwargs=method_kwargs,\n            task_name=task_name,\n        )\n\n    return results\n\n\ndef run_loop(func, inputs, method_kwargs=None, task_name=\"\"):\n    \"\"\"Loop over inputs an run function\"\"\"\n    results = []\n\n    callback = method_kwargs.get(\"callback\", None)\n\n    for arguments in progress_bar(inputs, desc=task_name):\n        result = func(*arguments)\n\n        if callback is not None:\n            result = callback(result)\n\n        results.append(result)\n\n    return results\n\n\ndef run_pool_star_map(pool, func, inputs, method_kwargs=None, task_name=\"\"):\n    \"\"\"Run function in parallel\"\"\"\n    return pool.starmap(func, progress_bar(inputs, desc=task_name), **method_kwargs)\n\n\ndef run_pool_async(pool, func, inputs, method_kwargs=None, task_name=\"\"):\n    \"\"\"Run function in parallel async\"\"\"\n    results = []\n\n    for arguments in progress_bar(inputs, desc=task_name):\n        result = pool.apply_async(func, arguments, **method_kwargs)\n        results.append(result)\n    # wait async run is done\n    [result.wait() for result in results]\n    return results\n\n\nPOOL_METHODS = {\n    PoolMethodEnum.starmap: run_pool_star_map,\n    PoolMethodEnum.apply_async: run_pool_async,\n}\n\nPARALLEL_BACKEND_MODULES = {\n    ParallelBackendEnum.multiprocessing: multiprocessing,\n}\n\nif is_ray_available():\n    PARALLEL_BACKEND_MODULES[ParallelBackendEnum.ray] = get_multiprocessing_ray()\n", "path": "gammapy/utils/parallel.py"}], "after_files": [{"content": "# Licensed under a 3-clause BSD style license - see LICENSE.rst\n\"\"\"Multiprocessing and multithreading setup\"\"\"\nimport importlib\nimport logging\nfrom enum import Enum\nfrom gammapy.utils.pbar import progress_bar\n\nlog = logging.getLogger(__name__)\n\n\nclass ParallelBackendEnum(Enum):\n    \"\"\"Enum for parallel backend\"\"\"\n\n    multiprocessing = \"multiprocessing\"\n    ray = \"ray\"\n\n    @classmethod\n    def from_str(cls, value):\n        \"\"\"Get enum from string\"\"\"\n        if value is None:\n            value = BACKEND_DEFAULT\n\n        if value == \"ray\" and not is_ray_available():\n            log.warning(\"Ray is not installed, falling back to multiprocessing backend\")\n            value = \"multiprocessing\"\n\n        return cls(value)\n\n\nclass PoolMethodEnum(Enum):\n    \"\"\"Enum for pool method\"\"\"\n\n    starmap = \"starmap\"\n    apply_async = \"apply_async\"\n\n\nBACKEND_DEFAULT = ParallelBackendEnum.multiprocessing\nN_JOBS_DEFAULT = 1\n\n\ndef get_multiprocessing():\n    \"\"\"Get multiprocessing module\"\"\"\n    import multiprocessing\n\n    return multiprocessing\n\n\ndef get_multiprocessing_ray():\n    \"\"\"Get multiprocessing module for ray backend\"\"\"\n    import ray.util.multiprocessing as multiprocessing\n\n    log.warning(\n        \"Gammapy support for parallelisation with ray is still a prototype and is not fully functional.\"\n    )\n    return multiprocessing\n\n\ndef is_ray_initialized():\n    \"\"\"Check if ray is initialized\"\"\"\n    try:\n        from ray import is_initialized\n\n        return is_initialized()\n    except ModuleNotFoundError:\n        return False\n\n\ndef is_ray_available():\n    \"\"\"Check if ray is available\"\"\"\n    try:\n        importlib.import_module(\"ray\")\n        return True\n    except ModuleNotFoundError:\n        return False\n\n\nclass ParallelMixin:\n    \"\"\"Mixin class to handle parallel processing\"\"\"\n\n    @property\n    def n_jobs(self):\n        \"\"\"Number of jobs (int)\"\"\"\n        # TODO: this is somewhat unusual behaviour. It deviates from a normal default value handling\n        if self._n_jobs is None:\n            return N_JOBS_DEFAULT\n\n        return self._n_jobs\n\n    @n_jobs.setter\n    def n_jobs(self, value):\n        \"\"\"Number of jobs setter (int)\"\"\"\n        if not isinstance(value, (int, type(None))):\n            raise ValueError(\n                f\"Invalid type: {value!r}, and integer or None is expected.\"\n            )\n\n        self._n_jobs = value\n\n    @property\n    def parallel_backend(self):\n        \"\"\"Parallel backend (str)\"\"\"\n        if self._parallel_backend is None:\n            return BACKEND_DEFAULT\n\n        return self._parallel_backend\n\n    @parallel_backend.setter\n    def parallel_backend(self, value):\n        \"\"\"Parallel backend setter (str)\"\"\"\n        self._parallel_backend = ParallelBackendEnum.from_str(value).value\n\n\ndef run_multiprocessing(\n    func,\n    inputs,\n    backend=None,\n    pool_kwargs=None,\n    method=\"starmap\",\n    method_kwargs=None,\n    task_name=\"\",\n):\n    \"\"\"Run function in a loop or in Parallel\n\n    Notes\n    -----\n    The progress bar can be displayed for this function.\n\n    Parameters\n    ----------\n    func : function\n        Function to run\n    inputs : list\n        List of arguments to pass to the function\n    backend : {'multiprocessing', 'ray'}\n        Backend to use.\n    pool_kwargs : dict\n        Keyword arguments passed to the pool. The number of processes is limited\n        to the number of physical CPUs.\n    method : {'starmap', 'apply_async'}\n        Pool method to use.\n    method_kwargs : dict\n        Keyword arguments passed to the method\n    task_name : str\n        Name of the task to display in the progress bar\n    \"\"\"\n    backend = ParallelBackendEnum.from_str(backend)\n\n    if method_kwargs is None:\n        method_kwargs = {}\n\n    if pool_kwargs is None:\n        pool_kwargs = {}\n\n    processes = pool_kwargs.get(\"processes\", N_JOBS_DEFAULT)\n\n    multiprocessing = PARALLEL_BACKEND_MODULES[backend]()\n\n    if backend == ParallelBackendEnum.multiprocessing:\n        cpu_count = multiprocessing.cpu_count()\n\n        if processes > cpu_count:\n            log.info(f\"Limiting number of processes from {processes} to {cpu_count}\")\n            processes = cpu_count\n\n        if multiprocessing.current_process().name != \"MainProcess\":\n            # subprocesses cannot have childs\n            processes = 1\n    # TODO: check for ray\n\n    if processes == 1:\n        return run_loop(\n            func=func, inputs=inputs, method_kwargs=method_kwargs, task_name=task_name\n        )\n\n    if backend == ParallelBackendEnum.ray:\n        address = \"auto\" if is_ray_initialized() else None\n        pool_kwargs.setdefault(\"ray_address\", address)\n\n    log.info(f\"Using {processes} processes to compute {task_name}\")\n\n    with multiprocessing.Pool(**pool_kwargs) as pool:\n        pool_func = POOL_METHODS[PoolMethodEnum(method)]\n        results = pool_func(\n            pool=pool,\n            func=func,\n            inputs=inputs,\n            method_kwargs=method_kwargs,\n            task_name=task_name,\n        )\n\n    return results\n\n\ndef run_loop(func, inputs, method_kwargs=None, task_name=\"\"):\n    \"\"\"Loop over inputs an run function\"\"\"\n    results = []\n\n    callback = method_kwargs.get(\"callback\", None)\n\n    for arguments in progress_bar(inputs, desc=task_name):\n        result = func(*arguments)\n\n        if callback is not None:\n            result = callback(result)\n\n        results.append(result)\n\n    return results\n\n\ndef run_pool_star_map(pool, func, inputs, method_kwargs=None, task_name=\"\"):\n    \"\"\"Run function in parallel\"\"\"\n    return pool.starmap(func, progress_bar(inputs, desc=task_name), **method_kwargs)\n\n\ndef run_pool_async(pool, func, inputs, method_kwargs=None, task_name=\"\"):\n    \"\"\"Run function in parallel async\"\"\"\n    results = []\n\n    for arguments in progress_bar(inputs, desc=task_name):\n        result = pool.apply_async(func, arguments, **method_kwargs)\n        results.append(result)\n    # wait async run is done\n    [result.wait() for result in results]\n    return results\n\n\nPOOL_METHODS = {\n    PoolMethodEnum.starmap: run_pool_star_map,\n    PoolMethodEnum.apply_async: run_pool_async,\n}\n\nPARALLEL_BACKEND_MODULES = {\n    ParallelBackendEnum.multiprocessing: get_multiprocessing,\n    ParallelBackendEnum.ray: get_multiprocessing_ray,\n}\n", "path": "gammapy/utils/parallel.py"}]}