예제 #1
0
 def __init__(self, **kwargs):
     super().__init__()
     self.events = EventSource(**kwargs)
     self._callbacks = {}
     self._lock = threading.Lock()
     self._zombies = {}
     self._forks = 0
     self._no_processes = None
     self._had_children = False
예제 #2
0
 def __init__(self, **kwargs):
     super().__init__()
     self.events = EventSource(**kwargs)
     self._callbacks = {}
     self._lock = threading.Lock()
     self._zombies = {}
     self._forks = 0
     self._no_processes = None
     self._had_children = False
예제 #3
0
 def setUp(self):
     self.h = handlers()
     self.e = EventSource()
예제 #4
0
class TestEvents(unittest.TestCase):
    def setUp(self):
        self.h = handlers()
        self.e = EventSource()

    def test_event1(self):
        self.e.add(onH1=self.h.handler1)
        self.e.add(onH1=self.h.handler1)
        self.e.onH1("First trigger")
        self.e.onH1("Second trigger")
        self.assertEqual(self.h.results, [
            'handler1:First trigger', 'handler1:First trigger',
            'handler1:Second trigger', 'handler1:Second trigger'
        ])
        self.e.remove(onH1=self.h.handler1)
        self.e.onH1("Third trigger")
        self.e.remove(onH1=self.h.handler1)
        self.e.onH1("Fourth trigger")
        self.assertEqual(self.h.results, [
            'handler1:First trigger', 'handler1:First trigger',
            'handler1:Second trigger', 'handler1:Second trigger',
            'handler1:Third trigger'
        ])

    def test_event2(self):
        self.e.add(onH1=self.h.handler1)
        self.assertRaisesRegex(TypeError, 'but 3 were given',
                               lambda: self.e.onH1("arg1", "arg2"))

    def test_event3(self):
        self.e.add(onMulti=self.h.handler1)
        self.e.add(onMulti=self.h.handler2)
        self.e.onMulti("TWO")
        self.e.add(onMulti=self.h.handler3)
        self.e.onMulti("THREE")
        self.assertEqual(self.h.results, [
            'handler1:TWO', 'handler2:TWO', 'handler1:THREE', 'handler2:THREE',
            'handler3:THREE'
        ])
        self.e.remove(onMulti=self.h.handler2)
        self.e.onMulti("AFTER-REMOVE")
        self.assertEqual(self.h.results, [
            'handler1:TWO', 'handler2:TWO', 'handler1:THREE', 'handler2:THREE',
            'handler3:THREE', 'handler1:AFTER-REMOVE', 'handler3:AFTER-REMOVE'
        ])
        self.e.remove(onMulti=self.h.handler1)
        self.e.remove(onMulti=self.h.handler2)
        self.e.remove(onMulti=self.h.handler3)
        self.e.onMulti("EMPTY")
        self.assertEqual(self.h.results, [
            'handler1:TWO', 'handler2:TWO', 'handler1:THREE', 'handler2:THREE',
            'handler3:THREE', 'handler1:AFTER-REMOVE', 'handler3:AFTER-REMOVE'
        ])
예제 #5
0
class InitChildWatcher(BaseChildWatcher):
    """An init-responsible child watcher.

    Plugs into the asyncio child watcher framework to allow harvesting of both known and unknown
    child processes.
    """
    def __init__(self, **kwargs):
        super().__init__()
        self.events = EventSource(**kwargs)
        self._callbacks = {}
        self._lock = threading.Lock()
        self._zombies = {}
        self._forks = 0
        self._no_processes = None
        self._had_children = False

    def close(self):
        self._callbacks.clear()
        self._zombies.clear()
        super().close()

    def __enter__(self):
        with self._lock:
            self._forks += 1

            return self

    def __exit__(self, a, b, c):
        with self._lock:
            self._forks -= 1

            if self._forks or not self._zombies:
                return

            collateral_victims = str(self._zombies)
            self._zombies.clear()

        info("Caught subprocesses termination from unknown pids: %s",
             collateral_victims)

    @property
    def number_of_waiters(self):
        return len(self._callbacks)

    def add_child_handler(self, pid, callback, *args):
        assert self._forks, "Must use the context manager"
        with self._lock:
            try:
                returncode = self._zombies.pop(pid)
            except KeyError:
                # The child is running.
                self._callbacks[pid] = callback, args
                return

        # The child is dead already. We can fire the callback.
        callback(pid, returncode, *args)

    def remove_child_handler(self, pid):
        try:
            del self._callbacks[pid]
            return True
        except KeyError:
            return False

    def check_processes(self):
        # Checks to see if any processes terminated, and triggers onNoProcesses
        self._do_waitpid_all()

    def _do_waitpid_all(self):
        # Because of signal coalescing, we must keep calling waitpid() as
        # long as we're able to reap a child.
        while True:
            try:
                pid, status = os.waitpid(-1, os.WNOHANG)
                debug("REAP pid={0},status={1}".format(pid, status))
            except ChildProcessError:
                # No more child processes exist.
                if self._had_children:
                    debug("no child processes present")
                    self.events.onNoProcesses()
                return
            else:
                self._had_children = True
                if pid == 0:
                    # A child process is still alive.
                    return

                returncode = ProcStatus(status)

            with self._lock:
                try:
                    callback, args = self._callbacks.pop(pid)
                except KeyError:
                    # unknown child
                    if self._forks:
                        # It may not be registered yet.
                        self._zombies[pid] = returncode
                        continue
                    callback = None

            if callback is None:
                info(
                    "Caught subprocess termination from unknown pid: "
                    "%d -> %d", pid, returncode)
            else:
                callback(pid, returncode, *args)
예제 #6
0
class InitChildWatcher(BaseChildWatcher):
    """An init-responsible child watcher.

    Plugs into the asyncio child watcher framework to allow harvesting of both known and unknown
    child processes.
    """
    def __init__(self, **kwargs):
        super().__init__()
        self.events = EventSource(**kwargs)
        self._callbacks = {}
        self._lock = threading.Lock()
        self._zombies = {}
        self._forks = 0
        self._no_processes = None
        self._had_children = False

    def close(self):
        self._callbacks.clear()
        self._zombies.clear()
        super().close()

    def __enter__(self):
        with self._lock:
            self._forks += 1

            return self

    def __exit__(self, a, b, c):
        with self._lock:
            self._forks -= 1

            if self._forks or not self._zombies:
                return

            collateral_victims = str(self._zombies)
            self._zombies.clear()

        info(
            "Caught subprocesses termination from unknown pids: %s",
            collateral_victims)

    @property
    def number_of_waiters(self):
        return len(self._callbacks)

    def add_child_handler(self, pid, callback, *args):
        assert self._forks, "Must use the context manager"
        with self._lock:
            try:
                returncode = self._zombies.pop(pid)
            except KeyError:
                # The child is running.
                self._callbacks[pid] = callback, args
                return

        # The child is dead already. We can fire the callback.
        callback(pid, returncode, *args)

    def remove_child_handler(self, pid):
        try:
            del self._callbacks[pid]
            return True
        except KeyError:
            return False

    def check_processes(self):
        # Checks to see if any processes terminated, and triggers onNoProcesses
        self._do_waitpid_all()

    def _do_waitpid_all(self):
        # Because of signal coalescing, we must keep calling waitpid() as
        # long as we're able to reap a child.
        while True:
            try:
                pid, status = os.waitpid(-1, os.WNOHANG)
                debug("REAP pid={0},status={1}".format(pid,status))
            except ChildProcessError:
                # No more child processes exist.
                if self._had_children:
                    debug("no child processes present")
                    self.events.onNoProcesses()
                return
            else:
                self._had_children = True
                if pid == 0:
                    # A child process is still alive.
                    return

                returncode = ProcStatus(status)

            with self._lock:
                try:
                    callback, args = self._callbacks.pop(pid)
                except KeyError:
                    # unknown child
                    if self._forks:
                        # It may not be registered yet.
                        self._zombies[pid] = returncode
                        continue
                    callback = None

            if callback is None:
                info(
                    "Caught subprocess termination from unknown pid: "
                    "%d -> %d", pid, returncode)
            else:
                callback(pid, returncode, *args)
예제 #7
0
 def setUp(self):
     self.h = handlers()
     self.e = EventSource()
예제 #8
0
class TestEvents(unittest.TestCase):

    def setUp(self):
        self.h = handlers()
        self.e = EventSource()

    def test_event1(self):
        self.e.add(onH1 = self.h.handler1)
        self.e.add(onH1 = self.h.handler1)
        self.e.onH1("First trigger")
        self.e.onH1("Second trigger")
        self.assertEqual(self.h.results,
                         ['handler1:First trigger', 'handler1:First trigger', 'handler1:Second trigger', 'handler1:Second trigger'])
        self.e.remove(onH1 = self.h.handler1)
        self.e.onH1("Third trigger")
        self.e.remove(onH1 = self.h.handler1)
        self.e.onH1("Fourth trigger")
        self.assertEqual(self.h.results,
                         ['handler1:First trigger', 'handler1:First trigger', 'handler1:Second trigger', 'handler1:Second trigger', 'handler1:Third trigger'])

    def test_event2(self):
        self.e.add(onH1 = self.h.handler1)
        self.assertRaisesRegex(TypeError, 'but 3 were given', lambda: self.e.onH1("arg1", "arg2"))

    def test_event3(self):
        self.e.add(onMulti = self.h.handler1)
        self.e.add(onMulti = self.h.handler2)
        self.e.onMulti("TWO")
        self.e.add(onMulti = self.h.handler3)
        self.e.onMulti("THREE")
        self.assertEqual(self.h.results,
                         ['handler1:TWO', 'handler2:TWO', 'handler1:THREE', 'handler2:THREE', 'handler3:THREE'])
        self.e.remove(onMulti = self.h.handler2)
        self.e.onMulti("AFTER-REMOVE")
        self.assertEqual(self.h.results,
                         ['handler1:TWO', 'handler2:TWO', 'handler1:THREE', 'handler2:THREE', 'handler3:THREE', 'handler1:AFTER-REMOVE', 'handler3:AFTER-REMOVE'])
        self.e.remove(onMulti = self.h.handler1)
        self.e.remove(onMulti = self.h.handler2)
        self.e.remove(onMulti = self.h.handler3)
        self.e.onMulti("EMPTY")
        self.assertEqual(self.h.results,
                         ['handler1:TWO', 'handler2:TWO', 'handler1:THREE', 'handler2:THREE', 'handler3:THREE', 'handler1:AFTER-REMOVE', 'handler3:AFTER-REMOVE'])
예제 #9
0
 def __init__(self, **kwargs):
     self.events = EventSource(**kwargs)