121 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			121 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Python
		
	
	
	
| 
 | |
| """ boxing - wrapping process with another process so we can run
 | |
| a process inside and see if it crashes
 | |
| """
 | |
| 
 | |
| import py
 | |
| import os
 | |
| import sys
 | |
| import marshal
 | |
| from py.__.test import config as pytestconfig
 | |
| 
 | |
| PYTESTSTDOUT = "pyteststdout"
 | |
| PYTESTSTDERR = "pyteststderr"
 | |
| PYTESTRETVAL = "pytestretval"
 | |
| 
 | |
| import tempfile
 | |
| import itertools
 | |
| from StringIO import StringIO
 | |
| 
 | |
| counter = itertools.count().next
 | |
| 
 | |
| class FileBox(object):
 | |
|     def __init__(self, fun, args=None, kwargs=None, config=None):
 | |
|         if args is None:
 | |
|             args = []
 | |
|         if kwargs is None:
 | |
|             kwargs = {}
 | |
|         self.fun = fun
 | |
|         self.config = config
 | |
|         assert self.config
 | |
|         self.args = args
 | |
|         self.kwargs = kwargs
 | |
|     
 | |
|     def run(self, continuation=False):
 | |
|         # XXX we should not use py.test.ensuretemp here
 | |
|         count = counter()
 | |
|         tempdir = py.test.ensuretemp("box%d" % count)
 | |
|         self.tempdir = tempdir
 | |
|         self.PYTESTRETVAL = tempdir.join('retval')
 | |
|         self.PYTESTSTDOUT = tempdir.join('stdout')
 | |
|         self.PYTESTSTDERR = tempdir.join('stderr')
 | |
| 
 | |
|         nice_level = self.config.getvalue('dist_nicelevel')
 | |
|         pid = os.fork()
 | |
|         if pid:
 | |
|             if not continuation:
 | |
|                 self.parent(pid)
 | |
|             else:
 | |
|                 return self.parent, pid
 | |
|         else:
 | |
|             try:
 | |
|                 outcome = self.children(nice_level)
 | |
|             except:
 | |
|                 excinfo = py.code.ExceptionInfo()
 | |
|                 x = open("/tmp/traceback", "w")
 | |
|                 print >>x, "Internal box error"
 | |
|                 for i in excinfo.traceback:
 | |
|                     print >>x, str(i)[2:-1]
 | |
|                 print >>x, excinfo
 | |
|                 x.close()
 | |
|                 os._exit(1)
 | |
|             os.close(1)
 | |
|             os.close(2)
 | |
|             os._exit(0)
 | |
|         return pid
 | |
|     
 | |
|     def children(self, nice_level):
 | |
|         # right now we need to call a function, but first we need to
 | |
|         # map all IO that might happen
 | |
|         # make sure sys.stdout points to file descriptor one
 | |
|         sys.stdout = stdout = self.PYTESTSTDOUT.open('w')
 | |
|         sys.stdout.flush()
 | |
|         fdstdout = stdout.fileno()
 | |
|         if fdstdout != 1:
 | |
|             os.dup2(fdstdout, 1)
 | |
|         sys.stderr = stderr = self.PYTESTSTDERR.open('w')
 | |
|         fdstderr = stderr.fileno()
 | |
|         if fdstderr != 2:
 | |
|             os.dup2(fdstderr, 2)
 | |
|         retvalf = self.PYTESTRETVAL.open("w")
 | |
|         try:
 | |
|             if nice_level:
 | |
|                 os.nice(nice_level)
 | |
|             # with fork() we have duplicated py.test's basetemp
 | |
|             # directory so we want to set it manually here. 
 | |
|             # this may be expensive for some test setups, 
 | |
|             # but that is what you get with boxing. 
 | |
|             # XXX but we are called in more than strict boxing
 | |
|             # mode ("AsyncExecutor") so we can't do the following without
 | |
|             # inflicting on --dist speed, hum: 
 | |
|             # pytestconfig.basetemp = self.tempdir.join("childbasetemp")
 | |
|             retval = self.fun(*self.args, **self.kwargs)
 | |
|             retvalf.write(marshal.dumps(retval))
 | |
|         finally:
 | |
|             stdout.close()
 | |
|             stderr.close()
 | |
|             retvalf.close()
 | |
|         os._exit(0)
 | |
|     
 | |
|     def parent(self, pid, waiter=os.waitpid):
 | |
|         pid, exitstat = waiter(pid, 0)
 | |
|         self.signal = exitstat & 0x7f
 | |
|         self.exitstat = exitstat & 0xff00
 | |
| 
 | |
|         
 | |
|         if not exitstat:
 | |
|             retval = self.PYTESTRETVAL.open()
 | |
|             try:
 | |
|                 retval_data = retval.read()
 | |
|             finally:
 | |
|                 retval.close()
 | |
|             self.retval = marshal.loads(retval_data)
 | |
|         else:
 | |
|             self.retval = None
 | |
|         
 | |
|         self.stdoutrepr = self.PYTESTSTDOUT.read()
 | |
|         self.stderrrepr = self.PYTESTSTDERR.read()
 | |
|         return self.stdoutrepr, self.stderrrepr
 | |
| 
 | |
| Box = FileBox
 |