Python, suds — работа с гнилыми кэшами и ответами WSDL

Последние пару дней я бился головой о дурацкий SOAP-сервер.

В моих журналах ошибок я получал исключение/журнал трассировки, который выглядел так: «Исключение: (виджет, нет, ), должно быть qref» -- (ключевая фраза «должно быть qref»).

Сначала я разобрался, что у нас сгнил кеш. Поэтому я отключил кеширование, но проблемы остались.

Во-вторых, я понял, что на самом деле это вина сервера за то, что он случайно не предоставил мне правильный WSDL; особенно при использовании нескольких рабочих экземпляров. Новостная вспышка — мой контактный оператор сервера подтверждает ошибку «сервер слишком занят» в своих журналах. "Вандербар!" По-видимому, сервер перенаправляет на страницу HTTP 200, когда он должен был предоставить HTTP 503.

Мне было интересно, как быть более грациозным в обращении с этим удивительно плохим поведением, и я на самом деле придумал полезное, довольно общее решение. Публикация здесь для дальнейшего использования.


person starlocke    schedule 08.05.2014    source источник


Ответы (1)


Что-то вроде этого:

from suds.client import Client
from suds.transport.https import HttpAuthenticated
from suds.xsd.doctor import ImportDoctor, Import
import os
import shutil
import time
from tempfile import gettempdir as tmp

class MyTransport(HttpAuthenticated):
    def __init__(self,*args,**kwargs):
        HttpAuthenticated.__init__(self,*args,**kwargs)
        self.last_headers = None
    def send(self,request):
        result = HttpAuthenticated.send(self,request)
        self.last_headers = result.headers
        return result

class TenaciousConnector():
    def init_client(self, wsdl_url):
        retries = 3
        services = None
        # Cached mode
        (client, services) = self._init_client(wsdl_url)
        while not services and retries > 0:
            nap_time = 6 - retries
            retries = retries - 1
            time.sleep(nap_time)
            # clear potentially rotten cache right before retrying
            shutil.rmtree(os.path.join(tmp(), 'suds'), True)
            (client, services) = self._init_client(wsdl_url)
        if not services:
            # No-cache mode
            retries = 3
            (client, services) = self._init_client(wsdl_url, False)
            while not services and retries > 0:
                nap_time = 6 - retries
                retries = retries - 1
                time.sleep(nap_time)
                (client, services) = self._init_client(wsdl_url, False)
        if not services:
            raise Exception("Failed at loading WSDL from {0}".format(wsdl_url))
        return client

    def _init_client(self, wsdl_url, cache=True):
        i_doc = ImportDoctor(Import('http://schemas.xmlsoap.org/soap/encoding/'))
        tx = MyTransport()
        if cache:
            client = Client(wsdl_url, doctor=i_doc, transport=tx)
        else:
            client = Client(wsdl_url, doctor=i_doc, transport=tx, cache=None)
        services = client.wsdl.services
        return (client, services)

Используется примерно так:

connector = TenaciousConnector()
client = connector.init_client("http://example.com/webservice/wsdl")
client.factory.create('Widget')
person starlocke    schedule 08.05.2014