Skip to content

Commit 1109c38

Browse files
committed
Throw exceptions more agressively
Also, preserve stack trace from setup.py exceptions
1 parent 9c29b5a commit 1109c38

File tree

2 files changed

+39
-43
lines changed

2 files changed

+39
-43
lines changed

colcon_core/package_identification/python.py

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

55
import multiprocessing
66
import os
7+
import traceback
78
from typing import Optional
89

910
from colcon_core.dependency_descriptor import DependencyDescriptor
@@ -32,24 +33,22 @@ def identify(self, desc): # noqa: D102
3233
if not setup_py.is_file():
3334
return
3435

35-
try:
36-
config = get_setup_result(setup_py, env=None)
37-
except Exception as e:
38-
logger.warning(e)
39-
return
36+
# after this point, we are convinced this is a Python package,
37+
# so we should fail with an Exception instead of silently
4038

41-
if 'metadata' not in config:
42-
return
39+
config = get_setup_result(setup_py, env=None)
4340

4441
name = config['metadata'].name
4542
if not name:
46-
return
43+
raise RuntimeError(
44+
'Setup script "{}" returned invalid name {!r}'
45+
.format(setup_py, name))
4746

4847
desc.type = 'python'
4948
if desc.name is not None and desc.name != name:
50-
msg = 'Package name already set to different value'
51-
logger.error(msg)
52-
raise RuntimeError(msg)
49+
raise RuntimeError(
50+
'Setup script "{}" returned different name {!r} != {!r}'
51+
.format(setup_py, name, desc.name))
5352
desc.name = name
5453

5554
desc.metadata['version'] = config['metadata'].version
@@ -79,29 +78,25 @@ def get_setup_result(setup_py, *, env: Optional[dict]):
7978
8079
:param setup_py: Path to a setup.py script
8180
:param env: Environment variables to set before running setup.py
82-
:return: None or Dictionary of data describing the package.
81+
:return: Dictionary of data describing the package.
82+
:raise: RuntimeError if the setup script encountered an error
8383
"""
8484
conn_recv, conn_send = multiprocessing.Pipe(duplex=False)
85-
p = multiprocessing.Process(
86-
target=_get_setup_result_target,
87-
args=(os.path.abspath(str(setup_py)), env, conn_send)
88-
)
89-
p.start()
90-
p.join()
91-
92-
if not conn_recv.poll():
93-
raise RuntimeError(
94-
'Python setup script "{}" did not return any data'
95-
.format(setup_py))
96-
97-
result_or_error = conn_recv.recv()
98-
conn_recv.close()
99-
100-
if isinstance(result_or_error, dict):
101-
return result_or_error
85+
with conn_send:
86+
p = multiprocessing.Process(
87+
target=_get_setup_result_target,
88+
args=(os.path.abspath(str(setup_py)), env, conn_send)
89+
)
90+
p.start()
91+
p.join()
92+
with conn_recv:
93+
result_or_exc_info = conn_recv.recv()
94+
95+
if isinstance(result_or_exc_info, dict):
96+
return result_or_exc_info
10297
raise RuntimeError(
103-
'Python setup script "{}" failed: {!r}'
104-
.format(setup_py, result_or_error)) from result_or_error
98+
'Failure when trying to run setup script {}:\n{}'
99+
.format(setup_py, traceback.format_exception(*result_or_exc_info)))
105100

106101

107102
def _get_setup_result_target(setup_py: str, env: Optional[dict], conn_send):
@@ -113,12 +108,12 @@ def _get_setup_result_target(setup_py: str, env: Optional[dict], conn_send):
113108
114109
:param setup_py: Absolute path to a setup.py script
115110
:param env: Environment variables to set before running setup.py
116-
:param conn_send: Connection to send the result as either a dict or an
117-
Exception.
111+
:param conn_send: Connection to send the result as either a dict or the
112+
result of sys.exc_info
118113
"""
114+
import distutils.core
115+
import sys
119116
try:
120-
import distutils.core
121-
122117
# need to be in setup.py's parent dir to detect any setup.cfg
123118
os.chdir(os.path.dirname(setup_py))
124119

@@ -144,10 +139,9 @@ def _get_setup_result_target(setup_py: str, env: Optional[dict], conn_send):
144139
# Private properties
145140
and not attr.startswith('_')
146141
)})
147-
except Exception as e:
148-
conn_send.send(e)
149-
finally:
150-
conn_send.close()
142+
except BaseException:
143+
conn_send.send(sys.exc_info())
144+
raise
151145

152146

153147
def create_dependency_descriptor(requirement_string):

test/test_package_identification_python.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@ def test_identify():
2828

2929
basepath = Path(basepath)
3030
(basepath / 'setup.py').write_text('')
31-
assert extension.identify(desc) is None
31+
with pytest.raises(RuntimeError):
32+
extension.identify(desc)
3233
assert desc.name is None
3334
assert desc.type is None
3435

3536
(basepath / 'setup.cfg').write_text('')
36-
assert extension.identify(desc) is None
37+
with pytest.raises(RuntimeError):
38+
extension.identify(desc)
3739
assert desc.name is None
3840
assert desc.type is None
3941

@@ -50,8 +52,8 @@ def test_identify():
5052
desc.name = 'other-name'
5153
with pytest.raises(RuntimeError) as e:
5254
extension.identify(desc)
53-
assert str(e.value).endswith(
54-
'Package name already set to different value')
55+
assert 'different name' in str(e.value)
56+
assert desc.name == 'other-name'
5557

5658
(basepath / 'setup.cfg').write_text(
5759
'[metadata]\n'

0 commit comments

Comments
 (0)