diff --git a/news/events-precmd.rst b/news/events-precmd.rst new file mode 100644 index 000000000..997c8d9d2 --- /dev/null +++ b/news/events-precmd.rst @@ -0,0 +1,13 @@ +**Added:** + +* Added ``on_precommand`` and ``on_postcommand`` `events `_ + +**Changed:** None + +**Deprecated:** None + +**Removed:** None + +**Fixed:** None + +**Security:** None diff --git a/tests/test_news.py b/tests/test_news.py index 88cc3a679..e3ed482c5 100644 --- a/tests/test_news.py +++ b/tests/test_news.py @@ -13,7 +13,7 @@ NEWSDIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'news') CATEGORIES = frozenset(['Added', 'Changed', 'Deprecated', 'Removed', 'Fixed', 'Security']) -single_grave_reg = re.compile(r'[^`]`[^`]+`[^`]') +single_grave_reg = re.compile(r'[^`]`[^`]+`[^`_]') def check_news_file(fname): name = fname.name @@ -51,8 +51,7 @@ def check_news_file(fname): pytest.fail('{}:{}: invalid rst'.format(name, i+1), pytrace=False) if '`' in line: - if line.count('`') % 4 != 0 \ - or single_grave_reg.search(line): + if single_grave_reg.search(line): pytest.fail("{}:{}: single grave accents" " are not valid rst".format(name, i+1), pytrace=False) diff --git a/xonsh/base_shell.py b/xonsh/base_shell.py index ea74e4759..928f6e3bc 100644 --- a/xonsh/base_shell.py +++ b/xonsh/base_shell.py @@ -159,6 +159,9 @@ class BaseShell(object): src, code = self.push(line) if code is None: return + + events.on_precommand.fire(src) + hist = builtins.__xonsh_history__ # pylint: disable=no-member ts1 = None store_stdout = builtins.__xonsh_env__.get('XONSH_STORE_STDOUT') # pylint: disable=no-member @@ -181,16 +184,50 @@ class BaseShell(object): ts1 = ts1 or time.time() self._append_history(inp=src, ts=[ts0, ts1], tee_out=tee.getvalue()) tee.close() - cwd = os.getcwd() - if cwd != builtins.__xonsh_env__['PWD']: - old = builtins.__xonsh_env__['PWD'] # working directory changed without updating $PWD - builtins.__xonsh_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(old, cwd) # fire event after cwd actually changed. + + self._fix_cwd() if builtins.__xonsh_exit__: # pylint: disable=no-member return True + def _append_history(self, tee_out=None, **info): + """ + Append information about the command to the history. + + (Also handles on_postcommand because this is the place where all the information is available) + """ + hist = builtins.__xonsh_history__ # pylint: disable=no-member + info['rtn'] = hist.last_cmd_rtn + tee_out = tee_out or None + last_out = hist.last_cmd_out or None + if last_out is None and tee_out is None: + pass + elif last_out is None and tee_out is not None: + info['out'] = tee_out + elif last_out is not None and tee_out is None: + info['out'] = last_out + else: + info['out'] = tee_out + '\n' + last_out + + events.on_postcommand.fire( + info['inp'], + info['rtn'], + info.get('out', None), + info['ts'] + ) + + hist.append(info) + hist.last_cmd_rtn = hist.last_cmd_out = None + + def _fix_cwd(self): + """Check if the cwd changed out from under us""" + cwd = os.getcwd() + if cwd != builtins.__xonsh_env__['PWD']: + old = builtins.__xonsh_env__['PWD'] # working directory changed without updating $PWD + builtins.__xonsh_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(old, 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 enables multiline input. @@ -280,23 +317,6 @@ class BaseShell(object): self.settitle() return p - def _append_history(self, tee_out=None, **info): - """Append information about the command to the history.""" - hist = builtins.__xonsh_history__ # pylint: disable=no-member - info['rtn'] = hist.last_cmd_rtn - tee_out = tee_out or None - last_out = hist.last_cmd_out or None - if last_out is None and tee_out is None: - pass - elif last_out is None and tee_out is not None: - info['out'] = tee_out - elif last_out is not None and tee_out is None: - info['out'] = last_out - else: - info['out'] = tee_out + '\n' + last_out - hist.append(info) - hist.last_cmd_rtn = hist.last_cmd_out = None - def format_color(self, string, **kwargs): """Formats the colors in a string. This base implmentation does not actually do any coloring, but just returns the string directly. diff --git a/xonsh/shell.py b/xonsh/shell.py index 192ab9219..f364a6686 100644 --- a/xonsh/shell.py +++ b/xonsh/shell.py @@ -11,6 +11,20 @@ from xonsh.execer import Execer from xonsh.platform import (best_shell_type, has_prompt_toolkit, ptk_version_is_supported) from xonsh.tools import XonshError, to_bool_or_int +from xonsh.events import events + + +events.doc('on_precommand', """ +on_precommand(cmd: str) -> None + +Fires just before a command is executed. +""") + +events.doc('on_postcommand', """ +on_postcommand(cmd: str, rtn: int, out: str or None, ts: list) -> None + +Fires just after a command is executed. +""") class Shell(object):