7
7
packages and create an API for specifying a full analysis pipeline in
8
8
python.
9
9
10
+ Much of the machinery at the beginning of this file has been copied over from
11
+ nibabel denoted by ## START - COPIED FROM NIBABEL and a corresponding ## END
12
+
10
13
"""
11
14
12
15
import sys
13
16
from glob import glob
17
+ import os
18
+
19
+ ## START - COPIED FROM NIBABEL
20
+ from os .path import join as pjoin
21
+ from functools import partial
14
22
15
- # Import build helpers
23
+ PY3 = sys .version_info [0 ] >= 3
24
+ if PY3 :
25
+ string_types = str ,
26
+ else :
27
+ string_types = basestring ,
16
28
try :
17
- from nisext . sexts import package_check , get_comrec_build
29
+ from ConfigParser import ConfigParser
18
30
except ImportError :
19
- raise RuntimeError ('Need nisext package from nibabel installation'
20
- ' - please install nibabel first' )
31
+ from configparser import ConfigParser
32
+
33
+ # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
34
+ # update it when the contents of directories change.
35
+ if os .path .exists ('MANIFEST' ): os .remove ('MANIFEST' )
36
+
37
+ # For some commands, use setuptools.
38
+ if len (set (('develop' , 'bdist_egg' , 'bdist_rpm' , 'bdist' , 'bdist_dumb' ,
39
+ 'install_egg_info' , 'egg_info' , 'easy_install' , 'bdist_wheel' ,
40
+ 'bdist_mpkg' )).intersection (sys .argv )) > 0 :
41
+ # setup_egg imports setuptools setup, thus monkeypatching distutils.
42
+ import setup_egg
43
+
44
+ from distutils .core import setup
45
+
46
+ from distutils .version import LooseVersion
47
+ from distutils .command .build_py import build_py
48
+
49
+ from distutils import log
50
+
51
+ def get_comrec_build (pkg_dir , build_cmd = build_py ):
52
+ """ Return extended build command class for recording commit
53
+
54
+ The extended command tries to run git to find the current commit, getting
55
+ the empty string if it fails. It then writes the commit hash into a file
56
+ in the `pkg_dir` path, named ``COMMIT_INFO.txt``.
57
+
58
+ In due course this information can be used by the package after it is
59
+ installed, to tell you what commit it was installed from if known.
60
+
61
+ To make use of this system, you need a package with a COMMIT_INFO.txt file -
62
+ e.g. ``myproject/COMMIT_INFO.txt`` - that might well look like this::
63
+
64
+ # This is an ini file that may contain information about the code state
65
+ [commit hash]
66
+ # The line below may contain a valid hash if it has been substituted
67
+ during 'git archive' archive_subst_hash=$Format:%h$
68
+ # This line may be modified by the install process
69
+ install_hash=
70
+
71
+ The COMMIT_INFO file above is also designed to be used with git substitution
72
+ - so you probably also want a ``.gitattributes`` file in the root directory
73
+ of your working tree that contains something like this::
74
+
75
+ myproject/COMMIT_INFO.txt export-subst
76
+
77
+ That will cause the ``COMMIT_INFO.txt`` file to get filled in by ``git
78
+ archive`` - useful in case someone makes such an archive - for example with
79
+ via the github 'download source' button.
80
+
81
+ Although all the above will work as is, you might consider having something
82
+ like a ``get_info()`` function in your package to display the commit
83
+ information at the terminal. See the ``pkg_info.py`` module in the nipy
84
+ package for an example.
85
+ """
86
+ class MyBuildPy (build_cmd ):
87
+ ''' Subclass to write commit data into installation tree '''
88
+ def run (self ):
89
+ build_cmd .run (self )
90
+ import subprocess
91
+ proc = subprocess .Popen ('git rev-parse HEAD' ,
92
+ stdout = subprocess .PIPE ,
93
+ stderr = subprocess .PIPE ,
94
+ shell = True )
95
+ repo_commit , _ = proc .communicate ()
96
+ # Fix for python 3
97
+ repo_commit = str (repo_commit )
98
+ # We write the installation commit even if it's empty
99
+ cfg_parser = ConfigParser ()
100
+ cfg_parser .read (pjoin (pkg_dir , 'COMMIT_INFO.txt' ))
101
+ cfg_parser .set ('commit hash' , 'install_hash' , repo_commit )
102
+ out_pth = pjoin (self .build_lib , pkg_dir , 'COMMIT_INFO.txt' )
103
+ cfg_parser .write (open (out_pth , 'wt' ))
104
+ return MyBuildPy
105
+
106
+ def _add_append_key (in_dict , key , value ):
107
+ """ Helper for appending dependencies to setuptools args """
108
+ # If in_dict[key] does not exist, create it
109
+ # If in_dict[key] is a string, make it len 1 list of strings
110
+ # Append value to in_dict[key] list
111
+ if key not in in_dict :
112
+ in_dict [key ] = []
113
+ elif isinstance (in_dict [key ], string_types ):
114
+ in_dict [key ] = [in_dict [key ]]
115
+ in_dict [key ].append (value )
116
+
117
+ # Dependency checks
118
+ def package_check (pkg_name , version = None ,
119
+ optional = False ,
120
+ checker = LooseVersion ,
121
+ version_getter = None ,
122
+ messages = None ,
123
+ setuptools_args = None ,
124
+ pypi_pkg_name = None
125
+ ):
126
+ ''' Check if package `pkg_name` is present and has good enough version
127
+
128
+ Has two modes of operation. If `setuptools_args` is None (the default),
129
+ raise an error for missing non-optional dependencies and log warnings for
130
+ missing optional dependencies. If `setuptools_args` is a dict, then fill
131
+ ``install_requires`` key value with any missing non-optional dependencies,
132
+ and the ``extras_requires`` key value with optional dependencies.
133
+
134
+ This allows us to work with and without setuptools. It also means we can
135
+ check for packages that have not been installed with setuptools to avoid
136
+ installing them again.
137
+
138
+ Parameters
139
+ ----------
140
+ pkg_name : str
141
+ name of package as imported into python
142
+ version : {None, str}, optional
143
+ minimum version of the package that we require. If None, we don't
144
+ check the version. Default is None
145
+ optional : bool or str, optional
146
+ If ``bool(optional)`` is False, raise error for absent package or wrong
147
+ version; otherwise warn. If ``setuptools_args`` is not None, and
148
+ ``bool(optional)`` is not False, then `optional` should be a string
149
+ giving the feature name for the ``extras_require`` argument to setup.
150
+ checker : callable, optional
151
+ callable with which to return comparable thing from version
152
+ string. Default is ``distutils.version.LooseVersion``
153
+ version_getter : {None, callable}:
154
+ Callable that takes `pkg_name` as argument, and returns the
155
+ package version string - as in::
156
+
157
+ ``version = version_getter(pkg_name)``
158
+
159
+ If None, equivalent to::
160
+
161
+ mod = __import__(pkg_name); version = mod.__version__``
162
+ messages : None or dict, optional
163
+ dictionary giving output messages
164
+ setuptools_args : None or dict
165
+ If None, raise errors / warnings for missing non-optional / optional
166
+ dependencies. If dict fill key values ``install_requires`` and
167
+ ``extras_require`` for non-optional and optional dependencies.
168
+ pypi_pkg_name : None or string
169
+ When the pypi package name differs from the installed module. This is the
170
+ case with the package python-dateutil which installs as dateutil.
171
+ '''
172
+ setuptools_mode = not setuptools_args is None
173
+ optional_tf = bool (optional )
174
+ if version_getter is None :
175
+ def version_getter (pkg_name ):
176
+ mod = __import__ (pkg_name )
177
+ return mod .__version__
178
+ if messages is None :
179
+ messages = {}
180
+ msgs = {
181
+ 'missing' : 'Cannot import package "%s" - is it installed?' ,
182
+ 'missing opt' : 'Missing optional package "%s"' ,
183
+ 'opt suffix' : '; you may get run-time errors' ,
184
+ 'version too old' : 'You have version %s of package "%s"'
185
+ ' but we need version >= %s' , }
186
+ msgs .update (messages )
187
+ status , have_version = _package_status (pkg_name ,
188
+ version ,
189
+ version_getter ,
190
+ checker )
191
+ if pypi_pkg_name :
192
+ pkg_name = pypi_pkg_name
193
+
194
+ if status == 'satisfied' :
195
+ return
196
+ if not setuptools_mode :
197
+ if status == 'missing' :
198
+ if not optional_tf :
199
+ raise RuntimeError (msgs ['missing' ] % pkg_name )
200
+ log .warn (msgs ['missing opt' ] % pkg_name +
201
+ msgs ['opt suffix' ])
202
+ return
203
+ elif status == 'no-version' :
204
+ raise RuntimeError ('Cannot find version for %s' % pkg_name )
205
+ assert status == 'low-version'
206
+ if not optional_tf :
207
+ raise RuntimeError (msgs ['version too old' ] % (have_version ,
208
+ pkg_name ,
209
+ version ))
210
+ log .warn (msgs ['version too old' ] % (have_version ,
211
+ pkg_name ,
212
+ version )
213
+ + msgs ['opt suffix' ])
214
+ return
215
+ # setuptools mode
216
+ if optional_tf and not isinstance (optional , string_types ):
217
+ raise RuntimeError ('Not-False optional arg should be string' )
218
+ dependency = pkg_name
219
+
220
+ if version :
221
+ dependency += '>=' + version
222
+ if optional_tf :
223
+ if not 'extras_require' in setuptools_args :
224
+ setuptools_args ['extras_require' ] = {}
225
+ _add_append_key (setuptools_args ['extras_require' ],
226
+ optional ,
227
+ dependency )
228
+ return
229
+ _add_append_key (setuptools_args , 'install_requires' , dependency )
230
+ return
231
+
232
+
233
+ def _package_status (pkg_name , version , version_getter , checker ):
234
+ try :
235
+ __import__ (pkg_name )
236
+ except ImportError :
237
+ return 'missing' , None
238
+ if not version :
239
+ return 'satisfied' , None
240
+ try :
241
+ have_version = version_getter (pkg_name )
242
+ except AttributeError :
243
+ return 'no-version' , None
244
+ if checker (have_version ) < checker (version ):
245
+ return 'low-version' , have_version
246
+ return 'satisfied' , have_version
247
+
248
+ ## END - COPIED FROM NIBABEL
21
249
22
250
from build_docs import cmdclass , INFO_VARS
23
251
@@ -41,26 +269,30 @@ def configuration(parent_package='',top_path=None):
41
269
config .add_subpackage ('nipype' , 'nipype' )
42
270
return config
43
271
44
- ################################################################################
45
- # For some commands, use setuptools
46
-
47
- if len (set (('develop' , 'bdist_egg' , 'bdist_rpm' , 'bdist' , 'bdist_dumb' ,
48
- 'bdist_wininst' , 'install_egg_info' , 'egg_info' , 'easy_install' ,
49
- )).intersection (sys .argv )) > 0 :
50
- from setup_egg import extra_setuptools_args
51
-
52
- # extra_setuptools_args can be defined from the line above, but it can
53
- # also be defined here because setup.py has been exec'ed from
54
- # setup_egg.py.
55
- if not 'extra_setuptools_args' in globals ():
56
- extra_setuptools_args = dict ()
272
+ # Prepare setuptools args
273
+ if 'setuptools' in sys .modules :
274
+ extra_setuptools_args = dict (
275
+ tests_require = ['nose' ],
276
+ test_suite = 'nose.collector' ,
277
+ zip_safe = False ,
278
+ extras_require = dict (
279
+ doc = 'Sphinx>=0.3' ,
280
+ test = 'nose>=0.10.1' ),
281
+ )
282
+ pkg_chk = partial (package_check , setuptools_args = extra_setuptools_args )
283
+ else :
284
+ extra_setuptools_args = {}
285
+ pkg_chk = package_check
57
286
58
287
# Hard and soft dependency checking
59
- package_check ('networkx' , INFO_VARS ['NETWORKX_MIN_VERSION' ])
60
- package_check ('nibabel' , INFO_VARS ['NIBABEL_MIN_VERSION' ])
61
- package_check ('numpy' , INFO_VARS ['NUMPY_MIN_VERSION' ])
62
- package_check ('scipy' , INFO_VARS ['SCIPY_MIN_VERSION' ])
63
- package_check ('traits' , INFO_VARS ['TRAITS_MIN_VERSION' ])
288
+ pkg_chk ('networkx' , INFO_VARS ['NETWORKX_MIN_VERSION' ])
289
+ pkg_chk ('nibabel' , INFO_VARS ['NIBABEL_MIN_VERSION' ])
290
+ pkg_chk ('numpy' , INFO_VARS ['NUMPY_MIN_VERSION' ])
291
+ pkg_chk ('scipy' , INFO_VARS ['SCIPY_MIN_VERSION' ])
292
+ pkg_chk ('traits' , INFO_VARS ['TRAITS_MIN_VERSION' ])
293
+ pkg_chk ('nose' , INFO_VARS ['NOSE_MIN_VERSION' ])
294
+ pkg_chk ('dateutil' , INFO_VARS ['DATEUTIL_MIN_VERSION' ],
295
+ pypi_pkg_name = 'python-dateutil' )
64
296
65
297
################################################################################
66
298
# Import the documentation building classes.
@@ -77,7 +309,6 @@ def configuration(parent_package='',top_path=None):
77
309
78
310
def main (** extra_args ):
79
311
from numpy .distutils .core import setup
80
-
81
312
setup (name = INFO_VARS ['NAME' ],
82
313
maintainer = INFO_VARS ['MAINTAINER' ],
83
314
maintainer_email = INFO_VARS ['MAINTAINER_EMAIL' ],
@@ -91,13 +322,10 @@ def main(**extra_args):
91
322
author_email = INFO_VARS ['AUTHOR_EMAIL' ],
92
323
platforms = INFO_VARS ['PLATFORMS' ],
93
324
version = INFO_VARS ['VERSION' ],
94
- requires = INFO_VARS ['REQUIRES' ],
95
- configuration = configuration ,
96
- cmdclass = cmdclass ,
97
- scripts = glob ('bin/*' ),
325
+ configuration = configuration ,
326
+ cmdclass = cmdclass ,
327
+ scripts = glob ('bin/*' ),
98
328
** extra_args )
99
329
100
-
101
-
102
330
if __name__ == "__main__" :
103
331
main (** extra_setuptools_args )
0 commit comments