or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

container-operations.mdcore-pty-management.mdindex.mdmain-entry-points.mdstream-management.mdterminal-control.md

terminal-control.mddocs/

0

# Terminal Control

1

2

Low-level terminal management for handling raw mode, TTY size detection, and terminal attribute manipulation. This module provides the foundation for dockerpty's terminal control capabilities.

3

4

## Capabilities

5

6

### Terminal Class

7

8

Wrapper functionality to temporarily make the TTY raw. This is useful when streaming data from a pseudo-terminal into the TTY.

9

10

```python { .api }

11

class Terminal:

12

def __init__(self, fd, raw=True):

13

"""

14

Initialize a terminal for the tty with stdin attached to fd.

15

16

Initializing the Terminal has no immediate side effects. The start()

17

method must be invoked, or 'with Terminal:' used before the terminal is affected.

18

19

Parameters:

20

- fd: file-like object, stdin file descriptor for the terminal

21

- raw: bool, whether to operate in raw mode (default: True)

22

"""

23

24

def __enter__(self):

25

"""

26

Context manager entry - invoked when entering a 'with' block.

27

28

Calls start() automatically.

29

30

Returns:

31

self

32

"""

33

34

def __exit__(self, *_):

35

"""

36

Context manager exit - invoked when exiting a 'with' block.

37

38

Calls stop() automatically.

39

40

Parameters:

41

- *_: Exception information (ignored)

42

43

Returns:

44

None

45

"""

46

47

def israw(self):

48

"""

49

Returns True if the TTY should operate in raw mode.

50

51

Returns:

52

bool - True if terminal should use raw mode

53

"""

54

55

def start(self):

56

"""

57

Saves the current terminal attributes and makes the tty raw.

58

59

This method returns immediately. If the fd is not a TTY or raw=False,

60

no changes are made.

61

62

Returns:

63

None

64

"""

65

66

def stop(self):

67

"""

68

Restores the terminal attributes back to before setting raw mode.

69

70

If the raw terminal was not started, does nothing.

71

72

Returns:

73

None

74

"""

75

```

76

77

Usage example:

78

79

```python

80

import sys

81

from dockerpty.tty import Terminal

82

83

# Use as context manager (recommended)

84

with Terminal(sys.stdin, raw=True):

85

# Terminal is now in raw mode

86

# Keys are passed through without interpretation

87

do_pty_operations()

88

# Terminal attributes automatically restored

89

90

# Or manual management

91

terminal = Terminal(sys.stdin, raw=True)

92

terminal.start()

93

try:

94

do_pty_operations()

95

finally:

96

terminal.stop() # Always restore terminal

97

```

98

99

### TTY Size Detection

100

101

Function to determine the size of a TTY in rows and columns.

102

103

```python { .api }

104

def size(fd):

105

"""

106

Return a tuple (rows,cols) representing the size of the TTY fd.

107

108

The provided file descriptor should be the stdout stream of the TTY.

109

Uses TIOCGWINSZ ioctl to get terminal dimensions, with fallback to

110

environment variables LINES and COLUMNS.

111

112

Parameters:

113

- fd: file-like object, stdout stream of the TTY

114

115

Returns:

116

tuple - (rows, cols) as integers, or None if size cannot be determined

117

"""

118

```

119

120

Usage example:

121

122

```python

123

import sys

124

from dockerpty.tty import size

125

126

# Get current terminal size

127

terminal_size = size(sys.stdout)

128

if terminal_size:

129

rows, cols = terminal_size

130

print(f"Terminal is {cols}x{rows}")

131

else:

132

print("Not running in a terminal")

133

```

134

135

### File Descriptor Control

136

137

Utility functions for managing file descriptor blocking modes and stream selection.

138

139

```python { .api }

140

def set_blocking(fd, blocking=True):

141

"""

142

Set the given file-descriptor blocking or non-blocking.

143

144

Uses fcntl to modify the O_NONBLOCK flag on the file descriptor.

145

146

Parameters:

147

- fd: file descriptor or file-like object with fileno() method

148

- blocking: bool, True for blocking mode, False for non-blocking (default: True)

149

150

Returns:

151

bool - original blocking status (True if was blocking, False if was non-blocking)

152

"""

153

154

def select(read_streams, write_streams, timeout=0):

155

"""

156

Select the streams ready for reading, and streams ready for writing.

157

158

Uses select.select() internally but only returns two lists of ready streams.

159

Handles POSIX signal interrupts (EINTR) gracefully by returning empty lists.

160

161

Parameters:

162

- read_streams: list of file-like objects to check for read readiness

163

- write_streams: list of file-like objects to check for write readiness

164

- timeout: float, timeout in seconds (default: 0 for immediate return)

165

166

Returns:

167

tuple - (ready_read_streams, ready_write_streams)

168

169

Raises:

170

select.error - for non-EINTR select errors

171

"""

172

```

173

174

Usage examples:

175

176

```python

177

import sys

178

import socket

179

from dockerpty.io import set_blocking, select

180

181

# Make stdin non-blocking

182

original_blocking = set_blocking(sys.stdin, False)

183

184

# Use select to wait for input

185

ready_read, ready_write = select([sys.stdin], [], timeout=5.0)

186

if ready_read:

187

data = sys.stdin.read()

188

189

# Restore original blocking mode

190

set_blocking(sys.stdin, original_blocking)

191

```

192

193

## Raw Mode Terminal Operation

194

195

### What Raw Mode Does

196

197

Raw mode disables terminal input processing, meaning:

198

199

- **No line buffering**: Characters are available immediately, not after Enter

200

- **No signal generation**: Ctrl+C, Ctrl+Z don't generate signals

201

- **No character interpretation**: No backspace processing, no echo

202

- **Direct key forwarding**: All keystrokes passed through to the application

203

204

This is essential for dockerpty because it needs to forward all user input directly to the container's PTY without the host terminal interpreting special keys.

205

206

### Terminal Attribute Management

207

208

The Terminal class manages terminal attributes using the `termios` module:

209

210

1. **Save current attributes**: `termios.tcgetattr()` saves original terminal state

211

2. **Set raw mode**: `tty.setraw()` configures raw mode attributes

212

3. **Restore on exit**: `termios.tcsetattr()` restores original attributes

213

214

This ensures the user's terminal is always restored to its original state, even if dockerpty exits unexpectedly.

215

216

## Window Size Handling

217

218

### Size Detection Methods

219

220

The `size()` function uses multiple methods to determine terminal size:

221

222

1. **TIOCGWINSZ ioctl**: Primary method using system call to get window size

223

2. **Environment variables**: Fallback to `LINES` and `COLUMNS` environment variables

224

3. **Return None**: If neither method works (not a terminal)

225

226

### Automatic Resize Handling

227

228

dockerpty uses SIGWINCH signal handling to automatically resize the container's PTY when the terminal window changes size:

229

230

1. **Signal installation**: WINCHHandler installs SIGWINCH handler

231

2. **Size detection**: Handler calls `tty.size()` to get new dimensions

232

3. **PTY resize**: Calls Docker API to resize container's PTY to match

233

4. **Signal restoration**: Original SIGWINCH handler restored on exit

234

235

## Error Handling

236

237

### Terminal Detection

238

239

Functions safely handle non-terminal file descriptors:

240

- `os.isatty()` checks if fd is a terminal before terminal operations

241

- Operations are skipped gracefully if not running in a terminal

242

- No errors are raised for non-terminal usage

243

244

### Signal Interruption

245

246

The `select()` function handles POSIX signal interruption:

247

- Catches `errno.EINTR` from interrupted system calls

248

- Returns empty lists to allow graceful continuation

249

- Re-raises other select errors that indicate real problems

250

251

### Attribute Restoration

252

253

Terminal attribute restoration is robust:

254

- Attributes are saved before any modifications

255

- Restoration happens in exception handlers and context manager exits

256

- Multiple restoration calls are safe (idempotent)

257

- Original handler restoration prevents signal handler leaks