Преобразование в скрученный асинхронный дизайн

Хорошо, у меня возникла проблема с выражением моих проблем с кодом, над которым я работаю, без сброса тонны кода; так вот что это будет синхронно (вместо того, чтобы спрашивать об этом с точки зрения асинхронности).

Также для классов, когда следует обращаться к переменной через аргумент метода и когда к ней следует обращаться через переменную экземпляра?

Синхронно это будет выглядеть так... Примечание: фактические URL-адреса сервера и парсинг отличаются, но только усложняют ситуацию. Также в следующем примере метод get_token принимает сеанс в качестве параметра, должен ли он вместо этого получить сеанс, используя вместо этого self.session?

import urllib
import time

class SyncExampleClass(object):

    def __init__(self):
        self.session = None
        self.token = None
        self.session_time = -1


    def get_session(self):
        s = urllib.urlopen("http://example.com/session/").read()
        self.session_time = int(time.time())
        return s

    def get_token(self, session):
        t = urllib.urlopen("http://example.com/token/?session=%s" % session).read()
        return t

    def construct_api_call(self, api_method):
        # if the session is over an hour old (is that the correct sign?)
        if time.time() - 3600 > self.session_time or self.session is None:
            self.session = get_session()
            self.token = get_token(self.session)

        call = urllib.urlopen("http://example.com/api/?method=%s%session=%s&token=%s" % (api_method, self.session, self.token) ).read()
        return call

person Zimm3r    schedule 11.08.2010    source источник
comment
После того, как вы в первый раз установили self.session в construct_api_call, вы установите другой, если прошло меньше часа с тех пор, как вы в последний раз делали это - если вы когда-нибудь проведете один час без вызова, вы никогда не установите другой сеанс (или токен) когда-либо снова. ОЧЕНЬ странное поведение — вы уверены, что < в if — это то, что вам нужно?! (Когда вы даете спецификации по коду, вам лучше дважды убедиться, что код делает именно то, что вы хотите, конечно, поскольку нет аналога в естественном языке, с которым можно было бы проверить спецификации).   -  person Alex Martelli    schedule 12.08.2010
comment
@ Алекс Мартелли Да, вы правы; Я имел в виду, если сеанс длится более часа. (Я исправил это в редактировании (так что, если вы посмотрите на это сейчас, это правильно (я думаю)) ))   -  person Zimm3r    schedule 12.08.2010
comment
Являются ли переменные session и token частью URL-адреса или это файлы cookie? Ваш призыв к urllib.urlopen в лучшем случае заблокирует реактор. Вместо этого вы должны использовать twisted.web.http.HTTPClient.   -  person MattH    schedule 12.08.2010
comment
На самом деле они являются почтовыми данными в фактической реализации, это то, как это выглядит без всех ненужных сложностей, и я знаю, что urllib заблокирует, поэтому это синхронный пример, а не асинхронный пример.   -  person Zimm3r    schedule 13.08.2010


Ответы (1)


Учитывая обстоятельства, это всего лишь скелет решения, которое подразумевало многое. Кажется, что предоставление решения с кодом, в котором многое подразумевается и не проверено, идет вразрез с инстинктом...

Однако, если бы я кодировал то, чего, по моему мнению, вы пытаетесь достичь, я мог бы сделать это примерно так:

from twisted.internet import defer
from twisted.web import client
from twisted.python import log
from urllib import urlencode
import time

class APIException(Exception):
    pass

class ASyncExampleClass(object):
    def __init__(self):
        self.session = None
        self.token = None

    @defer.inlineCallbacks
    def api_call(self, api_method,tries=3,timeout=10):
        attempt = 1
        while attempt <= tries:
            attempt += 1
            if self.session = None:
                yield sess_data = client.getPage("http://example.com/session/",timeout=timeout)
                self.session = extractSessionFromData(sess_data)
            if self.token = None:
                yield token_data = client.getPage("http://example.com/token/?%s" % urlencode(dict(session=self.session)),timeout=timeout)
                self.token = extractTokenFromData(token_data)
            # Place "the" call
            yield api_result = client.getPage("http://example.com/api/?%s" % urlencode(dict(api_method=api_method,session=self.session,token=self.token)),timeout=timeout)
            #
            if sessionInvalid(api_result):
                log.msg("Request for %s failed because invalid session %s" % (api_method,self.session))
                self.session = None
                self.token = None
                continue
            if tokenInvalid(api_result):
                log.msg("Request for %s failed because invalid token %s" % (api_method,self.token))
                self.token = None
                continue
            # Any other checks for valid result
            returnValue(api_result)
            break # Not sure if this is needed, not in an position to test readily.
        else:
            raise APIException("Tried and failed %s times to do %s" % (attempt - 1, api_method))

Метод, создающий внешний API, использует inlineCallbacks. и сам обрабатывает логику получения и обновления сеансов и токенов. Я предположил, что когда сеанс недействителен, любые токены, полученные с его помощью, также недействительны. Он реализует цикл повтора, который также может содержать блок try/except для лучшей обработки исключений HTTP.

Вы можете использовать twisted.web.client.getPage с POST, дополнительные аргументы, которые вы предоставляете, обрабатываются HTTPClientFactory.

Кроме того, я бы не стал засекать время сеанса, а просто продлевал его по мере необходимости.

person MattH    schedule 13.08.2010
comment
Большое спасибо, MattH, это было то, что я искал, и сначала я избегал inlineCallbacks, но это представило их как элегантные, а не хакерские. Нажимает "Добавить комментарий", а затем голосует :) - person Zimm3r; 14.08.2010