Подтвердить что ты не робот

Как написать код исключения ререйза, совместимый как с Python 2, так и с Python 3?

Я пытаюсь сделать совместимость с WSGI-сервером как с Python 2, так и с Python 3. У меня был этот код:

def start_response(status, response_headers, exc_info = None):
    if exc_info:
        try:
            if headers_sent:
                # Re-raise original exception if headers sent.
                raise exc_info[0], exc_info[1], exc_info[2]
        finally:
            # Avoid dangling circular ref.
            exc_info = None
    elif headers_set:
        raise AssertionError("Headers already set!")

    headers_set[:] = [status, response_headers]
    return write

... с соответствующей частью:

# Re-raise original exception if headers sent.
raise exc_info[0], exc_info[1], exc_info[2]

Python 3 больше не поддерживает этот синтаксис, поэтому он должен быть переведен на:

raise exc_info[0].with_traceback(exc_info[1], exc_info[2])

Проблема: синтаксис Python 2 генерирует ошибку синтаксического анализа в Python 3. Как написать код, который может быть проанализирован как Python 2, так и Python 3? Я пробовал следующее, но это не работает:

if sys.version_info[0] >= 3:
    raise exc_info[0].with_traceback(exc_info[1], exc_info[2])
else:
    eval("raise exc_info[0], exc_info[1], exc_info[2]; 1", None, { 'exc_info': exc_info })
4b9b3361

Ответ 1

Вы можете сделать что-то творческое.

У вас есть проверка в начале вашего кода - ваш конструктор или что-то еще, проверьте, какая версия python вы используете, так как ваша нормальная проверка версии не работает, попробуйте это вместо:

try:
  eval('a python 3 expression') # something that only works in python3+
  python_version = 3
except:
  python_version = 2

Тогда остальная часть вашего кода может просто просто ссылаться на это, чтобы знать, что использовать.

Что касается ошибок синтаксического анализа, вы можете использовать exec в функции, например:

def what_to_run():
    if python_version = 3:
        return 'raise exc_info[0].with_traceback(exc_info[1], exc_info[2])'
    else:
        return 'raise exc_info[0], exc_info[1], exc_info[2]'

В вашей функции вы должны написать это:

def start_response(status, response_headers, exc_info = None):
    if exc_info:
        try:
            if headers_sent:
                # Re-raise original exception if headers sent.
                exec(what_to_run())
        finally:
            # Avoid dangling circular ref.
            exc_info = None
    elif headers_set:
        raise AssertionError("Headers already set!")

    headers_set[:] = [status, response_headers]
    return write

Немного грязный, непроверенный, но он должен работать, по крайней мере, вы понимаете идею.