##// END OF EJS Templates
Let Travis know about es6-promise,...
Jonathan Frederic -
Show More
@@ -1,755 +1,754 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 from __future__ import print_function
16 16
17 17 import errno
18 18 import os
19 19 import sys
20 20
21 21 from distutils import log
22 22 from distutils.command.build_py import build_py
23 23 from distutils.command.build_scripts import build_scripts
24 24 from distutils.command.install import install
25 25 from distutils.command.install_scripts import install_scripts
26 26 from distutils.cmd import Command
27 27 from fnmatch import fnmatch
28 28 from glob import glob
29 29 from subprocess import check_call
30 30
31 31 from setupext import install_data_ext
32 32
33 33 #-------------------------------------------------------------------------------
34 34 # Useful globals and utility functions
35 35 #-------------------------------------------------------------------------------
36 36
37 37 # A few handy globals
38 38 isfile = os.path.isfile
39 39 pjoin = os.path.join
40 40 repo_root = os.path.dirname(os.path.abspath(__file__))
41 41
42 42 def oscmd(s):
43 43 print(">", s)
44 44 os.system(s)
45 45
46 46 # Py3 compatibility hacks, without assuming IPython itself is installed with
47 47 # the full py3compat machinery.
48 48
49 49 try:
50 50 execfile
51 51 except NameError:
52 52 def execfile(fname, globs, locs=None):
53 53 locs = locs or globs
54 54 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
55 55
56 56 # A little utility we'll need below, since glob() does NOT allow you to do
57 57 # exclusion on multiple endings!
58 58 def file_doesnt_endwith(test,endings):
59 59 """Return true if test is a file and its name does NOT end with any
60 60 of the strings listed in endings."""
61 61 if not isfile(test):
62 62 return False
63 63 for e in endings:
64 64 if test.endswith(e):
65 65 return False
66 66 return True
67 67
68 68 #---------------------------------------------------------------------------
69 69 # Basic project information
70 70 #---------------------------------------------------------------------------
71 71
72 72 # release.py contains version, authors, license, url, keywords, etc.
73 73 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
74 74
75 75 # Create a dict with the basic information
76 76 # This dict is eventually passed to setup after additional keys are added.
77 77 setup_args = dict(
78 78 name = name,
79 79 version = version,
80 80 description = description,
81 81 long_description = long_description,
82 82 author = author,
83 83 author_email = author_email,
84 84 url = url,
85 85 download_url = download_url,
86 86 license = license,
87 87 platforms = platforms,
88 88 keywords = keywords,
89 89 classifiers = classifiers,
90 90 cmdclass = {'install_data': install_data_ext},
91 91 )
92 92
93 93
94 94 #---------------------------------------------------------------------------
95 95 # Find packages
96 96 #---------------------------------------------------------------------------
97 97
98 98 def find_packages():
99 99 """
100 100 Find all of IPython's packages.
101 101 """
102 102 excludes = ['deathrow', 'quarantine']
103 103 packages = []
104 104 for dir,subdirs,files in os.walk('IPython'):
105 105 package = dir.replace(os.path.sep, '.')
106 106 if any(package.startswith('IPython.'+exc) for exc in excludes):
107 107 # package is to be excluded (e.g. deathrow)
108 108 continue
109 109 if '__init__.py' not in files:
110 110 # not a package
111 111 continue
112 112 packages.append(package)
113 113 return packages
114 114
115 115 #---------------------------------------------------------------------------
116 116 # Find package data
117 117 #---------------------------------------------------------------------------
118 118
119 119 def find_package_data():
120 120 """
121 121 Find IPython's package_data.
122 122 """
123 123 # This is not enough for these things to appear in an sdist.
124 124 # We need to muck with the MANIFEST to get this to work
125 125
126 126 # exclude components and less from the walk;
127 127 # we will build the components separately
128 128 excludes = [
129 129 pjoin('static', 'components'),
130 130 pjoin('static', '*', 'less'),
131 131 ]
132 132
133 133 # walk notebook resources:
134 134 cwd = os.getcwd()
135 135 os.chdir(os.path.join('IPython', 'html'))
136 136 static_data = []
137 137 for parent, dirs, files in os.walk('static'):
138 138 if any(fnmatch(parent, pat) for pat in excludes):
139 139 # prevent descending into subdirs
140 140 dirs[:] = []
141 141 continue
142 142 for f in files:
143 143 static_data.append(pjoin(parent, f))
144 144
145 145 components = pjoin("static", "components")
146 146 # select the components we actually need to install
147 147 # (there are lots of resources we bundle for sdist-reasons that we don't actually use)
148 148 static_data.extend([
149 149 pjoin(components, "backbone", "backbone-min.js"),
150 150 pjoin(components, "bootstrap", "js", "bootstrap.min.js"),
151 151 pjoin(components, "bootstrap-tour", "build", "css", "bootstrap-tour.min.css"),
152 152 pjoin(components, "bootstrap-tour", "build", "js", "bootstrap-tour.min.js"),
153 153 pjoin(components, "es6-promise", "*.js"),
154 154 pjoin(components, "font-awesome", "fonts", "*.*"),
155 155 pjoin(components, "google-caja", "html-css-sanitizer-minified.js"),
156 156 pjoin(components, "jquery", "jquery.min.js"),
157 157 pjoin(components, "jquery-ui", "ui", "minified", "jquery-ui.min.js"),
158 158 pjoin(components, "jquery-ui", "themes", "smoothness", "jquery-ui.min.css"),
159 159 pjoin(components, "jquery-ui", "themes", "smoothness", "images", "*"),
160 160 pjoin(components, "marked", "lib", "marked.js"),
161 161 pjoin(components, "requirejs", "require.js"),
162 pjoin(components, "rsvp", "rsvp.js"),
163 162 pjoin(components, "underscore", "underscore-min.js"),
164 163 pjoin(components, "moment", "moment.js"),
165 164 pjoin(components, "moment", "min", "moment.min.js"),
166 165 pjoin(components, "term.js", "src", "term.js"),
167 166 pjoin(components, "text-encoding", "lib", "encoding.js"),
168 167 ])
169 168
170 169 # Ship all of Codemirror's CSS and JS
171 170 for parent, dirs, files in os.walk(pjoin(components, 'codemirror')):
172 171 for f in files:
173 172 if f.endswith(('.js', '.css')):
174 173 static_data.append(pjoin(parent, f))
175 174
176 175 os.chdir(os.path.join('tests',))
177 176 js_tests = glob('*.js') + glob('*/*.js')
178 177
179 178 os.chdir(os.path.join(cwd, 'IPython', 'nbconvert'))
180 179 nbconvert_templates = [os.path.join(dirpath, '*.*')
181 180 for dirpath, _, _ in os.walk('templates')]
182 181
183 182 os.chdir(cwd)
184 183
185 184 package_data = {
186 185 'IPython.config.profile' : ['README*', '*/*.py'],
187 186 'IPython.core.tests' : ['*.png', '*.jpg'],
188 187 'IPython.lib.tests' : ['*.wav'],
189 188 'IPython.testing.plugin' : ['*.txt'],
190 189 'IPython.html' : ['templates/*'] + static_data,
191 190 'IPython.html.tests' : js_tests,
192 191 'IPython.qt.console' : ['resources/icon/*.svg'],
193 192 'IPython.nbconvert' : nbconvert_templates +
194 193 [
195 194 'tests/files/*.*',
196 195 'exporters/tests/files/*.*',
197 196 'preprocessors/tests/files/*.*',
198 197 ],
199 198 'IPython.nbconvert.filters' : ['marked.js'],
200 199 'IPython.nbformat' : [
201 200 'tests/*.ipynb',
202 201 'v3/nbformat.v3.schema.json',
203 202 'v4/nbformat.v4.schema.json',
204 203 ]
205 204 }
206 205
207 206 return package_data
208 207
209 208
210 209 def check_package_data(package_data):
211 210 """verify that package_data globs make sense"""
212 211 print("checking package data")
213 212 for pkg, data in package_data.items():
214 213 pkg_root = pjoin(*pkg.split('.'))
215 214 for d in data:
216 215 path = pjoin(pkg_root, d)
217 216 if '*' in path:
218 217 assert len(glob(path)) > 0, "No files match pattern %s" % path
219 218 else:
220 219 assert os.path.exists(path), "Missing package data: %s" % path
221 220
222 221
223 222 def check_package_data_first(command):
224 223 """decorator for checking package_data before running a given command
225 224
226 225 Probably only needs to wrap build_py
227 226 """
228 227 class DecoratedCommand(command):
229 228 def run(self):
230 229 check_package_data(self.package_data)
231 230 command.run(self)
232 231 return DecoratedCommand
233 232
234 233
235 234 #---------------------------------------------------------------------------
236 235 # Find data files
237 236 #---------------------------------------------------------------------------
238 237
239 238 def make_dir_struct(tag,base,out_base):
240 239 """Make the directory structure of all files below a starting dir.
241 240
242 241 This is just a convenience routine to help build a nested directory
243 242 hierarchy because distutils is too stupid to do this by itself.
244 243
245 244 XXX - this needs a proper docstring!
246 245 """
247 246
248 247 # we'll use these a lot below
249 248 lbase = len(base)
250 249 pathsep = os.path.sep
251 250 lpathsep = len(pathsep)
252 251
253 252 out = []
254 253 for (dirpath,dirnames,filenames) in os.walk(base):
255 254 # we need to strip out the dirpath from the base to map it to the
256 255 # output (installation) path. This requires possibly stripping the
257 256 # path separator, because otherwise pjoin will not work correctly
258 257 # (pjoin('foo/','/bar') returns '/bar').
259 258
260 259 dp_eff = dirpath[lbase:]
261 260 if dp_eff.startswith(pathsep):
262 261 dp_eff = dp_eff[lpathsep:]
263 262 # The output path must be anchored at the out_base marker
264 263 out_path = pjoin(out_base,dp_eff)
265 264 # Now we can generate the final filenames. Since os.walk only produces
266 265 # filenames, we must join back with the dirpath to get full valid file
267 266 # paths:
268 267 pfiles = [pjoin(dirpath,f) for f in filenames]
269 268 # Finally, generate the entry we need, which is a pari of (output
270 269 # path, files) for use as a data_files parameter in install_data.
271 270 out.append((out_path, pfiles))
272 271
273 272 return out
274 273
275 274
276 275 def find_data_files():
277 276 """
278 277 Find IPython's data_files.
279 278
280 279 Just man pages at this point.
281 280 """
282 281
283 282 manpagebase = pjoin('share', 'man', 'man1')
284 283
285 284 # Simple file lists can be made by hand
286 285 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
287 286 if not manpages:
288 287 # When running from a source tree, the manpages aren't gzipped
289 288 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
290 289
291 290 # And assemble the entire output list
292 291 data_files = [ (manpagebase, manpages) ]
293 292
294 293 return data_files
295 294
296 295
297 296 def make_man_update_target(manpage):
298 297 """Return a target_update-compliant tuple for the given manpage.
299 298
300 299 Parameters
301 300 ----------
302 301 manpage : string
303 302 Name of the manpage, must include the section number (trailing number).
304 303
305 304 Example
306 305 -------
307 306
308 307 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
309 308 ('docs/man/ipython.1.gz',
310 309 ['docs/man/ipython.1'],
311 310 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
312 311 """
313 312 man_dir = pjoin('docs', 'man')
314 313 manpage_gz = manpage + '.gz'
315 314 manpath = pjoin(man_dir, manpage)
316 315 manpath_gz = pjoin(man_dir, manpage_gz)
317 316 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
318 317 locals() )
319 318 return (manpath_gz, [manpath], gz_cmd)
320 319
321 320 # The two functions below are copied from IPython.utils.path, so we don't need
322 321 # to import IPython during setup, which fails on Python 3.
323 322
324 323 def target_outdated(target,deps):
325 324 """Determine whether a target is out of date.
326 325
327 326 target_outdated(target,deps) -> 1/0
328 327
329 328 deps: list of filenames which MUST exist.
330 329 target: single filename which may or may not exist.
331 330
332 331 If target doesn't exist or is older than any file listed in deps, return
333 332 true, otherwise return false.
334 333 """
335 334 try:
336 335 target_time = os.path.getmtime(target)
337 336 except os.error:
338 337 return 1
339 338 for dep in deps:
340 339 dep_time = os.path.getmtime(dep)
341 340 if dep_time > target_time:
342 341 #print "For target",target,"Dep failed:",dep # dbg
343 342 #print "times (dep,tar):",dep_time,target_time # dbg
344 343 return 1
345 344 return 0
346 345
347 346
348 347 def target_update(target,deps,cmd):
349 348 """Update a target with a given command given a list of dependencies.
350 349
351 350 target_update(target,deps,cmd) -> runs cmd if target is outdated.
352 351
353 352 This is just a wrapper around target_outdated() which calls the given
354 353 command if target is outdated."""
355 354
356 355 if target_outdated(target,deps):
357 356 os.system(cmd)
358 357
359 358 #---------------------------------------------------------------------------
360 359 # Find scripts
361 360 #---------------------------------------------------------------------------
362 361
363 362 def find_entry_points():
364 363 """Defines the command line entry points for IPython
365 364
366 365 This always uses setuptools-style entry points. When setuptools is not in
367 366 use, our own build_scripts_entrypt class below parses these and builds
368 367 command line scripts.
369 368
370 369 Each of our entry points gets both a plain name, e.g. ipython, and one
371 370 suffixed with the Python major version number, e.g. ipython3.
372 371 """
373 372 ep = [
374 373 'ipython%s = IPython:start_ipython',
375 374 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
376 375 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
377 376 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
378 377 'iptest%s = IPython.testing.iptestcontroller:main',
379 378 ]
380 379 suffix = str(sys.version_info[0])
381 380 return [e % '' for e in ep] + [e % suffix for e in ep]
382 381
383 382 script_src = """#!{executable}
384 383 # This script was automatically generated by setup.py
385 384 if __name__ == '__main__':
386 385 from {mod} import {func}
387 386 {func}()
388 387 """
389 388
390 389 class build_scripts_entrypt(build_scripts):
391 390 """Build the command line scripts
392 391
393 392 Parse setuptools style entry points and write simple scripts to run the
394 393 target functions.
395 394
396 395 On Windows, this also creates .cmd wrappers for the scripts so that you can
397 396 easily launch them from a command line.
398 397 """
399 398 def run(self):
400 399 self.mkpath(self.build_dir)
401 400 outfiles = []
402 401 for script in find_entry_points():
403 402 name, entrypt = script.split('=')
404 403 name = name.strip()
405 404 entrypt = entrypt.strip()
406 405 outfile = os.path.join(self.build_dir, name)
407 406 outfiles.append(outfile)
408 407 print('Writing script to', outfile)
409 408
410 409 mod, func = entrypt.split(':')
411 410 with open(outfile, 'w') as f:
412 411 f.write(script_src.format(executable=sys.executable,
413 412 mod=mod, func=func))
414 413
415 414 if sys.platform == 'win32':
416 415 # Write .cmd wrappers for Windows so 'ipython' etc. work at the
417 416 # command line
418 417 cmd_file = os.path.join(self.build_dir, name + '.cmd')
419 418 cmd = '@"{python}" "%~dp0\{script}" %*\r\n'.format(
420 419 python=sys.executable, script=name)
421 420 log.info("Writing %s wrapper script" % cmd_file)
422 421 with open(cmd_file, 'w') as f:
423 422 f.write(cmd)
424 423
425 424 return outfiles, outfiles
426 425
427 426 class install_lib_symlink(Command):
428 427 user_options = [
429 428 ('install-dir=', 'd', "directory to install to"),
430 429 ]
431 430
432 431 def initialize_options(self):
433 432 self.install_dir = None
434 433
435 434 def finalize_options(self):
436 435 self.set_undefined_options('symlink',
437 436 ('install_lib', 'install_dir'),
438 437 )
439 438
440 439 def run(self):
441 440 if sys.platform == 'win32':
442 441 raise Exception("This doesn't work on Windows.")
443 442 pkg = os.path.join(os.getcwd(), 'IPython')
444 443 dest = os.path.join(self.install_dir, 'IPython')
445 444 if os.path.islink(dest):
446 445 print('removing existing symlink at %s' % dest)
447 446 os.unlink(dest)
448 447 print('symlinking %s -> %s' % (pkg, dest))
449 448 os.symlink(pkg, dest)
450 449
451 450 class unsymlink(install):
452 451 def run(self):
453 452 dest = os.path.join(self.install_lib, 'IPython')
454 453 if os.path.islink(dest):
455 454 print('removing symlink at %s' % dest)
456 455 os.unlink(dest)
457 456 else:
458 457 print('No symlink exists at %s' % dest)
459 458
460 459 class install_symlinked(install):
461 460 def run(self):
462 461 if sys.platform == 'win32':
463 462 raise Exception("This doesn't work on Windows.")
464 463
465 464 # Run all sub-commands (at least those that need to be run)
466 465 for cmd_name in self.get_sub_commands():
467 466 self.run_command(cmd_name)
468 467
469 468 # 'sub_commands': a list of commands this command might have to run to
470 469 # get its work done. See cmd.py for more info.
471 470 sub_commands = [('install_lib_symlink', lambda self:True),
472 471 ('install_scripts_sym', lambda self:True),
473 472 ]
474 473
475 474 class install_scripts_for_symlink(install_scripts):
476 475 """Redefined to get options from 'symlink' instead of 'install'.
477 476
478 477 I love distutils almost as much as I love setuptools.
479 478 """
480 479 def finalize_options(self):
481 480 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
482 481 self.set_undefined_options('symlink',
483 482 ('install_scripts', 'install_dir'),
484 483 ('force', 'force'),
485 484 ('skip_build', 'skip_build'),
486 485 )
487 486
488 487 #---------------------------------------------------------------------------
489 488 # Verify all dependencies
490 489 #---------------------------------------------------------------------------
491 490
492 491 def check_for_dependencies():
493 492 """Check for IPython's dependencies.
494 493
495 494 This function should NOT be called if running under setuptools!
496 495 """
497 496 from setupext.setupext import (
498 497 print_line, print_raw, print_status,
499 498 check_for_sphinx, check_for_pygments,
500 499 check_for_nose, check_for_pexpect,
501 500 check_for_pyzmq, check_for_readline,
502 501 check_for_jinja2, check_for_tornado
503 502 )
504 503 print_line()
505 504 print_raw("BUILDING IPYTHON")
506 505 print_status('python', sys.version)
507 506 print_status('platform', sys.platform)
508 507 if sys.platform == 'win32':
509 508 print_status('Windows version', sys.getwindowsversion())
510 509
511 510 print_raw("")
512 511 print_raw("OPTIONAL DEPENDENCIES")
513 512
514 513 check_for_sphinx()
515 514 check_for_pygments()
516 515 check_for_nose()
517 516 if os.name == 'posix':
518 517 check_for_pexpect()
519 518 check_for_pyzmq()
520 519 check_for_tornado()
521 520 check_for_readline()
522 521 check_for_jinja2()
523 522
524 523 #---------------------------------------------------------------------------
525 524 # VCS related
526 525 #---------------------------------------------------------------------------
527 526
528 527 # utils.submodule has checks for submodule status
529 528 execfile(pjoin('IPython','utils','submodule.py'), globals())
530 529
531 530 class UpdateSubmodules(Command):
532 531 """Update git submodules
533 532
534 533 IPython's external javascript dependencies live in a separate repo.
535 534 """
536 535 description = "Update git submodules"
537 536 user_options = []
538 537
539 538 def initialize_options(self):
540 539 pass
541 540
542 541 def finalize_options(self):
543 542 pass
544 543
545 544 def run(self):
546 545 failure = False
547 546 try:
548 547 self.spawn('git submodule init'.split())
549 548 self.spawn('git submodule update --recursive'.split())
550 549 except Exception as e:
551 550 failure = e
552 551 print(e)
553 552
554 553 if not check_submodule_status(repo_root) == 'clean':
555 554 print("submodules could not be checked out")
556 555 sys.exit(1)
557 556
558 557
559 558 def git_prebuild(pkg_dir, build_cmd=build_py):
560 559 """Return extended build or sdist command class for recording commit
561 560
562 561 records git commit in IPython.utils._sysinfo.commit
563 562
564 563 for use in IPython.utils.sysinfo.sys_info() calls after installation.
565 564
566 565 Also ensures that submodules exist prior to running
567 566 """
568 567
569 568 class MyBuildPy(build_cmd):
570 569 ''' Subclass to write commit data into installation tree '''
571 570 def run(self):
572 571 build_cmd.run(self)
573 572 # this one will only fire for build commands
574 573 if hasattr(self, 'build_lib'):
575 574 self._record_commit(self.build_lib)
576 575
577 576 def make_release_tree(self, base_dir, files):
578 577 # this one will fire for sdist
579 578 build_cmd.make_release_tree(self, base_dir, files)
580 579 self._record_commit(base_dir)
581 580
582 581 def _record_commit(self, base_dir):
583 582 import subprocess
584 583 proc = subprocess.Popen('git rev-parse --short HEAD',
585 584 stdout=subprocess.PIPE,
586 585 stderr=subprocess.PIPE,
587 586 shell=True)
588 587 repo_commit, _ = proc.communicate()
589 588 repo_commit = repo_commit.strip().decode("ascii")
590 589
591 590 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
592 591 if os.path.isfile(out_pth) and not repo_commit:
593 592 # nothing to write, don't clobber
594 593 return
595 594
596 595 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
597 596
598 597 # remove to avoid overwriting original via hard link
599 598 try:
600 599 os.remove(out_pth)
601 600 except (IOError, OSError):
602 601 pass
603 602 with open(out_pth, 'w') as out_file:
604 603 out_file.writelines([
605 604 '# GENERATED BY setup.py\n',
606 605 'commit = u"%s"\n' % repo_commit,
607 606 ])
608 607 return require_submodules(MyBuildPy)
609 608
610 609
611 610 def require_submodules(command):
612 611 """decorator for instructing a command to check for submodules before running"""
613 612 class DecoratedCommand(command):
614 613 def run(self):
615 614 if not check_submodule_status(repo_root) == 'clean':
616 615 print("submodules missing! Run `setup.py submodule` and try again")
617 616 sys.exit(1)
618 617 command.run(self)
619 618 return DecoratedCommand
620 619
621 620 #---------------------------------------------------------------------------
622 621 # bdist related
623 622 #---------------------------------------------------------------------------
624 623
625 624 def get_bdist_wheel():
626 625 """Construct bdist_wheel command for building wheels
627 626
628 627 Constructs py2-none-any tag, instead of py2.7-none-any
629 628 """
630 629 class RequiresWheel(Command):
631 630 description = "Dummy command for missing bdist_wheel"
632 631 user_options = []
633 632
634 633 def initialize_options(self):
635 634 pass
636 635
637 636 def finalize_options(self):
638 637 pass
639 638
640 639 def run(self):
641 640 print("bdist_wheel requires the wheel package")
642 641 sys.exit(1)
643 642
644 643 if 'setuptools' not in sys.modules:
645 644 return RequiresWheel
646 645 else:
647 646 try:
648 647 from wheel.bdist_wheel import bdist_wheel, read_pkg_info, write_pkg_info
649 648 except ImportError:
650 649 return RequiresWheel
651 650
652 651 class bdist_wheel_tag(bdist_wheel):
653 652
654 653 def add_requirements(self, metadata_path):
655 654 """transform platform-dependent requirements"""
656 655 pkg_info = read_pkg_info(metadata_path)
657 656 # pkg_info is an email.Message object (?!)
658 657 # we have to remove the unconditional 'readline' and/or 'pyreadline' entries
659 658 # and transform them to conditionals
660 659 requires = pkg_info.get_all('Requires-Dist')
661 660 del pkg_info['Requires-Dist']
662 661 def _remove_startswith(lis, prefix):
663 662 """like list.remove, but with startswith instead of =="""
664 663 found = False
665 664 for idx, item in enumerate(lis):
666 665 if item.startswith(prefix):
667 666 found = True
668 667 break
669 668 if found:
670 669 lis.pop(idx)
671 670
672 671 for pkg in ("gnureadline", "pyreadline", "mock"):
673 672 _remove_startswith(requires, pkg)
674 673 requires.append("gnureadline; sys.platform == 'darwin' and platform.python_implementation == 'CPython'")
675 674 requires.append("pyreadline (>=2.0); extra == 'terminal' and sys.platform == 'win32' and platform.python_implementation == 'CPython'")
676 675 requires.append("pyreadline (>=2.0); extra == 'all' and sys.platform == 'win32' and platform.python_implementation == 'CPython'")
677 676 requires.append("mock; extra == 'test' and python_version < '3.3'")
678 677 for r in requires:
679 678 pkg_info['Requires-Dist'] = r
680 679 write_pkg_info(metadata_path, pkg_info)
681 680
682 681 return bdist_wheel_tag
683 682
684 683 #---------------------------------------------------------------------------
685 684 # Notebook related
686 685 #---------------------------------------------------------------------------
687 686
688 687 class CompileCSS(Command):
689 688 """Recompile Notebook CSS
690 689
691 690 Regenerate the compiled CSS from LESS sources.
692 691
693 692 Requires various dev dependencies, such as invoke and lessc.
694 693 """
695 694 description = "Recompile Notebook CSS"
696 695 user_options = [
697 696 ('minify', 'x', "minify CSS"),
698 697 ('force', 'f', "force recompilation of CSS"),
699 698 ]
700 699
701 700 def initialize_options(self):
702 701 self.minify = False
703 702 self.force = False
704 703
705 704 def finalize_options(self):
706 705 self.minify = bool(self.minify)
707 706 self.force = bool(self.force)
708 707
709 708 def run(self):
710 709 cmd = ['invoke', 'css']
711 710 if self.minify:
712 711 cmd.append('--minify')
713 712 if self.force:
714 713 cmd.append('--force')
715 714 check_call(cmd, cwd=pjoin(repo_root, "IPython", "html"))
716 715
717 716
718 717 class JavascriptVersion(Command):
719 718 """write the javascript version to notebook javascript"""
720 719 description = "Write IPython version to javascript"
721 720 user_options = []
722 721
723 722 def initialize_options(self):
724 723 pass
725 724
726 725 def finalize_options(self):
727 726 pass
728 727
729 728 def run(self):
730 729 nsfile = pjoin(repo_root, "IPython", "html", "static", "base", "js", "namespace.js")
731 730 with open(nsfile) as f:
732 731 lines = f.readlines()
733 732 with open(nsfile, 'w') as f:
734 733 for line in lines:
735 734 if line.startswith("IPython.version"):
736 735 line = 'IPython.version = "{0}";\n'.format(version)
737 736 f.write(line)
738 737
739 738
740 739 def css_js_prerelease(command, strict=True):
741 740 """decorator for building js/minified css prior to a release"""
742 741 class DecoratedCommand(command):
743 742 def run(self):
744 743 self.distribution.run_command('jsversion')
745 744 css = self.distribution.get_command_obj('css')
746 745 css.minify = True
747 746 try:
748 747 self.distribution.run_command('css')
749 748 except Exception as e:
750 749 if strict:
751 750 raise
752 751 else:
753 752 log.warn("Failed to build css sourcemaps: %s" % e)
754 753 command.run(self)
755 754 return DecoratedCommand
General Comments 0
You need to be logged in to leave comments. Login now