o b[D@sdZddgZddlZddlmZddlmZmZmZm Z m Z m Z m Z m Z mZmZmZddlmZmZmZmZmZmZmZmZmZmZmZddlmZdd lm Z m!Z!m"Z"dd l#m$Z$dZ%d Z&d Z'Gd dde"Z(eeGddde!Z)dddZ*dS)z A reactor for integrating with U{CFRunLoop}, the CoreFoundation main loop used by macOS. This is useful for integrating Twisted with U{PyObjC} applications. install CFReactorN) implementer) CFSocketCreateRunLoopSourceCFSocketCreateWithNativeCFSocketDisableCallBacksCFSocketEnableCallBacksCFSocketInvalidateCFSocketSetSocketFlags*kCFSocketAutomaticallyReenableReadCallBack+kCFSocketAutomaticallyReenableWriteCallBackkCFSocketConnectCallBackkCFSocketReadCallBackkCFSocketWriteCallBack) CFAbsoluteTimeGetCurrentCFRunLoopAddSourceCFRunLoopAddTimerCFRunLoopGetMainCFRunLoopRemoveSource CFRunLoopRun CFRunLoopStopCFRunLoopTimerCreateCFRunLoopTimerInvalidatekCFAllocatorDefaultkCFRunLoopCommonModes) IReactorFDSet) _NO_FILEDESCPosixReactorBase_Waker)log@c@seZdZdZddZdS) _WakerPlusa The normal Twisted waker will simply wake up the main loop, which causes an iteration to run, which in turn causes L{ReactorBase.runUntilCurrent} to get invoked. L{CFReactor} has a slightly different model of iteration, though: rather than have each iteration process the thread queue, then timed calls, then file descriptors, each callback is run as it is dispatched by the CFRunLoop observer which triggered it. So this waker needs to not only unblock the loop, but also make sure the work gets done; so, it reschedules the invocation of C{runUntilCurrent} to be immediate (0 seconds from now) even if there is no timed call work to do. cCst|}|jd|S)zw Wake up the loop and force C{runUntilCurrent} to run immediately in the next timed iteration. T)rdoReadreactor_scheduleSimulate)selfresultr(._drdw)r/rr2rrcallWithLogger) r&cfSocketrIignoredAddress ignoredDatacontextfd smugglesrcsrcsktrLr(rHr)_socketCallbacks zCFReactor._socketCallbackc Cs|dkrtd||jvr|j|\}}}}nLg}||tt|ttBtB|j|}t |t t Bt Bt t|d}||t|j|tt|ttBtBddg}||jt|<||||f|j|<d|||<t||dS)a Register a file descriptor with the C{CFRunLoop}, or modify its state so that it's listening for both notifications (read and write) rather than just one; used to implement C{addReader} and C{addWriter}. @param fd: The file descriptor. @type fd: L{int} @param descr: the L{IReadDescriptor} or L{IWriteDescriptor} @param flag: the flag to register for callbacks on, either C{kCFSocketReadCallBack} or C{kCFSocketWriteCallBack} r<zInvalid file descriptor.rFTN) RuntimeErrorr/appendrrrrr rVr r r _preserveSOErrorrrr2rrr0id _flag2idxr) r&rRdescrflagrTcfsgotdescrrKctxr(r(r)_watchFDsR    zCFReactor._watchFDcCstttti|S)a- Convert a C{kCFSocket...} constant to an index into the read/write state list (C{_READ} or C{_WRITE}) (the 4th element of the value of C{self._fdmap}). @param flag: C{kCFSocketReadCallBack} or C{kCFSocketWriteCallBack} @return: C{_READ} or C{_WRITE} )rr>rr?)r&r]r(r(r)r[ s zCFReactor._flag2idxcCst||jvr dS|dkr|jt|}n|}|j|\}}}}t||d|||<|tsK|tsM|jt|=|j|=t|j|t t |dSdSdS)a Unregister a file descriptor with the C{CFRunLoop}, or modify its state so that it's listening for only one notification (read or write) as opposed to both; used to implement C{removeReader} and C{removeWriter}. @param fd: a file descriptor @type fd: C{int} @param descr: an L{IReadDescriptor} or L{IWriteDescriptor} @param flag: C{kCFSocketWriteCallBack} C{kCFSocketReadCallBack} Nr<F) rZr0r/rr[r>r?rr2rr )r&rRr\r]realfdrTr^rKr(r(r) _unwatchFDs   zCFReactor._unwatchFDcC|||tdS)z7 Implement L{IReactorFDSet.addReader}. N)rar=rr&readerr(r(r)r97zCFReactor.addReadercCrd)z7 Implement L{IReactorFDSet.addWriter}. N)rar=rr&writerr(r(r) addWriter=rgzCFReactor.addWritercCrd)z: Implement L{IReactorFDSet.removeReader}. N)rcr=rrer(r(r) removeReaderCrgzCFReactor.removeReadercCrd)z: Implement L{IReactorFDSet.removeWriter}. N)rcr=rrhr(r(r) removeWriterIrgzCFReactor.removeWritercCsHdd|jD}|t|j8}|D] }||||qt|S)z7 Implement L{IReactorFDSet.removeAll}. cSsh|]\}}}}|qSr(r(.0rTr^r\rKr(r(r) Ssz&CFReactor.removeAll..)r/valuessetr7rkrllist)r&allDescdescr(r(r) removeAllOs   zCFReactor.removeAllcCdd|jDS)z8 Implement L{IReactorFDSet.getReaders}. cS g|] \}}}}|tr|qSr()r>rmr(r(r) ^ z(CFReactor.getReaders..r/rpr:r(r(r) getReadersZzCFReactor.getReaderscCrv)z8 Implement L{IReactorFDSet.getWriters}. cSrwr()r?rmr(r(r)rxdryz(CFReactor.getWriters..rzr:r(r(r) getWriters`r|zCFReactor.getWriterscCst||}||S)aw Override L{PosixReactorBase}'s implementation of L{IDelayedCall.reset} so that it will immediately reschedule. Normally C{_moveCallLaterSooner} depends on the fact that C{runUntilCurrent} is always run before the mainloop goes back to sleep, so this forces it to immediately recompute how long the loop needs to stay asleep. )r_moveCallLaterSoonerr%)r&tpler'r(r(r)r~fs zCFReactor._moveCallLaterSoonerFcCs$d|_z |Wd|_dSd|_w)z Run the runner (C{CFRunLoopRun} or something that calls it), which runs the run loop until C{crash()} is called. TFN) _inCFLoopr1r:r(r(r)mainLoopts zCFReactor.mainLoopcsxjdur tjd_}|rd}|dur:t|}fdd}tt|ddd|d}_tj|tdSdS)aV Schedule a call to C{self.runUntilCurrent}. This will cancel the currently scheduled call if it is already scheduled. @param force: Even if there are no timed calls, make sure that C{runUntilCurrent} runs immediately (in a 0-seconds-from-now C{CFRunLoopTimer}). This is necessary for calls which need to trigger behavior of C{runUntilCurrent} other than running timed calls, such as draining the thread call queue or calling C{crash()} when the appropriate flags are set. @type force: C{bool} Ngcsd_dSr.)_currentSimulatorrunUntilCurrentr%)cftimerextrar:r(r)simulates z-CFReactor._scheduleSimulate..simulater) rrtimeoutrrrrr2r)r&forcerfireDatercr(r:r)r%s     zCFReactor._scheduleSimulatecOs(tj|||g|Ri|}||S)z6 Implement L{IReactorTime.callLater}. )r callLaterr%)r&_seconds_fargskw delayedCallr(r(r)rszCFReactor.callLatercCst||ddS)z1 Implement L{IReactorCore.stop}. TN)rstopr%r:r(r(r)rs zCFReactor.stopcCs<|j}t||jr|dS|r|d|jdSdS)z1 Implement L{IReactorCore.crash} rN)_startedrcrashr_stopNowr)r& wasStartedr(r(r)rs  zCFReactor.crashcCst|jdS)zJ Immediately stop the CFRunLoop (which must be running!). N)rr2r:r(r(r)rszCFReactor._stopNowrcCs|||j|dS)z Emulate the behavior of C{iterate()} for things that want to call it, by letting the loop run for a little while and then scheduling a timed call to exit it. N)rrr)r&delayr(r(r)iterates zCFReactor.iterateNN)F)r)r*r+r,r-r3r;rVrar[rcr9rjrkrlrur{r}r~rrrr%rrrrrr(r(r(r)rRs2 &  B;    ! cCs$t||d}ddlm}|||S)a6 Configure the twisted mainloop to be run inside CFRunLoop. @param runLoop: the run loop to use. @param runner: the function to call in order to actually invoke the main loop. This will default to C{CFRunLoopRun} if not specified. However, this is not an appropriate choice for GUI applications, as you need to run NSApplicationMain (or something like it). For example, to run the Twisted mainloop in a PyObjC application, your C{main.py} should look something like this:: from PyObjCTools import AppHelper from twisted.internet.cfreactor import install install(runner=AppHelper.runEventLoop) # initialize your application reactor.run() @return: The installed reactor. @rtype: C{CFReactor} )r4r5r)installReactor)rtwisted.internet.mainr)r4r5r$rr(r(r)rs  r)+r-__all__rBzope.interfacer CFNetworkrrrrr r r r r rrCoreFoundationrrrrrrrrrrrtwisted.internet.interfacesrtwisted.internet.posixbaserrrtwisted.pythonrr>r?rYr"rrr(r(r(r)s$ 44  |