prompt-toolkit Documentation

repository·main·Indexed 27 days ago

https://github.com/prompt-toolkit/python-prompt-toolkit

A pure Python library for building powerful interactive command line and terminal applications. It serves as an advanced replacement for GNU readline or a framework for full-screen applications, featuring syntax highlighting, multi-line editing, code completion, mouse support, and both Emacs and Vi key bindings. Version 3.0 requires Python 3.6+ and supports Linux, OS X, OpenBSD, and Windows, with native integration for asyncio environments.

Tokens
34.2K
Snippets
89
Records
156
Agent score
94%

What's inside prompt-toolkit

  1. Overview of prompt_toolkit use cases

    main

    prompt_toolkit supports two primary modes of operation:

    1. Readline replacement: Used to simply read input from a user. It uses a built-in layout consisting of an input buffer, a prompt, an autocompletion float, and an optional input validation toolbar.
    2. Full-screen terminal applications: Used to build complex interfaces (like Pyvim or pymux). This involves using the layout engine to create custom layouts with horizontal and vertical splits, floats, and custom user controls, supported by a flexible key binding system.
  2. Overview of prompt_toolkit features

    main

    prompt_toolkit is a pure Python library for building interactive command line and terminal applications. It can serve as an advanced replacement for GNU readline or be used to build full-screen applications.

    Key features include:

    • Syntax highlighting: Real-time highlighting of input (e.g., using Pygments).
    • Multi-line editing: Support for multi-line input.
    • Code completion: Advanced completion capabilities.
    • Text selection: Copy/paste support for both Emacs and Vi styles.
    • Mouse support: Cursor positioning and scrolling via mouse.
    • Auto suggestions: Similar to the fish shell experience.
    • Key bindings: Support for both Emacs and Vi modes.
    • Search: Reverse and forward incremental search.
    • Unicode support: Handles double-width characters (e.g., Chinese input) correctly.
    • No global state: Designed to avoid side effects from global state.
  3. Explore projects using prompt_toolkit

    main

    The prompt_toolkit library is used by a wide variety of shells, full-screen applications, and libraries. You can use these existing projects as inspiration or reference for building interactive command-line interfaces.

    Shells and REPLs

    • Python REPLs: ptpython, ipython, ptrepl (run any command as REPL).
    • Database Clients: pgcli (Postgres), mycli (MySQL), litecli (SQLite), mssql-cli (Microsoft SQL Server), vcli (Vertica), athenacli (AWS Athena), cycli (Cypher), EdgeDB.
    • Cloud & Infrastructure: saws (AWS CLI), aws-shell (AWS CLI integrated shell), Kube-shell (Kubernetes), wharfee (Docker).
    • Other Shells: xonsh (Python-ish/BASH-compatible), Ergonomica (Bash alternative), radian (R console).
    • Debuggers & Tools: ptpdb (pdb replacement), robotframework-debuglibrary (RobotFramework), click-repl (Subcommand REPL for click apps).

    Full-screen Applications

    • Terminal Utilities: pymux (terminal multiplexer), pypager (pager like less), pyvim (Vim clone).
    • Specialized Tools: pydoro (Pomodoro timer), Hummingbot (Crypto trading), ass (OpenAI Assistants API client), sanctuary-zero (Secure chatroom).

    Libraries built on prompt_toolkit

    • ptterm: A terminal emulator widget.
    • PyInquirer: An immersive command-line application library (inspired by Inquirer.js).
    • clintermission: A non-fullscreen command-line selection menu.
  4. Understand the prompt_toolkit rendering pipeline

    main

    The rendering pipeline is the process that occurs after every keystroke to update the terminal UI. It follows these stages:

    1. Input Waiting: The application sits in an event loop waiting for I/O (user input).
    2. Input Reading: The read_from_input function (in application.py) reads from the prompt_toolkit.input.Input object via read_keys. This stage handles UTF-8 decoding and VT100 parsing (using Vt100Parser) to handle multi-byte characters and escape sequences.
    3. Key Processing: Key objects are passed to prompt_toolkit.key_binding.key_processor.KeyProcessor. This component matches sequences of keys against registered bindings, handling complex sequences (like jj in Vi mode) and evaluating attached filters.
    4. Handler Execution: Once a sequence is matched, the associated handler is executed (e.g., text manipulation or focus changes).
    5. UI Invalidation & Rendering: The UI is invalidated and a new frame is calculated:
      • Dimension Calculation: The root prompt_toolkit.layout.Container calculates preferred heights/widths recursively.
      • Painting: A prompt_toolkit.layout.screen.Screen object acts as a canvas. The root container's write_to_screen method is called, which recursively calls write_to_screen on child containers until it reaches prompt_toolkit.layout.Window objects, which perform the actual painting.
      • Stdout Output: The system computes the difference between the new screen and the previous screen and uses the prompt_toolkit.output.Output back-end to update only the necessary parts of the terminal.
  5. Understand Application components

    main

    A prompt_toolkit.application.Application consists of four main components:

    1. I/O objects: An Input instance (stdin abstraction) and an Output instance (stdout abstraction). These are optional and usually handled by defaults.
    2. Layout: Defines the graphical structure (e.g., text boxes, buttons) using a collection of 'widgets'.
    3. Style: Defines colors, bold, italic, and underline styles used throughout the application.
    4. Key bindings: A set of rules defining how user input triggers actions.

    Applications run via an internal event loop that waits for user input and dispatches it to handlers. Use Application.exit() to quit the application.

  6. Understand the prompt-toolkit architecture

    main

    The prompt-toolkit architecture follows a data flow from input to rendering:

    1. InputStream: Parses VT100-compatible terminal input into data and control characters (e.g., translating \x1b[6~ into Keys.PageDown).
    2. InputStreamHandler: Uses a Registry of key bindings to call appropriate handlers based on the received keys and the current input mode (e.g., Vi or Emacs).
    3. Key Bindings: Functions that receive an Event. They typically operate on a Buffer to insert data or move the cursor, but can also modify UI elements like menus or color schemes.
    4. Buffer: Holds the current input state (text and cursor position) via a Document object. It provides methods for text manipulation and cursor movement (e.g., cursor_forward, insert_char, delete_word).
    5. Layout: When the Renderer triggers a redraw, the layout determines the visual structure (toolbars, menus, prompt, etc.) using a Screen object.
    6. Renderer: Calculates the difference between the previous and current output and writes the changes to the terminal.
  7. Fix get_event_loop calls for version 3.0

    main

    In version 3.0, get_event_loop must be imported from asyncio instead of prompt_toolkit.eventloop. Note that in 2.0, get_event_loop returned a prompt_toolkit.EventLoop object, whereas in 3.0 it returns an asyncio event loop.

    Event Loop API Changes:

    MethodVersion 2.0Version 3.0 (asyncio)
    run_in_executorloop.run_in_executor(callback)loop.run_in_executor(None, callback)
    Thread-safe callsloop.call_from_executor(callback)loop.call_soon_threadsafe(callback)
    if PTK3:
        from asyncio import get_event_loop
    else:
        from prompt_toolkit.eventloop import get_event_loop
  8. Update dialog function usage for version 3.0

    main

    In version 3.0, dialog functions (like input_dialog) no longer return the result directly. Instead, they return a prompt_toolkit.Application object. To display the dialog and get the result, you must call either the .run() or .run_async() method on the returned object. The async_ parameter has been removed.

    # Synchronous usage in 3.0
    if PTK3:
        result = input_dialog(title='...', text='...').run()
    else:
        result = input_dialog(title='...', text='...')
    
    # Asynchronous usage in 3.0
    if PTK3:
        result = await input_dialog(title='...', text='...').run_async()
    else:
        result = await input_dialog(title='...', text='...', async_=True)
  9. Apply styles using class names and the Style object

    main

    Instead of inline styling, you can attach class names to UI controls using the class: prefix and define a global Style object passed to the Application.

    Key Concepts:

    • Multiple Classes: Use a comma-separated list (class:left,bottom) or repeat the prefix (class:left class:bottom).
    • Dot Notation: class:a.b.c expands to class:a class:a.b class:a.b.c. This is useful for hierarchical styling or Pygments tokens.
    • Combining Styles: You can combine class names and inline styles. The order determines priority (right-most/later items override earlier ones).
    • Complex Selectors: A style rule can target multiple classes simultaneously (e.g., ('header left', 'underline') targets elements with both header and left classes).
    from prompt_toolkit.layout import VSplit, Window
    from prompt_toolkit.styles import Style
    
    layout = VSplit([
        Window(BufferControl(...), style='class:left'),
        HSplit([
            Window(BufferControl(...), style='class:top'),
            Window(BufferControl(...), style='class:bottom'),
        ], style='class:right')
    ])
    
    style = Style([
         ('left', 'bg:ansired'),
         ('top', 'fg:#00aaaa'),
         ('bottom', 'underline bold'),
     ]),
    
    # To use it, pass it to the Application
    app = Application(layout=layout, style=style)
  10. Create a Pytest fixture for prompt_toolkit testing

    main

    To reduce boilerplate in your test suite, you can create a pytest fixture that automatically sets up a pipe input and a DummyOutput within a create_app_session context.

    To maintain compatibility with pytest's capsys fixture (which replaces sys.stdout), use an autouse fixture with scope="function" to ensure a fresh AppSession is created for every test case.

    import pytest
    from prompt_toolkit.application import create_app_session
    from prompt_toolkit.input import create_pipe_input
    from prompt_toolkit.output import DummyOutput
    
    @pytest.fixture(autouse=True, scope="function")
    def mock_input():
        with create_pipe_input() as pipe_input:
            with create_app_session(input=pipe_input, output=DummyOutput()):
                yield pipe_input
    
    # For compatibility with pytest's capsys fixture
    @pytest.fixture(autouse=True, scope="function")
    def _pt_app_session():
        with create_app_session():
            yield
  11. Add a bottom toolbar to a prompt

    main

    Pass a bottom_toolbar argument to prompt() to display information at the bottom of the interface. The argument can be:

    • Plain text.
    • Formatted text (e.g., HTML).
    • A callable that returns plain or formatted text. Callables are executed every time the prompt renders, allowing for dynamic updates.

    Note: The toolbar is erased when the prompt returns. By default, the toolbar uses a reversed style, so you may need to set the background color explicitly in your styles.

    from prompt_toolkit import prompt
    from prompt_toolkit.formatted_text import HTML
    
    def bottom_toolbar():
        return HTML('This is a <b><style bg="ansired">Toolbar</style></b>!')
    
    text = prompt("> ", bottom_toolbar=bottom_toolbar)
  12. Use input hooks to integrate an external event loop

    main

    Input hooks allow you to insert an external event loop into the prompt_toolkit (asyncio) event loop. This enables the external loop to run whenever prompt_toolkit is idle. This pattern is useful for applications that need to integrate GUI toolkits (like IPython) so that windows remain responsive while waiting for user input at the prompt.

    When using input hooks, the application will "trampoline" back and forth between the two event loops.

    Important Note on Windows: This implementation uses asyncio.SelectorEventLoop rather than asyncio.ProactorEventLoop on Windows.