##// END OF EJS Templates
Fix iptest setuptools entry point
Thomas Kluyver -
Show More
@@ -1,476 +1,476 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 from __future__ import print_function
12 12
13 13 #-------------------------------------------------------------------------------
14 14 # Copyright (C) 2008 The IPython Development Team
15 15 #
16 16 # Distributed under the terms of the BSD License. The full license is in
17 17 # the file COPYING, distributed as part of this software.
18 18 #-------------------------------------------------------------------------------
19 19
20 20 #-------------------------------------------------------------------------------
21 21 # Imports
22 22 #-------------------------------------------------------------------------------
23 23 import os
24 24 import sys
25 25
26 26 try:
27 27 from configparser import ConfigParser
28 28 except:
29 29 from ConfigParser import ConfigParser
30 30 from distutils.command.build_py import build_py
31 31 from distutils.cmd import Command
32 32 from glob import glob
33 33
34 34 from setupext import install_data_ext
35 35
36 36 #-------------------------------------------------------------------------------
37 37 # Useful globals and utility functions
38 38 #-------------------------------------------------------------------------------
39 39
40 40 # A few handy globals
41 41 isfile = os.path.isfile
42 42 pjoin = os.path.join
43 43 repo_root = os.path.dirname(os.path.abspath(__file__))
44 44
45 45 def oscmd(s):
46 46 print(">", s)
47 47 os.system(s)
48 48
49 49 # Py3 compatibility hacks, without assuming IPython itself is installed with
50 50 # the full py3compat machinery.
51 51
52 52 try:
53 53 execfile
54 54 except NameError:
55 55 def execfile(fname, globs, locs=None):
56 56 locs = locs or globs
57 57 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
58 58
59 59 # A little utility we'll need below, since glob() does NOT allow you to do
60 60 # exclusion on multiple endings!
61 61 def file_doesnt_endwith(test,endings):
62 62 """Return true if test is a file and its name does NOT end with any
63 63 of the strings listed in endings."""
64 64 if not isfile(test):
65 65 return False
66 66 for e in endings:
67 67 if test.endswith(e):
68 68 return False
69 69 return True
70 70
71 71 #---------------------------------------------------------------------------
72 72 # Basic project information
73 73 #---------------------------------------------------------------------------
74 74
75 75 # release.py contains version, authors, license, url, keywords, etc.
76 76 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
77 77
78 78 # Create a dict with the basic information
79 79 # This dict is eventually passed to setup after additional keys are added.
80 80 setup_args = dict(
81 81 name = name,
82 82 version = version,
83 83 description = description,
84 84 long_description = long_description,
85 85 author = author,
86 86 author_email = author_email,
87 87 url = url,
88 88 download_url = download_url,
89 89 license = license,
90 90 platforms = platforms,
91 91 keywords = keywords,
92 92 classifiers = classifiers,
93 93 cmdclass = {'install_data': install_data_ext},
94 94 )
95 95
96 96
97 97 #---------------------------------------------------------------------------
98 98 # Find packages
99 99 #---------------------------------------------------------------------------
100 100
101 101 def find_packages():
102 102 """
103 103 Find all of IPython's packages.
104 104 """
105 105 excludes = ['deathrow', 'quarantine']
106 106 packages = []
107 107 for dir,subdirs,files in os.walk('IPython'):
108 108 package = dir.replace(os.path.sep, '.')
109 109 if any(package.startswith('IPython.'+exc) for exc in excludes):
110 110 # package is to be excluded (e.g. deathrow)
111 111 continue
112 112 if '__init__.py' not in files:
113 113 # not a package
114 114 continue
115 115 packages.append(package)
116 116 return packages
117 117
118 118 #---------------------------------------------------------------------------
119 119 # Find package data
120 120 #---------------------------------------------------------------------------
121 121
122 122 def find_package_data():
123 123 """
124 124 Find IPython's package_data.
125 125 """
126 126 # This is not enough for these things to appear in an sdist.
127 127 # We need to muck with the MANIFEST to get this to work
128 128
129 129 # exclude static things that we don't ship (e.g. mathjax)
130 130 excludes = ['mathjax']
131 131
132 132 # add 'static/' prefix to exclusions, and tuplify for use in startswith
133 133 excludes = tuple([os.path.join('static', ex) for ex in excludes])
134 134
135 135 # walk notebook resources:
136 136 cwd = os.getcwd()
137 137 os.chdir(os.path.join('IPython', 'html'))
138 138 static_walk = list(os.walk('static'))
139 139 os.chdir(cwd)
140 140 static_data = []
141 141 for parent, dirs, files in static_walk:
142 142 if parent.startswith(excludes):
143 143 continue
144 144 for f in files:
145 145 static_data.append(os.path.join(parent, f))
146 146
147 147 package_data = {
148 148 'IPython.config.profile' : ['README*', '*/*.py'],
149 149 'IPython.core.tests' : ['*.png', '*.jpg'],
150 150 'IPython.testing' : ['*.txt'],
151 151 'IPython.testing.plugin' : ['*.txt'],
152 152 'IPython.html' : ['templates/*'] + static_data,
153 153 'IPython.qt.console' : ['resources/icon/*.svg'],
154 154 'IPython.nbconvert' : ['templates/*.tpl', 'templates/latex/*.tplx',
155 155 'templates/latex/skeleton/*.tplx', 'templates/skeleton/*',
156 156 'templates/reveal_internals/*.tpl', 'tests/files/*.*',
157 157 'exporters/tests/files/*.*']
158 158 }
159 159 return package_data
160 160
161 161
162 162 #---------------------------------------------------------------------------
163 163 # Find data files
164 164 #---------------------------------------------------------------------------
165 165
166 166 def make_dir_struct(tag,base,out_base):
167 167 """Make the directory structure of all files below a starting dir.
168 168
169 169 This is just a convenience routine to help build a nested directory
170 170 hierarchy because distutils is too stupid to do this by itself.
171 171
172 172 XXX - this needs a proper docstring!
173 173 """
174 174
175 175 # we'll use these a lot below
176 176 lbase = len(base)
177 177 pathsep = os.path.sep
178 178 lpathsep = len(pathsep)
179 179
180 180 out = []
181 181 for (dirpath,dirnames,filenames) in os.walk(base):
182 182 # we need to strip out the dirpath from the base to map it to the
183 183 # output (installation) path. This requires possibly stripping the
184 184 # path separator, because otherwise pjoin will not work correctly
185 185 # (pjoin('foo/','/bar') returns '/bar').
186 186
187 187 dp_eff = dirpath[lbase:]
188 188 if dp_eff.startswith(pathsep):
189 189 dp_eff = dp_eff[lpathsep:]
190 190 # The output path must be anchored at the out_base marker
191 191 out_path = pjoin(out_base,dp_eff)
192 192 # Now we can generate the final filenames. Since os.walk only produces
193 193 # filenames, we must join back with the dirpath to get full valid file
194 194 # paths:
195 195 pfiles = [pjoin(dirpath,f) for f in filenames]
196 196 # Finally, generate the entry we need, which is a pari of (output
197 197 # path, files) for use as a data_files parameter in install_data.
198 198 out.append((out_path, pfiles))
199 199
200 200 return out
201 201
202 202
203 203 def find_data_files():
204 204 """
205 205 Find IPython's data_files.
206 206
207 207 Most of these are docs.
208 208 """
209 209
210 210 docdirbase = pjoin('share', 'doc', 'ipython')
211 211 manpagebase = pjoin('share', 'man', 'man1')
212 212
213 213 # Simple file lists can be made by hand
214 214 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
215 215 if not manpages:
216 216 # When running from a source tree, the manpages aren't gzipped
217 217 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
218 218
219 219 igridhelpfiles = [f for f in glob(pjoin('IPython','extensions','igrid_help.*')) if isfile(f)]
220 220
221 221 # For nested structures, use the utility above
222 222 example_files = make_dir_struct(
223 223 'data',
224 224 pjoin('docs','examples'),
225 225 pjoin(docdirbase,'examples')
226 226 )
227 227 manual_files = make_dir_struct(
228 228 'data',
229 229 pjoin('docs','html'),
230 230 pjoin(docdirbase,'manual')
231 231 )
232 232
233 233 # And assemble the entire output list
234 234 data_files = [ (manpagebase, manpages),
235 235 (pjoin(docdirbase, 'extensions'), igridhelpfiles),
236 236 ] + manual_files + example_files
237 237
238 238 return data_files
239 239
240 240
241 241 def make_man_update_target(manpage):
242 242 """Return a target_update-compliant tuple for the given manpage.
243 243
244 244 Parameters
245 245 ----------
246 246 manpage : string
247 247 Name of the manpage, must include the section number (trailing number).
248 248
249 249 Example
250 250 -------
251 251
252 252 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
253 253 ('docs/man/ipython.1.gz',
254 254 ['docs/man/ipython.1'],
255 255 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
256 256 """
257 257 man_dir = pjoin('docs', 'man')
258 258 manpage_gz = manpage + '.gz'
259 259 manpath = pjoin(man_dir, manpage)
260 260 manpath_gz = pjoin(man_dir, manpage_gz)
261 261 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
262 262 locals() )
263 263 return (manpath_gz, [manpath], gz_cmd)
264 264
265 265 # The two functions below are copied from IPython.utils.path, so we don't need
266 266 # to import IPython during setup, which fails on Python 3.
267 267
268 268 def target_outdated(target,deps):
269 269 """Determine whether a target is out of date.
270 270
271 271 target_outdated(target,deps) -> 1/0
272 272
273 273 deps: list of filenames which MUST exist.
274 274 target: single filename which may or may not exist.
275 275
276 276 If target doesn't exist or is older than any file listed in deps, return
277 277 true, otherwise return false.
278 278 """
279 279 try:
280 280 target_time = os.path.getmtime(target)
281 281 except os.error:
282 282 return 1
283 283 for dep in deps:
284 284 dep_time = os.path.getmtime(dep)
285 285 if dep_time > target_time:
286 286 #print "For target",target,"Dep failed:",dep # dbg
287 287 #print "times (dep,tar):",dep_time,target_time # dbg
288 288 return 1
289 289 return 0
290 290
291 291
292 292 def target_update(target,deps,cmd):
293 293 """Update a target with a given command given a list of dependencies.
294 294
295 295 target_update(target,deps,cmd) -> runs cmd if target is outdated.
296 296
297 297 This is just a wrapper around target_outdated() which calls the given
298 298 command if target is outdated."""
299 299
300 300 if target_outdated(target,deps):
301 301 os.system(cmd)
302 302
303 303 #---------------------------------------------------------------------------
304 304 # Find scripts
305 305 #---------------------------------------------------------------------------
306 306
307 307 def find_scripts(entry_points=False, suffix=''):
308 308 """Find IPython's scripts.
309 309
310 310 if entry_points is True:
311 311 return setuptools entry_point-style definitions
312 312 else:
313 313 return file paths of plain scripts [default]
314 314
315 315 suffix is appended to script names if entry_points is True, so that the
316 316 Python 3 scripts get named "ipython3" etc.
317 317 """
318 318 if entry_points:
319 319 console_scripts = [s % suffix for s in [
320 320 'ipython%s = IPython:start_ipython',
321 321 'pycolor%s = IPython.utils.PyColorize:main',
322 322 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
323 323 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
324 324 'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance',
325 325 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
326 'iptest%s = IPython.testing.iptest:main',
326 'iptest%s = IPython.testing.iptestcontroller:main',
327 327 'irunner%s = IPython.lib.irunner:main',
328 328 ]]
329 329 gui_scripts = []
330 330 scripts = dict(console_scripts=console_scripts, gui_scripts=gui_scripts)
331 331 else:
332 332 parallel_scripts = pjoin('IPython','parallel','scripts')
333 333 main_scripts = pjoin('IPython','scripts')
334 334 scripts = [
335 335 pjoin(parallel_scripts, 'ipengine'),
336 336 pjoin(parallel_scripts, 'ipcontroller'),
337 337 pjoin(parallel_scripts, 'ipcluster'),
338 338 pjoin(parallel_scripts, 'iplogger'),
339 339 pjoin(main_scripts, 'ipython'),
340 340 pjoin(main_scripts, 'pycolor'),
341 341 pjoin(main_scripts, 'irunner'),
342 342 pjoin(main_scripts, 'iptest')
343 343 ]
344 344 return scripts
345 345
346 346 #---------------------------------------------------------------------------
347 347 # Verify all dependencies
348 348 #---------------------------------------------------------------------------
349 349
350 350 def check_for_dependencies():
351 351 """Check for IPython's dependencies.
352 352
353 353 This function should NOT be called if running under setuptools!
354 354 """
355 355 from setupext.setupext import (
356 356 print_line, print_raw, print_status,
357 357 check_for_sphinx, check_for_pygments,
358 358 check_for_nose, check_for_pexpect,
359 359 check_for_pyzmq, check_for_readline,
360 360 check_for_jinja2, check_for_tornado
361 361 )
362 362 print_line()
363 363 print_raw("BUILDING IPYTHON")
364 364 print_status('python', sys.version)
365 365 print_status('platform', sys.platform)
366 366 if sys.platform == 'win32':
367 367 print_status('Windows version', sys.getwindowsversion())
368 368
369 369 print_raw("")
370 370 print_raw("OPTIONAL DEPENDENCIES")
371 371
372 372 check_for_sphinx()
373 373 check_for_pygments()
374 374 check_for_nose()
375 375 check_for_pexpect()
376 376 check_for_pyzmq()
377 377 check_for_tornado()
378 378 check_for_readline()
379 379 check_for_jinja2()
380 380
381 381 #---------------------------------------------------------------------------
382 382 # VCS related
383 383 #---------------------------------------------------------------------------
384 384
385 385 # utils.submodule has checks for submodule status
386 386 execfile(pjoin('IPython','utils','submodule.py'), globals())
387 387
388 388 class UpdateSubmodules(Command):
389 389 """Update git submodules
390 390
391 391 IPython's external javascript dependencies live in a separate repo.
392 392 """
393 393 description = "Update git submodules"
394 394 user_options = []
395 395
396 396 def initialize_options(self):
397 397 pass
398 398
399 399 def finalize_options(self):
400 400 pass
401 401
402 402 def run(self):
403 403 failure = False
404 404 try:
405 405 self.spawn('git submodule init'.split())
406 406 self.spawn('git submodule update --recursive'.split())
407 407 except Exception as e:
408 408 failure = e
409 409 print(e)
410 410
411 411 if not check_submodule_status(repo_root) == 'clean':
412 412 print("submodules could not be checked out")
413 413 sys.exit(1)
414 414
415 415
416 416 def git_prebuild(pkg_dir, build_cmd=build_py):
417 417 """Return extended build or sdist command class for recording commit
418 418
419 419 records git commit in IPython.utils._sysinfo.commit
420 420
421 421 for use in IPython.utils.sysinfo.sys_info() calls after installation.
422 422
423 423 Also ensures that submodules exist prior to running
424 424 """
425 425
426 426 class MyBuildPy(build_cmd):
427 427 ''' Subclass to write commit data into installation tree '''
428 428 def run(self):
429 429 build_cmd.run(self)
430 430 # this one will only fire for build commands
431 431 if hasattr(self, 'build_lib'):
432 432 self._record_commit(self.build_lib)
433 433
434 434 def make_release_tree(self, base_dir, files):
435 435 # this one will fire for sdist
436 436 build_cmd.make_release_tree(self, base_dir, files)
437 437 self._record_commit(base_dir)
438 438
439 439 def _record_commit(self, base_dir):
440 440 import subprocess
441 441 proc = subprocess.Popen('git rev-parse --short HEAD',
442 442 stdout=subprocess.PIPE,
443 443 stderr=subprocess.PIPE,
444 444 shell=True)
445 445 repo_commit, _ = proc.communicate()
446 446 repo_commit = repo_commit.strip().decode("ascii")
447 447
448 448 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
449 449 if os.path.isfile(out_pth) and not repo_commit:
450 450 # nothing to write, don't clobber
451 451 return
452 452
453 453 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
454 454
455 455 # remove to avoid overwriting original via hard link
456 456 try:
457 457 os.remove(out_pth)
458 458 except (IOError, OSError):
459 459 pass
460 460 with open(out_pth, 'w') as out_file:
461 461 out_file.writelines([
462 462 '# GENERATED BY setup.py\n',
463 463 'commit = "%s"\n' % repo_commit,
464 464 ])
465 465 return require_submodules(MyBuildPy)
466 466
467 467
468 468 def require_submodules(command):
469 469 """decorator for instructing a command to check for submodules before running"""
470 470 class DecoratedCommand(command):
471 471 def run(self):
472 472 if not check_submodule_status(repo_root) == 'clean':
473 473 print("submodules missing! Run `setup.py submodule` and try again")
474 474 sys.exit(1)
475 475 command.run(self)
476 476 return DecoratedCommand
General Comments 0
You need to be logged in to leave comments. Login now