##// END OF EJS Templates
Please linter
Matthias Bussonnier -
Show More
@@ -1,351 +1,349 b''
1 1 # encoding: utf-8
2 2 """
3 3 This module defines the things that are used in setup.py for building IPython
4 4
5 5 This includes:
6 6
7 7 * The basic arguments to setup
8 8 * Functions for finding things like packages, package data, etc.
9 9 * A function for checking dependencies.
10 10 """
11 11
12 12 # Copyright (c) IPython Development Team.
13 13 # Distributed under the terms of the Modified BSD License.
14 14
15 15 import os
16 16 import re
17 17 import sys
18 18 from glob import glob
19 19 from logging import log
20 20
21 21 from setuptools import Command
22 22 from setuptools.command.build_py import build_py
23 23
24 24 from setuptools.command.install import install
25 25 from setuptools.command.install_scripts import install_scripts
26 26
27 27
28 28 #-------------------------------------------------------------------------------
29 29 # Useful globals and utility functions
30 30 #-------------------------------------------------------------------------------
31 31
32 32 # A few handy globals
33 33 isfile = os.path.isfile
34 34 pjoin = os.path.join
35 35 repo_root = os.path.dirname(os.path.abspath(__file__))
36 36
37 37 def execfile(fname, globs, locs=None):
38 38 locs = locs or globs
39 39 with open(fname, encoding="utf-8") as f:
40 40 exec(compile(f.read(), fname, "exec"), globs, locs)
41 41
42 42 # A little utility we'll need below, since glob() does NOT allow you to do
43 43 # exclusion on multiple endings!
44 44 def file_doesnt_endwith(test,endings):
45 45 """Return true if test is a file and its name does NOT end with any
46 46 of the strings listed in endings."""
47 47 if not isfile(test):
48 48 return False
49 49 for e in endings:
50 50 if test.endswith(e):
51 51 return False
52 52 return True
53 53
54 54 #---------------------------------------------------------------------------
55 55 # Basic project information
56 56 #---------------------------------------------------------------------------
57 57
58 58 # release.py contains version, authors, license, url, keywords, etc.
59 59 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
60 60
61 61 # Create a dict with the basic information
62 62 # This dict is eventually passed to setup after additional keys are added.
63 63 setup_args = dict(
64 64 author = author,
65 65 author_email = author_email,
66 66 license = license,
67 67 )
68 68
69 69
70 70 #---------------------------------------------------------------------------
71 71 # Find packages
72 72 #---------------------------------------------------------------------------
73 73
74 74 def find_packages():
75 75 """
76 76 Find all of IPython's packages.
77 77 """
78 78 excludes = ['deathrow', 'quarantine']
79 79 packages = []
80 80 for directory, subdirs, files in os.walk("IPython"):
81 81 package = directory.replace(os.path.sep, ".")
82 82 if any(package.startswith("IPython." + exc) for exc in excludes):
83 83 # package is to be excluded (e.g. deathrow)
84 84 continue
85 85 if '__init__.py' not in files:
86 86 # not a package
87 87 continue
88 88 packages.append(package)
89 89 return packages
90 90
91 91 #---------------------------------------------------------------------------
92 92 # Find package data
93 93 #---------------------------------------------------------------------------
94 94
95 95 def find_package_data():
96 96 """
97 97 Find IPython's package_data.
98 98 """
99 99 # This is not enough for these things to appear in an sdist.
100 100 # We need to muck with the MANIFEST to get this to work
101 101
102 102 package_data = {
103 103 'IPython.core' : ['profile/README*'],
104 104 'IPython.core.tests' : ['*.png', '*.jpg', 'daft_extension/*.py'],
105 105 'IPython.lib.tests' : ['*.wav'],
106 106 'IPython.testing.plugin' : ['*.txt'],
107 107 }
108 108
109 109 return package_data
110 110
111 111
112 112 def check_package_data(package_data):
113 113 """verify that package_data globs make sense"""
114 114 print("checking package data")
115 115 for pkg, data in package_data.items():
116 116 pkg_root = pjoin(*pkg.split('.'))
117 117 for d in data:
118 118 path = pjoin(pkg_root, d)
119 119 if '*' in path:
120 120 assert len(glob(path)) > 0, "No files match pattern %s" % path
121 121 else:
122 122 assert os.path.exists(path), "Missing package data: %s" % path
123 123
124 124
125 125 def check_package_data_first(command):
126 126 """decorator for checking package_data before running a given command
127 127
128 128 Probably only needs to wrap build_py
129 129 """
130 130 class DecoratedCommand(command):
131 131 def run(self):
132 132 check_package_data(self.package_data)
133 133 command.run(self)
134 134 return DecoratedCommand
135 135
136 136
137 137 #---------------------------------------------------------------------------
138 138 # Find data files
139 139 #---------------------------------------------------------------------------
140 140
141 141 def find_data_files():
142 142 """
143 143 Find IPython's data_files.
144 144
145 145 Just man pages at this point.
146 146 """
147 147
148 148 if "freebsd" in sys.platform:
149 149 manpagebase = pjoin('man', 'man1')
150 150 else:
151 151 manpagebase = pjoin('share', 'man', 'man1')
152 152
153 153 # Simple file lists can be made by hand
154 154 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
155 155 if not manpages:
156 156 # When running from a source tree, the manpages aren't gzipped
157 157 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
158 158
159 159 # And assemble the entire output list
160 160 data_files = [ (manpagebase, manpages) ]
161 161
162 162 return data_files
163 163
164 164
165 165 # The two functions below are copied from IPython.utils.path, so we don't need
166 166 # to import IPython during setup, which fails on Python 3.
167 167
168 168 def target_outdated(target,deps):
169 169 """Determine whether a target is out of date.
170 170
171 171 target_outdated(target,deps) -> 1/0
172 172
173 173 deps: list of filenames which MUST exist.
174 174 target: single filename which may or may not exist.
175 175
176 176 If target doesn't exist or is older than any file listed in deps, return
177 177 true, otherwise return false.
178 178 """
179 179 try:
180 180 target_time = os.path.getmtime(target)
181 181 except os.error:
182 182 return 1
183 183 for dep in deps:
184 184 dep_time = os.path.getmtime(dep)
185 185 if dep_time > target_time:
186 186 #print "For target",target,"Dep failed:",dep # dbg
187 187 #print "times (dep,tar):",dep_time,target_time # dbg
188 188 return 1
189 189 return 0
190 190
191 191
192 192 def target_update(target,deps,cmd):
193 193 """Update a target with a given command given a list of dependencies.
194 194
195 195 target_update(target,deps,cmd) -> runs cmd if target is outdated.
196 196
197 197 This is just a wrapper around target_outdated() which calls the given
198 198 command if target is outdated."""
199 199
200 200 if target_outdated(target,deps):
201 201 os.system(cmd)
202 202
203 203 #---------------------------------------------------------------------------
204 204 # Find scripts
205 205 #---------------------------------------------------------------------------
206 206
207 207 def find_entry_points():
208 208 """Defines the command line entry points for IPython
209 209
210 210 This always uses setuptools-style entry points. When setuptools is not in
211 211 use, our own build_scripts_entrypt class below parses these and builds
212 212 command line scripts.
213 213
214 214 Each of our entry points gets a plain name, e.g. ipython, and a name
215 215 suffixed with the Python major version number, e.g. ipython3.
216 216 """
217 217 ep = [
218 218 'ipython%s = IPython:start_ipython',
219 219 ]
220 220 major_suffix = str(sys.version_info[0])
221 return (
222 [e % "" for e in ep]
223 + [e % major_suffix for e in ep]
224 )
221 return [e % "" for e in ep] + [e % major_suffix for e in ep]
222
225 223
226 224 class install_lib_symlink(Command):
227 225 user_options = [
228 226 ('install-dir=', 'd', "directory to install to"),
229 227 ]
230 228
231 229 def initialize_options(self):
232 230 self.install_dir = None
233 231
234 232 def finalize_options(self):
235 233 self.set_undefined_options('symlink',
236 234 ('install_lib', 'install_dir'),
237 235 )
238 236
239 237 def run(self):
240 238 if sys.platform == 'win32':
241 239 raise Exception("This doesn't work on Windows.")
242 240 pkg = os.path.join(os.getcwd(), 'IPython')
243 241 dest = os.path.join(self.install_dir, 'IPython')
244 242 if os.path.islink(dest):
245 243 print('removing existing symlink at %s' % dest)
246 244 os.unlink(dest)
247 245 print('symlinking %s -> %s' % (pkg, dest))
248 246 os.symlink(pkg, dest)
249 247
250 248 class unsymlink(install):
251 249 def run(self):
252 250 dest = os.path.join(self.install_lib, 'IPython')
253 251 if os.path.islink(dest):
254 252 print('removing symlink at %s' % dest)
255 253 os.unlink(dest)
256 254 else:
257 255 print('No symlink exists at %s' % dest)
258 256
259 257 class install_symlinked(install):
260 258 def run(self):
261 259 if sys.platform == 'win32':
262 260 raise Exception("This doesn't work on Windows.")
263 261
264 262 # Run all sub-commands (at least those that need to be run)
265 263 for cmd_name in self.get_sub_commands():
266 264 self.run_command(cmd_name)
267 265
268 266 # 'sub_commands': a list of commands this command might have to run to
269 267 # get its work done. See cmd.py for more info.
270 268 sub_commands = [('install_lib_symlink', lambda self:True),
271 269 ('install_scripts_sym', lambda self:True),
272 270 ]
273 271
274 272 class install_scripts_for_symlink(install_scripts):
275 273 """Redefined to get options from 'symlink' instead of 'install'.
276 274
277 275 I love distutils almost as much as I love setuptools.
278 276 """
279 277 def finalize_options(self):
280 278 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
281 279 self.set_undefined_options('symlink',
282 280 ('install_scripts', 'install_dir'),
283 281 ('force', 'force'),
284 282 ('skip_build', 'skip_build'),
285 283 )
286 284
287 285
288 286 #---------------------------------------------------------------------------
289 287 # VCS related
290 288 #---------------------------------------------------------------------------
291 289
292 290
293 291 def git_prebuild(pkg_dir, build_cmd=build_py):
294 292 """Return extended build or sdist command class for recording commit
295 293
296 294 records git commit in IPython.utils._sysinfo.commit
297 295
298 296 for use in IPython.utils.sysinfo.sys_info() calls after installation.
299 297 """
300 298
301 299 class MyBuildPy(build_cmd):
302 300 ''' Subclass to write commit data into installation tree '''
303 301 def run(self):
304 302 # loose as `.dev` is suppose to be invalid
305 303 print("check version number")
306 304 loose_pep440re = re.compile(r'^(\d+)\.(\d+)\.(\d+((a|b|rc)\d+)?)(\.post\d+)?(\.dev\d*)?$')
307 305 if not loose_pep440re.match(version):
308 306 raise ValueError("Version number '%s' is not valid (should match [N!]N(.N)*[{a|b|rc}N][.postN][.devN])" % version)
309 307
310 308
311 309 build_cmd.run(self)
312 310 # this one will only fire for build commands
313 311 if hasattr(self, 'build_lib'):
314 312 self._record_commit(self.build_lib)
315 313
316 314 def make_release_tree(self, base_dir, files):
317 315 # this one will fire for sdist
318 316 build_cmd.make_release_tree(self, base_dir, files)
319 317 self._record_commit(base_dir)
320 318
321 319 def _record_commit(self, base_dir):
322 320 import subprocess
323 321 proc = subprocess.Popen('git rev-parse --short HEAD',
324 322 stdout=subprocess.PIPE,
325 323 stderr=subprocess.PIPE,
326 324 shell=True)
327 325 repo_commit, _ = proc.communicate()
328 326 repo_commit = repo_commit.strip().decode("ascii")
329 327
330 328 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
331 329 if os.path.isfile(out_pth) and not repo_commit:
332 330 # nothing to write, don't clobber
333 331 return
334 332
335 333 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
336 334
337 335 # remove to avoid overwriting original via hard link
338 336 try:
339 337 os.remove(out_pth)
340 338 except (IOError, OSError):
341 339 pass
342 340 with open(out_pth, "w", encoding="utf-8") as out_file:
343 341 out_file.writelines(
344 342 [
345 343 "# GENERATED BY setup.py\n",
346 344 'commit = "%s"\n' % repo_commit,
347 345 ]
348 346 )
349 347
350 348 return MyBuildPy
351 349
General Comments 0
You need to be logged in to leave comments. Login now