[python] Pylons/Mako in WSGI geeft UnicodeDecodeError

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • GX
  • Registratie: Augustus 2000
  • Laatst online: 14-05 09:40
Beste medetweakers,

deze week probeer ik een Pylons 1.0-applicatie te plaatsen onder Apache. Tot nu toe hebben we alle ontwikkelingen gedaan met behulp van paster, voor het eenvoudigere debug en testwerk. Daarin kwamen we in totaal geen problemen tegen.

Echter, onder mod_wsgi krijgen we overal UnicodeDecodeErrors waar we karakters gebruiken buiten de ASCII-range, zoals bijvoorbeeld ë, ï of à. Voorbeeld van een stuk mako-template die zo'n code genereert:

HTML:
1
<p>${c.candidate.getHeadline()}</p>


Waarin getHeadline een unicode-string teruggeeft die "hällo" heet.

De configuratie van Pylons en mako is hier al opgericht, zie het volgende segment uit de environment.ini:

Python:
1
2
3
4
5
6
7
    config['pylons.app_globals'].mako_lookup = TemplateLookup(
        directories=paths['templates'],
        error_handler=handle_mako_error,
        module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
        input_encoding='utf-8', default_filters=['escape'],
        output_encoding='utf-8',
        imports=['from webhelpers.html import escape'])


We hebben ondertussen ook al andere dingen geprobeerd die we op willekeurige sites vonden. Veel suggesties waren voor 0.9.7 of lager en moeilijk of niet toepasbaar. Anderen maakten gewoon niets uit. Een mooie samenvatting daarvan:

• Entiteiten zoals ${c.candidate.getHeadline()} vervangen door ${c.candidate.getHeadline() | unicode};
• Input en output encoding expliciet geconfigureerd;
• Bestanden prefixen met encoding-regel (# -*- encoding: utf-8 -*-);
• Een default-filter toegepast.

Maar het mocht niet baten. Hebben jullie allicht een idee waardoor het komt?

Acties:
  • 0 Henk 'm!

  • GX
  • Registratie: Augustus 2000
  • Laatst online: 14-05 09:40
Niemand van jullie dit eerder tegengekomen? Het probleem knaagt wel een beetje.

Acties:
  • 0 Henk 'm!

  • Xudonax
  • Registratie: November 2010
  • Laatst online: 15-09 10:16
Ik kan het zo 1-2-3 niet terugvinden, maar ik meen op de mod_wsgi mailinglist gezien te hebben dat WSGI enkel ASCII of ISO 8859-15 tekens lust. Is het mogelijk om een simpele Pylons applicatie te proberen welke enkel ASCII tekens als uitvoer geeft en een die ISO 8859-15 tekens als uitvoer geeft?

De betreffende link die ik op de mod_wsgi mailinglijst zag: http://www.python.org/dev...3/#a-note-on-string-types.
Je kunt dit ook op de mod_wsgi Google Group proberen natuurlijk :)

Acties:
  • 0 Henk 'm!

  • WouZz
  • Registratie: Mei 2000
  • Niet online

WouZz

Elvis is alive!

Het lijkt een bekend probleem te zijn. Ik zou zeggen, verder debuggen testen etc. Vergeet trouwens niet dat je onder Apache / WSGI je web app moet herladen (beter gezegd je source code moet reloaden) om wijzigingen door te voeren.

[ Voor 8% gewijzigd door WouZz op 29-01-2011 17:52 ]

On track


Acties:
  • 0 Henk 'm!

  • ValHallASW
  • Registratie: Februari 2003
  • Niet online
Kan je eens de volledige stacktrace posten? Een UnicodeDecodeError is namelijk vreemd: dat impliceert dat de string eerst naar een bytestring wordt omgezet (wat redelijk is, gegeven de output_encoding='utf-8') maar vervolgens weer wordt ingelezen met ascii als encoding!

Wat je kunt proberen, maar dat is wel heel ranzig, is example 9.14 van de Dive into Python tutorial. Dat lost je probleem alleen niet echt op - er zit immers nog steeds ergens een bug, maar die wordt door een andere default niet meer getriggerd.

Acties:
  • 0 Henk 'm!

  • GX
  • Registratie: Augustus 2000
  • Laatst online: 14-05 09:40
Hier een volledige stack, met enige redactie. Ik twijfel er sterk aan of het je überhaupts iets verteld. Het gaat fout op een regel output (rechtstreeks uit de database) waarin een ë staat.

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
mod_wsgi (pid=18718): Exception occurred within WSGI script '~/www/conf/lalalla.wsgi'.
Traceback (most recent call last):
  File "~/www/env/pylons10/lib/python2.6/site-packages/Paste-1.7.5.1-py2.6.egg/paste/cascade.py", line 130, in __call__
    return self.apps[-1](environ, start_response)
  File "~/www/env/pylons10/lib/python2.6/site-packages/Paste-1.7.5.1-py2.6.egg/paste/registry.py", line 379, in __call__
    app_iter = self.application(environ, start_response)
  File "~/www/env/pylons10/lib/python2.6/site-packages/Beaker-1.5.4-py2.6.egg/beaker/middleware.py", line 152, in __call__
    return self.wrap_app(environ, session_start_response)
  File "~/www/env/pylons10/lib/python2.6/site-packages/Routes-1.12.3-py2.6.egg/routes/middleware.py", line 131, in __call__
    response = self.app(environ, start_response)
  File "~/www/env/pylons10/lib/python2.6/site-packages/Pylons-1.0-py2.6.egg/pylons/wsgiapp.py", line 107, in __call__
    response = self.dispatch(controller, environ, start_response)
  File "~/www/env/pylons10/lib/python2.6/site-packages/Pylons-1.0-py2.6.egg/pylons/wsgiapp.py", line 312, in dispatch
    return controller(environ, start_response)
  File "~/www/app/lalalla/src/lalalla/lalalla/lib/base.py", line 22, in __call__
    return WSGIController.__call__(self, environ, start_response)
  File "~/www/env/pylons10/lib/python2.6/site-packages/Pylons-1.0-py2.6.egg/pylons/controllers/core.py", line 211, in __call__
    response = self._dispatch_call()
  File "~/www/env/pylons10/lib/python2.6/site-packages/Pylons-1.0-py2.6.egg/pylons/controllers/core.py", line 162, in _dispatch_call
    response = self._inspect_call(func)
  File "~/www/env/pylons10/lib/python2.6/site-packages/Pylons-1.0-py2.6.egg/pylons/controllers/core.py", line 105, in _inspect_call
    result = self._perform_call(func, args)
  File "~/www/env/pylons10/lib/python2.6/site-packages/Pylons-1.0-py2.6.egg/pylons/controllers/core.py", line 57, in _perform_call
    return func(**args)
  File "~/www/app/lalalla/src/lalalla/lalalla/controllers/intake.py", line 17, in detail
    return render('/pages/intake/intake.html')
  File "~/www/env/pylons10/lib/python2.6/site-packages/Pylons-1.0-py2.6.egg/pylons/templating.py", line 243, in render_mako
    cache_type=cache_type, cache_expire=cache_expire)
  File "~/www/env/pylons10/lib/python2.6/site-packages/Pylons-1.0-py2.6.egg/pylons/templating.py", line 218, in cached_template
    return render_func()
  File "~/www/env/pylons10/lib/python2.6/site-packages/Pylons-1.0-py2.6.egg/pylons/templating.py", line 240, in render_template
    return literal(template.render_unicode(**globs))
  File "~/www/env/pylons10/lib/python2.6/site-packages/Mako-0.3.6-py2.6.egg/mako/template.py", line 292, in render_unicode
    as_unicode=True)
  File "~/www/env/pylons10/lib/python2.6/site-packages/Mako-0.3.6-py2.6.egg/mako/runtime.py", line 575, in _render
    **_kwargs_for_callable(callable_, data))
  File "~/www/env/pylons10/lib/python2.6/site-packages/Mako-0.3.6-py2.6.egg/mako/runtime.py", line 607, in _render_context
    _exec_template(inherit, lclcontext, args=args, kwargs=kwargs)
  File "~/www/env/pylons10/lib/python2.6/site-packages/Mako-0.3.6-py2.6.egg/mako/runtime.py", line 628, in _exec_template
    _render_error(template, context, e)
  File "~/www/env/pylons10/lib/python2.6/site-packages/Mako-0.3.6-py2.6.egg/mako/runtime.py", line 637, in _render_error
    result = template.error_handler(context, error)
  File "~/www/env/pylons10/lib/python2.6/site-packages/Mako-0.3.6-py2.6.egg/mako/runtime.py", line 626, in _exec_template
    callable_(context, *args, **kwargs)
  File "~/www/app/lalalla/src/lalalla/data/templates/pages/intake/intake.html.py", line 79, in render_body
    __M_writer(unicode(filters.decode.utf8(h.literal(c.intake.candidate.website_title) )))
  File "~/www/env/pylons10/lib/python2.6/site-packages/MarkupSafe-0.11-py2.6-linux-x86_64.egg/markupsafe/__init__.py", line 71, in __new__
    return unicode.__new__(cls, base)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 8: ordinal not in range(128)


De regel is dus intake.html.py: line 79 (één na laatste file).

Het zou natuurlijk uitermate ridicuul zijn als mod_wsgi geen unicode aankan, waar paster dat wel probleemloos doet. Ook _iedere_ call naar mogelijke-utf-8-output wijzigen is meer dan weken werk, dus dat is geen oplossing.

Acties:
  • 0 Henk 'm!

  • ValHallASW
  • Registratie: Februari 2003
  • Niet online
Pfoe, dat is wel een flinke stacktrace ja. Er staan echter wel een aantal interessante dingen in:
code:
1
2
  File "~/www/env/pylons10/lib/python2.6/site-packages/Mako-0.3.6-py2.6.egg/mako/runtime.py", line 628, in _exec_template
    _render_error(template, context, e)

Er lijkt hier al sprake te zijn van foutafhandeling. Ik neem aan dat deze pagina geen foutmeldingspagina zou moeten zijn, dus dat doet vermoeden dat het al eerder fout gaat!

code:
1
2
  File "~/www/app/lalalla/src/lalalla/data/templates/pages/intake/intake.html.py", line 79, in render_body
    __M_writer(unicode(filters.decode.utf8(h.literal(c.intake.candidate.website_title) )))

Dit is een vrij nare oneliner, maargoed. Dit biedt in ieder geval een aanknopingspunt om te beginnen met debuggen. Voeg vóór die regel eens de regels
code:
1
2
3
4
c.intake.candidate.website_title
h.literal(c.intake.candidate.website_title)
filters.decode.utf8(h.literal(c.intake.candidate.website_title)
unicode(filters.decode.utf8(h.literal(c.intake.candidate.website_title)

toe, en kijk of er daar al iets foutgaat - dan is het in ieder geval in welke functie het foutgaat.

Dan als laatste opvallende punt:
code:
1
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 8: ordinal not in range(128)

0xe2 is een vreemde eet in de byte. Dat is in latin-1 een Â, maar in utf-8 bij west-europese tekens onverwacht het impliceert namelijk een teken in de reeks 0x0800 tot 0xFFFF.
Het zou natuurlijk uitermate ridicuul zijn als mod_wsgi geen unicode aankan, waar paster dat wel probleemloos doet.
Nee. Mod_wsgi heeft namelijk helemaal niets te maken met je encoding - die stuurt alleen maar bytes door. Het is aan je framework om van je unicode strings een goede representatie, zij het in utf-8, zij het in latin-1 of wat mij betreft shift-jis, te maken. Het lijkt erop dat het framework dat goed doet voor paster, maar niet voor mod_wsgi. Wellicht dat paster er maar gewoon vanuitgaat dat de output wel utf-8 zou moeten zijn.

Enfin, allereerst zou ik met bovenstaande regels code kijken waar het probleem zich laat zien.
Stel dat dat op de regel "h.literal(c.intake.candidate.website_title)" gebeurt, dan zou ik een regel daarvoor
code:
1
raise Exception(repr(c.intake.candidate.website_title))

neerzetten en het resultaat daarvan bekijken (in je error log, wederom). Dit vertelt je namelijk wat de precieze input voor de volgende functie is, zodat je met behulp van de python-console even her en der verder kunt prikken.

Acties:
  • 0 Henk 'm!

  • GX
  • Registratie: Augustus 2000
  • Laatst online: 14-05 09:40
die .html.py files worden gegenereerd door Mako. Wat we feitelijke regel aan Mako voeren is:

code:
1
2
3
4
5
6
7
8
103                 <tr>
104                     <td>Titel op website (max 33 psts):</td>
105                     <td>
106 %if c.intake.candidate.website_title:
107                     ${h.literal(c.intake.candidate.website_title)}
108 %endif
109                     </td>
110                 </tr>


Ik durf zelf niet goed in gegenereerde mako templates te kloten, dus het debuggen zoals jij aanvoerde gaat niet zo makkelijk, het in paster debuggen heeft uiteraard ook geen zin. De server staat me dat ook niet toe trouwens, met hun idiote inrichting.

Ik sta wel open voor andere suggesties.

P.S. val, wanneer kom je weer eens op IRC?

Acties:
  • 0 Henk 'm!

  • djc
  • Registratie: December 2001
  • Laatst online: 08-09 23:18

djc

GX schreef op zondag 30 januari 2011 @ 15:48:
Ik durf zelf niet goed in gegenereerde mako templates te kloten, dus het debuggen zoals jij aanvoerde gaat niet zo makkelijk, het in paster debuggen heeft uiteraard ook geen zin. De server staat me dat ook niet toe trouwens, met hun idiote inrichting.

Ik sta wel open voor andere suggesties.
Gebruik Jinja in plaats van Mako?

In ieder geval, wat zijn c, c.candidate, en c.candidate.headline()? Het lijkt alsof je een beetje voor de vuist weg oplossingen aan het proberen bent zonder echt tot de kern van het probleem te komen.

Rustacean


Acties:
  • 0 Henk 'm!

  • GX
  • Registratie: Augustus 2000
  • Laatst online: 14-05 09:40
djc schreef op maandag 31 januari 2011 @ 12:40:
[...]
Gebruik Jinja in plaats van Mako?
Hey! Bedankt voor de input! Goed idee ook, aan het einde van een multi-maand project. Ik vraag niet of een ander framework een oplossing is. Stel anders voor om de hele applicatie in C# te herschrijven.
In ieder geval, wat zijn c, c.candidate, en c.candidate.headline()? Het lijkt alsof je een beetje voor de vuist weg oplossingen aan het proberen bent zonder echt tot de kern van het probleem te komen.
c is tmpl_context, c.candidate is een model-object, en getHeadline() is een functie die een unicode string teruggeeft. Als je ook maar een béétje bekend met met Pylons of Mako had je deze vragen niet hoeven stellen. Als ik moeite moet stoppen in mijn threads hier, mag jij ook best wat moeite stoppen in je ondoordachte reacties.

Ik heb het idee dat jij geen idee hebt waar je over praat, dus houd je alsjeblieft weg. Andere mensen in deze draad hebben al zinnigere dingen gezegd.

Acties:
  • 0 Henk 'm!

  • GX
  • Registratie: Augustus 2000
  • Laatst online: 14-05 09:40
Ok! Met dank aan Valhalla is het opgelost. Het probleem is altijd iets waar je al de hele tijd over heen kijkt of dat net een gedachtekronkel nodig heeft. In dit geval dus ook, het was simpel:

code:
1
sqlalchemy.convert_unicode=True


toevoegen in de configuratie lost het op; de teksten uit de database kwamen rauw door als bytestring. Dat paster dat pakt is puur coulance, maar het hoort niet.

Let wel op dat als je je software bouwt, je wel je pickles en dergelijke nog even moet bijwerken (die snappen unicode niet).

Nogmaals bedankt, ValHallASW!
Pagina: 1