Skip to main content

Hacky extensions and helper functions for the new Python REPL.

Project description

pyrepl-hacks

Hacky extensions and helper functions for the new Python REPL.

import pyrepl_hacks as repl

repl.bind("Alt+M", "move-to-indentation")   # Move to first non-space in current line
repl.bind("Shift+Tab", "dedent")            # Dedent the whole input
repl.bind("Alt+Down", "move-line-down")     # Swap current line with next in block
repl.bind("Alt+Up", "move-line-up")         # Swap current line with previous in block
repl.bind("Shift+Home", "home")             # Move to first character in the input
repl.bind("Shift+End", "end")               # Move to last character in the input

# Make Ctrl+N insert a specific list of numbers
repl.bind_to_insert("Ctrl+N", "[2, 1, 3, 4, 7, 11, 18, 29]")


@repl.bind(r"Ctrl+X Ctrl+R", with_event=True)
def subprocess_run(reader, event_name, event):
    """Ctrl+X followed by Ctrl+R will insert a subprocess.run command."""
    reader.insert("import subprocess\n")
    code = 'subprocess.run("", shell=True)'
    reader.insert(code)
    for _ in range(len(code) - code.index('""') - 1):
        repl.commands.left(reader, event_name, event)

⚠️ WARNING: this could all break

This library relies on Python implementation details which may change in future Python versions.

This library uses the _pyrepl module (and optionally _colorize). As the _ prefix implies, these modules are not designed for public use.

That means that when you upgrade to a newer Python (for example Python 3.15) this code may break. For that reason, the Python versions this package claims to work with are pinned to only known-to-be-working Python versions.

Installing

To install globally:

pip install pyrepl-hacks

Then you can use it in your PYTHONSTARTUP file:

def _main():
    try:
        import pyrepl_hacks as repl
    except ImportError:
        pass  # We must be on Python 3.12 or earlier
    else:
        repl.bind("Alt+M", "move-to-indentation")
        repl.bind("Shift+Tab", "dedent")
        repl.bind("Alt+Down", "move-line-down")
        repl.bind("Alt+Up", "move-line-up")
        repl.bind("Shift+Home", "home")
        repl.bind("Shift+End", "end")

        repl.bind_to_insert("Ctrl+N", "[2, 1, 3, 4, 7, 11, 18, 29]")

_main()
del _main  # Don't polute the global namespace in our REPL

Note that this will only modify the REPL in environments where pyrepl-hacks is installed. So if you want it everywhere, you would need to install pyrepl-hacks system-wide and in every virtual environment.

If you just want to play with this tool, try this:

uvx --with pyrepl-hacks python

Command Registering and Key Binding

This library includes features for easily registering and binding new REPL commands.

Binding to existing commands

You can bind a key to an existing command:

import pyrepl_hacks as repl

repl.bind("Shift+Home", "home")

Inserting text with a binding

You can use the bind_to_insert helper to bind a key to insert specific text:

import pyrepl_hacks as repl

repl.bind_to_insert("Ctrl+P", "Python?!")

Registering new commands

Need something fancy that doesn't exist yet?

You can register a new command:

import pyrepl_hacks as repl

@repl.register_command
def exit(reader):
    """Exits Python immediately."""
    import sys
    sys.exit(0)

The register_command decorator will turn the under_score separated name into a kebab-case name by default.

The register_command can optionally accept a command name and, if the command needs access to the event name and event object, a with_event=True argument can be provided:

import pyrepl_hacks as repl

@repl.register_command("delete-line", with_event=True)
def delete_whole_line(reader, event_name, event):
    """Move to beginning of line and delete all text."""
    reader.pos = reader.bol()
    repl.commands.kill_line(reader, event_name, event)

After commands have been registered, they can be used with the bind function to bind them to specific keys:

import pyrepl_hacks as repl

repl.bind("F4", "exit")
repl.bind("Ctrl+X Ctrl+D", "delete-line")

Binding keys while registering

The bind function can also be used as a decorator to register a command and bind it to a specific key combination at the same time:

import pyrepl_hacks as repl

@repl.bind("F4")
def exit(reader):
    """Exits Python immediately."""
    import sys
    sys.exit(0)

Since there's not much point in making a new command without binding it, you'll usually want to use bind instead of register_command.

Just like register_command, bind decorator can also accept a with_event=True argument to pass the event name and event object into the command function.

Available Commands

Here are some of the interesting commands provided by Python (in _pyrepl.commands):

  • clear-screen: Clear screen (Ctrl+L)
  • accept: Run current code block (Alt+Enter)
  • beginning-of-line: Move cursor to the first character of the current line (Ctrl+A)
  • end-of-line: Move cursor to the last character of the current line (Ctrl+E)
  • home: Move cursor the first character in the code block
  • end: Move cursor the last character in the code block
  • kill-line: Delete to end of line (Ctrl+K)
  • unix-line-discard: Delete to beginning of line (Ctrl+U)
  • backward-word: Move cursor back one word (Ctrl+Left)
  • forward-word: Move cursor forward one word (Ctrl+Right)
  • backward-kill-word: Delete to beginning of word (Alt+Backspace)
  • kill-word: Delete to end of word (Alt+D)

This pyrepl-hacks project provides some additional commands as well:

  • move-to-indentation: Move to first non-space in current line
  • dedent: Dedent the whole code block
  • move-line-down: Swap current line with next one in the block
  • move-line-up: Swap current line with previous one in the block

These 4 additional commands have no key bindings by default.

I recommend binding these commands as well as the home and end commands (provided by _pyrepl.commands) which are also unbound by default:

repl.bind("Alt+M", "move-to-indentation")   # Move to first non-space in current line
repl.bind("Shift+Tab", "dedent")            # Dedent the whole input
repl.bind("Alt+Down", "move-line-down")     # Swap current line with next in block
repl.bind("Alt+Up", "move-line-up")         # Swap current line with previous in block
repl.bind("Shift+Home", "home")             # Move to first character in the input
repl.bind("Shift+End", "end")               # Move to last character in the input

Note that these custom REPL commands and all existing commands provided by _pyrepl.commands include wrapper functions in the commands submodule. These functions are named the same as their command name, except - must be replaced by _:

from repl.commands import move_to_indentation, clear_screen

The Future is Obsolescence?

My hope is that this package will be obsolete one day.

I hope that Python will eventually include an official interface for creating new REPL commands and binding keys to commands.

I also hope that some (or all?) of the 4 new commands this module includes will eventually be included with Python by default.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyrepl_hacks-0.1.0.tar.gz (8.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyrepl_hacks-0.1.0-py3-none-any.whl (7.5 kB view details)

Uploaded Python 3

File details

Details for the file pyrepl_hacks-0.1.0.tar.gz.

File metadata

  • Download URL: pyrepl_hacks-0.1.0.tar.gz
  • Upload date:
  • Size: 8.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.11

File hashes

Hashes for pyrepl_hacks-0.1.0.tar.gz
Algorithm Hash digest
SHA256 0b50e551f000ec914d749630492b3982792d2837f4e5531b5b8f36e64cd989d4
MD5 e210609636c0f87457e825c91904d7a2
BLAKE2b-256 999568c43e76a12d2a471634f60a8f59c40b57db5a6e5dfd3de2a7988c664ab4

See more details on using hashes here.

File details

Details for the file pyrepl_hacks-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pyrepl_hacks-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 09205339a08181119eda852704db01bb999efa93d1699454d56b5fc781ce157d
MD5 f27b7e9f6af93ec5c1527ff49994b13e
BLAKE2b-256 3935f03144caa370b2a9af3e039039469255159f6ee5182d38b182a0f6534786

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page