Search code examples
pythonmacosttyfcntltermios

Disable DSUSP in Python


An OSX user submitted a bug that CTRL+Y causes a python terminal application to be suspended, via dsusp causing SIGTSTP to be sent when the Python program tried to read on stdin. The code below to solves the problem: (context)

import sys
import termios
if sys.platform == 'darwin':
    attrs = termios.tcgetattr(0)
    VDSUSP = termios.VSUSP + 1
    attrs[-1][VDSUSP] = 0
    termios.tcsetattr(0, termios.TCSANOW, attrs)
  • How can this feature (dsusp) be detected? Is there a heuristic I could use based on os.uname() or similar?
  • termios.VDSUSP doesn't exist, even on systems that have it. Is there a reason it's missing?
  • How widespread is this behavior of turning it off? Programs that use readline seem to ignore CTRL+Y on OSX, so it's at least reasonably common. I added stty dsusp undef to my .bashrc long ago so haven't noticed it.

To see this suspend behavior, run cat and enter CTRL+Y Return on OSX or something else with this feature.

$ cat
^Y

[1]+ Stopped             cat
$ fg
cat
cat: stdin: Resource temporarily unavailable

Solution

  • I am unable to check the actual behaviour for this as I do not have any computers with an operating system that would have the DSUSP behaviour. However while investigating the issue, I came across issue 7695 in Python bug tracker. It seems that the VDSUSP is available in termios only from 3.4 onwards.


    Glibc documentation, that says

    Macro: int VDSUSP

    This is the subscript for the DSUSP character in the special control character array. termios.c_cc[VDSUSP] holds the character itself.

    The DSUSP (suspend) character is recognized only if the implementation supports job control (see Job Control). It sends a SIGTSTP signal, like the SUSP character, but not right away—only when the program tries to read it as input. Not all systems with job control support DSUSP; only BSD-compatible systems (including GNU/Hurd systems).


    Thus I'd propose that you'd check the existence of VDSUSP in termios - it will be there from 3.4 onwards; and otherwise fall back to

    if any(i in sys.platform for i in ['bsd', 'darwin']):
    

    Which should match BSD and your OS X; the Hurd is a big question mark since I couldn't find out whether your fix would be correct at all there or not (I guess it would work similarly on all BSD versions).

    Even easier would be to just do:

    import subprocess
    import time
    from distutils.spawn import find_executable
    
    def exec_cmd(*args):
        p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, _ = p.communicate()
        return stdout
    
    if find_executable('stty'):
        modesave = exec_cmd('stty', '-g').strip()
        exec_cmd('stty', 'dsusp', 'undef')
        print("disabled ctrl-y")
        time.sleep(2)
        print("enabled ctrl-y")
        exec_cmd('stty', modesave)
        time.sleep(2)
        print("exiting")
    

    At least this does not blow up my Linux, and the stty command itself and -g etc are POSIX standards.