Skip to content

Commit 99dbbb8

Browse files
committed
BF - updated nisext
1 parent 13a8dda commit 99dbbb8

File tree

4 files changed

+154
-18
lines changed

4 files changed

+154
-18
lines changed

nisext/__init__.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,28 @@
11
# init for sext package
2+
""" Setuptools extensions that can be shared across projects
3+
4+
Typical use for these routines is as a git subtree merge
5+
6+
For example::
7+
8+
# Add a remote pointing to repository
9+
git remote add nisext git://github.com/nipy/nisext.git
10+
git fetch nisext
11+
# Label nisext history as merged
12+
git merge -s ours --no-commit nisext/master
13+
# Read nisext contents as nisext subdirectory
14+
git read-tree --prefix=nisext/ -u nisext/master
15+
git commit -m "Merge nisext project as subtree"
16+
17+
Then you would typically add a makefile target like::
18+
19+
# Update nisext subtree from remote
20+
update-nisext:
21+
git fetch nisext
22+
git merge --squash -s subtree --no-commit nisext/master
23+
24+
and commit when you have changes you want. This allows you to keep the nisext
25+
tree updated from the upstream repository, but the tree will be there and ready
26+
for someone without this machinery or remote.
27+
"""
28+

nisext/sexts.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,42 @@
88

99
from distutils import log
1010

11-
def get_build_cmd(pkg_dir):
12-
class MyBuildPy(build_py):
11+
def get_comrec_build(pkg_dir, build_cmd=build_py):
12+
""" Return extended build command class for recording commit
13+
14+
The extended command tries to run git to find the current commit, getting
15+
the empty string if it fails. It then writes the commit hash into a file
16+
in the `pkg_dir` path, named ``COMMIT_INFO.txt``.
17+
18+
In due course this information can be used by the package after it is
19+
installed, to tell you what commit it was installed from if known.
20+
21+
To make use of this system, you need a package with a COMMIT_INFO.txt file -
22+
e.g. ``myproject/COMMIT_INFO.txt`` - that might well look like this::
23+
24+
# This is an ini file that may contain information about the code state
25+
[commit hash]
26+
# The line below may contain a valid hash if it has been substituted during 'git archive'
27+
archive_subst_hash=$Format:%h$
28+
# This line may be modified by the install process
29+
install_hash=
30+
31+
The COMMIT_INFO file above is also designed to be used with git substitution
32+
- so you probably also want a ``.gitattributes`` file in the root directory
33+
of your working tree that contains something like this::
34+
35+
myproject/COMMIT_INFO.txt export-subst
36+
37+
That will cause the ``COMMIT_INFO.txt`` file to get filled in by ``git
38+
archive`` - useful in case someone makes such an archive - for example with
39+
via the github 'download source' button.
40+
41+
Although all the above will work as is, you might consider having something
42+
like a ``get_info()`` function in your package to display the commit
43+
information at the terminal. See the ``pkg_info.py`` module in the nipy
44+
package for an example.
45+
"""
46+
class MyBuildPy(build_cmd):
1347
''' Subclass to write commit data into installation tree '''
1448
def run(self):
1549
build_py.run(self)

nisext/testers.py

Lines changed: 89 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
11
''' Test package information in various install settings
2+
3+
The routines here install the package in various settings and print out the
4+
corresponding version info from the installation.
5+
6+
The typical use for this module is as a makefile target, as in::
7+
8+
# Print out info for possible install methods
9+
check-version-info:
10+
python -c 'from nisext.testers import info_from_here; info_from_here("mypackage")'
11+
212
'''
313

414
import os
@@ -32,7 +42,7 @@ def sys_print_info(mod_name, pkg_path):
3242
shutil.rmtree(tmpdir)
3343

3444

35-
def zip_extract_all(fname):
45+
def zip_extract_all(fname, path=None):
3646
''' Extract all members from zipfile
3747
3848
Deals with situation where the directory is stored in the zipfile as a name,
@@ -42,7 +52,61 @@ def zip_extract_all(fname):
4252
members = zf.namelist()
4353
# Remove members that are just bare directories
4454
members = [m for m in members if not m.endswith('/')]
45-
zf.extractall(members = members)
55+
for zipinfo in members:
56+
zf.extract(zipinfo, path, None)
57+
58+
59+
def install_from_to(from_dir, to_dir, py_lib_sdir):
60+
""" Install package in `from_dir` to standard location in `to_dir`
61+
62+
Return path to directory containing package directory. The package directory
63+
is the directory containing __init__.py
64+
"""
65+
site_pkgs_path = os.path.join(to_dir, py_lib_sdir)
66+
py_lib_locs = ' --install-purelib=%s --install-platlib=%s' % (
67+
site_pkgs_path, site_pkgs_path)
68+
pwd = os.path.abspath(os.getcwd())
69+
try:
70+
os.chdir(from_dir)
71+
my_call('python setup.py --quiet install --prefix=%s %s' % (to_dir,
72+
py_lib_locs))
73+
finally:
74+
os.chdir(pwd)
75+
return site_pkgs_path
76+
77+
78+
def check_installed_files(repo_mod_path, install_mod_path):
79+
""" Check files in `repo_mod_path` are installed at `install_mod_path`
80+
81+
At the moment, all this does is check that all the ``*.py`` files in
82+
`repo_mod_path` are installed at `install_mod_path`.
83+
84+
Parameters
85+
----------
86+
repo_mod_path : str
87+
repository path containing package files, e.g. <nibabel-repo>/nibabel>
88+
install_mod_path : str
89+
path at which package has been installed. This is the path where the
90+
root package ``__init__.py`` lives.
91+
92+
Return
93+
------
94+
uninstalled : list
95+
list of files that should have been installed, but have not been
96+
installed
97+
"""
98+
repo_mod_path = os.path.abspath(repo_mod_path)
99+
uninstalled = []
100+
# Walk directory tree to get py files
101+
for dirpath, dirnames, filenames in os.walk(repo_mod_path):
102+
out_dirpath = dirpath.replace(repo_mod_path, install_mod_path)
103+
for fname in filenames:
104+
if not fname.lower().endswith('.py'):
105+
continue
106+
equiv_fname = os.path.join(out_dirpath, fname)
107+
if not os.path.isfile(equiv_fname):
108+
uninstalled.append(pjoin(dirpath, fname))
109+
return uninstalled
46110

47111

48112
def contexts_print_info(mod_name, repo_path, install_path):
@@ -54,7 +118,8 @@ def contexts_print_info(mod_name, repo_path, install_path):
54118
* with setup.py install from repository directory
55119
* just running code from repository directory
56120
57-
and prints out result of get_info in each case
121+
and prints out result of get_info in each case. There will be many files
122+
written into `install_path` that you may want to clean up somehow.
58123
59124
Parameters
60125
----------
@@ -69,21 +134,29 @@ def contexts_print_info(mod_name, repo_path, install_path):
69134
py_lib_locs = ' --install-purelib=%s --install-platlib=%s' % (
70135
site_pkgs_path, site_pkgs_path)
71136
# first test archive
72-
os.chdir(repo_path)
137+
pwd = os.path.abspath(os.getcwd())
73138
out_fname = pjoin(install_path, 'test.zip')
74-
my_call('git archive --format zip -o %s master' % out_fname)
75-
os.chdir(install_path)
76-
zip_extract_all('test.zip')
77-
my_call('python setup.py --quiet install --prefix=%s %s' % (install_path,
78-
py_lib_locs))
139+
try:
140+
os.chdir(repo_path)
141+
my_call('git archive --format zip -o %s HEAD' % out_fname)
142+
finally:
143+
os.chdir(pwd)
144+
install_from = pjoin(install_path, mod_name)
145+
zip_extract_all(out_fname, install_from)
146+
site_pkgs_path = install_from_to(install_from,
147+
install_path,
148+
PY_LIB_SDIR)
79149
sys_print_info(mod_name, site_pkgs_path)
80-
# remove installation
81-
shutil.rmtree(site_pkgs_path)
82150
# now test install into a directory from the repository
83-
os.chdir(repo_path)
84-
my_call('python setup.py --quiet install --prefix=%s %s' % (install_path,
85-
py_lib_locs))
151+
site_pkgs_path = install_from_to(repo_path,
152+
install_path,
153+
PY_LIB_SDIR)
86154
sys_print_info(mod_name, site_pkgs_path)
155+
# Take the opportunity to audit the py files
156+
repo_mod_path = os.path.join(repo_path, mod_name)
157+
install_mod_path = os.path.join(site_pkgs_path, mod_name)
158+
print 'Files not taken across by the installation:'
159+
print check_installed_files(repo_mod_path, install_mod_path)
87160
# test from development tree
88161
sys_print_info(mod_name, repo_path)
89162
return
@@ -106,3 +179,5 @@ def info_from_here(mod_name):
106179
contexts_print_info(mod_name, repo_path, install_path)
107180
finally:
108181
shutil.rmtree(install_path)
182+
183+

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
if not 'extra_setuptools_args' in globals():
3333
extra_setuptools_args = dict()
3434

35-
from nisext.sexts import get_build_cmd, package_check
36-
cmdclass = {'build_py': get_build_cmd('nibabel')}
35+
from nisext.sexts import get_comrec_build, package_check
36+
cmdclass = {'build_py': get_comrec_build('nibabel')}
3737

3838
# Get version and release info, which is all stored in nibabel/info.py
3939
ver_file = os.path.join('nibabel', 'info.py')

0 commit comments

Comments
 (0)