Fix CI failures
1 |
"""
|
|
2 |
This module contains various utilities for introspecting the distutils
|
|
3 |
module and the setup process.
|
|
4 |
|
|
5 |
Some of these utilities require the
|
|
6 |
`astropy_helpers.setup_helpers.register_commands` function to be called first,
|
|
7 |
as it will affect introspection of setuptools command-line arguments. Other
|
|
8 |
utilities in this module do not have that restriction.
|
|
9 |
"""
|
|
10 |
|
|
11 | 20 |
import os |
12 | 20 |
import sys |
13 |
|
|
14 | 20 |
from distutils import ccompiler, log |
15 | 20 |
from distutils.dist import Distribution |
16 | 20 |
from distutils.errors import DistutilsError |
17 |
|
|
18 | 20 |
from .utils import silence |
19 |
|
|
20 |
|
|
21 |
# This function, and any functions that call it, require the setup in
|
|
22 |
# `astropy_helpers.setup_helpers.register_commands` to be run first.
|
|
23 | 20 |
def get_dummy_distribution(): |
24 |
"""
|
|
25 |
Returns a distutils Distribution object used to instrument the setup
|
|
26 |
environment before calling the actual setup() function.
|
|
27 |
"""
|
|
28 |
|
|
29 | 20 |
from .setup_helpers import _module_state |
30 |
|
|
31 | 20 |
if _module_state['registered_commands'] is None: |
32 |
raise RuntimeError( |
|
33 |
'astropy_helpers.setup_helpers.register_commands() must be '
|
|
34 |
'called before using '
|
|
35 |
'astropy_helpers.setup_helpers.get_dummy_distribution()') |
|
36 |
|
|
37 |
# Pre-parse the Distutils command-line options and config files to if
|
|
38 |
# the option is set.
|
|
39 | 20 |
dist = Distribution({'script_name': os.path.basename(sys.argv[0]), |
40 |
'script_args': sys.argv[1:]}) |
|
41 | 20 |
dist.cmdclass.update(_module_state['registered_commands']) |
42 |
|
|
43 | 20 |
with silence(): |
44 | 20 |
try: |
45 | 20 |
dist.parse_config_files() |
46 | 20 |
dist.parse_command_line() |
47 |
except (DistutilsError, AttributeError, SystemExit): |
|
48 |
# Let distutils handle DistutilsErrors itself AttributeErrors can
|
|
49 |
# get raise for ./setup.py --help SystemExit can be raised if a
|
|
50 |
# display option was used, for example
|
|
51 |
pass
|
|
52 |
|
|
53 | 20 |
return dist |
54 |
|
|
55 |
|
|
56 | 20 |
def get_main_package_directory(distribution): |
57 |
"""
|
|
58 |
Given a Distribution object, return the main package directory.
|
|
59 |
"""
|
|
60 | 20 |
return min(distribution.packages, key=len).replace('.', os.sep) |
61 |
|
|
62 | 20 |
def get_distutils_option(option, commands): |
63 |
""" Returns the value of the given distutils option.
|
|
64 |
|
|
65 |
Parameters
|
|
66 |
----------
|
|
67 |
option : str
|
|
68 |
The name of the option
|
|
69 |
|
|
70 |
commands : list of str
|
|
71 |
The list of commands on which this option is available
|
|
72 |
|
|
73 |
Returns
|
|
74 |
-------
|
|
75 |
val : str or None
|
|
76 |
the value of the given distutils option. If the option is not set,
|
|
77 |
returns None.
|
|
78 |
"""
|
|
79 |
|
|
80 | 20 |
dist = get_dummy_distribution() |
81 |
|
|
82 | 20 |
for cmd in commands: |
83 | 20 |
cmd_opts = dist.command_options.get(cmd) |
84 | 20 |
if cmd_opts is not None and option in cmd_opts: |
85 |
return cmd_opts[option][1] |
|
86 |
else: |
|
87 | 20 |
return None |
88 |
|
|
89 |
|
|
90 | 20 |
def get_distutils_build_option(option): |
91 |
""" Returns the value of the given distutils build option.
|
|
92 |
|
|
93 |
Parameters
|
|
94 |
----------
|
|
95 |
option : str
|
|
96 |
The name of the option
|
|
97 |
|
|
98 |
Returns
|
|
99 |
-------
|
|
100 |
val : str or None
|
|
101 |
The value of the given distutils build option. If the option
|
|
102 |
is not set, returns None.
|
|
103 |
"""
|
|
104 | 20 |
return get_distutils_option(option, ['build', 'build_ext', 'build_clib']) |
105 |
|
|
106 |
|
|
107 | 20 |
def get_distutils_install_option(option): |
108 |
""" Returns the value of the given distutils install option.
|
|
109 |
|
|
110 |
Parameters
|
|
111 |
----------
|
|
112 |
option : str
|
|
113 |
The name of the option
|
|
114 |
|
|
115 |
Returns
|
|
116 |
-------
|
|
117 |
val : str or None
|
|
118 |
The value of the given distutils build option. If the option
|
|
119 |
is not set, returns None.
|
|
120 |
"""
|
|
121 |
return get_distutils_option(option, ['install']) |
|
122 |
|
|
123 |
|
|
124 | 20 |
def get_distutils_build_or_install_option(option): |
125 |
""" Returns the value of the given distutils build or install option.
|
|
126 |
|
|
127 |
Parameters
|
|
128 |
----------
|
|
129 |
option : str
|
|
130 |
The name of the option
|
|
131 |
|
|
132 |
Returns
|
|
133 |
-------
|
|
134 |
val : str or None
|
|
135 |
The value of the given distutils build or install option. If the
|
|
136 |
option is not set, returns None.
|
|
137 |
"""
|
|
138 |
return get_distutils_option(option, ['build', 'build_ext', 'build_clib', |
|
139 |
'install']) |
|
140 |
|
|
141 |
|
|
142 | 20 |
def get_compiler_option(): |
143 |
""" Determines the compiler that will be used to build extension modules.
|
|
144 |
|
|
145 |
Returns
|
|
146 |
-------
|
|
147 |
compiler : str
|
|
148 |
The compiler option specified for the build, build_ext, or build_clib
|
|
149 |
command; or the default compiler for the platform if none was
|
|
150 |
specified.
|
|
151 |
|
|
152 |
"""
|
|
153 |
|
|
154 | 20 |
compiler = get_distutils_build_option('compiler') |
155 | 20 |
if compiler is None: |
156 | 20 |
return ccompiler.get_default_compiler() |
157 |
|
|
158 |
return compiler |
|
159 |
|
|
160 |
|
|
161 | 20 |
def add_command_option(command, name, doc, is_bool=False): |
162 |
"""
|
|
163 |
Add a custom option to a setup command.
|
|
164 |
|
|
165 |
Issues a warning if the option already exists on that command.
|
|
166 |
|
|
167 |
Parameters
|
|
168 |
----------
|
|
169 |
command : str
|
|
170 |
The name of the command as given on the command line
|
|
171 |
|
|
172 |
name : str
|
|
173 |
The name of the build option
|
|
174 |
|
|
175 |
doc : str
|
|
176 |
A short description of the option, for the `--help` message
|
|
177 |
|
|
178 |
is_bool : bool, optional
|
|
179 |
When `True`, the option is a boolean option and doesn't
|
|
180 |
require an associated value.
|
|
181 |
"""
|
|
182 |
|
|
183 | 20 |
dist = get_dummy_distribution() |
184 | 20 |
cmdcls = dist.get_command_class(command) |
185 |
|
|
186 | 20 |
if (hasattr(cmdcls, '_astropy_helpers_options') and |
187 |
name in cmdcls._astropy_helpers_options): |
|
188 |
return
|
|
189 |
|
|
190 | 20 |
attr = name.replace('-', '_') |
191 |
|
|
192 | 20 |
if hasattr(cmdcls, attr): |
193 |
raise RuntimeError( |
|
194 |
'{0!r} already has a {1!r} class attribute, barring {2!r} from '
|
|
195 |
'being usable as a custom option name.'.format(cmdcls, attr, name)) |
|
196 |
|
|
197 | 20 |
for idx, cmd in enumerate(cmdcls.user_options): |
198 | 20 |
if cmd[0] == name: |
199 |
log.warn('Overriding existing {0!r} option ' |
|
200 |
'{1!r}'.format(command, name)) |
|
201 |
del cmdcls.user_options[idx] |
|
202 |
if name in cmdcls.boolean_options: |
|
203 |
cmdcls.boolean_options.remove(name) |
|
204 |
break
|
|
205 |
|
|
206 | 20 |
cmdcls.user_options.append((name, None, doc)) |
207 |
|
|
208 | 20 |
if is_bool: |
209 | 20 |
cmdcls.boolean_options.append(name) |
210 |
|
|
211 |
# Distutils' command parsing requires that a command object have an
|
|
212 |
# attribute with the same name as the option (with '-' replaced with '_')
|
|
213 |
# in order for that option to be recognized as valid
|
|
214 | 20 |
setattr(cmdcls, attr, None) |
215 |
|
|
216 |
# This caches the options added through add_command_option so that if it is
|
|
217 |
# run multiple times in the same interpreter repeated adds are ignored
|
|
218 |
# (this way we can still raise a RuntimeError if a custom option overrides
|
|
219 |
# a built-in option)
|
|
220 | 20 |
if not hasattr(cmdcls, '_astropy_helpers_options'): |
221 | 20 |
cmdcls._astropy_helpers_options = set([name]) |
222 |
else: |
|
223 |
cmdcls._astropy_helpers_options.add(name) |
|
224 |
|
|
225 |
|
|
226 | 20 |
def get_distutils_display_options(): |
227 |
""" Returns a set of all the distutils display options in their long and
|
|
228 |
short forms. These are the setup.py arguments such as --name or --version
|
|
229 |
which print the project's metadata and then exit.
|
|
230 |
|
|
231 |
Returns
|
|
232 |
-------
|
|
233 |
opts : set
|
|
234 |
The long and short form display option arguments, including the - or --
|
|
235 |
"""
|
|
236 |
|
|
237 | 20 |
short_display_opts = set('-' + o[1] for o in Distribution.display_options |
238 |
if o[1]) |
|
239 | 20 |
long_display_opts = set('--' + o[0] for o in Distribution.display_options) |
240 |
|
|
241 |
# Include -h and --help which are not explicitly listed in
|
|
242 |
# Distribution.display_options (as they are handled by optparse)
|
|
243 | 20 |
short_display_opts.add('-h') |
244 | 20 |
long_display_opts.add('--help') |
245 |
|
|
246 |
# This isn't the greatest approach to hardcode these commands.
|
|
247 |
# However, there doesn't seem to be a good way to determine
|
|
248 |
# whether build *will be* run as part of the command at this
|
|
249 |
# phase.
|
|
250 | 20 |
display_commands = set([ |
251 |
'clean', 'register', 'setopt', 'saveopts', 'egg_info', |
|
252 |
'alias']) |
|
253 |
|
|
254 | 20 |
return short_display_opts.union(long_display_opts.union(display_commands)) |
255 |
|
|
256 |
|
|
257 | 20 |
def is_distutils_display_option(): |
258 |
""" Returns True if sys.argv contains any of the distutils display options
|
|
259 |
such as --version or --name.
|
|
260 |
"""
|
|
261 |
|
|
262 | 20 |
display_options = get_distutils_display_options() |
263 | 20 |
return bool(set(sys.argv[1:]).intersection(display_options)) |
Read our documentation on viewing source code .