⚡ Sangsakala Proxy 🏠 Home
Skip to content

SIGCONT crashes code.interact() shells due to conflicting pyrepl UnixConsole init #161254

Open

Description

@meshulam

Bug report

Bug description

Hosts that call sys.__interactivehook__ to enable history and tab completion, then start a REPL through code.interact() (or similar non-pyrepl input handler) will crash when the user suspends the process (ctrl-z / SIGTSTP) and resumes it (fg / SIGCONT), on Python ≥ 3.14:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
    import code, sys; sys.__interactivehook__(); code.interact()
  File "cpython/Lib/code.py", line 382, in interact
    console.interact(banner, exitmsg)
  File "cpython/Lib/code.py", line 271, in interact
    line = self.raw_input(prompt)
  File "cpython/Lib/code.py", line 340, in raw_input
    return input(prompt)
  File "cpython/Lib/_pyrepl/unix_console.py", line 273, in _sigcont_handler
    self.restore()
  File "cpython/Lib/_pyrepl/unix_console.py", line 518, in restore
    self.__write(ANSIColors.RESET)
  File "cpython/Lib/_pyrepl/unix_console.py", line 954, in __write
    self.__buffer.append((text, False))
AttributeError: 'UnixConsole' object has no attribute '_UnixConsole__buffer'. Did you mean '._UnixConsole__move' instead of '._UnixConsole__buffer'?

A second symptom (since 3.13, same root cause): history typed into a code.interact() shell is never saved, and existing history in ~/.python_history is not accessible.

With PYTHON_BASIC_REPL=1 both symptoms disappear.

Reproduction

In a pty/terminal:

python -c 'import code, sys; sys.__interactivehook__(); code.interact()'
[ctrl-Z to suspend to shell]
fg
-> resumes with "now exiting InteractiveConsole..." followed by traceback above

I discovered this in Django's shell, which uses this exact mechanism, and I imagine other REPL tools do as well. I believe this is a cpython issue because as far as I can tell code.interact() can't get the full benefits of interactive mode when pyrepl is enabled.

I have reproduced this on 3.14.9, 3.15.2, and main. I have a fix for the crash itself but the history issue is still outstanding.

Root cause

site.register_readline() (installed as sys.__interactivehook__) performs a half-install of pyrepl when CAN_USE_PYREPL:

  1. To load history, it calls _pyrepl.readline.read_history_file(), which goes through the module-level _ReadlineWrapper singleton — lazily constructing the process's only UnixConsole (_ReadlineWrapper.get_reader()).
  2. UnixConsole.__init__ ends with signal.signal(signal.SIGCONT, self._sigcont_handler). The handler calls self.restore(), then self.prepare().
  3. All the state the handler touches — __buffer, __svtermstate, the rendered screen — is created only in prepare(), which is called only from reader.readline().
  4. A code.interact() host never drives that reader: code.py imports the C readline module for builtins.input(), and _pyrepl.readline._setup() — the only place builtins.input is monkeypatched to pyrepl — never runs. So the hook-created console stays armed-but-never-prepared for the whole session.
  5. SIGCONT then fires the dormant console's handler → restore() → AttributeError on __buffer. An unhandled exception inside a signal handler during input() kills the shell.

The history-loss symptom is the same half-install acting through the C module: the hook loads history into the pyrepl reader while code.interact()'s input goes through C readline/libedit, and the atexit hook in site.register_readline() writes the pyrepl reader's (empty) history list back over the file.

The official REPL is unaffected because its console is driven through pyrepl's own reader.readline().

AI disclosure

I used an AI assistant to help find and explain the root cause.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic-replRelated to the interactive shelltype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions