##// END OF EJS Templates
Merge pull request #4318 from minrk/sys.path...
Thomas Kluyver -
r12862:4d6aad34 merge
parent child Browse files
Show More
@@ -1,383 +1,383
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 An application for IPython.
3 An application for IPython.
4
4
5 All top-level applications should use the classes in this module for
5 All top-level applications should use the classes in this module for
6 handling configuration and creating configurables.
6 handling configuration and creating configurables.
7
7
8 The job of an :class:`Application` is to create the master configuration
8 The job of an :class:`Application` is to create the master configuration
9 object and then create the configurable objects, passing the config to them.
9 object and then create the configurable objects, passing the config to them.
10
10
11 Authors:
11 Authors:
12
12
13 * Brian Granger
13 * Brian Granger
14 * Fernando Perez
14 * Fernando Perez
15 * Min RK
15 * Min RK
16
16
17 """
17 """
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Copyright (C) 2008 The IPython Development Team
20 # Copyright (C) 2008 The IPython Development Team
21 #
21 #
22 # Distributed under the terms of the BSD License. The full license is in
22 # Distributed under the terms of the BSD License. The full license is in
23 # the file COPYING, distributed as part of this software.
23 # the file COPYING, distributed as part of this software.
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Imports
27 # Imports
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 import atexit
30 import atexit
31 import errno
31 import errno
32 import glob
32 import glob
33 import logging
33 import logging
34 import os
34 import os
35 import shutil
35 import shutil
36 import sys
36 import sys
37
37
38 from IPython.config.application import Application, catch_config_error
38 from IPython.config.application import Application, catch_config_error
39 from IPython.config.loader import ConfigFileNotFound
39 from IPython.config.loader import ConfigFileNotFound
40 from IPython.core import release, crashhandler
40 from IPython.core import release, crashhandler
41 from IPython.core.profiledir import ProfileDir, ProfileDirError
41 from IPython.core.profiledir import ProfileDir, ProfileDirError
42 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
42 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
43 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict, Set, Instance
43 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict, Set, Instance
44
44
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46 # Classes and functions
46 # Classes and functions
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48
48
49
49
50 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
51 # Base Application Class
51 # Base Application Class
52 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
53
53
54 # aliases and flags
54 # aliases and flags
55
55
56 base_aliases = {
56 base_aliases = {
57 'profile-dir' : 'ProfileDir.location',
57 'profile-dir' : 'ProfileDir.location',
58 'profile' : 'BaseIPythonApplication.profile',
58 'profile' : 'BaseIPythonApplication.profile',
59 'ipython-dir' : 'BaseIPythonApplication.ipython_dir',
59 'ipython-dir' : 'BaseIPythonApplication.ipython_dir',
60 'log-level' : 'Application.log_level',
60 'log-level' : 'Application.log_level',
61 'config' : 'BaseIPythonApplication.extra_config_file',
61 'config' : 'BaseIPythonApplication.extra_config_file',
62 }
62 }
63
63
64 base_flags = dict(
64 base_flags = dict(
65 debug = ({'Application' : {'log_level' : logging.DEBUG}},
65 debug = ({'Application' : {'log_level' : logging.DEBUG}},
66 "set log level to logging.DEBUG (maximize logging output)"),
66 "set log level to logging.DEBUG (maximize logging output)"),
67 quiet = ({'Application' : {'log_level' : logging.CRITICAL}},
67 quiet = ({'Application' : {'log_level' : logging.CRITICAL}},
68 "set log level to logging.CRITICAL (minimize logging output)"),
68 "set log level to logging.CRITICAL (minimize logging output)"),
69 init = ({'BaseIPythonApplication' : {
69 init = ({'BaseIPythonApplication' : {
70 'copy_config_files' : True,
70 'copy_config_files' : True,
71 'auto_create' : True}
71 'auto_create' : True}
72 }, """Initialize profile with default config files. This is equivalent
72 }, """Initialize profile with default config files. This is equivalent
73 to running `ipython profile create <profile>` prior to startup.
73 to running `ipython profile create <profile>` prior to startup.
74 """)
74 """)
75 )
75 )
76
76
77
77
78 class BaseIPythonApplication(Application):
78 class BaseIPythonApplication(Application):
79
79
80 name = Unicode(u'ipython')
80 name = Unicode(u'ipython')
81 description = Unicode(u'IPython: an enhanced interactive Python shell.')
81 description = Unicode(u'IPython: an enhanced interactive Python shell.')
82 version = Unicode(release.version)
82 version = Unicode(release.version)
83
83
84 aliases = Dict(base_aliases)
84 aliases = Dict(base_aliases)
85 flags = Dict(base_flags)
85 flags = Dict(base_flags)
86 classes = List([ProfileDir])
86 classes = List([ProfileDir])
87
87
88 # Track whether the config_file has changed,
88 # Track whether the config_file has changed,
89 # because some logic happens only if we aren't using the default.
89 # because some logic happens only if we aren't using the default.
90 config_file_specified = Set()
90 config_file_specified = Set()
91
91
92 config_file_name = Unicode()
92 config_file_name = Unicode()
93 def _config_file_name_default(self):
93 def _config_file_name_default(self):
94 return self.name.replace('-','_') + u'_config.py'
94 return self.name.replace('-','_') + u'_config.py'
95 def _config_file_name_changed(self, name, old, new):
95 def _config_file_name_changed(self, name, old, new):
96 if new != old:
96 if new != old:
97 self.config_file_specified.add(new)
97 self.config_file_specified.add(new)
98
98
99 # The directory that contains IPython's builtin profiles.
99 # The directory that contains IPython's builtin profiles.
100 builtin_profile_dir = Unicode(
100 builtin_profile_dir = Unicode(
101 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
101 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
102 )
102 )
103
103
104 config_file_paths = List(Unicode)
104 config_file_paths = List(Unicode)
105 def _config_file_paths_default(self):
105 def _config_file_paths_default(self):
106 return [os.getcwdu()]
106 return [os.getcwdu()]
107
107
108 extra_config_file = Unicode(config=True,
108 extra_config_file = Unicode(config=True,
109 help="""Path to an extra config file to load.
109 help="""Path to an extra config file to load.
110
110
111 If specified, load this config file in addition to any other IPython config.
111 If specified, load this config file in addition to any other IPython config.
112 """)
112 """)
113 def _extra_config_file_changed(self, name, old, new):
113 def _extra_config_file_changed(self, name, old, new):
114 try:
114 try:
115 self.config_files.remove(old)
115 self.config_files.remove(old)
116 except ValueError:
116 except ValueError:
117 pass
117 pass
118 self.config_file_specified.add(new)
118 self.config_file_specified.add(new)
119 self.config_files.append(new)
119 self.config_files.append(new)
120
120
121 profile = Unicode(u'default', config=True,
121 profile = Unicode(u'default', config=True,
122 help="""The IPython profile to use."""
122 help="""The IPython profile to use."""
123 )
123 )
124
124
125 def _profile_changed(self, name, old, new):
125 def _profile_changed(self, name, old, new):
126 self.builtin_profile_dir = os.path.join(
126 self.builtin_profile_dir = os.path.join(
127 get_ipython_package_dir(), u'config', u'profile', new
127 get_ipython_package_dir(), u'config', u'profile', new
128 )
128 )
129
129
130 ipython_dir = Unicode(config=True,
130 ipython_dir = Unicode(config=True,
131 help="""
131 help="""
132 The name of the IPython directory. This directory is used for logging
132 The name of the IPython directory. This directory is used for logging
133 configuration (through profiles), history storage, etc. The default
133 configuration (through profiles), history storage, etc. The default
134 is usually $HOME/.ipython. This options can also be specified through
134 is usually $HOME/.ipython. This options can also be specified through
135 the environment variable IPYTHONDIR.
135 the environment variable IPYTHONDIR.
136 """
136 """
137 )
137 )
138 def _ipython_dir_default(self):
138 def _ipython_dir_default(self):
139 d = get_ipython_dir()
139 d = get_ipython_dir()
140 self._ipython_dir_changed('ipython_dir', '', d)
140 self._ipython_dir_changed('ipython_dir', d, d)
141 return d
141 return d
142
142
143 _in_init_profile_dir = False
143 _in_init_profile_dir = False
144 profile_dir = Instance(ProfileDir)
144 profile_dir = Instance(ProfileDir)
145 def _profile_dir_default(self):
145 def _profile_dir_default(self):
146 # avoid recursion
146 # avoid recursion
147 if self._in_init_profile_dir:
147 if self._in_init_profile_dir:
148 return
148 return
149 # profile_dir requested early, force initialization
149 # profile_dir requested early, force initialization
150 self.init_profile_dir()
150 self.init_profile_dir()
151 return self.profile_dir
151 return self.profile_dir
152
152
153 overwrite = Bool(False, config=True,
153 overwrite = Bool(False, config=True,
154 help="""Whether to overwrite existing config files when copying""")
154 help="""Whether to overwrite existing config files when copying""")
155 auto_create = Bool(False, config=True,
155 auto_create = Bool(False, config=True,
156 help="""Whether to create profile dir if it doesn't exist""")
156 help="""Whether to create profile dir if it doesn't exist""")
157
157
158 config_files = List(Unicode)
158 config_files = List(Unicode)
159 def _config_files_default(self):
159 def _config_files_default(self):
160 return [self.config_file_name]
160 return [self.config_file_name]
161
161
162 copy_config_files = Bool(False, config=True,
162 copy_config_files = Bool(False, config=True,
163 help="""Whether to install the default config files into the profile dir.
163 help="""Whether to install the default config files into the profile dir.
164 If a new profile is being created, and IPython contains config files for that
164 If a new profile is being created, and IPython contains config files for that
165 profile, then they will be staged into the new directory. Otherwise,
165 profile, then they will be staged into the new directory. Otherwise,
166 default config files will be automatically generated.
166 default config files will be automatically generated.
167 """)
167 """)
168
168
169 verbose_crash = Bool(False, config=True,
169 verbose_crash = Bool(False, config=True,
170 help="""Create a massive crash report when IPython encounters what may be an
170 help="""Create a massive crash report when IPython encounters what may be an
171 internal error. The default is to append a short message to the
171 internal error. The default is to append a short message to the
172 usual traceback""")
172 usual traceback""")
173
173
174 # The class to use as the crash handler.
174 # The class to use as the crash handler.
175 crash_handler_class = Type(crashhandler.CrashHandler)
175 crash_handler_class = Type(crashhandler.CrashHandler)
176
176
177 @catch_config_error
177 @catch_config_error
178 def __init__(self, **kwargs):
178 def __init__(self, **kwargs):
179 super(BaseIPythonApplication, self).__init__(**kwargs)
179 super(BaseIPythonApplication, self).__init__(**kwargs)
180 # ensure current working directory exists
180 # ensure current working directory exists
181 try:
181 try:
182 directory = os.getcwdu()
182 directory = os.getcwdu()
183 except:
183 except:
184 # raise exception
184 # raise exception
185 self.log.error("Current working directory doesn't exist.")
185 self.log.error("Current working directory doesn't exist.")
186 raise
186 raise
187
187
188 #-------------------------------------------------------------------------
188 #-------------------------------------------------------------------------
189 # Various stages of Application creation
189 # Various stages of Application creation
190 #-------------------------------------------------------------------------
190 #-------------------------------------------------------------------------
191
191
192 def init_crash_handler(self):
192 def init_crash_handler(self):
193 """Create a crash handler, typically setting sys.excepthook to it."""
193 """Create a crash handler, typically setting sys.excepthook to it."""
194 self.crash_handler = self.crash_handler_class(self)
194 self.crash_handler = self.crash_handler_class(self)
195 sys.excepthook = self.excepthook
195 sys.excepthook = self.excepthook
196 def unset_crashhandler():
196 def unset_crashhandler():
197 sys.excepthook = sys.__excepthook__
197 sys.excepthook = sys.__excepthook__
198 atexit.register(unset_crashhandler)
198 atexit.register(unset_crashhandler)
199
199
200 def excepthook(self, etype, evalue, tb):
200 def excepthook(self, etype, evalue, tb):
201 """this is sys.excepthook after init_crashhandler
201 """this is sys.excepthook after init_crashhandler
202
202
203 set self.verbose_crash=True to use our full crashhandler, instead of
203 set self.verbose_crash=True to use our full crashhandler, instead of
204 a regular traceback with a short message (crash_handler_lite)
204 a regular traceback with a short message (crash_handler_lite)
205 """
205 """
206
206
207 if self.verbose_crash:
207 if self.verbose_crash:
208 return self.crash_handler(etype, evalue, tb)
208 return self.crash_handler(etype, evalue, tb)
209 else:
209 else:
210 return crashhandler.crash_handler_lite(etype, evalue, tb)
210 return crashhandler.crash_handler_lite(etype, evalue, tb)
211
211
212 def _ipython_dir_changed(self, name, old, new):
212 def _ipython_dir_changed(self, name, old, new):
213 if old in sys.path:
213 if old in sys.path:
214 sys.path.remove(old)
214 sys.path.remove(old)
215 sys.path.append(os.path.abspath(new))
215 sys.path.append(os.path.abspath(new))
216 if not os.path.isdir(new):
216 if not os.path.isdir(new):
217 os.makedirs(new, mode=0o777)
217 os.makedirs(new, mode=0o777)
218 readme = os.path.join(new, 'README')
218 readme = os.path.join(new, 'README')
219 readme_src = os.path.join(get_ipython_package_dir(), u'config', u'profile', 'README')
219 readme_src = os.path.join(get_ipython_package_dir(), u'config', u'profile', 'README')
220 if not os.path.exists(readme) and os.path.exists(readme_src):
220 if not os.path.exists(readme) and os.path.exists(readme_src):
221 shutil.copy(readme_src, readme)
221 shutil.copy(readme_src, readme)
222 for d in ('extensions', 'nbextensions'):
222 for d in ('extensions', 'nbextensions'):
223 path = os.path.join(new, d)
223 path = os.path.join(new, d)
224 if not os.path.exists(path):
224 if not os.path.exists(path):
225 try:
225 try:
226 os.mkdir(path)
226 os.mkdir(path)
227 except OSError as e:
227 except OSError as e:
228 if e.errno != errno.EEXIST:
228 if e.errno != errno.EEXIST:
229 self.log.error("couldn't create path %s: %s", path, e)
229 self.log.error("couldn't create path %s: %s", path, e)
230 self.log.debug("IPYTHONDIR set to: %s" % new)
230 self.log.debug("IPYTHONDIR set to: %s" % new)
231
231
232 def load_config_file(self, suppress_errors=True):
232 def load_config_file(self, suppress_errors=True):
233 """Load the config file.
233 """Load the config file.
234
234
235 By default, errors in loading config are handled, and a warning
235 By default, errors in loading config are handled, and a warning
236 printed on screen. For testing, the suppress_errors option is set
236 printed on screen. For testing, the suppress_errors option is set
237 to False, so errors will make tests fail.
237 to False, so errors will make tests fail.
238 """
238 """
239 self.log.debug("Searching path %s for config files", self.config_file_paths)
239 self.log.debug("Searching path %s for config files", self.config_file_paths)
240 base_config = 'ipython_config.py'
240 base_config = 'ipython_config.py'
241 self.log.debug("Attempting to load config file: %s" %
241 self.log.debug("Attempting to load config file: %s" %
242 base_config)
242 base_config)
243 try:
243 try:
244 Application.load_config_file(
244 Application.load_config_file(
245 self,
245 self,
246 base_config,
246 base_config,
247 path=self.config_file_paths
247 path=self.config_file_paths
248 )
248 )
249 except ConfigFileNotFound:
249 except ConfigFileNotFound:
250 # ignore errors loading parent
250 # ignore errors loading parent
251 self.log.debug("Config file %s not found", base_config)
251 self.log.debug("Config file %s not found", base_config)
252 pass
252 pass
253
253
254 for config_file_name in self.config_files:
254 for config_file_name in self.config_files:
255 if not config_file_name or config_file_name == base_config:
255 if not config_file_name or config_file_name == base_config:
256 continue
256 continue
257 self.log.debug("Attempting to load config file: %s" %
257 self.log.debug("Attempting to load config file: %s" %
258 self.config_file_name)
258 self.config_file_name)
259 try:
259 try:
260 Application.load_config_file(
260 Application.load_config_file(
261 self,
261 self,
262 config_file_name,
262 config_file_name,
263 path=self.config_file_paths
263 path=self.config_file_paths
264 )
264 )
265 except ConfigFileNotFound:
265 except ConfigFileNotFound:
266 # Only warn if the default config file was NOT being used.
266 # Only warn if the default config file was NOT being used.
267 if config_file_name in self.config_file_specified:
267 if config_file_name in self.config_file_specified:
268 msg = self.log.warn
268 msg = self.log.warn
269 else:
269 else:
270 msg = self.log.debug
270 msg = self.log.debug
271 msg("Config file not found, skipping: %s", config_file_name)
271 msg("Config file not found, skipping: %s", config_file_name)
272 except:
272 except:
273 # For testing purposes.
273 # For testing purposes.
274 if not suppress_errors:
274 if not suppress_errors:
275 raise
275 raise
276 self.log.warn("Error loading config file: %s" %
276 self.log.warn("Error loading config file: %s" %
277 self.config_file_name, exc_info=True)
277 self.config_file_name, exc_info=True)
278
278
279 def init_profile_dir(self):
279 def init_profile_dir(self):
280 """initialize the profile dir"""
280 """initialize the profile dir"""
281 self._in_init_profile_dir = True
281 self._in_init_profile_dir = True
282 if self.profile_dir is not None:
282 if self.profile_dir is not None:
283 # already ran
283 # already ran
284 return
284 return
285 try:
285 try:
286 # location explicitly specified:
286 # location explicitly specified:
287 location = self.config.ProfileDir.location
287 location = self.config.ProfileDir.location
288 except AttributeError:
288 except AttributeError:
289 # location not specified, find by profile name
289 # location not specified, find by profile name
290 try:
290 try:
291 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
291 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
292 except ProfileDirError:
292 except ProfileDirError:
293 # not found, maybe create it (always create default profile)
293 # not found, maybe create it (always create default profile)
294 if self.auto_create or self.profile == 'default':
294 if self.auto_create or self.profile == 'default':
295 try:
295 try:
296 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
296 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
297 except ProfileDirError:
297 except ProfileDirError:
298 self.log.fatal("Could not create profile: %r"%self.profile)
298 self.log.fatal("Could not create profile: %r"%self.profile)
299 self.exit(1)
299 self.exit(1)
300 else:
300 else:
301 self.log.info("Created profile dir: %r"%p.location)
301 self.log.info("Created profile dir: %r"%p.location)
302 else:
302 else:
303 self.log.fatal("Profile %r not found."%self.profile)
303 self.log.fatal("Profile %r not found."%self.profile)
304 self.exit(1)
304 self.exit(1)
305 else:
305 else:
306 self.log.info("Using existing profile dir: %r"%p.location)
306 self.log.info("Using existing profile dir: %r"%p.location)
307 else:
307 else:
308 # location is fully specified
308 # location is fully specified
309 try:
309 try:
310 p = ProfileDir.find_profile_dir(location, self.config)
310 p = ProfileDir.find_profile_dir(location, self.config)
311 except ProfileDirError:
311 except ProfileDirError:
312 # not found, maybe create it
312 # not found, maybe create it
313 if self.auto_create:
313 if self.auto_create:
314 try:
314 try:
315 p = ProfileDir.create_profile_dir(location, self.config)
315 p = ProfileDir.create_profile_dir(location, self.config)
316 except ProfileDirError:
316 except ProfileDirError:
317 self.log.fatal("Could not create profile directory: %r"%location)
317 self.log.fatal("Could not create profile directory: %r"%location)
318 self.exit(1)
318 self.exit(1)
319 else:
319 else:
320 self.log.info("Creating new profile dir: %r"%location)
320 self.log.info("Creating new profile dir: %r"%location)
321 else:
321 else:
322 self.log.fatal("Profile directory %r not found."%location)
322 self.log.fatal("Profile directory %r not found."%location)
323 self.exit(1)
323 self.exit(1)
324 else:
324 else:
325 self.log.info("Using existing profile dir: %r"%location)
325 self.log.info("Using existing profile dir: %r"%location)
326
326
327 self.profile_dir = p
327 self.profile_dir = p
328 self.config_file_paths.append(p.location)
328 self.config_file_paths.append(p.location)
329 self._in_init_profile_dir = False
329 self._in_init_profile_dir = False
330
330
331 def init_config_files(self):
331 def init_config_files(self):
332 """[optionally] copy default config files into profile dir."""
332 """[optionally] copy default config files into profile dir."""
333 # copy config files
333 # copy config files
334 path = self.builtin_profile_dir
334 path = self.builtin_profile_dir
335 if self.copy_config_files:
335 if self.copy_config_files:
336 src = self.profile
336 src = self.profile
337
337
338 cfg = self.config_file_name
338 cfg = self.config_file_name
339 if path and os.path.exists(os.path.join(path, cfg)):
339 if path and os.path.exists(os.path.join(path, cfg)):
340 self.log.warn("Staging %r from %s into %r [overwrite=%s]"%(
340 self.log.warn("Staging %r from %s into %r [overwrite=%s]"%(
341 cfg, src, self.profile_dir.location, self.overwrite)
341 cfg, src, self.profile_dir.location, self.overwrite)
342 )
342 )
343 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
343 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
344 else:
344 else:
345 self.stage_default_config_file()
345 self.stage_default_config_file()
346 else:
346 else:
347 # Still stage *bundled* config files, but not generated ones
347 # Still stage *bundled* config files, but not generated ones
348 # This is necessary for `ipython profile=sympy` to load the profile
348 # This is necessary for `ipython profile=sympy` to load the profile
349 # on the first go
349 # on the first go
350 files = glob.glob(os.path.join(path, '*.py'))
350 files = glob.glob(os.path.join(path, '*.py'))
351 for fullpath in files:
351 for fullpath in files:
352 cfg = os.path.basename(fullpath)
352 cfg = os.path.basename(fullpath)
353 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
353 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
354 # file was copied
354 # file was copied
355 self.log.warn("Staging bundled %s from %s into %r"%(
355 self.log.warn("Staging bundled %s from %s into %r"%(
356 cfg, self.profile, self.profile_dir.location)
356 cfg, self.profile, self.profile_dir.location)
357 )
357 )
358
358
359
359
360 def stage_default_config_file(self):
360 def stage_default_config_file(self):
361 """auto generate default config file, and stage it into the profile."""
361 """auto generate default config file, and stage it into the profile."""
362 s = self.generate_config_file()
362 s = self.generate_config_file()
363 fname = os.path.join(self.profile_dir.location, self.config_file_name)
363 fname = os.path.join(self.profile_dir.location, self.config_file_name)
364 if self.overwrite or not os.path.exists(fname):
364 if self.overwrite or not os.path.exists(fname):
365 self.log.warn("Generating default config file: %r"%(fname))
365 self.log.warn("Generating default config file: %r"%(fname))
366 with open(fname, 'w') as f:
366 with open(fname, 'w') as f:
367 f.write(s)
367 f.write(s)
368
368
369 @catch_config_error
369 @catch_config_error
370 def initialize(self, argv=None):
370 def initialize(self, argv=None):
371 # don't hook up crash handler before parsing command-line
371 # don't hook up crash handler before parsing command-line
372 self.parse_command_line(argv)
372 self.parse_command_line(argv)
373 self.init_crash_handler()
373 self.init_crash_handler()
374 if self.subapp is not None:
374 if self.subapp is not None:
375 # stop here if subapp is taking over
375 # stop here if subapp is taking over
376 return
376 return
377 cl_config = self.config
377 cl_config = self.config
378 self.init_profile_dir()
378 self.init_profile_dir()
379 self.init_config_files()
379 self.init_config_files()
380 self.load_config_file()
380 self.load_config_file()
381 # enforce cl-opts override configfile opts:
381 # enforce cl-opts override configfile opts:
382 self.update_config(cl_config)
382 self.update_config(cl_config)
383
383
@@ -1,170 +1,186
1 """test the IPython Kernel"""
1 """test the IPython Kernel"""
2
2
3 #-------------------------------------------------------------------------------
3 #-------------------------------------------------------------------------------
4 # Copyright (C) 2013 The IPython Development Team
4 # Copyright (C) 2013 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
7 # the file COPYING, distributed as part of this software.
8 #-------------------------------------------------------------------------------
8 #-------------------------------------------------------------------------------
9
9
10 #-------------------------------------------------------------------------------
10 #-------------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13
13
14 import sys
14 import sys
15
15
16 import nose.tools as nt
16 import nose.tools as nt
17
17
18 from IPython.testing import decorators as dec, tools as tt
18 from IPython.testing import decorators as dec, tools as tt
19 from IPython.utils import py3compat
19 from IPython.utils import py3compat
20 from IPython.utils.path import locate_profile
20
21
21 from .utils import new_kernel, kernel, TIMEOUT, assemble_output, execute, flush_channels
22 from .utils import new_kernel, kernel, TIMEOUT, assemble_output, execute, flush_channels
22
23
23 #-------------------------------------------------------------------------------
24 #-------------------------------------------------------------------------------
24 # Tests
25 # Tests
25 #-------------------------------------------------------------------------------
26 #-------------------------------------------------------------------------------
26
27
27
28
28 def _check_mp_mode(kc, expected=False, stream="stdout"):
29 def _check_mp_mode(kc, expected=False, stream="stdout"):
29 execute(kc=kc, code="import sys")
30 execute(kc=kc, code="import sys")
30 flush_channels(kc)
31 flush_channels(kc)
31 msg_id, content = execute(kc=kc, code="print (sys.%s._check_mp_mode())" % stream)
32 msg_id, content = execute(kc=kc, code="print (sys.%s._check_mp_mode())" % stream)
32 stdout, stderr = assemble_output(kc.iopub_channel)
33 stdout, stderr = assemble_output(kc.iopub_channel)
33 nt.assert_equal(eval(stdout.strip()), expected)
34 nt.assert_equal(eval(stdout.strip()), expected)
34
35
35
36
36 # printing tests
37 # printing tests
37
38
38 def test_simple_print():
39 def test_simple_print():
39 """simple print statement in kernel"""
40 """simple print statement in kernel"""
40 with kernel() as kc:
41 with kernel() as kc:
41 iopub = kc.iopub_channel
42 iopub = kc.iopub_channel
42 msg_id, content = execute(kc=kc, code="print ('hi')")
43 msg_id, content = execute(kc=kc, code="print ('hi')")
43 stdout, stderr = assemble_output(iopub)
44 stdout, stderr = assemble_output(iopub)
44 nt.assert_equal(stdout, 'hi\n')
45 nt.assert_equal(stdout, 'hi\n')
45 nt.assert_equal(stderr, '')
46 nt.assert_equal(stderr, '')
46 _check_mp_mode(kc, expected=False)
47 _check_mp_mode(kc, expected=False)
47
48
48
49
50 def test_sys_path():
51 """test that sys.path doesn't get messed up by default"""
52 with kernel() as kc:
53 msg_id, content = execute(kc=kc, code="import sys; print (repr(sys.path[0]))")
54 stdout, stderr = assemble_output(kc.iopub_channel)
55 nt.assert_equal(stdout, "''\n")
56
57 def test_sys_path_profile_dir():
58 """test that sys.path doesn't get messed up when `--profile-dir` is specified"""
59
60 with new_kernel(['--profile-dir', locate_profile('default')]) as kc:
61 msg_id, content = execute(kc=kc, code="import sys; print (repr(sys.path[0]))")
62 stdout, stderr = assemble_output(kc.iopub_channel)
63 nt.assert_equal(stdout, "''\n")
64
49 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
65 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
50 def test_subprocess_print():
66 def test_subprocess_print():
51 """printing from forked mp.Process"""
67 """printing from forked mp.Process"""
52 with new_kernel() as kc:
68 with new_kernel() as kc:
53 iopub = kc.iopub_channel
69 iopub = kc.iopub_channel
54
70
55 _check_mp_mode(kc, expected=False)
71 _check_mp_mode(kc, expected=False)
56 flush_channels(kc)
72 flush_channels(kc)
57 np = 5
73 np = 5
58 code = '\n'.join([
74 code = '\n'.join([
59 "from __future__ import print_function",
75 "from __future__ import print_function",
60 "import multiprocessing as mp",
76 "import multiprocessing as mp",
61 "pool = [mp.Process(target=print, args=('hello', i,)) for i in range(%i)]" % np,
77 "pool = [mp.Process(target=print, args=('hello', i,)) for i in range(%i)]" % np,
62 "for p in pool: p.start()",
78 "for p in pool: p.start()",
63 "for p in pool: p.join()"
79 "for p in pool: p.join()"
64 ])
80 ])
65
81
66 expected = '\n'.join([
82 expected = '\n'.join([
67 "hello %s" % i for i in range(np)
83 "hello %s" % i for i in range(np)
68 ]) + '\n'
84 ]) + '\n'
69
85
70 msg_id, content = execute(kc=kc, code=code)
86 msg_id, content = execute(kc=kc, code=code)
71 stdout, stderr = assemble_output(iopub)
87 stdout, stderr = assemble_output(iopub)
72 nt.assert_equal(stdout.count("hello"), np, stdout)
88 nt.assert_equal(stdout.count("hello"), np, stdout)
73 for n in range(np):
89 for n in range(np):
74 nt.assert_equal(stdout.count(str(n)), 1, stdout)
90 nt.assert_equal(stdout.count(str(n)), 1, stdout)
75 nt.assert_equal(stderr, '')
91 nt.assert_equal(stderr, '')
76 _check_mp_mode(kc, expected=False)
92 _check_mp_mode(kc, expected=False)
77 _check_mp_mode(kc, expected=False, stream="stderr")
93 _check_mp_mode(kc, expected=False, stream="stderr")
78
94
79
95
80 def test_subprocess_noprint():
96 def test_subprocess_noprint():
81 """mp.Process without print doesn't trigger iostream mp_mode"""
97 """mp.Process without print doesn't trigger iostream mp_mode"""
82 with kernel() as kc:
98 with kernel() as kc:
83 iopub = kc.iopub_channel
99 iopub = kc.iopub_channel
84
100
85 np = 5
101 np = 5
86 code = '\n'.join([
102 code = '\n'.join([
87 "import multiprocessing as mp",
103 "import multiprocessing as mp",
88 "pool = [mp.Process(target=range, args=(i,)) for i in range(%i)]" % np,
104 "pool = [mp.Process(target=range, args=(i,)) for i in range(%i)]" % np,
89 "for p in pool: p.start()",
105 "for p in pool: p.start()",
90 "for p in pool: p.join()"
106 "for p in pool: p.join()"
91 ])
107 ])
92
108
93 msg_id, content = execute(kc=kc, code=code)
109 msg_id, content = execute(kc=kc, code=code)
94 stdout, stderr = assemble_output(iopub)
110 stdout, stderr = assemble_output(iopub)
95 nt.assert_equal(stdout, '')
111 nt.assert_equal(stdout, '')
96 nt.assert_equal(stderr, '')
112 nt.assert_equal(stderr, '')
97
113
98 _check_mp_mode(kc, expected=False)
114 _check_mp_mode(kc, expected=False)
99 _check_mp_mode(kc, expected=False, stream="stderr")
115 _check_mp_mode(kc, expected=False, stream="stderr")
100
116
101
117
102 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
118 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
103 def test_subprocess_error():
119 def test_subprocess_error():
104 """error in mp.Process doesn't crash"""
120 """error in mp.Process doesn't crash"""
105 with new_kernel() as kc:
121 with new_kernel() as kc:
106 iopub = kc.iopub_channel
122 iopub = kc.iopub_channel
107
123
108 code = '\n'.join([
124 code = '\n'.join([
109 "import multiprocessing as mp",
125 "import multiprocessing as mp",
110 "p = mp.Process(target=int, args=('hi',))",
126 "p = mp.Process(target=int, args=('hi',))",
111 "p.start()",
127 "p.start()",
112 "p.join()",
128 "p.join()",
113 ])
129 ])
114
130
115 msg_id, content = execute(kc=kc, code=code)
131 msg_id, content = execute(kc=kc, code=code)
116 stdout, stderr = assemble_output(iopub)
132 stdout, stderr = assemble_output(iopub)
117 nt.assert_equal(stdout, '')
133 nt.assert_equal(stdout, '')
118 nt.assert_true("ValueError" in stderr, stderr)
134 nt.assert_true("ValueError" in stderr, stderr)
119
135
120 _check_mp_mode(kc, expected=False)
136 _check_mp_mode(kc, expected=False)
121 _check_mp_mode(kc, expected=False, stream="stderr")
137 _check_mp_mode(kc, expected=False, stream="stderr")
122
138
123 # raw_input tests
139 # raw_input tests
124
140
125 def test_raw_input():
141 def test_raw_input():
126 """test [raw_]input"""
142 """test [raw_]input"""
127 with kernel() as kc:
143 with kernel() as kc:
128 iopub = kc.iopub_channel
144 iopub = kc.iopub_channel
129
145
130 input_f = "input" if py3compat.PY3 else "raw_input"
146 input_f = "input" if py3compat.PY3 else "raw_input"
131 theprompt = "prompt> "
147 theprompt = "prompt> "
132 code = 'print({input_f}("{theprompt}"))'.format(**locals())
148 code = 'print({input_f}("{theprompt}"))'.format(**locals())
133 msg_id = kc.execute(code, allow_stdin=True)
149 msg_id = kc.execute(code, allow_stdin=True)
134 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
150 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
135 nt.assert_equal(msg['header']['msg_type'], u'input_request')
151 nt.assert_equal(msg['header']['msg_type'], u'input_request')
136 content = msg['content']
152 content = msg['content']
137 nt.assert_equal(content['prompt'], theprompt)
153 nt.assert_equal(content['prompt'], theprompt)
138 text = "some text"
154 text = "some text"
139 kc.input(text)
155 kc.input(text)
140 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
156 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
141 nt.assert_equal(reply['content']['status'], 'ok')
157 nt.assert_equal(reply['content']['status'], 'ok')
142 stdout, stderr = assemble_output(iopub)
158 stdout, stderr = assemble_output(iopub)
143 nt.assert_equal(stdout, text + "\n")
159 nt.assert_equal(stdout, text + "\n")
144
160
145
161
146 @dec.skipif(py3compat.PY3)
162 @dec.skipif(py3compat.PY3)
147 def test_eval_input():
163 def test_eval_input():
148 """test input() on Python 2"""
164 """test input() on Python 2"""
149 with kernel() as kc:
165 with kernel() as kc:
150 iopub = kc.iopub_channel
166 iopub = kc.iopub_channel
151
167
152 input_f = "input" if py3compat.PY3 else "raw_input"
168 input_f = "input" if py3compat.PY3 else "raw_input"
153 theprompt = "prompt> "
169 theprompt = "prompt> "
154 code = 'print(input("{theprompt}"))'.format(**locals())
170 code = 'print(input("{theprompt}"))'.format(**locals())
155 msg_id = kc.execute(code, allow_stdin=True)
171 msg_id = kc.execute(code, allow_stdin=True)
156 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
172 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
157 nt.assert_equal(msg['header']['msg_type'], u'input_request')
173 nt.assert_equal(msg['header']['msg_type'], u'input_request')
158 content = msg['content']
174 content = msg['content']
159 nt.assert_equal(content['prompt'], theprompt)
175 nt.assert_equal(content['prompt'], theprompt)
160 kc.input("1+1")
176 kc.input("1+1")
161 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
177 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
162 nt.assert_equal(reply['content']['status'], 'ok')
178 nt.assert_equal(reply['content']['status'], 'ok')
163 stdout, stderr = assemble_output(iopub)
179 stdout, stderr = assemble_output(iopub)
164 nt.assert_equal(stdout, "2\n")
180 nt.assert_equal(stdout, "2\n")
165
181
166
182
167 def test_help_output():
183 def test_help_output():
168 """ipython kernel --help-all works"""
184 """ipython kernel --help-all works"""
169 tt.help_all_output_test('kernel')
185 tt.help_all_output_test('kernel')
170
186
@@ -1,167 +1,170
1 """utilities for testing IPython kernels"""
1 """utilities for testing IPython kernels"""
2
2
3 #-------------------------------------------------------------------------------
3 #-------------------------------------------------------------------------------
4 # Copyright (C) 2013 The IPython Development Team
4 # Copyright (C) 2013 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
7 # the file COPYING, distributed as part of this software.
8 #-------------------------------------------------------------------------------
8 #-------------------------------------------------------------------------------
9
9
10 #-------------------------------------------------------------------------------
10 #-------------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13
13
14 import atexit
14 import atexit
15
15
16 from contextlib import contextmanager
16 from contextlib import contextmanager
17 from subprocess import PIPE
17 from subprocess import PIPE
18 from Queue import Empty
18 from Queue import Empty
19
19
20 import nose.tools as nt
20 import nose.tools as nt
21
21
22 from IPython.kernel import KernelManager
22 from IPython.kernel import KernelManager
23
23
24 #-------------------------------------------------------------------------------
24 #-------------------------------------------------------------------------------
25 # Globals
25 # Globals
26 #-------------------------------------------------------------------------------
26 #-------------------------------------------------------------------------------
27
27
28 STARTUP_TIMEOUT = 60
28 STARTUP_TIMEOUT = 60
29 TIMEOUT = 15
29 TIMEOUT = 15
30
30
31 KM = None
31 KM = None
32 KC = None
32 KC = None
33
33
34 #-------------------------------------------------------------------------------
34 #-------------------------------------------------------------------------------
35 # code
35 # code
36 #-------------------------------------------------------------------------------
36 #-------------------------------------------------------------------------------
37
37
38
38
39 def start_new_kernel():
39 def start_new_kernel(argv=None):
40 """start a new kernel, and return its Manager and Client"""
40 """start a new kernel, and return its Manager and Client"""
41 km = KernelManager()
41 km = KernelManager()
42 km.start_kernel(stdout=PIPE, stderr=PIPE)
42 kwargs = dict(stdout=PIPE, stderr=PIPE)
43 if argv:
44 kwargs['extra_arguments'] = argv
45 km.start_kernel(**kwargs)
43 kc = km.client()
46 kc = km.client()
44 kc.start_channels()
47 kc.start_channels()
45
48
46 msg_id = kc.kernel_info()
49 msg_id = kc.kernel_info()
47 kc.get_shell_msg(block=True, timeout=STARTUP_TIMEOUT)
50 kc.get_shell_msg(block=True, timeout=STARTUP_TIMEOUT)
48 flush_channels(kc)
51 flush_channels(kc)
49 return km, kc
52 return km, kc
50
53
51 def flush_channels(kc=None):
54 def flush_channels(kc=None):
52 """flush any messages waiting on the queue"""
55 """flush any messages waiting on the queue"""
53 from .test_message_spec import validate_message
56 from .test_message_spec import validate_message
54
57
55 if kc is None:
58 if kc is None:
56 kc = KC
59 kc = KC
57 for channel in (kc.shell_channel, kc.iopub_channel):
60 for channel in (kc.shell_channel, kc.iopub_channel):
58 while True:
61 while True:
59 try:
62 try:
60 msg = channel.get_msg(block=True, timeout=0.1)
63 msg = channel.get_msg(block=True, timeout=0.1)
61 except Empty:
64 except Empty:
62 break
65 break
63 else:
66 else:
64 validate_message(msg)
67 validate_message(msg)
65
68
66
69
67 def execute(code='', kc=None, **kwargs):
70 def execute(code='', kc=None, **kwargs):
68 """wrapper for doing common steps for validating an execution request"""
71 """wrapper for doing common steps for validating an execution request"""
69 from .test_message_spec import validate_message
72 from .test_message_spec import validate_message
70 if kc is None:
73 if kc is None:
71 kc = KC
74 kc = KC
72 msg_id = kc.execute(code=code, **kwargs)
75 msg_id = kc.execute(code=code, **kwargs)
73 reply = kc.get_shell_msg(timeout=TIMEOUT)
76 reply = kc.get_shell_msg(timeout=TIMEOUT)
74 validate_message(reply, 'execute_reply', msg_id)
77 validate_message(reply, 'execute_reply', msg_id)
75 busy = kc.get_iopub_msg(timeout=TIMEOUT)
78 busy = kc.get_iopub_msg(timeout=TIMEOUT)
76 validate_message(busy, 'status', msg_id)
79 validate_message(busy, 'status', msg_id)
77 nt.assert_equal(busy['content']['execution_state'], 'busy')
80 nt.assert_equal(busy['content']['execution_state'], 'busy')
78
81
79 if not kwargs.get('silent'):
82 if not kwargs.get('silent'):
80 pyin = kc.get_iopub_msg(timeout=TIMEOUT)
83 pyin = kc.get_iopub_msg(timeout=TIMEOUT)
81 validate_message(pyin, 'pyin', msg_id)
84 validate_message(pyin, 'pyin', msg_id)
82 nt.assert_equal(pyin['content']['code'], code)
85 nt.assert_equal(pyin['content']['code'], code)
83
86
84 return msg_id, reply['content']
87 return msg_id, reply['content']
85
88
86 def start_global_kernel():
89 def start_global_kernel():
87 """start the global kernel (if it isn't running) and return its client"""
90 """start the global kernel (if it isn't running) and return its client"""
88 global KM, KC
91 global KM, KC
89 if KM is None:
92 if KM is None:
90 KM, KC = start_new_kernel()
93 KM, KC = start_new_kernel()
91 atexit.register(stop_global_kernel)
94 atexit.register(stop_global_kernel)
92 return KC
95 return KC
93
96
94 @contextmanager
97 @contextmanager
95 def kernel():
98 def kernel():
96 """Context manager for the global kernel instance
99 """Context manager for the global kernel instance
97
100
98 Should be used for most kernel tests
101 Should be used for most kernel tests
99
102
100 Returns
103 Returns
101 -------
104 -------
102 kernel_client: connected KernelClient instance
105 kernel_client: connected KernelClient instance
103 """
106 """
104 yield start_global_kernel()
107 yield start_global_kernel()
105
108
106 def uses_kernel(test_f):
109 def uses_kernel(test_f):
107 """Decorator for tests that use the global kernel"""
110 """Decorator for tests that use the global kernel"""
108 def wrapped_test():
111 def wrapped_test():
109 with kernel() as kc:
112 with kernel() as kc:
110 test_f(kc)
113 test_f(kc)
111 wrapped_test.__doc__ = test_f.__doc__
114 wrapped_test.__doc__ = test_f.__doc__
112 wrapped_test.__name__ = test_f.__name__
115 wrapped_test.__name__ = test_f.__name__
113 return wrapped_test
116 return wrapped_test
114
117
115 def stop_global_kernel():
118 def stop_global_kernel():
116 """Stop the global shared kernel instance, if it exists"""
119 """Stop the global shared kernel instance, if it exists"""
117 global KM, KC
120 global KM, KC
118 KC.stop_channels()
121 KC.stop_channels()
119 KC = None
122 KC = None
120 if KM is None:
123 if KM is None:
121 return
124 return
122 KM.shutdown_kernel(now=True)
125 KM.shutdown_kernel(now=True)
123 KM = None
126 KM = None
124
127
125 @contextmanager
128 @contextmanager
126 def new_kernel():
129 def new_kernel(argv=None):
127 """Context manager for a new kernel in a subprocess
130 """Context manager for a new kernel in a subprocess
128
131
129 Should only be used for tests where the kernel must not be re-used.
132 Should only be used for tests where the kernel must not be re-used.
130
133
131 Returns
134 Returns
132 -------
135 -------
133 kernel_client: connected KernelClient instance
136 kernel_client: connected KernelClient instance
134 """
137 """
135 km, kc = start_new_kernel()
138 km, kc = start_new_kernel(argv)
136 try:
139 try:
137 yield kc
140 yield kc
138 finally:
141 finally:
139 kc.stop_channels()
142 kc.stop_channels()
140 km.shutdown_kernel(now=True)
143 km.shutdown_kernel(now=True)
141
144
142
145
143 def assemble_output(iopub):
146 def assemble_output(iopub):
144 """assemble stdout/err from an execution"""
147 """assemble stdout/err from an execution"""
145 stdout = ''
148 stdout = ''
146 stderr = ''
149 stderr = ''
147 while True:
150 while True:
148 msg = iopub.get_msg(block=True, timeout=1)
151 msg = iopub.get_msg(block=True, timeout=1)
149 msg_type = msg['msg_type']
152 msg_type = msg['msg_type']
150 content = msg['content']
153 content = msg['content']
151 if msg_type == 'status' and content['execution_state'] == 'idle':
154 if msg_type == 'status' and content['execution_state'] == 'idle':
152 # idle message signals end of output
155 # idle message signals end of output
153 break
156 break
154 elif msg['msg_type'] == 'stream':
157 elif msg['msg_type'] == 'stream':
155 if content['name'] == 'stdout':
158 if content['name'] == 'stdout':
156 stdout += content['data']
159 stdout += content['data']
157 elif content['name'] == 'stderr':
160 elif content['name'] == 'stderr':
158 stderr += content['data']
161 stderr += content['data']
159 else:
162 else:
160 raise KeyError("bad stream: %r" % content['name'])
163 raise KeyError("bad stream: %r" % content['name'])
161 else:
164 else:
162 # other output, ignored
165 # other output, ignored
163 pass
166 pass
164 return stdout, stderr
167 return stdout, stderr
165
168
166
169
167
170
General Comments 0
You need to be logged in to leave comments. Login now