20110615 ======== Noch ein UnicodeDecodeError --------------------------- Gestern trat ein paarmal folgender Fehler auf bei dem Kunden, dem ich die neue Version installiert hatte:: Traceback (most recent call last): File "/var/snapshots/django/django/core/handlers/base.py", line 111, in get_response response = callback(request, *callback_args, **callback_kwargs) File "/var/snapshots/lino/lino/ui/extjs3/ext_ui.py", line 1118, in api_element_view error=e) UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 52: ordinal not in range(128) Und hier der dazugehörige Code:: try: return a.run(ar,elem) except Exception,e: msg = _("Action %(action)s failed for %(record)s: %(error)s") % dict( action=a, record=obj2str(elem), error=e) ## lino 1118 logger.warning(msg) logger.exception(e) return error_response(e,msg) Also fast die gleiche Lektion wie :lino:`vor 2 Wochen <0527>`: "When you pass more than one formatting argument to a logger function, and one of them is a unicode string, then the other argument(s) may not be bytestrings containing non-ascii chars." Mit der Verallgemeinerung, dass das für alle String-Formatierungen (Operator ``%``) gilt, und zwar sowohl für Positionsargumente wie für Keyword-Argumente. Der Fehler ist reproduzierbar, indem ich auf `Vertrag # 88 `_ gehe und dort :guilabel:`Drucken` klicke. Beim Einchecken und Testen entdecke ich, dass die Sache noch perverser ist als gedacht. Wenn ich die Exception nicht selber abfange, dann tut Django das, und der kann es. Der schickt dann dem Benutzer eine freundliche 500-Meldung und den Systemverwaltern (`setting:`ADMINS`) eine E-Mail, dank derer ich erstmals überhaupt die Exception an sich sehe:: Traceback (most recent call last): File "/var/snapshots/django/django/core/handlers/base.py", line 111, in get_response response = callback(request, *callback_args, **callback_kwargs) File "/var/snapshots/lino/lino/ui/extjs3/ext_ui.py", line 1111, in api_element_view return a.run(ar,elem) File "/var/snapshots/lino/lino/mixins/printable.py", line 407, in run bm.build(self,elem) File "/var/snapshots/lino/lino/mixins/printable.py", line 186, in build tpl_leaf = self.get_template_leaf(action,elem) File "/var/snapshots/lino/lino/mixins/printable.py", line 162, in get_template_leaf tpls = action.get_print_templates(self,elem) File "/var/snapshots/lino/lino/mixins/printable.py", line 399, in get_print_templates return elem.get_print_templates(bm,self) File "/var/snapshots/lino/lino/mixins/printable.py", line 610, in get_print_templates (ptype.__class__.__name__,ptype,bm.template_ext)) Exception: Invalid template configured for ContractType "Art.60§7 intern". Expected filename ending with '.odt'. Der besagte Code ist:: if not ptype.template.endswith(bm.template_ext): raise Exception( "Invalid template configured for %s \"%s\". Expected filename ending with '%s'." % (ptype.__class__.__name__,ptype,bm.template_ext)) Also das "§"-Zeichen in "Art.60§7 intern" ist der Auslöser. Was ich noch nicht wusste: es gibt `Python issue #2517 `_, : Eine `Exception` mit einem Unicode-String führt zumindest in Python 2.6 noch allgemein an vielen Stellen zu Problemen. Lektion : `Exceptions` dürfen ausschließlich ASCII-Code enthalten und folglich auch nicht internationalisert sein. Im obigen Beispiel reicht:: if not ptype.template.endswith(bm.template_ext): raise Exception( "Invalid template configured for %s %r. Expected filename ending with %r." % (ptype.__class__.__name__,unicode(ptype),bm.template_ext)) Dass ich meine Instanz `ptype` noch selber in ein `unicode()` einpacken muss, ist unlogisch und liegt daran, dass Djangos `Model.__repr__()` unter Umständen einen Unicode-String zurückgeben kann. Ich habe das in :djangoticket:`16261` gemeldet, aber das werden die wohl kaum akzeptieren, weil das ziemlich unkompatibel wäre. Nachdem mir das klargeworden ist, habe ich noch ein Problemchen entdeckt: in meiner :func:`lino.utils.log.configure` muss ich den `AdminEmailHandler` nicht nur den Logger ``django`` hinzufügen, sondern auch meinem Logger ``lino``. Neuer Testcase :func:`lino.apps.dsbe.tests.dsbe_tests.test01b`. Voilà. Jetzt bin ich zufrieden: Der Bentuzer kriegt eine Meldung "Action Drucken failed for Contract #88 (Vertrag # 88). An error report has been sent to the system administrator." und die Systemverwalter eine E-Mail mit detaillierter Fehlerbeschreibung, unter anderem "Invalid template configured for ContractType u'Art.60\xa77 intern'. Expected filename ending with '.odt'." All das ist die Checkin-Serie 20110615, die noch mit nach :lino:`/releases/2011/0613` reinkommt. Viele kleine Benutzerwünsche ---------------------------- .. currentmodule:: lino.modlib.contacts.models - :meth:`Person.get_full_name` now renders the :attr:`Person.last_name` in all-uppercase letters - :meth:`Person.address_person_lines` now uses :meth:`Person.get_full_name`. - :meth:`Person.get_full_name` now includes a :func:`salutation `. .. currentmodule:: lino.apps.dsbe.models - New field :attr:`PersonGroup.ref_name` used for ordering. - New field :attr:`Person.duties_person` - Field :attr:`Person.native_language` replaced by :attr:`LanguageKnowledge.native` - New function :func:`lino.utils.babel.dtomy` available in :class:`lino.mixins.printable.AppyBuildMethod`. Erstes Check-in der Serie 20110615b um 19.35 wegen Feierabend. Release 1.1.17 -------------- Um die bereits erfassten Muttersprachen zu übernehmen, musste ich mir was Neues einfallen lassen. Erstens eine kleine Liste der bestehenden Fälle: >>> import codecs >>> from lino.apps.dsbe.models import Person >>> f = codecs.open('nl.txt','w',encoding='utf-8') >>> f.write('\n'.join(["%s : %s + %s" % (unicode(p),p.native_language,', '.join([unicode(x) for x in p.languageknowledge_set.all()])) for p in qs])) >>> f.close() Aber vor allem: Personen, die bisher in native_language etwas stehen haben, sollen stattdessen jetzt einen Eintrag in :class:`LanguageKnowledge` haben. Manchmal existiert der schon (und muss dann nur ``native=True`` gesetzt kriegen und manchmal nicht (und muss dann erstellt werden ohne Angaben für spoken und written) Und weil die evtl. bestehenden LanguageKnowledge-Records ja noch gar nicht gespeichert worden sind während der `objects()`, kann ich in der `create_contacts_person()` nicht schauen, ob schon ein Eintrag für die Muttersprache existerte oder nicht. Also muss ich den Kram in eine Liste `NATIVES` einsammeln und erst später speichern, wenn alles andere eingelesen ist. Deshalb die neue Funktion `after_load`, die vom :class:`lino.utils.dpy.Deserializer` nach dem Speichern der objects ausgeführt wird (wenn sie existiert). Und dann noch was cooles Neues: Modul :mod: `lino.apps.dsbe.migrations`. Um die Konvertierung nicht manuell in die Dump-Datei beim Kunden reinkopieren zu müssen, habe ich ein neues System ausgedacht, damit ich die Migration einmal schreiben kann und die Dump-Datei sie sich automagisch installiert. Ganz automagisch geht das allerdings noch nicht: die entsprechenden Zeilen müssen manuell rauskommentiert werden. Weiteres Check-in der Serie 20110615b zwecks Installation beim Kunden. Released :lino:`/releases/2011/0615`.