Opened 16 months ago

Last modified 7 months ago

#30416 new Bug

Runserver's reloading mechanism should restore terminal state completely

Reported by: Daniel Hahler Owned by: nobody
Component: Core (Management commands) Version: 2.2
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Daniel Hahler)

Currently it only ensures that echo is on, but not that e.g. Ctrl-C works etc.

This can be triggered when using pdb++, which uses pyrepl itself: it puts the terminal into raw state.

If the reloader then kicks in while at the prompt this will not be restored (except for echo).

Related issue, which mentions that a better mechanism would be good: https://code.djangoproject.com/ticket/15880

btw: pyrepl has no chance to restore itself: listening to SIGTERM is not possible in threads apparently, and e.g. atexit is also not triggered there.

Change History (6)

comment:1 Changed 16 months ago by Daniel Hahler

Description: modified (diff)

comment:2 Changed 16 months ago by Carlton Gibson

Triage Stage: UnreviewedAccepted

Hi Daniel. I'm going to Accept this, because I see where you're coming from, but I would be grateful if you could add a detailed set of reproduce steps and/or an example project so that someone picking it up can get going on it easily.

Thanks.

comment:3 Changed 8 months ago by Carlton Gibson

Resolution: needsinfo
Status: newclosed

Without more details (after 7 months) this isn't really actionable. Happy to see it taken on, with a concrete action plan in place.

comment:4 Changed 8 months ago by Daniel Hahler

@Carlton

I would be grateful if you could add a detailed set of reproduce steps and/or an example project so that someone picking it up can get going on it easily.

I think everybody being able to fix this has enough info in the original issue already.

I've mentioned one way to trigger this: use pdb++ (with pyrepl), then have the reloader kick in while in the debugger.
A simpler method would be using termios.tcsetattr directly to put it into e.g. raw state, then have the reloader kick in.

Therefore I suggest re-opening it already - it's an existing issue after all, and was mentioned in another issue (linked above) already.

comment:5 Changed 8 months ago by Daniel Hahler

Also note that the reloader should not really kick in in the first place when the debugger is used / only when the request finished.
(The use case is having a pdb.set_trace(), the debugger being used, and then saving some file - it should not "kill" the debugger then (resulting in this issue))
Therefore waiting for the request / debugger to be finished before reloading would work around this issues (allowing the debugger (pyrepl in this case) to restore the terminal state itself).

Related: https://github.com/pallets/werkzeug/pull/1525 (for Werkzeug's reloader, which is similar IIRC).

comment:6 Changed 7 months ago by Daniel Hahler

Resolution: needsinfo
Status: closednew

I've rebased and added a test to https://github.com/django/django/pull/11291.

To reproduce this:

  1. install pdbpp (with pyrepl)
  2. put pdb.set_trace() in a view
  3. manage.py runserver
  4. trigger the reloader or use ctrl-c directly
  5. see that ctrl-c does not work on your shell anymore until you use reset

Alternatively:

  1. add the following code to a view or similar:
            import sys
            import termios
    
            fd = sys.stdin.fileno()
            attr = termios.tcgetattr(fd).copy()
            attr[3] &= ~(
                termios.ICANON | termios.ECHO | termios.IEXTEN | (termios.ISIG * 1)
            )
    
            termios.tcsetattr(fd, termios.TCSANOW, attr)
    
  2. trigger the code
  3. observer that ctrl-c does not work anymore

With the patch/fix it will work again after the reloader gets triggered, and/or when killing the runserver manually.

Note: See TracTickets for help on using tickets.
Back to Top