diff --git a/IPython/utils/path.py b/IPython/utils/path.py index e7c7edd..b1ee7ae 100644 --- a/IPython/utils/path.py +++ b/IPython/utils/path.py @@ -16,6 +16,7 @@ Utilities for path handling. import os import sys +import tempfile from hashlib import md5 import IPython @@ -41,6 +42,10 @@ def _get_long_path_name(path): """Dummy no-op.""" return path +def _writable_dir(path): + """Whether `path` is a directory, to which the user has write access.""" + return os.path.isdir(path) and os.access(path, os.W_OK) + if sys.platform == 'win32': def _get_long_path_name(path): """Get a long path name (expand ~) on Windows using ctypes. @@ -166,7 +171,6 @@ def get_home_dir(): raised for all other OSes. """ - isdir = os.path.isdir env = os.environ # first, check py2exe distribution root directory for _ipython. @@ -178,7 +182,7 @@ def get_home_dir(): else: root=os.path.join(os.path.split(IPython.__file__)[0],"../../") root=os.path.abspath(root).rstrip('\\') - if isdir(os.path.join(root, '_ipython')): + if _writable_dir(os.path.join(root, '_ipython')): os.environ["IPYKITROOT"] = root return _cast_unicode(root, fs_encoding) @@ -212,7 +216,7 @@ def get_home_dir(): except KeyError: pass else: - if isdir(homedir): + if _writable_dir(homedir): return _cast_unicode(homedir, fs_encoding) # Now look for a local home directory @@ -221,7 +225,7 @@ def get_home_dir(): except KeyError: pass else: - if isdir(homedir): + if _writable_dir(homedir): return _cast_unicode(homedir, fs_encoding) # Now the users profile directory @@ -230,7 +234,7 @@ def get_home_dir(): except KeyError: pass else: - if isdir(homedir): + if _writable_dir(homedir): return _cast_unicode(homedir, fs_encoding) # Use the registry to get the 'My Documents' folder. @@ -245,7 +249,7 @@ def get_home_dir(): except: pass else: - if isdir(homedir): + if _writable_dir(homedir): return _cast_unicode(homedir, fs_encoding) # A user with a lot of unix tools in win32 may have defined $HOME. @@ -255,7 +259,7 @@ def get_home_dir(): except KeyError: pass else: - if isdir(homedir): + if _writable_dir(homedir): return _cast_unicode(homedir, fs_encoding) # If all else fails, raise HomeDirError @@ -272,14 +276,13 @@ def get_xdg_dir(): This is only for posix (Linux,Unix,OS X, etc) systems. """ - isdir = os.path.isdir env = os.environ if os.name == 'posix': # Linux, Unix, AIX, OS X # use ~/.config if not set OR empty xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config') - if xdg and isdir(xdg): + if xdg and _writable_dir(xdg): return _cast_unicode(xdg, fs_encoding) return None @@ -294,7 +297,7 @@ def get_ipython_dir(): env = os.environ pjoin = os.path.join - exists = os.path.exists + ipdir_def = '.ipython' xdg_def = 'ipython' @@ -312,7 +315,7 @@ def get_ipython_dir(): xdg_ipdir = pjoin(xdg_dir, xdg_def) - if exists(xdg_ipdir) or not exists(home_ipdir): + if _writable_dir(xdg_ipdir) or not _writable_dir(home_ipdir): ipdir = xdg_ipdir if ipdir is None: @@ -320,6 +323,19 @@ def get_ipython_dir(): ipdir = home_ipdir ipdir = os.path.normpath(os.path.expanduser(ipdir)) + + if os.path.exists(ipdir) and not _writable_dir(ipdir): + # ipdir exists, but is not writable + warn.warn("IPython dir '%s' is not a writable location," + " using a temp directory."%ipdir) + ipdir = tempfile.mkdtemp() + elif not os.path.exists(ipdir): + parent = ipdir.rsplit(os.path.sep, 1)[0] + if not _writable_dir(parent): + # ipdir does not exist and parent isn't writable + warn.warn("IPython parent '%s' is not a writable location," + " using a temp directory."%parent) + ipdir = tempfile.mkdtemp() return _cast_unicode(ipdir, fs_encoding) diff --git a/IPython/utils/tests/test_path.py b/IPython/utils/tests/test_path.py index d464f31..b92c0a0 100644 --- a/IPython/utils/tests/test_path.py +++ b/IPython/utils/tests/test_path.py @@ -16,6 +16,7 @@ import os import shutil import sys import tempfile +import StringIO from os.path import join, abspath, split @@ -26,7 +27,7 @@ from nose import with_setup import IPython from IPython.testing import decorators as dec from IPython.testing.decorators import skip_if_not_win32, skip_win32 -from IPython.utils import path +from IPython.utils import path, io # Platform-dependent imports try: @@ -92,7 +93,8 @@ def teardown_environment(): """Restore things that were remebered by the setup_environment function """ (oldenv, os.name, path.get_home_dir, IPython.__file__,) = oldstuff - + reload(path) + for key in env.keys(): if key not in oldenv: del env[key] @@ -229,6 +231,7 @@ def test_get_home_dir_8(): def test_get_ipython_dir_1(): """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions.""" env_ipdir = os.path.join("someplace", ".ipython") + path._writable_dir = lambda path: True env['IPYTHON_DIR'] = env_ipdir ipdir = path.get_ipython_dir() nt.assert_equal(ipdir, env_ipdir) @@ -238,6 +241,8 @@ def test_get_ipython_dir_1(): def test_get_ipython_dir_2(): """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions.""" path.get_home_dir = lambda : "someplace" + path.get_xdg_dir = lambda : None + path._writable_dir = lambda path: True os.name = "posix" env.pop('IPYTHON_DIR', None) env.pop('IPYTHONDIR', None) @@ -249,6 +254,7 @@ def test_get_ipython_dir_2(): def test_get_ipython_dir_3(): """test_get_ipython_dir_3, use XDG if defined, and .ipython doesn't exist.""" path.get_home_dir = lambda : "someplace" + path._writable_dir = lambda path: True os.name = "posix" env.pop('IPYTHON_DIR', None) env.pop('IPYTHONDIR', None) @@ -283,18 +289,23 @@ def test_get_ipython_dir_5(): @with_environment def test_get_ipython_dir_6(): """test_get_ipython_dir_6, use XDG if defined and neither exist.""" - path.get_home_dir = lambda : 'somehome' - path.get_xdg_dir = lambda : 'somexdg' + xdg = os.path.join(HOME_TEST_DIR, 'somexdg') + os.mkdir(xdg) + shutil.rmtree(os.path.join(HOME_TEST_DIR, '.ipython')) + path.get_home_dir = lambda : HOME_TEST_DIR + path.get_xdg_dir = lambda : xdg os.name = "posix" env.pop('IPYTHON_DIR', None) env.pop('IPYTHONDIR', None) - xdg_ipdir = os.path.join("somexdg", "ipython") + env.pop('XDG_CONFIG_HOME', None) + xdg_ipdir = os.path.join(xdg, "ipython") ipdir = path.get_ipython_dir() nt.assert_equal(ipdir, xdg_ipdir) @with_environment def test_get_ipython_dir_7(): """test_get_ipython_dir_7, test home directory expansion on IPYTHON_DIR""" + path._writable_dir = lambda path: True home_dir = os.path.expanduser('~') env['IPYTHON_DIR'] = os.path.join('~', 'somewhere') ipdir = path.get_ipython_dir() @@ -305,6 +316,7 @@ def test_get_ipython_dir_7(): def test_get_xdg_dir_1(): """test_get_xdg_dir_1, check xdg_dir""" reload(path) + path._writable_dir = lambda path: True path.get_home_dir = lambda : 'somewhere' os.name = "posix" env.pop('IPYTHON_DIR', None) @@ -369,3 +381,24 @@ def test_get_long_path_name(): p = path.get_long_path_name('/usr/local') nt.assert_equals(p,'/usr/local') +@dec.skip_win32 # can't create not-user-writable dir on win +@with_environment +def test_not_writable_ipdir(): + tmpdir = tempfile.mkdtemp() + os.name = "posix" + env.pop('IPYTHON_DIR', None) + env.pop('IPYTHONDIR', None) + env.pop('XDG_CONFIG_HOME', None) + env['HOME'] = tmpdir + ipdir = os.path.join(tmpdir, '.ipython') + os.mkdir(ipdir) + os.chmod(ipdir, 600) + stderr = io.stderr + pipe = StringIO.StringIO() + io.stderr = pipe + ipdir = path.get_ipython_dir() + io.stderr.flush() + io.stderr = stderr + nt.assert_true('WARNING' in pipe.getvalue()) + env.pop('IPYTHON_DIR', None) + \ No newline at end of file diff --git a/IPython/zmq/ipkernel.py b/IPython/zmq/ipkernel.py index fec751d..d68a420 100755 --- a/IPython/zmq/ipkernel.py +++ b/IPython/zmq/ipkernel.py @@ -164,8 +164,14 @@ class Kernel(Configurable): # reason for this to be anything less than ~ 0.1s # since it is a real poller and will respond # to events immediately - poller.poll(10*1000*self._poll_interval) - self.do_one_iteration() + + # double nested try/except, to properly catch KeyboardInterrupt + # due to pyzmq Issue #130 + try: + poller.poll(10*1000*self._poll_interval) + self.do_one_iteration() + except: + raise except KeyboardInterrupt: # Ctrl-C shouldn't crash the kernel io.raw_print("KeyboardInterrupt caught in kernel")