Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

""" 

 

Helper functions for writing to terminals and files. 

 

""" 

 

 

import sys, os 

import py 

py3k = sys.version_info[0] >= 3 

from py.builtin import text, bytes 

 

win32_and_ctypes = False 

colorama = None 

16if sys.platform == "win32": 

    try: 

        import colorama 

    except ImportError: 

        try: 

            import ctypes 

            win32_and_ctypes = True 

        except ImportError: 

            pass 

 

 

def _getdimensions(): 

    import termios,fcntl,struct 

    call = fcntl.ioctl(1,termios.TIOCGWINSZ,"\000"*8) 

    height,width = struct.unpack( "hhhh", call ) [:2] 

    return height, width 

 

 

def get_terminal_width(): 

    height = width = 0 

    try: 

        height, width = _getdimensions() 

    except py.builtin._sysex: 

        raise 

    except: 

        # pass to fallback below 

        pass 

 

47    if width == 0: 

        # FALLBACK: 

        # * some exception happened 

        # * or this is emacs terminal which reports (0,0) 

        width = int(os.environ.get('COLUMNS', 80)) 

 

    # XXX the windows getdimensions may be bogus, let's sanify a bit 

51    if width < 40: 

        width = 80 

    return width 

 

terminal_width = get_terminal_width() 

 

# XXX unify with _escaped func below 

def ansi_print(text, esc, file=None, newline=True, flush=False): 

    if file is None: 

        file = sys.stderr 

    text = text.rstrip() 

    if esc and not isinstance(esc, tuple): 

        esc = (esc,) 

    if esc and sys.platform != "win32" and file.isatty(): 

        text = (''.join(['\x1b[%sm' % cod for cod in esc])  + 

                text + 

                '\x1b[0m')     # ANSI color code "reset" 

    if newline: 

        text += '\n' 

 

    if esc and win32_and_ctypes and file.isatty(): 

        if 1 in esc: 

            bold = True 

            esc = tuple([x for x in esc if x != 1]) 

        else: 

            bold = False 

        esctable = {()   : FOREGROUND_WHITE,                 # normal 

                    (31,): FOREGROUND_RED,                   # red 

                    (32,): FOREGROUND_GREEN,                 # green 

                    (33,): FOREGROUND_GREEN|FOREGROUND_RED,  # yellow 

                    (34,): FOREGROUND_BLUE,                  # blue 

                    (35,): FOREGROUND_BLUE|FOREGROUND_RED,   # purple 

                    (36,): FOREGROUND_BLUE|FOREGROUND_GREEN, # cyan 

                    (37,): FOREGROUND_WHITE,                 # white 

                    (39,): FOREGROUND_WHITE,                 # reset 

                    } 

        attr = esctable.get(esc, FOREGROUND_WHITE) 

        if bold: 

            attr |= FOREGROUND_INTENSITY 

        STD_OUTPUT_HANDLE = -11 

        STD_ERROR_HANDLE = -12 

        if file is sys.stderr: 

            handle = GetStdHandle(STD_ERROR_HANDLE) 

        else: 

            handle = GetStdHandle(STD_OUTPUT_HANDLE) 

        oldcolors = GetConsoleInfo(handle).wAttributes 

        attr |= (oldcolors & 0x0f0) 

        SetConsoleTextAttribute(handle, attr) 

        while len(text) > 32768: 

            file.write(text[:32768]) 

            text = text[32768:] 

        if text: 

            file.write(text) 

        SetConsoleTextAttribute(handle, oldcolors) 

    else: 

        file.write(text) 

 

    if flush: 

        file.flush() 

 

def should_do_markup(file): 

112    if os.environ.get('PY_COLORS') == '1': 

        return True 

114    if os.environ.get('PY_COLORS') == '0': 

        return False 

    return hasattr(file, 'isatty') and file.isatty() \ 

           and os.environ.get('TERM') != 'dumb' \ 

           and not (sys.platform.startswith('java') and os._name == 'nt') 

 

class TerminalWriter(object): 

    _esctable = dict(black=30, red=31, green=32, yellow=33, 

                     blue=34, purple=35, cyan=36, white=37, 

                     Black=40, Red=41, Green=42, Yellow=43, 

                     Blue=44, Purple=45, Cyan=46, White=47, 

                     bold=1, light=2, blink=5, invert=7) 

 

    # XXX deprecate stringio argument 

    def __init__(self, file=None, stringio=False, encoding=None): 

129        if file is None: 

            if stringio: 

                self.stringio = file = py.io.TextIO() 

            else: 

                file = py.std.sys.stdout 

135        elif py.builtin.callable(file) and not ( 

             hasattr(file, "write") and hasattr(file, "flush")): 

            file = WriteFile(file, encoding=encoding) 

137        if hasattr(file, "isatty") and file.isatty() and colorama: 

            file = colorama.AnsiToWin32(file).stream 

        self.encoding = encoding or getattr(file, 'encoding', "utf-8") 

        self._file = file 

        self.fullwidth = get_terminal_width() 

        self.hasmarkup = should_do_markup(file) 

        self._lastlen = 0 

 

    def _escaped(self, text, esc): 

148        if esc and self.hasmarkup: 

            text = (''.join(['\x1b[%sm' % cod for cod in esc])  + 

                text +'\x1b[0m') 

        return text 

 

    def markup(self, text, **kw): 

        esc = [] 

        for name in kw: 

154            if name not in self._esctable: 

                raise ValueError("unknown markup: %r" %(name,)) 

152            if kw[name]: 

                esc.append(self._esctable[name]) 

        return self._escaped(text, tuple(esc)) 

 

    def sep(self, sepchar, title=None, fullwidth=None, **kw): 

164        if fullwidth is None: 

            fullwidth = self.fullwidth 

        # the goal is to have the line be as long as possible 

        # under the condition that len(line) <= fullwidth 

169        if sys.platform == "win32": 

            # if we print in the last column on windows we are on a 

            # new line but there is no way to verify/neutralize this 

            # (we may not know the exact line width) 

            # so let's be defensive to avoid empty lines in the output 

            fullwidth -= 1 

181        if title is not None: 

            # we want 2 + 2*len(fill) + len(title) <= fullwidth 

            # i.e.    2 + 2*len(sepchar)*N + len(title) <= fullwidth 

            #         2*len(sepchar)*N <= fullwidth - len(title) - 2 

            #         N <= (fullwidth - len(title) - 2) // (2*len(sepchar)) 

            N = (fullwidth - len(title) - 2) // (2*len(sepchar)) 

            fill = sepchar * N 

            line = "%s %s %s" % (fill, title, fill) 

        else: 

            # we want len(sepchar)*N <= fullwidth 

            # i.e.    N <= fullwidth // len(sepchar) 

            line = sepchar * (fullwidth // len(sepchar)) 

        # in some situations there is room for an extra sepchar at the right, 

        # in particular if we consider that with a sepchar like "_ " the 

        # trailing space is not important at the end of the line 

188        if len(line) + len(sepchar.rstrip()) <= fullwidth: 

            line += sepchar.rstrip() 

 

        self.line(line, **kw) 

 

    def write(self, msg, **kw): 

        if msg: 

193            if not isinstance(msg, (bytes, text)): 

                msg = text(msg) 

            if self.hasmarkup and kw: 

                markupmsg = self.markup(msg, **kw) 

            else: 

                markupmsg = msg 

            write_out(self._file, markupmsg) 

 

    def line(self, s='', **kw): 

        self.write(s, **kw) 

        self._checkfill(s) 

        self.write('\n') 

 

    def reline(self, line, **kw): 

        if not self.hasmarkup: 

            raise ValueError("cannot use rewrite-line without terminal") 

        self.write(line, **kw) 

        self._checkfill(line) 

        self.write('\r') 

        self._lastlen = len(line) 

 

    def _checkfill(self, line): 

        diff2last = self._lastlen - len(line) 

216        if diff2last > 0: 

            self.write(" " * diff2last) 

 

class Win32ConsoleWriter(TerminalWriter): 

    def write(self, msg, **kw): 

        if msg: 

            if not isinstance(msg, (bytes, text)): 

                msg = text(msg) 

            oldcolors = None 

            if self.hasmarkup and kw: 

                handle = GetStdHandle(STD_OUTPUT_HANDLE) 

                oldcolors = GetConsoleInfo(handle).wAttributes 

                default_bg = oldcolors & 0x00F0 

                attr = default_bg 

                if kw.pop('bold', False): 

                    attr |= FOREGROUND_INTENSITY 

 

                if kw.pop('red', False): 

                    attr |= FOREGROUND_RED 

                elif kw.pop('blue', False): 

                    attr |= FOREGROUND_BLUE 

                elif kw.pop('green', False): 

                    attr |= FOREGROUND_GREEN 

                elif kw.pop('yellow', False): 

                    attr |= FOREGROUND_GREEN|FOREGROUND_RED 

                else: 

                    attr |= oldcolors & 0x0007 

 

                SetConsoleTextAttribute(handle, attr) 

            write_out(self._file, msg) 

            if oldcolors: 

                SetConsoleTextAttribute(handle, oldcolors) 

 

class WriteFile(object): 

    def __init__(self, writemethod, encoding=None): 

        self.encoding = encoding 

        self._writemethod = writemethod 

 

    def write(self, data): 

        if self.encoding: 

            data = data.encode(self.encoding, "replace") 

        self._writemethod(data) 

 

    def flush(self): 

        return 

 

 

263if win32_and_ctypes: 

    TerminalWriter = Win32ConsoleWriter 

    import ctypes 

    from ctypes import wintypes 

 

    # ctypes access to the Windows console 

    STD_OUTPUT_HANDLE = -11 

    STD_ERROR_HANDLE  = -12 

    FOREGROUND_BLACK     = 0x0000 # black text 

    FOREGROUND_BLUE      = 0x0001 # text color contains blue. 

    FOREGROUND_GREEN     = 0x0002 # text color contains green. 

    FOREGROUND_RED       = 0x0004 # text color contains red. 

    FOREGROUND_WHITE     = 0x0007 

    FOREGROUND_INTENSITY = 0x0008 # text color is intensified. 

    BACKGROUND_BLACK     = 0x0000 # background color black 

    BACKGROUND_BLUE      = 0x0010 # background color contains blue. 

    BACKGROUND_GREEN     = 0x0020 # background color contains green. 

    BACKGROUND_RED       = 0x0040 # background color contains red. 

    BACKGROUND_WHITE     = 0x0070 

    BACKGROUND_INTENSITY = 0x0080 # background color is intensified. 

 

    SHORT = ctypes.c_short 

    class COORD(ctypes.Structure): 

        _fields_ = [('X', SHORT), 

                    ('Y', SHORT)] 

    class SMALL_RECT(ctypes.Structure): 

        _fields_ = [('Left', SHORT), 

                    ('Top', SHORT), 

                    ('Right', SHORT), 

                    ('Bottom', SHORT)] 

    class CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure): 

        _fields_ = [('dwSize', COORD), 

                    ('dwCursorPosition', COORD), 

                    ('wAttributes', wintypes.WORD), 

                    ('srWindow', SMALL_RECT), 

                    ('dwMaximumWindowSize', COORD)] 

 

    _GetStdHandle = ctypes.windll.kernel32.GetStdHandle 

    _GetStdHandle.argtypes = [wintypes.DWORD] 

    _GetStdHandle.restype = wintypes.HANDLE 

    def GetStdHandle(kind): 

        return _GetStdHandle(kind) 

 

    SetConsoleTextAttribute = ctypes.windll.kernel32.SetConsoleTextAttribute 

    SetConsoleTextAttribute.argtypes = [wintypes.HANDLE, wintypes.WORD] 

    SetConsoleTextAttribute.restype = wintypes.BOOL 

 

    _GetConsoleScreenBufferInfo = \ 

        ctypes.windll.kernel32.GetConsoleScreenBufferInfo 

    _GetConsoleScreenBufferInfo.argtypes = [wintypes.HANDLE, 

                                ctypes.POINTER(CONSOLE_SCREEN_BUFFER_INFO)] 

    _GetConsoleScreenBufferInfo.restype = wintypes.BOOL 

    def GetConsoleInfo(handle): 

        info = CONSOLE_SCREEN_BUFFER_INFO() 

        _GetConsoleScreenBufferInfo(handle, ctypes.byref(info)) 

        return info 

 

    def _getdimensions(): 

        handle = GetStdHandle(STD_OUTPUT_HANDLE) 

        info = GetConsoleInfo(handle) 

        # Substract one from the width, otherwise the cursor wraps 

        # and the ending \n causes an empty line to display. 

        return info.dwSize.Y, info.dwSize.X - 1 

 

def write_out(fil, msg): 

    # XXX sometimes "msg" is of type bytes, sometimes text which 

    # complicates the situation.  Should we try to enforce unicode? 

    try: 

        # on py27 and above writing out to sys.stdout with an encoding 

        # should usually work for unicode messages (if the encoding is 

        # capable of it) 

        fil.write(msg) 

    except UnicodeEncodeError: 

        # on py26 it might not work because stdout expects bytes 

        if fil.encoding: 

            try: 

                fil.write(msg.encode(fil.encoding)) 

            except UnicodeEncodeError: 

                # it might still fail if the encoding is not capable 

                pass 

            else: 

                fil.flush() 

                return 

        # fallback: escape all unicode characters 

        msg = msg.encode("unicode-escape").decode("ascii") 

        fil.write(msg) 

    fil.flush()