diff --git a/news/delcwd.rst b/news/delcwd.rst new file mode 100644 index 000000000..ee4d4f840 --- /dev/null +++ b/news/delcwd.rst @@ -0,0 +1,18 @@ +**Added:** + +* Xonsh will issue a warning message when the current working + directory has been remove out from under it and not replaced + prior to running the next command. + +**Changed:** None + +**Deprecated:** None + +**Removed:** None + +**Fixed:** + +* Xonsh will no longer crash is the current working directory is + removed out from it. + +**Security:** None diff --git a/xonsh/base_shell.py b/xonsh/base_shell.py index 00fbd8749..04ba8d154 100644 --- a/xonsh/base_shell.py +++ b/xonsh/base_shell.py @@ -375,8 +375,26 @@ class BaseShell(object): def _fix_cwd(self): """Check if the cwd changed out from under us.""" env = builtins.__xonsh_env__ - cwd = os.getcwd() - if 'PWD' not in env: + try: + cwd = os.getcwd() + except (FileNotFoundError, OSError): + cwd = None + if cwd is None: + # directory has been deleted out from under us, most likely + pwd = env.get('PWD', None) + if pwd is None: + # we have no idea where we are + env['PWD'] = '' + elif os.path.isdir(pwd): + # unclear why os.getcwd() failed. do nothing. + pass + else: + # OK PWD is really gone. + msg = '{UNDERLINE_INTENSE_WHITE}{BACKGROUND_INTENSE_BLACK}' + msg += "xonsh: working directory does not exist: " + pwd + msg += '{NO_COLOR}' + self.print_color(msg, file=sys.stderr) + elif 'PWD' not in env: # $PWD is missing from env, recreate it env['PWD'] = cwd elif os.path.realpath(cwd) != os.path.realpath(env['PWD']): diff --git a/xonsh/jobs.py b/xonsh/jobs.py index 863bfca83..5e7208009 100644 --- a/xonsh/jobs.py +++ b/xonsh/jobs.py @@ -144,6 +144,11 @@ else: # finished, then we don't need to do anything here, see # issue #2220 return False + elif e.errno == 25: # [Errno 25] Inappropriate ioctl for device + # There are also cases where we are not connected to a + # real TTY, even though we may be run in interactive + # mode. See issue #2267 for an example with emacs + return False else: raise finally: diff --git a/xonsh/prompt/vc.py b/xonsh/prompt/vc.py index b26084978..3f7ad6161 100644 --- a/xonsh/prompt/vc.py +++ b/xonsh/prompt/vc.py @@ -54,8 +54,10 @@ def get_git_branch(): def _get_hg_root(q): - _curpwd = os.getcwd() + _curpwd = builtins.__xonsh_env__['PWD'] while True: + if not os.path.isdir(_curpwd): + return False if any([b.name == '.hg' for b in os.scandir(_curpwd)]): q.put(_curpwd) break diff --git a/xonsh/pyghooks.py b/xonsh/pyghooks.py index 957754395..e5b4eebd6 100644 --- a/xonsh/pyghooks.py +++ b/xonsh/pyghooks.py @@ -34,7 +34,10 @@ load_module_in_background('pkg_resources', debug='XONSH_DEBUG', def _command_is_valid(cmd): - cmd_abspath = os.path.abspath(os.path.expanduser(cmd)) + try: + cmd_abspath = os.path.abspath(os.path.expanduser(cmd)) + except (FileNotFoundError, OSError): + return False return cmd in builtins.__xonsh_commands_cache__ or \ (os.path.isfile(cmd_abspath) and os.access(cmd_abspath, os.X_OK)) @@ -42,7 +45,10 @@ def _command_is_valid(cmd): def _command_is_autocd(cmd): if not builtins.__xonsh_env__.get('AUTO_CD', False): return False - cmd_abspath = os.path.abspath(os.path.expanduser(cmd)) + try: + cmd_abspath = os.path.abspath(os.path.expanduser(cmd)) + except (FileNotFoundError, OSError): + return False return os.path.isdir(cmd_abspath) @@ -57,9 +63,11 @@ def subproc_cmd_callback(_, match): def subproc_arg_callback(_, match): """Check if match contains valid path""" text = match.group() - yield (match.start(), - Name.Constant if os.path.exists(os.path.expanduser(text)) else Text, - text) + try: + ispath = os.path.exists(os.path.expanduser(text)) + except (FileNotFoundError, OSError): + ispath = False + yield (match.start(), Name.Constant if ispath else Text, text) COMMAND_TOKEN_RE = r'[^=\s\[\]{}()$"\'`<&|;!]+(?=\s|$|\)|\]|\}|!)'