@@ -120,6 +120,47 @@ def isrunning(self, ignore_zombies=True):
120120 )
121121
122122
123+ class XProcessResources :
124+ """Resources used by a running process.
125+ Each time XProcess.ensure is called a single XProcessResources
126+ instance will be created and all resources used by the started
127+ process will be held by it. Namely: file handle, XProcessInfo
128+ and Popen instance.
129+ """
130+
131+ def __init__ (self , timeout ):
132+ self .timeout = timeout
133+ # handle to the process logfile
134+ self .fhandle = None
135+ # XProcessInfo holding information on XProcess instance
136+ self .info = None
137+ # Each XProcess will have a related Popen instance
138+ # used for process management through python's
139+ # subprocess API
140+ self .popen = None
141+
142+ def __del__ (self ):
143+ self .release ()
144+
145+ def __repr__ (self ):
146+ return "<XProcessResources {}, {}, {}>" .format (
147+ self .fhandle , self .info , self .popen
148+ )
149+
150+ def release (self ):
151+ # file handles should always be closed
152+ # in order to avoid ResourceWarnings
153+ self .fhandle .close ()
154+
155+ # We should wait on procs exit status if
156+ # termination signal has been issued
157+ try :
158+ if self .info [0 ]._termination_signal :
159+ self .popen .wait (self .timeout )
160+ except TypeError :
161+ pass
162+
163+
123164class XProcess :
124165 """Main xprocess class. Represents a running process instance for which
125166 a set of actions is offered, such as process startup, command line actions
@@ -130,11 +171,9 @@ def __init__(self, config, rootdir, log=None, proc_wait_timeout=60):
130171 self .rootdir = rootdir
131172 self .proc_wait_timeout = proc_wait_timeout
132173
133- # these will be used to keep all necessary
134- # references for proper cleanup before exiting
135- self ._info_objects = []
136- self ._file_handles = []
137- self ._popen_instances = []
174+ # used to keep all necessary references
175+ # for proper cleanup before exiting
176+ self .resources = []
138177
139178 class Log :
140179 def debug (self , msg , * args ):
@@ -175,6 +214,8 @@ def ensure(self, name, preparefunc, restart=False):
175214
176215 from subprocess import Popen , STDOUT
177216
217+ xresource = XProcessResources (self .proc_wait_timeout )
218+
178219 info = self .getinfo (name )
179220 if not restart and not info .isrunning ():
180221 restart = True
@@ -213,22 +254,29 @@ def ensure(self, name, preparefunc, restart=False):
213254
214255 # keep references of all popen
215256 # and info objects for cleanup
216- self . _info_objects . append (( info , starter .terminate_on_interrupt ) )
217- self . _popen_instances . append ( Popen (args , ** popen_kwargs , ** kwargs ) )
257+ xresource . info = ( info , starter .terminate_on_interrupt )
258+ xresource . popen = Popen (args , ** popen_kwargs , ** kwargs )
218259
219- info .pid = pid = self . _popen_instances [ - 1 ] .pid
260+ info .pid = pid = xresource . popen .pid
220261 info .pidpath .write (str (pid ))
221262 self .log .debug ("process %r started pid=%s" , name , pid )
222263 stdout .close ()
223264
224265 # keep track of all file handles so we can
225266 # cleanup later during teardown phase
226- self ._file_handles .append (info .logpath .open ())
267+ xresource .fhandle = info .logpath .open ()
268+
269+ self .resources .append (xresource )
270+ print (
271+ "self.resources at end of ensure function: " ,
272+ self .resources ,
273+ file = sys .stderr ,
274+ )
227275
228276 if not restart :
229- self . _file_handles [ - 1 ] .seek (0 , 2 )
277+ xresource . fhandle .seek (0 , 2 )
230278 else :
231- if not starter .wait (self . _file_handles [ - 1 ] ):
279+ if not starter .wait (xresource . fhandle ):
232280 raise RuntimeError (
233281 "Could not start process {}, the specified "
234282 "log pattern was not found within {} lines." .format (
@@ -238,7 +286,7 @@ def ensure(self, name, preparefunc, restart=False):
238286 self .log .debug ("%s process startup detected" , name )
239287
240288 pytest_extlogfiles = self .config .__dict__ .setdefault ("_extlogfiles" , {})
241- pytest_extlogfiles [name ] = self . _file_handles [ - 1 ]
289+ pytest_extlogfiles [name ] = xresource . fhandle
242290 self .getinfo (name )
243291
244292 return info .pid , info .logpath
@@ -267,19 +315,9 @@ def _xshow(self, tw):
267315 tw .line (tmpl .format (** locals ()))
268316 return 0
269317
270- def _clean_up_resources (self ):
271- # file handles should always be closed
272- # in order to avoid ResourceWarnings
273- for f in self ._file_handles :
274- f .close ()
275- # XProcessInfo objects and Popen objects have
276- # a one to one relation, so we should wait on
277- # procs exit status if termination signal has
278- # been isued for that particular XProcessInfo
279- # Object (subprocess requirement)
280- for (info , _ ), proc in zip (self ._info_objects , self ._popen_instances ):
281- if info ._termination_signal :
282- proc .wait (self .proc_wait_timeout )
318+ def _force_clean_up (self ):
319+ for xresource in self .resources :
320+ xresource .release ()
283321
284322
285323class ProcessStarter (ABC ):
0 commit comments