Обновление общих данных с помощью Twisted

Как я могу поделиться блоком данных с помощью сервера Twisted, периодически обновляя эти данные в фоновом режиме?:

from twisted.internet import reactor
from twisted.internet import task
from twisted.web.server import Site
from twisted.web.resource import Resource

data = 1

def update_data():
    data += 1

class DataPage(Resource):
    isLeaf = True
    def render_GET(self, request):
        return "<html><body>%s</body></html>" % (data, )

root = Resource()
root.putChild("data", DataPage())
factory = Site(root)
reactor.listenTCP(8880, factory)

m = task.LoopingCall(update_data)
m.start(10.0)

print "running"
reactor.run()

Приведенный выше код не работает из-за следующего исключения:

C:\temp>python discovery.py
Unhandled error in Deferred:
Traceback (most recent call last):
  File "discovery.py", line 23, in <module>
    m.start(10.0)
  File "c:\python25\lib\site-packages\twisted\internet\task.py", line 163, in start
    self()
  File "c:\python25\lib\site-packages\twisted\internet\task.py", line 194, in __call__
    d = defer.maybeDeferred(self.f, *self.a, **self.kw)
--- <exception caught here> ---
  File "c:\python25\lib\site-packages\twisted\internet\defer.py", line 102, in maybeDeferred
    result = f(*args, **kw)
  File "discovery.py", line 10, in update_data
    data += 1
exceptions.UnboundLocalError: local variable 'data' referenced before assignment

В этом примере я хочу, чтобы HTTP-клиенты имели доступ к http://127.0.0.1:8880/data. и получить текущее значение данных, в то же время запланировав какую-то другую задачу для периодического обновления данных.

Более того, я действительно не хочу использовать LoopingCall(), потому что мне может понадобиться изменить интервал в зависимости от того, будет ли обновление успешным или нет; обновление будет своего рода удаленным вызовом API. Могу ли я вместо этого каким-то образом использовать CallLater()?

Я уверен, что это глупый вопрос! Спасибо.

РЕДАКТИРОВАТЬ: вам помогли правильно сделать переменную данных глобальной. Для тех, кто последует, вот как вписать callLater() в код:

from twisted.internet import reactor
from twisted.internet import task
from twisted.web.server import Site
from twisted.web.resource import Resource

data = 1

def update_data():
    global data
    data += 1
    reactor.callLater(10, update_data)

class DataPage(Resource):
    isLeaf = True
    def render_GET(self, request):
        return "<html><body>%s</body></html>" % (data, )

root = Resource()
root.putChild("data", DataPage())
factory = Site(root)
reactor.listenTCP(8880, factory)

update_data()

print "running"
reactor.run()

Этот код все еще кажется немного хакерским. Мне не нравится объявлять переменные уровня модуля, не говоря уже об использовании глобальных переменных. Я приветствую любые предложения, которые избегают такой практики и делают код более чистым и пригодным для повторного использования.


person Asim Ihsan    schedule 04.05.2011    source источник
comment
Вы также можете сделать data атрибутом DataPage. Или атрибут какого-то другого объекта, к которому DataPage имеет доступ.   -  person Jean-Paul Calderone    schedule 04.05.2011
comment
Я согласен с JPC. DataPage кажется гораздо лучшим местом для его хранения.   -  person    schedule 04.05.2011
comment
Вероятно, я сделаю данные и функцию, ответственную за их периодическое обновление, дочерними элементами DataPage, а сам процесс обновления вынесу в отдельный класс. Спасибо!   -  person Asim Ihsan    schedule 05.05.2011


Ответы (1)


Добавьте глобальную защиту в update_data():

def update_data():
    global data
    data += 1
person Community    schedule 04.05.2011
comment
Кроме того, CallLater кажется разумным способом сделать то, что вы описываете, но было бы полезно получить более подробную информацию. Вам также может понадобиться начать беспокоиться о блокировке, если несколько потоков будут обращаться к общему ресурсу и обновлять его. - person ; 04.05.2011
comment
Спасибо, я действительно идиот. RE: темы. Мое понимание Twisted заключается в том, что данный реактор запускается только в одном потоке. Следовательно, мне действительно нужно беспокоиться о блокировке, даже если я использую CallLater()? Какие еще подробности вам нужны? - person Asim Ihsan; 04.05.2011
comment
Нет, если вы просто используете CallLater, нет. - person ; 04.05.2011
comment
Ваше понимание реактора как работающего в одном потоке совершенно верно. Вы можете использовать потоки с Twisted, но все это очень явно с callInThread и callFromThread - если вы не вызываете API таким образом, вы не используете потоки. - person Glyph; 04.05.2011