Package _emerge :: Module SubProcess
[hide private]

Source Code for Module _emerge.SubProcess

  1  # Copyright 1999-2013 Gentoo Foundation 
  2  # Distributed under the terms of the GNU General Public License v2 
  3   
  4  from portage import os 
  5  from _emerge.AbstractPollTask import AbstractPollTask 
  6  import signal 
  7  import errno 
  8   
9 -class SubProcess(AbstractPollTask):
10 11 __slots__ = ("pid",) + \ 12 ("_dummy_pipe_fd", "_files", "_reg_id") 13 14 # This is how much time we allow for waitpid to succeed after 15 # we've sent a kill signal to our subprocess. 16 _cancel_timeout = 1000 # 1 second 17
18 - def _poll(self):
19 if self.returncode is not None: 20 return self.returncode 21 if self.pid is None: 22 return self.returncode 23 if self._registered: 24 return self.returncode 25 26 try: 27 # With waitpid and WNOHANG, only check the 28 # first element of the tuple since the second 29 # element may vary (bug #337465). 30 retval = os.waitpid(self.pid, os.WNOHANG) 31 except OSError as e: 32 if e.errno != errno.ECHILD: 33 raise 34 del e 35 retval = (self.pid, 1) 36 37 if retval[0] == 0: 38 return None 39 self._set_returncode(retval) 40 self.wait() 41 return self.returncode
42
43 - def _cancel(self):
44 if self.isAlive(): 45 try: 46 os.kill(self.pid, signal.SIGTERM) 47 except OSError as e: 48 if e.errno != errno.ESRCH: 49 raise
50
51 - def isAlive(self):
52 return self.pid is not None and \ 53 self.returncode is None
54
55 - def _wait(self):
56 57 if self.returncode is not None: 58 return self.returncode 59 60 if self._registered: 61 if self.cancelled: 62 self._wait_loop(timeout=self._cancel_timeout) 63 if self._registered: 64 try: 65 os.kill(self.pid, signal.SIGKILL) 66 except OSError as e: 67 if e.errno != errno.ESRCH: 68 raise 69 del e 70 self._wait_loop(timeout=self._cancel_timeout) 71 if self._registered: 72 self._orphan_process_warn() 73 else: 74 self._wait_loop() 75 76 if self.returncode is not None: 77 return self.returncode 78 79 if not isinstance(self.pid, int): 80 # Get debug info for bug #403697. 81 raise AssertionError( 82 "%s: pid is non-integer: %s" % 83 (self.__class__.__name__, repr(self.pid))) 84 85 self._waitpid_loop() 86 87 return self.returncode
88
89 - def _waitpid_loop(self):
90 source_id = self.scheduler.child_watch_add( 91 self.pid, self._waitpid_cb) 92 try: 93 while self.returncode is None: 94 self.scheduler.iteration() 95 finally: 96 self.scheduler.source_remove(source_id)
97
98 - def _waitpid_cb(self, pid, condition, user_data=None):
99 if pid != self.pid: 100 raise AssertionError("expected pid %s, got %s" % (self.pid, pid)) 101 self._set_returncode((pid, condition))
102
103 - def _orphan_process_warn(self):
104 pass
105
106 - def _unregister(self):
107 """ 108 Unregister from the scheduler and close open files. 109 """ 110 111 self._registered = False 112 113 if self._reg_id is not None: 114 self.scheduler.source_remove(self._reg_id) 115 self._reg_id = None 116 117 if self._files is not None: 118 for f in self._files.values(): 119 if isinstance(f, int): 120 os.close(f) 121 else: 122 f.close() 123 self._files = None
124
125 - def _set_returncode(self, wait_retval):
126 """ 127 Set the returncode in a manner compatible with 128 subprocess.Popen.returncode: A negative value -N indicates 129 that the child was terminated by signal N (Unix only). 130 """ 131 self._unregister() 132 133 pid, status = wait_retval 134 135 if os.WIFSIGNALED(status): 136 retval = - os.WTERMSIG(status) 137 else: 138 retval = os.WEXITSTATUS(status) 139 140 self.returncode = retval
141