Skip to content
Back to top
View this page
Edit this page

# Built-in Events

The following events are defined by xonsh itself. For more information about events, see the events tutorial.

on_chdir | on_command_not_found | on_completer_filter
on_envvar_change | on_envvar_new | on_exit
on_import_post_create_module | on_import_post_exec_module | on_import_post_find_spec
on_import_pre_create_module | on_import_pre_exec_module | on_import_pre_find_spec
on_lscolors_change | on_post_cmdloop | on_post_init
on_post_prompt | on_post_rc | on_postcommand
on_pre_cmdloop | on_pre_prompt | on_pre_prompt_format
on_pre_rc | on_pre_spec_run_ls | on_precommand
on_ptk_create | on_transform_command | on_xonfig_info_requested
on_xontribs_loaded |  |

## Listing

### `on_chdir(olddir: str, newdir: str) -> None`

Fires when the current directory is changed for any reason.

    @events.on_chdir
    def _source_env_xsh(olddir, newdir):
        '''Source `env.xsh` file if it's exist in the new directory.'''
        env_file = @.imp.pathlib.Path(newdir) / 'env.xsh'
        if env_file.exists():
            print(f'Source {env_file}')
            source @(env_file)

### `on_command_not_found(cmd: list[str]) -> list[str] | tuple[str, ...] | dict | None`

Fires if a command is not found (only in interactive sessions).

Parameters:

- `cmd`: The command that was attempted

Returns:

- `list[str]` or `tuple[str, ...]`: A replacement command to execute instead. The first valid replacement from any handler will be used.

- `dict`: A dict with a required `"cmd"` key (list of command tokens) and an optional `"env"` key (dict of environment variables to set for the replacement command). Same convention as `@Aliases.return_command`.

- `None`: To let the error be raised normally

Note: If the replacement command also fails, the original error is shown.

Examples:

    @events.on_command_not_found
    def _vim_to_vi(cmd, **kwargs):
        '''If vim not found let's try to use vi.'''
        if cmd[0] == 'vim':
            return ['vi'] + cmd[1:]

    @events.on_command_not_found
    def _node_with_path(cmd, **kwargs):
        '''Run node with a custom NODE_PATH.'''
        if cmd[0] == 'mynode':
            return {"cmd": ["node"] + cmd[1:], "env": {"NODE_PATH": "/opt/libs"}}

### `on_completer_filter(completer: str, command: str, context: CompletionContext) -> bool | None`

Fires once for every registered completer just before it is invoked, allowing handlers to veto the call. Return `False` to skip the completer; return `True`, `None` (or nothing) to let it run. If any handler returns `False`, the completer is skipped.

Parameters:

- `completer`: the registered name of the completer about to run (e.g. `'bash'`, `'path'`).

- `command`: the basename of the command being completed (e.g. `'kubectl'` even when the user typed `/usr/bin/kubectl`). Empty string when no command has been typed yet, or when the completion happens outside the command-line context.

- `context`: the full `CompletionContext` for the current completion request.

Use this to short-circuit completers such as `bash` on commands where the user does not need bash-supplied completions:

    @events.on_completer_filter
    def _only_bash_for_some(completer, command, context, **_):
        return command in {'kubectl', 'docker'} if completer == 'bash' else True

Handlers run on the completion thread; exceptions raised inside a handler are logged and treated as if no value was returned (i.e. the completer is still allowed to run).

### `on_envvar_change(name: str, oldvalue: Any, newvalue: Any) -> None`

Fires after an environment variable is changed. Note: Setting envvars inside the handler might cause a recursion until the limit.

    @events.on_envvar_change
    def _on_env_path_change(name, oldvalue, newvalue):
        '''Keep `/tmp/bin` on top of PATH.'''
        if name == 'PATH' and (newvalue == [] or newvalue[0] != '/tmp/bin'):
            $PATH = ['/tmp/bin'] + [v for v in newvalue if v != '/tmp/bin']

### `on_envvar_new(name: str, value: Any) -> None`

Fires after a new environment variable is created. Note: Setting envvars inside the handler might cause a recursion until the limit.

Parameters:

- `name`: The name of the new environment variable.

- `value`: The value assigned to it.

Example:

    @events.on_envvar_new
    def _event_log_new_var(name, value, **kw):
        print(f"New env var created: {name}={value!r}")

### `on_exit(exit_code : int) -> None`

Fired after all commands have been executed, before tear-down occurs.

NOTE: All the caveats of the `atexit` module also apply to this event.

Parameters:

- `exit_code`: The shell’s exit code (`0` for success).

Example:

    @events.on_exit
    def _event_save_session_log(exit_code, **kw):
        import datetime
        with open("/tmp/xonsh_sessions.log", "a") as f:
            f.write(f"{datetime.datetime.now()}: exited with code {exit_code}\n")

### `on_import_post_create_module(module: Module, spec: ModuleSpec) -> None`

Fires after a module is created by its loader but before the loader returns it. The parameters here are the module object itself and the spec object. See importlib for more details.

### `on_import_post_exec_module(module: Module) -> None`

Fires after a module is executed by its loader but before the loader returns it. The only parameter is the module itself. See importlib for more details.

### `on_import_post_find_spec(spec, fullname, path, target) -> None`

Fires after all import find_spec() calls have been executed. The parameters here the spec and the arguments importlib.abc.MetaPathFinder.find_spec(). Namely,

`spec`:

  A ModuleSpec object if the spec was found, or None if it was not.

`fullname`:

  The full name of the module to import.

`path`:

  None if a top-level import, otherwise the `__path__` of the parent package.

`target`:

  Target module used to make a better guess about the package spec.

### `on_import_pre_create_module(spec: ModuleSpec) -> None`

Fires right before a module is created by its loader. The only parameter is the spec object. See importlib for more details.

### `on_import_pre_exec_module(module: Module) -> None`

Fires right before a module is executed by its loader. The only parameter is the module itself. See importlib for more details.

### `on_import_pre_find_spec(fullname: str, path: str, target: module or None) -> None`

Fires before any import find_spec() calls have been executed. The parameters here are the same as importlib.abc.MetaPathFinder.find_spec(). Namely,

`fullname`:

  The full name of the module to import.

`path`:

  None if a top-level import, otherwise the `__path__` of the parent package.

`target`:

  Target module used to make a better guess about the package spec.

### `on_lscolors_change(key: str, oldvalue: Any, newvalue: Any) -> None`

Fires after a value in LS_COLORS changes, when a new key is added (oldvalue is None) or when an existing key is deleted (newvalue is None). LS_COLORS values must be (ANSI color) strings, None is unambiguous. Does not fire when the whole environment variable changes (see on_envvar_change). Does not fire for each value when LS_COLORS is first instantiated. Normal usage is to arm the event handler, then read (not modify) all existing values.

Parameters:

- `key`: The LS_COLORS key that changed (e.g. `"di"` for directories).

- `oldvalue`: Previous ANSI color string, or `None` if the key is new.

- `newvalue`: New ANSI color string, or `None` if the key was deleted.

Example:

    @events.on_lscolors_change
    def _event_log_lscolors_change(key, oldvalue, newvalue, **kw):
        if newvalue is None:
            print(f"LS_COLORS: removed key {key!r}")
        elif oldvalue is None:
            print(f"LS_COLORS: added {key!r}={newvalue!r}")
        else:
            print(f"LS_COLORS: changed {key!r} from {oldvalue!r} to {newvalue!r}")

### `on_post_cmdloop() -> None`

Fired just after the command loop finishes, if it is.

NOTE: All the caveats of the `atexit` module also apply to this event.

Example:

    @events.on_post_cmdloop
    def _event_session_summary(**kw):
        print(f"Session ended after {len(@.history)} commands.")

### `on_post_init() -> None`

Fired after all initialization is finished and we’re ready to do work.

NOTE: This is fired before the wizard is automatically started.

Example:

    @events.on_post_init
    def _event_greet(**kw):
        from xonsh.built_ins import XSH
        name = XSH.env.get("USER", "user")
        print(f"Welcome back, {name}!")

### `on_post_prompt() -> None`

Fires just after the prompt returns

Example:

    @events.on_post_prompt
    def _event_log_session_time(**kw):
        import time
        print(f"[session time: {time.strftime('%H:%M:%S')}]")

### `on_post_rc() -> None`

Fired just after rc files are loaded, if they are.

Example:

    @events.on_post_rc
    def _event_rc_loaded(**kw):
        print("RC files loaded successfully.")

### `on_postcommand(cmd: str, rtn: int, out: str or None, ts: list) -> None`

Fires just after a command is executed. The arguments are the same as history. This event only fires in interactive mode.

Parameters:

- `cmd`: The command that was executed — the final form after transformation and dedent.

- `rtn`: The result of the command executed (`0` for success)

- `out`: If xonsh stores command output, this is the output

- `ts`: Timestamps, in the order of `[starting, ending]`

Example:

    @events.on_postcommand
    def _prompt_err_command_again(cmd, rtn, out, ts):
        '''If the result of executing the command is not zero, repeat the command on the next command prompt.'''
        if rtn != 0:
            $XONSH_PROMPT_NEXT_CMD = cmd.rstrip()

### `on_pre_cmdloop() -> None`

Fired just before the command loop is started, if it is.

Example:

    @events.on_pre_cmdloop
    def _event_show_tip(**kw):
        import random
        tips = ["Use Tab for completion", "Try 'xonfig' to configure xonsh"]
        print("Tip:", random.choice(tips))

### `on_pre_prompt() -> None`

Fires just before showing the prompt

Example:

    @events.on_pre_prompt
    def _event_ring_bell(**kw):
        print("\a", end="", flush=True)

### `on_pre_prompt_format() -> None`

Fires before the prompt will be formatted

Example:

    @events.on_pre_prompt_format
    def _event_update_vcs_status(**kw):
        # refresh any cached VCS info before the prompt template is rendered
        import os
        os.environ.pop("GIT_STATUS_CACHE", None)

### `on_pre_rc() -> None`

Fired just before rc files are loaded, if they are.

Example:

    @events.on_pre_rc
    def _event_announce_rc(**kw):
        print("Loading rc files...")

### `on_pre_spec_run_ls(spec: xonsh.built_ins.SubprocSpec) -> None`

Fires right before a SubprocSpec.run() is called for the ls command.

Parameters:

- `spec`: The `SubprocSpec` object for the `ls` invocation.

Example:

    @events.on_pre_spec_run_ls
    def _event_inject_color_flag(spec, **kw):
        # Automatically add --color=auto if not already present
        if not any(arg.startswith("--color") for arg in spec.args):
            spec.args.append("--color=auto")

### `on_precommand(cmd: str) -> None`

Fires just before a command is executed, after `on_transform_command` has produced its final form. Handlers cannot modify the command — use `on_transform_command` if you need to change the source before it runs. This event only fires in interactive mode.

Parameters:

- `cmd`: The command about to be executed.

Example:

    @events.on_precommand
    def _audit(cmd, **kw):
        '''Log each command to a file right before execution.'''
        with open('/tmp/xonsh_audit.log', 'a') as f:
            f.write(cmd)

### `on_ptk_create(prompter: PromptSession, history: PromptToolkitHistory, completer: PromptToolkitCompleter, bindings: KeyBindings) ->`

Fired after prompt toolkit has been initialized. Use this event in xonsh RC file.

    # ~/.xonshrc
    @events.on_ptk_create
    def _custom_keybindings(bindings, **kw):
        @bindings.add(@.imp.prompt_toolkit.keys.Keys.ControlW)
        def say_hi(event):
            event.current_buffer.insert_text('hi')

### `on_transform_command(cmd: str) -> str | None`

Fired to request xontribs to transform a command line. Return the transformed command, or the same command if no transformation occurs. Return None to indicate no transformation (equivalent to returning the original command). Only done for interactive sessions.

This may be fired multiple times per command, with other transformers input or output, so design any handlers for this carefully.

    @events.on_transform_command
    def _pipe_prev_command(cmd, **kw):
        '''Pipe prev command e.g. `| grep 1` will be `<prev cmd> | grep 1`.'''
        if cmd and cmd.startswith('| ') and __xonsh__.history:
            return __xonsh__.history[-1].cmd.rstrip() + cmd.rstrip()
        return cmd

### `on_xonfig_info_requested() -> list[tuple[str, str]]`

Register a callable that will return extra info when `xonfig info` is called.

### `on_xontribs_loaded() -> None`

Fired after external xontribs with `entrypoints defined` are loaded.

Example:

    @events.on_xontribs_loaded
    def _event_check_xontribs(**kw):
        from xonsh.xontribs import xontribs_loaded
        if "coreutils" not in xontribs_loaded():
            print("Hint: 'xontrib load coreutils' adds cross-platform cp, mv, rm, ...")

## Event Categories

Additionally, there are a few categories of events whose names are part of the specification of the event. These events are fired if they exist, and are ignored otherwise. Here are their specifications.

### `on_pre_spec_run(spec: SubprocSpec) -> None`

This event fires whenever any command has its `SubprocSpec.run()` method called. This is fired prior to the run call executing anything at all. This receives the `SubprocSpec` object as `spec` that triggered the event, allowing the handler to modify the spec if needed. For example, if we wanted to intercept all specs, we could write:

    @events.on_pre_spec_run
    def print_when_ls(spec=None, **kwargs):
        print("Running a command")

To mask an environment variable for the subprocess this spec is about to run, set `spec.env[name] = @.env.DELETE_VAR`. The variable will not appear in the environment passed to the child process, even if it is set in the session env:

    @events.on_pre_spec_run
    def strip_secrets(spec, **_):
        spec.env = (spec.env or {}) | {'SECRET_TOKEN': @.env.DELETE_VAR}

### `on_pre_spec_run_<cmd-name>(spec: SubprocSpec) -> None`

This event fires whenever a command with a give name (`<cmd-name>`) has its `SubprocSpec.run()` method called. This is fired prior to the run call executing anything at all. This receives the `SubprocSpec` object as `spec` that triggered the event, allowing the handler to modify the spec if needed. For example, if we wanted to intercept an `ls` spec, we could write:

    @events.on_pre_spec_run_ls
    def print_when_ls(spec=None, **kwargs):
        print("Look at me list stuff!")

### `on_post_spec_run(spec: SubprocSpec) -> None`

This event fires whenever any command has its `SubprocSpec.run()` method called. This is fired after to the run call has executed everything except returning. This recieves the `SubprocSpec` object as `spec` that triggered the event, allowing the handler to modify the spec if needed. Note that because of the way process pipelines and specs work in xonsh, the command will have started running, but won’t necessarily have completed. This is because `SubprocSpec.run()` does not block. For example, if we wanted to get any spec after a command has started running, we could write:

    @events.on_post_spec_run
    def print_while_ls(spec=None, **kwargs):
        print("A command is running")

### `on_post_spec_run_<cmd-name>(spec: SubprocSpec) -> None`

This event fires whenever a command with a give name (`<cmd-name>`) has its `SubprocSpec.run()` method called. This is fired after to the run call has executed everything except returning. This recieves the `SubprocSpec` object as `spec` that triggered the event, allowing the handler to modify the spec if needed. Note that because of the way process pipelines and specs work in xonsh, the command will have started running, but won’t necessarily have completed. This is because `SubprocSpec.run()` does not block. For example, if we wanted to get an `ls` spec after ls has started running, we could write:

    @events.on_post_spec_run_ls
    def print_while_ls(spec=None, **kwargs):
        print("Mom! I'm listing!")