[Twisted-Python] Could Service.startService return a Deferred?

Peter Westlake peter.westlake at pobox.com
Thu Nov 28 08:01:16 MST 2013


On Wed, Nov 27, 2013, at 19:20, exarkun at twistedmatrix.com wrote:
> On 06:00 pm, peter.westlake at pobox.com wrote:
... 

> >class Runner(service.Service):
> >    def __init__(self, baton):
> >        self.baton = baton
> >
> >    def startService(self):
> >        print 'startService', self.name, reactor.running
> >        self.baton.addCallback(lambda ignore:deferLater(reactor, 1.0,
> >        self.realStartService))
> >        self.baton.addErrback(report)
> 
> I'd be concerned with the state of `Runner` at this point in the 
> process.
> 
> What happens if the application gets shut down while that `deferLater` 
> is still pending?
> 
> `stopService` has a harder job because it might need to deal with a 
> service that is partially initialized or a service that is completely 
> initialized (and "partially initialized" may cover a multitude of 
> different states depending on the complexity of your service).
> >
...
> 
> What about something like this instead?
> 
>   @implementer(IService)
>   class Runner(object):
>       ...
>       @classmethod
>       def loadFromWhatever(cls, name):
>           return deferLater(reactor, Runner, name)
> 
>       def __init__(self, name):
>           self.name = name
> 
>       def startService(self):
>           self.running = True
>           print 'realStartService', self.name, reactor.running
> 
>   def parent(service):
>       application.setServiceParent(service)
> 
>   loading = Runner.initializeFromWhatever("foo")
>   loading.addCallback(parent)
>   loading.addCallback(lambda ignored: 
> Runner.initializeFromWhatever("bar"))
>   loading.addCallback(parent)
>   loading.addErrback(stopTheReactorOrWhatever)
> 
> The advantage I see of this approach is that a `Runner` never exists in
> the service hierarchy until it is fully initialized, started, and 
> running.
> 
> If a `Runner` is only partially ready and the process shuts down then 
> its `stopService` method isn't called because it's not part of the 
> service hierarchy.

I'll do that, thank you!

Could the documentation for Service say something about which methods
can be called when? For instance, it would never have occurred to me
that
setServiceParent could be called after control had passed out of the
.tac
file and the reactor had started running. I see from the source that it
calls
startService, but this is definitely the sort of non-obvious tip that it
would
be helpful to have written down. Likewise the fact that startService
normally runs before the reactor starts.

Out of interest, was there a reason for not making Runner a subclass
of Service? There are some methods of IService that this version
doesn't implement.

> I could definitely imagine a library to help with this kind of thing. 
> For example, perhaps you want the above encapsulated as:
> 
>   asynchronouslySetUpServices(application, [
>       lambda: Runner.initializeFromWhatever("foo"),
>       lambda: Runner.initialifrFromWhatever("bar")])
> 
> And maybe then you want to add in some logic so that if the application 
> gets shut down while some things are still being initialized then you 
> cancel their Deferred.  Then you have good cleanup support for the 
> uninitialized case - without complicating `stopService` (the cleanup 
> logic is isolated in the implementation of Deferred cancellation where 
> it belongs - eg, with this `deferLater`-based asynchronousness it's 
> alreay present since deferLater implements cancellation already).

I'll add it to my list of things to do one of these years :-)
Would you like to put in a ticket with a spec?

Thanks for the help,

Peter.



More information about the Twisted-Python mailing list