##// END OF EJS Templates
Merge branch 'master' into htmlnotebook
Brian E. Granger -
r4546:cc6010e3 merge
parent child Browse files
Show More
@@ -1,450 +1,466 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Utilities for path handling.
3 Utilities for path handling.
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 import os
17 import os
18 import sys
18 import sys
19 import tempfile
19 from hashlib import md5
20 from hashlib import md5
20
21
21 import IPython
22 import IPython
22 from IPython.utils import warn
23 from IPython.utils import warn
23 from IPython.utils.process import system
24 from IPython.utils.process import system
24 from IPython.utils.importstring import import_item
25 from IPython.utils.importstring import import_item
25
26
26 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
27 # Code
28 # Code
28 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
29
30
30 fs_encoding = sys.getfilesystemencoding()
31 fs_encoding = sys.getfilesystemencoding()
31
32
32 def _cast_unicode(s, enc=None):
33 def _cast_unicode(s, enc=None):
33 """Turn 8-bit strings into unicode."""
34 """Turn 8-bit strings into unicode."""
34 if isinstance(s, bytes):
35 if isinstance(s, bytes):
35 enc = enc or sys.getdefaultencoding()
36 enc = enc or sys.getdefaultencoding()
36 return s.decode(enc)
37 return s.decode(enc)
37 return s
38 return s
38
39
39
40
40 def _get_long_path_name(path):
41 def _get_long_path_name(path):
41 """Dummy no-op."""
42 """Dummy no-op."""
42 return path
43 return path
43
44
45 def _writable_dir(path):
46 """Whether `path` is a directory, to which the user has write access."""
47 return os.path.isdir(path) and os.access(path, os.W_OK)
48
44 if sys.platform == 'win32':
49 if sys.platform == 'win32':
45 def _get_long_path_name(path):
50 def _get_long_path_name(path):
46 """Get a long path name (expand ~) on Windows using ctypes.
51 """Get a long path name (expand ~) on Windows using ctypes.
47
52
48 Examples
53 Examples
49 --------
54 --------
50
55
51 >>> get_long_path_name('c:\\docume~1')
56 >>> get_long_path_name('c:\\docume~1')
52 u'c:\\\\Documents and Settings'
57 u'c:\\\\Documents and Settings'
53
58
54 """
59 """
55 try:
60 try:
56 import ctypes
61 import ctypes
57 except ImportError:
62 except ImportError:
58 raise ImportError('you need to have ctypes installed for this to work')
63 raise ImportError('you need to have ctypes installed for this to work')
59 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
64 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
60 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
65 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
61 ctypes.c_uint ]
66 ctypes.c_uint ]
62
67
63 buf = ctypes.create_unicode_buffer(260)
68 buf = ctypes.create_unicode_buffer(260)
64 rv = _GetLongPathName(path, buf, 260)
69 rv = _GetLongPathName(path, buf, 260)
65 if rv == 0 or rv > 260:
70 if rv == 0 or rv > 260:
66 return path
71 return path
67 else:
72 else:
68 return buf.value
73 return buf.value
69
74
70
75
71 def get_long_path_name(path):
76 def get_long_path_name(path):
72 """Expand a path into its long form.
77 """Expand a path into its long form.
73
78
74 On Windows this expands any ~ in the paths. On other platforms, it is
79 On Windows this expands any ~ in the paths. On other platforms, it is
75 a null operation.
80 a null operation.
76 """
81 """
77 return _get_long_path_name(path)
82 return _get_long_path_name(path)
78
83
79
84
80 def get_py_filename(name):
85 def get_py_filename(name):
81 """Return a valid python filename in the current directory.
86 """Return a valid python filename in the current directory.
82
87
83 If the given name is not a file, it adds '.py' and searches again.
88 If the given name is not a file, it adds '.py' and searches again.
84 Raises IOError with an informative message if the file isn't found."""
89 Raises IOError with an informative message if the file isn't found."""
85
90
86 name = os.path.expanduser(name)
91 name = os.path.expanduser(name)
87 if not os.path.isfile(name) and not name.endswith('.py'):
92 if not os.path.isfile(name) and not name.endswith('.py'):
88 name += '.py'
93 name += '.py'
89 if os.path.isfile(name):
94 if os.path.isfile(name):
90 return name
95 return name
91 else:
96 else:
92 raise IOError,'File `%s` not found.' % name
97 raise IOError,'File `%s` not found.' % name
93
98
94
99
95 def filefind(filename, path_dirs=None):
100 def filefind(filename, path_dirs=None):
96 """Find a file by looking through a sequence of paths.
101 """Find a file by looking through a sequence of paths.
97
102
98 This iterates through a sequence of paths looking for a file and returns
103 This iterates through a sequence of paths looking for a file and returns
99 the full, absolute path of the first occurence of the file. If no set of
104 the full, absolute path of the first occurence of the file. If no set of
100 path dirs is given, the filename is tested as is, after running through
105 path dirs is given, the filename is tested as is, after running through
101 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
106 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
102
107
103 filefind('myfile.txt')
108 filefind('myfile.txt')
104
109
105 will find the file in the current working dir, but::
110 will find the file in the current working dir, but::
106
111
107 filefind('~/myfile.txt')
112 filefind('~/myfile.txt')
108
113
109 Will find the file in the users home directory. This function does not
114 Will find the file in the users home directory. This function does not
110 automatically try any paths, such as the cwd or the user's home directory.
115 automatically try any paths, such as the cwd or the user's home directory.
111
116
112 Parameters
117 Parameters
113 ----------
118 ----------
114 filename : str
119 filename : str
115 The filename to look for.
120 The filename to look for.
116 path_dirs : str, None or sequence of str
121 path_dirs : str, None or sequence of str
117 The sequence of paths to look for the file in. If None, the filename
122 The sequence of paths to look for the file in. If None, the filename
118 need to be absolute or be in the cwd. If a string, the string is
123 need to be absolute or be in the cwd. If a string, the string is
119 put into a sequence and the searched. If a sequence, walk through
124 put into a sequence and the searched. If a sequence, walk through
120 each element and join with ``filename``, calling :func:`expandvars`
125 each element and join with ``filename``, calling :func:`expandvars`
121 and :func:`expanduser` before testing for existence.
126 and :func:`expanduser` before testing for existence.
122
127
123 Returns
128 Returns
124 -------
129 -------
125 Raises :exc:`IOError` or returns absolute path to file.
130 Raises :exc:`IOError` or returns absolute path to file.
126 """
131 """
127
132
128 # If paths are quoted, abspath gets confused, strip them...
133 # If paths are quoted, abspath gets confused, strip them...
129 filename = filename.strip('"').strip("'")
134 filename = filename.strip('"').strip("'")
130 # If the input is an absolute path, just check it exists
135 # If the input is an absolute path, just check it exists
131 if os.path.isabs(filename) and os.path.isfile(filename):
136 if os.path.isabs(filename) and os.path.isfile(filename):
132 return filename
137 return filename
133
138
134 if path_dirs is None:
139 if path_dirs is None:
135 path_dirs = ("",)
140 path_dirs = ("",)
136 elif isinstance(path_dirs, basestring):
141 elif isinstance(path_dirs, basestring):
137 path_dirs = (path_dirs,)
142 path_dirs = (path_dirs,)
138
143
139 for path in path_dirs:
144 for path in path_dirs:
140 if path == '.': path = os.getcwdu()
145 if path == '.': path = os.getcwdu()
141 testname = expand_path(os.path.join(path, filename))
146 testname = expand_path(os.path.join(path, filename))
142 if os.path.isfile(testname):
147 if os.path.isfile(testname):
143 return os.path.abspath(testname)
148 return os.path.abspath(testname)
144
149
145 raise IOError("File %r does not exist in any of the search paths: %r" %
150 raise IOError("File %r does not exist in any of the search paths: %r" %
146 (filename, path_dirs) )
151 (filename, path_dirs) )
147
152
148
153
149 class HomeDirError(Exception):
154 class HomeDirError(Exception):
150 pass
155 pass
151
156
152
157
153 def get_home_dir():
158 def get_home_dir():
154 """Return the closest possible equivalent to a 'home' directory.
159 """Return the closest possible equivalent to a 'home' directory.
155
160
156 * On POSIX, we try $HOME.
161 * On POSIX, we try $HOME.
157 * On Windows we try:
162 * On Windows we try:
158 - %HOMESHARE%
163 - %HOMESHARE%
159 - %HOMEDRIVE\%HOMEPATH%
164 - %HOMEDRIVE\%HOMEPATH%
160 - %USERPROFILE%
165 - %USERPROFILE%
161 - Registry hack for My Documents
166 - Registry hack for My Documents
162 - %HOME%: rare, but some people with unix-like setups may have defined it
167 - %HOME%: rare, but some people with unix-like setups may have defined it
163 * On Dos C:\
168 * On Dos C:\
164
169
165 Currently only Posix and NT are implemented, a HomeDirError exception is
170 Currently only Posix and NT are implemented, a HomeDirError exception is
166 raised for all other OSes.
171 raised for all other OSes.
167 """
172 """
168
173
169 isdir = os.path.isdir
170 env = os.environ
174 env = os.environ
171
175
172 # first, check py2exe distribution root directory for _ipython.
176 # first, check py2exe distribution root directory for _ipython.
173 # This overrides all. Normally does not exist.
177 # This overrides all. Normally does not exist.
174
178
175 if hasattr(sys, "frozen"): #Is frozen by py2exe
179 if hasattr(sys, "frozen"): #Is frozen by py2exe
176 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
180 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
177 root, rest = IPython.__file__.lower().split('library.zip')
181 root, rest = IPython.__file__.lower().split('library.zip')
178 else:
182 else:
179 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
183 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
180 root=os.path.abspath(root).rstrip('\\')
184 root=os.path.abspath(root).rstrip('\\')
181 if isdir(os.path.join(root, '_ipython')):
185 if _writable_dir(os.path.join(root, '_ipython')):
182 os.environ["IPYKITROOT"] = root
186 os.environ["IPYKITROOT"] = root
183 return _cast_unicode(root, fs_encoding)
187 return _cast_unicode(root, fs_encoding)
184
188
185 if os.name == 'posix':
189 if os.name == 'posix':
186 # Linux, Unix, AIX, OS X
190 # Linux, Unix, AIX, OS X
187 try:
191 try:
188 homedir = env['HOME']
192 homedir = env['HOME']
189 except KeyError:
193 except KeyError:
190 # Last-ditch attempt at finding a suitable $HOME, on systems where
194 # Last-ditch attempt at finding a suitable $HOME, on systems where
191 # it may not be defined in the environment but the system shell
195 # it may not be defined in the environment but the system shell
192 # still knows it - reported once as:
196 # still knows it - reported once as:
193 # https://github.com/ipython/ipython/issues/154
197 # https://github.com/ipython/ipython/issues/154
194 from subprocess import Popen, PIPE
198 from subprocess import Popen, PIPE
195 homedir = Popen('echo $HOME', shell=True,
199 homedir = Popen('echo $HOME', shell=True,
196 stdout=PIPE).communicate()[0].strip()
200 stdout=PIPE).communicate()[0].strip()
197 if homedir:
201 if homedir:
198 return _cast_unicode(homedir, fs_encoding)
202 return _cast_unicode(homedir, fs_encoding)
199 else:
203 else:
200 raise HomeDirError('Undefined $HOME, IPython cannot proceed.')
204 raise HomeDirError('Undefined $HOME, IPython cannot proceed.')
201 else:
205 else:
202 return _cast_unicode(homedir, fs_encoding)
206 return _cast_unicode(homedir, fs_encoding)
203 elif os.name == 'nt':
207 elif os.name == 'nt':
204 # Now for win9x, XP, Vista, 7?
208 # Now for win9x, XP, Vista, 7?
205 # For some strange reason all of these return 'nt' for os.name.
209 # For some strange reason all of these return 'nt' for os.name.
206 # First look for a network home directory. This will return the UNC
210 # First look for a network home directory. This will return the UNC
207 # path (\\server\\Users\%username%) not the mapped path (Z:\). This
211 # path (\\server\\Users\%username%) not the mapped path (Z:\). This
208 # is needed when running IPython on cluster where all paths have to
212 # is needed when running IPython on cluster where all paths have to
209 # be UNC.
213 # be UNC.
210 try:
214 try:
211 homedir = env['HOMESHARE']
215 homedir = env['HOMESHARE']
212 except KeyError:
216 except KeyError:
213 pass
217 pass
214 else:
218 else:
215 if isdir(homedir):
219 if _writable_dir(homedir):
216 return _cast_unicode(homedir, fs_encoding)
220 return _cast_unicode(homedir, fs_encoding)
217
221
218 # Now look for a local home directory
222 # Now look for a local home directory
219 try:
223 try:
220 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
224 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
221 except KeyError:
225 except KeyError:
222 pass
226 pass
223 else:
227 else:
224 if isdir(homedir):
228 if _writable_dir(homedir):
225 return _cast_unicode(homedir, fs_encoding)
229 return _cast_unicode(homedir, fs_encoding)
226
230
227 # Now the users profile directory
231 # Now the users profile directory
228 try:
232 try:
229 homedir = os.path.join(env['USERPROFILE'])
233 homedir = os.path.join(env['USERPROFILE'])
230 except KeyError:
234 except KeyError:
231 pass
235 pass
232 else:
236 else:
233 if isdir(homedir):
237 if _writable_dir(homedir):
234 return _cast_unicode(homedir, fs_encoding)
238 return _cast_unicode(homedir, fs_encoding)
235
239
236 # Use the registry to get the 'My Documents' folder.
240 # Use the registry to get the 'My Documents' folder.
237 try:
241 try:
238 import _winreg as wreg
242 import _winreg as wreg
239 key = wreg.OpenKey(
243 key = wreg.OpenKey(
240 wreg.HKEY_CURRENT_USER,
244 wreg.HKEY_CURRENT_USER,
241 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
245 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
242 )
246 )
243 homedir = wreg.QueryValueEx(key,'Personal')[0]
247 homedir = wreg.QueryValueEx(key,'Personal')[0]
244 key.Close()
248 key.Close()
245 except:
249 except:
246 pass
250 pass
247 else:
251 else:
248 if isdir(homedir):
252 if _writable_dir(homedir):
249 return _cast_unicode(homedir, fs_encoding)
253 return _cast_unicode(homedir, fs_encoding)
250
254
251 # A user with a lot of unix tools in win32 may have defined $HOME.
255 # A user with a lot of unix tools in win32 may have defined $HOME.
252 # Try this as a last ditch option.
256 # Try this as a last ditch option.
253 try:
257 try:
254 homedir = env['HOME']
258 homedir = env['HOME']
255 except KeyError:
259 except KeyError:
256 pass
260 pass
257 else:
261 else:
258 if isdir(homedir):
262 if _writable_dir(homedir):
259 return _cast_unicode(homedir, fs_encoding)
263 return _cast_unicode(homedir, fs_encoding)
260
264
261 # If all else fails, raise HomeDirError
265 # If all else fails, raise HomeDirError
262 raise HomeDirError('No valid home directory could be found')
266 raise HomeDirError('No valid home directory could be found')
263 elif os.name == 'dos':
267 elif os.name == 'dos':
264 # Desperate, may do absurd things in classic MacOS. May work under DOS.
268 # Desperate, may do absurd things in classic MacOS. May work under DOS.
265 return u'C:\\'
269 return u'C:\\'
266 else:
270 else:
267 raise HomeDirError('No valid home directory could be found for your OS')
271 raise HomeDirError('No valid home directory could be found for your OS')
268
272
269 def get_xdg_dir():
273 def get_xdg_dir():
270 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
274 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
271
275
272 This is only for posix (Linux,Unix,OS X, etc) systems.
276 This is only for posix (Linux,Unix,OS X, etc) systems.
273 """
277 """
274
278
275 isdir = os.path.isdir
276 env = os.environ
279 env = os.environ
277
280
278 if os.name == 'posix':
281 if os.name == 'posix':
279 # Linux, Unix, AIX, OS X
282 # Linux, Unix, AIX, OS X
280 # use ~/.config if not set OR empty
283 # use ~/.config if not set OR empty
281 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
284 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
282 if xdg and isdir(xdg):
285 if xdg and _writable_dir(xdg):
283 return _cast_unicode(xdg, fs_encoding)
286 return _cast_unicode(xdg, fs_encoding)
284
287
285 return None
288 return None
286
289
287
290
288 def get_ipython_dir():
291 def get_ipython_dir():
289 """Get the IPython directory for this platform and user.
292 """Get the IPython directory for this platform and user.
290
293
291 This uses the logic in `get_home_dir` to find the home directory
294 This uses the logic in `get_home_dir` to find the home directory
292 and the adds .ipython to the end of the path.
295 and the adds .ipython to the end of the path.
293 """
296 """
294
297
295 env = os.environ
298 env = os.environ
296 pjoin = os.path.join
299 pjoin = os.path.join
297 exists = os.path.exists
300
298
301
299 ipdir_def = '.ipython'
302 ipdir_def = '.ipython'
300 xdg_def = 'ipython'
303 xdg_def = 'ipython'
301
304
302 home_dir = get_home_dir()
305 home_dir = get_home_dir()
303 xdg_dir = get_xdg_dir()
306 xdg_dir = get_xdg_dir()
304 # import pdb; pdb.set_trace() # dbg
307 # import pdb; pdb.set_trace() # dbg
305 ipdir = env.get('IPYTHON_DIR', env.get('IPYTHONDIR', None))
308 ipdir = env.get('IPYTHON_DIR', env.get('IPYTHONDIR', None))
306 if ipdir is None:
309 if ipdir is None:
307 # not set explicitly, use XDG_CONFIG_HOME or HOME
310 # not set explicitly, use XDG_CONFIG_HOME or HOME
308 home_ipdir = pjoin(home_dir, ipdir_def)
311 home_ipdir = pjoin(home_dir, ipdir_def)
309 if xdg_dir:
312 if xdg_dir:
310 # use XDG, as long as the user isn't already
313 # use XDG, as long as the user isn't already
311 # using $HOME/.ipython and *not* XDG/ipython
314 # using $HOME/.ipython and *not* XDG/ipython
312
315
313 xdg_ipdir = pjoin(xdg_dir, xdg_def)
316 xdg_ipdir = pjoin(xdg_dir, xdg_def)
314
317
315 if exists(xdg_ipdir) or not exists(home_ipdir):
318 if _writable_dir(xdg_ipdir) or not _writable_dir(home_ipdir):
316 ipdir = xdg_ipdir
319 ipdir = xdg_ipdir
317
320
318 if ipdir is None:
321 if ipdir is None:
319 # not using XDG
322 # not using XDG
320 ipdir = home_ipdir
323 ipdir = home_ipdir
321
324
322 ipdir = os.path.normpath(os.path.expanduser(ipdir))
325 ipdir = os.path.normpath(os.path.expanduser(ipdir))
326
327 if os.path.exists(ipdir) and not _writable_dir(ipdir):
328 # ipdir exists, but is not writable
329 warn.warn("IPython dir '%s' is not a writable location,"
330 " using a temp directory."%ipdir)
331 ipdir = tempfile.mkdtemp()
332 elif not os.path.exists(ipdir):
333 parent = ipdir.rsplit(os.path.sep, 1)[0]
334 if not _writable_dir(parent):
335 # ipdir does not exist and parent isn't writable
336 warn.warn("IPython parent '%s' is not a writable location,"
337 " using a temp directory."%parent)
338 ipdir = tempfile.mkdtemp()
323
339
324 return _cast_unicode(ipdir, fs_encoding)
340 return _cast_unicode(ipdir, fs_encoding)
325
341
326
342
327 def get_ipython_package_dir():
343 def get_ipython_package_dir():
328 """Get the base directory where IPython itself is installed."""
344 """Get the base directory where IPython itself is installed."""
329 ipdir = os.path.dirname(IPython.__file__)
345 ipdir = os.path.dirname(IPython.__file__)
330 return _cast_unicode(ipdir, fs_encoding)
346 return _cast_unicode(ipdir, fs_encoding)
331
347
332
348
333 def get_ipython_module_path(module_str):
349 def get_ipython_module_path(module_str):
334 """Find the path to an IPython module in this version of IPython.
350 """Find the path to an IPython module in this version of IPython.
335
351
336 This will always find the version of the module that is in this importable
352 This will always find the version of the module that is in this importable
337 IPython package. This will always return the path to the ``.py``
353 IPython package. This will always return the path to the ``.py``
338 version of the module.
354 version of the module.
339 """
355 """
340 if module_str == 'IPython':
356 if module_str == 'IPython':
341 return os.path.join(get_ipython_package_dir(), '__init__.py')
357 return os.path.join(get_ipython_package_dir(), '__init__.py')
342 mod = import_item(module_str)
358 mod = import_item(module_str)
343 the_path = mod.__file__.replace('.pyc', '.py')
359 the_path = mod.__file__.replace('.pyc', '.py')
344 the_path = the_path.replace('.pyo', '.py')
360 the_path = the_path.replace('.pyo', '.py')
345 return _cast_unicode(the_path, fs_encoding)
361 return _cast_unicode(the_path, fs_encoding)
346
362
347
363
348 def expand_path(s):
364 def expand_path(s):
349 """Expand $VARS and ~names in a string, like a shell
365 """Expand $VARS and ~names in a string, like a shell
350
366
351 :Examples:
367 :Examples:
352
368
353 In [2]: os.environ['FOO']='test'
369 In [2]: os.environ['FOO']='test'
354
370
355 In [3]: expand_path('variable FOO is $FOO')
371 In [3]: expand_path('variable FOO is $FOO')
356 Out[3]: 'variable FOO is test'
372 Out[3]: 'variable FOO is test'
357 """
373 """
358 # This is a pretty subtle hack. When expand user is given a UNC path
374 # This is a pretty subtle hack. When expand user is given a UNC path
359 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
375 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
360 # the $ to get (\\server\share\%username%). I think it considered $
376 # the $ to get (\\server\share\%username%). I think it considered $
361 # alone an empty var. But, we need the $ to remains there (it indicates
377 # alone an empty var. But, we need the $ to remains there (it indicates
362 # a hidden share).
378 # a hidden share).
363 if os.name=='nt':
379 if os.name=='nt':
364 s = s.replace('$\\', 'IPYTHON_TEMP')
380 s = s.replace('$\\', 'IPYTHON_TEMP')
365 s = os.path.expandvars(os.path.expanduser(s))
381 s = os.path.expandvars(os.path.expanduser(s))
366 if os.name=='nt':
382 if os.name=='nt':
367 s = s.replace('IPYTHON_TEMP', '$\\')
383 s = s.replace('IPYTHON_TEMP', '$\\')
368 return s
384 return s
369
385
370
386
371 def target_outdated(target,deps):
387 def target_outdated(target,deps):
372 """Determine whether a target is out of date.
388 """Determine whether a target is out of date.
373
389
374 target_outdated(target,deps) -> 1/0
390 target_outdated(target,deps) -> 1/0
375
391
376 deps: list of filenames which MUST exist.
392 deps: list of filenames which MUST exist.
377 target: single filename which may or may not exist.
393 target: single filename which may or may not exist.
378
394
379 If target doesn't exist or is older than any file listed in deps, return
395 If target doesn't exist or is older than any file listed in deps, return
380 true, otherwise return false.
396 true, otherwise return false.
381 """
397 """
382 try:
398 try:
383 target_time = os.path.getmtime(target)
399 target_time = os.path.getmtime(target)
384 except os.error:
400 except os.error:
385 return 1
401 return 1
386 for dep in deps:
402 for dep in deps:
387 dep_time = os.path.getmtime(dep)
403 dep_time = os.path.getmtime(dep)
388 if dep_time > target_time:
404 if dep_time > target_time:
389 #print "For target",target,"Dep failed:",dep # dbg
405 #print "For target",target,"Dep failed:",dep # dbg
390 #print "times (dep,tar):",dep_time,target_time # dbg
406 #print "times (dep,tar):",dep_time,target_time # dbg
391 return 1
407 return 1
392 return 0
408 return 0
393
409
394
410
395 def target_update(target,deps,cmd):
411 def target_update(target,deps,cmd):
396 """Update a target with a given command given a list of dependencies.
412 """Update a target with a given command given a list of dependencies.
397
413
398 target_update(target,deps,cmd) -> runs cmd if target is outdated.
414 target_update(target,deps,cmd) -> runs cmd if target is outdated.
399
415
400 This is just a wrapper around target_outdated() which calls the given
416 This is just a wrapper around target_outdated() which calls the given
401 command if target is outdated."""
417 command if target is outdated."""
402
418
403 if target_outdated(target,deps):
419 if target_outdated(target,deps):
404 system(cmd)
420 system(cmd)
405
421
406 def filehash(path):
422 def filehash(path):
407 """Make an MD5 hash of a file, ignoring any differences in line
423 """Make an MD5 hash of a file, ignoring any differences in line
408 ending characters."""
424 ending characters."""
409 with open(path, "rU") as f:
425 with open(path, "rU") as f:
410 return md5(f.read()).hexdigest()
426 return md5(f.read()).hexdigest()
411
427
412 # If the config is unmodified from the default, we'll just delete it.
428 # If the config is unmodified from the default, we'll just delete it.
413 # These are consistent for 0.10.x, thankfully. We're not going to worry about
429 # These are consistent for 0.10.x, thankfully. We're not going to worry about
414 # older versions.
430 # older versions.
415 old_config_md5 = {'ipy_user_conf.py': 'fc108bedff4b9a00f91fa0a5999140d3',
431 old_config_md5 = {'ipy_user_conf.py': 'fc108bedff4b9a00f91fa0a5999140d3',
416 'ipythonrc': '12a68954f3403eea2eec09dc8fe5a9b5'}
432 'ipythonrc': '12a68954f3403eea2eec09dc8fe5a9b5'}
417
433
418 def check_for_old_config(ipython_dir=None):
434 def check_for_old_config(ipython_dir=None):
419 """Check for old config files, and present a warning if they exist.
435 """Check for old config files, and present a warning if they exist.
420
436
421 A link to the docs of the new config is included in the message.
437 A link to the docs of the new config is included in the message.
422
438
423 This should mitigate confusion with the transition to the new
439 This should mitigate confusion with the transition to the new
424 config system in 0.11.
440 config system in 0.11.
425 """
441 """
426 if ipython_dir is None:
442 if ipython_dir is None:
427 ipython_dir = get_ipython_dir()
443 ipython_dir = get_ipython_dir()
428
444
429 old_configs = ['ipy_user_conf.py', 'ipythonrc', 'ipython_config.py']
445 old_configs = ['ipy_user_conf.py', 'ipythonrc', 'ipython_config.py']
430 warned = False
446 warned = False
431 for cfg in old_configs:
447 for cfg in old_configs:
432 f = os.path.join(ipython_dir, cfg)
448 f = os.path.join(ipython_dir, cfg)
433 if os.path.exists(f):
449 if os.path.exists(f):
434 if filehash(f) == old_config_md5.get(cfg, ''):
450 if filehash(f) == old_config_md5.get(cfg, ''):
435 os.unlink(f)
451 os.unlink(f)
436 else:
452 else:
437 warn.warn("Found old IPython config file %r (modified by user)"%f)
453 warn.warn("Found old IPython config file %r (modified by user)"%f)
438 warned = True
454 warned = True
439
455
440 if warned:
456 if warned:
441 warn.info("""
457 warn.info("""
442 The IPython configuration system has changed as of 0.11, and these files will
458 The IPython configuration system has changed as of 0.11, and these files will
443 be ignored. See http://ipython.github.com/ipython-doc/dev/config for details
459 be ignored. See http://ipython.github.com/ipython-doc/dev/config for details
444 of the new config system.
460 of the new config system.
445 To start configuring IPython, do `ipython profile create`, and edit
461 To start configuring IPython, do `ipython profile create`, and edit
446 `ipython_config.py` in <ipython_dir>/profile_default.
462 `ipython_config.py` in <ipython_dir>/profile_default.
447 If you need to leave the old config files in place for an older version of
463 If you need to leave the old config files in place for an older version of
448 IPython and want to suppress this warning message, set
464 IPython and want to suppress this warning message, set
449 `c.InteractiveShellApp.ignore_old_config=True` in the new config.""")
465 `c.InteractiveShellApp.ignore_old_config=True` in the new config.""")
450
466
@@ -1,371 +1,404 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for IPython.utils.path.py"""
2 """Tests for IPython.utils.path.py"""
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2008 The IPython Development Team
5 # Copyright (C) 2008 The IPython Development Team
6 #
6 #
7 # Distributed under the terms of the BSD License. The full license is in
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 import os
15 import os
16 import shutil
16 import shutil
17 import sys
17 import sys
18 import tempfile
18 import tempfile
19 import StringIO
19
20
20 from os.path import join, abspath, split
21 from os.path import join, abspath, split
21
22
22 import nose.tools as nt
23 import nose.tools as nt
23
24
24 from nose import with_setup
25 from nose import with_setup
25
26
26 import IPython
27 import IPython
27 from IPython.testing import decorators as dec
28 from IPython.testing import decorators as dec
28 from IPython.testing.decorators import skip_if_not_win32, skip_win32
29 from IPython.testing.decorators import skip_if_not_win32, skip_win32
29 from IPython.utils import path
30 from IPython.utils import path, io
30
31
31 # Platform-dependent imports
32 # Platform-dependent imports
32 try:
33 try:
33 import _winreg as wreg
34 import _winreg as wreg
34 except ImportError:
35 except ImportError:
35 #Fake _winreg module on none windows platforms
36 #Fake _winreg module on none windows platforms
36 import new
37 import new
37 sys.modules["_winreg"] = new.module("_winreg")
38 sys.modules["_winreg"] = new.module("_winreg")
38 import _winreg as wreg
39 import _winreg as wreg
39 #Add entries that needs to be stubbed by the testing code
40 #Add entries that needs to be stubbed by the testing code
40 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
41 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
41
42
42 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
43 # Globals
44 # Globals
44 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
45 env = os.environ
46 env = os.environ
46 TEST_FILE_PATH = split(abspath(__file__))[0]
47 TEST_FILE_PATH = split(abspath(__file__))[0]
47 TMP_TEST_DIR = tempfile.mkdtemp()
48 TMP_TEST_DIR = tempfile.mkdtemp()
48 HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")
49 HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")
49 XDG_TEST_DIR = join(HOME_TEST_DIR, "xdg_test_dir")
50 XDG_TEST_DIR = join(HOME_TEST_DIR, "xdg_test_dir")
50 IP_TEST_DIR = join(HOME_TEST_DIR,'.ipython')
51 IP_TEST_DIR = join(HOME_TEST_DIR,'.ipython')
51 #
52 #
52 # Setup/teardown functions/decorators
53 # Setup/teardown functions/decorators
53 #
54 #
54
55
55 def setup():
56 def setup():
56 """Setup testenvironment for the module:
57 """Setup testenvironment for the module:
57
58
58 - Adds dummy home dir tree
59 - Adds dummy home dir tree
59 """
60 """
60 # Do not mask exceptions here. In particular, catching WindowsError is a
61 # Do not mask exceptions here. In particular, catching WindowsError is a
61 # problem because that exception is only defined on Windows...
62 # problem because that exception is only defined on Windows...
62 os.makedirs(IP_TEST_DIR)
63 os.makedirs(IP_TEST_DIR)
63 os.makedirs(os.path.join(XDG_TEST_DIR, 'ipython'))
64 os.makedirs(os.path.join(XDG_TEST_DIR, 'ipython'))
64
65
65
66
66 def teardown():
67 def teardown():
67 """Teardown testenvironment for the module:
68 """Teardown testenvironment for the module:
68
69
69 - Remove dummy home dir tree
70 - Remove dummy home dir tree
70 """
71 """
71 # Note: we remove the parent test dir, which is the root of all test
72 # Note: we remove the parent test dir, which is the root of all test
72 # subdirs we may have created. Use shutil instead of os.removedirs, so
73 # subdirs we may have created. Use shutil instead of os.removedirs, so
73 # that non-empty directories are all recursively removed.
74 # that non-empty directories are all recursively removed.
74 shutil.rmtree(TMP_TEST_DIR)
75 shutil.rmtree(TMP_TEST_DIR)
75
76
76
77
77 def setup_environment():
78 def setup_environment():
78 """Setup testenvironment for some functions that are tested
79 """Setup testenvironment for some functions that are tested
79 in this module. In particular this functions stores attributes
80 in this module. In particular this functions stores attributes
80 and other things that we need to stub in some test functions.
81 and other things that we need to stub in some test functions.
81 This needs to be done on a function level and not module level because
82 This needs to be done on a function level and not module level because
82 each testfunction needs a pristine environment.
83 each testfunction needs a pristine environment.
83 """
84 """
84 global oldstuff, platformstuff
85 global oldstuff, platformstuff
85 oldstuff = (env.copy(), os.name, path.get_home_dir, IPython.__file__)
86 oldstuff = (env.copy(), os.name, path.get_home_dir, IPython.__file__)
86
87
87 if os.name == 'nt':
88 if os.name == 'nt':
88 platformstuff = (wreg.OpenKey, wreg.QueryValueEx,)
89 platformstuff = (wreg.OpenKey, wreg.QueryValueEx,)
89
90
90
91
91 def teardown_environment():
92 def teardown_environment():
92 """Restore things that were remebered by the setup_environment function
93 """Restore things that were remebered by the setup_environment function
93 """
94 """
94 (oldenv, os.name, path.get_home_dir, IPython.__file__,) = oldstuff
95 (oldenv, os.name, path.get_home_dir, IPython.__file__,) = oldstuff
95
96 reload(path)
97
96 for key in env.keys():
98 for key in env.keys():
97 if key not in oldenv:
99 if key not in oldenv:
98 del env[key]
100 del env[key]
99 env.update(oldenv)
101 env.update(oldenv)
100 if hasattr(sys, 'frozen'):
102 if hasattr(sys, 'frozen'):
101 del sys.frozen
103 del sys.frozen
102 if os.name == 'nt':
104 if os.name == 'nt':
103 (wreg.OpenKey, wreg.QueryValueEx,) = platformstuff
105 (wreg.OpenKey, wreg.QueryValueEx,) = platformstuff
104
106
105 # Build decorator that uses the setup_environment/setup_environment
107 # Build decorator that uses the setup_environment/setup_environment
106 with_environment = with_setup(setup_environment, teardown_environment)
108 with_environment = with_setup(setup_environment, teardown_environment)
107
109
108
110
109 @skip_if_not_win32
111 @skip_if_not_win32
110 @with_environment
112 @with_environment
111 def test_get_home_dir_1():
113 def test_get_home_dir_1():
112 """Testcase for py2exe logic, un-compressed lib
114 """Testcase for py2exe logic, un-compressed lib
113 """
115 """
114 sys.frozen = True
116 sys.frozen = True
115
117
116 #fake filename for IPython.__init__
118 #fake filename for IPython.__init__
117 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
119 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
118
120
119 home_dir = path.get_home_dir()
121 home_dir = path.get_home_dir()
120 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
122 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
121
123
122
124
123 @skip_if_not_win32
125 @skip_if_not_win32
124 @with_environment
126 @with_environment
125 def test_get_home_dir_2():
127 def test_get_home_dir_2():
126 """Testcase for py2exe logic, compressed lib
128 """Testcase for py2exe logic, compressed lib
127 """
129 """
128 sys.frozen = True
130 sys.frozen = True
129 #fake filename for IPython.__init__
131 #fake filename for IPython.__init__
130 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
132 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
131
133
132 home_dir = path.get_home_dir()
134 home_dir = path.get_home_dir()
133 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR).lower())
135 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR).lower())
134
136
135
137
136 @with_environment
138 @with_environment
137 @skip_win32
139 @skip_win32
138 def test_get_home_dir_3():
140 def test_get_home_dir_3():
139 """Testcase $HOME is set, then use its value as home directory."""
141 """Testcase $HOME is set, then use its value as home directory."""
140 env["HOME"] = HOME_TEST_DIR
142 env["HOME"] = HOME_TEST_DIR
141 home_dir = path.get_home_dir()
143 home_dir = path.get_home_dir()
142 nt.assert_equal(home_dir, env["HOME"])
144 nt.assert_equal(home_dir, env["HOME"])
143
145
144
146
145 @with_environment
147 @with_environment
146 @skip_win32
148 @skip_win32
147 def test_get_home_dir_4():
149 def test_get_home_dir_4():
148 """Testcase $HOME is not set, os=='posix'.
150 """Testcase $HOME is not set, os=='posix'.
149 This should fail with HomeDirError"""
151 This should fail with HomeDirError"""
150
152
151 os.name = 'posix'
153 os.name = 'posix'
152 if 'HOME' in env: del env['HOME']
154 if 'HOME' in env: del env['HOME']
153 nt.assert_raises(path.HomeDirError, path.get_home_dir)
155 nt.assert_raises(path.HomeDirError, path.get_home_dir)
154
156
155
157
156 @skip_if_not_win32
158 @skip_if_not_win32
157 @with_environment
159 @with_environment
158 def test_get_home_dir_5():
160 def test_get_home_dir_5():
159 """Using HOMEDRIVE + HOMEPATH, os=='nt'.
161 """Using HOMEDRIVE + HOMEPATH, os=='nt'.
160
162
161 HOMESHARE is missing.
163 HOMESHARE is missing.
162 """
164 """
163
165
164 os.name = 'nt'
166 os.name = 'nt'
165 env.pop('HOMESHARE', None)
167 env.pop('HOMESHARE', None)
166 env['HOMEDRIVE'], env['HOMEPATH'] = os.path.splitdrive(HOME_TEST_DIR)
168 env['HOMEDRIVE'], env['HOMEPATH'] = os.path.splitdrive(HOME_TEST_DIR)
167 home_dir = path.get_home_dir()
169 home_dir = path.get_home_dir()
168 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
170 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
169
171
170
172
171 @skip_if_not_win32
173 @skip_if_not_win32
172 @with_environment
174 @with_environment
173 def test_get_home_dir_6():
175 def test_get_home_dir_6():
174 """Using USERPROFILE, os=='nt'.
176 """Using USERPROFILE, os=='nt'.
175
177
176 HOMESHARE, HOMEDRIVE, HOMEPATH are missing.
178 HOMESHARE, HOMEDRIVE, HOMEPATH are missing.
177 """
179 """
178
180
179 os.name = 'nt'
181 os.name = 'nt'
180 env.pop('HOMESHARE', None)
182 env.pop('HOMESHARE', None)
181 env.pop('HOMEDRIVE', None)
183 env.pop('HOMEDRIVE', None)
182 env.pop('HOMEPATH', None)
184 env.pop('HOMEPATH', None)
183 env["USERPROFILE"] = abspath(HOME_TEST_DIR)
185 env["USERPROFILE"] = abspath(HOME_TEST_DIR)
184 home_dir = path.get_home_dir()
186 home_dir = path.get_home_dir()
185 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
187 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
186
188
187
189
188 @skip_if_not_win32
190 @skip_if_not_win32
189 @with_environment
191 @with_environment
190 def test_get_home_dir_7():
192 def test_get_home_dir_7():
191 """Using HOMESHARE, os=='nt'."""
193 """Using HOMESHARE, os=='nt'."""
192
194
193 os.name = 'nt'
195 os.name = 'nt'
194 env["HOMESHARE"] = abspath(HOME_TEST_DIR)
196 env["HOMESHARE"] = abspath(HOME_TEST_DIR)
195 home_dir = path.get_home_dir()
197 home_dir = path.get_home_dir()
196 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
198 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
197
199
198
200
199 # Should we stub wreg fully so we can run the test on all platforms?
201 # Should we stub wreg fully so we can run the test on all platforms?
200 @skip_if_not_win32
202 @skip_if_not_win32
201 @with_environment
203 @with_environment
202 def test_get_home_dir_8():
204 def test_get_home_dir_8():
203 """Using registry hack for 'My Documents', os=='nt'
205 """Using registry hack for 'My Documents', os=='nt'
204
206
205 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
207 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
206 """
208 """
207 os.name = 'nt'
209 os.name = 'nt'
208 # Remove from stub environment all keys that may be set
210 # Remove from stub environment all keys that may be set
209 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
211 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
210 env.pop(key, None)
212 env.pop(key, None)
211
213
212 #Stub windows registry functions
214 #Stub windows registry functions
213 def OpenKey(x, y):
215 def OpenKey(x, y):
214 class key:
216 class key:
215 def Close(self):
217 def Close(self):
216 pass
218 pass
217 return key()
219 return key()
218 def QueryValueEx(x, y):
220 def QueryValueEx(x, y):
219 return [abspath(HOME_TEST_DIR)]
221 return [abspath(HOME_TEST_DIR)]
220
222
221 wreg.OpenKey = OpenKey
223 wreg.OpenKey = OpenKey
222 wreg.QueryValueEx = QueryValueEx
224 wreg.QueryValueEx = QueryValueEx
223
225
224 home_dir = path.get_home_dir()
226 home_dir = path.get_home_dir()
225 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
227 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
226
228
227
229
228 @with_environment
230 @with_environment
229 def test_get_ipython_dir_1():
231 def test_get_ipython_dir_1():
230 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
232 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
231 env_ipdir = os.path.join("someplace", ".ipython")
233 env_ipdir = os.path.join("someplace", ".ipython")
234 path._writable_dir = lambda path: True
232 env['IPYTHON_DIR'] = env_ipdir
235 env['IPYTHON_DIR'] = env_ipdir
233 ipdir = path.get_ipython_dir()
236 ipdir = path.get_ipython_dir()
234 nt.assert_equal(ipdir, env_ipdir)
237 nt.assert_equal(ipdir, env_ipdir)
235
238
236
239
237 @with_environment
240 @with_environment
238 def test_get_ipython_dir_2():
241 def test_get_ipython_dir_2():
239 """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
242 """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
240 path.get_home_dir = lambda : "someplace"
243 path.get_home_dir = lambda : "someplace"
244 path.get_xdg_dir = lambda : None
245 path._writable_dir = lambda path: True
241 os.name = "posix"
246 os.name = "posix"
242 env.pop('IPYTHON_DIR', None)
247 env.pop('IPYTHON_DIR', None)
243 env.pop('IPYTHONDIR', None)
248 env.pop('IPYTHONDIR', None)
244 env.pop('XDG_CONFIG_HOME', None)
249 env.pop('XDG_CONFIG_HOME', None)
245 ipdir = path.get_ipython_dir()
250 ipdir = path.get_ipython_dir()
246 nt.assert_equal(ipdir, os.path.join("someplace", ".ipython"))
251 nt.assert_equal(ipdir, os.path.join("someplace", ".ipython"))
247
252
248 @with_environment
253 @with_environment
249 def test_get_ipython_dir_3():
254 def test_get_ipython_dir_3():
250 """test_get_ipython_dir_3, use XDG if defined, and .ipython doesn't exist."""
255 """test_get_ipython_dir_3, use XDG if defined, and .ipython doesn't exist."""
251 path.get_home_dir = lambda : "someplace"
256 path.get_home_dir = lambda : "someplace"
257 path._writable_dir = lambda path: True
252 os.name = "posix"
258 os.name = "posix"
253 env.pop('IPYTHON_DIR', None)
259 env.pop('IPYTHON_DIR', None)
254 env.pop('IPYTHONDIR', None)
260 env.pop('IPYTHONDIR', None)
255 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
261 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
256 ipdir = path.get_ipython_dir()
262 ipdir = path.get_ipython_dir()
257 nt.assert_equal(ipdir, os.path.join(XDG_TEST_DIR, "ipython"))
263 nt.assert_equal(ipdir, os.path.join(XDG_TEST_DIR, "ipython"))
258
264
259 @with_environment
265 @with_environment
260 def test_get_ipython_dir_4():
266 def test_get_ipython_dir_4():
261 """test_get_ipython_dir_4, use XDG if both exist."""
267 """test_get_ipython_dir_4, use XDG if both exist."""
262 path.get_home_dir = lambda : HOME_TEST_DIR
268 path.get_home_dir = lambda : HOME_TEST_DIR
263 os.name = "posix"
269 os.name = "posix"
264 env.pop('IPYTHON_DIR', None)
270 env.pop('IPYTHON_DIR', None)
265 env.pop('IPYTHONDIR', None)
271 env.pop('IPYTHONDIR', None)
266 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
272 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
267 xdg_ipdir = os.path.join(XDG_TEST_DIR, "ipython")
273 xdg_ipdir = os.path.join(XDG_TEST_DIR, "ipython")
268 ipdir = path.get_ipython_dir()
274 ipdir = path.get_ipython_dir()
269 nt.assert_equal(ipdir, xdg_ipdir)
275 nt.assert_equal(ipdir, xdg_ipdir)
270
276
271 @with_environment
277 @with_environment
272 def test_get_ipython_dir_5():
278 def test_get_ipython_dir_5():
273 """test_get_ipython_dir_5, use .ipython if exists and XDG defined, but doesn't exist."""
279 """test_get_ipython_dir_5, use .ipython if exists and XDG defined, but doesn't exist."""
274 path.get_home_dir = lambda : HOME_TEST_DIR
280 path.get_home_dir = lambda : HOME_TEST_DIR
275 os.name = "posix"
281 os.name = "posix"
276 env.pop('IPYTHON_DIR', None)
282 env.pop('IPYTHON_DIR', None)
277 env.pop('IPYTHONDIR', None)
283 env.pop('IPYTHONDIR', None)
278 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
284 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
279 os.rmdir(os.path.join(XDG_TEST_DIR, 'ipython'))
285 os.rmdir(os.path.join(XDG_TEST_DIR, 'ipython'))
280 ipdir = path.get_ipython_dir()
286 ipdir = path.get_ipython_dir()
281 nt.assert_equal(ipdir, IP_TEST_DIR)
287 nt.assert_equal(ipdir, IP_TEST_DIR)
282
288
283 @with_environment
289 @with_environment
284 def test_get_ipython_dir_6():
290 def test_get_ipython_dir_6():
285 """test_get_ipython_dir_6, use XDG if defined and neither exist."""
291 """test_get_ipython_dir_6, use XDG if defined and neither exist."""
286 path.get_home_dir = lambda : 'somehome'
292 xdg = os.path.join(HOME_TEST_DIR, 'somexdg')
287 path.get_xdg_dir = lambda : 'somexdg'
293 os.mkdir(xdg)
294 shutil.rmtree(os.path.join(HOME_TEST_DIR, '.ipython'))
295 path.get_home_dir = lambda : HOME_TEST_DIR
296 path.get_xdg_dir = lambda : xdg
288 os.name = "posix"
297 os.name = "posix"
289 env.pop('IPYTHON_DIR', None)
298 env.pop('IPYTHON_DIR', None)
290 env.pop('IPYTHONDIR', None)
299 env.pop('IPYTHONDIR', None)
291 xdg_ipdir = os.path.join("somexdg", "ipython")
300 env.pop('XDG_CONFIG_HOME', None)
301 xdg_ipdir = os.path.join(xdg, "ipython")
292 ipdir = path.get_ipython_dir()
302 ipdir = path.get_ipython_dir()
293 nt.assert_equal(ipdir, xdg_ipdir)
303 nt.assert_equal(ipdir, xdg_ipdir)
294
304
295 @with_environment
305 @with_environment
296 def test_get_ipython_dir_7():
306 def test_get_ipython_dir_7():
297 """test_get_ipython_dir_7, test home directory expansion on IPYTHON_DIR"""
307 """test_get_ipython_dir_7, test home directory expansion on IPYTHON_DIR"""
308 path._writable_dir = lambda path: True
298 home_dir = os.path.expanduser('~')
309 home_dir = os.path.expanduser('~')
299 env['IPYTHON_DIR'] = os.path.join('~', 'somewhere')
310 env['IPYTHON_DIR'] = os.path.join('~', 'somewhere')
300 ipdir = path.get_ipython_dir()
311 ipdir = path.get_ipython_dir()
301 nt.assert_equal(ipdir, os.path.join(home_dir, 'somewhere'))
312 nt.assert_equal(ipdir, os.path.join(home_dir, 'somewhere'))
302
313
303
314
304 @with_environment
315 @with_environment
305 def test_get_xdg_dir_1():
316 def test_get_xdg_dir_1():
306 """test_get_xdg_dir_1, check xdg_dir"""
317 """test_get_xdg_dir_1, check xdg_dir"""
307 reload(path)
318 reload(path)
319 path._writable_dir = lambda path: True
308 path.get_home_dir = lambda : 'somewhere'
320 path.get_home_dir = lambda : 'somewhere'
309 os.name = "posix"
321 os.name = "posix"
310 env.pop('IPYTHON_DIR', None)
322 env.pop('IPYTHON_DIR', None)
311 env.pop('IPYTHONDIR', None)
323 env.pop('IPYTHONDIR', None)
312 env.pop('XDG_CONFIG_HOME', None)
324 env.pop('XDG_CONFIG_HOME', None)
313
325
314 nt.assert_equal(path.get_xdg_dir(), os.path.join('somewhere', '.config'))
326 nt.assert_equal(path.get_xdg_dir(), os.path.join('somewhere', '.config'))
315
327
316
328
317 @with_environment
329 @with_environment
318 def test_get_xdg_dir_1():
330 def test_get_xdg_dir_1():
319 """test_get_xdg_dir_1, check nonexistant xdg_dir"""
331 """test_get_xdg_dir_1, check nonexistant xdg_dir"""
320 reload(path)
332 reload(path)
321 path.get_home_dir = lambda : HOME_TEST_DIR
333 path.get_home_dir = lambda : HOME_TEST_DIR
322 os.name = "posix"
334 os.name = "posix"
323 env.pop('IPYTHON_DIR', None)
335 env.pop('IPYTHON_DIR', None)
324 env.pop('IPYTHONDIR', None)
336 env.pop('IPYTHONDIR', None)
325 env.pop('XDG_CONFIG_HOME', None)
337 env.pop('XDG_CONFIG_HOME', None)
326 nt.assert_equal(path.get_xdg_dir(), None)
338 nt.assert_equal(path.get_xdg_dir(), None)
327
339
328 @with_environment
340 @with_environment
329 def test_get_xdg_dir_2():
341 def test_get_xdg_dir_2():
330 """test_get_xdg_dir_2, check xdg_dir default to ~/.config"""
342 """test_get_xdg_dir_2, check xdg_dir default to ~/.config"""
331 reload(path)
343 reload(path)
332 path.get_home_dir = lambda : HOME_TEST_DIR
344 path.get_home_dir = lambda : HOME_TEST_DIR
333 os.name = "posix"
345 os.name = "posix"
334 env.pop('IPYTHON_DIR', None)
346 env.pop('IPYTHON_DIR', None)
335 env.pop('IPYTHONDIR', None)
347 env.pop('IPYTHONDIR', None)
336 env.pop('XDG_CONFIG_HOME', None)
348 env.pop('XDG_CONFIG_HOME', None)
337 cfgdir=os.path.join(path.get_home_dir(), '.config')
349 cfgdir=os.path.join(path.get_home_dir(), '.config')
338 os.makedirs(cfgdir)
350 os.makedirs(cfgdir)
339
351
340 nt.assert_equal(path.get_xdg_dir(), cfgdir)
352 nt.assert_equal(path.get_xdg_dir(), cfgdir)
341
353
342 def test_filefind():
354 def test_filefind():
343 """Various tests for filefind"""
355 """Various tests for filefind"""
344 f = tempfile.NamedTemporaryFile()
356 f = tempfile.NamedTemporaryFile()
345 # print 'fname:',f.name
357 # print 'fname:',f.name
346 alt_dirs = path.get_ipython_dir()
358 alt_dirs = path.get_ipython_dir()
347 t = path.filefind(f.name, alt_dirs)
359 t = path.filefind(f.name, alt_dirs)
348 # print 'found:',t
360 # print 'found:',t
349
361
350
362
351 def test_get_ipython_package_dir():
363 def test_get_ipython_package_dir():
352 ipdir = path.get_ipython_package_dir()
364 ipdir = path.get_ipython_package_dir()
353 nt.assert_true(os.path.isdir(ipdir))
365 nt.assert_true(os.path.isdir(ipdir))
354
366
355
367
356 def test_get_ipython_module_path():
368 def test_get_ipython_module_path():
357 ipapp_path = path.get_ipython_module_path('IPython.frontend.terminal.ipapp')
369 ipapp_path = path.get_ipython_module_path('IPython.frontend.terminal.ipapp')
358 nt.assert_true(os.path.isfile(ipapp_path))
370 nt.assert_true(os.path.isfile(ipapp_path))
359
371
360
372
361 @dec.skip_if_not_win32
373 @dec.skip_if_not_win32
362 def test_get_long_path_name_win32():
374 def test_get_long_path_name_win32():
363 p = path.get_long_path_name('c:\\docume~1')
375 p = path.get_long_path_name('c:\\docume~1')
364 nt.assert_equals(p,u'c:\\Documents and Settings')
376 nt.assert_equals(p,u'c:\\Documents and Settings')
365
377
366
378
367 @dec.skip_win32
379 @dec.skip_win32
368 def test_get_long_path_name():
380 def test_get_long_path_name():
369 p = path.get_long_path_name('/usr/local')
381 p = path.get_long_path_name('/usr/local')
370 nt.assert_equals(p,'/usr/local')
382 nt.assert_equals(p,'/usr/local')
371
383
384 @dec.skip_win32 # can't create not-user-writable dir on win
385 @with_environment
386 def test_not_writable_ipdir():
387 tmpdir = tempfile.mkdtemp()
388 os.name = "posix"
389 env.pop('IPYTHON_DIR', None)
390 env.pop('IPYTHONDIR', None)
391 env.pop('XDG_CONFIG_HOME', None)
392 env['HOME'] = tmpdir
393 ipdir = os.path.join(tmpdir, '.ipython')
394 os.mkdir(ipdir)
395 os.chmod(ipdir, 600)
396 stderr = io.stderr
397 pipe = StringIO.StringIO()
398 io.stderr = pipe
399 ipdir = path.get_ipython_dir()
400 io.stderr.flush()
401 io.stderr = stderr
402 nt.assert_true('WARNING' in pipe.getvalue())
403 env.pop('IPYTHON_DIR', None)
404 No newline at end of file
@@ -1,679 +1,685 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """A simple interactive kernel that talks to a frontend over 0MQ.
2 """A simple interactive kernel that talks to a frontend over 0MQ.
3
3
4 Things to do:
4 Things to do:
5
5
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 call set_parent on all the PUB objects with the message about to be executed.
7 call set_parent on all the PUB objects with the message about to be executed.
8 * Implement random port and security key logic.
8 * Implement random port and security key logic.
9 * Implement control messages.
9 * Implement control messages.
10 * Implement event loop and poll version.
10 * Implement event loop and poll version.
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Standard library imports.
18 # Standard library imports.
19 import __builtin__
19 import __builtin__
20 import atexit
20 import atexit
21 import sys
21 import sys
22 import time
22 import time
23 import traceback
23 import traceback
24 import logging
24 import logging
25 # System library imports.
25 # System library imports.
26 import zmq
26 import zmq
27
27
28 # Local imports.
28 # Local imports.
29 from IPython.config.configurable import Configurable
29 from IPython.config.configurable import Configurable
30 from IPython.config.application import boolean_flag
30 from IPython.config.application import boolean_flag
31 from IPython.core.application import ProfileDir
31 from IPython.core.application import ProfileDir
32 from IPython.core.shellapp import (
32 from IPython.core.shellapp import (
33 InteractiveShellApp, shell_flags, shell_aliases
33 InteractiveShellApp, shell_flags, shell_aliases
34 )
34 )
35 from IPython.utils import io
35 from IPython.utils import io
36 from IPython.utils.jsonutil import json_clean
36 from IPython.utils.jsonutil import json_clean
37 from IPython.lib import pylabtools
37 from IPython.lib import pylabtools
38 from IPython.utils.traitlets import (
38 from IPython.utils.traitlets import (
39 List, Instance, Float, Dict, Bool, Int, Unicode, CaselessStrEnum
39 List, Instance, Float, Dict, Bool, Int, Unicode, CaselessStrEnum
40 )
40 )
41
41
42 from entry_point import base_launch_kernel
42 from entry_point import base_launch_kernel
43 from kernelapp import KernelApp, kernel_flags, kernel_aliases
43 from kernelapp import KernelApp, kernel_flags, kernel_aliases
44 from iostream import OutStream
44 from iostream import OutStream
45 from session import Session, Message
45 from session import Session, Message
46 from zmqshell import ZMQInteractiveShell
46 from zmqshell import ZMQInteractiveShell
47
47
48
48
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50 # Main kernel class
50 # Main kernel class
51 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
52
52
53 class Kernel(Configurable):
53 class Kernel(Configurable):
54
54
55 #---------------------------------------------------------------------------
55 #---------------------------------------------------------------------------
56 # Kernel interface
56 # Kernel interface
57 #---------------------------------------------------------------------------
57 #---------------------------------------------------------------------------
58
58
59 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
59 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
60 session = Instance(Session)
60 session = Instance(Session)
61 shell_socket = Instance('zmq.Socket')
61 shell_socket = Instance('zmq.Socket')
62 iopub_socket = Instance('zmq.Socket')
62 iopub_socket = Instance('zmq.Socket')
63 stdin_socket = Instance('zmq.Socket')
63 stdin_socket = Instance('zmq.Socket')
64 log = Instance(logging.Logger)
64 log = Instance(logging.Logger)
65
65
66 # Private interface
66 # Private interface
67
67
68 # Time to sleep after flushing the stdout/err buffers in each execute
68 # Time to sleep after flushing the stdout/err buffers in each execute
69 # cycle. While this introduces a hard limit on the minimal latency of the
69 # cycle. While this introduces a hard limit on the minimal latency of the
70 # execute cycle, it helps prevent output synchronization problems for
70 # execute cycle, it helps prevent output synchronization problems for
71 # clients.
71 # clients.
72 # Units are in seconds. The minimum zmq latency on local host is probably
72 # Units are in seconds. The minimum zmq latency on local host is probably
73 # ~150 microseconds, set this to 500us for now. We may need to increase it
73 # ~150 microseconds, set this to 500us for now. We may need to increase it
74 # a little if it's not enough after more interactive testing.
74 # a little if it's not enough after more interactive testing.
75 _execute_sleep = Float(0.0005, config=True)
75 _execute_sleep = Float(0.0005, config=True)
76
76
77 # Frequency of the kernel's event loop.
77 # Frequency of the kernel's event loop.
78 # Units are in seconds, kernel subclasses for GUI toolkits may need to
78 # Units are in seconds, kernel subclasses for GUI toolkits may need to
79 # adapt to milliseconds.
79 # adapt to milliseconds.
80 _poll_interval = Float(0.05, config=True)
80 _poll_interval = Float(0.05, config=True)
81
81
82 # If the shutdown was requested over the network, we leave here the
82 # If the shutdown was requested over the network, we leave here the
83 # necessary reply message so it can be sent by our registered atexit
83 # necessary reply message so it can be sent by our registered atexit
84 # handler. This ensures that the reply is only sent to clients truly at
84 # handler. This ensures that the reply is only sent to clients truly at
85 # the end of our shutdown process (which happens after the underlying
85 # the end of our shutdown process (which happens after the underlying
86 # IPython shell's own shutdown).
86 # IPython shell's own shutdown).
87 _shutdown_message = None
87 _shutdown_message = None
88
88
89 # This is a dict of port number that the kernel is listening on. It is set
89 # This is a dict of port number that the kernel is listening on. It is set
90 # by record_ports and used by connect_request.
90 # by record_ports and used by connect_request.
91 _recorded_ports = Dict()
91 _recorded_ports = Dict()
92
92
93
93
94
94
95 def __init__(self, **kwargs):
95 def __init__(self, **kwargs):
96 super(Kernel, self).__init__(**kwargs)
96 super(Kernel, self).__init__(**kwargs)
97
97
98 # Before we even start up the shell, register *first* our exit handlers
98 # Before we even start up the shell, register *first* our exit handlers
99 # so they come before the shell's
99 # so they come before the shell's
100 atexit.register(self._at_shutdown)
100 atexit.register(self._at_shutdown)
101
101
102 # Initialize the InteractiveShell subclass
102 # Initialize the InteractiveShell subclass
103 self.shell = ZMQInteractiveShell.instance(config=self.config)
103 self.shell = ZMQInteractiveShell.instance(config=self.config)
104 self.shell.displayhook.session = self.session
104 self.shell.displayhook.session = self.session
105 self.shell.displayhook.pub_socket = self.iopub_socket
105 self.shell.displayhook.pub_socket = self.iopub_socket
106 self.shell.display_pub.session = self.session
106 self.shell.display_pub.session = self.session
107 self.shell.display_pub.pub_socket = self.iopub_socket
107 self.shell.display_pub.pub_socket = self.iopub_socket
108
108
109 # TMP - hack while developing
109 # TMP - hack while developing
110 self.shell._reply_content = None
110 self.shell._reply_content = None
111
111
112 # Build dict of handlers for message types
112 # Build dict of handlers for message types
113 msg_types = [ 'execute_request', 'complete_request',
113 msg_types = [ 'execute_request', 'complete_request',
114 'object_info_request', 'history_request',
114 'object_info_request', 'history_request',
115 'connect_request', 'shutdown_request']
115 'connect_request', 'shutdown_request']
116 self.handlers = {}
116 self.handlers = {}
117 for msg_type in msg_types:
117 for msg_type in msg_types:
118 self.handlers[msg_type] = getattr(self, msg_type)
118 self.handlers[msg_type] = getattr(self, msg_type)
119
119
120 def do_one_iteration(self):
120 def do_one_iteration(self):
121 """Do one iteration of the kernel's evaluation loop.
121 """Do one iteration of the kernel's evaluation loop.
122 """
122 """
123 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
123 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
124 if msg is None:
124 if msg is None:
125 return
125 return
126
126
127 msg_type = msg['header']['msg_type']
127 msg_type = msg['header']['msg_type']
128
128
129 # This assert will raise in versions of zeromq 2.0.7 and lesser.
129 # This assert will raise in versions of zeromq 2.0.7 and lesser.
130 # We now require 2.0.8 or above, so we can uncomment for safety.
130 # We now require 2.0.8 or above, so we can uncomment for safety.
131 # print(ident,msg, file=sys.__stdout__)
131 # print(ident,msg, file=sys.__stdout__)
132 assert ident is not None, "Missing message part."
132 assert ident is not None, "Missing message part."
133
133
134 # Print some info about this message and leave a '--->' marker, so it's
134 # Print some info about this message and leave a '--->' marker, so it's
135 # easier to trace visually the message chain when debugging. Each
135 # easier to trace visually the message chain when debugging. Each
136 # handler prints its message at the end.
136 # handler prints its message at the end.
137 self.log.debug('\n*** MESSAGE TYPE:'+str(msg_type)+'***')
137 self.log.debug('\n*** MESSAGE TYPE:'+str(msg_type)+'***')
138 self.log.debug(' Content: '+str(msg['content'])+'\n --->\n ')
138 self.log.debug(' Content: '+str(msg['content'])+'\n --->\n ')
139
139
140 # Find and call actual handler for message
140 # Find and call actual handler for message
141 handler = self.handlers.get(msg_type, None)
141 handler = self.handlers.get(msg_type, None)
142 if handler is None:
142 if handler is None:
143 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
143 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
144 else:
144 else:
145 handler(ident, msg)
145 handler(ident, msg)
146
146
147 # Check whether we should exit, in case the incoming message set the
147 # Check whether we should exit, in case the incoming message set the
148 # exit flag on
148 # exit flag on
149 if self.shell.exit_now:
149 if self.shell.exit_now:
150 self.log.debug('\nExiting IPython kernel...')
150 self.log.debug('\nExiting IPython kernel...')
151 # We do a normal, clean exit, which allows any actions registered
151 # We do a normal, clean exit, which allows any actions registered
152 # via atexit (such as history saving) to take place.
152 # via atexit (such as history saving) to take place.
153 sys.exit(0)
153 sys.exit(0)
154
154
155
155
156 def start(self):
156 def start(self):
157 """ Start the kernel main loop.
157 """ Start the kernel main loop.
158 """
158 """
159 poller = zmq.Poller()
159 poller = zmq.Poller()
160 poller.register(self.shell_socket, zmq.POLLIN)
160 poller.register(self.shell_socket, zmq.POLLIN)
161 while True:
161 while True:
162 try:
162 try:
163 # scale by extra factor of 10, because there is no
163 # scale by extra factor of 10, because there is no
164 # reason for this to be anything less than ~ 0.1s
164 # reason for this to be anything less than ~ 0.1s
165 # since it is a real poller and will respond
165 # since it is a real poller and will respond
166 # to events immediately
166 # to events immediately
167 poller.poll(10*1000*self._poll_interval)
167
168 self.do_one_iteration()
168 # double nested try/except, to properly catch KeyboardInterrupt
169 # due to pyzmq Issue #130
170 try:
171 poller.poll(10*1000*self._poll_interval)
172 self.do_one_iteration()
173 except:
174 raise
169 except KeyboardInterrupt:
175 except KeyboardInterrupt:
170 # Ctrl-C shouldn't crash the kernel
176 # Ctrl-C shouldn't crash the kernel
171 io.raw_print("KeyboardInterrupt caught in kernel")
177 io.raw_print("KeyboardInterrupt caught in kernel")
172
178
173 def record_ports(self, ports):
179 def record_ports(self, ports):
174 """Record the ports that this kernel is using.
180 """Record the ports that this kernel is using.
175
181
176 The creator of the Kernel instance must call this methods if they
182 The creator of the Kernel instance must call this methods if they
177 want the :meth:`connect_request` method to return the port numbers.
183 want the :meth:`connect_request` method to return the port numbers.
178 """
184 """
179 self._recorded_ports = ports
185 self._recorded_ports = ports
180
186
181 #---------------------------------------------------------------------------
187 #---------------------------------------------------------------------------
182 # Kernel request handlers
188 # Kernel request handlers
183 #---------------------------------------------------------------------------
189 #---------------------------------------------------------------------------
184
190
185 def _publish_pyin(self, code, parent):
191 def _publish_pyin(self, code, parent):
186 """Publish the code request on the pyin stream."""
192 """Publish the code request on the pyin stream."""
187
193
188 pyin_msg = self.session.send(self.iopub_socket, u'pyin',{u'code':code}, parent=parent)
194 pyin_msg = self.session.send(self.iopub_socket, u'pyin',{u'code':code}, parent=parent)
189
195
190 def execute_request(self, ident, parent):
196 def execute_request(self, ident, parent):
191
197
192 status_msg = self.session.send(self.iopub_socket,
198 status_msg = self.session.send(self.iopub_socket,
193 u'status',
199 u'status',
194 {u'execution_state':u'busy'},
200 {u'execution_state':u'busy'},
195 parent=parent
201 parent=parent
196 )
202 )
197
203
198 try:
204 try:
199 content = parent[u'content']
205 content = parent[u'content']
200 code = content[u'code']
206 code = content[u'code']
201 silent = content[u'silent']
207 silent = content[u'silent']
202 except:
208 except:
203 self.log.error("Got bad msg: ")
209 self.log.error("Got bad msg: ")
204 self.log.error(str(Message(parent)))
210 self.log.error(str(Message(parent)))
205 return
211 return
206
212
207 shell = self.shell # we'll need this a lot here
213 shell = self.shell # we'll need this a lot here
208
214
209 # Replace raw_input. Note that is not sufficient to replace
215 # Replace raw_input. Note that is not sufficient to replace
210 # raw_input in the user namespace.
216 # raw_input in the user namespace.
211 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
217 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
212 __builtin__.raw_input = raw_input
218 __builtin__.raw_input = raw_input
213
219
214 # Set the parent message of the display hook and out streams.
220 # Set the parent message of the display hook and out streams.
215 shell.displayhook.set_parent(parent)
221 shell.displayhook.set_parent(parent)
216 shell.display_pub.set_parent(parent)
222 shell.display_pub.set_parent(parent)
217 sys.stdout.set_parent(parent)
223 sys.stdout.set_parent(parent)
218 sys.stderr.set_parent(parent)
224 sys.stderr.set_parent(parent)
219
225
220 # Re-broadcast our input for the benefit of listening clients, and
226 # Re-broadcast our input for the benefit of listening clients, and
221 # start computing output
227 # start computing output
222 if not silent:
228 if not silent:
223 self._publish_pyin(code, parent)
229 self._publish_pyin(code, parent)
224
230
225 reply_content = {}
231 reply_content = {}
226 try:
232 try:
227 if silent:
233 if silent:
228 # run_code uses 'exec' mode, so no displayhook will fire, and it
234 # run_code uses 'exec' mode, so no displayhook will fire, and it
229 # doesn't call logging or history manipulations. Print
235 # doesn't call logging or history manipulations. Print
230 # statements in that code will obviously still execute.
236 # statements in that code will obviously still execute.
231 shell.run_code(code)
237 shell.run_code(code)
232 else:
238 else:
233 # FIXME: the shell calls the exception handler itself.
239 # FIXME: the shell calls the exception handler itself.
234 shell.run_cell(code)
240 shell.run_cell(code)
235 except:
241 except:
236 status = u'error'
242 status = u'error'
237 # FIXME: this code right now isn't being used yet by default,
243 # FIXME: this code right now isn't being used yet by default,
238 # because the run_cell() call above directly fires off exception
244 # because the run_cell() call above directly fires off exception
239 # reporting. This code, therefore, is only active in the scenario
245 # reporting. This code, therefore, is only active in the scenario
240 # where runlines itself has an unhandled exception. We need to
246 # where runlines itself has an unhandled exception. We need to
241 # uniformize this, for all exception construction to come from a
247 # uniformize this, for all exception construction to come from a
242 # single location in the codbase.
248 # single location in the codbase.
243 etype, evalue, tb = sys.exc_info()
249 etype, evalue, tb = sys.exc_info()
244 tb_list = traceback.format_exception(etype, evalue, tb)
250 tb_list = traceback.format_exception(etype, evalue, tb)
245 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
251 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
246 else:
252 else:
247 status = u'ok'
253 status = u'ok'
248
254
249 reply_content[u'status'] = status
255 reply_content[u'status'] = status
250
256
251 # Return the execution counter so clients can display prompts
257 # Return the execution counter so clients can display prompts
252 reply_content['execution_count'] = shell.execution_count -1
258 reply_content['execution_count'] = shell.execution_count -1
253
259
254 # FIXME - fish exception info out of shell, possibly left there by
260 # FIXME - fish exception info out of shell, possibly left there by
255 # runlines. We'll need to clean up this logic later.
261 # runlines. We'll need to clean up this logic later.
256 if shell._reply_content is not None:
262 if shell._reply_content is not None:
257 reply_content.update(shell._reply_content)
263 reply_content.update(shell._reply_content)
258 # reset after use
264 # reset after use
259 shell._reply_content = None
265 shell._reply_content = None
260
266
261 # At this point, we can tell whether the main code execution succeeded
267 # At this point, we can tell whether the main code execution succeeded
262 # or not. If it did, we proceed to evaluate user_variables/expressions
268 # or not. If it did, we proceed to evaluate user_variables/expressions
263 if reply_content['status'] == 'ok':
269 if reply_content['status'] == 'ok':
264 reply_content[u'user_variables'] = \
270 reply_content[u'user_variables'] = \
265 shell.user_variables(content[u'user_variables'])
271 shell.user_variables(content[u'user_variables'])
266 reply_content[u'user_expressions'] = \
272 reply_content[u'user_expressions'] = \
267 shell.user_expressions(content[u'user_expressions'])
273 shell.user_expressions(content[u'user_expressions'])
268 else:
274 else:
269 # If there was an error, don't even try to compute variables or
275 # If there was an error, don't even try to compute variables or
270 # expressions
276 # expressions
271 reply_content[u'user_variables'] = {}
277 reply_content[u'user_variables'] = {}
272 reply_content[u'user_expressions'] = {}
278 reply_content[u'user_expressions'] = {}
273
279
274 # Payloads should be retrieved regardless of outcome, so we can both
280 # Payloads should be retrieved regardless of outcome, so we can both
275 # recover partial output (that could have been generated early in a
281 # recover partial output (that could have been generated early in a
276 # block, before an error) and clear the payload system always.
282 # block, before an error) and clear the payload system always.
277 reply_content[u'payload'] = shell.payload_manager.read_payload()
283 reply_content[u'payload'] = shell.payload_manager.read_payload()
278 # Be agressive about clearing the payload because we don't want
284 # Be agressive about clearing the payload because we don't want
279 # it to sit in memory until the next execute_request comes in.
285 # it to sit in memory until the next execute_request comes in.
280 shell.payload_manager.clear_payload()
286 shell.payload_manager.clear_payload()
281
287
282 # Flush output before sending the reply.
288 # Flush output before sending the reply.
283 sys.stdout.flush()
289 sys.stdout.flush()
284 sys.stderr.flush()
290 sys.stderr.flush()
285 # FIXME: on rare occasions, the flush doesn't seem to make it to the
291 # FIXME: on rare occasions, the flush doesn't seem to make it to the
286 # clients... This seems to mitigate the problem, but we definitely need
292 # clients... This seems to mitigate the problem, but we definitely need
287 # to better understand what's going on.
293 # to better understand what's going on.
288 if self._execute_sleep:
294 if self._execute_sleep:
289 time.sleep(self._execute_sleep)
295 time.sleep(self._execute_sleep)
290
296
291 # Send the reply.
297 # Send the reply.
292 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
298 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
293 reply_content, parent, ident=ident)
299 reply_content, parent, ident=ident)
294 self.log.debug(str(reply_msg))
300 self.log.debug(str(reply_msg))
295
301
296 if reply_msg['content']['status'] == u'error':
302 if reply_msg['content']['status'] == u'error':
297 self._abort_queue()
303 self._abort_queue()
298
304
299 status_msg = self.session.send(self.iopub_socket,
305 status_msg = self.session.send(self.iopub_socket,
300 u'status',
306 u'status',
301 {u'execution_state':u'idle'},
307 {u'execution_state':u'idle'},
302 parent=parent
308 parent=parent
303 )
309 )
304
310
305 def complete_request(self, ident, parent):
311 def complete_request(self, ident, parent):
306 txt, matches = self._complete(parent)
312 txt, matches = self._complete(parent)
307 matches = {'matches' : matches,
313 matches = {'matches' : matches,
308 'matched_text' : txt,
314 'matched_text' : txt,
309 'status' : 'ok'}
315 'status' : 'ok'}
310 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
316 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
311 matches, parent, ident)
317 matches, parent, ident)
312 self.log.debug(str(completion_msg))
318 self.log.debug(str(completion_msg))
313
319
314 def object_info_request(self, ident, parent):
320 def object_info_request(self, ident, parent):
315 object_info = self.shell.object_inspect(parent['content']['oname'])
321 object_info = self.shell.object_inspect(parent['content']['oname'])
316 # Before we send this object over, we scrub it for JSON usage
322 # Before we send this object over, we scrub it for JSON usage
317 oinfo = json_clean(object_info)
323 oinfo = json_clean(object_info)
318 msg = self.session.send(self.shell_socket, 'object_info_reply',
324 msg = self.session.send(self.shell_socket, 'object_info_reply',
319 oinfo, parent, ident)
325 oinfo, parent, ident)
320 self.log.debug(msg)
326 self.log.debug(msg)
321
327
322 def history_request(self, ident, parent):
328 def history_request(self, ident, parent):
323 # We need to pull these out, as passing **kwargs doesn't work with
329 # We need to pull these out, as passing **kwargs doesn't work with
324 # unicode keys before Python 2.6.5.
330 # unicode keys before Python 2.6.5.
325 hist_access_type = parent['content']['hist_access_type']
331 hist_access_type = parent['content']['hist_access_type']
326 raw = parent['content']['raw']
332 raw = parent['content']['raw']
327 output = parent['content']['output']
333 output = parent['content']['output']
328 if hist_access_type == 'tail':
334 if hist_access_type == 'tail':
329 n = parent['content']['n']
335 n = parent['content']['n']
330 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
336 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
331 include_latest=True)
337 include_latest=True)
332
338
333 elif hist_access_type == 'range':
339 elif hist_access_type == 'range':
334 session = parent['content']['session']
340 session = parent['content']['session']
335 start = parent['content']['start']
341 start = parent['content']['start']
336 stop = parent['content']['stop']
342 stop = parent['content']['stop']
337 hist = self.shell.history_manager.get_range(session, start, stop,
343 hist = self.shell.history_manager.get_range(session, start, stop,
338 raw=raw, output=output)
344 raw=raw, output=output)
339
345
340 elif hist_access_type == 'search':
346 elif hist_access_type == 'search':
341 pattern = parent['content']['pattern']
347 pattern = parent['content']['pattern']
342 hist = self.shell.history_manager.search(pattern, raw=raw, output=output)
348 hist = self.shell.history_manager.search(pattern, raw=raw, output=output)
343
349
344 else:
350 else:
345 hist = []
351 hist = []
346 content = {'history' : list(hist)}
352 content = {'history' : list(hist)}
347 msg = self.session.send(self.shell_socket, 'history_reply',
353 msg = self.session.send(self.shell_socket, 'history_reply',
348 content, parent, ident)
354 content, parent, ident)
349 self.log.debug(str(msg))
355 self.log.debug(str(msg))
350
356
351 def connect_request(self, ident, parent):
357 def connect_request(self, ident, parent):
352 if self._recorded_ports is not None:
358 if self._recorded_ports is not None:
353 content = self._recorded_ports.copy()
359 content = self._recorded_ports.copy()
354 else:
360 else:
355 content = {}
361 content = {}
356 msg = self.session.send(self.shell_socket, 'connect_reply',
362 msg = self.session.send(self.shell_socket, 'connect_reply',
357 content, parent, ident)
363 content, parent, ident)
358 self.log.debug(msg)
364 self.log.debug(msg)
359
365
360 def shutdown_request(self, ident, parent):
366 def shutdown_request(self, ident, parent):
361 self.shell.exit_now = True
367 self.shell.exit_now = True
362 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
368 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
363 sys.exit(0)
369 sys.exit(0)
364
370
365 #---------------------------------------------------------------------------
371 #---------------------------------------------------------------------------
366 # Protected interface
372 # Protected interface
367 #---------------------------------------------------------------------------
373 #---------------------------------------------------------------------------
368
374
369 def _abort_queue(self):
375 def _abort_queue(self):
370 while True:
376 while True:
371 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
377 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
372 if msg is None:
378 if msg is None:
373 break
379 break
374 else:
380 else:
375 assert ident is not None, \
381 assert ident is not None, \
376 "Unexpected missing message part."
382 "Unexpected missing message part."
377
383
378 self.log.debug("Aborting:\n"+str(Message(msg)))
384 self.log.debug("Aborting:\n"+str(Message(msg)))
379 msg_type = msg['header']['msg_type']
385 msg_type = msg['header']['msg_type']
380 reply_type = msg_type.split('_')[0] + '_reply'
386 reply_type = msg_type.split('_')[0] + '_reply'
381 reply_msg = self.session.send(self.shell_socket, reply_type,
387 reply_msg = self.session.send(self.shell_socket, reply_type,
382 {'status' : 'aborted'}, msg, ident=ident)
388 {'status' : 'aborted'}, msg, ident=ident)
383 self.log.debug(reply_msg)
389 self.log.debug(reply_msg)
384 # We need to wait a bit for requests to come in. This can probably
390 # We need to wait a bit for requests to come in. This can probably
385 # be set shorter for true asynchronous clients.
391 # be set shorter for true asynchronous clients.
386 time.sleep(0.1)
392 time.sleep(0.1)
387
393
388 def _raw_input(self, prompt, ident, parent):
394 def _raw_input(self, prompt, ident, parent):
389 # Flush output before making the request.
395 # Flush output before making the request.
390 sys.stderr.flush()
396 sys.stderr.flush()
391 sys.stdout.flush()
397 sys.stdout.flush()
392
398
393 # Send the input request.
399 # Send the input request.
394 content = dict(prompt=prompt)
400 content = dict(prompt=prompt)
395 msg = self.session.send(self.stdin_socket, u'input_request', content, parent)
401 msg = self.session.send(self.stdin_socket, u'input_request', content, parent)
396
402
397 # Await a response.
403 # Await a response.
398 ident, reply = self.session.recv(self.stdin_socket, 0)
404 ident, reply = self.session.recv(self.stdin_socket, 0)
399 try:
405 try:
400 value = reply['content']['value']
406 value = reply['content']['value']
401 except:
407 except:
402 self.log.error("Got bad raw_input reply: ")
408 self.log.error("Got bad raw_input reply: ")
403 self.log.error(str(Message(parent)))
409 self.log.error(str(Message(parent)))
404 value = ''
410 value = ''
405 return value
411 return value
406
412
407 def _complete(self, msg):
413 def _complete(self, msg):
408 c = msg['content']
414 c = msg['content']
409 try:
415 try:
410 cpos = int(c['cursor_pos'])
416 cpos = int(c['cursor_pos'])
411 except:
417 except:
412 # If we don't get something that we can convert to an integer, at
418 # If we don't get something that we can convert to an integer, at
413 # least attempt the completion guessing the cursor is at the end of
419 # least attempt the completion guessing the cursor is at the end of
414 # the text, if there's any, and otherwise of the line
420 # the text, if there's any, and otherwise of the line
415 cpos = len(c['text'])
421 cpos = len(c['text'])
416 if cpos==0:
422 if cpos==0:
417 cpos = len(c['line'])
423 cpos = len(c['line'])
418 return self.shell.complete(c['text'], c['line'], cpos)
424 return self.shell.complete(c['text'], c['line'], cpos)
419
425
420 def _object_info(self, context):
426 def _object_info(self, context):
421 symbol, leftover = self._symbol_from_context(context)
427 symbol, leftover = self._symbol_from_context(context)
422 if symbol is not None and not leftover:
428 if symbol is not None and not leftover:
423 doc = getattr(symbol, '__doc__', '')
429 doc = getattr(symbol, '__doc__', '')
424 else:
430 else:
425 doc = ''
431 doc = ''
426 object_info = dict(docstring = doc)
432 object_info = dict(docstring = doc)
427 return object_info
433 return object_info
428
434
429 def _symbol_from_context(self, context):
435 def _symbol_from_context(self, context):
430 if not context:
436 if not context:
431 return None, context
437 return None, context
432
438
433 base_symbol_string = context[0]
439 base_symbol_string = context[0]
434 symbol = self.shell.user_ns.get(base_symbol_string, None)
440 symbol = self.shell.user_ns.get(base_symbol_string, None)
435 if symbol is None:
441 if symbol is None:
436 symbol = __builtin__.__dict__.get(base_symbol_string, None)
442 symbol = __builtin__.__dict__.get(base_symbol_string, None)
437 if symbol is None:
443 if symbol is None:
438 return None, context
444 return None, context
439
445
440 context = context[1:]
446 context = context[1:]
441 for i, name in enumerate(context):
447 for i, name in enumerate(context):
442 new_symbol = getattr(symbol, name, None)
448 new_symbol = getattr(symbol, name, None)
443 if new_symbol is None:
449 if new_symbol is None:
444 return symbol, context[i:]
450 return symbol, context[i:]
445 else:
451 else:
446 symbol = new_symbol
452 symbol = new_symbol
447
453
448 return symbol, []
454 return symbol, []
449
455
450 def _at_shutdown(self):
456 def _at_shutdown(self):
451 """Actions taken at shutdown by the kernel, called by python's atexit.
457 """Actions taken at shutdown by the kernel, called by python's atexit.
452 """
458 """
453 # io.rprint("Kernel at_shutdown") # dbg
459 # io.rprint("Kernel at_shutdown") # dbg
454 if self._shutdown_message is not None:
460 if self._shutdown_message is not None:
455 self.session.send(self.shell_socket, self._shutdown_message)
461 self.session.send(self.shell_socket, self._shutdown_message)
456 self.session.send(self.iopub_socket, self._shutdown_message)
462 self.session.send(self.iopub_socket, self._shutdown_message)
457 self.log.debug(str(self._shutdown_message))
463 self.log.debug(str(self._shutdown_message))
458 # A very short sleep to give zmq time to flush its message buffers
464 # A very short sleep to give zmq time to flush its message buffers
459 # before Python truly shuts down.
465 # before Python truly shuts down.
460 time.sleep(0.01)
466 time.sleep(0.01)
461
467
462
468
463 class QtKernel(Kernel):
469 class QtKernel(Kernel):
464 """A Kernel subclass with Qt support."""
470 """A Kernel subclass with Qt support."""
465
471
466 def start(self):
472 def start(self):
467 """Start a kernel with QtPy4 event loop integration."""
473 """Start a kernel with QtPy4 event loop integration."""
468
474
469 from IPython.external.qt_for_kernel import QtCore
475 from IPython.external.qt_for_kernel import QtCore
470 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
476 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
471
477
472 self.app = get_app_qt4([" "])
478 self.app = get_app_qt4([" "])
473 self.app.setQuitOnLastWindowClosed(False)
479 self.app.setQuitOnLastWindowClosed(False)
474 self.timer = QtCore.QTimer()
480 self.timer = QtCore.QTimer()
475 self.timer.timeout.connect(self.do_one_iteration)
481 self.timer.timeout.connect(self.do_one_iteration)
476 # Units for the timer are in milliseconds
482 # Units for the timer are in milliseconds
477 self.timer.start(1000*self._poll_interval)
483 self.timer.start(1000*self._poll_interval)
478 start_event_loop_qt4(self.app)
484 start_event_loop_qt4(self.app)
479
485
480
486
481 class WxKernel(Kernel):
487 class WxKernel(Kernel):
482 """A Kernel subclass with Wx support."""
488 """A Kernel subclass with Wx support."""
483
489
484 def start(self):
490 def start(self):
485 """Start a kernel with wx event loop support."""
491 """Start a kernel with wx event loop support."""
486
492
487 import wx
493 import wx
488 from IPython.lib.guisupport import start_event_loop_wx
494 from IPython.lib.guisupport import start_event_loop_wx
489
495
490 doi = self.do_one_iteration
496 doi = self.do_one_iteration
491 # Wx uses milliseconds
497 # Wx uses milliseconds
492 poll_interval = int(1000*self._poll_interval)
498 poll_interval = int(1000*self._poll_interval)
493
499
494 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
500 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
495 # We make the Frame hidden when we create it in the main app below.
501 # We make the Frame hidden when we create it in the main app below.
496 class TimerFrame(wx.Frame):
502 class TimerFrame(wx.Frame):
497 def __init__(self, func):
503 def __init__(self, func):
498 wx.Frame.__init__(self, None, -1)
504 wx.Frame.__init__(self, None, -1)
499 self.timer = wx.Timer(self)
505 self.timer = wx.Timer(self)
500 # Units for the timer are in milliseconds
506 # Units for the timer are in milliseconds
501 self.timer.Start(poll_interval)
507 self.timer.Start(poll_interval)
502 self.Bind(wx.EVT_TIMER, self.on_timer)
508 self.Bind(wx.EVT_TIMER, self.on_timer)
503 self.func = func
509 self.func = func
504
510
505 def on_timer(self, event):
511 def on_timer(self, event):
506 self.func()
512 self.func()
507
513
508 # We need a custom wx.App to create our Frame subclass that has the
514 # We need a custom wx.App to create our Frame subclass that has the
509 # wx.Timer to drive the ZMQ event loop.
515 # wx.Timer to drive the ZMQ event loop.
510 class IPWxApp(wx.App):
516 class IPWxApp(wx.App):
511 def OnInit(self):
517 def OnInit(self):
512 self.frame = TimerFrame(doi)
518 self.frame = TimerFrame(doi)
513 self.frame.Show(False)
519 self.frame.Show(False)
514 return True
520 return True
515
521
516 # The redirect=False here makes sure that wx doesn't replace
522 # The redirect=False here makes sure that wx doesn't replace
517 # sys.stdout/stderr with its own classes.
523 # sys.stdout/stderr with its own classes.
518 self.app = IPWxApp(redirect=False)
524 self.app = IPWxApp(redirect=False)
519 start_event_loop_wx(self.app)
525 start_event_loop_wx(self.app)
520
526
521
527
522 class TkKernel(Kernel):
528 class TkKernel(Kernel):
523 """A Kernel subclass with Tk support."""
529 """A Kernel subclass with Tk support."""
524
530
525 def start(self):
531 def start(self):
526 """Start a Tk enabled event loop."""
532 """Start a Tk enabled event loop."""
527
533
528 import Tkinter
534 import Tkinter
529 doi = self.do_one_iteration
535 doi = self.do_one_iteration
530 # Tk uses milliseconds
536 # Tk uses milliseconds
531 poll_interval = int(1000*self._poll_interval)
537 poll_interval = int(1000*self._poll_interval)
532 # For Tkinter, we create a Tk object and call its withdraw method.
538 # For Tkinter, we create a Tk object and call its withdraw method.
533 class Timer(object):
539 class Timer(object):
534 def __init__(self, func):
540 def __init__(self, func):
535 self.app = Tkinter.Tk()
541 self.app = Tkinter.Tk()
536 self.app.withdraw()
542 self.app.withdraw()
537 self.func = func
543 self.func = func
538
544
539 def on_timer(self):
545 def on_timer(self):
540 self.func()
546 self.func()
541 self.app.after(poll_interval, self.on_timer)
547 self.app.after(poll_interval, self.on_timer)
542
548
543 def start(self):
549 def start(self):
544 self.on_timer() # Call it once to get things going.
550 self.on_timer() # Call it once to get things going.
545 self.app.mainloop()
551 self.app.mainloop()
546
552
547 self.timer = Timer(doi)
553 self.timer = Timer(doi)
548 self.timer.start()
554 self.timer.start()
549
555
550
556
551 class GTKKernel(Kernel):
557 class GTKKernel(Kernel):
552 """A Kernel subclass with GTK support."""
558 """A Kernel subclass with GTK support."""
553
559
554 def start(self):
560 def start(self):
555 """Start the kernel, coordinating with the GTK event loop"""
561 """Start the kernel, coordinating with the GTK event loop"""
556 from .gui.gtkembed import GTKEmbed
562 from .gui.gtkembed import GTKEmbed
557
563
558 gtk_kernel = GTKEmbed(self)
564 gtk_kernel = GTKEmbed(self)
559 gtk_kernel.start()
565 gtk_kernel.start()
560
566
561
567
562 #-----------------------------------------------------------------------------
568 #-----------------------------------------------------------------------------
563 # Aliases and Flags for the IPKernelApp
569 # Aliases and Flags for the IPKernelApp
564 #-----------------------------------------------------------------------------
570 #-----------------------------------------------------------------------------
565
571
566 flags = dict(kernel_flags)
572 flags = dict(kernel_flags)
567 flags.update(shell_flags)
573 flags.update(shell_flags)
568
574
569 addflag = lambda *args: flags.update(boolean_flag(*args))
575 addflag = lambda *args: flags.update(boolean_flag(*args))
570
576
571 flags['pylab'] = (
577 flags['pylab'] = (
572 {'IPKernelApp' : {'pylab' : 'auto'}},
578 {'IPKernelApp' : {'pylab' : 'auto'}},
573 """Pre-load matplotlib and numpy for interactive use with
579 """Pre-load matplotlib and numpy for interactive use with
574 the default matplotlib backend."""
580 the default matplotlib backend."""
575 )
581 )
576
582
577 aliases = dict(kernel_aliases)
583 aliases = dict(kernel_aliases)
578 aliases.update(shell_aliases)
584 aliases.update(shell_aliases)
579
585
580 # it's possible we don't want short aliases for *all* of these:
586 # it's possible we don't want short aliases for *all* of these:
581 aliases.update(dict(
587 aliases.update(dict(
582 pylab='IPKernelApp.pylab',
588 pylab='IPKernelApp.pylab',
583 ))
589 ))
584
590
585 #-----------------------------------------------------------------------------
591 #-----------------------------------------------------------------------------
586 # The IPKernelApp class
592 # The IPKernelApp class
587 #-----------------------------------------------------------------------------
593 #-----------------------------------------------------------------------------
588
594
589 class IPKernelApp(KernelApp, InteractiveShellApp):
595 class IPKernelApp(KernelApp, InteractiveShellApp):
590 name = 'ipkernel'
596 name = 'ipkernel'
591
597
592 aliases = Dict(aliases)
598 aliases = Dict(aliases)
593 flags = Dict(flags)
599 flags = Dict(flags)
594 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
600 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
595 # configurables
601 # configurables
596 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
602 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
597 config=True,
603 config=True,
598 help="""Pre-load matplotlib and numpy for interactive use,
604 help="""Pre-load matplotlib and numpy for interactive use,
599 selecting a particular matplotlib backend and loop integration.
605 selecting a particular matplotlib backend and loop integration.
600 """
606 """
601 )
607 )
602 pylab_import_all = Bool(True, config=True,
608 pylab_import_all = Bool(True, config=True,
603 help="""If true, an 'import *' is done from numpy and pylab,
609 help="""If true, an 'import *' is done from numpy and pylab,
604 when using pylab"""
610 when using pylab"""
605 )
611 )
606 def initialize(self, argv=None):
612 def initialize(self, argv=None):
607 super(IPKernelApp, self).initialize(argv)
613 super(IPKernelApp, self).initialize(argv)
608 self.init_shell()
614 self.init_shell()
609 self.init_extensions()
615 self.init_extensions()
610 self.init_code()
616 self.init_code()
611
617
612 def init_kernel(self):
618 def init_kernel(self):
613 kernel_factory = Kernel
619 kernel_factory = Kernel
614
620
615 kernel_map = {
621 kernel_map = {
616 'qt' : QtKernel,
622 'qt' : QtKernel,
617 'qt4': QtKernel,
623 'qt4': QtKernel,
618 'inline': Kernel,
624 'inline': Kernel,
619 'osx': TkKernel,
625 'osx': TkKernel,
620 'wx' : WxKernel,
626 'wx' : WxKernel,
621 'tk' : TkKernel,
627 'tk' : TkKernel,
622 'gtk': GTKKernel,
628 'gtk': GTKKernel,
623 }
629 }
624
630
625 if self.pylab:
631 if self.pylab:
626 key = None if self.pylab == 'auto' else self.pylab
632 key = None if self.pylab == 'auto' else self.pylab
627 gui, backend = pylabtools.find_gui_and_backend(key)
633 gui, backend = pylabtools.find_gui_and_backend(key)
628 kernel_factory = kernel_map.get(gui)
634 kernel_factory = kernel_map.get(gui)
629 if kernel_factory is None:
635 if kernel_factory is None:
630 raise ValueError('GUI is not supported: %r' % gui)
636 raise ValueError('GUI is not supported: %r' % gui)
631 pylabtools.activate_matplotlib(backend)
637 pylabtools.activate_matplotlib(backend)
632
638
633 kernel = kernel_factory(config=self.config, session=self.session,
639 kernel = kernel_factory(config=self.config, session=self.session,
634 shell_socket=self.shell_socket,
640 shell_socket=self.shell_socket,
635 iopub_socket=self.iopub_socket,
641 iopub_socket=self.iopub_socket,
636 stdin_socket=self.stdin_socket,
642 stdin_socket=self.stdin_socket,
637 log=self.log
643 log=self.log
638 )
644 )
639 self.kernel = kernel
645 self.kernel = kernel
640 kernel.record_ports(self.ports)
646 kernel.record_ports(self.ports)
641
647
642 if self.pylab:
648 if self.pylab:
643 import_all = self.pylab_import_all
649 import_all = self.pylab_import_all
644 pylabtools.import_pylab(kernel.shell.user_ns, backend, import_all,
650 pylabtools.import_pylab(kernel.shell.user_ns, backend, import_all,
645 shell=kernel.shell)
651 shell=kernel.shell)
646
652
647 def init_shell(self):
653 def init_shell(self):
648 self.shell = self.kernel.shell
654 self.shell = self.kernel.shell
649
655
650
656
651 #-----------------------------------------------------------------------------
657 #-----------------------------------------------------------------------------
652 # Kernel main and launch functions
658 # Kernel main and launch functions
653 #-----------------------------------------------------------------------------
659 #-----------------------------------------------------------------------------
654
660
655 def launch_kernel(*args, **kwargs):
661 def launch_kernel(*args, **kwargs):
656 """Launches a localhost IPython kernel, binding to the specified ports.
662 """Launches a localhost IPython kernel, binding to the specified ports.
657
663
658 This function simply calls entry_point.base_launch_kernel with the right first
664 This function simply calls entry_point.base_launch_kernel with the right first
659 command to start an ipkernel. See base_launch_kernel for arguments.
665 command to start an ipkernel. See base_launch_kernel for arguments.
660
666
661 Returns
667 Returns
662 -------
668 -------
663 A tuple of form:
669 A tuple of form:
664 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
670 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
665 where kernel_process is a Popen object and the ports are integers.
671 where kernel_process is a Popen object and the ports are integers.
666 """
672 """
667 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
673 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
668 *args, **kwargs)
674 *args, **kwargs)
669
675
670
676
671 def main():
677 def main():
672 """Run an IPKernel as an application"""
678 """Run an IPKernel as an application"""
673 app = IPKernelApp.instance()
679 app = IPKernelApp.instance()
674 app.initialize()
680 app.initialize()
675 app.start()
681 app.start()
676
682
677
683
678 if __name__ == '__main__':
684 if __name__ == '__main__':
679 main()
685 main()
General Comments 0
You need to be logged in to leave comments. Login now