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