Skip to content

Commit 4780b6f

Browse files
committed
RF: refactor installation checks to check scripts
Rewrite of testers module for clarity, in part. Add a routine that allows us to check if any scripts have been missed in the installation. Add this to the Makefile as a target and to the release checklist.
1 parent 8eeee8c commit 4780b6f

File tree

3 files changed

+139
-51
lines changed

3 files changed

+139
-51
lines changed

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,9 @@ bdist_rpm:
229229
bdist_mpkg:
230230
$(PYTHON) tools/mpkg_wrapper.py setup.py install
231231

232+
# Check for files not installed
233+
check-files:
234+
$(PYTHON) -c 'from nisext.testers import check_files; check_files("nibabel")'
232235

233236
# Print out info for possible install methods
234237
check-version-info:

doc/source/devel/make_release.rst

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,20 @@ Release checklist
9696
{'sys_version': '2.6.6 (r266:84374, Aug 31 2010, 11:00:51) \n[GCC 4.0.1 (Apple Inc. build 5493)]', 'commit_source': 'archive substitution', 'np_version': '1.5.0', 'commit_hash': '25b4125', 'pkg_path': '/var/folders/jg/jgfZ12ZXHwGSFKD85xLpLk+++TI/-Tmp-/tmpGPiD3E/pylib/nibabel', 'sys_executable': '/Library/Frameworks/Python.framework/Versions/2.6/Resources/Python.app/Contents/MacOS/Python', 'sys_platform': 'darwin'}
9797
/var/folders/jg/jgfZ12ZXHwGSFKD85xLpLk+++TI/-Tmp-/tmpGPiD3E/pylib/nibabel/__init__.pyc
9898
{'sys_version': '2.6.6 (r266:84374, Aug 31 2010, 11:00:51) \n[GCC 4.0.1 (Apple Inc. build 5493)]', 'commit_source': 'installation', 'np_version': '1.5.0', 'commit_hash': '25b4125', 'pkg_path': '/var/folders/jg/jgfZ12ZXHwGSFKD85xLpLk+++TI/-Tmp-/tmpGPiD3E/pylib/nibabel', 'sys_executable': '/Library/Frameworks/Python.framework/Versions/2.6/Resources/Python.app/Contents/MacOS/Python', 'sys_platform': 'darwin'}
99-
Files not taken across by the installation:
100-
[]
10199
/Users/mb312/dev_trees/nibabel/nibabel/__init__.pyc
102100
{'sys_version': '2.6.6 (r266:84374, Aug 31 2010, 11:00:51) \n[GCC 4.0.1 (Apple Inc. build 5493)]', 'commit_source': 'repository', 'np_version': '1.5.0', 'commit_hash': '25b4125', 'pkg_path': '/Users/mb312/dev_trees/nibabel/nibabel', 'sys_executable': '/Library/Frameworks/Python.framework/Versions/2.6/Resources/Python.app/Contents/MacOS/Python', 'sys_platform': 'darwin'}
103101

102+
* Check the ``setup.py`` file is picking up all the library code and scripts,
103+
with::
104+
105+
make check-files
106+
107+
Look for output at the end about missed files, such as::
108+
109+
Missed script files: /Users/mb312/dev_trees/nibabel/bin/nib-dicomfs, /Users/mb312/dev_trees/nibabel/bin/nifti1_diagnose.py
110+
111+
Fix ``setup.py`` to carry across any files that should be in the distribution.
112+
104113
* You probably have virtualenvs for different python versions. Check the tests
105114
pass for different configurations. If you have pytox_ and a network
106115
connnection, and lots of pythons installed, you might be able to do::

nisext/testers.py

Lines changed: 125 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@
1212
'''
1313

1414
import os
15-
from os.path import join as pjoin
15+
from os.path import join as pjoin, abspath
1616
from glob import glob
1717
import shutil
1818
import tempfile
1919
import zipfile
20+
import re
2021
from subprocess import call
2122
from functools import partial
2223

@@ -65,11 +66,20 @@ def zip_extract_all(fname, path=None):
6566
zf.extract(zipinfo, path, None)
6667

6768

68-
def install_from_to(from_dir, to_dir, py_lib_sdir):
69+
def install_from_to(from_dir, to_dir, py_lib_sdir, bin_sdir='bin'):
6970
""" Install package in `from_dir` to standard location in `to_dir`
7071
71-
Return path to directory containing package directory. The package directory
72-
is the directory containing __init__.py
72+
Parameters
73+
----------
74+
from_dir : str
75+
path containing files to install with ``python setup.py ...``
76+
to_dir : str
77+
prefix path to which files will be installed, as in ``python setup.py
78+
install --prefix=to_dir``
79+
py_lib_sdir : str
80+
subdirectory within `to_dir` to which library code will be installed
81+
bin_sdir : str, optional
82+
subdirectory within `to_dir` to which scripts will be installed
7383
"""
7484
site_pkgs_path = os.path.join(to_dir, py_lib_sdir)
7585
py_lib_locs = ' --install-purelib=%s --install-platlib=%s' % (
@@ -81,7 +91,6 @@ def install_from_to(from_dir, to_dir, py_lib_sdir):
8191
py_lib_locs))
8292
finally:
8393
os.chdir(pwd)
84-
return site_pkgs_path
8594

8695

8796
def check_installed_files(repo_mod_path, install_mod_path):
@@ -104,20 +113,60 @@ def check_installed_files(repo_mod_path, install_mod_path):
104113
list of files that should have been installed, but have not been
105114
installed
106115
"""
107-
repo_mod_path = os.path.abspath(repo_mod_path)
116+
return missing_from(repo_mod_path, install_mod_path, filter=r"\.py$")
117+
118+
119+
def missing_from(path0, path1, filter=None):
120+
""" Return filenames present in `path0` but not in `path1`
121+
122+
Parameters
123+
----------
124+
path0 : str
125+
path which contains all files of interest
126+
path1 : str
127+
path which should contain all files of interest
128+
filter : None or str or regexp, optional
129+
A successful result from ``filter.search(fname)`` means the file is of
130+
interest. None means all files are of interest
131+
132+
Returns
133+
-------
134+
path1_missing : list
135+
list of all files missing from `path1` that are in `path0` at the same
136+
relative path.
137+
"""
138+
if not filter is None:
139+
filter = re.compile(filter)
140+
repo_mod_path = os.path.abspath(path0)
108141
uninstalled = []
109142
# Walk directory tree to get py files
110-
for dirpath, dirnames, filenames in os.walk(repo_mod_path):
111-
out_dirpath = dirpath.replace(repo_mod_path, install_mod_path)
143+
for dirpath, dirnames, filenames in os.walk(path0):
144+
out_dirpath = dirpath.replace(path0, path1)
112145
for fname in filenames:
113-
if not fname.lower().endswith('.py'):
146+
if not filter is None and filter.search(fname) is None:
114147
continue
115148
equiv_fname = os.path.join(out_dirpath, fname)
116149
if not os.path.isfile(equiv_fname):
117150
uninstalled.append(pjoin(dirpath, fname))
118151
return uninstalled
119152

120153

154+
def install_from_zip(zip_fname, install_path, pkg_finder=None,
155+
py_lib_sdir=PY_LIB_SDIR,
156+
script_sdir='bin'):
157+
unzip_path = tempfile.mkdtemp()
158+
try:
159+
# Zip may unpack module into current directory
160+
zip_extract_all(zip_fname, unzip_path)
161+
if pkg_finder is None:
162+
from_path = unzip_path
163+
else:
164+
from_path = pkg_finder(unzip_path)
165+
install_from_to(from_path, install_path, py_lib_sdir, script_sdir)
166+
finally:
167+
shutil.rmtree(unzip_path)
168+
169+
121170
def contexts_print_info(mod_name, repo_path, install_path):
122171
''' Print result of get_info from different installation routes
123172
@@ -148,23 +197,12 @@ def contexts_print_info(mod_name, repo_path, install_path):
148197
my_call('git archive --format zip -o %s HEAD' % out_fname)
149198
finally:
150199
os.chdir(pwd)
151-
install_from = pjoin(install_path, mod_name)
152-
zip_extract_all(out_fname, install_from)
153-
site_pkgs_path = install_from_to(install_from,
154-
install_path,
155-
PY_LIB_SDIR)
200+
install_from_zip(out_fname, install_path, None)
156201
cmd_str = 'print(%s.get_info())' % mod_name
157202
run_mod_cmd(mod_name, site_pkgs_path, cmd_str)
158203
# now test install into a directory from the repository
159-
site_pkgs_path = install_from_to(repo_path,
160-
install_path,
161-
PY_LIB_SDIR)
204+
install_from_to(repo_path, install_path, PY_LIB_SDIR)
162205
run_mod_cmd(mod_name, site_pkgs_path, cmd_str)
163-
# Take the opportunity to audit the py files
164-
repo_mod_path = os.path.join(repo_path, mod_name)
165-
install_mod_path = os.path.join(site_pkgs_path, mod_name)
166-
print('Files not taken across by the installation:')
167-
print(check_installed_files(repo_mod_path, install_mod_path))
168206
# test from development tree
169207
run_mod_cmd(mod_name, repo_path, cmd_str)
170208
return
@@ -202,10 +240,9 @@ def tests_installed(mod_name, source_path=None):
202240
if source_path is None:
203241
source_path = os.path.abspath(os.getcwd())
204242
install_path = tempfile.mkdtemp()
243+
site_pkgs_path = pjoin(install_path, PY_LIB_SDIR)
205244
try:
206-
site_pkgs_path = install_from_to(source_path,
207-
install_path,
208-
PY_LIB_SDIR)
245+
install_from_to(source_path, install_path, PY_LIB_SDIR)
209246
run_mod_cmd(mod_name, site_pkgs_path, mod_name + '.test()')
210247
finally:
211248
shutil.rmtree(install_path)
@@ -214,53 +251,92 @@ def tests_installed(mod_name, source_path=None):
214251
tests_installed.__test__ = False
215252

216253

217-
def tests_from_zip(mod_name, zip_fname):
218-
""" Runs test from sdist zip source archive """
254+
def check_files(mod_name, repo_path=None, scripts_sdir='bin'):
255+
""" Print library and script files not picked up during install
256+
"""
257+
if repo_path is None:
258+
repo_path = abspath(os.getcwd())
219259
install_path = tempfile.mkdtemp()
260+
repo_mod_path = pjoin(repo_path, mod_name)
261+
installed_mod_path = pjoin(install_path, PY_LIB_SDIR, mod_name)
262+
repo_bin = pjoin(repo_path, 'bin')
263+
installed_bin = pjoin(install_path, 'bin')
220264
try:
221-
zip_extract_all(zip_fname, install_path)
222-
pkg_dirs = glob(pjoin(install_path, mod_name + "*"))
223-
if len(pkg_dirs) != 1:
224-
raise OSError('There must be one and only one package dir')
225-
pkg_contents = pjoin(install_path, pkg_dirs[0])
226-
tests_installed(mod_name, pkg_contents)
265+
zip_fname = make_dist(repo_path,
266+
install_path,
267+
'sdist --formats=zip',
268+
'*.zip')
269+
pf = get_sdist_finder(mod_name)
270+
install_from_zip(zip_fname, install_path, pf, PY_LIB_SDIR, scripts_sdir)
271+
lib_misses = missing_from(repo_mod_path, installed_mod_path, r"\.py$")
272+
script_misses = missing_from(repo_bin, installed_bin)
227273
finally:
228274
shutil.rmtree(install_path)
275+
if lib_misses:
276+
print "Missed library files: ", ', '.join(lib_misses)
277+
if script_misses:
278+
print "Missed script files: ", ', '.join(script_misses)
229279

230-
tests_from_zip.__test__ = False
280+
281+
282+
def get_sdist_finder(mod_name):
283+
def pf(pth):
284+
pkg_dirs = glob(pjoin(pth, mod_name + '-*'))
285+
if len(pkg_dirs) != 1:
286+
raise OSError('There must be one and only one package dir')
287+
return pkg_dirs[0]
288+
return pf
231289

232290

233291
def sdist_tests(mod_name, repo_path=None):
234292
""" Make sdist zip, install from it, and run tests """
235-
buildzip_run_tests(mod_name, 'sdist --formats=zip', '*.zip', repo_path)
293+
if repo_path is None:
294+
repo_path = abspath(os.getcwd())
295+
install_path = tempfile.mkdtemp()
296+
try:
297+
zip_fname = make_dist(repo_path,
298+
install_path,
299+
'sdist --formats=zip',
300+
'*.zip')
301+
site_pkgs_path = pjoin(install_path, PY_LIB_SDIR)
302+
pf = get_sdist_finder(mod_name)
303+
install_from_zip(zip_fname, install_path, pf, PY_LIB_SDIR)
304+
run_mod_cmd(mod_name, site_pkgs_path, mod_name + '.test()')
305+
finally:
306+
shutil.rmtree(install_path)
236307

237308
sdist_tests.__test__ = False
238309

239310

240311
def bdist_egg_tests(mod_name, repo_path=None):
241-
""" Make bdist_egg, install from it, and run tests """
242-
buildzip_run_tests(mod_name, 'bdist_egg', '*.egg', repo_path)
312+
""" Make bdist_egg, unzip it, and run tests from result """
313+
if repo_path is None:
314+
repo_path = abspath(os.getcwd())
315+
install_path = tempfile.mkdtemp()
316+
try:
317+
zip_fname = make_dist(repo_path,
318+
install_path,
319+
'bdist_egg',
320+
'*.egg')
321+
zip_extract_all(zip_fname, install_path)
322+
run_mod_cmd(mod_name, install_path, mod_name + '.test()')
323+
finally:
324+
shutil.rmtree(install_path)
243325

244326
bdist_egg_tests.__test__ = False
245327

246328

247-
def buildzip_run_tests(mod_name, setup_params, zipglob, repo_path=None):
329+
def make_dist(repo_path, out_dir, setup_params, zipglob):
248330
pwd = os.path.abspath(os.getcwd())
249-
if repo_path is None:
250-
repo_path = pwd
251-
install_path = tempfile.mkdtemp()
252331
try:
253332
os.chdir(repo_path)
254333
my_call('python setup.py %s --dist-dir=%s'
255-
% (setup_params, install_path))
256-
zips = glob(pjoin(install_path, zipglob))
334+
% (setup_params, out_dir))
335+
zips = glob(pjoin(out_dir, zipglob))
257336
if len(zips) != 1:
258337
raise OSError('There must be one and only one %s file, '
259-
'but I found "%s"' %
260-
(zipglob, ': '.join(zips)))
261-
tests_from_zip(mod_name, zips[0])
338+
'but I found "%s"' %
339+
(zipglob, ': '.join(zips)))
262340
finally:
263341
os.chdir(pwd)
264-
shutil.rmtree(install_path)
265-
266-
buildzip_run_tests.__test__ = False
342+
return zips[0]

0 commit comments

Comments
 (0)