Fix how `$PWD` is managed in order to work with symlinks gracefully

This commit is contained in:
Fabien Dubosson 2017-02-23 23:15:05 +01:00
parent 580f43c2dc
commit 72d04e9961
3 changed files with 37 additions and 9 deletions

16
news/fix_pwd.rst Normal file
View file

@ -0,0 +1,16 @@
**Added:**
* Add a ``-P`` flag to the ``cd`` function in order to change directory and
following symlinks.
**Changed:** None
**Deprecated:** None
**Removed:** None
**Fixed:**
* Fix how ``$PWD`` is managed in order to work with symlinks gracefully
**Security:** None

View file

@ -372,14 +372,15 @@ class BaseShell(object):
hist.last_cmd_rtn = hist.last_cmd_out = None
def _fix_cwd(self):
"""Check if the cwd changed out from under us"""
"""Check if the cwd changed out from under us."""
env = builtins.__xonsh_env__
cwd = os.getcwd()
if cwd != builtins.__xonsh_env__.get('PWD'):
old = builtins.__xonsh_env__.get('PWD') # working directory changed without updating $PWD
builtins.__xonsh_env__['PWD'] = cwd # track it now
if 'PWD' in env and os.path.realpath(cwd) != os.path.realpath(env['PWD']):
old = env['PWD'] # working directory changed without updating $PWD
env['PWD'] = cwd # track it now
if old is not None:
builtins.__xonsh_env__['OLDPWD'] = old # and update $OLDPWD like dirstack.
events.on_chdir.fire(olddir=old, newdir=cwd) # fire event after cwd actually changed.
env['OLDPWD'] = old # and update $OLDPWD like dirstack.
events.on_chdir.fire(olddir=old, newdir=cwd) # fire event after cwd actually changed.
def push(self, line):
"""Pushes a line onto the buffer and compiles the code in a way that

View file

@ -129,11 +129,15 @@ def _get_cwd():
return None
def _change_working_directory(newdir):
def _change_working_directory(newdir, follow_symlinks=False):
env = builtins.__xonsh_env__
old = env['PWD']
new = os.path.join(old, newdir)
absnew = os.path.abspath(new)
if follow_symlinks:
absnew = os.path.realpath(absnew)
try:
os.chdir(absnew)
except (OSError, FileNotFoundError):
@ -180,6 +184,11 @@ def cd(args, stdin=None):
oldpwd = env.get('OLDPWD', None)
cwd = env['PWD']
follow_symlinks = False
if len(args) > 0 and args[0] == '-P':
follow_symlinks = True
del args[0]
if len(args) == 0:
d = os.path.expanduser('~')
elif len(args) == 1:
@ -207,7 +216,9 @@ def cd(args, stdin=None):
else:
d = _try_cdpath(d)
else:
return '', 'cd takes 0 or 1 arguments, not {0}\n'.format(len(args)), 1
return '', ('cd takes 0 or 1 arguments, not {0}. An additional `-P` '
'flag can be passed in first position to follow symlinks.'
'\n'.format(len(args))), 1
if not os.path.exists(d):
return '', 'cd: no such file or directory: {0}\n'.format(d), 1
if not os.path.isdir(d):
@ -223,7 +234,7 @@ def cd(args, stdin=None):
pushd(['-n', '-q', cwd])
if ON_WINDOWS and _is_unc_path(d):
d = _unc_map_temp_drive(d)
_change_working_directory(d)
_change_working_directory(d, follow_symlinks)
return None, None, 0