##// END OF EJS Templates
update references for IPython.html
MinRK -
Show More
@@ -1,306 +1,306 b''
1 1 # encoding: utf-8
2 2 """
3 3 An application for managing IPython profiles.
4 4
5 5 To be invoked as the `ipython profile` subcommand.
6 6
7 7 Authors:
8 8
9 9 * Min RK
10 10
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Copyright (C) 2008-2011 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
24 24 import os
25 25
26 26 from IPython.config.application import Application
27 27 from IPython.core.application import (
28 28 BaseIPythonApplication, base_flags
29 29 )
30 30 from IPython.core.profiledir import ProfileDir
31 31 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
32 32 from IPython.utils.traitlets import Unicode, Bool, Dict
33 33
34 34 #-----------------------------------------------------------------------------
35 35 # Constants
36 36 #-----------------------------------------------------------------------------
37 37
38 38 create_help = """Create an IPython profile by name
39 39
40 40 Create an ipython profile directory by its name or
41 41 profile directory path. Profile directories contain
42 42 configuration, log and security related files and are named
43 43 using the convention 'profile_<name>'. By default they are
44 44 located in your ipython directory. Once created, you will
45 45 can edit the configuration files in the profile
46 46 directory to configure IPython. Most users will create a
47 47 profile directory by name,
48 48 `ipython profile create myprofile`, which will put the directory
49 49 in `<ipython_dir>/profile_myprofile`.
50 50 """
51 51 list_help = """List available IPython profiles
52 52
53 53 List all available profiles, by profile location, that can
54 54 be found in the current working directly or in the ipython
55 55 directory. Profile directories are named using the convention
56 56 'profile_<profile>'.
57 57 """
58 58 profile_help = """Manage IPython profiles
59 59
60 60 Profile directories contain
61 61 configuration, log and security related files and are named
62 62 using the convention 'profile_<name>'. By default they are
63 63 located in your ipython directory. You can create profiles
64 64 with `ipython profile create <name>`, or see the profiles you
65 65 already have with `ipython profile list`
66 66
67 67 To get started configuring IPython, simply do:
68 68
69 69 $> ipython profile create
70 70
71 71 and IPython will create the default profile in <ipython_dir>/profile_default,
72 72 where you can edit ipython_config.py to start configuring IPython.
73 73
74 74 """
75 75
76 76 _list_examples = "ipython profile list # list all profiles"
77 77
78 78 _create_examples = """
79 79 ipython profile create foo # create profile foo w/ default config files
80 80 ipython profile create foo --reset # restage default config files over current
81 81 ipython profile create foo --parallel # also stage parallel config files
82 82 """
83 83
84 84 _main_examples = """
85 85 ipython profile create -h # show the help string for the create subcommand
86 86 ipython profile list -h # show the help string for the list subcommand
87 87
88 88 ipython locate profile foo # print the path to the directory for profile 'foo'
89 89 """
90 90
91 91 #-----------------------------------------------------------------------------
92 92 # Profile Application Class (for `ipython profile` subcommand)
93 93 #-----------------------------------------------------------------------------
94 94
95 95
96 96 def list_profiles_in(path):
97 97 """list profiles in a given root directory"""
98 98 files = os.listdir(path)
99 99 profiles = []
100 100 for f in files:
101 101 full_path = os.path.join(path, f)
102 102 if os.path.isdir(full_path) and f.startswith('profile_'):
103 103 profiles.append(f.split('_',1)[-1])
104 104 return profiles
105 105
106 106
107 107 def list_bundled_profiles():
108 108 """list profiles that are bundled with IPython."""
109 109 path = os.path.join(get_ipython_package_dir(), u'config', u'profile')
110 110 files = os.listdir(path)
111 111 profiles = []
112 112 for profile in files:
113 113 full_path = os.path.join(path, profile)
114 114 if os.path.isdir(full_path) and profile != "__pycache__":
115 115 profiles.append(profile)
116 116 return profiles
117 117
118 118
119 119 class ProfileLocate(BaseIPythonApplication):
120 120 description = """print the path an IPython profile dir"""
121 121
122 122 def parse_command_line(self, argv=None):
123 123 super(ProfileLocate, self).parse_command_line(argv)
124 124 if self.extra_args:
125 125 self.profile = self.extra_args[0]
126 126
127 127 def start(self):
128 128 print self.profile_dir.location
129 129
130 130
131 131 class ProfileList(Application):
132 132 name = u'ipython-profile'
133 133 description = list_help
134 134 examples = _list_examples
135 135
136 136 aliases = Dict({
137 137 'ipython-dir' : 'ProfileList.ipython_dir',
138 138 'log-level' : 'Application.log_level',
139 139 })
140 140 flags = Dict(dict(
141 141 debug = ({'Application' : {'log_level' : 0}},
142 142 "Set Application.log_level to 0, maximizing log output."
143 143 )
144 144 ))
145 145
146 146 ipython_dir = Unicode(get_ipython_dir(), config=True,
147 147 help="""
148 148 The name of the IPython directory. This directory is used for logging
149 149 configuration (through profiles), history storage, etc. The default
150 150 is usually $HOME/.ipython. This options can also be specified through
151 151 the environment variable IPYTHONDIR.
152 152 """
153 153 )
154 154
155 155
156 156 def _print_profiles(self, profiles):
157 157 """print list of profiles, indented."""
158 158 for profile in profiles:
159 159 print ' %s' % profile
160 160
161 161 def list_profile_dirs(self):
162 162 profiles = list_bundled_profiles()
163 163 if profiles:
164 164 print
165 165 print "Available profiles in IPython:"
166 166 self._print_profiles(profiles)
167 167 print
168 168 print " The first request for a bundled profile will copy it"
169 169 print " into your IPython directory (%s)," % self.ipython_dir
170 170 print " where you can customize it."
171 171
172 172 profiles = list_profiles_in(self.ipython_dir)
173 173 if profiles:
174 174 print
175 175 print "Available profiles in %s:" % self.ipython_dir
176 176 self._print_profiles(profiles)
177 177
178 178 profiles = list_profiles_in(os.getcwdu())
179 179 if profiles:
180 180 print
181 181 print "Available profiles in current directory (%s):" % os.getcwdu()
182 182 self._print_profiles(profiles)
183 183
184 184 print
185 185 print "To use any of the above profiles, start IPython with:"
186 186 print " ipython --profile=<name>"
187 187 print
188 188
189 189 def start(self):
190 190 self.list_profile_dirs()
191 191
192 192
193 193 create_flags = {}
194 194 create_flags.update(base_flags)
195 195 # don't include '--init' flag, which implies running profile create in other apps
196 196 create_flags.pop('init')
197 197 create_flags['reset'] = ({'ProfileCreate': {'overwrite' : True}},
198 198 "reset config files in this profile to the defaults.")
199 199 create_flags['parallel'] = ({'ProfileCreate': {'parallel' : True}},
200 200 "Include the config files for parallel "
201 201 "computing apps (ipengine, ipcontroller, etc.)")
202 202
203 203
204 204 class ProfileCreate(BaseIPythonApplication):
205 205 name = u'ipython-profile'
206 206 description = create_help
207 207 examples = _create_examples
208 208 auto_create = Bool(True, config=False)
209 209
210 210 def _copy_config_files_default(self):
211 211 return True
212 212
213 213 parallel = Bool(False, config=True,
214 214 help="whether to include parallel computing config files")
215 215 def _parallel_changed(self, name, old, new):
216 216 parallel_files = [ 'ipcontroller_config.py',
217 217 'ipengine_config.py',
218 218 'ipcluster_config.py'
219 219 ]
220 220 if new:
221 221 for cf in parallel_files:
222 222 self.config_files.append(cf)
223 223 else:
224 224 for cf in parallel_files:
225 225 if cf in self.config_files:
226 226 self.config_files.remove(cf)
227 227
228 228 def parse_command_line(self, argv):
229 229 super(ProfileCreate, self).parse_command_line(argv)
230 230 # accept positional arg as profile name
231 231 if self.extra_args:
232 232 self.profile = self.extra_args[0]
233 233
234 234 flags = Dict(create_flags)
235 235
236 236 classes = [ProfileDir]
237 237
238 238 def init_config_files(self):
239 239 super(ProfileCreate, self).init_config_files()
240 240 # use local imports, since these classes may import from here
241 241 from IPython.terminal.ipapp import TerminalIPythonApp
242 242 apps = [TerminalIPythonApp]
243 243 try:
244 244 from IPython.qt.console.qtconsoleapp import IPythonQtConsoleApp
245 245 except Exception:
246 246 # this should be ImportError, but under weird circumstances
247 247 # this might be an AttributeError, or possibly others
248 248 # in any case, nothing should cause the profile creation to crash.
249 249 pass
250 250 else:
251 251 apps.append(IPythonQtConsoleApp)
252 252 try:
253 from IPython.html.notebook.notebookapp import NotebookApp
253 from IPython.html.notebookapp import NotebookApp
254 254 except ImportError:
255 255 pass
256 256 except Exception:
257 257 self.log.debug('Unexpected error when importing NotebookApp',
258 258 exc_info=True
259 259 )
260 260 else:
261 261 apps.append(NotebookApp)
262 262 if self.parallel:
263 263 from IPython.parallel.apps.ipcontrollerapp import IPControllerApp
264 264 from IPython.parallel.apps.ipengineapp import IPEngineApp
265 265 from IPython.parallel.apps.ipclusterapp import IPClusterStart
266 266 from IPython.parallel.apps.iploggerapp import IPLoggerApp
267 267 apps.extend([
268 268 IPControllerApp,
269 269 IPEngineApp,
270 270 IPClusterStart,
271 271 IPLoggerApp,
272 272 ])
273 273 for App in apps:
274 274 app = App()
275 275 app.config.update(self.config)
276 276 app.log = self.log
277 277 app.overwrite = self.overwrite
278 278 app.copy_config_files=True
279 279 app.profile = self.profile
280 280 app.init_profile_dir()
281 281 app.init_config_files()
282 282
283 283 def stage_default_config_file(self):
284 284 pass
285 285
286 286
287 287 class ProfileApp(Application):
288 288 name = u'ipython-profile'
289 289 description = profile_help
290 290 examples = _main_examples
291 291
292 292 subcommands = Dict(dict(
293 293 create = (ProfileCreate, ProfileCreate.description.splitlines()[0]),
294 294 list = (ProfileList, ProfileList.description.splitlines()[0]),
295 295 ))
296 296
297 297 def start(self):
298 298 if self.subapp is None:
299 299 print "No subcommand specified. Must specify one of: %s"%(self.subcommands.keys())
300 300 print
301 301 self.print_description()
302 302 self.print_subcommands()
303 303 self.exit(1)
304 304 else:
305 305 return self.subapp.start()
306 306
@@ -1,38 +1,38 b''
1 1 #-----------------------------------------------------------------------------
2 2 # Copyright (C) 2010-2011 The IPython Development Team.
3 3 #
4 4 # Distributed under the terms of the BSD License.
5 5 #
6 6 # The full license is in the file COPYING.txt, distributed with this software.
7 7 #-----------------------------------------------------------------------------
8 8 import os
9 9
10 10 import nose.tools as nt
11 11
12 12 from IPython.core import display
13 13 from IPython.utils import path as ipath
14 14
15 15 def test_image_size():
16 16 """Simple test for display.Image(args, width=x,height=y)"""
17 17 thisurl = 'http://www.google.fr/images/srpr/logo3w.png'
18 18 img = display.Image(url=thisurl, width=200, height=200)
19 19 nt.assert_equal(u'<img src="%s" width="200" height="200"/>' % (thisurl), img._repr_html_())
20 20 img = display.Image(url=thisurl, width=200)
21 21 nt.assert_equal(u'<img src="%s" width="200"/>' % (thisurl), img._repr_html_())
22 22 img = display.Image(url=thisurl)
23 23 nt.assert_equal(u'<img src="%s"/>' % (thisurl), img._repr_html_())
24 24
25 25 def test_image_filename_defaults():
26 26 '''test format constraint, and validity of jpeg and png'''
27 27 tpath = ipath.get_ipython_package_dir()
28 28 nt.assert_raises(ValueError, display.Image, filename=os.path.join(tpath, 'testing/tests/badformat.gif'),
29 29 embed=True)
30 30 nt.assert_raises(ValueError, display.Image)
31 31 nt.assert_raises(ValueError, display.Image, data='this is not an image', format='badformat', embed=True)
32 imgfile = os.path.join(tpath, 'html/notebook/static/base/images/ipynblogo.png')
32 imgfile = os.path.join(tpath, 'html/static/base/images/ipynblogo.png')
33 33 img = display.Image(filename=imgfile)
34 34 nt.assert_equal('png', img.format)
35 35 nt.assert_is_not_none(img._repr_png_())
36 36 img = display.Image(filename=os.path.join(tpath, 'testing/tests/logo.jpg'), embed=False)
37 37 nt.assert_equal('jpeg', img.format)
38 38 nt.assert_is_none(img._repr_jpeg_())
@@ -1,73 +1,73 b''
1 1 # IPython Notebook development
2 2
3 3 ## Development dependencies
4 4
5 5 Developers of the IPython Notebook will need to install the following tools:
6 6
7 7 * fabric
8 8 * node.js
9 9 * less (`npm install -g less`)
10 10 * bower (`npm install -g bower`)
11 11
12 12 ## Components
13 13
14 14 We are moving to a model where our JavaScript dependencies are managed using
15 15 [bower](http://bower.io/). These packages are installed in `static/components`
16 16 and commited into our git repo. Our dependencies are described in the file
17 17 `static/bower.json`. To update our bower packages, run `fab components` in this
18 18 directory.
19 19
20 20 Because CodeMirror does not use proper semantic versioning for its GitHub tags,
21 21 we maintain our own fork of CodeMirror that is used with bower. This fork should
22 22 track the upstream CodeMirror exactly; the only difference is that we are adding
23 23 semantic versioned tags to our repo.
24 24
25 25 ## less
26 26
27 27 If you edit our `.less` files you will need to run the less compiler to build
28 28 our minified css files. This can be done by running `fab css` from this directory.
29 29
30 30 ## JavaScript Documentation
31 31
32 32
33 33 How to Build/ view the doc for JavaScript. JavaScript documentation should follow a
34 34 style close to JSDoc one, so you should be able to build them with your favorite
35 35 documentation builder. Still the documentation comment are mainly written to be read
36 36 with YUI doc. You can either build a static version, or start a YUIdoc server that
37 37 will live update the doc at every page request.
38 38
39 39
40 40
41 41 To do so, you will need to install YUIdoc.
42 42
43 43 ### Install NodeJS
44 44
45 45 Node is a browser less javascript interpreter. To install it please refer to
46 46 the documentation for your platform. Install also NPM (node package manager) if
47 47 it does not come bundled with it.
48 48
49 49 ### Get YUIdoc
50 50
51 51 npm does by default install package in `./node_modules` instead of doing a
52 52 system wide install. I'll leave you to yuidoc docs if you want to make a system
53 53 wide install.
54 54
55 55 First, cd into js directory :
56 56 ```bash
57 cd IPython/html/notebook/static/js/
57 cd IPython/html/static/js/
58 58 # install yuidoc
59 59 npm install yuidocjs
60 60 ```
61 61
62 62
63 63 ### Run YUIdoc server
64 64
65 From IPython/html/notebook/static/js/
65 From IPython/html/static/js/
66 66 ```bash
67 67 # run yuidoc for install dir
68 68 ./node_modules/yuidocjs/lib/cli.js --server .
69 69 ```
70 70
71 71 Follow the instruction and the documentation should be available on localhost:3000
72 72
73 73 Omitting `--server` will build a static version in the `out` folder by default.
@@ -1,753 +1,753 b''
1 1 # coding: utf-8
2 2 """A tornado based IPython notebook server.
3 3
4 4 Authors:
5 5
6 6 * Brian Granger
7 7 """
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2013 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 # stdlib
20 20 import errno
21 21 import logging
22 22 import os
23 23 import random
24 24 import select
25 25 import signal
26 26 import socket
27 27 import sys
28 28 import threading
29 29 import time
30 30 import uuid
31 31 import webbrowser
32 32
33 33
34 34 # Third party
35 35 # check for pyzmq 2.1.11
36 36 from IPython.utils.zmqrelated import check_for_zmq
37 check_for_zmq('2.1.11', 'IPython.html.notebook')
37 check_for_zmq('2.1.11', 'IPython.html')
38 38
39 39 import zmq
40 40 from jinja2 import Environment, FileSystemLoader
41 41
42 42 # Install the pyzmq ioloop. This has to be done before anything else from
43 43 # tornado is imported.
44 44 from zmq.eventloop import ioloop
45 45 ioloop.install()
46 46
47 47 # check for tornado 2.1.0
48 48 msg = "The IPython Notebook requires tornado >= 2.1.0"
49 49 try:
50 50 import tornado
51 51 except ImportError:
52 52 raise ImportError(msg)
53 53 try:
54 54 version_info = tornado.version_info
55 55 except AttributeError:
56 56 raise ImportError(msg + ", but you have < 1.1.0")
57 57 if version_info < (2,1,0):
58 58 raise ImportError(msg + ", but you have %s" % tornado.version)
59 59
60 60 from tornado import httpserver
61 61 from tornado import web
62 62
63 63 # Our own libraries
64 from IPython.html.notebook import DEFAULT_STATIC_FILES_PATH
64 from IPython.html import DEFAULT_STATIC_FILES_PATH
65 65
66 66 from .services.kernels.kernelmanager import MappingKernelManager
67 67 from .services.notebooks.nbmanager import NotebookManager
68 68 from .services.notebooks.filenbmanager import FileNotebookManager
69 69 from .services.clusters.clustermanager import ClusterManager
70 70
71 71 from .base.handlers import AuthenticatedFileHandler, FileFindHandler
72 72
73 73 from IPython.config.application import catch_config_error, boolean_flag
74 74 from IPython.core.application import BaseIPythonApplication
75 75 from IPython.consoleapp import IPythonConsoleApp
76 76 from IPython.kernel import swallow_argv
77 77 from IPython.kernel.zmq.session import default_secure
78 78 from IPython.kernel.zmq.kernelapp import (
79 79 kernel_flags,
80 80 kernel_aliases,
81 81 )
82 82 from IPython.utils.importstring import import_item
83 83 from IPython.utils.localinterfaces import LOCALHOST
84 84 from IPython.utils import submodule
85 85 from IPython.utils.traitlets import (
86 86 Dict, Unicode, Integer, List, Bool, Bytes,
87 87 DottedObjectName
88 88 )
89 89 from IPython.utils import py3compat
90 90 from IPython.utils.path import filefind
91 91
92 92 from .utils import url_path_join
93 93
94 94 #-----------------------------------------------------------------------------
95 95 # Module globals
96 96 #-----------------------------------------------------------------------------
97 97
98 98 _examples = """
99 99 ipython notebook # start the notebook
100 100 ipython notebook --profile=sympy # use the sympy profile
101 101 ipython notebook --pylab=inline # pylab in inline plotting mode
102 102 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
103 103 ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces
104 104 """
105 105
106 106 #-----------------------------------------------------------------------------
107 107 # Helper functions
108 108 #-----------------------------------------------------------------------------
109 109
110 110 def random_ports(port, n):
111 111 """Generate a list of n random ports near the given port.
112 112
113 113 The first 5 ports will be sequential, and the remaining n-5 will be
114 114 randomly selected in the range [port-2*n, port+2*n].
115 115 """
116 116 for i in range(min(5, n)):
117 117 yield port + i
118 118 for i in range(n-5):
119 119 yield port + random.randint(-2*n, 2*n)
120 120
121 121 def load_handlers(name):
122 122 """Load the (URL pattern, handler) tuples for each component."""
123 name = 'IPython.html.notebook.' + name
123 name = 'IPython.html.' + name
124 124 mod = __import__(name, fromlist=['default_handlers'])
125 125 return mod.default_handlers
126 126
127 127 #-----------------------------------------------------------------------------
128 128 # The Tornado web application
129 129 #-----------------------------------------------------------------------------
130 130
131 131 class NotebookWebApplication(web.Application):
132 132
133 133 def __init__(self, ipython_app, kernel_manager, notebook_manager,
134 134 cluster_manager, log,
135 135 base_project_url, settings_overrides):
136 136
137 137 settings = self.init_settings(
138 138 ipython_app, kernel_manager, notebook_manager, cluster_manager,
139 139 log, base_project_url, settings_overrides)
140 140 handlers = self.init_handlers(settings)
141 141
142 142 super(NotebookWebApplication, self).__init__(handlers, **settings)
143 143
144 144 def init_settings(self, ipython_app, kernel_manager, notebook_manager,
145 145 cluster_manager, log,
146 146 base_project_url, settings_overrides):
147 147 # Python < 2.6.5 doesn't accept unicode keys in f(**kwargs), and
148 148 # base_project_url will always be unicode, which will in turn
149 149 # make the patterns unicode, and ultimately result in unicode
150 150 # keys in kwargs to handler._execute(**kwargs) in tornado.
151 151 # This enforces that base_project_url be ascii in that situation.
152 152 #
153 153 # Note that the URLs these patterns check against are escaped,
154 154 # and thus guaranteed to be ASCII: 'hΓ©llo' is really 'h%C3%A9llo'.
155 155 base_project_url = py3compat.unicode_to_str(base_project_url, 'ascii')
156 156 template_path = os.path.join(os.path.dirname(__file__), "templates")
157 157 settings = dict(
158 158 # basics
159 159 base_project_url=base_project_url,
160 160 base_kernel_url=ipython_app.base_kernel_url,
161 161 template_path=template_path,
162 162 static_path=ipython_app.static_file_path,
163 163 static_handler_class = FileFindHandler,
164 164 static_url_prefix = url_path_join(base_project_url,'/static/'),
165 165
166 166 # authentication
167 167 cookie_secret=ipython_app.cookie_secret,
168 168 login_url=url_path_join(base_project_url,'/login'),
169 169 read_only=ipython_app.read_only,
170 170 password=ipython_app.password,
171 171
172 172 # managers
173 173 kernel_manager=kernel_manager,
174 174 notebook_manager=notebook_manager,
175 175 cluster_manager=cluster_manager,
176 176
177 177 # IPython stuff
178 178 mathjax_url=ipython_app.mathjax_url,
179 179 max_msg_size=ipython_app.max_msg_size,
180 180 config=ipython_app.config,
181 181 use_less=ipython_app.use_less,
182 182 jinja2_env=Environment(loader=FileSystemLoader(template_path)),
183 183 )
184 184
185 185 # allow custom overrides for the tornado web app.
186 186 settings.update(settings_overrides)
187 187 return settings
188 188
189 189 def init_handlers(self, settings):
190 190 # Load the (URL pattern, handler) tuples for each component.
191 191 handlers = []
192 192 handlers.extend(load_handlers('base.handlers'))
193 193 handlers.extend(load_handlers('tree.handlers'))
194 194 handlers.extend(load_handlers('auth.login'))
195 195 handlers.extend(load_handlers('auth.logout'))
196 196 handlers.extend(load_handlers('notebook.handlers'))
197 197 handlers.extend(load_handlers('services.kernels.handlers'))
198 198 handlers.extend(load_handlers('services.notebooks.handlers'))
199 199 handlers.extend(load_handlers('services.clusters.handlers'))
200 200 handlers.extend([
201 201 (r"/files/(.*)", AuthenticatedFileHandler, {'path' : settings['notebook_manager'].notebook_dir}),
202 202 ])
203 203 # prepend base_project_url onto the patterns that we match
204 204 new_handlers = []
205 205 for handler in handlers:
206 206 pattern = url_path_join(settings['base_project_url'], handler[0])
207 207 new_handler = tuple([pattern] + list(handler[1:]))
208 208 new_handlers.append(new_handler)
209 209 return new_handlers
210 210
211 211
212 212
213 213 #-----------------------------------------------------------------------------
214 214 # Aliases and Flags
215 215 #-----------------------------------------------------------------------------
216 216
217 217 flags = dict(kernel_flags)
218 218 flags['no-browser']=(
219 219 {'NotebookApp' : {'open_browser' : False}},
220 220 "Don't open the notebook in a browser after startup."
221 221 )
222 222 flags['no-mathjax']=(
223 223 {'NotebookApp' : {'enable_mathjax' : False}},
224 224 """Disable MathJax
225 225
226 226 MathJax is the javascript library IPython uses to render math/LaTeX. It is
227 227 very large, so you may want to disable it if you have a slow internet
228 228 connection, or for offline use of the notebook.
229 229
230 230 When disabled, equations etc. will appear as their untransformed TeX source.
231 231 """
232 232 )
233 233 flags['read-only'] = (
234 234 {'NotebookApp' : {'read_only' : True}},
235 235 """Allow read-only access to notebooks.
236 236
237 237 When using a password to protect the notebook server, this flag
238 238 allows unauthenticated clients to view the notebook list, and
239 239 individual notebooks, but not edit them, start kernels, or run
240 240 code.
241 241
242 242 If no password is set, the server will be entirely read-only.
243 243 """
244 244 )
245 245
246 246 # Add notebook manager flags
247 247 flags.update(boolean_flag('script', 'FileNotebookManager.save_script',
248 248 'Auto-save a .py script everytime the .ipynb notebook is saved',
249 249 'Do not auto-save .py scripts for every notebook'))
250 250
251 251 # the flags that are specific to the frontend
252 252 # these must be scrubbed before being passed to the kernel,
253 253 # or it will raise an error on unrecognized flags
254 254 notebook_flags = ['no-browser', 'no-mathjax', 'read-only', 'script', 'no-script']
255 255
256 256 aliases = dict(kernel_aliases)
257 257
258 258 aliases.update({
259 259 'ip': 'NotebookApp.ip',
260 260 'port': 'NotebookApp.port',
261 261 'port-retries': 'NotebookApp.port_retries',
262 262 'transport': 'KernelManager.transport',
263 263 'keyfile': 'NotebookApp.keyfile',
264 264 'certfile': 'NotebookApp.certfile',
265 265 'notebook-dir': 'NotebookManager.notebook_dir',
266 266 'browser': 'NotebookApp.browser',
267 267 })
268 268
269 269 # remove ipkernel flags that are singletons, and don't make sense in
270 270 # multi-kernel evironment:
271 271 aliases.pop('f', None)
272 272
273 273 notebook_aliases = [u'port', u'port-retries', u'ip', u'keyfile', u'certfile',
274 274 u'notebook-dir']
275 275
276 276 #-----------------------------------------------------------------------------
277 277 # NotebookApp
278 278 #-----------------------------------------------------------------------------
279 279
280 280 class NotebookApp(BaseIPythonApplication):
281 281
282 282 name = 'ipython-notebook'
283 283 default_config_file_name='ipython_notebook_config.py'
284 284
285 285 description = """
286 286 The IPython HTML Notebook.
287 287
288 288 This launches a Tornado based HTML Notebook Server that serves up an
289 289 HTML5/Javascript Notebook client.
290 290 """
291 291 examples = _examples
292 292
293 293 classes = IPythonConsoleApp.classes + [MappingKernelManager, NotebookManager,
294 294 FileNotebookManager]
295 295 flags = Dict(flags)
296 296 aliases = Dict(aliases)
297 297
298 298 kernel_argv = List(Unicode)
299 299
300 300 max_msg_size = Integer(65536, config=True, help="""
301 301 The max raw message size accepted from the browser
302 302 over a WebSocket connection.
303 303 """)
304 304
305 305 def _log_level_default(self):
306 306 return logging.INFO
307 307
308 308 def _log_format_default(self):
309 309 """override default log format to include time"""
310 310 return u"%(asctime)s.%(msecs).03d [%(name)s]%(highlevel)s %(message)s"
311 311
312 312 # create requested profiles by default, if they don't exist:
313 313 auto_create = Bool(True)
314 314
315 315 # file to be opened in the notebook server
316 316 file_to_run = Unicode('')
317 317
318 318 # Network related information.
319 319
320 320 ip = Unicode(LOCALHOST, config=True,
321 321 help="The IP address the notebook server will listen on."
322 322 )
323 323
324 324 def _ip_changed(self, name, old, new):
325 325 if new == u'*': self.ip = u''
326 326
327 327 port = Integer(8888, config=True,
328 328 help="The port the notebook server will listen on."
329 329 )
330 330 port_retries = Integer(50, config=True,
331 331 help="The number of additional ports to try if the specified port is not available."
332 332 )
333 333
334 334 certfile = Unicode(u'', config=True,
335 335 help="""The full path to an SSL/TLS certificate file."""
336 336 )
337 337
338 338 keyfile = Unicode(u'', config=True,
339 339 help="""The full path to a private key file for usage with SSL/TLS."""
340 340 )
341 341
342 342 cookie_secret = Bytes(b'', config=True,
343 343 help="""The random bytes used to secure cookies.
344 344 By default this is a new random number every time you start the Notebook.
345 345 Set it to a value in a config file to enable logins to persist across server sessions.
346 346
347 347 Note: Cookie secrets should be kept private, do not share config files with
348 348 cookie_secret stored in plaintext (you can read the value from a file).
349 349 """
350 350 )
351 351 def _cookie_secret_default(self):
352 352 return os.urandom(1024)
353 353
354 354 password = Unicode(u'', config=True,
355 355 help="""Hashed password to use for web authentication.
356 356
357 357 To generate, type in a python/IPython shell:
358 358
359 359 from IPython.lib import passwd; passwd()
360 360
361 361 The string should be of the form type:salt:hashed-password.
362 362 """
363 363 )
364 364
365 365 open_browser = Bool(True, config=True,
366 366 help="""Whether to open in a browser after starting.
367 367 The specific browser used is platform dependent and
368 368 determined by the python standard library `webbrowser`
369 369 module, unless it is overridden using the --browser
370 370 (NotebookApp.browser) configuration option.
371 371 """)
372 372
373 373 browser = Unicode(u'', config=True,
374 374 help="""Specify what command to use to invoke a web
375 375 browser when opening the notebook. If not specified, the
376 376 default browser will be determined by the `webbrowser`
377 377 standard library module, which allows setting of the
378 378 BROWSER environment variable to override it.
379 379 """)
380 380
381 381 read_only = Bool(False, config=True,
382 382 help="Whether to prevent editing/execution of notebooks."
383 383 )
384 384
385 385 use_less = Bool(False, config=True,
386 386 help="""Wether to use Browser Side less-css parsing
387 387 instead of compiled css version in templates that allows
388 388 it. This is mainly convenient when working on the less
389 389 file to avoid a build step, or if user want to overwrite
390 390 some of the less variables without having to recompile
391 391 everything.
392 392
393 393 You will need to install the less.js component in the static directory
394 394 either in the source tree or in your profile folder.
395 395 """)
396 396
397 397 webapp_settings = Dict(config=True,
398 398 help="Supply overrides for the tornado.web.Application that the "
399 399 "IPython notebook uses.")
400 400
401 401 enable_mathjax = Bool(True, config=True,
402 402 help="""Whether to enable MathJax for typesetting math/TeX
403 403
404 404 MathJax is the javascript library IPython uses to render math/LaTeX. It is
405 405 very large, so you may want to disable it if you have a slow internet
406 406 connection, or for offline use of the notebook.
407 407
408 408 When disabled, equations etc. will appear as their untransformed TeX source.
409 409 """
410 410 )
411 411 def _enable_mathjax_changed(self, name, old, new):
412 412 """set mathjax url to empty if mathjax is disabled"""
413 413 if not new:
414 414 self.mathjax_url = u''
415 415
416 416 base_project_url = Unicode('/', config=True,
417 417 help='''The base URL for the notebook server.
418 418
419 419 Leading and trailing slashes can be omitted,
420 420 and will automatically be added.
421 421 ''')
422 422 def _base_project_url_changed(self, name, old, new):
423 423 if not new.startswith('/'):
424 424 self.base_project_url = '/'+new
425 425 elif not new.endswith('/'):
426 426 self.base_project_url = new+'/'
427 427
428 428 base_kernel_url = Unicode('/', config=True,
429 429 help='''The base URL for the kernel server
430 430
431 431 Leading and trailing slashes can be omitted,
432 432 and will automatically be added.
433 433 ''')
434 434 def _base_kernel_url_changed(self, name, old, new):
435 435 if not new.startswith('/'):
436 436 self.base_kernel_url = '/'+new
437 437 elif not new.endswith('/'):
438 438 self.base_kernel_url = new+'/'
439 439
440 440 websocket_url = Unicode("", config=True,
441 441 help="""The base URL for the websocket server,
442 442 if it differs from the HTTP server (hint: it almost certainly doesn't).
443 443
444 444 Should be in the form of an HTTP origin: ws[s]://hostname[:port]
445 445 """
446 446 )
447 447
448 448 extra_static_paths = List(Unicode, config=True,
449 449 help="""Extra paths to search for serving static files.
450 450
451 451 This allows adding javascript/css to be available from the notebook server machine,
452 452 or overriding individual files in the IPython"""
453 453 )
454 454 def _extra_static_paths_default(self):
455 455 return [os.path.join(self.profile_dir.location, 'static')]
456 456
457 457 @property
458 458 def static_file_path(self):
459 459 """return extra paths + the default location"""
460 460 return self.extra_static_paths + [DEFAULT_STATIC_FILES_PATH]
461 461
462 462 mathjax_url = Unicode("", config=True,
463 463 help="""The url for MathJax.js."""
464 464 )
465 465 def _mathjax_url_default(self):
466 466 if not self.enable_mathjax:
467 467 return u''
468 468 static_url_prefix = self.webapp_settings.get("static_url_prefix",
469 469 "/static/")
470 470 try:
471 471 mathjax = filefind(os.path.join('mathjax', 'MathJax.js'), self.static_file_path)
472 472 except IOError:
473 473 if self.certfile:
474 474 # HTTPS: load from Rackspace CDN, because SSL certificate requires it
475 475 base = u"https://c328740.ssl.cf1.rackcdn.com"
476 476 else:
477 477 base = u"http://cdn.mathjax.org"
478 478
479 479 url = base + u"/mathjax/latest/MathJax.js"
480 480 self.log.info("Using MathJax from CDN: %s", url)
481 481 return url
482 482 else:
483 483 self.log.info("Using local MathJax from %s" % mathjax)
484 484 return static_url_prefix+u"mathjax/MathJax.js"
485 485
486 486 def _mathjax_url_changed(self, name, old, new):
487 487 if new and not self.enable_mathjax:
488 488 # enable_mathjax=False overrides mathjax_url
489 489 self.mathjax_url = u''
490 490 else:
491 491 self.log.info("Using MathJax: %s", new)
492 492
493 notebook_manager_class = DottedObjectName('IPython.html.notebook.services.notebooks.filenbmanager.FileNotebookManager',
493 notebook_manager_class = DottedObjectName('IPython.html.services.notebooks.filenbmanager.FileNotebookManager',
494 494 config=True,
495 495 help='The notebook manager class to use.')
496 496
497 497 trust_xheaders = Bool(False, config=True,
498 498 help=("Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headers"
499 499 "sent by the upstream reverse proxy. Neccesary if the proxy handles SSL")
500 500 )
501 501
502 502 def parse_command_line(self, argv=None):
503 503 super(NotebookApp, self).parse_command_line(argv)
504 504 if argv is None:
505 505 argv = sys.argv[1:]
506 506
507 507 # Scrub frontend-specific flags
508 508 self.kernel_argv = swallow_argv(argv, notebook_aliases, notebook_flags)
509 509 # Kernel should inherit default config file from frontend
510 510 self.kernel_argv.append("--IPKernelApp.parent_appname='%s'" % self.name)
511 511
512 512 if self.extra_args:
513 513 f = os.path.abspath(self.extra_args[0])
514 514 if os.path.isdir(f):
515 515 nbdir = f
516 516 else:
517 517 self.file_to_run = f
518 518 nbdir = os.path.dirname(f)
519 519 self.config.NotebookManager.notebook_dir = nbdir
520 520
521 521 def init_configurables(self):
522 522 # force Session default to be secure
523 523 default_secure(self.config)
524 524 self.kernel_manager = MappingKernelManager(
525 525 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
526 526 connection_dir = self.profile_dir.security_dir,
527 527 )
528 528 kls = import_item(self.notebook_manager_class)
529 529 self.notebook_manager = kls(config=self.config, log=self.log)
530 530 self.notebook_manager.load_notebook_names()
531 531 self.cluster_manager = ClusterManager(config=self.config, log=self.log)
532 532 self.cluster_manager.update_profiles()
533 533
534 534 def init_logging(self):
535 535 # This prevents double log messages because tornado use a root logger that
536 536 # self.log is a child of. The logging module dipatches log messages to a log
537 537 # and all of its ancenstors until propagate is set to False.
538 538 self.log.propagate = False
539 539
540 540 # hook up tornado 3's loggers to our app handlers
541 541 for name in ('access', 'application', 'general'):
542 542 logging.getLogger('tornado.%s' % name).handlers = self.log.handlers
543 543
544 544 def init_webapp(self):
545 545 """initialize tornado webapp and httpserver"""
546 546 self.web_app = NotebookWebApplication(
547 547 self, self.kernel_manager, self.notebook_manager,
548 548 self.cluster_manager, self.log,
549 549 self.base_project_url, self.webapp_settings
550 550 )
551 551 if self.certfile:
552 552 ssl_options = dict(certfile=self.certfile)
553 553 if self.keyfile:
554 554 ssl_options['keyfile'] = self.keyfile
555 555 else:
556 556 ssl_options = None
557 557 self.web_app.password = self.password
558 558 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options,
559 559 xheaders=self.trust_xheaders)
560 560 if not self.ip:
561 561 warning = "WARNING: The notebook server is listening on all IP addresses"
562 562 if ssl_options is None:
563 563 self.log.critical(warning + " and not using encryption. This "
564 564 "is not recommended.")
565 565 if not self.password and not self.read_only:
566 566 self.log.critical(warning + " and not using authentication. "
567 567 "This is highly insecure and not recommended.")
568 568 success = None
569 569 for port in random_ports(self.port, self.port_retries+1):
570 570 try:
571 571 self.http_server.listen(port, self.ip)
572 572 except socket.error as e:
573 573 # XXX: remove the e.errno == -9 block when we require
574 574 # tornado >= 3.0
575 575 if e.errno == -9 and tornado.version_info[0] < 3:
576 576 # The flags passed to socket.getaddrinfo from
577 577 # tornado.netutils.bind_sockets can cause "gaierror:
578 578 # [Errno -9] Address family for hostname not supported"
579 579 # when the interface is not associated, for example.
580 580 # Changing the flags to exclude socket.AI_ADDRCONFIG does
581 581 # not cause this error, but the only way to do this is to
582 582 # monkeypatch socket to remove the AI_ADDRCONFIG attribute
583 583 saved_AI_ADDRCONFIG = socket.AI_ADDRCONFIG
584 584 self.log.warn('Monkeypatching socket to fix tornado bug')
585 585 del(socket.AI_ADDRCONFIG)
586 586 try:
587 587 # retry the tornado call without AI_ADDRCONFIG flags
588 588 self.http_server.listen(port, self.ip)
589 589 except socket.error as e2:
590 590 e = e2
591 591 else:
592 592 self.port = port
593 593 success = True
594 594 break
595 595 # restore the monekypatch
596 596 socket.AI_ADDRCONFIG = saved_AI_ADDRCONFIG
597 597 if e.errno != errno.EADDRINUSE:
598 598 raise
599 599 self.log.info('The port %i is already in use, trying another random port.' % port)
600 600 else:
601 601 self.port = port
602 602 success = True
603 603 break
604 604 if not success:
605 605 self.log.critical('ERROR: the notebook server could not be started because '
606 606 'no available port could be found.')
607 607 self.exit(1)
608 608
609 609 def init_signal(self):
610 610 if not sys.platform.startswith('win'):
611 611 signal.signal(signal.SIGINT, self._handle_sigint)
612 612 signal.signal(signal.SIGTERM, self._signal_stop)
613 613 if hasattr(signal, 'SIGUSR1'):
614 614 # Windows doesn't support SIGUSR1
615 615 signal.signal(signal.SIGUSR1, self._signal_info)
616 616 if hasattr(signal, 'SIGINFO'):
617 617 # only on BSD-based systems
618 618 signal.signal(signal.SIGINFO, self._signal_info)
619 619
620 620 def _handle_sigint(self, sig, frame):
621 621 """SIGINT handler spawns confirmation dialog"""
622 622 # register more forceful signal handler for ^C^C case
623 623 signal.signal(signal.SIGINT, self._signal_stop)
624 624 # request confirmation dialog in bg thread, to avoid
625 625 # blocking the App
626 626 thread = threading.Thread(target=self._confirm_exit)
627 627 thread.daemon = True
628 628 thread.start()
629 629
630 630 def _restore_sigint_handler(self):
631 631 """callback for restoring original SIGINT handler"""
632 632 signal.signal(signal.SIGINT, self._handle_sigint)
633 633
634 634 def _confirm_exit(self):
635 635 """confirm shutdown on ^C
636 636
637 637 A second ^C, or answering 'y' within 5s will cause shutdown,
638 638 otherwise original SIGINT handler will be restored.
639 639
640 640 This doesn't work on Windows.
641 641 """
642 642 # FIXME: remove this delay when pyzmq dependency is >= 2.1.11
643 643 time.sleep(0.1)
644 644 info = self.log.info
645 645 info('interrupted')
646 646 print self.notebook_info()
647 647 sys.stdout.write("Shutdown this notebook server (y/[n])? ")
648 648 sys.stdout.flush()
649 649 r,w,x = select.select([sys.stdin], [], [], 5)
650 650 if r:
651 651 line = sys.stdin.readline()
652 652 if line.lower().startswith('y'):
653 653 self.log.critical("Shutdown confirmed")
654 654 ioloop.IOLoop.instance().stop()
655 655 return
656 656 else:
657 657 print "No answer for 5s:",
658 658 print "resuming operation..."
659 659 # no answer, or answer is no:
660 660 # set it back to original SIGINT handler
661 661 # use IOLoop.add_callback because signal.signal must be called
662 662 # from main thread
663 663 ioloop.IOLoop.instance().add_callback(self._restore_sigint_handler)
664 664
665 665 def _signal_stop(self, sig, frame):
666 666 self.log.critical("received signal %s, stopping", sig)
667 667 ioloop.IOLoop.instance().stop()
668 668
669 669 def _signal_info(self, sig, frame):
670 670 print self.notebook_info()
671 671
672 672 def init_components(self):
673 673 """Check the components submodule, and warn if it's unclean"""
674 674 status = submodule.check_submodule_status()
675 675 if status == 'missing':
676 676 self.log.warn("components submodule missing, running `git submodule update`")
677 677 submodule.update_submodules(submodule.ipython_parent())
678 678 elif status == 'unclean':
679 679 self.log.warn("components submodule unclean, you may see 404s on static/components")
680 680 self.log.warn("run `setup.py submodule` or `git submodule update` to update")
681 681
682 682
683 683 @catch_config_error
684 684 def initialize(self, argv=None):
685 685 self.init_logging()
686 686 super(NotebookApp, self).initialize(argv)
687 687 self.init_configurables()
688 688 self.init_components()
689 689 self.init_webapp()
690 690 self.init_signal()
691 691
692 692 def cleanup_kernels(self):
693 693 """Shutdown all kernels.
694 694
695 695 The kernels will shutdown themselves when this process no longer exists,
696 696 but explicit shutdown allows the KernelManagers to cleanup the connection files.
697 697 """
698 698 self.log.info('Shutting down kernels')
699 699 self.kernel_manager.shutdown_all()
700 700
701 701 def notebook_info(self):
702 702 "Return the current working directory and the server url information"
703 703 mgr_info = self.notebook_manager.info_string() + "\n"
704 704 return mgr_info +"The IPython Notebook is running at: %s" % self._url
705 705
706 706 def start(self):
707 707 """ Start the IPython Notebook server app, after initialization
708 708
709 709 This method takes no arguments so all configuration and initialization
710 710 must be done prior to calling this method."""
711 711 ip = self.ip if self.ip else '[all ip addresses on your system]'
712 712 proto = 'https' if self.certfile else 'http'
713 713 info = self.log.info
714 714 self._url = "%s://%s:%i%s" % (proto, ip, self.port,
715 715 self.base_project_url)
716 716 for line in self.notebook_info().split("\n"):
717 717 info(line)
718 718 info("Use Control-C to stop this server and shut down all kernels.")
719 719
720 720 if self.open_browser or self.file_to_run:
721 721 ip = self.ip or LOCALHOST
722 722 try:
723 723 browser = webbrowser.get(self.browser or None)
724 724 except webbrowser.Error as e:
725 725 self.log.warn('No web browser found: %s.' % e)
726 726 browser = None
727 727
728 728 if self.file_to_run:
729 729 name, _ = os.path.splitext(os.path.basename(self.file_to_run))
730 730 url = self.notebook_manager.rev_mapping.get(name, '')
731 731 else:
732 732 url = ''
733 733 if browser:
734 734 b = lambda : browser.open("%s://%s:%i%s%s" % (proto, ip,
735 735 self.port, self.base_project_url, url), new=2)
736 736 threading.Thread(target=b).start()
737 737 try:
738 738 ioloop.IOLoop.instance().start()
739 739 except KeyboardInterrupt:
740 740 info("Interrupted...")
741 741 finally:
742 742 self.cleanup_kernels()
743 743
744 744
745 745 #-----------------------------------------------------------------------------
746 746 # Main entry point
747 747 #-----------------------------------------------------------------------------
748 748
749 749 def launch_new_instance():
750 750 app = NotebookApp.instance()
751 751 app.initialize()
752 752 app.start()
753 753
@@ -1,395 +1,395 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 The :class:`~IPython.core.application.Application` object for the command
5 5 line :command:`ipython` program.
6 6
7 7 Authors
8 8 -------
9 9
10 10 * Brian Granger
11 11 * Fernando Perez
12 12 * Min Ragan-Kelley
13 13 """
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Copyright (C) 2008-2011 The IPython Development Team
17 17 #
18 18 # Distributed under the terms of the BSD License. The full license is in
19 19 # the file COPYING, distributed as part of this software.
20 20 #-----------------------------------------------------------------------------
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Imports
24 24 #-----------------------------------------------------------------------------
25 25
26 26 from __future__ import absolute_import
27 27
28 28 import logging
29 29 import os
30 30 import sys
31 31
32 32 from IPython.config.loader import (
33 33 Config, PyFileConfigLoader, ConfigFileNotFound
34 34 )
35 35 from IPython.config.application import boolean_flag, catch_config_error
36 36 from IPython.core import release
37 37 from IPython.core import usage
38 38 from IPython.core.completer import IPCompleter
39 39 from IPython.core.crashhandler import CrashHandler
40 40 from IPython.core.formatters import PlainTextFormatter
41 41 from IPython.core.history import HistoryManager
42 42 from IPython.core.prompts import PromptManager
43 43 from IPython.core.application import (
44 44 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
45 45 )
46 46 from IPython.core.magics import ScriptMagics
47 47 from IPython.core.shellapp import (
48 48 InteractiveShellApp, shell_flags, shell_aliases
49 49 )
50 50 from IPython.terminal.interactiveshell import TerminalInteractiveShell
51 51 from IPython.utils import warn
52 52 from IPython.utils.path import get_ipython_dir, check_for_old_config
53 53 from IPython.utils.traitlets import (
54 54 Bool, List, Dict, CaselessStrEnum
55 55 )
56 56
57 57 #-----------------------------------------------------------------------------
58 58 # Globals, utilities and helpers
59 59 #-----------------------------------------------------------------------------
60 60
61 61 #: The default config file name for this application.
62 62 default_config_file_name = u'ipython_config.py'
63 63
64 64 _examples = """
65 65 ipython --pylab # start in pylab mode
66 66 ipython --pylab=qt # start in pylab mode with the qt4 backend
67 67 ipython --log-level=DEBUG # set logging to DEBUG
68 68 ipython --profile=foo # start with profile foo
69 69
70 70 ipython qtconsole # start the qtconsole GUI application
71 71 ipython help qtconsole # show the help for the qtconsole subcmd
72 72
73 73 ipython console # start the terminal-based console application
74 74 ipython help console # show the help for the console subcmd
75 75
76 76 ipython notebook # start the IPython notebook
77 77 ipython help notebook # show the help for the notebook subcmd
78 78
79 79 ipython profile create foo # create profile foo w/ default config files
80 80 ipython help profile # show the help for the profile subcmd
81 81
82 82 ipython locate # print the path to the IPython directory
83 83 ipython locate profile foo # print the path to the directory for profile `foo`
84 84 """
85 85
86 86 #-----------------------------------------------------------------------------
87 87 # Crash handler for this application
88 88 #-----------------------------------------------------------------------------
89 89
90 90 class IPAppCrashHandler(CrashHandler):
91 91 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
92 92
93 93 def __init__(self, app):
94 94 contact_name = release.author
95 95 contact_email = release.author_email
96 96 bug_tracker = 'https://github.com/ipython/ipython/issues'
97 97 super(IPAppCrashHandler,self).__init__(
98 98 app, contact_name, contact_email, bug_tracker
99 99 )
100 100
101 101 def make_report(self,traceback):
102 102 """Return a string containing a crash report."""
103 103
104 104 sec_sep = self.section_sep
105 105 # Start with parent report
106 106 report = [super(IPAppCrashHandler, self).make_report(traceback)]
107 107 # Add interactive-specific info we may have
108 108 rpt_add = report.append
109 109 try:
110 110 rpt_add(sec_sep+"History of session input:")
111 111 for line in self.app.shell.user_ns['_ih']:
112 112 rpt_add(line)
113 113 rpt_add('\n*** Last line of input (may not be in above history):\n')
114 114 rpt_add(self.app.shell._last_input_line+'\n')
115 115 except:
116 116 pass
117 117
118 118 return ''.join(report)
119 119
120 120 #-----------------------------------------------------------------------------
121 121 # Aliases and Flags
122 122 #-----------------------------------------------------------------------------
123 123 flags = dict(base_flags)
124 124 flags.update(shell_flags)
125 125 frontend_flags = {}
126 126 addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
127 127 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
128 128 'Turn on auto editing of files with syntax errors.',
129 129 'Turn off auto editing of files with syntax errors.'
130 130 )
131 131 addflag('banner', 'TerminalIPythonApp.display_banner',
132 132 "Display a banner upon starting IPython.",
133 133 "Don't display a banner upon starting IPython."
134 134 )
135 135 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
136 136 """Set to confirm when you try to exit IPython with an EOF (Control-D
137 137 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
138 138 you can force a direct exit without any confirmation.""",
139 139 "Don't prompt the user when exiting."
140 140 )
141 141 addflag('term-title', 'TerminalInteractiveShell.term_title',
142 142 "Enable auto setting the terminal title.",
143 143 "Disable auto setting the terminal title."
144 144 )
145 145 classic_config = Config()
146 146 classic_config.InteractiveShell.cache_size = 0
147 147 classic_config.PlainTextFormatter.pprint = False
148 148 classic_config.PromptManager.in_template = '>>> '
149 149 classic_config.PromptManager.in2_template = '... '
150 150 classic_config.PromptManager.out_template = ''
151 151 classic_config.InteractiveShell.separate_in = ''
152 152 classic_config.InteractiveShell.separate_out = ''
153 153 classic_config.InteractiveShell.separate_out2 = ''
154 154 classic_config.InteractiveShell.colors = 'NoColor'
155 155 classic_config.InteractiveShell.xmode = 'Plain'
156 156
157 157 frontend_flags['classic']=(
158 158 classic_config,
159 159 "Gives IPython a similar feel to the classic Python prompt."
160 160 )
161 161 # # log doesn't make so much sense this way anymore
162 162 # paa('--log','-l',
163 163 # action='store_true', dest='InteractiveShell.logstart',
164 164 # help="Start logging to the default log file (./ipython_log.py).")
165 165 #
166 166 # # quick is harder to implement
167 167 frontend_flags['quick']=(
168 168 {'TerminalIPythonApp' : {'quick' : True}},
169 169 "Enable quick startup with no config files."
170 170 )
171 171
172 172 frontend_flags['i'] = (
173 173 {'TerminalIPythonApp' : {'force_interact' : True}},
174 174 """If running code from the command line, become interactive afterwards.
175 175 Note: can also be given simply as '-i.'"""
176 176 )
177 177 flags.update(frontend_flags)
178 178
179 179 aliases = dict(base_aliases)
180 180 aliases.update(shell_aliases)
181 181
182 182 #-----------------------------------------------------------------------------
183 183 # Main classes and functions
184 184 #-----------------------------------------------------------------------------
185 185
186 186
187 187 class LocateIPythonApp(BaseIPythonApplication):
188 188 description = """print the path to the IPython dir"""
189 189 subcommands = Dict(dict(
190 190 profile=('IPython.core.profileapp.ProfileLocate',
191 191 "print the path to an IPython profile directory",
192 192 ),
193 193 ))
194 194 def start(self):
195 195 if self.subapp is not None:
196 196 return self.subapp.start()
197 197 else:
198 198 print self.ipython_dir
199 199
200 200
201 201 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
202 202 name = u'ipython'
203 203 description = usage.cl_usage
204 204 default_config_file_name = default_config_file_name
205 205 crash_handler_class = IPAppCrashHandler
206 206 examples = _examples
207 207
208 208 flags = Dict(flags)
209 209 aliases = Dict(aliases)
210 210 classes = List()
211 211 def _classes_default(self):
212 212 """This has to be in a method, for TerminalIPythonApp to be available."""
213 213 return [
214 214 InteractiveShellApp, # ShellApp comes before TerminalApp, because
215 215 self.__class__, # it will also affect subclasses (e.g. QtConsole)
216 216 TerminalInteractiveShell,
217 217 PromptManager,
218 218 HistoryManager,
219 219 ProfileDir,
220 220 PlainTextFormatter,
221 221 IPCompleter,
222 222 ScriptMagics,
223 223 ]
224 224
225 225 subcommands = Dict(dict(
226 226 qtconsole=('IPython.qt.console.qtconsoleapp.IPythonQtConsoleApp',
227 227 """Launch the IPython Qt Console."""
228 228 ),
229 notebook=('IPython.html.notebook.notebookapp.NotebookApp',
229 notebook=('IPython.html.notebookapp.NotebookApp',
230 230 """Launch the IPython HTML Notebook Server."""
231 231 ),
232 232 profile = ("IPython.core.profileapp.ProfileApp",
233 233 "Create and manage IPython profiles."
234 234 ),
235 235 kernel = ("IPython.kernel.zmq.kernelapp.IPKernelApp",
236 236 "Start a kernel without an attached frontend."
237 237 ),
238 238 console=('IPython.terminal.console.app.ZMQTerminalIPythonApp',
239 239 """Launch the IPython terminal-based Console."""
240 240 ),
241 241 locate=('IPython.terminal.ipapp.LocateIPythonApp',
242 242 LocateIPythonApp.description
243 243 ),
244 244 history=('IPython.core.historyapp.HistoryApp',
245 245 "Manage the IPython history database."
246 246 ),
247 247 ))
248 248
249 249 # *do* autocreate requested profile, but don't create the config file.
250 250 auto_create=Bool(True)
251 251 # configurables
252 252 ignore_old_config=Bool(False, config=True,
253 253 help="Suppress warning messages about legacy config files"
254 254 )
255 255 quick = Bool(False, config=True,
256 256 help="""Start IPython quickly by skipping the loading of config files."""
257 257 )
258 258 def _quick_changed(self, name, old, new):
259 259 if new:
260 260 self.load_config_file = lambda *a, **kw: None
261 261 self.ignore_old_config=True
262 262
263 263 display_banner = Bool(True, config=True,
264 264 help="Whether to display a banner upon starting IPython."
265 265 )
266 266
267 267 # if there is code of files to run from the cmd line, don't interact
268 268 # unless the --i flag (App.force_interact) is true.
269 269 force_interact = Bool(False, config=True,
270 270 help="""If a command or file is given via the command-line,
271 271 e.g. 'ipython foo.py"""
272 272 )
273 273 def _force_interact_changed(self, name, old, new):
274 274 if new:
275 275 self.interact = True
276 276
277 277 def _file_to_run_changed(self, name, old, new):
278 278 if new:
279 279 self.something_to_run = True
280 280 if new and not self.force_interact:
281 281 self.interact = False
282 282 _code_to_run_changed = _file_to_run_changed
283 283 _module_to_run_changed = _file_to_run_changed
284 284
285 285 # internal, not-configurable
286 286 interact=Bool(True)
287 287 something_to_run=Bool(False)
288 288
289 289 def parse_command_line(self, argv=None):
290 290 """override to allow old '-pylab' flag with deprecation warning"""
291 291
292 292 argv = sys.argv[1:] if argv is None else argv
293 293
294 294 if '-pylab' in argv:
295 295 # deprecated `-pylab` given,
296 296 # warn and transform into current syntax
297 297 argv = argv[:] # copy, don't clobber
298 298 idx = argv.index('-pylab')
299 299 warn.warn("`-pylab` flag has been deprecated.\n"
300 300 " Use `--pylab` instead, or `--pylab=foo` to specify a backend.")
301 301 sub = '--pylab'
302 302 if len(argv) > idx+1:
303 303 # check for gui arg, as in '-pylab qt'
304 304 gui = argv[idx+1]
305 305 if gui in ('wx', 'qt', 'qt4', 'gtk', 'auto'):
306 306 sub = '--pylab='+gui
307 307 argv.pop(idx+1)
308 308 argv[idx] = sub
309 309
310 310 return super(TerminalIPythonApp, self).parse_command_line(argv)
311 311
312 312 @catch_config_error
313 313 def initialize(self, argv=None):
314 314 """Do actions after construct, but before starting the app."""
315 315 super(TerminalIPythonApp, self).initialize(argv)
316 316 if self.subapp is not None:
317 317 # don't bother initializing further, starting subapp
318 318 return
319 319 if not self.ignore_old_config:
320 320 check_for_old_config(self.ipython_dir)
321 321 # print self.extra_args
322 322 if self.extra_args and not self.something_to_run:
323 323 self.file_to_run = self.extra_args[0]
324 324 self.init_path()
325 325 # create the shell
326 326 self.init_shell()
327 327 # and draw the banner
328 328 self.init_banner()
329 329 # Now a variety of things that happen after the banner is printed.
330 330 self.init_gui_pylab()
331 331 self.init_extensions()
332 332 self.init_code()
333 333
334 334 def init_shell(self):
335 335 """initialize the InteractiveShell instance"""
336 336 # Create an InteractiveShell instance.
337 337 # shell.display_banner should always be False for the terminal
338 338 # based app, because we call shell.show_banner() by hand below
339 339 # so the banner shows *before* all extension loading stuff.
340 340 self.shell = TerminalInteractiveShell.instance(config=self.config,
341 341 display_banner=False, profile_dir=self.profile_dir,
342 342 ipython_dir=self.ipython_dir)
343 343 self.shell.configurables.append(self)
344 344
345 345 def init_banner(self):
346 346 """optionally display the banner"""
347 347 if self.display_banner and self.interact:
348 348 self.shell.show_banner()
349 349 # Make sure there is a space below the banner.
350 350 if self.log_level <= logging.INFO: print
351 351
352 352 def _pylab_changed(self, name, old, new):
353 353 """Replace --pylab='inline' with --pylab='auto'"""
354 354 if new == 'inline':
355 355 warn.warn("'inline' not available as pylab backend, "
356 356 "using 'auto' instead.")
357 357 self.pylab = 'auto'
358 358
359 359 def start(self):
360 360 if self.subapp is not None:
361 361 return self.subapp.start()
362 362 # perform any prexec steps:
363 363 if self.interact:
364 364 self.log.debug("Starting IPython's mainloop...")
365 365 self.shell.mainloop()
366 366 else:
367 367 self.log.debug("IPython not interactive...")
368 368
369 369
370 370 def load_default_config(ipython_dir=None):
371 371 """Load the default config file from the default ipython_dir.
372 372
373 373 This is useful for embedded shells.
374 374 """
375 375 if ipython_dir is None:
376 376 ipython_dir = get_ipython_dir()
377 377 profile_dir = os.path.join(ipython_dir, 'profile_default')
378 378 cl = PyFileConfigLoader(default_config_file_name, profile_dir)
379 379 try:
380 380 config = cl.load_config()
381 381 except ConfigFileNotFound:
382 382 # no config found
383 383 config = Config()
384 384 return config
385 385
386 386
387 387 def launch_new_instance():
388 388 """Create and run a full blown IPython instance"""
389 389 app = TerminalIPythonApp.instance()
390 390 app.initialize()
391 391 app.start()
392 392
393 393
394 394 if __name__ == '__main__':
395 395 launch_new_instance()
@@ -1,610 +1,610 b''
1 1 # -*- coding: utf-8 -*-
2 2 """IPython Test Suite Runner.
3 3
4 4 This module provides a main entry point to a user script to test IPython
5 5 itself from the command line. There are two ways of running this script:
6 6
7 7 1. With the syntax `iptest all`. This runs our entire test suite by
8 8 calling this script (with different arguments) recursively. This
9 9 causes modules and package to be tested in different processes, using nose
10 10 or trial where appropriate.
11 11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
12 12 the script simply calls nose, but with special command line flags and
13 13 plugins loaded.
14 14
15 15 """
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Copyright (C) 2009-2011 The IPython Development Team
19 19 #
20 20 # Distributed under the terms of the BSD License. The full license is in
21 21 # the file COPYING, distributed as part of this software.
22 22 #-----------------------------------------------------------------------------
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Imports
26 26 #-----------------------------------------------------------------------------
27 27 from __future__ import print_function
28 28
29 29 # Stdlib
30 30 import glob
31 31 import os
32 32 import os.path as path
33 33 import signal
34 34 import sys
35 35 import subprocess
36 36 import tempfile
37 37 import time
38 38 import warnings
39 39
40 40 # Note: monkeypatch!
41 41 # We need to monkeypatch a small problem in nose itself first, before importing
42 42 # it for actual use. This should get into nose upstream, but its release cycle
43 43 # is slow and we need it for our parametric tests to work correctly.
44 44 from IPython.testing import nosepatch
45 45
46 46 # Monkeypatch extra assert methods into nose.tools if they're not already there.
47 47 # This can be dropped once we no longer test on Python 2.6
48 48 from IPython.testing import nose_assert_methods
49 49
50 50 # Now, proceed to import nose itself
51 51 import nose.plugins.builtin
52 52 from nose.plugins.xunit import Xunit
53 53 from nose import SkipTest
54 54 from nose.core import TestProgram
55 55
56 56 # Our own imports
57 57 from IPython.utils import py3compat
58 58 from IPython.utils.importstring import import_item
59 59 from IPython.utils.path import get_ipython_module_path, get_ipython_package_dir
60 60 from IPython.utils.process import find_cmd, pycmd2argv
61 61 from IPython.utils.sysinfo import sys_info
62 62 from IPython.utils.tempdir import TemporaryDirectory
63 63 from IPython.utils.warn import warn
64 64
65 65 from IPython.testing import globalipapp
66 66 from IPython.testing.plugin.ipdoctest import IPythonDoctest
67 67 from IPython.external.decorators import KnownFailure, knownfailureif
68 68
69 69 pjoin = path.join
70 70
71 71
72 72 #-----------------------------------------------------------------------------
73 73 # Globals
74 74 #-----------------------------------------------------------------------------
75 75
76 76
77 77 #-----------------------------------------------------------------------------
78 78 # Warnings control
79 79 #-----------------------------------------------------------------------------
80 80
81 81 # Twisted generates annoying warnings with Python 2.6, as will do other code
82 82 # that imports 'sets' as of today
83 83 warnings.filterwarnings('ignore', 'the sets module is deprecated',
84 84 DeprecationWarning )
85 85
86 86 # This one also comes from Twisted
87 87 warnings.filterwarnings('ignore', 'the sha module is deprecated',
88 88 DeprecationWarning)
89 89
90 90 # Wx on Fedora11 spits these out
91 91 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
92 92 UserWarning)
93 93
94 94 # ------------------------------------------------------------------------------
95 95 # Monkeypatch Xunit to count known failures as skipped.
96 96 # ------------------------------------------------------------------------------
97 97 def monkeypatch_xunit():
98 98 try:
99 99 knownfailureif(True)(lambda: None)()
100 100 except Exception as e:
101 101 KnownFailureTest = type(e)
102 102
103 103 def addError(self, test, err, capt=None):
104 104 if issubclass(err[0], KnownFailureTest):
105 105 err = (SkipTest,) + err[1:]
106 106 return self.orig_addError(test, err, capt)
107 107
108 108 Xunit.orig_addError = Xunit.addError
109 109 Xunit.addError = addError
110 110
111 111 #-----------------------------------------------------------------------------
112 112 # Logic for skipping doctests
113 113 #-----------------------------------------------------------------------------
114 114 def extract_version(mod):
115 115 return mod.__version__
116 116
117 117 def test_for(item, min_version=None, callback=extract_version):
118 118 """Test to see if item is importable, and optionally check against a minimum
119 119 version.
120 120
121 121 If min_version is given, the default behavior is to check against the
122 122 `__version__` attribute of the item, but specifying `callback` allows you to
123 123 extract the value you are interested in. e.g::
124 124
125 125 In [1]: import sys
126 126
127 127 In [2]: from IPython.testing.iptest import test_for
128 128
129 129 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
130 130 Out[3]: True
131 131
132 132 """
133 133 try:
134 134 check = import_item(item)
135 135 except (ImportError, RuntimeError):
136 136 # GTK reports Runtime error if it can't be initialized even if it's
137 137 # importable.
138 138 return False
139 139 else:
140 140 if min_version:
141 141 if callback:
142 142 # extra processing step to get version to compare
143 143 check = callback(check)
144 144
145 145 return check >= min_version
146 146 else:
147 147 return True
148 148
149 149 # Global dict where we can store information on what we have and what we don't
150 150 # have available at test run time
151 151 have = {}
152 152
153 153 have['curses'] = test_for('_curses')
154 154 have['matplotlib'] = test_for('matplotlib')
155 155 have['numpy'] = test_for('numpy')
156 156 have['pexpect'] = test_for('IPython.external.pexpect')
157 157 have['pymongo'] = test_for('pymongo')
158 158 have['pygments'] = test_for('pygments')
159 159 have['qt'] = test_for('IPython.external.qt')
160 160 have['rpy2'] = test_for('rpy2')
161 161 have['sqlite3'] = test_for('sqlite3')
162 162 have['cython'] = test_for('Cython')
163 163 have['oct2py'] = test_for('oct2py')
164 164 have['tornado'] = test_for('tornado.version_info', (2,1,0), callback=None)
165 165 have['jinja2'] = test_for('jinja2')
166 166 have['wx'] = test_for('wx')
167 167 have['wx.aui'] = test_for('wx.aui')
168 168 have['azure'] = test_for('azure')
169 169
170 170 min_zmq = (2,1,11)
171 171
172 172 have['zmq'] = test_for('zmq.pyzmq_version_info', min_zmq, callback=lambda x: x())
173 173
174 174 #-----------------------------------------------------------------------------
175 175 # Functions and classes
176 176 #-----------------------------------------------------------------------------
177 177
178 178 def report():
179 179 """Return a string with a summary report of test-related variables."""
180 180
181 181 out = [ sys_info(), '\n']
182 182
183 183 avail = []
184 184 not_avail = []
185 185
186 186 for k, is_avail in have.items():
187 187 if is_avail:
188 188 avail.append(k)
189 189 else:
190 190 not_avail.append(k)
191 191
192 192 if avail:
193 193 out.append('\nTools and libraries available at test time:\n')
194 194 avail.sort()
195 195 out.append(' ' + ' '.join(avail)+'\n')
196 196
197 197 if not_avail:
198 198 out.append('\nTools and libraries NOT available at test time:\n')
199 199 not_avail.sort()
200 200 out.append(' ' + ' '.join(not_avail)+'\n')
201 201
202 202 return ''.join(out)
203 203
204 204
205 205 def make_exclude():
206 206 """Make patterns of modules and packages to exclude from testing.
207 207
208 208 For the IPythonDoctest plugin, we need to exclude certain patterns that
209 209 cause testing problems. We should strive to minimize the number of
210 210 skipped modules, since this means untested code.
211 211
212 212 These modules and packages will NOT get scanned by nose at all for tests.
213 213 """
214 214 # Simple utility to make IPython paths more readably, we need a lot of
215 215 # these below
216 216 ipjoin = lambda *paths: pjoin('IPython', *paths)
217 217
218 218 exclusions = [ipjoin('external'),
219 219 ipjoin('quarantine'),
220 220 ipjoin('deathrow'),
221 221 # This guy is probably attic material
222 222 ipjoin('testing', 'mkdoctests'),
223 223 # Testing inputhook will need a lot of thought, to figure out
224 224 # how to have tests that don't lock up with the gui event
225 225 # loops in the picture
226 226 ipjoin('lib', 'inputhook'),
227 227 # Config files aren't really importable stand-alone
228 228 ipjoin('config', 'profile'),
229 229 # The notebook 'static' directory contains JS, css and other
230 230 # files for web serving. Occasionally projects may put a .py
231 231 # file in there (MathJax ships a conf.py), so we might as
232 232 # well play it safe and skip the whole thing.
233 ipjoin('html', 'notebook', 'static'),
234 ipjoin('html', 'notebook', 'fabfile'),
233 ipjoin('html', 'static'),
234 ipjoin('html', 'fabfile'),
235 235 ]
236 236 if not have['sqlite3']:
237 237 exclusions.append(ipjoin('core', 'tests', 'test_history'))
238 238 exclusions.append(ipjoin('core', 'history'))
239 239 if not have['wx']:
240 240 exclusions.append(ipjoin('lib', 'inputhookwx'))
241 241
242 242 if 'IPython.kernel.inprocess' not in sys.argv:
243 243 exclusions.append(ipjoin('kernel', 'inprocess'))
244 244
245 245 # FIXME: temporarily disable autoreload tests, as they can produce
246 246 # spurious failures in subsequent tests (cythonmagic).
247 247 exclusions.append(ipjoin('extensions', 'autoreload'))
248 248 exclusions.append(ipjoin('extensions', 'tests', 'test_autoreload'))
249 249
250 250 # We do this unconditionally, so that the test suite doesn't import
251 251 # gtk, changing the default encoding and masking some unicode bugs.
252 252 exclusions.append(ipjoin('lib', 'inputhookgtk'))
253 253 exclusions.append(ipjoin('kernel', 'zmq', 'gui', 'gtkembed'))
254 254
255 255 # These have to be skipped on win32 because the use echo, rm, cd, etc.
256 256 # See ticket https://github.com/ipython/ipython/issues/87
257 257 if sys.platform == 'win32':
258 258 exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip'))
259 259 exclusions.append(ipjoin('testing', 'plugin', 'dtexample'))
260 260
261 261 if not have['pexpect']:
262 262 exclusions.extend([ipjoin('lib', 'irunner'),
263 263 ipjoin('lib', 'tests', 'test_irunner'),
264 264 ipjoin('terminal', 'console'),
265 265 ])
266 266
267 267 if not have['zmq']:
268 268 exclusions.append(ipjoin('kernel'))
269 269 exclusions.append(ipjoin('qt'))
270 270 exclusions.append(ipjoin('html'))
271 271 exclusions.append(ipjoin('consoleapp.py'))
272 272 exclusions.append(ipjoin('terminal', 'console'))
273 273 exclusions.append(ipjoin('parallel'))
274 274 elif not have['qt'] or not have['pygments']:
275 275 exclusions.append(ipjoin('qt'))
276 276
277 277 if not have['pymongo']:
278 278 exclusions.append(ipjoin('parallel', 'controller', 'mongodb'))
279 279 exclusions.append(ipjoin('parallel', 'tests', 'test_mongodb'))
280 280
281 281 if not have['matplotlib']:
282 282 exclusions.extend([ipjoin('core', 'pylabtools'),
283 283 ipjoin('core', 'tests', 'test_pylabtools'),
284 284 ipjoin('kernel', 'zmq', 'pylab'),
285 285 ])
286 286
287 287 if not have['cython']:
288 288 exclusions.extend([ipjoin('extensions', 'cythonmagic')])
289 289 exclusions.extend([ipjoin('extensions', 'tests', 'test_cythonmagic')])
290 290
291 291 if not have['oct2py']:
292 292 exclusions.extend([ipjoin('extensions', 'octavemagic')])
293 293 exclusions.extend([ipjoin('extensions', 'tests', 'test_octavemagic')])
294 294
295 295 if not have['tornado']:
296 296 exclusions.append(ipjoin('html'))
297 297
298 298 if not have['jinja2']:
299 exclusions.append(ipjoin('html', 'notebook', 'notebookapp'))
299 exclusions.append(ipjoin('html', 'notebookapp'))
300 300
301 301 if not have['rpy2'] or not have['numpy']:
302 302 exclusions.append(ipjoin('extensions', 'rmagic'))
303 303 exclusions.append(ipjoin('extensions', 'tests', 'test_rmagic'))
304 304
305 305 if not have['azure']:
306 exclusions.append(ipjoin('html', 'notebook', 'services', 'notebooks', 'azurenbmanager'))
306 exclusions.append(ipjoin('html', 'services', 'notebooks', 'azurenbmanager'))
307 307
308 308 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
309 309 if sys.platform == 'win32':
310 310 exclusions = [s.replace('\\','\\\\') for s in exclusions]
311 311
312 312 # check for any exclusions that don't seem to exist:
313 313 parent, _ = os.path.split(get_ipython_package_dir())
314 314 for exclusion in exclusions:
315 315 if exclusion.endswith(('deathrow', 'quarantine')):
316 316 # ignore deathrow/quarantine, which exist in dev, but not install
317 317 continue
318 318 fullpath = pjoin(parent, exclusion)
319 319 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
320 320 warn("Excluding nonexistent file: %r" % exclusion)
321 321
322 322 return exclusions
323 323
324 324
325 325 class IPTester(object):
326 326 """Call that calls iptest or trial in a subprocess.
327 327 """
328 328 #: string, name of test runner that will be called
329 329 runner = None
330 330 #: list, parameters for test runner
331 331 params = None
332 332 #: list, arguments of system call to be made to call test runner
333 333 call_args = None
334 334 #: list, subprocesses we start (for cleanup)
335 335 processes = None
336 336 #: str, coverage xml output file
337 337 coverage_xml = None
338 338
339 339 def __init__(self, runner='iptest', params=None):
340 340 """Create new test runner."""
341 341 p = os.path
342 342 if runner == 'iptest':
343 343 iptest_app = get_ipython_module_path('IPython.testing.iptest')
344 344 self.runner = pycmd2argv(iptest_app) + sys.argv[1:]
345 345 else:
346 346 raise Exception('Not a valid test runner: %s' % repr(runner))
347 347 if params is None:
348 348 params = []
349 349 if isinstance(params, str):
350 350 params = [params]
351 351 self.params = params
352 352
353 353 # Assemble call
354 354 self.call_args = self.runner+self.params
355 355
356 356 # Find the section we're testing (IPython.foo)
357 357 for sect in self.params:
358 358 if sect.startswith('IPython'): break
359 359 else:
360 360 raise ValueError("Section not found", self.params)
361 361
362 362 if '--with-xunit' in self.call_args:
363 363
364 364 self.call_args.append('--xunit-file')
365 365 # FIXME: when Windows uses subprocess.call, these extra quotes are unnecessary:
366 366 xunit_file = path.abspath(sect+'.xunit.xml')
367 367 if sys.platform == 'win32':
368 368 xunit_file = '"%s"' % xunit_file
369 369 self.call_args.append(xunit_file)
370 370
371 371 if '--with-xml-coverage' in self.call_args:
372 372 self.coverage_xml = path.abspath(sect+".coverage.xml")
373 373 self.call_args.remove('--with-xml-coverage')
374 374 self.call_args = ["coverage", "run", "--source="+sect] + self.call_args[1:]
375 375
376 376 # Store anything we start to clean up on deletion
377 377 self.processes = []
378 378
379 379 def _run_cmd(self):
380 380 with TemporaryDirectory() as IPYTHONDIR:
381 381 env = os.environ.copy()
382 382 env['IPYTHONDIR'] = IPYTHONDIR
383 383 # print >> sys.stderr, '*** CMD:', ' '.join(self.call_args) # dbg
384 384 subp = subprocess.Popen(self.call_args, env=env)
385 385 self.processes.append(subp)
386 386 # If this fails, the process will be left in self.processes and
387 387 # cleaned up later, but if the wait call succeeds, then we can
388 388 # clear the stored process.
389 389 retcode = subp.wait()
390 390 self.processes.pop()
391 391 return retcode
392 392
393 393 def run(self):
394 394 """Run the stored commands"""
395 395 try:
396 396 retcode = self._run_cmd()
397 397 except KeyboardInterrupt:
398 398 return -signal.SIGINT
399 399 except:
400 400 import traceback
401 401 traceback.print_exc()
402 402 return 1 # signal failure
403 403
404 404 if self.coverage_xml:
405 405 subprocess.call(["coverage", "xml", "-o", self.coverage_xml])
406 406 return retcode
407 407
408 408 def __del__(self):
409 409 """Cleanup on exit by killing any leftover processes."""
410 410 for subp in self.processes:
411 411 if subp.poll() is not None:
412 412 continue # process is already dead
413 413
414 414 try:
415 415 print('Cleaning up stale PID: %d' % subp.pid)
416 416 subp.kill()
417 417 except: # (OSError, WindowsError) ?
418 418 # This is just a best effort, if we fail or the process was
419 419 # really gone, ignore it.
420 420 pass
421 421 else:
422 422 for i in range(10):
423 423 if subp.poll() is None:
424 424 time.sleep(0.1)
425 425 else:
426 426 break
427 427
428 428 if subp.poll() is None:
429 429 # The process did not die...
430 430 print('... failed. Manual cleanup may be required.')
431 431
432 432
433 433 def make_runners(inc_slow=False):
434 434 """Define the top-level packages that need to be tested.
435 435 """
436 436
437 437 # Packages to be tested via nose, that only depend on the stdlib
438 438 nose_pkg_names = ['config', 'core', 'extensions', 'lib', 'terminal',
439 439 'testing', 'utils', 'nbformat' ]
440 440
441 441 if have['qt']:
442 442 nose_pkg_names.append('qt')
443 443
444 444 if have['tornado']:
445 445 nose_pkg_names.append('html')
446 446
447 447 if have['zmq']:
448 448 nose_pkg_names.append('kernel')
449 449 nose_pkg_names.append('kernel.inprocess')
450 450 if inc_slow:
451 451 nose_pkg_names.append('parallel')
452 452
453 453 # For debugging this code, only load quick stuff
454 454 #nose_pkg_names = ['core', 'extensions'] # dbg
455 455
456 456 # Make fully qualified package names prepending 'IPython.' to our name lists
457 457 nose_packages = ['IPython.%s' % m for m in nose_pkg_names ]
458 458
459 459 # Make runners
460 460 runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ]
461 461
462 462 return runners
463 463
464 464
465 465 def run_iptest():
466 466 """Run the IPython test suite using nose.
467 467
468 468 This function is called when this script is **not** called with the form
469 469 `iptest all`. It simply calls nose with appropriate command line flags
470 470 and accepts all of the standard nose arguments.
471 471 """
472 472 # Apply our monkeypatch to Xunit
473 473 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
474 474 monkeypatch_xunit()
475 475
476 476 warnings.filterwarnings('ignore',
477 477 'This will be removed soon. Use IPython.testing.util instead')
478 478
479 479 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
480 480
481 481 '--with-ipdoctest',
482 482 '--ipdoctest-tests','--ipdoctest-extension=txt',
483 483
484 484 # We add --exe because of setuptools' imbecility (it
485 485 # blindly does chmod +x on ALL files). Nose does the
486 486 # right thing and it tries to avoid executables,
487 487 # setuptools unfortunately forces our hand here. This
488 488 # has been discussed on the distutils list and the
489 489 # setuptools devs refuse to fix this problem!
490 490 '--exe',
491 491 ]
492 492 if '-a' not in argv and '-A' not in argv:
493 493 argv = argv + ['-a', '!crash']
494 494
495 495 if nose.__version__ >= '0.11':
496 496 # I don't fully understand why we need this one, but depending on what
497 497 # directory the test suite is run from, if we don't give it, 0 tests
498 498 # get run. Specifically, if the test suite is run from the source dir
499 499 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
500 500 # even if the same call done in this directory works fine). It appears
501 501 # that if the requested package is in the current dir, nose bails early
502 502 # by default. Since it's otherwise harmless, leave it in by default
503 503 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
504 504 argv.append('--traverse-namespace')
505 505
506 506 # use our plugin for doctesting. It will remove the standard doctest plugin
507 507 # if it finds it enabled
508 508 plugins = [IPythonDoctest(make_exclude()), KnownFailure()]
509 509
510 510 # We need a global ipython running in this process, but the special
511 511 # in-process group spawns its own IPython kernels, so for *that* group we
512 512 # must avoid also opening the global one (otherwise there's a conflict of
513 513 # singletons). Ultimately the solution to this problem is to refactor our
514 514 # assumptions about what needs to be a singleton and what doesn't (app
515 515 # objects should, individual shells shouldn't). But for now, this
516 516 # workaround allows the test suite for the inprocess module to complete.
517 517 if not 'IPython.kernel.inprocess' in sys.argv:
518 518 globalipapp.start_ipython()
519 519
520 520 # Now nose can run
521 521 TestProgram(argv=argv, addplugins=plugins)
522 522
523 523
524 524 def run_iptestall(inc_slow=False):
525 525 """Run the entire IPython test suite by calling nose and trial.
526 526
527 527 This function constructs :class:`IPTester` instances for all IPython
528 528 modules and package and then runs each of them. This causes the modules
529 529 and packages of IPython to be tested each in their own subprocess using
530 530 nose.
531 531
532 532 Parameters
533 533 ----------
534 534
535 535 inc_slow : bool, optional
536 536 Include slow tests, like IPython.parallel. By default, these tests aren't
537 537 run.
538 538 """
539 539
540 540 runners = make_runners(inc_slow=inc_slow)
541 541
542 542 # Run the test runners in a temporary dir so we can nuke it when finished
543 543 # to clean up any junk files left over by accident. This also makes it
544 544 # robust against being run in non-writeable directories by mistake, as the
545 545 # temp dir will always be user-writeable.
546 546 curdir = os.getcwdu()
547 547 testdir = tempfile.gettempdir()
548 548 os.chdir(testdir)
549 549
550 550 # Run all test runners, tracking execution time
551 551 failed = []
552 552 t_start = time.time()
553 553 try:
554 554 for (name, runner) in runners:
555 555 print('*'*70)
556 556 print('IPython test group:',name)
557 557 res = runner.run()
558 558 if res:
559 559 failed.append( (name, runner) )
560 560 if res == -signal.SIGINT:
561 561 print("Interrupted")
562 562 break
563 563 finally:
564 564 os.chdir(curdir)
565 565 t_end = time.time()
566 566 t_tests = t_end - t_start
567 567 nrunners = len(runners)
568 568 nfail = len(failed)
569 569 # summarize results
570 570 print()
571 571 print('*'*70)
572 572 print('Test suite completed for system with the following information:')
573 573 print(report())
574 574 print('Ran %s test groups in %.3fs' % (nrunners, t_tests))
575 575 print()
576 576 print('Status:')
577 577 if not failed:
578 578 print('OK')
579 579 else:
580 580 # If anything went wrong, point out what command to rerun manually to
581 581 # see the actual errors and individual summary
582 582 print('ERROR - %s out of %s test groups failed.' % (nfail, nrunners))
583 583 for name, failed_runner in failed:
584 584 print('-'*40)
585 585 print('Runner failed:',name)
586 586 print('You may wish to rerun this one individually, with:')
587 587 failed_call_args = [py3compat.cast_unicode(x) for x in failed_runner.call_args]
588 588 print(u' '.join(failed_call_args))
589 589 print()
590 590 # Ensure that our exit code indicates failure
591 591 sys.exit(1)
592 592
593 593
594 594 def main():
595 595 for arg in sys.argv[1:]:
596 596 if arg.startswith('IPython'):
597 597 # This is in-process
598 598 run_iptest()
599 599 else:
600 600 if "--all" in sys.argv:
601 601 sys.argv.remove("--all")
602 602 inc_slow = True
603 603 else:
604 604 inc_slow = False
605 605 # This starts subprocesses
606 606 run_iptestall(inc_slow=inc_slow)
607 607
608 608
609 609 if __name__ == '__main__':
610 610 main()
@@ -1,92 +1,92 b''
1 1 """utilities for checking submodule status"""
2 2
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (C) 2013 The IPython Development Team
5 5 #
6 6 # Distributed under the terms of the BSD License. The full license is in
7 7 # the file COPYING, distributed as part of this software.
8 8 #-----------------------------------------------------------------------------
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Imports
12 12 #-----------------------------------------------------------------------------
13 13
14 14 import os
15 15 import subprocess
16 16 import sys
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Globals
20 20 #-----------------------------------------------------------------------------
21 21
22 22 pjoin = os.path.join
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Code
26 26 #-----------------------------------------------------------------------------
27 27
28 28 def ipython_parent():
29 29 """return IPython's parent (i.e. root if run from git)"""
30 30 from IPython.utils.path import get_ipython_package_dir
31 31 return os.path.abspath(os.path.dirname(get_ipython_package_dir()))
32 32
33 33 def ipython_submodules(root):
34 34 """return IPython submodules relative to root"""
35 35 return [
36 pjoin(root, 'IPython', 'html', 'notebook', 'static', 'components'),
36 pjoin(root, 'IPython', 'html', 'static', 'components'),
37 37 ]
38 38
39 39 def is_repo(d):
40 40 """is d a git repo?"""
41 41 return os.path.exists(pjoin(d, '.git'))
42 42
43 43 def check_submodule_status(root=None):
44 44 """check submodule status
45 45
46 46 Has three return values:
47 47
48 48 'missing' - submodules are absent
49 49 'unclean' - submodules have unstaged changes
50 50 'clean' - all submodules are up to date
51 51 """
52 52
53 53 if hasattr(sys, "frozen"):
54 54 # frozen via py2exe or similar, don't bother
55 55 return 'clean'
56 56
57 57 if not root:
58 58 root = ipython_parent()
59 59
60 60 if not is_repo(root):
61 61 # not in git, assume clean
62 62 return 'clean'
63 63
64 64 submodules = ipython_submodules(root)
65 65
66 66 for submodule in submodules:
67 67 if not os.path.exists(submodule):
68 68 return 'missing'
69 69
70 70 # check with git submodule status
71 71 proc = subprocess.Popen('git submodule status',
72 72 stdout=subprocess.PIPE,
73 73 stderr=subprocess.PIPE,
74 74 shell=True,
75 75 cwd=root,
76 76 )
77 77 status, _ = proc.communicate()
78 78 status = status.decode("ascii")
79 79
80 80 for line in status.splitlines():
81 81 if status.startswith('-'):
82 82 return 'missing'
83 83 elif status.startswith('+'):
84 84 return 'unclean'
85 85
86 86 return 'clean'
87 87
88 88 def update_submodules(repo_dir):
89 89 """update submodules in a repo"""
90 90 subprocess.check_call("git submodule init", cwd=repo_dir, shell=True)
91 91 subprocess.check_call("git submodule update --recursive", cwd=repo_dir, shell=True)
92 92
@@ -1,39 +1,39 b''
1 1 include README.rst
2 2 include COPYING.txt
3 3 include setupbase.py
4 4 include setupegg.py
5 5
6 6 graft setupext
7 7
8 8 graft scripts
9 9
10 10 # Load main dir but exclude things we don't want in the distro
11 11 graft IPython
12 12 prune IPython/deathrow
13 13 prune IPython/external/js
14 prune IPython/html/notebook/static/mathjax
14 prune IPython/html/static/mathjax
15 15
16 16 # Include some specific files and data resources we need
17 17 include IPython/.git_commit_info.ini
18 18 include IPython/qt/console/resources/icon/IPythonConsole.svg
19 19
20 20 # Documentation
21 21 graft docs
22 22 exclude docs/\#*
23 23 exclude docs/man/*.1.gz
24 24
25 25 # Examples
26 26 graft examples
27 27
28 28 # docs subdirs we want to skip
29 29 prune docs/attic
30 30 prune docs/build
31 31 prune docs/gh-pages
32 32 prune docs/dist
33 33
34 34 # Patterns to exclude from any directory
35 35 global-exclude *~
36 36 global-exclude *.flc
37 37 global-exclude *.pyc
38 38 global-exclude *.pyo
39 39 global-exclude .dircopy.log
@@ -1,482 +1,482 b''
1 1 .. _htmlnotebook:
2 2
3 3 =========================
4 4 An HTML Notebook IPython
5 5 =========================
6 6
7 7 .. seealso::
8 8
9 9 :ref:`Installation requirements <installnotebook>` for the Notebook.
10 10
11 11 The IPython Notebook consists of two related components:
12 12
13 13 * An JSON based Notebook document format for recording and distributing
14 14 Python code and rich text.
15 15 * A web-based user interface for authoring and running notebook documents.
16 16
17 17 The Notebook can be used by starting the Notebook server with the
18 18 command::
19 19
20 20 $ ipython notebook
21 21
22 22 Note that by default, the notebook doesn't load pylab, it's just a normal
23 23 IPython session like any other. If you want pylab support, you must use::
24 24
25 25 $ ipython notebook --pylab
26 26
27 27 which will behave similar to the terminal and Qt console versions, using your
28 28 default matplotlib backend and providing floating interactive plot windows. If
29 29 you want inline figures, you must manually select the ``inline`` backend::
30 30
31 31 $ ipython notebook --pylab inline
32 32
33 33 This server uses the same ZeroMQ-based two process kernel architecture as
34 34 the QT Console as well Tornado for serving HTTP/S requests. Some of the main
35 35 features of the Notebook include:
36 36
37 37 * Display rich data (png/html/latex/svg) in the browser as a result of
38 38 computations.
39 39 * Compose text cells using HTML and Markdown.
40 40 * Import and export notebook documents in range of formats (.ipynb, .py).
41 41 * In browser syntax highlighting, tab completion and autoindentation.
42 42 * Inline matplotlib plots that can be stored in Notebook documents and opened
43 43 later.
44 44
45 45 See :ref:`our installation documentation <install_index>` for directions on
46 46 how to install the notebook and its dependencies.
47 47
48 48 .. note::
49 49
50 50 You can start more than one notebook server at the same time, if you want to
51 51 work on notebooks in different directories. By default the first notebook
52 52 server starts in port 8888, later notebooks search for random ports near
53 53 that one. You can also manually specify the port with the ``--port``
54 54 option.
55 55
56 56
57 57 Basic Usage
58 58 ===========
59 59
60 60 The landing page of the notebook server application, which we call the IPython
61 61 Notebook *dashboard*, shows the notebooks currently available in the directory
62 62 in which the application was started, and allows you to create new notebooks.
63 63
64 64 A notebook is a combination of two things:
65 65
66 66 1. An interactive session connected to an IPython kernel, controlled by a web
67 67 application that can send input to the console and display many types of
68 68 output (text, graphics, mathematics and more). This is the same kernel used
69 69 by the :ref:`Qt console <qtconsole>`, but in this case the web console sends
70 70 input in persistent cells that you can edit in-place instead of the
71 71 vertically scrolling terminal style used by the Qt console.
72 72
73 73 2. A document that can save the inputs and outputs of the session as well as
74 74 additional text that accompanies the code but is not meant for execution.
75 75 In this way, notebook files serve as a complete computational record of a
76 76 session including explanatory text and mathematics, code and resulting
77 77 figures. These documents are internally JSON files and are saved with the
78 78 ``.ipynb`` extension.
79 79
80 80 If you have ever used the Mathematica or Sage notebooks (the latter is also
81 81 web-based__) you should feel right at home. If you have not, you should be
82 82 able to learn how to use it in just a few minutes.
83 83
84 84 .. __: http://sagenb.org
85 85
86 86
87 87 Creating and editing notebooks
88 88 ------------------------------
89 89
90 90 You can create new notebooks from the dashboard with the ``New Notebook``
91 91 button or open existing ones by clicking on their name. Once in a notebook,
92 92 your browser tab will reflect the name of that notebook (prefixed with "IPy:").
93 93 The URL for that notebook is not meant to be human-readable and is *not*
94 94 persistent across invocations of the notebook server.
95 95
96 96 You can also drag and drop into the area listing files any python file: it
97 97 will be imported into a notebook with the same name (but ``.ipynb`` extension)
98 98 located in the directory where the notebook server was started. This notebook
99 99 will consist of a single cell with all the code in the file, which you can
100 100 later manually partition into individual cells for gradual execution, add text
101 101 and graphics, etc.
102 102
103 103
104 104 Workflow and limitations
105 105 ------------------------
106 106
107 107 The normal workflow in a notebook is quite similar to a normal IPython session,
108 108 with the difference that you can edit a cell in-place multiple times until you
109 109 obtain the desired results rather than having to rerun separate scripts with
110 110 the ``%run`` magic (though magics also work in the notebook). Typically
111 111 you'll work on a problem in pieces, organizing related pieces into cells and
112 112 moving forward as previous parts work correctly. This is much more convenient
113 113 for interactive exploration than breaking up a computation into scripts that
114 114 must be executed together, especially if parts of them take a long time to run
115 115 (In the traditional terminal-based IPython, you can use tricks with namespaces
116 116 and ``%run -i`` to achieve this capability, but we think the notebook is a more
117 117 natural solution for that kind of problem).
118 118
119 119 The only significant limitation the notebook currently has, compared to the qt
120 120 console, is that it can not run any code that expects input from the kernel
121 121 (such as scripts that call :func:`raw_input`). Very importantly, this means
122 122 that the ``%debug`` magic does *not* work in the notebook! We intend to
123 123 correct this limitation, but in the meantime, there is a way to debug problems
124 124 in the notebook: you can attach a Qt console to your existing notebook kernel,
125 125 and run ``%debug`` from the Qt console. If your notebook is running on a local
126 126 computer (i.e. if you are accessing it via your localhost address at
127 127 127.0.0.1), you can just type ``%qtconsole`` in the notebook and a Qt console
128 128 will open up connected to that same kernel.
129 129
130 130 In general, the notebook server prints the full details of how to connect to
131 131 each kernel at the terminal, with lines like::
132 132
133 133 [IPKernelApp] To connect another client to this kernel, use:
134 134 [IPKernelApp] --existing kernel-3bb93edd-6b5a-455c-99c8-3b658f45dde5.json
135 135
136 136 This is the name of a JSON file that contains all the port and validation
137 137 information necessary to connect to the kernel. You can manually start a
138 138 qt console with::
139 139
140 140 ipython qtconsole --existing kernel-3bb93edd-6b5a-455c-99c8-3b658f45dde5.json
141 141
142 142 and if you only have a single kernel running, simply typing::
143 143
144 144 ipython qtconsole --existing
145 145
146 146 will automatically find it (it will always find the most recently started
147 147 kernel if there is more than one). You can also request this connection data
148 148 by typing ``%connect_info``; this will print the same file information as well
149 149 as the content of the JSON data structure it contains.
150 150
151 151
152 152 Text input
153 153 ----------
154 154
155 155 In addition to code cells and the output they produce (such as figures), you
156 156 can also type text not meant for execution. To type text, change the type of a
157 157 cell from ``Code`` to ``Markdown`` by using the button or the :kbd:`Ctrl-m m`
158 158 keybinding (see below). You can then type any text in Markdown_ syntax, as
159 159 well as mathematical expressions if you use ``$...$`` for inline math or
160 160 ``$$...$$`` for displayed math.
161 161
162 162
163 163 Exporting a notebook and importing existing scripts
164 164 ---------------------------------------------------
165 165
166 166 If you want to provide others with a static HTML or PDF view of your notebook,
167 167 use the ``Print`` button. This opens a static view of the document, which you
168 168 can print to PDF using your operating system's facilities, or save to a file
169 169 with your web browser's 'Save' option (note that typically, this will create
170 170 both an html file *and* a directory called `notebook_name_files` next to it
171 171 that contains all the necessary style information, so if you intend to share
172 172 this, you must send the directory along with the main html file).
173 173
174 174 The `Download` button lets you save a notebook file to the Download area
175 175 configured by your web browser (particularly useful if you are running the
176 176 notebook server on a remote host and need a file locally). The notebook is
177 177 saved by default with the ``.ipynb`` extension and the files contain JSON data
178 178 that is not meant for human editing or consumption. But you can always export
179 179 the input part of a notebook to a plain python script by choosing Python format
180 180 in the `Download` drop list. This removes all output and saves the text cells
181 181 in comment areas. See ref:`below <notebook_format>` for more details on the
182 182 notebook format.
183 183
184 184 The notebook can also *import* ``.py`` files as notebooks, by dragging and
185 185 dropping the file into the notebook dashboard file list area. By default, the
186 186 entire contents of the file will be loaded into a single code cell. But if
187 187 prior to import, you manually add the ``# <nbformat>2</nbformat>`` marker at
188 188 the start and then add separators for text/code cells, you can get a cleaner
189 189 import with the file broken into individual cells.
190 190
191 191 .. warning::
192 192
193 193 While in simple cases you can roundtrip a notebook to Python, edit the
194 194 python file and import it back without loss of main content, this is in
195 195 general *not guaranteed to work at all*. First, there is extra metadata
196 196 saved in the notebook that may not be saved to the ``.py`` format. And as
197 197 the notebook format evolves in complexity, there will be attributes of the
198 198 notebook that will not survive a roundtrip through the Python form. You
199 199 should think of the Python format as a way to output a script version of a
200 200 notebook and the import capabilities as a way to load existing code to get a
201 201 notebook started. But the Python version is *not* an alternate notebook
202 202 format.
203 203
204 204
205 205 Importing or executing a notebook as a normal Python file
206 206 ---------------------------------------------------------
207 207
208 208 The native format of the notebook, a file with a ``.ipynb`` extension, is a
209 209 JSON container of all the input and output of the notebook, and therefore not
210 210 valid Python by itself. This means that by default, you can not import a
211 211 notebook or execute it as a normal python script. But if you want use
212 212 notebooks as regular Python files, you can start the notebook server with::
213 213
214 214 ipython notebook --script
215 215
216 216 or you can set this option permanently in your configuration file with::
217 217
218 218 c.NotebookManager.save_script=True
219 219
220 220 This will instruct the notebook server to save the ``.py`` export of each
221 221 notebook adjacent to the ``.ipynb`` at every save. These files can be
222 222 ``%run``, imported from regular IPython sessions or other notebooks, or
223 223 executed at the command-line as normal Python files. Since we export the raw
224 224 code you have typed, for these files to be importable from other code you will
225 225 have to avoid using syntax such as ``%magics`` and other IPython-specific
226 226 extensions to the language.
227 227
228 228 In regular practice, the standard way to differentiate importable code from the
229 229 'executable' part of a script is to put at the bottom::
230 230
231 231 if __name__ == '__main__':
232 232 # rest of the code...
233 233
234 234 Since all cells in the notebook are run as top-level code, you'll need to
235 235 similarly protect *all* cells that you do not want executed when other scripts
236 236 try to import your notebook. A convenient shortand for this is to define early
237 237 on::
238 238
239 239 script = __name__ == '__main__'
240 240
241 241 and then on any cell that you need to protect, use::
242 242
243 243 if script:
244 244 # rest of the cell...
245 245
246 246 Configuration
247 247 -------------
248 248
249 249 The IPython notebook server can be run with a variety of command line arguments.
250 250 To see a list of available options enter:
251 251
252 252 $ ipython notebook --help
253 253
254 254 Defaults for these options can also be set by creating a file named
255 255 ipython_notebook_config.py in your IPython profile folder. The profile folder is
256 256 a subfolder of your IPython directory (`ipython locate` will show you where that
257 257 is). To create default configuration files (with lots of info on available
258 258 options) use:
259 259
260 260 $ ipython profile create
261 261
262 262 .. seealso:
263 263
264 264 :ref:`config_overview`, in particular :ref:`Profiles`.
265 265
266 266
267 267 Keyboard use
268 268 ------------
269 269
270 270 All actions in the notebook can be achieved with the mouse, but we have also
271 271 added keyboard shortcuts for the most common ones, so that productive use of
272 272 the notebook can be achieved with minimal mouse intervention. The main
273 273 key bindings you need to remember are:
274 274
275 275 * :kbd:`Shift-Enter`: execute the current cell (similar to the Qt console),
276 276 show output (if any) and jump to the next cell below. If :kbd:`Shift-Enter`
277 277 was invoked on the last input line, a new code cell will also be created. Note
278 278 that in the notebook, simply using :kbd:`Enter` *never* forces execution,
279 279 it simply inserts a new line in the current cell. Therefore, in the notebook
280 280 you must always use :kbd:`Shift-Enter` to get execution (or use the mouse and
281 281 click on the ``Run Selected`` button).
282 282
283 283 * :kbd:`Alt-Enter`: this combination is similar to the previous one, with the
284 284 exception that, if the next cell below is not empty, a new code cell will be
285 285 added to the notebook, even if the cell execution happens not in the last cell.
286 286 In this regard, :kbd:`Alt-Enter`: is simply a shortcut for the :kbd:`Shift-Enter`,
287 287 :kbd:`Ctrl-m a` sequence.
288 288
289 289 * :kbd:`Ctrl-Enter`: execute the current cell in "terminal mode", where any
290 290 output is shown but the cursor stays in the current cell, whose input
291 291 area is flushed empty. This is convenient to do quick in-place experiments
292 292 or query things like filesystem content without creating additional cells you
293 293 may not want saved in your notebook.
294 294
295 295 * :kbd:`Ctrl-m`: this is the prefix for all other keybindings, which consist
296 296 of an additional single letter. Type :kbd:`Ctrl-m h` (that is, the sole
297 297 letter :kbd:`h` after :kbd:`Ctrl-m`) and IPython will show you the remaining
298 298 available keybindings.
299 299
300 300
301 301 .. _notebook_security:
302 302
303 303 Security
304 304 ========
305 305
306 306 You can protect your notebook server with a simple single-password by
307 307 setting the :attr:`NotebookApp.password` configurable. You can prepare a
308 308 hashed password using the function :func:`IPython.lib.security.passwd`:
309 309
310 310 .. sourcecode:: ipython
311 311
312 312 In [1]: from IPython.lib import passwd
313 313 In [2]: passwd()
314 314 Enter password:
315 315 Verify password:
316 316 Out[2]: 'sha1:67c9e60bb8b6:9ffede0825894254b2e042ea597d771089e11aed'
317 317
318 318 .. note::
319 319
320 320 :func:`~IPython.lib.security.passwd` can also take the password as a string
321 321 argument. **Do not** pass it as an argument inside an IPython session, as it
322 322 will be saved in your input history.
323 323
324 324 You can then add this to your :file:`ipython_notebook_config.py`, e.g.::
325 325
326 326 # Password to use for web authentication
327 327 c.NotebookApp.password = u'sha1:67c9e60bb8b6:9ffede0825894254b2e042ea597d771089e11aed'
328 328
329 329 When using a password, it is a good idea to also use SSL, so that your password
330 330 is not sent unencrypted by your browser. You can start the notebook to
331 331 communicate via a secure protocol mode using a self-signed certificate by
332 332 typing::
333 333
334 334 $ ipython notebook --certfile=mycert.pem
335 335
336 336 .. note::
337 337
338 338 A self-signed certificate can be generated with openssl. For example, the
339 339 following command will create a certificate valid for 365 days with both
340 340 the key and certificate data written to the same file::
341 341
342 342 $ openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem
343 343
344 344 Your browser will warn you of a dangerous certificate because it is
345 345 self-signed. If you want to have a fully compliant certificate that will not
346 346 raise warnings, it is possible (but rather involved) to obtain one for free,
347 347 `as explained in detailed in this tutorial`__.
348 348
349 349 .. __: http://arstechnica.com/security/news/2009/12/how-to-get-set-with-a-secure-sertificate-for-free.ars
350 350
351 351 Keep in mind that when you enable SSL support, you'll need to access the
352 352 notebook server over ``https://``, not over plain ``http://``. The startup
353 353 message from the server prints this, but it's easy to overlook and think the
354 354 server is for some reason non-responsive.
355 355
356 356 Quick how to's
357 357 ==============
358 358
359 359 Running a public notebook server
360 360 --------------------------------
361 361
362 362 If you want to access your notebook server remotely with just a web browser,
363 363 here is a quick set of instructions. Start by creating a certificate file and
364 364 a hashed password as explained above. Then, create a custom profile for the
365 365 notebook. At the command line, type::
366 366
367 367 ipython profile create nbserver
368 368
369 369 In the profile directory, edit the file ``ipython_notebook_config.py``. By
370 370 default the file has all fields commented, the minimum set you need to
371 371 uncomment and edit is here::
372 372
373 373 c = get_config()
374 374
375 375 # Kernel config
376 376 c.IPKernelApp.pylab = 'inline' # if you want plotting support always
377 377
378 378 # Notebook config
379 379 c.NotebookApp.certfile = u'/absolute/path/to/your/certificate/mycert.pem'
380 380 c.NotebookApp.ip = '*'
381 381 c.NotebookApp.open_browser = False
382 382 c.NotebookApp.password = u'sha1:bcd259ccf...your hashed password here'
383 383 # It's a good idea to put it on a known, fixed port
384 384 c.NotebookApp.port = 9999
385 385
386 386 You can then start the notebook and access it later by pointing your browser to
387 387 ``https://your.host.com:9999`` with ``ipython notebook --profile=nbserver``.
388 388
389 389 Running with a different URL prefix
390 390 -----------------------------------
391 391
392 392 The notebook dashboard (i.e. the default landing page with an overview
393 393 of all your notebooks) typically lives at a URL path of
394 394 "http://localhost:8888/". If you want to have it, and the rest of the
395 395 notebook, live under a sub-directory,
396 396 e.g. "http://localhost:8888/ipython/", you can do so with
397 397 configuration options like these (see above for instructions about
398 398 modifying ``ipython_notebook_config.py``)::
399 399
400 400 c.NotebookApp.base_project_url = '/ipython/'
401 401 c.NotebookApp.base_kernel_url = '/ipython/'
402 402 c.NotebookApp.webapp_settings = {'static_url_prefix':'/ipython/static/'}
403 403
404 404 Using a different notebook store
405 405 --------------------------------
406 406
407 407 By default the notebook server stores notebooks as files in the working
408 408 directory of the notebook server, also known as the ``notebook_dir``. This
409 409 logic is implemented in the :class:`FileNotebookManager` class. However, the
410 410 server can be configured to use a different notebook manager class, which can
411 411 store the notebooks in a different format. Currently, we ship a
412 412 :class:`AzureNotebookManager` class that stores notebooks in Azure blob
413 413 storage. This can be used by adding the following lines to your
414 414 ``ipython_notebook_config.py`` file::
415 415
416 c.NotebookApp.notebook_manager_class = 'IPython.frontend.html.notebook.azurenbmanager.AzureNotebookManager'
416 c.NotebookApp.notebook_manager_class = 'IPython.html.services.notebooks.azurenbmanager.AzureNotebookManager'
417 417 c.AzureNotebookManager.account_name = u'paste_your_account_name_here'
418 418 c.AzureNotebookManager.account_key = u'paste_your_account_key_here'
419 419 c.AzureNotebookManager.container = u'notebooks'
420 420
421 421 In addition to providing your Azure Blob Storage account name and key, you will
422 422 have to provide a container name; you can use multiple containers to organize
423 423 your Notebooks.
424 424
425 425 .. _notebook_format:
426 426
427 427 The notebook format
428 428 ===================
429 429
430 430 The notebooks themselves are JSON files with an ``ipynb`` extension, formatted
431 431 as legibly as possible with minimal extra indentation and cell content broken
432 432 across lines to make them reasonably friendly to use in version-control
433 433 workflows. You should be very careful if you ever edit manually this JSON
434 434 data, as it is extremely easy to corrupt its internal structure and make the
435 435 file impossible to load. In general, you should consider the notebook as a
436 436 file meant only to be edited by IPython itself, not for hand-editing.
437 437
438 438 .. note::
439 439
440 440 Binary data such as figures are directly saved in the JSON file. This
441 441 provides convenient single-file portability but means the files can be
442 442 large and diffs of binary data aren't very meaningful. Since the binary
443 443 blobs are encoded in a single line they only affect one line of the diff
444 444 output, but they are typically very long lines. You can use the
445 445 'ClearAll' button to remove all output from a notebook prior to
446 446 committing it to version control, if this is a concern.
447 447
448 448 The notebook server can also generate a pure-python version of your notebook,
449 449 by clicking on the 'Download' button and selecting ``py`` as the format. This
450 450 file will contain all the code cells from your notebook verbatim, and all text
451 451 cells prepended with a comment marker. The separation between code and text
452 452 cells is indicated with special comments and there is a header indicating the
453 453 format version. All output is stripped out when exporting to python.
454 454
455 455 Here is an example of a simple notebook with one text cell and one code input
456 456 cell, when exported to python format::
457 457
458 458 # <nbformat>2</nbformat>
459 459
460 460 # <markdowncell>
461 461
462 462 # A text cell
463 463
464 464 # <codecell>
465 465
466 466 print "hello IPython"
467 467
468 468
469 469 Known issues
470 470 ============
471 471
472 472 When behind a proxy, especially if your system or browser is set to autodetect
473 473 the proxy, the html notebook might fail to connect to the server's websockets,
474 474 and present you with a warning at startup. In this case, you need to configure
475 475 your system not to use the proxy for the server's address.
476 476
477 477 In Firefox, for example, go to the Preferences panel, Advanced section,
478 478 Network tab, click 'Settings...', and add the address of the notebook server
479 479 to the 'No proxy for' field.
480 480
481 481
482 482 .. _Markdown: http://daringfireball.net/projects/markdown/basics
@@ -1,468 +1,468 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 os
24 24 import sys
25 25
26 26 try:
27 27 from configparser import ConfigParser
28 28 except:
29 29 from ConfigParser import ConfigParser
30 30 from distutils.command.build_py import build_py
31 31 from distutils.cmd import Command
32 32 from glob import glob
33 33
34 34 from setupext import install_data_ext
35 35
36 36 #-------------------------------------------------------------------------------
37 37 # Useful globals and utility functions
38 38 #-------------------------------------------------------------------------------
39 39
40 40 # A few handy globals
41 41 isfile = os.path.isfile
42 42 pjoin = os.path.join
43 43 repo_root = os.path.dirname(os.path.abspath(__file__))
44 44
45 45 def oscmd(s):
46 46 print(">", s)
47 47 os.system(s)
48 48
49 49 # Py3 compatibility hacks, without assuming IPython itself is installed with
50 50 # the full py3compat machinery.
51 51
52 52 try:
53 53 execfile
54 54 except NameError:
55 55 def execfile(fname, globs, locs=None):
56 56 locs = locs or globs
57 57 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
58 58
59 59 # A little utility we'll need below, since glob() does NOT allow you to do
60 60 # exclusion on multiple endings!
61 61 def file_doesnt_endwith(test,endings):
62 62 """Return true if test is a file and its name does NOT end with any
63 63 of the strings listed in endings."""
64 64 if not isfile(test):
65 65 return False
66 66 for e in endings:
67 67 if test.endswith(e):
68 68 return False
69 69 return True
70 70
71 71 #---------------------------------------------------------------------------
72 72 # Basic project information
73 73 #---------------------------------------------------------------------------
74 74
75 75 # release.py contains version, authors, license, url, keywords, etc.
76 76 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
77 77
78 78 # Create a dict with the basic information
79 79 # This dict is eventually passed to setup after additional keys are added.
80 80 setup_args = dict(
81 81 name = name,
82 82 version = version,
83 83 description = description,
84 84 long_description = long_description,
85 85 author = author,
86 86 author_email = author_email,
87 87 url = url,
88 88 download_url = download_url,
89 89 license = license,
90 90 platforms = platforms,
91 91 keywords = keywords,
92 92 classifiers = classifiers,
93 93 cmdclass = {'install_data': install_data_ext},
94 94 )
95 95
96 96
97 97 #---------------------------------------------------------------------------
98 98 # Find packages
99 99 #---------------------------------------------------------------------------
100 100
101 101 def find_packages():
102 102 """
103 103 Find all of IPython's packages.
104 104 """
105 105 excludes = ['deathrow', 'quarantine']
106 106 packages = []
107 107 for dir,subdirs,files in os.walk('IPython'):
108 108 package = dir.replace(os.path.sep, '.')
109 109 if any(package.startswith('IPython.'+exc) for exc in excludes):
110 110 # package is to be excluded (e.g. deathrow)
111 111 continue
112 112 if '__init__.py' not in files:
113 113 # not a package
114 114 continue
115 115 packages.append(package)
116 116 return packages
117 117
118 118 #---------------------------------------------------------------------------
119 119 # Find package data
120 120 #---------------------------------------------------------------------------
121 121
122 122 def find_package_data():
123 123 """
124 124 Find IPython's package_data.
125 125 """
126 126 # This is not enough for these things to appear in an sdist.
127 127 # We need to muck with the MANIFEST to get this to work
128 128
129 129 # exclude static things that we don't ship (e.g. mathjax)
130 130 excludes = ['mathjax']
131 131
132 132 # add 'static/' prefix to exclusions, and tuplify for use in startswith
133 133 excludes = tuple([os.path.join('static', ex) for ex in excludes])
134 134
135 135 # walk notebook resources:
136 136 cwd = os.getcwd()
137 os.chdir(os.path.join('IPython', 'html', 'notebook'))
137 os.chdir(os.path.join('IPython', 'html'))
138 138 static_walk = list(os.walk('static'))
139 139 os.chdir(cwd)
140 140 static_data = []
141 141 for parent, dirs, files in static_walk:
142 142 if parent.startswith(excludes):
143 143 continue
144 144 for f in files:
145 145 static_data.append(os.path.join(parent, f))
146 146
147 147 package_data = {
148 148 'IPython.config.profile' : ['README*', '*/*.py'],
149 149 'IPython.testing' : ['*.txt'],
150 150 'IPython.testing.plugin' : ['*.txt'],
151 'IPython.html.notebook' : ['templates/*'] + static_data,
151 'IPython.html' : ['templates/*'] + static_data,
152 152 'IPython.qt.console' : ['resources/icon/*.svg'],
153 153 }
154 154 return package_data
155 155
156 156
157 157 #---------------------------------------------------------------------------
158 158 # Find data files
159 159 #---------------------------------------------------------------------------
160 160
161 161 def make_dir_struct(tag,base,out_base):
162 162 """Make the directory structure of all files below a starting dir.
163 163
164 164 This is just a convenience routine to help build a nested directory
165 165 hierarchy because distutils is too stupid to do this by itself.
166 166
167 167 XXX - this needs a proper docstring!
168 168 """
169 169
170 170 # we'll use these a lot below
171 171 lbase = len(base)
172 172 pathsep = os.path.sep
173 173 lpathsep = len(pathsep)
174 174
175 175 out = []
176 176 for (dirpath,dirnames,filenames) in os.walk(base):
177 177 # we need to strip out the dirpath from the base to map it to the
178 178 # output (installation) path. This requires possibly stripping the
179 179 # path separator, because otherwise pjoin will not work correctly
180 180 # (pjoin('foo/','/bar') returns '/bar').
181 181
182 182 dp_eff = dirpath[lbase:]
183 183 if dp_eff.startswith(pathsep):
184 184 dp_eff = dp_eff[lpathsep:]
185 185 # The output path must be anchored at the out_base marker
186 186 out_path = pjoin(out_base,dp_eff)
187 187 # Now we can generate the final filenames. Since os.walk only produces
188 188 # filenames, we must join back with the dirpath to get full valid file
189 189 # paths:
190 190 pfiles = [pjoin(dirpath,f) for f in filenames]
191 191 # Finally, generate the entry we need, which is a pari of (output
192 192 # path, files) for use as a data_files parameter in install_data.
193 193 out.append((out_path, pfiles))
194 194
195 195 return out
196 196
197 197
198 198 def find_data_files():
199 199 """
200 200 Find IPython's data_files.
201 201
202 202 Most of these are docs.
203 203 """
204 204
205 205 docdirbase = pjoin('share', 'doc', 'ipython')
206 206 manpagebase = pjoin('share', 'man', 'man1')
207 207
208 208 # Simple file lists can be made by hand
209 209 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
210 210 if not manpages:
211 211 # When running from a source tree, the manpages aren't gzipped
212 212 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
213 213
214 214 igridhelpfiles = [f for f in glob(pjoin('IPython','extensions','igrid_help.*')) if isfile(f)]
215 215
216 216 # For nested structures, use the utility above
217 217 example_files = make_dir_struct(
218 218 'data',
219 219 pjoin('docs','examples'),
220 220 pjoin(docdirbase,'examples')
221 221 )
222 222 manual_files = make_dir_struct(
223 223 'data',
224 224 pjoin('docs','html'),
225 225 pjoin(docdirbase,'manual')
226 226 )
227 227
228 228 # And assemble the entire output list
229 229 data_files = [ (manpagebase, manpages),
230 230 (pjoin(docdirbase, 'extensions'), igridhelpfiles),
231 231 ] + manual_files + example_files
232 232
233 233 return data_files
234 234
235 235
236 236 def make_man_update_target(manpage):
237 237 """Return a target_update-compliant tuple for the given manpage.
238 238
239 239 Parameters
240 240 ----------
241 241 manpage : string
242 242 Name of the manpage, must include the section number (trailing number).
243 243
244 244 Example
245 245 -------
246 246
247 247 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
248 248 ('docs/man/ipython.1.gz',
249 249 ['docs/man/ipython.1'],
250 250 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
251 251 """
252 252 man_dir = pjoin('docs', 'man')
253 253 manpage_gz = manpage + '.gz'
254 254 manpath = pjoin(man_dir, manpage)
255 255 manpath_gz = pjoin(man_dir, manpage_gz)
256 256 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
257 257 locals() )
258 258 return (manpath_gz, [manpath], gz_cmd)
259 259
260 260 # The two functions below are copied from IPython.utils.path, so we don't need
261 261 # to import IPython during setup, which fails on Python 3.
262 262
263 263 def target_outdated(target,deps):
264 264 """Determine whether a target is out of date.
265 265
266 266 target_outdated(target,deps) -> 1/0
267 267
268 268 deps: list of filenames which MUST exist.
269 269 target: single filename which may or may not exist.
270 270
271 271 If target doesn't exist or is older than any file listed in deps, return
272 272 true, otherwise return false.
273 273 """
274 274 try:
275 275 target_time = os.path.getmtime(target)
276 276 except os.error:
277 277 return 1
278 278 for dep in deps:
279 279 dep_time = os.path.getmtime(dep)
280 280 if dep_time > target_time:
281 281 #print "For target",target,"Dep failed:",dep # dbg
282 282 #print "times (dep,tar):",dep_time,target_time # dbg
283 283 return 1
284 284 return 0
285 285
286 286
287 287 def target_update(target,deps,cmd):
288 288 """Update a target with a given command given a list of dependencies.
289 289
290 290 target_update(target,deps,cmd) -> runs cmd if target is outdated.
291 291
292 292 This is just a wrapper around target_outdated() which calls the given
293 293 command if target is outdated."""
294 294
295 295 if target_outdated(target,deps):
296 296 os.system(cmd)
297 297
298 298 #---------------------------------------------------------------------------
299 299 # Find scripts
300 300 #---------------------------------------------------------------------------
301 301
302 302 def find_scripts(entry_points=False, suffix=''):
303 303 """Find IPython's scripts.
304 304
305 305 if entry_points is True:
306 306 return setuptools entry_point-style definitions
307 307 else:
308 308 return file paths of plain scripts [default]
309 309
310 310 suffix is appended to script names if entry_points is True, so that the
311 311 Python 3 scripts get named "ipython3" etc.
312 312 """
313 313 if entry_points:
314 314 console_scripts = [s % suffix for s in [
315 315 'ipython%s = IPython.terminal.ipapp:launch_new_instance',
316 316 'pycolor%s = IPython.utils.PyColorize:main',
317 317 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
318 318 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
319 319 'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance',
320 320 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
321 321 'iptest%s = IPython.testing.iptest:main',
322 322 'irunner%s = IPython.lib.irunner:main'
323 323 ]]
324 324 gui_scripts = []
325 325 scripts = dict(console_scripts=console_scripts, gui_scripts=gui_scripts)
326 326 else:
327 327 parallel_scripts = pjoin('IPython','parallel','scripts')
328 328 main_scripts = pjoin('IPython','scripts')
329 329 scripts = [
330 330 pjoin(parallel_scripts, 'ipengine'),
331 331 pjoin(parallel_scripts, 'ipcontroller'),
332 332 pjoin(parallel_scripts, 'ipcluster'),
333 333 pjoin(parallel_scripts, 'iplogger'),
334 334 pjoin(main_scripts, 'ipython'),
335 335 pjoin(main_scripts, 'pycolor'),
336 336 pjoin(main_scripts, 'irunner'),
337 337 pjoin(main_scripts, 'iptest')
338 338 ]
339 339 return scripts
340 340
341 341 #---------------------------------------------------------------------------
342 342 # Verify all dependencies
343 343 #---------------------------------------------------------------------------
344 344
345 345 def check_for_dependencies():
346 346 """Check for IPython's dependencies.
347 347
348 348 This function should NOT be called if running under setuptools!
349 349 """
350 350 from setupext.setupext import (
351 351 print_line, print_raw, print_status,
352 352 check_for_sphinx, check_for_pygments,
353 353 check_for_nose, check_for_pexpect,
354 354 check_for_pyzmq, check_for_readline
355 355 )
356 356 print_line()
357 357 print_raw("BUILDING IPYTHON")
358 358 print_status('python', sys.version)
359 359 print_status('platform', sys.platform)
360 360 if sys.platform == 'win32':
361 361 print_status('Windows version', sys.getwindowsversion())
362 362
363 363 print_raw("")
364 364 print_raw("OPTIONAL DEPENDENCIES")
365 365
366 366 check_for_sphinx()
367 367 check_for_pygments()
368 368 check_for_nose()
369 369 check_for_pexpect()
370 370 check_for_pyzmq()
371 371 check_for_readline()
372 372
373 373 #---------------------------------------------------------------------------
374 374 # VCS related
375 375 #---------------------------------------------------------------------------
376 376
377 377 # utils.submodule has checks for submodule status
378 378 execfile(pjoin('IPython','utils','submodule.py'), globals())
379 379
380 380 class UpdateSubmodules(Command):
381 381 """Update git submodules
382 382
383 383 IPython's external javascript dependencies live in a separate repo.
384 384 """
385 385 description = "Update git submodules"
386 386 user_options = []
387 387
388 388 def initialize_options(self):
389 389 pass
390 390
391 391 def finalize_options(self):
392 392 pass
393 393
394 394 def run(self):
395 395 failure = False
396 396 try:
397 397 self.spawn('git submodule init'.split())
398 398 self.spawn('git submodule update --recursive'.split())
399 399 except Exception as e:
400 400 failure = e
401 401 print(e)
402 402
403 403 if not check_submodule_status(repo_root) == 'clean':
404 404 print("submodules could not be checked out")
405 405 sys.exit(1)
406 406
407 407
408 408 def git_prebuild(pkg_dir, build_cmd=build_py):
409 409 """Return extended build or sdist command class for recording commit
410 410
411 411 records git commit in IPython.utils._sysinfo.commit
412 412
413 413 for use in IPython.utils.sysinfo.sys_info() calls after installation.
414 414
415 415 Also ensures that submodules exist prior to running
416 416 """
417 417
418 418 class MyBuildPy(build_cmd):
419 419 ''' Subclass to write commit data into installation tree '''
420 420 def run(self):
421 421 build_cmd.run(self)
422 422 # this one will only fire for build commands
423 423 if hasattr(self, 'build_lib'):
424 424 self._record_commit(self.build_lib)
425 425
426 426 def make_release_tree(self, base_dir, files):
427 427 # this one will fire for sdist
428 428 build_cmd.make_release_tree(self, base_dir, files)
429 429 self._record_commit(base_dir)
430 430
431 431 def _record_commit(self, base_dir):
432 432 import subprocess
433 433 proc = subprocess.Popen('git rev-parse --short HEAD',
434 434 stdout=subprocess.PIPE,
435 435 stderr=subprocess.PIPE,
436 436 shell=True)
437 437 repo_commit, _ = proc.communicate()
438 438 repo_commit = repo_commit.strip().decode("ascii")
439 439
440 440 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
441 441 if os.path.isfile(out_pth) and not repo_commit:
442 442 # nothing to write, don't clobber
443 443 return
444 444
445 445 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
446 446
447 447 # remove to avoid overwriting original via hard link
448 448 try:
449 449 os.remove(out_pth)
450 450 except (IOError, OSError):
451 451 pass
452 452 with open(out_pth, 'w') as out_file:
453 453 out_file.writelines([
454 454 '# GENERATED BY setup.py\n',
455 455 'commit = "%s"\n' % repo_commit,
456 456 ])
457 457 return require_submodules(MyBuildPy)
458 458
459 459
460 460 def require_submodules(command):
461 461 """decorator for instructing a command to check for submodules before running"""
462 462 class DecoratedCommand(command):
463 463 def run(self):
464 464 if not check_submodule_status(repo_root) == 'clean':
465 465 print("submodules missing! Run `setup.py submodule` and try again")
466 466 sys.exit(1)
467 467 command.run(self)
468 468 return DecoratedCommand
General Comments 0
You need to be logged in to leave comments. Login now