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

#python #twisted

#python #twisted

Вопрос:

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

 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:python25libsite-packagestwistedinternettask.py", line 163, in start
    self()
  File "c:python25libsite-packagestwistedinternettask.py", line 194, in __call__
    d = defer.maybeDeferred(self.f, *self.a, **self.kw)
--- <exception caught here> ---
  File "c:python25libsite-packagestwistedinternetdefer.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 () вместо этого?

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

РЕДАКТИРОВАТЬ: это помогло правильно сделать переменную data глобальной. Для тех, кто последует, вот как вписаться 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()
  

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

Комментарии:

1. Вы также можете создать data атрибут DataPage . Или атрибут какого-либо другого объекта, к которому DataPage имеет доступ.

2. Я согласен с JPC. Страница данных кажется гораздо лучшим местом для ее хранения.

3. Вероятно, я сделаю данные и функцию, ответственную за их пероидное обновление, дочерними элементами DataPage и выделю процесс обновления в отдельный класс. Спасибо!

Ответ №1:

Добавьте глобальное определение в update_data():

 def update_data():
    global data
    data  = 1
  

Комментарии:

1. Кроме того, callLater кажется разумным способом сделать то, что вы описываете, но было бы полезно узнать больше деталей. Возможно, вам также придется начать беспокоиться о блокировке, если несколько потоков будут получать доступ к общему ресурсу и обновлять его.

2. Спасибо, я действительно идиот. RE: потоки. Мое понимание Twisted заключается в том, что данный реактор запускается только в одном потоке. Следовательно, мне действительно нужно беспокоиться о блокировке, даже если я использую callLater()? Какие дополнительные сведения вам требуются?

3. Нет, если вы просто используете callLater, нет.

4. Ваше понимание реактора как работающего в одном потоке абсолютно верно. Вы можете использовать потоки с помощью Twisted, но все это очень явно с помощью callInThread и callFromThread — если вы не вызываете подобный API, вы не используете потоки.