From b5ca6465b4e99657837c2376cfbd50d679a12a24 2011-11-23 05:00:19 From: MinRK Date: 2011-11-23 05:00:19 Subject: [PATCH] defer to stdlib for path.get_home_dir() We have elaborate and fragile logic for determining home dir, and it is ultimately less reliable than the stdlib behavior used for `os.path.expanduser('~')`. This commit defers to that in all cases other than a bundled Python in py2exe/py2app environments. The one case where the default guess will *not* be correct, based on inline comments, is on WinHPC, where all paths must be UNC (`\\foo`), and thus HOMESHARE is the logical first choice. However, HOMESHARE is the wrong answer in approximately all other cases where it is defined, and the fix for WinHPC users is the trivial `HOME=%HOMESHARE%`. This removes the various tests of our Windows path resolution logic, which are no longer relevant. Further, $HOME is used by the stdlib as first priority on *all* platforms, so tests for this behavior are no longer posix-specific. closes gh-970 closes gh-747 --- diff --git a/IPython/utils/path.py b/IPython/utils/path.py index 96e96a2..5e224bb 100644 --- a/IPython/utils/path.py +++ b/IPython/utils/path.py @@ -170,21 +170,14 @@ class HomeDirError(Exception): def get_home_dir(): """Return the closest possible equivalent to a 'home' directory. - * On POSIX, we try $HOME. - * On Windows we try: - - %HOMESHARE% - - %HOMEDRIVE\%HOMEPATH% - - %USERPROFILE% - - Registry hack for My Documents - - %HOME%: rare, but some people with unix-like setups may have defined it - * On Dos C:\ - - Currently only Posix and NT are implemented, a HomeDirError exception is - raised for all other OSes. + * First, check for frozen env in case of py2exe + * Otherwise, defer to os.path.expanduser('~'), ensuring unicode + + See stdlib docs for how this is determined. + + $HOME is first priority on *ALL* platforms. """ - env = os.environ - # first, check py2exe distribution root directory for _ipython. # This overrides all. Normally does not exist. @@ -197,90 +190,12 @@ def get_home_dir(): if _writable_dir(os.path.join(root, '_ipython')): os.environ["IPYKITROOT"] = root return py3compat.cast_unicode(root, fs_encoding) - - if os.name == 'posix': - # Linux, Unix, AIX, OS X - try: - homedir = env['HOME'] - except KeyError: - # Last-ditch attempt at finding a suitable $HOME, on systems where - # it may not be defined in the environment but the system shell - # still knows it - reported once as: - # https://github.com/ipython/ipython/issues/154 - from subprocess import Popen, PIPE - homedir = Popen('echo $HOME', shell=True, - stdout=PIPE).communicate()[0].strip() - if homedir: - return py3compat.cast_unicode(homedir, fs_encoding) - else: - raise HomeDirError('Undefined $HOME, IPython cannot proceed.') - else: - return py3compat.cast_unicode(homedir, fs_encoding) - elif os.name == 'nt': - # Now for win9x, XP, Vista, 7? - # For some strange reason all of these return 'nt' for os.name. - # First look for a network home directory. This will return the UNC - # path (\\server\\Users\%username%) not the mapped path (Z:\). This - # is needed when running IPython on cluster where all paths have to - # be UNC. - try: - homedir = env['HOMESHARE'] - except KeyError: - pass - else: - if _writable_dir(homedir): - return py3compat.cast_unicode(homedir, fs_encoding) - - # Now look for a local home directory - try: - homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH']) - except KeyError: - pass - else: - if _writable_dir(homedir): - return py3compat.cast_unicode(homedir, fs_encoding) - - # Now the users profile directory - try: - homedir = os.path.join(env['USERPROFILE']) - except KeyError: - pass - else: - if _writable_dir(homedir): - return py3compat.cast_unicode(homedir, fs_encoding) - - # Use the registry to get the 'My Documents' folder. - try: - import _winreg as wreg - key = wreg.OpenKey( - wreg.HKEY_CURRENT_USER, - "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" - ) - homedir = wreg.QueryValueEx(key,'Personal')[0] - key.Close() - except: - pass - else: - if _writable_dir(homedir): - return py3compat.cast_unicode(homedir, fs_encoding) - - # A user with a lot of unix tools in win32 may have defined $HOME. - # Try this as a last ditch option. - try: - homedir = env['HOME'] - except KeyError: - pass - else: - if _writable_dir(homedir): - return py3compat.cast_unicode(homedir, fs_encoding) - - # If all else fails, raise HomeDirError - raise HomeDirError('No valid home directory could be found') - elif os.name == 'dos': - # Desperate, may do absurd things in classic MacOS. May work under DOS. - return u'C:\\' + + homedir = os.path.expanduser('~') + if _writable_dir(homedir): + return py3compat.cast_unicode(homedir, fs_encoding) else: - raise HomeDirError('No valid home directory could be found for your OS') + raise HomeDirError('%s not a writable dir, set $HOME env to override' % homedir) def get_xdg_dir(): """Return the XDG_CONFIG_HOME, if it is defined and exists, else None. diff --git a/IPython/utils/tests/test_path.py b/IPython/utils/tests/test_path.py index 9881bff..e9429d2 100644 --- a/IPython/utils/tests/test_path.py +++ b/IPython/utils/tests/test_path.py @@ -118,7 +118,6 @@ def teardown_environment(): # Build decorator that uses the setup_environment/setup_environment with_environment = with_setup(setup_environment, teardown_environment) - @skip_if_not_win32 @with_environment def test_get_home_dir_1(): @@ -147,96 +146,27 @@ def test_get_home_dir_2(): @with_environment -@skip_win32 def test_get_home_dir_3(): - """Testcase $HOME is set, then use its value as home directory.""" + """get_home_dir() uses $HOME if set""" env["HOME"] = HOME_TEST_DIR home_dir = path.get_home_dir() nt.assert_equal(home_dir, env["HOME"]) @with_environment -@skip_win32 def test_get_home_dir_4(): - """Testcase $HOME is not set, os=='posix'. - This should fail with HomeDirError""" + """get_home_dir() still works if $HOME is not set""" - os.name = 'posix' if 'HOME' in env: del env['HOME'] - nt.assert_raises(path.HomeDirError, path.get_home_dir) - + # this should still succeed, but we don't know what the answer should be + home = path.get_home_dir() + nt.assert_true(path._writable_dir(home)) -@skip_if_not_win32 @with_environment def test_get_home_dir_5(): - """Using HOMEDRIVE + HOMEPATH, os=='nt'. - - HOMESHARE is missing. - """ - - os.name = 'nt' - env.pop('HOMESHARE', None) - env['HOMEDRIVE'], env['HOMEPATH'] = os.path.splitdrive(HOME_TEST_DIR) - home_dir = path.get_home_dir() - nt.assert_equal(home_dir, abspath(HOME_TEST_DIR)) - - -@skip_if_not_win32 -@with_environment -def test_get_home_dir_6(): - """Using USERPROFILE, os=='nt'. - - HOMESHARE, HOMEDRIVE, HOMEPATH are missing. - """ - - os.name = 'nt' - env.pop('HOMESHARE', None) - env.pop('HOMEDRIVE', None) - env.pop('HOMEPATH', None) - env["USERPROFILE"] = abspath(HOME_TEST_DIR) - home_dir = path.get_home_dir() - nt.assert_equal(home_dir, abspath(HOME_TEST_DIR)) - - -@skip_if_not_win32 -@with_environment -def test_get_home_dir_7(): - """Using HOMESHARE, os=='nt'.""" - - os.name = 'nt' - env["HOMESHARE"] = abspath(HOME_TEST_DIR) - home_dir = path.get_home_dir() - nt.assert_equal(home_dir, abspath(HOME_TEST_DIR)) - - -# Should we stub wreg fully so we can run the test on all platforms? -@skip_if_not_win32 -@with_environment -def test_get_home_dir_8(): - """Using registry hack for 'My Documents', os=='nt' - - HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing. - """ - os.name = 'nt' - # Remove from stub environment all keys that may be set - for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']: - env.pop(key, None) - - #Stub windows registry functions - def OpenKey(x, y): - class key: - def Close(self): - pass - return key() - def QueryValueEx(x, y): - return [abspath(HOME_TEST_DIR)] - - wreg.OpenKey = OpenKey - wreg.QueryValueEx = QueryValueEx - - home_dir = path.get_home_dir() - nt.assert_equal(home_dir, abspath(HOME_TEST_DIR)) - + """raise HomeDirError if $HOME is specified, but not a writable dir""" + env['HOME'] = abspath(HOME_TEST_DIR+'garbage') + nt.assert_raises(path.HomeDirError, path.get_home_dir) @with_environment def test_get_ipython_dir_1():