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