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