Skip to content

Commit c24e6dd

Browse files
committed
1. Move dependency on setuptools closer to where it's needed 2. Scope multiprocessing Pool so a pool left open doesn't hang the process 3. Move run_setup_py to its own file so the subprocess doesn't need to import much
1 parent 7c8df83 commit c24e6dd

File tree

2 files changed

+54
-56
lines changed

2 files changed

+54
-56
lines changed

colcon_python_setup_py/package_identification/python_setup_py.py

Lines changed: 13 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@
88
import os
99
from pathlib import Path
1010
import runpy
11-
try:
12-
import setuptools
13-
except ImportError:
14-
pass
1511
import subprocess
1612
import sys
1713
from threading import Lock
@@ -25,6 +21,7 @@
2521
create_dependency_descriptor
2622
from colcon_core.plugin_system import satisfies_version
2723

24+
from .run_setup_py import run_setup_py
2825

2926
class PythonPackageIdentification(PackageIdentificationExtensionPoint):
3027
"""Identify Python packages with `setup.py` files."""
@@ -98,6 +95,7 @@ def get_setup_arguments(setup_py):
9895
'colcon_python_setup_py.package_identification.python_setup_py.'
9996
'get_setup_information() instead',
10097
stacklevel=2)
98+
import setuptools
10199
global cwd_lock
102100
if not cwd_lock:
103101
cwd_lock = Lock()
@@ -237,9 +235,6 @@ def get_setup_arguments_with_context(setup_py, env):
237235
return ast.literal_eval(output)
238236

239237

240-
_process_pool = multiprocessing.Pool()
241-
242-
243238
def get_setup_information(setup_py: Path, *, env: Mapping[str, str]):
244239
"""
245240
Dry run the setup.py file and get the configuration information.
@@ -250,56 +245,18 @@ def get_setup_information(setup_py: Path, *, env: Mapping[str, str]):
250245
:raise: RuntimeError if the setup script encountered an error
251246
"""
252247
try:
253-
return _process_pool.apply(
254-
run_setup_py,
255-
kwds={
256-
'cwd': os.path.abspath(str(setup_py.parent)),
257-
# might be os.environ, which is not picklable
258-
'env': dict(env),
259-
'script_args': ('--dry-run',),
260-
'stop_after': 'config'
261-
}
262-
)
248+
with multiprocessing.Pool(1) as _process_pool:
249+
return _process_pool.apply(
250+
run_setup_py,
251+
kwds={
252+
'cwd': os.path.abspath(str(setup_py.parent)),
253+
# might be os.environ, which is not picklable
254+
'env': dict(env),
255+
'script_args': ('--dry-run',),
256+
'stop_after': 'config'
257+
}
258+
)
263259
except Exception as e:
264260
raise RuntimeError(
265261
"Failed to dry run setup script '{setup_py}': "
266262
.format_map(locals()) + traceback.format_exc()) from e
267-
268-
269-
def run_setup_py(cwd, env, script_args=(), stop_after='run'):
270-
"""
271-
Modify the current process and run setup.py.
272-
273-
This should be run in a subprocess to not affect the state of the current
274-
process.
275-
276-
:param str cwd: absolute path to a directory containing a setup.py script
277-
:param dict env: environment variables to set before running setup.py
278-
:param script_args: command-line arguments to pass to setup.py
279-
:param stop_after: tells setup() when to stop processing
280-
:returns: the public properties of a Distribution object, minus objects
281-
with are generally not picklable
282-
"""
283-
# need to be in setup.py's parent dir to detect any setup.cfg
284-
os.chdir(cwd)
285-
286-
os.environ.clear()
287-
os.environ.update(env)
288-
289-
result = distutils.core.run_setup(
290-
'setup.py', script_args=script_args, stop_after=stop_after)
291-
292-
return {
293-
key: value for key, value in result.__dict__.items()
294-
if (
295-
# Private properties
296-
not key.startswith('_') and
297-
# Getter methods
298-
not callable(value) and
299-
# Objects that are generally not picklable
300-
key not in ('cmdclass', 'distclass', 'ext_modules') and
301-
# These *seem* useful but always have the value 0.
302-
# Look for their values in the 'metadata' object instead.
303-
key not in result.display_option_names
304-
)
305-
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import distutils.core
2+
import os
3+
4+
5+
def run_setup_py(cwd, env, script_args=(), stop_after='run'):
6+
"""
7+
Modify the current process and run setup.py.
8+
9+
This should be run in a subprocess to not affect the state of the current
10+
process.
11+
12+
:param str cwd: absolute path to a directory containing a setup.py script
13+
:param dict env: environment variables to set before running setup.py
14+
:param script_args: command-line arguments to pass to setup.py
15+
:param stop_after: tells setup() when to stop processing
16+
:returns: the public properties of a Distribution object, minus objects
17+
with are generally not picklable
18+
"""
19+
# need to be in setup.py's parent dir to detect any setup.cfg
20+
os.chdir(cwd)
21+
22+
os.environ.clear()
23+
os.environ.update(env)
24+
25+
result = distutils.core.run_setup(
26+
'setup.py', script_args=script_args, stop_after=stop_after)
27+
28+
return {
29+
key: value for key, value in result.__dict__.items()
30+
if (
31+
# Private properties
32+
not key.startswith('_') and
33+
# Getter methods
34+
not callable(value) and
35+
# Objects that are generally not picklable
36+
key not in ('cmdclass', 'distclass', 'ext_modules') and
37+
# These *seem* useful but always have the value 0.
38+
# Look for their values in the 'metadata' object instead.
39+
key not in result.display_option_names
40+
)
41+
}

0 commit comments

Comments
 (0)