Fix CI failures
1 |
import os |
|
2 |
import sys |
|
3 |
import importlib |
|
4 |
|
|
5 |
import pytest |
|
6 |
|
|
7 |
from textwrap import dedent |
|
8 |
|
|
9 |
from ..setup_helpers import get_package_info, register_commands |
|
10 |
|
|
11 |
from . import reset_setup_helpers, reset_distutils_log # noqa |
|
12 |
from . import run_setup, cleanup_import |
|
13 |
|
|
14 |
ASTROPY_HELPERS_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) # noqa |
|
15 |
|
|
16 |
|
|
17 |
def teardown_module(module): |
|
18 |
# Remove file generated by test_generate_openmp_enabled_py but
|
|
19 |
# somehow needed in test_cython_autoextensions
|
|
20 |
tmpfile = 'openmp_enabled.py' |
|
21 |
if os.path.exists(tmpfile): |
|
22 |
os.remove(tmpfile) |
|
23 |
|
|
24 |
|
|
25 |
def _extension_test_package(tmpdir, request, extension_type='c', |
|
26 |
include_numpy=False): |
|
27 |
"""Creates a simple test package with an extension module."""
|
|
28 |
|
|
29 |
test_pkg = tmpdir.mkdir('test_pkg') |
|
30 |
test_pkg.mkdir('apyhtest_eva').ensure('__init__.py') |
|
31 |
|
|
32 |
# TODO: It might be later worth making this particular test package into a
|
|
33 |
# reusable fixture for other build_ext tests
|
|
34 |
|
|
35 |
if extension_type in ('c', 'both'): |
|
36 |
# A minimal C extension for testing
|
|
37 |
test_pkg.join('apyhtest_eva', 'unit01.c').write(dedent("""\ |
|
38 |
#include <Python.h>
|
|
39 |
|
|
40 |
static struct PyModuleDef moduledef = {
|
|
41 |
PyModuleDef_HEAD_INIT,
|
|
42 |
"unit01",
|
|
43 |
NULL,
|
|
44 |
-1,
|
|
45 |
NULL
|
|
46 |
};
|
|
47 |
PyMODINIT_FUNC
|
|
48 |
PyInit_unit01(void) {
|
|
49 |
return PyModule_Create(&moduledef);
|
|
50 |
}
|
|
51 |
""")) |
|
52 |
|
|
53 |
if extension_type in ('pyx', 'both'): |
|
54 |
# A minimal Cython extension for testing
|
|
55 |
test_pkg.join('apyhtest_eva', 'unit02.pyx').write(dedent("""\ |
|
56 |
print("Hello cruel angel.")
|
|
57 |
""")) |
|
58 |
|
|
59 |
if extension_type == 'c': |
|
60 |
extensions = ['unit01.c'] |
|
61 |
elif extension_type == 'pyx': |
|
62 |
extensions = ['unit02.pyx'] |
|
63 |
elif extension_type == 'both': |
|
64 |
extensions = ['unit01.c', 'unit02.pyx'] |
|
65 |
|
|
66 |
include_dirs = ['numpy'] if include_numpy else [] |
|
67 |
|
|
68 |
extensions_list = [ |
|
69 |
"Extension('apyhtest_eva.{0}', [join('apyhtest_eva', '{1}')], include_dirs={2})".format( |
|
70 |
os.path.splitext(extension)[0], extension, include_dirs) |
|
71 |
for extension in extensions] |
|
72 |
|
|
73 |
test_pkg.join('apyhtest_eva', 'setup_package.py').write(dedent("""\ |
|
74 |
from setuptools import Extension
|
|
75 |
from os.path import join
|
|
76 |
def get_extensions():
|
|
77 |
return [{0}]
|
|
78 |
""".format(', '.join(extensions_list)))) |
|
79 |
|
|
80 |
test_pkg.join('setup.py').write(dedent("""\ |
|
81 |
import sys
|
|
82 |
from os.path import join
|
|
83 |
from setuptools import setup
|
|
84 |
sys.path.insert(0, r'{astropy_helpers_path}')
|
|
85 |
from astropy_helpers.setup_helpers import register_commands
|
|
86 |
from astropy_helpers.setup_helpers import get_package_info
|
|
87 |
from astropy_helpers.version_helpers import generate_version_py
|
|
88 |
|
|
89 |
if '--no-cython' in sys.argv:
|
|
90 |
from astropy_helpers.commands import build_ext
|
|
91 |
build_ext.should_build_with_cython = lambda *args: False
|
|
92 |
sys.argv.remove('--no-cython')
|
|
93 |
|
|
94 |
NAME = 'apyhtest_eva'
|
|
95 |
VERSION = '0.1'
|
|
96 |
RELEASE = True
|
|
97 |
|
|
98 |
cmdclassd = register_commands(NAME, VERSION, RELEASE)
|
|
99 |
generate_version_py(NAME, VERSION, RELEASE, False, False)
|
|
100 |
package_info = get_package_info()
|
|
101 |
|
|
102 |
setup(
|
|
103 |
name=NAME,
|
|
104 |
version=VERSION,
|
|
105 |
cmdclass=cmdclassd,
|
|
106 |
**package_info
|
|
107 |
)
|
|
108 |
""".format(astropy_helpers_path=ASTROPY_HELPERS_PATH))) |
|
109 |
|
|
110 |
if '' in sys.path: |
|
111 |
sys.path.remove('') |
|
112 |
|
|
113 |
sys.path.insert(0, '') |
|
114 |
|
|
115 |
def finalize(): |
|
116 |
cleanup_import('apyhtest_eva') |
|
117 |
|
|
118 |
request.addfinalizer(finalize) |
|
119 |
|
|
120 |
return test_pkg |
|
121 |
|
|
122 |
|
|
123 |
@pytest.fixture
|
|
124 |
def extension_test_package(tmpdir, request): |
|
125 |
return _extension_test_package(tmpdir, request, extension_type='both') |
|
126 |
|
|
127 |
|
|
128 |
@pytest.fixture
|
|
129 |
def c_extension_test_package(tmpdir, request): |
|
130 |
# Check whether numpy is installed in the test environment
|
|
131 |
has_numpy = bool(importlib.util.find_spec('numpy')) |
|
132 |
return _extension_test_package(tmpdir, request, extension_type='c', |
|
133 |
include_numpy=has_numpy) |
|
134 |
|
|
135 |
|
|
136 |
@pytest.fixture
|
|
137 |
def pyx_extension_test_package(tmpdir, request): |
|
138 |
return _extension_test_package(tmpdir, request, extension_type='pyx') |
|
139 |
|
|
140 |
|
|
141 |
def test_cython_autoextensions(tmpdir): |
|
142 |
"""
|
|
143 |
Regression test for https://github.com/astropy/astropy-helpers/pull/19
|
|
144 |
|
|
145 |
Ensures that Cython extensions in sub-packages are discovered and built
|
|
146 |
only once.
|
|
147 |
"""
|
|
148 |
|
|
149 |
# Make a simple test package
|
|
150 |
test_pkg = tmpdir.mkdir('test_pkg') |
|
151 |
test_pkg.mkdir('yoda').mkdir('luke') |
|
152 |
test_pkg.ensure('yoda', '__init__.py') |
|
153 |
test_pkg.ensure('yoda', 'luke', '__init__.py') |
|
154 |
test_pkg.join('yoda', 'luke', 'dagobah.pyx').write( |
|
155 |
"""def testfunc(): pass""") |
|
156 |
|
|
157 |
# Required, currently, for get_package_info to work
|
|
158 |
register_commands('yoda', '0.0', False, srcdir=str(test_pkg)) |
|
159 |
package_info = get_package_info(str(test_pkg)) |
|
160 |
|
|
161 |
assert len(package_info['ext_modules']) == 1 |
|
162 |
assert package_info['ext_modules'][0].name == 'yoda.luke.dagobah' |
|
163 |
|
|
164 |
|
|
165 |
def test_compiler_module(capsys, c_extension_test_package): |
|
166 |
"""
|
|
167 |
Test ensuring that the compiler module is built and installed for packages
|
|
168 |
that have extension modules.
|
|
169 |
"""
|
|
170 |
|
|
171 |
test_pkg = c_extension_test_package |
|
172 |
install_temp = test_pkg.mkdir('install_temp') |
|
173 |
|
|
174 |
with test_pkg.as_cwd(): |
|
175 |
# This is one of the simplest ways to install just a package into a
|
|
176 |
# test directory
|
|
177 |
run_setup('setup.py', |
|
178 |
['install', |
|
179 |
'--single-version-externally-managed', |
|
180 |
'--install-lib={0}'.format(install_temp), |
|
181 |
'--record={0}'.format(install_temp.join('record.txt'))]) |
|
182 |
|
|
183 |
stdout, stderr = capsys.readouterr() |
|
184 |
assert "No git repository present at" in stderr |
|
185 |
|
|
186 |
with install_temp.as_cwd(): |
|
187 |
import apyhtest_eva |
|
188 |
# Make sure we imported the apyhtest_eva package from the correct place
|
|
189 |
dirname = os.path.abspath(os.path.dirname(apyhtest_eva.__file__)) |
|
190 |
assert dirname == str(install_temp.join('apyhtest_eva')) |
|
191 |
|
|
192 |
import apyhtest_eva.compiler_version |
|
193 |
assert apyhtest_eva.compiler_version != 'unknown' |
|
194 |
|
|
195 |
|
|
196 |
def test_no_cython_buildext(capsys, c_extension_test_package, monkeypatch): |
|
197 |
"""
|
|
198 |
Regression test for https://github.com/astropy/astropy-helpers/pull/35
|
|
199 |
|
|
200 |
This tests the custom build_ext command installed by astropy_helpers when
|
|
201 |
used with a project that has no Cython extensions (but does have one or
|
|
202 |
more normal C extensions).
|
|
203 |
"""
|
|
204 |
|
|
205 |
test_pkg = c_extension_test_package |
|
206 |
|
|
207 |
with test_pkg.as_cwd(): |
|
208 |
|
|
209 |
run_setup('setup.py', ['build_ext', '--inplace', '--no-cython']) |
|
210 |
|
|
211 |
stdout, stderr = capsys.readouterr() |
|
212 |
assert "No git repository present at" in stderr |
|
213 |
|
|
214 |
sys.path.insert(0, str(test_pkg)) |
|
215 |
|
|
216 |
try: |
|
217 |
import apyhtest_eva.unit01 |
|
218 |
dirname = os.path.abspath(os.path.dirname(apyhtest_eva.unit01.__file__)) |
|
219 |
assert dirname == str(test_pkg.join('apyhtest_eva')) |
|
220 |
finally: |
|
221 |
sys.path.remove(str(test_pkg)) |
|
222 |
|
|
223 |
|
|
224 |
def test_missing_cython_c_files(capsys, pyx_extension_test_package, |
|
225 |
monkeypatch): |
|
226 |
"""
|
|
227 |
Regression test for https://github.com/astropy/astropy-helpers/pull/181
|
|
228 |
|
|
229 |
Test failure mode when building a package that has Cython modules, but
|
|
230 |
where Cython is not installed and the generated C files are missing.
|
|
231 |
"""
|
|
232 |
|
|
233 |
test_pkg = pyx_extension_test_package |
|
234 |
|
|
235 |
with test_pkg.as_cwd(): |
|
236 |
|
|
237 |
with pytest.raises(SystemExit): |
|
238 |
run_setup('setup.py', ['build_ext', '--inplace', '--no-cython']) |
|
239 |
|
|
240 |
stdout, stderr = capsys.readouterr() |
|
241 |
assert "No git repository present at" in stderr |
|
242 |
|
|
243 |
msg = ('Could not find C/C++ file {0}' |
|
244 |
'.(c/cpp)'.format('apyhtest_eva/unit02'.replace('/', os.sep))) |
|
245 |
|
|
246 |
assert msg in stderr |
|
247 |
|
|
248 |
|
|
249 |
@pytest.mark.parametrize('mode', ['cli', 'cli-w', 'cli-sphinx', 'cli-l', 'cli-parallel']) |
|
250 |
def test_build_docs(capsys, tmpdir, mode): |
|
251 |
"""
|
|
252 |
Test for build_docs
|
|
253 |
"""
|
|
254 |
|
|
255 |
test_pkg = tmpdir.mkdir('test_pkg') |
|
256 |
|
|
257 |
test_pkg.mkdir('mypackage') |
|
258 |
|
|
259 |
test_pkg.join('mypackage').join('__init__.py').write(dedent("""\ |
|
260 |
def test_function():
|
|
261 |
pass
|
|
262 |
|
|
263 |
class A():
|
|
264 |
pass
|
|
265 |
|
|
266 |
class B(A):
|
|
267 |
pass
|
|
268 |
""")) |
|
269 |
|
|
270 |
test_pkg.mkdir('docs') |
|
271 |
|
|
272 |
docs_dir = test_pkg.join('docs') |
|
273 |
docs_dir.join('conf.py').write(dedent("""\ |
|
274 |
import warnings
|
|
275 |
with warnings.catch_warnings(): # ignore matplotlib warning
|
|
276 |
warnings.simplefilter("ignore")
|
|
277 |
from sphinx_astropy.conf import *
|
|
278 |
exclude_patterns.append('_templates')
|
|
279 |
suppress_warnings = ['app.add_directive', 'app.add_node', 'app.add_role']
|
|
280 |
""")) # noqa |
|
281 |
|
|
282 |
docs_dir.join('index.rst').write(dedent("""\ |
|
283 |
.. automodapi:: mypackage
|
|
284 |
:no-inheritance-diagram:
|
|
285 |
""")) |
|
286 |
|
|
287 |
# For this test we try out the new way of calling register_commands without
|
|
288 |
# arugments, instead getting the information from setup.cfg.
|
|
289 |
test_pkg.join('setup.cfg').write(dedent(""" |
|
290 |
[metadata]
|
|
291 |
name = mypackage
|
|
292 |
version = 0.1
|
|
293 |
""")) |
|
294 |
|
|
295 |
test_pkg.join('setup.py').write(dedent("""\ |
|
296 |
import sys
|
|
297 |
sys.path.insert(0, r'{astropy_helpers_path}')
|
|
298 |
from astropy_helpers.setup_helpers import setup
|
|
299 |
setup()
|
|
300 |
""".format(astropy_helpers_path=ASTROPY_HELPERS_PATH))) |
|
301 |
|
|
302 |
with test_pkg.as_cwd(): |
|
303 |
|
|
304 |
if mode == 'cli': |
|
305 |
run_setup('setup.py', ['build_docs']) |
|
306 |
elif mode == 'cli-w': |
|
307 |
run_setup('setup.py', ['build_docs', '-w']) |
|
308 |
elif mode == 'cli-l': |
|
309 |
run_setup('setup.py', ['build_docs', '-l']) |
|
310 |
elif mode == 'cli-sphinx': |
|
311 |
run_setup('setup.py', ['build_sphinx']) |
|
312 |
elif mode == 'cli-parallel': |
|
313 |
run_setup('setup.py', ['build_docs', '--parallel=2']) |
|
314 |
|
|
315 |
assert os.path.exists(docs_dir.join('_build', 'html', 'index.html').strpath) |
|
316 |
|
|
317 |
|
|
318 |
def test_command_hooks(tmpdir, capsys): |
|
319 |
"""A basic test for pre- and post-command hooks."""
|
|
320 |
|
|
321 |
test_pkg = tmpdir.mkdir('test_pkg') |
|
322 |
test_pkg.mkdir('_welltall_') |
|
323 |
test_pkg.join('_welltall_', '__init__.py').ensure() |
|
324 |
|
|
325 |
# Create a setup_package module with a couple of command hooks in it
|
|
326 |
test_pkg.join('_welltall_', 'setup_package.py').write(dedent("""\ |
|
327 |
def pre_build_hook(cmd_obj):
|
|
328 |
print('Hello build!')
|
|
329 |
|
|
330 |
def post_build_hook(cmd_obj):
|
|
331 |
print('Goodbye build!')
|
|
332 |
|
|
333 |
""")) |
|
334 |
|
|
335 |
# A simple setup.py for the test package--running register_commands should
|
|
336 |
# discover and enable the command hooks
|
|
337 |
test_pkg.join('setup.py').write(dedent("""\ |
|
338 |
import sys
|
|
339 |
from os.path import join
|
|
340 |
from setuptools import setup, Extension
|
|
341 |
sys.path.insert(0, r'{astropy_helpers_path}')
|
|
342 |
from astropy_helpers.setup_helpers import register_commands, get_package_info
|
|
343 |
|
|
344 |
NAME = '_welltall_'
|
|
345 |
VERSION = '0.1'
|
|
346 |
RELEASE = True
|
|
347 |
|
|
348 |
cmdclassd = register_commands(NAME, VERSION, RELEASE)
|
|
349 |
|
|
350 |
setup(
|
|
351 |
name=NAME,
|
|
352 |
version=VERSION,
|
|
353 |
cmdclass=cmdclassd
|
|
354 |
)
|
|
355 |
""".format(astropy_helpers_path=ASTROPY_HELPERS_PATH))) |
|
356 |
|
|
357 |
with test_pkg.as_cwd(): |
|
358 |
try: |
|
359 |
run_setup('setup.py', ['build']) |
|
360 |
finally: |
|
361 |
cleanup_import('_welltall_') |
|
362 |
|
|
363 |
stdout, stderr = capsys.readouterr() |
|
364 |
want = dedent("""\ |
|
365 |
running build
|
|
366 |
running pre_hook from _welltall_.setup_package for build command
|
|
367 |
Hello build!
|
|
368 |
running post_hook from _welltall_.setup_package for build command
|
|
369 |
Goodbye build!
|
|
370 |
""").strip() |
|
371 |
|
|
372 |
assert want in stdout.replace('\r\n', '\n').replace('\r', '\n') |
|
373 |
|
|
374 |
|
|
375 |
def test_invalid_package_exclusion(tmpdir, capsys): |
|
376 |
|
|
377 |
module_name = 'foobar' |
|
378 |
setup_header = dedent("""\ |
|
379 |
import sys
|
|
380 |
from os.path import join
|
|
381 |
from setuptools import setup, Extension
|
|
382 |
sys.path.insert(0, r'{astropy_helpers_path}')
|
|
383 |
from astropy_helpers.setup_helpers import register_commands, \\ |
|
384 |
get_package_info, add_exclude_packages
|
|
385 |
|
|
386 |
NAME = {module_name!r}
|
|
387 |
VERSION = '0.1'
|
|
388 |
RELEASE = True
|
|
389 |
|
|
390 |
""".format(module_name=module_name, |
|
391 |
astropy_helpers_path=ASTROPY_HELPERS_PATH)) |
|
392 |
|
|
393 |
setup_footer = dedent("""\ |
|
394 |
setup(
|
|
395 |
name=NAME,
|
|
396 |
version=VERSION,
|
|
397 |
cmdclass=cmdclassd,
|
|
398 |
**package_info
|
|
399 |
)
|
|
400 |
""") |
|
401 |
|
|
402 |
# Test error when using add_package_excludes out of order
|
|
403 |
error_commands = dedent("""\ |
|
404 |
cmdclassd = register_commands(NAME, VERSION, RELEASE)
|
|
405 |
package_info = get_package_info()
|
|
406 |
add_exclude_packages(['tests*'])
|
|
407 |
|
|
408 |
""") |
|
409 |
|
|
410 |
error_pkg = tmpdir.mkdir('error_pkg') |
|
411 |
error_pkg.join('setup.py').write( |
|
412 |
setup_header + error_commands + setup_footer) |
|
413 |
|
|
414 |
with error_pkg.as_cwd(): |
|
415 |
with pytest.raises(SystemExit): |
|
416 |
run_setup('setup.py', ['build']) |
|
417 |
|
|
418 |
stdout, stderr = capsys.readouterr() |
|
419 |
assert "RuntimeError" in stderr |
|
420 |
|
|
421 |
# Test warning when using deprecated exclude parameter
|
|
422 |
warn_commands = dedent("""\ |
|
423 |
cmdclassd = register_commands(NAME, VERSION, RELEASE)
|
|
424 |
package_info = get_package_info(exclude=['test*'])
|
|
425 |
|
|
426 |
""") |
|
427 |
|
|
428 |
warn_pkg = tmpdir.mkdir('warn_pkg') |
|
429 |
warn_pkg.join('setup.py').write( |
|
430 |
setup_header + warn_commands + setup_footer) |
|
431 |
|
|
432 |
with warn_pkg.as_cwd(): |
|
433 |
run_setup('setup.py', ['build']) |
|
434 |
stdout, stderr = capsys.readouterr() |
|
435 |
assert 'AstropyDeprecationWarning' in stderr |
Read our documentation on viewing source code .