"Fossies" - the Fresh Open Source Software Archive

Member "buildbot-2.3.1/buildbot/test/fake/reactor.py" (23 May 2019, 6029 Bytes) of package /linux/misc/buildbot-2.3.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "reactor.py": 2.1.0_vs_2.2.0.

    1 # Copyright Buildbot Team Members
    2 # Portions copyright 2015-2016 ClusterHQ Inc.
    3 #
    4 # Permission is hereby granted, free of charge, to any person obtaining
    5 # a copy of this software and associated documentation files (the
    6 # "Software"), to deal in the Software without restriction, including
    7 # without limitation the rights to use, copy, modify, merge, publish,
    8 # distribute, sublicense, and/or sell copies of the Software, and to
    9 # permit persons to whom the Software is furnished to do so, subject to
   10 # the following conditions:
   11 #
   12 # The above copyright notice and this permission notice shall be
   13 # included in all copies or substantial portions of the Software.
   14 #
   15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   16 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   17 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
   19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
   21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   22 
   23 
   24 from twisted.internet import defer
   25 from twisted.internet import reactor
   26 from twisted.internet.base import _ThreePhaseEvent
   27 from twisted.internet.interfaces import IReactorCore
   28 from twisted.internet.interfaces import IReactorThreads
   29 from twisted.internet.task import Clock
   30 from twisted.python import log
   31 from twisted.python.failure import Failure
   32 from zope.interface import implementer
   33 
   34 # The code here is based on the implementations in
   35 # https://twistedmatrix.com/trac/ticket/8295
   36 # https://twistedmatrix.com/trac/ticket/8296
   37 
   38 
   39 @implementer(IReactorCore)
   40 class CoreReactor:
   41 
   42     """
   43     Partial implementation of ``IReactorCore``.
   44     """
   45 
   46     def __init__(self):
   47         super().__init__()
   48         self._triggers = {}
   49 
   50     def addSystemEventTrigger(self, phase, eventType, f, *args, **kw):
   51         event = self._triggers.setdefault(eventType, _ThreePhaseEvent())
   52         return eventType, event.addTrigger(phase, f, *args, **kw)
   53 
   54     def removeSystemEventTrigger(self, triggerID):
   55         eventType, handle = triggerID
   56         event = self._triggers.setdefault(eventType, _ThreePhaseEvent())
   57         event.removeTrigger(handle)
   58 
   59     def fireSystemEvent(self, eventType):
   60         event = self._triggers.get(eventType)
   61         if event is not None:
   62             event.fireEvent()
   63 
   64     def callWhenRunning(self, f, *args, **kwargs):
   65         f(*args, **kwargs)
   66 
   67 
   68 class NonThreadPool:
   69 
   70     """
   71     A stand-in for ``twisted.python.threadpool.ThreadPool`` so that the
   72     majority of the test suite does not need to use multithreading.
   73 
   74     This implementation takes the function call which is meant to run in a
   75     thread pool and runs it synchronously in the calling thread.
   76 
   77     :ivar int calls: The number of calls which have been dispatched to this
   78         object.
   79     """
   80     calls = 0
   81 
   82     def __init__(self, **kwargs):
   83         pass
   84 
   85     def callInThreadWithCallback(self, onResult, func, *args, **kw):
   86         self.calls += 1
   87         try:
   88             result = func(*args, **kw)
   89         except:  # noqa pylint: disable=bare-except
   90             # We catch *everything* here, since normally this code would be
   91             # running in a thread, where there is nothing that will catch
   92             # error.
   93             onResult(False, Failure())
   94         else:
   95             onResult(True, result)
   96 
   97     def start(self):
   98         pass
   99 
  100     def stop(self):
  101         pass
  102 
  103 
  104 @implementer(IReactorThreads)
  105 class NonReactor:
  106 
  107     """
  108     A partial implementation of ``IReactorThreads`` which fits into
  109     the execution model defined by ``NonThreadPool``.
  110     """
  111 
  112     def callFromThread(self, f, *args, **kwargs):
  113         f(*args, **kwargs)
  114 
  115     def getThreadPool(self):
  116         return NonThreadPool()
  117 
  118 
  119 class TestReactor(NonReactor, CoreReactor, Clock):
  120 
  121     def __init__(self):
  122         super().__init__()
  123 
  124         # whether there are calls that should run right now
  125         self._pendingCurrentCalls = False
  126         self.stop_called = False
  127 
  128     def _executeCurrentDelayedCalls(self):
  129         while self.getDelayedCalls():
  130             first = sorted(self.getDelayedCalls(),
  131                            key=lambda a: a.getTime())[0]
  132             if first.getTime() > self.seconds():
  133                 break
  134             self.advance(0)
  135 
  136         self._pendingCurrentCalls = False
  137 
  138     @defer.inlineCallbacks
  139     def _catchPrintExceptions(self, what, *a, **kw):
  140         try:
  141             r = what(*a, **kw)
  142             if isinstance(r, defer.Deferred):
  143                 yield r
  144         except Exception as e:
  145             log.msg('Unhandled exception from deferred when doing '
  146                     'TestReactor.advance()', e)
  147             raise
  148 
  149     def callLater(self, when, what, *a, **kw):
  150         # Buildbot often uses callLater(0, ...) to defer execution of certain
  151         # code to the next iteration of the reactor. This means that often
  152         # there are pending callbacks registered to the reactor that might
  153         # block other code from proceeding unless the test reactor has an
  154         # iteration. To avoid deadlocks in tests we give the real reactor a
  155         # chance to advance the test reactor whenever we detect that there
  156         # are callbacks that should run in the next iteration of the test
  157         # reactor.
  158         #
  159         # Additionally, we wrap all calls with a function that prints any
  160         # unhandled exceptions
  161         if when <= 0 and not self._pendingCurrentCalls:
  162             reactor.callLater(0, self._executeCurrentDelayedCalls)
  163 
  164         return super().callLater(when, self._catchPrintExceptions,
  165                                  what, *a, **kw)
  166 
  167     def stop(self):
  168         # first fire pending calls until the current time. Note that the real
  169         # reactor only advances until the current time in the case of shutdown.
  170         self.advance(0)
  171 
  172         # then, fire the shutdown event
  173         self.fireSystemEvent('shutdown')
  174 
  175         self.stop_called = True