1
# -*- coding: utf-8 -*-
2

3 0
import glob
4 0
import os
5 0
import json
6 0
import textwrap
7

8 0
from distutils.version import LooseVersion
9

10 0
import setuptools
11

12 0
import pytest
13

14 0
from . import reset_setup_helpers, reset_distutils_log  # noqa
15 0
from . import run_cmd, run_setup, testpackage, create_testpackage
16 0
from ..utils import silence
17

18

19 0
TEST_SETUP_PY = """\
20
#!/usr/bin/env python
21

22
import os
23
import sys
24

25
# This import is not the real run of ah_bootstrap for the purposes of the test,
26
# so we need to preserve the command-line arguments otherwise these get eaten
27
# up by this import
28
args = sys.argv[:]
29
import ah_bootstrap
30
sys.argv = args
31

32
{extra}
33

34
# reset the name of the package installed by ah_boostrap to
35
# _astropy_helpers_test_--this will prevent any confusion by pkg_resources with
36
# any already installed packages named astropy_helpers
37
# We also disable auto-upgrade by default
38
ah_bootstrap.DIST_NAME = 'astropy-helpers-test'
39
ah_bootstrap.PACKAGE_NAME = '_astropy_helpers_test_'
40
ah_bootstrap.AUTO_UPGRADE = False
41
ah_bootstrap.DOWNLOAD_IF_NEEDED = False
42
try:
43
    ah_bootstrap.BOOTSTRAPPER = ah_bootstrap._Bootstrapper.main()
44
    ah_bootstrap.use_astropy_helpers({args})
45
finally:
46
    ah_bootstrap.DIST_NAME = 'astropy-helpers'
47
    ah_bootstrap.PACKAGE_NAME = 'astropy_helpers'
48
    ah_bootstrap.AUTO_UPGRADE = True
49
    ah_bootstrap.DOWNLOAD_IF_NEEDED = True
50

51
# Kind of a hacky way to do this, but this assertion is specifically
52
# for test_check_submodule_no_git
53
# TODO: Rework the tests in this module so that it's easier to test specific
54
# behaviors of ah_bootstrap for each test
55
assert '--no-git' not in sys.argv
56

57
import _astropy_helpers_test_
58
filename = os.path.abspath(_astropy_helpers_test_.__file__)
59
filename = filename.replace('.pyc', '.py')  # More consistent this way
60

61
# We print out variables that are needed in tests below in JSON
62
import json
63
data = {{}}
64
data['filename'] = filename
65
data['ah_bootstrap.BOOTSTRAPPER.use_git'] = ah_bootstrap.BOOTSTRAPPER.use_git
66
print(json.dumps(data))
67
"""
68

69 0
AH_BOOTSTRAP_FILE = os.path.join(os.path.dirname(__file__), '..', '..', 'ah_bootstrap.py')
70

71 0
with open(AH_BOOTSTRAP_FILE) as f:
72 0
    AH_BOOTSTRAP = f.read()
73

74
# The behavior checked in some of the tests depends on the version of
75
# setuptools
76 0
try:
77
    # We need to use LooseVersion here instead of StrictVersion since developer
78
    # versions of setuptools ('35.0.2.post20170530') don't satisfy the
79
    # StrictVersion criteria even though they satisfy PEP440
80 0
    SETUPTOOLS_VERSION = LooseVersion(setuptools.__version__).version
81 0
except:
82
    # Broken setuptools? ¯\_(ツ)_/¯
83 0
    SETUPTOOLS_VERSION = (0, 0, 0)
84

85

86 0
def test_bootstrap_from_submodule(tmpdir, testpackage, capsys):
87
    """
88
    Tests importing _astropy_helpers_test_ from a submodule in a git
89
    repository.  This tests actually performing a fresh clone of the repository
90
    without the submodule initialized, and that importing astropy_helpers in
91
    that context works transparently after calling
92
    `ah_boostrap.use_astropy_helpers`.
93
    """
94

95 0
    orig_repo = tmpdir.mkdir('orig')
96

97 0
    with orig_repo.as_cwd():
98

99 0
        run_cmd('git', ['init'])
100

101 0
        orig_repo.join('ah_bootstrap.py').write(AH_BOOTSTRAP)
102 0
        run_cmd('git', ['add', 'ah_bootstrap.py'])
103

104
        # Write a test setup.py that uses ah_bootstrap; it also ensures that
105
        # any previous reference to astropy_helpers is first wiped from
106
        # sys.modules
107 0
        orig_repo.join('setup.py').write(TEST_SETUP_PY.format(args='', extra=''))
108 0
        run_cmd('git', ['add', 'setup.py'])
109

110
        # Add our own clone of the astropy_helpers repo as a submodule named
111
        # astropy_helpers
112 0
        run_cmd('git', ['submodule', 'add', str(testpackage),
113
                        '_astropy_helpers_test_'])
114

115 0
        run_cmd('git', ['commit', '-m', 'test repository'])
116

117 0
        os.chdir(str(tmpdir))
118

119
        # Creates a clone of our test repo in the directory 'clone'
120 0
        run_cmd('git', ['clone', 'orig', 'clone'])
121

122 0
        os.chdir('clone')
123

124 0
        run_setup('setup.py', [])
125

126 0
        stdout, stderr = capsys.readouterr()
127 0
        path = json.loads(stdout.strip())['filename']
128

129
        # Ensure that the astropy_helpers used by the setup.py is the one that
130
        # was imported from git submodule
131 0
        a = os.path.normcase(path)
132 0
        b = os.path.normcase(str(tmpdir.join('clone', '_astropy_helpers_test_',
133
                                             '_astropy_helpers_test_',
134
                                             '__init__.py')))
135 0
        assert a == b
136

137

138 0
def test_bootstrap_from_submodule_no_locale(tmpdir, testpackage, capsys,
139
                                            monkeypatch):
140
    """
141
    Regression test for https://github.com/astropy/astropy/issues/2749
142

143
    Runs test_bootstrap_from_submodule but with missing locale/language
144
    settings.
145
    """
146

147 0
    for varname in ('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE'):
148 0
        monkeypatch.delenv(varname, raising=False)
149

150 0
    test_bootstrap_from_submodule(tmpdir, testpackage, capsys)
151

152

153 0
def test_bootstrap_from_submodule_bad_locale(tmpdir, testpackage, capsys,
154
                                             monkeypatch):
155
    """
156
    Additional regression test for
157
    https://github.com/astropy/astropy/issues/2749
158
    """
159

160 0
    for varname in ('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE'):
161 0
        monkeypatch.delenv(varname, raising=False)
162

163
    # Test also with bad LC_CTYPE a la http://bugs.python.org/issue18378
164 0
    monkeypatch.setenv('LC_CTYPE', 'UTF-8')
165

166 0
    test_bootstrap_from_submodule(tmpdir, testpackage, capsys)
167

168

169 0
UPDATE_ERROR_PATCH = """
170
class UpgradeError(Exception):
171
    pass
172

173
def _do_upgrade(*args, **kwargs):
174
    raise UpgradeError()
175

176
ah_bootstrap._Bootstrapper._do_upgrade = _do_upgrade
177
"""
178

179

180 0
def test_check_submodule_no_git(capsys, tmpdir, testpackage):
181
    """
182
    Tests that when importing astropy_helpers from a submodule, it is still
183
    recognized as a submodule even when using the --no-git option.
184

185
    In particular this ensures that the auto-upgrade feature is not activated.
186
    """
187

188 0
    orig_repo = tmpdir.mkdir('orig')
189

190 0
    with orig_repo.as_cwd():
191

192 0
        orig_repo.join('ah_bootstrap.py').write(AH_BOOTSTRAP)
193

194 0
        run_cmd('git', ['init'])
195

196
        # Write a test setup.py that uses ah_bootstrap; it also ensures that
197
        # any previous reference to astropy_helpers is first wiped from
198
        # sys.modules
199 0
        args = 'auto_upgrade=True'
200 0
        orig_repo.join('setup.py').write(TEST_SETUP_PY.format(args=args, extra=UPDATE_ERROR_PATCH))
201 0
        run_cmd('git', ['add', 'setup.py'])
202

203
        # Add our own clone of the astropy_helpers repo as a submodule named
204
        # astropy_helpers
205 0
        run_cmd('git', ['submodule', 'add', str(testpackage),
206
                        '_astropy_helpers_test_'])
207

208 0
        run_cmd('git', ['commit', '-m', 'test repository'])
209

210 0
        run_setup('setup.py', ['--no-git'])
211

212 0
        stdout, stderr = capsys.readouterr()
213

214 0
        use_git = bool(json.loads(stdout.strip())['ah_bootstrap.BOOTSTRAPPER.use_git'])
215

216 0
        if 'UpgradeError' in stderr:
217 0
            pytest.fail('Attempted to run auto-upgrade despite importing '
218
                        '_astropy_helpers_test_ from a git submodule')
219

220
        # Ensure that the no-git option was in fact set
221 0
        assert not use_git
222

223

224 0
def test_bootstrap_from_directory(tmpdir, testpackage, capsys):
225
    """
226
    Tests simply bundling a copy of the astropy_helpers source code in its
227
    entirety bundled directly in the source package and not in an archive.
228
    """
229

230 0
    source = tmpdir.mkdir('source')
231 0
    testpackage.copy(source.join('_astropy_helpers_test_'))
232

233 0
    with source.as_cwd():
234

235 0
        source.join('ah_bootstrap.py').write(AH_BOOTSTRAP)
236

237 0
        source.join('setup.py').write(TEST_SETUP_PY.format(args='', extra=''))
238 0
        run_setup('setup.py', [])
239 0
        stdout, stderr = capsys.readouterr()
240 0
        path = json.loads(stdout.strip())['filename']
241

242
        # Ensure that the astropy_helpers used by the setup.py is the one that
243
        # was imported from git submodule
244 0
        a = os.path.normcase(path)
245 0
        b = os.path.normcase(str(source.join('_astropy_helpers_test_',
246
                                             '_astropy_helpers_test_',
247
                                             '__init__.py')))
248 0
        assert a == b
249

250

251 0
def test_bootstrap_from_archive(tmpdir, testpackage, capsys):
252
    """
253
    Tests importing _astropy_helpers_test_ from a .tar.gz source archive
254
    shipped alongside the package that uses it.
255
    """
256

257 0
    orig_repo = tmpdir.mkdir('orig')
258

259
    # Make a source distribution of the test package
260 0
    with silence():
261 0
        run_setup(str(testpackage.join('setup.py')),
262
                  ['sdist', '--dist-dir=dist', '--formats=gztar'])
263

264 0
    dist_dir = testpackage.join('dist')
265 0
    for dist_file in dist_dir.visit('*.tar.gz'):
266 0
        dist_file.copy(orig_repo)
267

268 0
    with orig_repo.as_cwd():
269

270 0
        orig_repo.join('ah_bootstrap.py').write(AH_BOOTSTRAP)
271

272
        # Write a test setup.py that uses ah_bootstrap; it also ensures that
273
        # any previous reference to astropy_helpers is first wiped from
274
        # sys.modules
275 0
        args = 'path={0!r}'.format(os.path.basename(str(dist_file)))
276 0
        orig_repo.join('setup.py').write(TEST_SETUP_PY.format(args=args, extra=''))
277

278 0
        run_setup('setup.py', [])
279

280 0
        stdout, stderr = capsys.readouterr()
281 0
        path = json.loads(stdout.strip())['filename']
282

283
        # Installation from the .tar.gz should have resulted in a .egg
284
        # directory that the _astropy_helpers_test_ package was imported from
285 0
        eggs = _get_local_eggs()
286 0
        assert eggs
287 0
        egg = orig_repo.join(eggs[0])
288 0
        assert os.path.isdir(str(egg))
289

290 0
        a = os.path.normcase(path)
291 0
        b = os.path.normcase(str(egg.join('_astropy_helpers_test_',
292
                                          '__init__.py')))
293

294 0
        assert a == b
295

296

297 0
def test_download_if_needed(tmpdir, testpackage, capsys):
298
    """
299
    Tests the case where astropy_helpers was not actually included in a
300
    package, or is otherwise missing, and we need to "download" it.
301

302
    This does not test actually downloading from the internet--this is normally
303
    done through setuptools' easy_install command which can also install from a
304
    source archive.  From the point of view of ah_boostrap the two actions are
305
    equivalent, so we can just as easily simulate this by providing a setup.cfg
306
    giving the path to a source archive to "download" (as though it were a
307
    URL).
308
    """
309

310 0
    source = tmpdir.mkdir('source')
311

312
    # Ensure ah_bootstrap is imported from the local directory
313 0
    import ah_bootstrap  # noqa
314

315
    # Make a source distribution of the test package
316 0
    with silence():
317 0
        run_setup(str(testpackage.join('setup.py')),
318
                  ['sdist', '--dist-dir=dist', '--formats=gztar'])
319

320 0
    dist_dir = testpackage.join('dist')
321

322 0
    with source.as_cwd():
323

324 0
        source.join('ah_bootstrap.py').write(AH_BOOTSTRAP)
325

326 0
        source.join('setup.py').write(TEST_SETUP_PY.format(
327
            args='download_if_needed=True', extra=''))
328 0
        source.join('setup.cfg').write(textwrap.dedent("""\
329
            [easy_install]
330
            find_links = {find_links}
331
        """.format(find_links=str(dist_dir))))
332

333 0
        run_setup('setup.py', [])
334

335 0
        stdout, stderr = capsys.readouterr()
336 0
        path = json.loads(stdout.strip())['filename']
337

338
        # easy_install should have worked by 'installing' astropy_helpers as a
339
        # .egg in the current directory
340 0
        eggs = _get_local_eggs()
341 0
        assert eggs
342 0
        egg = source.join(eggs[0])
343 0
        assert os.path.isdir(str(egg))
344

345 0
        a = os.path.normcase(path)
346 0
        b = os.path.normcase(str(egg.join('_astropy_helpers_test_',
347
                                          '__init__.py')))
348 0
        assert a == b
349

350

351 0
EXTRA_PACKAGE_INDEX = """
352
from setuptools.package_index import PackageIndex
353

354
class FakePackageIndex(PackageIndex):
355
    def __init__(self, *args, **kwargs):
356
        PackageIndex.__init__(self, *args, **kwargs)
357
        self.to_scan = {dists}
358

359
    def find_packages(self, requirement):
360
        # no-op
361
        pass
362

363
ah_bootstrap.PackageIndex = FakePackageIndex
364
"""
365

366

367 0
def test_upgrade(tmpdir, capsys):
368 0
    orig_dir = create_testpackage(tmpdir.mkdir('orig'))
369

370
    # Make a test package that uses _astropy_helpers_test_
371 0
    source = tmpdir.mkdir('source')
372 0
    dist_dir = source.mkdir('dists')
373 0
    orig_dir.copy(source.join('_astropy_helpers_test_'))
374

375 0
    with source.as_cwd():
376

377 0
        source.join('ah_bootstrap.py').write(AH_BOOTSTRAP)
378

379 0
        setup_py = TEST_SETUP_PY.format(args='auto_upgrade=True', extra='')
380 0
        source.join('setup.py').write(setup_py)
381

382
        # This will be used to later to fake downloading the upgrade package
383 0
        source.join('setup.cfg').write(textwrap.dedent("""\
384
            [easy_install]
385
            find_links = {find_links}
386
        """.format(find_links=str(dist_dir))))
387

388
    # Make additional "upgrade" versions of the _astropy_helpers_test_
389
    # package--one of them is version 0.2 and the other is version 0.1.1.  The
390
    # auto-upgrade should ignore version 0.2 but use version 0.1.1.
391 0
    upgrade_dir_1 = create_testpackage(tmpdir.mkdir('upgrade_1'), version='0.2')
392 0
    upgrade_dir_2 = create_testpackage(tmpdir.mkdir('upgrade_2'), version='0.1.1')
393

394 0
    dists = []
395
    # For each upgrade package go ahead and build a source distribution of it
396
    # and copy that source distribution to a dist directory we'll use later to
397
    # simulate a 'download'
398 0
    for upgrade_dir in [upgrade_dir_1, upgrade_dir_2]:
399 0
        with silence():
400 0
            run_setup(str(upgrade_dir.join('setup.py')),
401
                      ['sdist', '--dist-dir=dist', '--formats=gztar'])
402 0
        dists.append(str(upgrade_dir.join('dist')))
403 0
        for dist_file in upgrade_dir.visit('*.tar.gz'):
404 0
            dist_file.copy(source.join('dists'))
405

406 0
    with source.as_cwd():
407

408 0
        setup_py = TEST_SETUP_PY.format(args='auto_upgrade=True',
409
                                        extra=EXTRA_PACKAGE_INDEX.format(dists=dists))
410 0
        source.join('setup.py').write(setup_py)
411

412
        # Now run the source setup.py; this test is similar to
413
        # test_download_if_needed, but we explicitly check that the correct
414
        # *version* of _astropy_helpers_test_ was used
415 0
        run_setup('setup.py', [])
416

417 0
        stdout, stderr = capsys.readouterr()
418 0
        path = json.loads(stdout.strip())['filename']
419 0
        eggs = _get_local_eggs()
420 0
        assert eggs
421

422 0
        egg = source.join(eggs[0])
423 0
        assert os.path.isdir(str(egg))
424 0
        a = os.path.normcase(path)
425 0
        b = os.path.normcase(str(egg.join('_astropy_helpers_test_',
426
                                          '__init__.py')))
427 0
        assert a == b
428 0
        assert 'astropy_helpers_test-0.1.1-' in str(egg)
429

430

431 0
def _get_local_eggs(path='.'):
432
    """
433
    Helper utility used by some tests to get the list of egg archive files
434
    in a local directory.
435
    """
436

437 0
    if SETUPTOOLS_VERSION[0] >= 7:
438 0
        eggs = glob.glob(os.path.join(path, '.eggs', '*.egg'))
439
    else:
440 0
        eggs = glob.glob('*.egg')
441

442 0
    return eggs

Read our documentation on viewing source code .

Loading