Hide keyboard shortcuts

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

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

import os 

import sys 

import py 

import tempfile 

 

try: 

from io import StringIO 

except ImportError: 

from StringIO import StringIO 

 

if sys.version_info < (3,0): 

class TextIO(StringIO): 

def write(self, data): 

if not isinstance(data, unicode): 

data = unicode(data, getattr(self, '_encoding', 'UTF-8'), 'replace') 

StringIO.write(self, data) 

else: 

TextIO = StringIO 

 

try: 

from io import BytesIO 

except ImportError: 

class BytesIO(StringIO): 

def write(self, data): 

if isinstance(data, unicode): 

raise TypeError("not a byte value: %r" %(data,)) 

StringIO.write(self, data) 

 

patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'} 

 

class FDCapture: 

""" Capture IO to/from a given os-level filedescriptor. """ 

 

def __init__(self, targetfd, tmpfile=None, now=True, patchsys=False): 

""" save targetfd descriptor, and open a new 

temporary file there. If no tmpfile is 

specified a tempfile.Tempfile() will be opened 

in text mode. 

""" 

self.targetfd = targetfd 

if tmpfile is None and targetfd != 0: 

f = tempfile.TemporaryFile('wb+') 

tmpfile = dupfile(f, encoding="UTF-8") 

f.close() 

self.tmpfile = tmpfile 

self._savefd = os.dup(self.targetfd) 

if patchsys: 

self._oldsys = getattr(sys, patchsysdict[targetfd]) 

if now: 

self.start() 

 

def start(self): 

try: 

os.fstat(self._savefd) 

except OSError: 

raise ValueError("saved filedescriptor not valid, " 

"did you call start() twice?") 

if self.targetfd == 0 and not self.tmpfile: 

fd = os.open(devnullpath, os.O_RDONLY) 

os.dup2(fd, 0) 

os.close(fd) 

if hasattr(self, '_oldsys'): 

setattr(sys, patchsysdict[self.targetfd], DontReadFromInput()) 

else: 

os.dup2(self.tmpfile.fileno(), self.targetfd) 

if hasattr(self, '_oldsys'): 

setattr(sys, patchsysdict[self.targetfd], self.tmpfile) 

 

def done(self): 

""" unpatch and clean up, returns the self.tmpfile (file object) 

""" 

os.dup2(self._savefd, self.targetfd) 

os.close(self._savefd) 

if self.targetfd != 0: 

self.tmpfile.seek(0) 

if hasattr(self, '_oldsys'): 

setattr(sys, patchsysdict[self.targetfd], self._oldsys) 

return self.tmpfile 

 

def writeorg(self, data): 

""" write a string to the original file descriptor 

""" 

tempfp = tempfile.TemporaryFile() 

try: 

os.dup2(self._savefd, tempfp.fileno()) 

tempfp.write(data) 

finally: 

tempfp.close() 

 

 

def dupfile(f, mode=None, buffering=0, raising=False, encoding=None): 

""" return a new open file object that's a duplicate of f 

 

mode is duplicated if not given, 'buffering' controls 

buffer size (defaulting to no buffering) and 'raising' 

defines whether an exception is raised when an incompatible 

file object is passed in (if raising is False, the file 

object itself will be returned) 

""" 

try: 

fd = f.fileno() 

mode = mode or f.mode 

except AttributeError: 

if raising: 

raise 

return f 

newfd = os.dup(fd) 

if sys.version_info >= (3,0): 

if encoding is not None: 

mode = mode.replace("b", "") 

buffering = True 

return os.fdopen(newfd, mode, buffering, encoding, closefd=True) 

else: 

f = os.fdopen(newfd, mode, buffering) 

if encoding is not None: 

return EncodedFile(f, encoding) 

return f 

 

class EncodedFile(object): 

def __init__(self, _stream, encoding): 

self._stream = _stream 

self.encoding = encoding 

 

def write(self, obj): 

if isinstance(obj, unicode): 

obj = obj.encode(self.encoding) 

elif isinstance(obj, str): 

pass 

else: 

obj = str(obj) 

self._stream.write(obj) 

 

def writelines(self, linelist): 

data = ''.join(linelist) 

self.write(data) 

 

def __getattr__(self, name): 

return getattr(self._stream, name) 

 

class Capture(object): 

def call(cls, func, *args, **kwargs): 

""" return a (res, out, err) tuple where 

out and err represent the output/error output 

during function execution. 

call the given function with args/kwargs 

and capture output/error during its execution. 

""" 

so = cls() 

try: 

res = func(*args, **kwargs) 

finally: 

out, err = so.reset() 

return res, out, err 

call = classmethod(call) 

 

def reset(self): 

""" reset sys.stdout/stderr and return captured output as strings. """ 

if hasattr(self, '_reset'): 

raise ValueError("was already reset") 

self._reset = True 

outfile, errfile = self.done(save=False) 

out, err = "", "" 

if outfile and not outfile.closed: 

out = outfile.read() 

outfile.close() 

if errfile and errfile != outfile and not errfile.closed: 

err = errfile.read() 

errfile.close() 

return out, err 

 

def suspend(self): 

""" return current snapshot captures, memorize tempfiles. """ 

outerr = self.readouterr() 

outfile, errfile = self.done() 

return outerr 

 

 

class StdCaptureFD(Capture): 

""" This class allows to capture writes to FD1 and FD2 

and may connect a NULL file to FD0 (and prevent 

reads from sys.stdin). If any of the 0,1,2 file descriptors 

is invalid it will not be captured. 

""" 

def __init__(self, out=True, err=True, mixed=False, 

in_=True, patchsys=True, now=True): 

self._options = { 

"out": out, 

"err": err, 

"mixed": mixed, 

"in_": in_, 

"patchsys": patchsys, 

"now": now, 

} 

self._save() 

if now: 

self.startall() 

 

def _save(self): 

in_ = self._options['in_'] 

out = self._options['out'] 

err = self._options['err'] 

mixed = self._options['mixed'] 

patchsys = self._options['patchsys'] 

if in_: 

try: 

self.in_ = FDCapture(0, tmpfile=None, now=False, 

patchsys=patchsys) 

except OSError: 

pass 

if out: 

tmpfile = None 

if hasattr(out, 'write'): 

tmpfile = out 

try: 

self.out = FDCapture(1, tmpfile=tmpfile, 

now=False, patchsys=patchsys) 

self._options['out'] = self.out.tmpfile 

except OSError: 

pass 

if err: 

if out and mixed: 

tmpfile = self.out.tmpfile 

elif hasattr(err, 'write'): 

tmpfile = err 

else: 

tmpfile = None 

try: 

self.err = FDCapture(2, tmpfile=tmpfile, 

now=False, patchsys=patchsys) 

self._options['err'] = self.err.tmpfile 

except OSError: 

pass 

 

def startall(self): 

if hasattr(self, 'in_'): 

self.in_.start() 

if hasattr(self, 'out'): 

self.out.start() 

if hasattr(self, 'err'): 

self.err.start() 

 

def resume(self): 

""" resume capturing with original temp files. """ 

self.startall() 

 

def done(self, save=True): 

""" return (outfile, errfile) and stop capturing. """ 

outfile = errfile = None 

if hasattr(self, 'out') and not self.out.tmpfile.closed: 

outfile = self.out.done() 

if hasattr(self, 'err') and not self.err.tmpfile.closed: 

errfile = self.err.done() 

if hasattr(self, 'in_'): 

tmpfile = self.in_.done() 

if save: 

self._save() 

return outfile, errfile 

 

def readouterr(self): 

""" return snapshot value of stdout/stderr capturings. """ 

if hasattr(self, "out"): 

out = self._readsnapshot(self.out.tmpfile) 

else: 

out = "" 

if hasattr(self, "err"): 

err = self._readsnapshot(self.err.tmpfile) 

else: 

err = "" 

return [out, err] 

 

def _readsnapshot(self, f): 

f.seek(0) 

res = f.read() 

enc = getattr(f, "encoding", None) 

if enc: 

res = py.builtin._totext(res, enc, "replace") 

f.truncate(0) 

f.seek(0) 

return res 

 

 

class StdCapture(Capture): 

""" This class allows to capture writes to sys.stdout|stderr "in-memory" 

and will raise errors on tries to read from sys.stdin. It only 

modifies sys.stdout|stderr|stdin attributes and does not 

touch underlying File Descriptors (use StdCaptureFD for that). 

""" 

def __init__(self, out=True, err=True, in_=True, mixed=False, now=True): 

self._oldout = sys.stdout 

self._olderr = sys.stderr 

self._oldin = sys.stdin 

if out and not hasattr(out, 'file'): 

out = TextIO() 

self.out = out 

if err: 

if mixed: 

err = out 

elif not hasattr(err, 'write'): 

err = TextIO() 

self.err = err 

self.in_ = in_ 

if now: 

self.startall() 

 

def startall(self): 

if self.out: 

sys.stdout = self.out 

if self.err: 

sys.stderr = self.err 

if self.in_: 

sys.stdin = self.in_ = DontReadFromInput() 

 

def done(self, save=True): 

""" return (outfile, errfile) and stop capturing. """ 

outfile = errfile = None 

if self.out and not self.out.closed: 

sys.stdout = self._oldout 

outfile = self.out 

outfile.seek(0) 

if self.err and not self.err.closed: 

sys.stderr = self._olderr 

errfile = self.err 

errfile.seek(0) 

if self.in_: 

sys.stdin = self._oldin 

return outfile, errfile 

 

def resume(self): 

""" resume capturing with original temp files. """ 

self.startall() 

 

def readouterr(self): 

""" return snapshot value of stdout/stderr capturings. """ 

out = err = "" 

if self.out: 

out = self.out.getvalue() 

self.out.truncate(0) 

self.out.seek(0) 

if self.err: 

err = self.err.getvalue() 

self.err.truncate(0) 

self.err.seek(0) 

return out, err 

 

class DontReadFromInput: 

"""Temporary stub class. Ideally when stdin is accessed, the 

capturing should be turned off, with possibly all data captured 

so far sent to the screen. This should be configurable, though, 

because in automated test runs it is better to crash than 

hang indefinitely. 

""" 

def read(self, *args): 

raise IOError("reading from stdin while output is captured") 

readline = read 

readlines = read 

__iter__ = read 

 

def fileno(self): 

raise ValueError("redirected Stdin is pseudofile, has no fileno()") 

def isatty(self): 

return False 

def close(self): 

pass 

 

try: 

devnullpath = os.devnull 

except AttributeError: 

if os.name == 'nt': 

devnullpath = 'NUL' 

else: 

devnullpath = '/dev/null'