python xml events network-programming twisted

python - Twisted XmlStream: ¿Cómo conectarse a eventos?



events network-programming (2)

El "único" componente relevante de XmlStream es el analizador de SAX. Aquí es cómo he implementado un analizador asíncrono SAX usando XmlStream y solo las funciones de análisis XML:

server.py

from twisted.words.xish.domish import Element from twisted.words.xish.xmlstream import XmlStream class XmlServer(XmlStream): def __init__(self): XmlStream.__init__(self) # possibly unnecessary def dataReceived(self, data): """ Overload this function to simply pass the incoming data into the XML parser """ try: self.stream.parse(data) # self.stream gets created after self._initializestream() is called except Exception as e: self._initializeStream() # reinit the DOM so other XML can be parsed def onDocumentStart(self, elementRoot): """ The root tag has been parsed """ print(''Root tag: {0}''.format(elementRoot.name)) print(''Attributes: {0}''.format(elementRoot.attributes)) def onElement(self, element): """ Children/Body elements parsed """ print(''/nElement tag: {0}''.format(element.name)) print(''Element attributes: {0}''.format(element.attributes)) print(''Element content: {0}''.format(str(element))) def onDocumentEnd(self): """ Parsing has finished, you should send your response now """ response = domish.Element(('''', ''response'')) response[''type''] = ''type 01'' response.addElement(''content'', content=''some response content'') self.send(response.toXml())

Luego crea una clase de Fábrica que producirá este Protocolo (del que ha demostrado que es capaz). Básicamente, obtendrá toda su información del XML en las funciones onDocumentStart y onElement y cuando llegue al final (es decir, onDocumentEnd ) enviará una respuesta basada en la información analizada. Además, asegúrese de llamar a self._initializestream() después de analizar cada mensaje XML o de lo contrario obtendrá una excepción. Eso debería servir como un buen esqueleto para ti.

Mis respuestas a tus preguntas:

  1. No se :)
  2. Es muy razonable Sin embargo, normalmente solo XmlStream subclase de XmlStream (que simplemente se hereda del Protocol ) y luego uso un objeto Factory normal.
  3. Esto es algo de lo que debe preocuparse cuando use Twisted (+1 para usted). Usando el enfoque anterior, puede disparar devoluciones de llamada / errbacks mientras analiza y pulsa un elemento o esperar hasta llegar al final del XML y luego disparar sus devoluciones de llamada a su corazón. Espero que tenga sentido :/
  4. Me he preguntado esto también en realidad. Creo que tiene algo que ver con las aplicaciones y los protocolos que utilizan el objeto XmlStream (como Jabber e IRC). Solo sobrecargue onDocumentEnd y haga que haga lo que usted quiere que haga. Esa es la belleza de OOP.

Referencia:

PD

Su problema es bastante común y muy simple de resolver (al menos en mi opinión), así que no se suicide al tratar de aprender el modelo Event Dipatcher. En realidad, parece que tienes un buen manejo de devoluciones de llamada y errbacks (también conocido como Deferred ), por lo que te sugiero que te limites a ellos y evites al despachador.

Me gustaría implementar un servidor Twisted que espere solicitudes XML y envíe respuestas XML a cambio:

<request type=''type 01''><content>some request content</content></request> <response type=''type 01''><content>some response content</content></response> <request type=''type 02''><content>other request content</content></request> <response type=''type 02''><content>other response content</content></response>

He creado un cliente y un servidor Twisted before que se intercambiaran cadenas simples y traté de extenderlo al uso de XML, pero parece que no puedo encontrar la manera de configurarlo todo correctamente.

client.py:

#!/usr/bin/env python # encoding: utf-8 from twisted.internet import reactor from twisted.internet.endpoints import TCP4ClientEndpoint, connectProtocol from twisted.words.xish.domish import Element, IElement from twisted.words.xish.xmlstream import XmlStream class XMLClient(XmlStream): def sendObject(self, obj): if IElement.providedBy(obj): print "[TX]: %s" % obj.toXml() else: print "[TX]: %s" % obj self.send(obj) def gotProtocol(p): request = Element((None, ''request'')) request[''type''] = ''type 01'' request.addElement(''content'').addContent(''some request content'') p.sendObject(request) request = Element((None, ''request'')) request[''type''] = ''type 02'' request.addElement(''content'').addContent(''other request content'') reactor.callLater(1, p.sendObject, request) reactor.callLater(2, p.transport.loseConnection) endpoint = TCP4ClientEndpoint(reactor, ''127.0.0.1'', 12345) d = connectProtocol(endpoint, XMLClient()) d.addCallback(gotProtocol) from twisted.python import log d.addErrback(log.err) reactor.run()

Como se mencionó en el enfoque anterior basado en cadenas, el cliente permanece inactivo hasta CTRL + C. Una vez que lo haga, se inspirará / inspirará en el ejemplo de Twisted XMPP .

server.py:

#!/usr/bin/env python # encoding: utf-8 from twisted.internet import reactor from twisted.internet.endpoints import TCP4ServerEndpoint from twisted.words.xish.xmlstream import XmlStream, XmlStreamFactory from twisted.words.xish.xmlstream import STREAM_CONNECTED_EVENT, STREAM_START_EVENT, STREAM_END_EVENT REQUEST_CONTENT_EVENT = intern("//request/content") class XMLServer(XmlStream): def __init__(self): XmlStream.__init__(self) self.addObserver(STREAM_CONNECTED_EVENT, self.onConnected) self.addObserver(STREAM_START_EVENT, self.onRequest) self.addObserver(STREAM_END_EVENT, self.onDisconnected) self.addObserver(REQUEST_CONTENT_EVENT, self.onRequestContent) def onConnected(self, xs): print ''onConnected(...)'' def onDisconnected(self, xs): print ''onDisconnected(...)'' def onRequest(self, xs): print ''onRequest(...)'' def onRequestContent(self, xs): print ''onRequestContent(...)'' class XMLServerFactory(XmlStreamFactory): protocol = XMLServer endpoint = TCP4ServerEndpoint(reactor, 12345, interface=''127.0.0.1'') endpoint.listen(XMLServerFactory()) reactor.run()

Salida de client.py :

TX [127.0.0.1]: <request type=''type 01''><content>some request content</content></request> TX [127.0.0.1]: <request type=''type 02''><content>other request content</content></request>

Salida de server.py :

onConnected(...) onRequest(...) onDisconnected(...)

Mis preguntas:

  1. ¿Cómo me suscribo a un evento activado cuando el servidor encuentra una determinada etiqueta XML? La consulta de //request/content XPath me parece bien, pero onRequestContent(...) no se llama :-(
  2. ¿La subclasificación de XmlStream y XmlStreamFactory un enfoque razonable? Se siente raro porque XMLServer suscribe a eventos enviados por su propia clase base y luego se pasa a sí mismo (?) Como parámetro xs ?!? ¿Debería hacer que XMLServer una clase normal y tener un objeto XmlStream como miembro de la clase? ¿Hay un enfoque canónico ?
  3. ¿Cómo agregaría un controlador de errores al servidor como addErrback(...) en el cliente? Me preocupa que las excepciones sean tragadas (sucedieron antes), pero no veo de dónde obtener un Deferred para adjuntarlo a ...
  4. ¿Por qué el servidor cierra la conexión por defecto después de la primera solicitud? Veo XmlStream.onDocumentEnd(...) llamando a loseConnection() ; Podría anular ese método, pero me pregunto si hay una razón para el cierre que no veo. ¿No es el enfoque "normal" dejar la conexión abierta hasta que se haya realizado toda la comunicación necesaria hasta el momento?

Espero que esta publicación no sea considerada demasiado específica; hablar de XML a través de la red es un lugar común, pero a pesar de buscar un día y medio, no pude encontrar ningún ejemplo de servidor XML retorcido. Tal vez logre convertir esto en un punto de partida para cualquier persona en el futuro con preguntas similares ...


Esto es principalmente una suposición, pero por lo que sé, necesita abrir la transmisión enviando una estrofa sin cerrarla.

En su ejemplo, cuando envía <request type=''type 01''><content>some request content</content></request> el servidor ve la stanza <request> como el start document pero luego envía </request> y El servidor lo verá como el end document .

Básicamente, su servidor consume <request> como documento de inicio y por eso su xpath, //request/content , no coincidirá, porque todo lo que queda del elemento es <content>...</content> .

Intente enviar algo como <stream> desde el cliente primero, luego las dos solicitudes y luego </stream> .

Además, la subclasificación de XmlStream está bien siempre y cuando se asegure de no anular ningún método de forma predeterminada.