odoo/odoo#28519 removed large parts of pycompat, but left reraise despite that not having much value. Remove that helper and replace it by just a `raise` in most cases: when raising from an except block, the old exception is automatically chained to the new one, no need to mess around. There is one exception: in http we have to re-raise an existing exception explicitly (aka `raise exc` rather than just `raise). This is less than ideal as Python *concatenates* stacks: the previously reified stack (from the except clause) is stacked on top of the new stack (from this raises), this leads to tracebacks "jumping around" at the break point of the handler and is somewhat confusing. So we want to use explicit chaining (`raise a from b`) with the "source" providing the caught exception's original traceback and the child providing the rest. However since callers rely on the exception making sense, we need the re-raised exception to be the original[0]. Copying the exception doesn't work (see [0]), chaining an exception to itself doesn't do anything useful, and while we could probably copy exceptions using the pickle method[1] that's still risky. So the most reliable option seems to be to create a new "cause" exception, move the old traceback over to it, then re-raise the original exception having cleared its traceback, chained to new the cause. [0] or a copy thereof but Odoo exceptions don't all work properly with copy.copy and we don't want that to fail so not really an option, we can't rely / bet on every new exception being cleanly copy-able [1] create an "empty" instance using __new__ (or an instance of something else onto which we re-set the __class__ in case the exctype actually overrides __new__) then copy the __dict__ closes odoo/odoo#39709 Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
39 lines
1.0 KiB
Python
39 lines
1.0 KiB
Python
# -*- coding: utf-8 -*-
|
|
#pylint: disable=deprecated-module
|
|
import csv
|
|
import codecs
|
|
import io
|
|
|
|
_reader = codecs.getreader('utf-8')
|
|
_writer = codecs.getwriter('utf-8')
|
|
|
|
|
|
def csv_reader(stream, **params):
|
|
assert not isinstance(stream, io.TextIOBase),\
|
|
"For cross-compatibility purposes, csv_reader takes a bytes stream"
|
|
return csv.reader(_reader(stream), **params)
|
|
|
|
|
|
def csv_writer(stream, **params):
|
|
assert not isinstance(stream, io.TextIOBase), \
|
|
"For cross-compatibility purposes, csv_writer takes a bytes stream"
|
|
return csv.writer(_writer(stream), **params)
|
|
|
|
|
|
def to_text(source):
|
|
""" Generates a text value (an instance of text_type) from an arbitrary
|
|
source.
|
|
|
|
* False and None are converted to empty strings
|
|
* text is passed through
|
|
* bytes are decoded as UTF-8
|
|
* rest is textified via the current version's relevant data model method
|
|
"""
|
|
if source is None or source is False:
|
|
return u''
|
|
|
|
if isinstance(source, bytes):
|
|
return source.decode('utf-8')
|
|
|
|
return str(source)
|