26 |
26 |
27 import csv |
27 import csv |
28 import re |
28 import re |
29 from io import StringIO |
29 from io import StringIO |
30 |
30 |
31 from six import PY2, PY3, text_type, binary_type, string_types, integer_types |
|
32 |
|
33 from logilab.mtconverter import xml_escape, html_unescape |
31 from logilab.mtconverter import xml_escape, html_unescape |
34 from logilab.common.date import ustrftime |
32 from logilab.common.date import ustrftime |
35 |
33 |
36 from cubicweb import _ |
34 from cubicweb import _ |
37 from cubicweb.utils import js_dumps |
35 from cubicweb.utils import js_dumps |
62 if props is not None and value and props.get('internationalizable'): |
60 if props is not None and value and props.get('internationalizable'): |
63 return req._(value) |
61 return req._(value) |
64 return value |
62 return value |
65 |
63 |
66 def print_int(value, req, props, displaytime=True): |
64 def print_int(value, req, props, displaytime=True): |
67 return text_type(value) |
65 return str(value) |
68 |
66 |
69 def print_date(value, req, props, displaytime=True): |
67 def print_date(value, req, props, displaytime=True): |
70 return ustrftime(value, req.property_value('ui.date-format')) |
68 return ustrftime(value, req.property_value('ui.date-format')) |
71 |
69 |
72 def print_time(value, req, props, displaytime=True): |
70 def print_time(value, req, props, displaytime=True): |
92 _('%d hours') |
90 _('%d hours') |
93 _('%d minutes') |
91 _('%d minutes') |
94 _('%d seconds') |
92 _('%d seconds') |
95 |
93 |
96 def print_timedelta(value, req, props, displaytime=True): |
94 def print_timedelta(value, req, props, displaytime=True): |
97 if isinstance(value, integer_types): |
95 if isinstance(value, int): |
98 # `date - date`, unlike `datetime - datetime` gives an int |
96 # `date - date`, unlike `datetime - datetime` gives an int |
99 # (number of days), not a timedelta |
97 # (number of days), not a timedelta |
100 # XXX should rql be fixed to return Int instead of Interval in |
98 # XXX should rql be fixed to return Int instead of Interval in |
101 # that case? that would be probably the proper fix but we |
99 # that case? that would be probably the proper fix but we |
102 # loose information on the way... |
100 # loose information on the way... |
122 if value: |
120 if value: |
123 return req._('yes') |
121 return req._('yes') |
124 return req._('no') |
122 return req._('no') |
125 |
123 |
126 def print_float(value, req, props, displaytime=True): |
124 def print_float(value, req, props, displaytime=True): |
127 return text_type(req.property_value('ui.float-format') % value) # XXX cast needed ? |
125 return str(req.property_value('ui.float-format') % value) # XXX cast needed ? |
128 |
126 |
129 PRINTERS = { |
127 PRINTERS = { |
130 'Bytes': print_bytes, |
128 'Bytes': print_bytes, |
131 'String': print_string, |
129 'String': print_string, |
132 'Int': print_int, |
130 'Int': print_int, |
330 |
328 |
331 class _JSId(object): |
329 class _JSId(object): |
332 def __init__(self, id, parent=None): |
330 def __init__(self, id, parent=None): |
333 self.id = id |
331 self.id = id |
334 self.parent = parent |
332 self.parent = parent |
335 def __unicode__(self): |
333 def __str__(self): |
336 if self.parent: |
334 if self.parent: |
337 return u'%s.%s' % (self.parent, self.id) |
335 return u'%s.%s' % (self.parent, self.id) |
338 return text_type(self.id) |
336 return str(self.id) |
339 __str__ = __unicode__ if PY3 else lambda self: self.__unicode__().encode('utf-8') |
|
340 def __getattr__(self, attr): |
337 def __getattr__(self, attr): |
341 return _JSId(attr, self) |
338 return _JSId(attr, self) |
342 def __call__(self, *args): |
339 def __call__(self, *args): |
343 return _JSCallArgs(args, self) |
340 return _JSCallArgs(args, self) |
344 |
341 |
345 class _JSCallArgs(_JSId): |
342 class _JSCallArgs(_JSId): |
346 def __init__(self, args, parent=None): |
343 def __init__(self, args, parent=None): |
347 assert isinstance(args, tuple) |
344 assert isinstance(args, tuple) |
348 self.args = args |
345 self.args = args |
349 self.parent = parent |
346 self.parent = parent |
350 def __unicode__(self): |
347 def __str__(self): |
351 args = [] |
348 args = [] |
352 for arg in self.args: |
349 for arg in self.args: |
353 args.append(js_dumps(arg)) |
350 args.append(js_dumps(arg)) |
354 if self.parent: |
351 if self.parent: |
355 return u'%s(%s)' % (self.parent, ','.join(args)) |
352 return u'%s(%s)' % (self.parent, ','.join(args)) |
356 return ','.join(args) |
353 return ','.join(args) |
357 __str__ = __unicode__ if PY3 else lambda self: self.__unicode__().encode('utf-8') |
|
358 |
354 |
359 class _JS(object): |
355 class _JS(object): |
360 def __getattr__(self, attr): |
356 def __getattr__(self, attr): |
361 return _JSId(attr) |
357 return _JSId(attr) |
362 |
358 |
385 |
381 |
386 HTML4_EMPTY_TAGS = frozenset(('base', 'meta', 'link', 'hr', 'br', 'param', |
382 HTML4_EMPTY_TAGS = frozenset(('base', 'meta', 'link', 'hr', 'br', 'param', |
387 'img', 'area', 'input', 'col')) |
383 'img', 'area', 'input', 'col')) |
388 |
384 |
389 def sgml_attributes(attrs): |
385 def sgml_attributes(attrs): |
390 return u' '.join(u'%s="%s"' % (attr, xml_escape(text_type(value))) |
386 return u' '.join(u'%s="%s"' % (attr, xml_escape(str(value))) |
391 for attr, value in sorted(attrs.items()) |
387 for attr, value in sorted(attrs.items()) |
392 if value is not None) |
388 if value is not None) |
393 |
389 |
394 def simple_sgml_tag(tag, content=None, escapecontent=True, **attrs): |
390 def simple_sgml_tag(tag, content=None, escapecontent=True, **attrs): |
395 """generation of a simple sgml tag (eg without children tags) easier |
391 """generation of a simple sgml tag (eg without children tags) easier |
432 from logilab.common.ureports import HTMLWriter |
428 from logilab.common.ureports import HTMLWriter |
433 formater = HTMLWriter(True) |
429 formater = HTMLWriter(True) |
434 stream = StringIO() #UStringIO() don't want unicode assertion |
430 stream = StringIO() #UStringIO() don't want unicode assertion |
435 formater.format(layout, stream) |
431 formater.format(layout, stream) |
436 res = stream.getvalue() |
432 res = stream.getvalue() |
437 if isinstance(res, binary_type): |
433 if isinstance(res, bytes): |
438 res = res.decode('UTF8') |
434 res = res.decode('UTF8') |
439 return res |
435 return res |
440 |
436 |
441 # traceback formatting ######################################################## |
437 # traceback formatting ######################################################## |
442 |
438 |
443 import traceback |
439 import traceback |
444 |
440 |
445 def exc_message(ex, encoding): |
441 def exc_message(ex, encoding): |
446 if PY3: |
442 excmsg = str(ex) |
447 excmsg = str(ex) |
|
448 else: |
|
449 try: |
|
450 excmsg = unicode(ex) |
|
451 except Exception: |
|
452 try: |
|
453 excmsg = unicode(str(ex), encoding, 'replace') |
|
454 except Exception: |
|
455 excmsg = unicode(repr(ex), encoding, 'replace') |
|
456 exctype = ex.__class__.__name__ |
443 exctype = ex.__class__.__name__ |
457 return u'%s: %s' % (exctype, excmsg) |
444 return u'%s: %s' % (exctype, excmsg) |
458 |
445 |
459 |
446 |
460 def rest_traceback(info, exception): |
447 def rest_traceback(info, exception): |
462 res = [u'Traceback\n---------\n::\n'] |
449 res = [u'Traceback\n---------\n::\n'] |
463 for stackentry in traceback.extract_tb(info[2]): |
450 for stackentry in traceback.extract_tb(info[2]): |
464 res.append(u'\tFile %s, line %s, function %s' % tuple(stackentry[:3])) |
451 res.append(u'\tFile %s, line %s, function %s' % tuple(stackentry[:3])) |
465 if stackentry[3]: |
452 if stackentry[3]: |
466 data = xml_escape(stackentry[3]) |
453 data = xml_escape(stackentry[3]) |
467 if PY2: |
|
468 data = data.decode('utf-8', 'replace') |
|
469 res.append(u'\t %s' % data) |
454 res.append(u'\t %s' % data) |
470 res.append(u'\n') |
455 res.append(u'\n') |
471 try: |
456 try: |
472 res.append(u'\t Error: %s\n' % exception) |
457 res.append(u'\t Error: %s\n' % exception) |
473 except Exception: |
458 except Exception: |
499 u'<b class="line">%s</b>, <b>function</b> ' |
484 u'<b class="line">%s</b>, <b>function</b> ' |
500 u'<b class="function">%s</b>:<br/>'%( |
485 u'<b class="function">%s</b>:<br/>'%( |
501 xml_escape(stackentry[0]), stackentry[1], xml_escape(stackentry[2]))) |
486 xml_escape(stackentry[0]), stackentry[1], xml_escape(stackentry[2]))) |
502 if stackentry[3]: |
487 if stackentry[3]: |
503 string = xml_escape(stackentry[3]) |
488 string = xml_escape(stackentry[3]) |
504 if PY2: |
|
505 string = string.decode('utf-8', 'replace') |
|
506 strings.append(u'  %s<br/>\n' % (string)) |
489 strings.append(u'  %s<br/>\n' % (string)) |
507 # add locals info for each entry |
490 # add locals info for each entry |
508 try: |
491 try: |
509 local_context = tcbk.tb_frame.f_locals |
492 local_context = tcbk.tb_frame.f_locals |
510 html_info = [] |
493 html_info = [] |
543 |
526 |
544 def write(self, data): |
527 def write(self, data): |
545 self.wfunc(data) |
528 self.wfunc(data) |
546 |
529 |
547 def writerow(self, row): |
530 def writerow(self, row): |
548 if PY3: |
531 self.writer.writerow(row) |
549 self.writer.writerow(row) |
532 return |
550 return |
|
551 csvrow = [] |
|
552 for elt in row: |
|
553 if isinstance(elt, text_type): |
|
554 csvrow.append(elt.encode(self.encoding)) |
|
555 else: |
|
556 csvrow.append(str(elt)) |
|
557 self.writer.writerow(csvrow) |
|
558 |
533 |
559 def writerows(self, rows): |
534 def writerows(self, rows): |
560 for row in rows: |
535 for row in rows: |
561 self.writerow(row) |
536 self.writerow(row) |
562 |
537 |
568 self.maxsize = maxsize |
543 self.maxsize = maxsize |
569 |
544 |
570 def __call__(self, function): |
545 def __call__(self, function): |
571 def newfunc(*args, **kwargs): |
546 def newfunc(*args, **kwargs): |
572 ret = function(*args, **kwargs) |
547 ret = function(*args, **kwargs) |
573 if isinstance(ret, string_types): |
548 if isinstance(ret, str): |
574 return ret[:self.maxsize] |
549 return ret[:self.maxsize] |
575 return ret |
550 return ret |
576 return newfunc |
551 return newfunc |
577 |
552 |
578 |
553 |
579 def htmlescape(function): |
554 def htmlescape(function): |
580 def newfunc(*args, **kwargs): |
555 def newfunc(*args, **kwargs): |
581 ret = function(*args, **kwargs) |
556 ret = function(*args, **kwargs) |
582 assert isinstance(ret, string_types) |
557 assert isinstance(ret, str) |
583 return xml_escape(ret) |
558 return xml_escape(ret) |
584 return newfunc |
559 return newfunc |