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