Skip to content

Commit a8338ec

Browse files
committed
Faster subprocesses by not using Process Pool
1 parent d8d25a0 commit a8338ec

File tree

2 files changed

+57
-13
lines changed

2 files changed

+57
-13
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import functools
2+
import traceback
3+
4+
5+
class OutOfProcessError(RuntimeError):
6+
"""An exception raised from a different process"""
7+
8+
def __init__(self, description):
9+
self.description = description
10+
11+
def __str__(self):
12+
return self.description
13+
14+
15+
def out_of_process(fn):
16+
"""Decorator to wrap a function call in a subprocess"""
17+
18+
@functools.wraps(fn)
19+
def wrapper(*args, **kwargs):
20+
import multiprocessing
21+
22+
parent_conn, child_conn = multiprocessing.Pipe(duplex=False)
23+
24+
def target():
25+
with child_conn:
26+
try:
27+
result = fn(*args, **kwargs)
28+
child_conn.send((True, result))
29+
except BaseException:
30+
child_conn.send((False, traceback.format_exc()))
31+
32+
with parent_conn:
33+
p = multiprocessing.Process(
34+
target=target,
35+
daemon=True, name='out_of_process ' + fn.__name__
36+
)
37+
try:
38+
p.start()
39+
ok, value = parent_conn.recv()
40+
if ok:
41+
return value
42+
else:
43+
raise OutOfProcessError(value)
44+
finally:
45+
p.terminate()
46+
47+
return wrapper

colcon_python_setup_py/package_identification/python_setup_py.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import ast
66
import distutils.core
7-
import multiprocessing
87
import os
98
from pathlib import Path
109
import runpy
@@ -20,6 +19,7 @@
2019
from colcon_core.package_identification.python import \
2120
create_dependency_descriptor
2221
from colcon_core.plugin_system import satisfies_version
22+
from .out_of_process import out_of_process
2323

2424
from .run_setup_py import run_setup_py
2525

@@ -245,19 +245,16 @@ def get_setup_information(setup_py: Path, *, env: Mapping[str, str]):
245245
:return: dictionary of data describing the package.
246246
:raise: RuntimeError if the setup script encountered an error
247247
"""
248+
setup_in_subprocess = out_of_process(run_setup_py)
248249
try:
249-
with multiprocessing.Pool(1) as _process_pool:
250-
return _process_pool.apply(
251-
run_setup_py,
252-
kwds={
253-
'cwd': os.path.abspath(str(setup_py.parent)),
254-
# might be os.environ, which is not picklable
255-
'env': dict(env),
256-
'script_args': ('--dry-run',),
257-
'stop_after': 'config'
258-
}
259-
)
250+
setup_in_subprocess(
251+
cwd=os.path.abspath(str(setup_py.parent)),
252+
# might be os.environ, which is not picklable
253+
env=dict(env),
254+
script_args=('--dry-run',),
255+
stop_after='config'
256+
)
260257
except Exception as e:
261258
raise RuntimeError(
262259
"Failed to dry run setup script '{setup_py}': "
263-
.format_map(locals()) + traceback.format_exc()) from e
260+
.format_map(locals()) + traceback.format_exc()) from e

0 commit comments

Comments
 (0)