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