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