Как объединить callLater и addCallback?

Это так сломано, надеюсь, вы со мной милостивы:

reactor.callLater(0, myFunction, parameter1).addCallback(reactor.stop)
reactor.run()

myFunction возвращает отложенную.

Я надеюсь, что это ясно, что я хочу сделать:

  • Как только работает реактор, я хочу позвонить myFunction. Вот почему я использую 0 в качестве параметра задержки. Нет другого способа, кроме callLater? Это выглядит смешно, чтобы передать ему задержку 0.
  • Я хочу остановить реактор, как только myFunction выполнит задачу.

Проблемы, которые у меня есть до сих пор:

  • AttributeError: DelayedCall instance has no attribute 'addCallback'. Справедливо! Как поместить обратный вызов в цепочку обратного вызова, начатую myFunction, затем?
  • exceptions.TypeError: stop() takes exactly 1 argument (2 given).

Чтобы решить вторую проблему, мне пришлось определить специальную функцию:

def stopReactor(result):
    gd.log.info( 'Result: %s' % result)
    gd.log.info( 'Stopping reactor immediatelly' )
    reactor.stop()

И измените код на:

reactor.callLater(0, myFunction, parameter1).addCallback(stopReactor)
reactor.run()

(все еще не работает из-за проблемы callLater, но stopReactor будет работать сейчас)

Нет ли другого способа вызова reactor.stop за исключением определения дополнительной функции?

Ответ 1

IReactorTime.callLater и Deferred смешиваются вместе twisted.internet.task.deferLater.

from twisted.internet import reactor, task

d = task.deferLater(reactor, 0, myFunction, parameter1)
d.addCallback(lambda _: reactor.stop())
reactor.run()

Ответ 2

Я хочу остановить реактор, как только myFunction выполнит задачу.

Итак, создайте обертку, которая работает myFunction, а затем остановит реактор?

def wrapper(reactor, *args):
    myFunction(*args)
    reactor.stop()

reactor.callLater(0, wrapper, reactor, ...)

Ответ 3

Вам нужно приложить обратный вызов к отложенному, который возвращает myFunction, поскольку callLater не возвращает функцию. Возможно, что-то подобное:

reactor.callLater(0, lambda: myFunction(parameter1).addCallback(lambda _: reactor.stop())

Но это не проверено.

Вам нужно написать новую функцию (здесь лямбда _: reactor.stop()), потому что обратные вызовы к отложенному всегда принимают результат до этого. Если вы обнаружите, что хотите использовать обратные вызовы для своих побочных эффектов, и вы часто не заботитесь о распространении значений, вы можете определить небольшую вспомогательную функцию:

def ignoringarg(f):
    return lambda _: f()

И затем выполните:

reactor.callLater(0, lambda: myFunction(paramater1).addCallback(ignoringarg(reactor.stop)))

(То, что было бы действительно аккуратно, было бы определить __rshift__ (и локальный аналог) для класса Deferred, чтобы вы могли сделать: myFunction(parameter1) >> reactor.stop, когда вы хотите отказаться от аргумента или myFunction(parameter1) >>= someotherfunc, если вы хотите распространять аргумент. Если вы считаете, что злоупотребление синтаксисом haskellish является "опрятным", во всяком случае.)

Ответ 4

Если вам нужно активировать обратный вызов с каким-либо действием, просто сделайте это (возможно, нет необходимости возвращать отложенную или smth). Просто, чтобы прояснить ситуацию (используя чисто отложенные):

from twisted.internet import reactor, defer

# That will be our deferred to play with
# it has callback and errback methods
d = defer.Deferred()

def my_function(x):
    print 'function', x
    # need to trigger deferred upon function run?
    # Lets tell it to do so:
    d.callback(x)

# That our callback to run after triggering `d`    
def d_callback(y):
    print 'callback ', y

# now let bind that callback to be actually launched by `d`
d.addCallback(d_callback)

# now adding another callback just to stop reactor
# note lambda simply helps to agree number of arguments
d.addCallback(lambda data: reactor.stop())

# so we'll call `my_function` in 2 secs, then it runs
# then it triggers `d` to fire its callbacks
# then `d` actually detonates the whole chain of its added callbacks

reactor.callLater(2, my_function, 'asdf') # 'asdf' is some stupid param

# Here how it works
print 'Lets start!'
reactor.run()
print 'Done!'