Xlsxwriter: TypeError: "expected string or buffer"
Question: I have that exception, as described in the title, when trying to generate an
excel file from a model which is filtered by a query. The query works as
expected and gives me the right and complete results. However, the big deal
occurs when I try to, actually, generate the excel file:

    
    
      File "/home/luismasuelli/Proyectos/CentralCar/main/views.py", line 100, in export_xls
        workbook.close()
      File "/home/luismasuelli/.virtualenvs/py27dj15/local/lib/python2.7/site-packages/xlsxwriter/workbook.py", line 237, in close
        self._store_workbook()
      File "/home/luismasuelli/.virtualenvs/py27dj15/local/lib/python2.7/site-packages/xlsxwriter/workbook.py", line 405, in _store_workbook
        xml_files = packager._create_package()
      File "/home/luismasuelli/.virtualenvs/py27dj15/local/lib/python2.7/site-packages/xlsxwriter/packager.py", line 139, in _create_package
        self._write_shared_strings_file()
      File "/home/luismasuelli/.virtualenvs/py27dj15/local/lib/python2.7/site-packages/xlsxwriter/packager.py", line 265, in _write_shared_strings_file
        sst._assemble_xml_file()
      File "/home/luismasuelli/.virtualenvs/py27dj15/local/lib/python2.7/site-packages/xlsxwriter/sharedstrings.py", line 53, in _assemble_xml_file
        self._write_sst_strings()
      File "/home/luismasuelli/.virtualenvs/py27dj15/local/lib/python2.7/site-packages/xlsxwriter/sharedstrings.py", line 83, in _write_sst_strings
        self._write_si(string)
      File "/home/luismasuelli/.virtualenvs/py27dj15/local/lib/python2.7/site-packages/xlsxwriter/sharedstrings.py", line 95, in _write_si
        string = re.sub('(_x[0-9a-fA-F]{4}_)', r'_x005F\1', string)
      File "/home/luismasuelli/.virtualenvs/py27dj15/lib/python2.7/re.py", line 151, in sub
        return _compile(pattern, flags).sub(repl, string, count)
    TypeError: expected string or buffer
    

What could be causing me that exception? I tried to change the encoding to put
utf8 as suggested but that doesn't help me here.

I'm working with Xlsxwriter 0.5.2 / python 2.7.4 (ubuntu) from a django app,
as follows:

    
    
    # -*- coding: utf8 -*-
    #...
    from StringIO import StringIO
    #...
    import datetime
    #...
    from django.http import HttpResponse, Http404
    #...
    import xlsxwriter
    #...
    from main.models import Contact
    
    #...
    
    def export_xls(request, period=''):
    
        deltas = {
            'D': datetime.timedelta(days=1),
            'W': datetime.timedelta(days=7),
            'M': datetime.timedelta(days=30),
            '6M': datetime.timedelta(days=182),
            'Y': datetime.timedelta(days=365)
        }
    
        query = Contact.objects.all().order_by('-enviado_en')
    
        if period:
            query = query.filter(enviado_en__gt=datetime.datetime.now() - deltas[period])
    
        stream = StringIO()
        workbook = xlsxwriter.Workbook(stream, {'in_memory': True})
        worksheet = workbook.add_worksheet('Contactos')
        #poner encabezados
        header_format = workbook.add_format()
        header_format.set_align("center")
        header_format.set_bold()
        header_format.set_font_name('Arial')
        worksheet.set_column(9, 9, 40)
        worksheet.write_string(0, 0, Contact._meta.get_field('enviado_en').verbose_name, header_format)
        worksheet.write_string(0, 1, Contact._meta.get_field('nombre').verbose_name, header_format)
        worksheet.write_string(0, 2, Contact._meta.get_field('apellido').verbose_name, header_format)
        worksheet.write_string(0, 3, Contact._meta.get_field('email').verbose_name, header_format)
        worksheet.write_string(0, 4, Contact._meta.get_field('telefono').verbose_name, header_format)
        worksheet.write_string(0, 5, Contact._meta.get_field('ciudad').verbose_name, header_format)
        worksheet.write_string(0, 6, Contact._meta.get_field('vehiculo').verbose_name, header_format)
        worksheet.write_string(0, 7, Contact._meta.get_field('kilometraje').verbose_name, header_format)
        worksheet.write_string(0, 8, Contact._meta.get_field('dia_preferente').verbose_name, header_format)
        worksheet.write_string(0, 9, Contact._meta.get_field('mensaje').verbose_name, header_format)
        #poner cada fila
        body_format = workbook.add_format()
        body_format.set_font_name('Arial')
        current_row = 1
        for contact in query:
            worksheet.write(current_row, 0, contact.enviado_en.strftime("%d/%m/%Y %H/%M/%S"), body_format)
            worksheet.write(current_row, 1, contact.nombre, body_format)
            worksheet.write(current_row, 2, contact.apellido, body_format)
            worksheet.write(current_row, 3, contact.email, body_format) 
            worksheet.write(current_row, 4, contact.telefono, body_format) 
            worksheet.write(current_row, 5, contact.ciudad, body_format) 
            worksheet.write(current_row, 6, contact.get_vehiculo_display(), body_format)
            worksheet.write(current_row, 7, contact.get_kilometraje_display(), body_format) 
            worksheet.write(current_row, 8, contact.get_dia_preferente_display(), body_format) 
            worksheet.write(current_row, 9, contact.mensaje, body_format)
            current_row += 1
        workbook.close()
        data = stream.getvalue()
        response = HttpResponse(content=data, content_type='application/vnd.ms-excel')
        response['Content-Disposition'] = 'attachment; filename=reporte-%s.xlsx' % datetime.datetime.now().strftime("%Y%m%d%H%M%S")
        return response
    

Being my model Contact:

    
    
    class Contact(models.Model):
    
        DIAS = (
            #('?', 'Cualquiera'),
            ('L', 'Lunes'),
            ('M', 'Martes'),
            ('I', 'Miercoles'),
            ('J', 'Jueves'),
            ('V', 'Viernes'),
            ('S', 'Sábado')
        )
        KILOMETRAJE = (
            #('??', '(No especifica)'),
            ('00', '0km'),
            ('05', '1 - 5000km'),
            ('10', '5000km - 10000km'),
            ('15', '10001km - 15000km'),
            ('20', '15001km - 20000km'),
            ('25', '20001km - 25000km'),
            ('30', '25001km - 30000km'),
            ('35', '30001km - 35000km'),
            ('40', '35001km - 40000km'),
            ('45', '40001km - 45000km'),
            ('50', '45001km - 50000km'),
            ('55', '50001km - 55000km'),
            ('60', '55001km - 60000km'),
            ('65', '60001km - 65000km'),
            ('70', '65001km - 70000km'),
            ('75', '70001km - 75000km'),
            ('80', '75001km - 80000km'),
            ('85', '80001km - 85000km'),
            ('90', '85001km - 90000km'),
            ('95', '90001km - 950000km'),
            ('XX', '95001km - 100000km'),
            ('++', 'Más de 100000km')
        )
    
        enviado_en = models.DateTimeField(default=tznow, null=False, db_index=True, verbose_name=_(u"Enviado en"))
        #campos de contacto
        nombre = models.CharField(max_length=30, null=False, blank=False, db_index=True, verbose_name=_(u"Nombre"))
        apellido = models.CharField(max_length=30, null=False, blank=False, db_index=True, verbose_name=_(u"Apellido"))
        email = models.EmailField(null=False, blank=False, db_index=True, verbose_name=_(u"E-mail"))
        telefono = models.CharField(max_length=10, null=False, blank=False, db_index=True, validators=[contact_phone_number_format], verbose_name=_(u"Teléfono"))
        ciudad = models.CharField(max_length=20, null=False, blank=False, validators=[validate_ciudad], verbose_name=_(u"Ciudad"))
        #campos solamente de cotizacion
        vehiculo = models.CharField(max_length=20, blank=True, db_index=True, choices=vehicle_choices(), verbose_name=_(u"Vehículo"))
        kilometraje = models.CharField(max_length=2, blank=True, db_index=True, choices=KILOMETRAJE, default='05', verbose_name=_(u"Kilometraje"))
        #campos solamente de agendar cita
        dia_preferente = models.CharField(max_length=1, blank=True, choices=DIAS, default='L', verbose_name=_(u'Día para cita'))
        #mensaje
        mensaje = models.TextField(max_length=4095, null=False, blank=False, verbose_name=_(u"Mensaje"))
    

assume vehicle_choices() exists and returns a valid choice set for the input
field.


Answer: I found the answer: verbose_name objects are, actually, proxies returned from
ugettext_lazy. calling force_unicode(ugl_obj) converts them to string
(conversion is not done automagically).

in my case, each value in the header printing was wrapped, e.g.:

    
    
        worksheet.write_string(0, 0, force_unicode(Contact._meta.get_field('enviado_en').verbose_name), header_format)
    

