Forum Programmation.python (débutant) Comment corriger une erreur unicode bébête et classique ?

Posté par  (site web personnel) . Licence CC By‑SA.
Étiquettes : aucune
1
26
avr.
2017

Je ne connais ni Python, ni l'objet, mais je dois faire marcher un script, dont le mainteneur tarde à répondre. Le pire c'est qu'il est payé… Il y a une erreur classique avec unicode:
UnicodeEncodeError: 'ascii' codec can't encode character u'\xa0' in position 20: ordinal not in range(128)

Quand les valeurs n'ont pas d'accent, tout marche, sinon tout plante, lors de l'utilisation de str(). J'ai vérifié tout ce que j'ai pu, tenté de comprendre, mais je ne sais pas faire marcher correctement str(). Je sais que par défaut ça prend de l'ASCII. Le script est préfixé par # -- coding: utf-8 -- et enregistré en UTF8.

Le script récupère les valeurs d'un formulaire dans un tableau (je crois) :

tx_values.update({
            # infos
            'crap_name': values.get('partner_name') and values.get('partner_name')[0:126].encode('utf-8') or '',
            'crap_zip': values.get('partner_zip') and values.get('partner_zip')[0:62].encode('utf-8') or '',
})

Puis les passe à une fonction "crapoto":

tx_values['calc_crapoto'] = self._generate_crapoto(self, tx_values)
        return tx_values

Voilà la fonction ; elle plante sur str() — je crois comprendre qu'il s'agit de convertir en chaîne ?

    def _generate_crapoto(self, crap_values):
        crapoto = ''
        for key in sorted(crap_values.iterkeys()):
            if key.startswith('crap_'):
                crapoto += str(crap_values[key]).decode('utf-8') + '+'
        crapoto += etc.
        etc.

Bien entendu, j'ai bricolé pas mal, je vous passe la liste, mais ne connaissant pas Python je bloque.

  • # int vers string, entier vers chaîne

    Posté par  (site web personnel) . Évalué à 2.

    J'oubliais
    Je ne comprends pas si tx_values contient des entiers (int) ou des chaînes (str).
    Si par exemple je fais

    crapoto += unicode(crap_values[key], 'ascii', errors='replace').decode('utf-8') + '+'

    Python sort un TypeError: coercing to Unicode: need string or buffer, int found

    "La liberté est à l'homme ce que les ailes sont à l'oiseau" Jean-Pierre Rosnay

    • [^] # Re: int vers string, entier vers chaîne

      Posté par  (site web personnel) . Évalué à 3. Dernière modification le 26 avril 2017 à 23:45.

      sûrement un script pensé pour Python 2 et exécuté avec Python 3.

      En Python 2 :
      "toto" est une string de bytes (et quand tu la parcours, tu obtiens "t", "o", "t", "o"), avec interdiction d'utiliser des accents
      u"toto" est une string de caractères unicode (et quand tu le parcours, tu obtiens u"t", u"o", u"t", u"o") , et tu peux utiliser des accents
      Tu peux aussi faire "to" + u"to" == u"toto", "to" + u"tô" == u"totô", mais pas "tô" + u"to" (UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 1: ordinal not in range(128))

      En Python 3 :
      "toto" est une string de caractères unicode (et quand tu le parcours, tu obtiens "t", "o", "t", "o") , et tu peux utiliser des accents
      b"toto" est une string de bytes (et quand tu la parcours, tu obtiens les codes ASCII : 116, 111, 116, 111), avec interdiction d'utiliser des accents
      Tu ne peux pas mixer les deux.

    • [^] # Re: int vers string, entier vers chaîne

      Posté par  (site web personnel) . Évalué à 3.

      Il se peut qu'il y ait les deux types stockés dans le dictionnaire.
      Place un print type(crap_values[key]) avant la ligne de concaténation pour vérifier.

      Python 3 - Apprendre à programmer dans l'écosystème Python → https://www.dunod.com/EAN/9782100809141

      • [^] # Re: int vers string, entier vers chaîne

        Posté par  (site web personnel) . Évalué à 2.

        Effectivement c'est un beau mélange !
        J'ai fait (à force de bricoler je finis par comprendre un peu Python):

        type_msg = key, vads_values[key], type(vads_values[key])
        _logger.error(type_msg)

        et ça donne :

        ('crap_action_mode', 'INTERACTIVE', <type 'str'>)
        ('crap_amount', 7490, <type 'int'>)
        ('crap_contrib', 'Odoo10_0.9.1/10.0-20170414', <type 'str'>)
        ('crap_ctx_mode', 'TEST', <type 'str'>)
        ('crap_crapoto', 978, <type 'int'>)
        ('crap_cust_address', 'b\xc3\xa9po', <type 'str'>)
        ('crap_cust_city', 'b\xc3\xa9po', <type 'str'>)
        ('crap_cust_country', u'FR', <type 'unicode'>)
        ('crap_cust_email', 'postmaster@alternatif.org', <type 'str'>)
        ('crap_cust_first_name', '', <type 'str'>)
        ('crap_cust_last_name', 'b\xc3\xa9po', <type 'str'>)
        ('crap_cust_name', 'b\xc3\xa9po', <type 'str'>)
        ('crap_cust_phone', '123', <type 'str'>)
        ('crap_cust_state', '', <type 'str'>)
        ('crap_cust_zip', '23', <type 'str'>)
        ('crap_crap_id', '/', <type 'str'>)
        ('crap_page_action', 'CRAPPY', <type 'str'>)
        ('crap_crapoto_config', 'SINGLE', <type 'str'>)
        ('crap_return_mode', 'GET', <type 'str'>)
        ('crap_site_id', u'16793461', <type 'unicode'>)
        ('crap_crapoto_date', '20170427092129', <type 'str'>)
        ('crap_crapoto_id', '336890', <type 'str'>)
        ('crap_url', u'http://example.com/crapoto/retour', <type 'unicode'>)
        

        "La liberté est à l'homme ce que les ailes sont à l'oiseau" Jean-Pierre Rosnay

  • # Encodage des entrées/sorties

    Posté par  (site web personnel) . Évalué à 4.

    Note: Une bonne raison de passer en Python 3… les chaînes sont en unicode. On se préoccupe des décodages / encodages lorsqu'on fait des entrées sorties (fichiers, BD, web…). Point.

    Première chose: l'encodage indiqué au début du fichier est juste là pour que Python sache comment interpréter les octets du fichier source (impacte principalement chaînes et commentaires), ça n'a aucune implication sur les données qui viennent d'ailleurs et sont manipulées par le code.

    Deuxième chose: vu le code du .update(), les valeurs qui sont dans tx_values sont des textes encodés en utf8. Dans le _generate_crapoto(), il décode ces textes donc récupère des chaînes Unicode, hors il essaie de faire de la concaténation avec crapoto qui est un simple str. Essaie de mettre crapoto = u''.

    A+

    Python 3 - Apprendre à programmer dans l'écosystème Python → https://www.dunod.com/EAN/9782100809141

    • [^] # Re: Encodage des entrées/sorties

      Posté par  (site web personnel) . Évalué à 2.

      Autre possibilité: concaténer les valeurs dans crapoto sans les décoder (ie. tout laisser en octets format utf8 dans une simple str).

      Python 3 - Apprendre à programmer dans l'écosystème Python → https://www.dunod.com/EAN/9782100809141

    • [^] # Re: Encodage des entrées/sorties

      Posté par  (site web personnel) . Évalué à 2.

      Je te remercie, tes explications m'ont bien aidées à comprendre (ça a fait Tilt!). C'est une bonne idée de préciser le type de crapoto dès le début, mais ça ne change rien. Je vais essayer la concaténation tout à l'heure — pour le moment je me plonge dans un cours sur Python, pour apprendre à le faire!

      Crapoto fonctionne la première fois, c'est une génération de signature, mais plante un peu plus tard lors d'une nouvelle génération de contrôle. Le problème doit être ailleurs…

      "La liberté est à l'homme ce que les ailes sont à l'oiseau" Jean-Pierre Rosnay

      • [^] # Re: Encodage des entrées/sorties

        Posté par  (site web personnel) . Évalué à 2.

        Avec ton idée de me faire loguer les types, tu m'as permis de comprendre que lors de la 2ème génération de contrôle, tout est en unicode et c'est ce qui fait planter.

        "La liberté est à l'homme ce que les ailes sont à l'oiseau" Jean-Pierre Rosnay

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.