encoding / xmlrpclib Problem

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
Damaskus
Administrator
Beiträge: 919
Registriert: Sonntag 6. März 2005, 20:08
Wohnort: Schwabenländle

Samstag 16. Februar 2008, 12:04

Guten Morgen,
ich hänge mal wieder an einem "Encoding Fehler" fest.

Ich übertrage per XMLRPC einen String der Sonderzeichen enthält von einem Windows PC an einen Debian Server. Beide Programme sind mit coding iso-8859-1 codiert und gespeichert.
Das Windows Programm erhält den String per sys.argv von einem anderen Programm.

Mein Problem ist jetzt das die Sonderzeichen nicht übertragen werden bzw. der Fehler
xml.parsers.expat.ExpatError: not well-formed (invalid token)
ausgegeben wird. Auch div. umcodieren und wandeln in unicode etc. hat nicht den gewünschten Erfolg gebracht.

Codeschnippsel kann ich euch leider keine posten da das Script zu groß ist.

Für einen Denkanstoß wäre ich recht dankbar.

Gruß
Damaskus
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Samstag 16. Februar 2008, 13:18

Der Fehler tritt auf, wenn ein Null-Byte in den Daten vorhanden ist. Vielleicht einfach xmlrpclib.Binary als Container für die Daten benutzen.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Samstag 16. Februar 2008, 16:16

Damaskus hat geschrieben:Ich übertrage per XMLRPC einen String der Sonderzeichen enthält von einem Windows PC an einen Debian Server... Für einen Denkanstoß wäre ich recht dankbar.
Hallo Damaskus!

Ohne Code keine genaueren Hinweise. Aber hier ein paar Gedanken:

Irgendwo habe ich das mal gelesen -- ich weiß nicht mehr wo.

- XMLRPC erwartet UTF-8 codierte Strings.
- Der Server sollte UTF-8 erwarten und UTF-8 zurück geben.
- Der Client sollte UTF-8 erwarten und UTF-8 zurück geben.
Damaskus hat geschrieben:Das Windows Programm erhält den String per sys.argv von einem anderen Programm.
Dann muss der String vorher von ``sys.getfilesystemencoding()`` über Unicode nach UTF-8 umgewandelt werden, bevor dieser über XMLRPC weitergereicht werden kann.
Damaskus hat geschrieben:xml.parsers.expat.ExpatError: not well-formed (invalid token)
???
Das kann vieles sein. Aber schau dir doch mal das an, was XMLRPC aus deinem String macht. Das hilft mir beim Fehler finden:

Code: Alles auswählen

>>> xmlrpclib.dumps((u"öäü",))
'<params>\n<param>\n<value><string>\xc3\xb6\xc3\xa4\xc3\xbc</string></value>\n</param>\n</params>\n'
>>> xmlrpclib.dumps(("öäü",))
'<params>\n<param>\n<value><string>\xf6\xe4\xfc</string></value>\n</param>\n</params>\n'
>>> xmlrpclib.dumps((u"öäü".encode("utf-8"),))
'<params>\n<param>\n<value><string>\xc3\xb6\xc3\xa4\xc3\xbc</string></value>\n</param>\n</params>\n'
>>> 
Wie man sieht, wird der Bytecode nicht umgewandelt. Zwischen einem Unicode-String und UTF-8 besteht aber kein Unterschied. Unicode wird intern also nach UTF-8 umgewandelt.

Und den Gedanken von Trundle würde ich auch nachverfolgen. Mit ``xmlrpclib.dumps`` hast du dafür ja ein Mittel in der Hand.

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
Damaskus
Administrator
Beiträge: 919
Registriert: Sonntag 6. März 2005, 20:08
Wohnort: Schwabenländle

Sonntag 17. Februar 2008, 22:12

Nabend,
nach langem Suchen an falscher Stelle hab ich den Fehler gefunden.
Es ist nicht XMLRPC dran schuld sondern die auf dem Server werden die Daten in ein Dictionary gepackt und von da aus dann per urlencode codiert.
Die Übernahme ins Dic. erfolgt automatisch in Unicode aber das abrufen der DAten mit urlencode decodiert den Unicode String leider nicht.

Codeschnippsel gibts morgen dazu.

Gruß
Damaskus
Benutzeravatar
Damaskus
Administrator
Beiträge: 919
Registriert: Sonntag 6. März 2005, 20:08
Wohnort: Schwabenländle

Montag 18. Februar 2008, 15:15

So nach etwas hin und her weiss ich nicht mehr richtig weiter.

Wenn ich folgendes Script ausführe funktioniert alles wie gewünscht:

Code: Alles auswählen

import urllib

s1 = "Ich bin ein Test"
s2 = "Ich bin der gleiche Test mit ä"
s3 = "Und ich mit öü und ß"
print s3

>>> Und ich mit öü und ß

data = {}
data["s1"] = s1
data["s2"] = s2
data["s3"] = s3
print data

>>> {'s3': 'Und ich mit \xc3\xb6\xc3\xbc und \xc3\x9f', 's2': 'Ich bin der gleiche Test mit \xc3\xa4', 's1': 'Ich bin ein Test'}

s4 = urllib.urlencode(data)
print s4

>>> s3=Und+ich+mit+%C3%B6%C3%BC+und+%C3%9F&s2=Ich+bin+der+gleiche+Test+mit+%C3%A4&s1=Ich+bin+ein+Test
übertrage ich s1, s2, s3 mit XMLRPC vom Client zum Server dann kann er das Dic. nicht richtig verarbeiten.

Hat jemand eine Idee dazu?

Gruß
Damaskus
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Montag 18. Februar 2008, 15:38

Hallo Damaskus!

Warum ``urllib``?

Code: Alles auswählen

>>> import xmlrpclib as x
>>> xml = x.dumps(
...     (
...         {
...             's3': 'Und ich mit \xc3\xb6\xc3\xbc und \xc3\x9f', 
...             's2': 'Ich bin der gleiche Test mit \xc3\xa4', 
...             's1': 'Ich bin ein Test'
...         },
...     )
... )
>>> x.loads(xml)
(({'s3': u'Und ich mit \xf6\xfc und \xdf', 
's2': u'Ich bin der gleiche Test mit \xe4', 
's1': 'Ich bin ein Test'},), None)
>>>
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Montag 18. Februar 2008, 15:51

server.py:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

from SimpleXMLRPCServer import SimpleXMLRPCServer
from random import randint

def get_data():
    
    d = {
        "s1": u"Ich bin am Client zwar nicht Unicode, aber ohne Fehler konvertierbar",
        "s2": u"Ich bin der gleiche Test mit ä (ich bin Unicode)",
        "s3": u"Und ich mit öü und ß (ich bin Unicode)",
    }
    return d

server = SimpleXMLRPCServer(("localhost", 50505))
server.register_function(get_data)
print "Der XMLRPC-Server horcht auf http://localhost:50505."
print "Er kann mit STRG+C beendet werden."
server.serve_forever()
client.py:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

import socket
socket.setdefaulttimeout(3) # Timeout auf 3 sec. setzen
import xmlrpclib

server = xmlrpclib.ServerProxy("http://localhost:50505")
print server.get_data()
Ausgabe am Client:

Code: Alles auswählen

{'s3': u'Und ich mit \xf6\xfc und \xdf (ich bin Unicode)', 
's2': u'Ich bin der gleiche Test mit \xe4 (ich bin Unicode)', 
's1': 'Ich bin am Client zwar nicht Unicode, aber ohne Fehler konvertierbar'}
Wenn Server und Client Pythonprogramme sind, dann gilt:
Unicode rein -- Unicode raus

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
Damaskus
Administrator
Beiträge: 919
Registriert: Sonntag 6. März 2005, 20:08
Wohnort: Schwabenländle

Dienstag 19. Februar 2008, 09:20

Hallo Gerold,
ich glaub ich hab mein Problem falsch geschildert.

Anfangs dachte ich der Fehler liegt an der Übertragung der Sonderzeichen per XMLRPC. War aber ein Fehler von mir, die Übertragung zwischen Client und Server funktioniert problemlos. Hier werden die Daten automatisch per Unicode übertragen.
Später hab ich festgestellt das der Fehler nur in Kombination folgender Schritte auftritt und eher bei urllib.urlencode zu suchen ist.

1. übergabe der Strings per sys.argv
2. übertragung von Windows Client auf Debian Server per xmlrpc (beides Python)
3. Strings in ein Dictionary packen
4. Strings aus dem Dictionary mit urllib.urlencode zu einem HTTP Request umschreiben

server.py

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

from SimpleXMLRPCServer import SimpleXMLRPCServer
import urllib

def mach_was(s1, s2, s3):

   data = {} 
   data["s1"] = s1
   data["s2"] = s2
   data["s3"] = s3
   link = "http://www.test.de/script.php"
   s4 = urllib.urlencode(data)
   return True

server = SimpleXMLRPCServer(("localhost", 50505))
server.register_function(mach_was)
print "Der XMLRPC-Server horcht auf http://localhost:50505."
print "Er kann mit STRG+C beendet werden."
server.serve_forever()
client.py

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

import socket
socket.setdefaulttimeout(3) # Timeout auf 3 sec. setzen
import xmlrpclib

server = xmlrpclib.ServerProxy("http://localhost:50505")
s1 = u"Ich bin ein Test"
s2 = u"Ich bin der gleiche Test mit ä"
s3 = u"Und ich mit öü und ß" 
server.mach_was(s1, s2, s3)
Ich hoffe das obiges Bsp. den Fehler aufzeigt.

Gruß
Damaskus
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Dienstag 19. Februar 2008, 09:50

Hallo Damaskus!

So wie es aussieht, kann urllib nichts mit Unicode anfangen. Ich halte das für schlampig und einen Bug. :cry:

Ich habe da mal ein wenig getestet:

Code: Alles auswählen

>>> import urllib
>>> data = {
...     "s1": u"Ich bin ein Test",
...     "s2": u"Ich bin der gleiche Test mit ä",
...     "s3": u"Und ich mit öü und ß"
... }
>>> data
{'s3': u'Und ich mit \xf6\xfc und \xdf', 's2': u'Ich bin der gleiche Test mit \xe4', 's1': u'Ich bin ein Test'}
>>> urllib.urlencode(data)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "J:\Python25\lib\urllib.py", line 1250, in urlencode
    v = quote_plus(str(v))
UnicodeEncodeError: 'ascii' codec can't encode characters in position 12-13: ordinal not in range(128)
>>> data = {
...     "s1": u"Ich bin ein Test".encode("utf-8"),
...     "s2": u"Ich bin der gleiche Test mit ä".encode("utf-8"),
...     "s3": u"Und ich mit öü und ß".encode("utf-8")
... }
>>> data
{'s3': 'Und ich mit \xc3\xb6\xc3\xbc und \xc3\x9f', 's2': 'Ich bin der gleiche Test mit \xc3\xa4', 's1': 'Ich bin ein Test'}
>>> urllib.urlencode(data)
's3=Und+ich+mit+%C3%B6%C3%BC+und+%C3%9F&s2=Ich+bin+der+gleiche+Test+mit+%C3%A4&s1=Ich+bin+ein+Test'
>>> data = {
...     "s1": u"Ich bin ein Test".encode("iso-8859-1"),
...     "s2": u"Ich bin der gleiche Test mit ä".encode("iso-8859-1"),
...     "s3": u"Und ich mit öü und ß".encode("iso-8859-1")
... }
>>> 
>>> urllib.urlencode(data)
's3=Und+ich+mit+%F6%FC+und+%DF&s2=Ich+bin+der+gleiche+Test+mit+%E4&s1=Ich+bin+ein+Test'
>>> 
Übergibt man an ``urllib.urlencode`` ein Dictionary mit Unicode, dann wirft es einen Fehler.
Übergibt man an ``urllib.urlencode`` ein dictionary mit UTF-8- oder ISO-8859-1-Bytestrings, dann liefert es je ein Ergebnis. Leider zwei verschiedene Ergebnisse.

Im Internet Explorer kann man einstellen, ob URLs immer als UTF-8 übertragen werden sollen. Daraus schließe ich, dass du evt. zu einem guten Ergebnis kommen könntest, wenn du die Strings vor dem Umwandeln mit ``urllib.urlencode`` nach UTF-8 umwandelst. Aber trotzdem finde ich das Verhalten von ``urllib.urlencode`` für dringend nachbesserungswürdig. Da gehört unbedingt noch Unicodehandling rein.

lg,
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
Damaskus
Administrator
Beiträge: 919
Registriert: Sonntag 6. März 2005, 20:08
Wohnort: Schwabenländle

Dienstag 19. Februar 2008, 10:53

Hallo Gerold,
genau so etwas in der Art habe ich vermutet.
Ich hab das Problem jetzt folgendermaßen gelöst.

server.py:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

from SimpleXMLRPCServer import SimpleXMLRPCServer
import urllib

def mach_was(s1, s2, s3):
   print s1, s2, s3
   data = {} 
   data["s1"] = s1.encode("utf-8")
   data["s2"] = s2.encode("utf-8")
   data["s3"] = s3.encode("utf-8")
   link = "http://www.test.de/script.php"
   print data
   s4 = urllib.urlencode(data)
   print s4
   return True

server = SimpleXMLRPCServer(("localhost", 50505))
server.register_function(mach_was)
print "Der XMLRPC-Server horcht auf http://localhost:50505."
print "Er kann mit STRG+C beendet werden."
server.serve_forever()
Jetzt muss ich nur noch testen welches Coding der Server der den Request erhält korrekt verarbeitet, was aber kein Problem. Das ganze Steuert dann ein SMS Gateway.

Danke für die Hilfe!

Gruß
Damaskus
Antworten