22# Copyright 2019 Rover Robotics
33# Licensed under the Apache License, Version 2.0
44
5+ import distutils .core
56import multiprocessing
67import os
78from typing import Optional
@@ -36,7 +37,16 @@ def identify(self, desc): # noqa: D102
3637 # after this point, we are convinced this is a Python package,
3738 # so we should fail with an Exception instead of silently
3839
39- config = get_setup_result (setup_py , env = None )
40+ if os .path .realpath (__file__ ).startswith (
41+ os .path .realpath (str (desc .path ))
42+ ):
43+ # Bootstrapping colcon.
44+ # todo: is this really necessary?
45+ get_setup_fn = get_setup_result_in_process
46+ else :
47+ get_setup_fn = get_setup_result
48+
49+ config = get_setup_fn (setup_py , env = None )
4050
4151 name = config ['metadata' ].name
4252 if not name :
@@ -64,8 +74,8 @@ def identify(self, desc): # noqa: D102
6474 for d in config [option_name ] or ()}
6575
6676 def getter (env ):
67- nonlocal setup_py
68- return get_setup_result (setup_py , env = env )
77+ nonlocal setup_py , get_setup_fn
78+ return get_setup_fn (setup_py , env = env )
6979
7080 desc .metadata ['get_python_setup_options' ] = getter
7181
@@ -134,7 +144,6 @@ def _get_setup_result_target(setup_py: str, env: dict, conn_send):
134144 :param conn_send: Connection to send the result as either a dict or an
135145 error string
136146 """
137- import distutils .core
138147 import traceback
139148 try :
140149 # need to be in setup.py's parent dir to detect any setup.cfg
@@ -146,25 +155,57 @@ def _get_setup_result_target(setup_py: str, env: dict, conn_send):
146155 result = distutils .core .run_setup (
147156 str (setup_py ), ('--dry-run' ,), stop_after = 'config' )
148157
149- # could just return all attrs in result.__dict__, but we take this
150- # opportunity to filter a few things that don't need to be there
151- conn_send .send ({
152- attr : value for attr , value in result .__dict__ .items ()
153- if (
154- # These *seem* useful but always have the value 0.
155- # Look for their values in the 'metadata' object instead.
156- attr not in result .display_option_names
157- # Getter methods
158- and not callable (value )
159- # Private properties
160- and not attr .startswith ('_' )
161- # Objects that are generally not picklable
162- and attr not in ('cmdclass' , 'distclass' , 'ext_modules' )
163- )})
158+ conn_send .send (_distribution_to_dict (result ))
164159 except BaseException :
165160 conn_send .send (traceback .format_exc ())
166161
167162
163+ def get_setup_result_in_process (setup_py , * , env : Optional [dict ]):
164+ """
165+ Run setup.py in this process.
166+
167+ Prefer get_setup_result, since it provides process isolation is
168+ threadsafe, and returns predictable errors.
169+ :param setup_py: Path to a setup.py script
170+ :param env: Environment variables to set before running setup.py
171+ :return: Dictionary of data describing the package.
172+ :raise: RuntimeError if the script doesn't appear to be a setup script.
173+ Any exception raised in the setup.py script.
174+ """
175+ save_env = os .environ .copy ()
176+ save_cwd = os .getcwd ()
177+
178+ try :
179+ if env is not None :
180+ os .environ .update (env )
181+ os .chdir (str (setup_py .parent ))
182+ dist = distutils .core .run_setup (
183+ 'setup.py' , ('--dry-run' ,), stop_after = 'config' )
184+ finally :
185+ if env is not None :
186+ os .environ .clear ()
187+ os .environ .update (save_env )
188+ os .chdir (save_cwd )
189+ return _distribution_to_dict (dist )
190+
191+
192+ def _distribution_to_dict (distribution_object ) -> dict :
193+ """Turn a distribution into a dict, discarding unpicklable attributes."""
194+ return {
195+ attr : value for attr , value in distribution_object .__dict__ .items ()
196+ if (
197+ # These *seem* useful but always have the value 0.
198+ # Look for their values in the 'metadata' object instead.
199+ attr not in distribution_object .display_option_names
200+ # Getter methods
201+ and not callable (value )
202+ # Private properties
203+ and not attr .startswith ('_' )
204+ # Objects that are generally not picklable
205+ and attr not in ('cmdclass' , 'distclass' , 'ext_modules' )
206+ )}
207+
208+
168209def create_dependency_descriptor (requirement_string ):
169210 """
170211 Create a DependencyDescriptor from a PEP440 compliant string.
0 commit comments