# -*- test-case-name: twisted.test.test_application -*- # Twisted, the Framework of Your Internet # Copyright (C) 2001-2003 Matthew W. Lefkowitz # # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General Public # License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """Reactor-based Services Here are services to run clients, servers and periodic services using the reactor. API Stability: unstable Maintainer: U{Moshe Zadka} """ from twisted.python import log from twisted.application import service class _VolatileDataService(service.Service): volatile = [] def __getstate__(self): d = service.Service.__getstate__(self) for attr in self.volatile: if d.has_key(attr): del d[attr] return d class _AbstractServer(_VolatileDataService): privileged = True volatile = ['_port'] method = None def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs def privilegedStartService(self): service.Service.privilegedStartService(self) self._port = self._getPort() def startService(self): service.Service.startService(self) if not hasattr(self,'_port'): self._port = self._getPort() def stopService(self): service.Service.stopService(self) # TODO: if startup failed, should shutdown skip stopListening? # _port won't exist d = self._port.stopListening() del self._port return d def _getPort(self): from twisted.internet import reactor return getattr(reactor, 'listen'+self.method)(*self.args, **self.kwargs) class _AbstractClient(_VolatileDataService): volatile = ['_connection'] method = None def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs def startService(self): service.Service.startService(self) self._connection = self._getConnection() def stopService(self): service.Service.stopService(self) #self._connection.disconnect() #TODO: needs testing def _getConnection(self): from twisted.internet import reactor return getattr(reactor, 'connect'+self.method)(*self.args, **self.kwargs) _doc={ 'Client': """Connect to %(tran)s Call reactor.connect%(method)s when the service starts, with the arguments given to the constructor. """, 'Server': """Serve %(tran)s clients Call reactor.listen%(method)s when the service starts, with the arguments given to the constructor. When the service stops, stop listening. See twisted.internet.interfaces for documentation on arguments to the reactor method. """, } # TCPServer TCPClient UNIXServer UNIXClient SSLServer SSLClient # UDPServer UDPClient UNIXDatagramServer UNIXDatagramClient # MulticastServer MulticastClient # # This message brought to you by the Twisted Committee for Better Grepping import new for tran in 'Generic TCP UNIX SSL UDP UNIXDatagram Multicast'.split(): for side in 'Server Client'.split(): base = globals()['_Abstract'+side] method = {'Generic': 'With'}.get(tran, tran) doc = _doc[side]%vars() klass = new.classobj(tran+side, (base,), {'method': method, '__doc__': doc}) globals()[tran+side] = klass class TimerService(_VolatileDataService): volatile = ['_call'] """Service to periodically call a function Every C{step} seconds call the given function with the given arguments. The service starts the calls when it starts, and cancels them when it stops. """ def __init__(self, step, callable, *args, **kwargs): self.step = step from twisted.internet.task import LoopingCall self.loop = LoopingCall(callable, *args, **kwargs) def startService(self): from twisted.internet import reactor service.Service.startService(self) self._call = reactor.callLater(self.step, self._startLoop, self.step) def _startLoop(self, step): self._call = None self.loop.start(step).addBoth(self._cleanupLoop).addErrback(log.err) def _cleanupLoop(self, result): self.loop = None return result def stopService(self): service.Service.stopService(self) if self._call is not None: _call, self._call = self._call, None _call.cancel() elif self.loop is not None: loop, self.loop = self.loop, None loop.stop() __all__ = (['TimerService']+ [tran+side for tran in 'Generic TCP UNIX SSL UDP UNIXDatagram Multicast'.split() for side in 'Server Client'.split()])