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 

 

18if 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'