1 0
import os
2 0
import sys
3 0
import importlib
4

5 0
import pytest
6

7 0
from textwrap import dedent
8

9 0
from ..setup_helpers import get_package_info, register_commands
10

11 0
from . import reset_setup_helpers, reset_distutils_log  # noqa
12 0
from . import run_setup, cleanup_import
13

14 0
ASTROPY_HELPERS_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))  # noqa
15

16

17 0
def teardown_module(module):
18
    # Remove file generated by test_generate_openmp_enabled_py but
19
    # somehow needed in test_cython_autoextensions
20 0
    tmpfile = 'openmp_enabled.py'
21 0
    if os.path.exists(tmpfile):
22 0
        os.remove(tmpfile)
23

24

25 0
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 0
    test_pkg = tmpdir.mkdir('test_pkg')
30 0
    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 0
    if extension_type in ('c', 'both'):
36
        # A minimal C extension for testing
37 0
        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 0
    if extension_type in ('pyx', 'both'):
54
        # A minimal Cython extension for testing
55 0
        test_pkg.join('apyhtest_eva', 'unit02.pyx').write(dedent("""\
56
            print("Hello cruel angel.")
57
        """))
58

59 0
    if extension_type == 'c':
60 0
        extensions = ['unit01.c']
61 0
    elif extension_type == 'pyx':
62 0
        extensions = ['unit02.pyx']
63 0
    elif extension_type == 'both':
64 0
        extensions = ['unit01.c', 'unit02.pyx']
65

66 0
    include_dirs = ['numpy'] if include_numpy else []
67

68 0
    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 0
    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 0
    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 0
    if '' in sys.path:
111 0
        sys.path.remove('')
112

113 0
    sys.path.insert(0, '')
114

115 0
    def finalize():
116 0
        cleanup_import('apyhtest_eva')
117

118 0
    request.addfinalizer(finalize)
119

120 0
    return test_pkg
121

122

123 0
@pytest.fixture
124 0
def extension_test_package(tmpdir, request):
125 0
    return _extension_test_package(tmpdir, request, extension_type='both')
126

127

128 0
@pytest.fixture
129 0
def c_extension_test_package(tmpdir, request):
130
    # Check whether numpy is installed in the test environment
131 0
    has_numpy = bool(importlib.util.find_spec('numpy'))
132 0
    return _extension_test_package(tmpdir, request, extension_type='c',
133
                                   include_numpy=has_numpy)
134

135

136 0
@pytest.fixture
137 0
def pyx_extension_test_package(tmpdir, request):
138 0
    return _extension_test_package(tmpdir, request, extension_type='pyx')
139

140

141 0
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 0
    test_pkg = tmpdir.mkdir('test_pkg')
151 0
    test_pkg.mkdir('yoda').mkdir('luke')
152 0
    test_pkg.ensure('yoda', '__init__.py')
153 0
    test_pkg.ensure('yoda', 'luke', '__init__.py')
154 0
    test_pkg.join('yoda', 'luke', 'dagobah.pyx').write(
155
        """def testfunc(): pass""")
156

157
    # Required, currently, for get_package_info to work
158 0
    register_commands('yoda', '0.0', False, srcdir=str(test_pkg))
159 0
    package_info = get_package_info(str(test_pkg))
160

161 0
    assert len(package_info['ext_modules']) == 1
162 0
    assert package_info['ext_modules'][0].name == 'yoda.luke.dagobah'
163

164

165 0
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 0
    test_pkg = c_extension_test_package
172 0
    install_temp = test_pkg.mkdir('install_temp')
173

174 0
    with test_pkg.as_cwd():
175
        # This is one of the simplest ways to install just a package into a
176
        # test directory
177 0
        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 0
        stdout, stderr = capsys.readouterr()
184 0
        assert "No git repository present at" in stderr
185

186 0
    with install_temp.as_cwd():
187 0
        import apyhtest_eva
188
        # Make sure we imported the apyhtest_eva package from the correct place
189 0
        dirname = os.path.abspath(os.path.dirname(apyhtest_eva.__file__))
190 0
        assert dirname == str(install_temp.join('apyhtest_eva'))
191

192 0
        import apyhtest_eva.compiler_version
193 0
        assert apyhtest_eva.compiler_version != 'unknown'
194

195

196 0
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 0
    test_pkg = c_extension_test_package
206

207 0
    with test_pkg.as_cwd():
208

209 0
        run_setup('setup.py', ['build_ext', '--inplace', '--no-cython'])
210

211 0
        stdout, stderr = capsys.readouterr()
212 0
        assert "No git repository present at" in stderr
213

214 0
    sys.path.insert(0, str(test_pkg))
215

216 0
    try:
217 0
        import apyhtest_eva.unit01
218 0
        dirname = os.path.abspath(os.path.dirname(apyhtest_eva.unit01.__file__))
219 0
        assert dirname == str(test_pkg.join('apyhtest_eva'))
220
    finally:
221 0
        sys.path.remove(str(test_pkg))
222

223

224 0
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 0
    test_pkg = pyx_extension_test_package
234

235 0
    with test_pkg.as_cwd():
236

237 0
        with pytest.raises(SystemExit):
238 0
            run_setup('setup.py', ['build_ext', '--inplace', '--no-cython'])
239

240 0
        stdout, stderr = capsys.readouterr()
241 0
        assert "No git repository present at" in stderr
242

243 0
        msg = ('Could not find C/C++ file {0}'
244
               '.(c/cpp)'.format('apyhtest_eva/unit02'.replace('/', os.sep)))
245

246 0
        assert msg in stderr
247

248

249 0
@pytest.mark.parametrize('mode', ['cli', 'cli-w', 'cli-sphinx', 'cli-l', 'cli-parallel'])
250 0
def test_build_docs(capsys, tmpdir, mode):
251
    """
252
    Test for build_docs
253
    """
254

255 0
    test_pkg = tmpdir.mkdir('test_pkg')
256

257 0
    test_pkg.mkdir('mypackage')
258

259 0
    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 0
    test_pkg.mkdir('docs')
271

272 0
    docs_dir = test_pkg.join('docs')
273 0
    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 0
    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 0
    test_pkg.join('setup.cfg').write(dedent("""
290
        [metadata]
291
        name = mypackage
292
        version = 0.1
293
    """))
294

295 0
    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 0
    with test_pkg.as_cwd():
303

304 0
        if mode == 'cli':
305 0
            run_setup('setup.py', ['build_docs'])
306 0
        elif mode == 'cli-w':
307 0
            run_setup('setup.py', ['build_docs', '-w'])
308 0
        elif mode == 'cli-l':
309 0
            run_setup('setup.py', ['build_docs', '-l'])
310 0
        elif mode == 'cli-sphinx':
311 0
            run_setup('setup.py', ['build_sphinx'])
312 0
        elif mode == 'cli-parallel':
313 0
            run_setup('setup.py', ['build_docs', '--parallel=2'])
314

315 0
    assert os.path.exists(docs_dir.join('_build', 'html', 'index.html').strpath)
316

317

318 0
def test_command_hooks(tmpdir, capsys):
319
    """A basic test for pre- and post-command hooks."""
320

321 0
    test_pkg = tmpdir.mkdir('test_pkg')
322 0
    test_pkg.mkdir('_welltall_')
323 0
    test_pkg.join('_welltall_', '__init__.py').ensure()
324

325
    # Create a setup_package module with a couple of command hooks in it
326 0
    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 0
    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 0
    with test_pkg.as_cwd():
358 0
        try:
359 0
            run_setup('setup.py', ['build'])
360
        finally:
361 0
            cleanup_import('_welltall_')
362

363 0
    stdout, stderr = capsys.readouterr()
364 0
    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 0
    assert want in stdout.replace('\r\n', '\n').replace('\r', '\n')
373

374

375 0
def test_invalid_package_exclusion(tmpdir, capsys):
376

377 0
    module_name = 'foobar'
378 0
    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 0
    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 0
    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 0
    error_pkg = tmpdir.mkdir('error_pkg')
411 0
    error_pkg.join('setup.py').write(
412
        setup_header + error_commands + setup_footer)
413

414 0
    with error_pkg.as_cwd():
415 0
        with pytest.raises(SystemExit):
416 0
            run_setup('setup.py', ['build'])
417

418 0
        stdout, stderr = capsys.readouterr()
419 0
        assert "RuntimeError" in stderr
420

421
    # Test warning when using deprecated exclude parameter
422 0
    warn_commands = dedent("""\
423
        cmdclassd = register_commands(NAME, VERSION, RELEASE)
424
        package_info = get_package_info(exclude=['test*'])
425

426
    """)
427

428 0
    warn_pkg = tmpdir.mkdir('warn_pkg')
429 0
    warn_pkg.join('setup.py').write(
430
        setup_header + warn_commands + setup_footer)
431

432 0
    with warn_pkg.as_cwd():
433 0
        run_setup('setup.py', ['build'])
434 0
        stdout, stderr = capsys.readouterr()
435 0
        assert 'AstropyDeprecationWarning' in stderr

Read our documentation on viewing source code .

Loading