Как исправить ошибку в питоне автоматически

For something like this (connecting to a web page), it’s often better to make the upper limit based on time instead of the number of times you attempt to connect. So use a while loop instead:

import numpy as np
import time

def main():
    np.load('file.csv')

start = time.time()
stop = start + 5
attempts = 0
result = 'failed'

while True:
    if time.time()<stop:
        try:
            main()
        except Exception as e:
            attempts += 1
            print e
            time.sleep(0.1) # optional
            print 'Restarting!'
            continue
        else:
            result = 'succeeded'
    print 'Connection %s after %i attempts.' % (result, attempts)
    break

Optional: I included a 100 ms pause after each failed attempt. This can help with establishing a connection sometimes.

Then wrap the whole thing up in a function you can use in the future for other projects:

# retry.py

import time

def retry(f, seconds, pause = 0):
    start = time.time()
    stop = start + seconds
    attempts = 0
    result = 'failed'

    while True:
        if time.time()<stop:
            try:
                f()
            except Exception as e:
                attempts += 1
                print e
                time.sleep(pause)
                print 'Restarting!'
                continue
            else:
                result = 'succeeded'
        print '%s after %i attempts.' % (result, attempts)
        break

now just do this:

import numpy as np
from retry import retry

def main():
    np.load('file.csv')

retry(main, 5, 0.1)

Testing procedure:

class RetryTest():
    def __init__(self, succeed_on = 0, excp = Exception()):
        self.succeed_on = succeed_on
        self.attempts = 0
        self.excp = excp
    def __call__(self):
        self.attempts += 1
        if self.succeed_on == self.attempts:
            self.attempts = 0
        else:
            raise self.excp

retry_test1 = RetryTest(3)
retry(retry_test1, 5, 0.1)
# succeeded after 3 attempts.
retry_test2 = RetryTest()
retry(retry_test2, 5, 0.1)
# failed after 50 attempts.

Время на прочтение
2 мин

Количество просмотров 25K

В эпоху все большей популярности различных js и css linter’ов, не удивительно появление удобного линтера с автокоррекцией для Python.

Приветствуйте, Yapf — готовое решение, для превращения каши из строк во вполне читаемый код. И поверьте, он вам пригодится.

image

Большинство современных линтеров для Python — например, autopep8 и pep8ify — сделаны, чтобы удалить ошибки в коде. Это имеет некоторые очевидные ограничения. Например, код, который соответствует PEP8, не может быть переформатирован. Но это не значит, что код выглядит хорошо.

YAPF использует другой подход. Он основан на «clang-format», разработанном Daniel Jasper. В сущности, алгоритм берет код и переформатирует его до формата, соответствующего стилю руководства, даже если исходный код не нарушает руководство по стилю. Идея также похожа на инструмент gofmt для языка программирования Go: конец всех священных войн о форматировании — если вся кодовая база проекта просто перекачивается через YAPF всякий раз, когда вносятся изменения, стиль остается согласованным во всем проекте, и нет смысла спорить о стиле в каждом обзоре кода.

Конечная цель состоит в том, что код python c YAPF пишется так же хорошо, как код, который мог бы написать программист, если бы следовал руководству по стилю. Это избавляет от тяжелой работы по поддержке кода.

А теперь перейдем к практической части:

YAPF можно использовать как из командной строки, так и в виде плагина для текстового редактора. Сейчас есть плагины для Emacs, VIM и Sublime Text.

Я в основном использую Sublime Text, поэтому покажу как его настроить для использования YAPF.
Плагин для Sublime Text — github.com/jason-kane/PyYapf

1. Установка.

pip install yapf

Установите Sublime Package Control, следуя инструкциям здесь (если вы еще этого не сделали).

Ctrl-Shift-P (Mac: Cmd-Shift-P) и выберите «Управление пакетами: установить пакет».

Найдите в списке «PyYapf Python Formatter».

2. Настройка.

После установки у вас появится PyYapf в меню настроек.

image

Чтобы все заработало, требуется указать, где у вас лежит файл Yapf (в моем случае, он был в папке Python)

Откройте настройки PyYapf — Settings default, скопируйте их. Потом откройте PyYapf — Settings User, вставьте скопированные правила и укажите путь до Yapf файла.

image

После этого можно уже использовать YAPF для форматирования кода. Нажмите Ctrl-Alt-F и код будет преобразован. По умолчанию применяются настройки PEP8.

Пример до и после.

image

Вы можете настроить в нем множество правил. YAPF позволяет делать гибкую настройку различных параметров, подробнее тут — https://github.com/google/yapf#id8

Надеюсь YAPF поможет вам писать красивый и чистый код, соответствующий многочисленным стандартам.

Понятие исключений и ошибок

Любой, даже самый опытный программист, в ходе разработки программ допускает различного рода ошибки, приводящие к тому, что программа либо не работает вообще,
либо работает, но выполняет вовсе не то, что задумывалось. Причинами ошибок могут быть как неправильное использование синтаксиса и пунктуации языка
(синтаксические ошибки), так и неверное понимание логики программы (семантические ошибки, которые в Python принято называть
исключениями). При этом, если первый тип ошибок легко обнаружить с помощью интерпретатора, то на устранение логических ошибок
порой может потребоваться не один час, поскольку такие ошибки обнаруживаются уже непосредственно в ходе использования программы, проявляясь либо в виде сбоев
в ее работе, либо в получении совершенно непредвиденных результатов (см. пример №1).

# Синтаксические ошибки обнаружить легко.

# Имя переменной начали с цифры.
# SyntaxError: invalid decimal literal
# 35_days = 35

# Строка должна содержать запись целого числа.
# ValueError: invalid literal for int() with base 10: 'три'
# num = int('три')    

# Числа и строки складывать нельзя.
# TypeError: unsupported operand type(s) for +: 'int' and 'str'
# res = 5 + 'один'     

# Логические ошибки всплывут потом.    

# На первый взгляд все верно, и программа даже будет  
# работать, но только до тех пор, пока пользователь 
# не введет ноль или неприводимое к числу значение.
a = int(input('Введите число: '))
print(10/a)
Введите число: 0
ZeroDivisionError: division by zero

















		
			

Пример №1. Синтаксические и логические ошибки в Python.

В примере мы показали лишь несколько видов исключений. На самом деле их намного больше. И очень важно знать о них, а также иметь инструменты для их обнаружения и
обработки. Именно поэтому в стандартной библиотеке Python присутствует внушительный набор готовых классов, представляющих различные
виды исключений и синтаксических ошибок. Все они перечислены в разделе Built-in exceptions,
где также представлена и подробная иерархия имеющихся классов исключений. Что касается обработки ошибок, то для этого Python
предоставляет ряд специальных инструкций, которые мы и будем рассматривать в данном параграфе.

Инструкция try/except/else/finally

Пожалуй, основной «рабочей лошадкой» по обработке исключений в Python является составная инструкция
try/except/else/finally, которая имеет следующий общий формат:

# Сначала выполняется основной блок инструкций.
try:
    <Основные инструкции>     
# Запускается, если в блоке try возникло исключение Exception_1.
except Exception_1:
    <Инструкции обработки исключения>     
# Запускается, если возникло любое из перечисленных исключений.
except (Exception_2, Exception_3):
    <Инструкции обработки исключений>     
# Работаем с экземпляром Exception_4, как с err.
except Exception_4 as err:
    <Инструкции обработки исключения>     
# Запускается для всех видов исключений.                    
except:
    <Инструкции обработки исключений>     
# Запускается, если в блоке try не возникло исключений.
else:
    <Дополнительные инструкции>
# Запускается в любом случае.
finally:
    <Финальные инструкции>

Первыми выполняются инструкции основного блока try. При обнаружении в нем ошибок, интерпретатор пытается найти соответствующий
возникшему исключению блок except и, в случае наличия такового, выполняет его инструкции. После того как исключение будет
обработано, оно уничтожается, а программа пытается продолжить работу в штатном режиме. Если же среди имеющихся блоков except
соот­ветствия найдено не будет, исключение переадресуется инструкции try, стоящей
выше в программе, или на верхний уровень процесса, что скорее всего вынудит интерпретатор аварийно завершить работу программы и вывести сообщение об ошибке по
умолчанию. В случае отсутствия ошибок в блоке try интерпретатор пропускает все блоки except и
начинает выполнять инструкции необязательного блока else, который разрешается использовать только при наличии хотя бы одного блока
except. При этом стоит помнить, что при наличии ошибок в блоке try инструкции данного блока
выполняться не будут. Что касается необязательного блока finally, то он используется для каких-либо завершающих операций, например,
закрытия файлов или открытых соединений с сервером. Его инструкции выполняются всегда, вне зависимости от того, возникли исключения в каком-либо блоке, включая блок
else, или нет (см. пример №2).

try:
    # Здесь исключения могут возбудиться из-за 
    # ввода нечислового значения или нуля.
    a = int(input('Введите число: '))
    print('10/{} = {}'.format(a, 10/a))    
# Если введено не целое число.
except ValueError:
    print('Введите целое число!')
# Если введен ноль.
except ZeroDivisionError:
    print('На ноль делить нельзя!')
# Выполнится только при отсутствии ошибок.	
else:
    print('Операция прошла успешно!')
# Выполняется в любом случае.
finally:
    print('Для завершения нажмите «Enter»!')    
    # Чтобы окно консоли не закрылось. 
    input()
Введите число: 0
На ноль делить нельзя!
Для завершения нажмите «Enter»!

-------------------------------

Введите число: один
Введите целое число!
Для завершения нажмите «Enter»!

-------------------------------

Введите число: 20
10/20 = 0.5
Операция прошла успешно!
Для завершения нажмите «Enter»!


			

Пример №2. Инструкция try/except/else/finally в Python (часть 1).

Стоит заметить, что в отличие от блоков except блок finally не останавливает распространение
исключений до вышестоящей инструкции try или до обработчика исключений по умолчанию, т.е. в случае возникновения ошибок
инструкции, следующие за блоком finally в программе, выполняться не будут (см. пример №3).

# Внешняя инструкция try.
try:
    # Внутреняя инструкция try.
    try:
        # Здесь исключения могут появиться из-за 
        # ввода нечислового значения или нуля.
        a = int(input('Введите число: '))
        print('10/{} = {}'.format(a, 10/a))    
    # Выполняется в любом случае.
    finally:
        print('Внутренний блок finally.')    
    
    # Выполняется при отсутствии ошибок
    # во внутреннем блоке try.
    print('Все ОК! Ошибок нет!')
    
# Если введено не целое число или ноль.
except (ValueError, ZeroDivisionError):
    print('Неверный ввод!')
# Выполнится только при отсутствии ошибок.	
else:
    print('Операция прошла успешно!') 
# Выполняется в любом случае.
finally:
    print('Внешний блок finally.')
    # Чтобы окно консоли не закрылось. 
    input() 
Введите число: 7
10/7 = 1.4285714285714286
Внутренний блок finally.
Все ОК! Ошибок нет!
Операция прошла успешно!
Внешний блок finally.

------------------------

Введите число: 0
Внутренний блок finally.
Неверный ввод!
Внешний блок finally.












		
			

Пример №3. Инструкция try/except/else/finally в Python (часть 2).

Если необходимо перехватывать сразу все возможные исключения, разрешается использовать инструкцию except без указания классов исключений.
Эта особенность может быть весьма удобна при разработке своих собственных обработчиков ошибок. Однако при использовании такого варианта могут перехватываться нежелательные
системные исключения, не связанные с работой создаваемого программного кода, а также случайно может прерываться распространение исключений, предназначенных для других
обработчиков. Данная проблема была частично решена в Python 3.0 за счет введения альтернативы в виде суперкласса
Exception, представляющего все прикладные исключения, но игнорирующего исключения, связанные с завершением программы (см. пример
№4).

# Основной блок try.
try:
    # Здесь исключения могут возбудиться из-за 
    # ввода нечислового значения или нуля.
    a = int(input('Введите число: '))
    print('10/{} = {}'.format(a, 10/a))    
     
# Выводим строковое представление исключения.
except Exception as err:
    print(err)
# Выполнится только при отсутствии ошибок.	
else:
    print('Операция прошла успешно!') 
# Выполняется в любом случае.
finally:
    # Чтобы окно консоли не закрылось. 
    input() 
Введите число: one
invalid literal for int() with base 10: 'one'

------------------------

Введите число: 0
division by zero

------------------------

Введите число: 10
10/10 = 1.0
Операция прошла успешно!


		
			

Пример №4. Инструкция try/except/else/finally в Python (часть 3).

В конце добавим, что помимо основного варианта использования try/except/else/finally инструкция
try может быть использована и в варианте try/finally, т.е. без блоков обработки исключений
except. Как не трудно догадаться, основной задачей такого варианта использования инструкции является выполнение заключительных
операций после выполнения кода основного блока.

Инструкция raise

В примерах выше мы полагались на то, что интерпретатор автоматически определит появление ошибок во время выполнения программного кода. Однако в
Python присутствует возможность и явного возбуждения исключений с помощью инструкции
raise. Все что от нас требуется, это указать через пробел имя класса или экземпляра
возбуждаемого исключения. Также разрешается использовать пустую инструкцию, тогда будет повторно возбуждено самое последнее исключение
(см. пример №5).

try:
    # Возбудим исключение IndexError принудительно.
    # Экземпляр исключения создается автоматически.
    raise IndexError    
# Обработаем его.
except IndexError:
    print('Перехватили IndexError!')

try:
    # Все тоже самое, но здесь создаем экземпляр сами.
    raise IndexError()   
# Обработаем его.
except IndexError:
    print('Перехватили IndexError!')  
    # Вложенная инструкция try.
    try:
        # Возбудили последнее исключение еще раз.
        raise   
    # Обработаем его.
    except IndexError:
        print('Перехватили IndexError!')
Перехватили IndexError!
Перехватили IndexError!
Перехватили IndexError!
















		
			

Пример №5. Инструкция raise в Python (часть 1).

Инструкция raise может использоваться для явного возбуждения не только встроенных исключений, но и созданных пользователем.
Пользовательские исключения представляют собой самые обычные классы, наследуемые от классов встроенных исключений, чаще всего от класса
Exception (см. пример №6).

# Создаем свой класс исключений, который 
# наследуется от встроенного Exception.
class OnlyAlpha(Exception): 
    # Переопределим строковое представление.
    def __str__(self): 
        return 'Разрешены только буквы!'

# Определим функцию проверки строки на
# наличие сторонних небуквенных символов.
def check_for_alpha(s):
    # Если присутствуют не только буквы.  
    if not s.isalpha():
        # Возбуждаем экземпляр своего исключения.
        raise OnlyAlpha()       

# Проверим, как все работает.
try:
    # Просим ввести строку.
    s = input('Введите имя: ')
    # Проверяем ввод.    
    check_for_alpha(s)
# Обрабатываем польз. исключение.
except OnlyAlpha as err:
    # Строковое представление экз.
    print(err)
    print('Попробуйте еще раз!')    
# Если все прошло гладко.
else:
    # Выводим введенное имя.
    print('Привет, {}!'.format(s))
# В любом случае дополняем сообщением.
finally:       
    print('Имя – это ваш ник и логин!')
Введите имя: okpython
Привет, okpython!
Имя – это ваш ник и логин!

--------------------------

Введите имя: okpython.net
Разрешены только буквы!
Попробуйте еще раз!
Имя – это ваш ник и логин!





















		
			

Пример №6. Инструкция raise в Python (часть 2).

Стоит добавить, что инструкция raise может использоваться и в формате raise/from, который используется
значительно реже, поэтому здесь мы его рассматривать не будем. Однако не поленитесь и самостоятельно посетите подраздел
«The raise statement» раздела «Simple statements» официального
справочника языка, где хотя бы бегло ознакомьтесь и с этим вариантом инструкции.

Инструкция assert

Инструкция assert представляет собой компактный условный вариант инструкции raise,
предназначенный в основном для возбуждения исключений на этапе отладки программы. В общем виде ее можно представить в формате
assert condition[, message], где condition – это условие, при невыполнении которого
(возвращается False) будет возбуждено встроенное исключение AssertionError, а также при необходимости
выведено необязательное сообщение message (см. пример №7).

# Допустим мы пишем функцию для расчета среднего
# значения чисел переданного ей списка.
def avg(li):
    len_li = len(li)
    # Список не должен быть пустым.
    assert len_li != 0, 'Список пуст!'    
    # Возвращаем среднее значение.
    return round(sum(li)/len_li, 3)

try:
   # Запускаем функцию.  
   res = avg([]) 
# Отлавливаем исключение для обработки.
except AssertionError as my_err:
    print(my_err)
# Результат выводим, если все в порядке.
else:
    print('Среднее значение: {}'.format(res)) 
Список пуст!















		
			

Пример №7. Инструкция assert в Python.

Важно не забывать, что инструкция assert главным образом предназначена для проверки соблюдения ограничений, накладываемых самим
программистом, а не для перехвата настоящих ошибок, которые интерпретатор Python в состоянии обработать самостоятельно во время
выполнения программы. Поэтому как правило инструкцию assert не используют для выявления таких проблем, как выход индекса за
допустимые пределы, несоответствие типов или деление на ноль.

Краткие итоги параграфа

  • Основной инструкцией для обработки исключений в Python является составная инструкция try/except/else/finally.
    Если в инструкциях обязательного блока try возникают ошибки, интерпретатор пытается перехватить и обработать их с помощью блоков
    except. В случае отсутствия соответствий исключение передается инструкции try, стоящей выше в программе,
    или на верхний уровень процесса. Необязательный блок else выполняется только при отсутствии ошибок, а необязательный блок
    finally выполняется всегда. При этом в отличие от блоков except блок
    finally не останавливает распространение исключений до вышестоящей инструкции try или до обработчика
    исключений по умолчанию, поэтому в случае возникновения ошибок инструкции, следующие за блоком finally в программе, выполняться не будут.
  • В стандартной библиотеке Python присутствует внушительный набор готовых классов, представляющих различные виды исключений и синтаксических
    ошибок. Все они перечислены в разделе Built-in exceptions, где также представлена и подробная
    иерархия имеющихся классов исключений.
  • Важно помнить, что при использовании блока except без указания класса перехватываемого исключения интерпретатор будет пытаться перехватить
    все имеющиеся виды встроенных исключений. Однако нужно быть осторожным при использовании такого варианта инструкции воизбежание перехвата нежелательных системных исключений,
    не связанных с работой создаваемого программного кода.
  • Для явного возбуждения исключений в Python предназначена инструкция raise, которой необходимо через
    пробел передавать имя класса или экземпляра возбуждаемого исключения. Данная инструкция может использоваться для явного возбуждения не только встроенных исключений, но
    и созданных пользователем. Пользовательские исключения представляют собой самые обычные классы, наследуемые от классов встроенных исключений, чаще всего от класса
    Exception.
  • Для возбуждения исключений на этапе отладки программы предназначена инструкция assert condition[, message], где
    condition – это условие, при невыполнении которого (возвращается False) будет возбуждено
    встроенное исключение AssertionError, а также при необходимости выведено необязательное сообщение message.
    Использовать инструкцию следует главным образом для проверки соблюдения ограничений, накладываемых самим программистом, а не для перехвата настоящих ошибок, которые могут
    быть спокойно перехвачены интерпретатором Python.

Вопросы и задания для самоконтроля

1. В каком случае выполняется код блока else составной инструкции
try/except/else/finally?

Показать решение.

Ответ. Инструкции необязательного блока else выполняются только при отсутствии
исключений в коде основного блока try.

2. Что произойдет с программой в случае возбуждения исключения, если в ней не предусмотреть его обработку?

Показать решение.

Ответ. В таком случае исключение будет передано обработчику предоставляемому интерпретатором по умолчанию.
Этот обработчик выведет сообщение об ошибке и завершит программу.

3. Перечислите верные форматы использования инструкции try:
try/except/else, try/else, try/finally,
try/except/finally, try/else/finally.

Показать решение.

Ответ. Блок else может использоваться только при наличии хотя бы одного блока
except, следовательно варианты try/else и try/else/finally
недопустимы.

4. В каком случае выполняется код блока finally?

Показать решение.

Ответ. Необязательный блок finally выполняется всегда. При этом в отличие от
блоков except блок finally не останавливает распространение исключений до вышестоящей
инструкции try или до обработчика исключений по умолчанию, поэтому в случае возникновения ошибок инструкции, следующие за
блоком finally в программе, выполняться не будут.

5. Присутствуют ли в коде условия ошибки? Проверьте свой ответ, запустив код на исполнение.
Показать решение.

try:
    a = 5; b = 0
    n = a/b   
exept Exeption as err:
    print('Ошибка: «{}».'.format(err))
else:
    print('a/b = {}'.format(n)) 
finally: 
    print('Обработка завершена!')

try:
    a = 5; b = 0
    n = a/b   
# Правильно писать except и Exception.
except Exception as err:
    print('Ошибка: «{}».'.format(err))
else:
    print('a/b = {}'.format(n)) 
finally: 
    print('Обработка завершена!')
Ошибка: «division by zero».
Обработка завершена!







			

6. Имеется ли в Python возможность создавать и возбуждать исключения вручную?

Показать решение.

Ответ. Да, имеется. Для явного возбуждения исключений в Python предназначена
инструкция raise, которой необходимо через пробел передавать имя класса или экземпляра возбуждаемого исключения. Данная
инструкция может использоваться для явного возбуждения не только встроенных исключений, но и созданных пользователем. Пользовательские исключения представляют
собой самые обычные классы, наследуемые от классов встроенных исключений, чаще всего от класса Exception.

7. Для чего служит инструкция assert?

Показать решение.

Ответ. Для возбуждения исключений на этапе отладки программы предназначена инструкция
assert condition[, message], где condition – это условие, при невыполнении
которого (возвращается False) будет возбуждено встроенное исключение AssertionError,
а также при необходимости выведено необязательное сообщение message. Использовать инструкцию следует главным образом
для проверки соблюдения ограничений, накладываемых самим программистом, а не для перехвата настоящих ошибок, которые могут быть спокойно перехвачены
интерпретатором Python.

8. Дополнительные упражнения и задачи по теме расположены в разделе
«Обработка исключений»
нашего сборника задач и упражнений по языку программирования Python.

Быстрый переход к другим страницам

() translation by (you can also view the original English article)

В этом уроке вы узнаете, как обрабатывать ошибки в Python со всех возможных точек зрения. Обработка ошибок является важнейшим аспектом проектирования, и она проходит от самых низких уровней (иногда это аппаратное обеспечение) через весь путь к конечным пользователям. Если у вас нет продуманной стратегии в этой области, ваша система будет ненадежной, а пользовательский опыт будет плохим, и вы будете иметь много проблем с отладкой и устранением неполадок.

Ключ к успеху, зная обо всех связанных сторонах вопроса, рассматривать их целостно и сформировать решение, которое учитывает каждый аспект.

Коды статуса против Исключений

Существует две основных модели обработок ошибок: Коды статуса и Исключения. Коды статуса могут использоваться в любом языке программирования. Исключения требуют поддержки языка/среды исполнения.

Python поддерживает исключения. Python и его стандартная библиотека использует исключения свободно для того, чтобы сообщить о различных состояниях, таких как IO ошибки, ошибки деления на ноль, ошибки пределов индексации, а также некоторые некритичные ситуаций, такие как конец итерации (хотя обычно эти ошибки скрыты). Большинство библиотек придерживаются этого принципа и вызывают исключения.

Это означает, что ваш код будет обрабатывать исключения, возникающие в Python или в библиотеке, во всяком случае, так что вы можете также вызывать исключения напрямую из вашего кода, когда это необходимо и не полагаться на коды статуса.

Небольшой пример

Прежде чем погрузиться в святая святых лучших практик обработки исключений и ошибок Python, давайте рассмотрим некоторые исключения в действии:

1
def f():
2

3
    return 4 / 0
4

5

6

7
def g():
8

9
    raise Exception("Don't call us. We'll call you")
10

11

12

13
def h():
14

15
    try:
16

17
        f()
18

19
    except Exception as e:
20

21
        print(e)
22

23
    try:
24

25
        g()
26

27
    except Exception as e:
28

29
        print(e)

При вызове h(), мы получаем на выходе:

1
h()
2

3
division by zero
4

5
Don't call us. We'll call you

Исключения Python 

Python исключениями являются объекты организованные в классовой иерархии.

Вот иерархия целиком:

1
BaseException
2

3
 +-- SystemExit
4

5
 +-- KeyboardInterrupt
6

7
 +-- GeneratorExit
8

9
 +-- Exception
10

11
      +-- StopIteration
12

13
      +-- StandardError
14

15
      |    +-- BufferError
16

17
      |    +-- ArithmeticError
18

19
      |    |    +-- FloatingPointError
20

21
      |    |    +-- OverflowError
22

23
      |    |    +-- ZeroDivisionError
24

25
      |    +-- AssertionError
26

27
      |    +-- AttributeError
28

29
      |    +-- EnvironmentError
30

31
      |    |    +-- IOError
32

33
      |    |    +-- OSError
34

35
      |    |         +-- WindowsError (Windows)
36

37
      |    |         +-- VMSError (VMS)
38

39
      |    +-- EOFError
40

41
      |    +-- ImportError
42

43
      |    +-- LookupError
44

45
      |    |    +-- IndexError
46

47
      |    |    +-- KeyError
48

49
      |    +-- MemoryError
50

51
      |    +-- NameError
52

53
      |    |    +-- UnboundLocalError
54

55
      |    +-- ReferenceError
56

57
      |    +-- RuntimeError
58

59
      |    |    +-- NotImplementedError
60

61
      |    +-- SyntaxError
62

63
      |    |    +-- IndentationError
64

65
      |    |         +-- TabError
66

67
      |    +-- SystemError
68

69
      |    +-- TypeError
70

71
      |    +-- ValueError
72

73
      |         +-- UnicodeError
74

75
      |              +-- UnicodeDecodeError
76

77
      |              +-- UnicodeEncodeError
78

79
      |              +-- UnicodeTranslateError
80

81
      +-- Warning
82

83
           +-- DeprecationWarning
84

85
           +-- PendingDeprecationWarning
86

87
           +-- RuntimeWarning
88

89
           +-- SyntaxWarning
90

91
           +-- UserWarning
92

93
           +-- FutureWarning
94

95
  +-- ImportWarning
96

97
  +-- UnicodeWarning
98

99
  +-- BytesWarning
100
 

Существует несколько специальных исключений, которые являются производными от BaseException, такие как SystemExit, KeyboardInterrupt и GeneratorExit. Еще есть класс Exception, который является базовым классом для StopIteration, StandardError и Warning. Все стандартные ошибки являются производными от StandardError.

Когда вы получаете исключение или функция, которую вы выполнили вызывает исключение, обычный порядок кода завершается и исключение начинает распространятся вверх по стеку вызовов до тех пор, пока не встречает обработчик соответствующих исключений. Если обработчик не доступен, процесс (или, точнее, текущий поток) будет прекращен с сообщением о необработанном исключении.

Вызов исключений

Вызов исключений очень прост. Вы просто используете ключевое слово raise чтобы вызвать объект, который является подклассом Exception. Это может быть экземпляр Exception, одно из стандартных исключений (напр., RuntimeError), или подкласс Exception, который вы получили. Вот небольшой фрагмент кода, который демонстрирует эти случаи:

1
# Raise an instance of the Exception class itself

2

3
raise Exception('Ummm... something is wrong')
4

5

6

7
# Raise an instance of the RuntimeError class

8

9
raise RuntimeError('Ummm... something is wrong')
10

11

12

13
# Raise a custom subclass of Exception that keeps the timestamp the exception was created

14

15
from datetime import datetime
16

17

18

19
class SuperError(Exception):
20

21
    def __init__(self, message):
22

23
        Exception.__init__(message)
24

25
        self.when = datetime.now()
26

27

28

29

30

31
raise SuperError('Ummm... something is wrong')

Перехват исключений

Вы получили исключение, с условием except, как вы видели в примере. Когда вы получили исключение, у вас есть три варианта:

  • Пропустить (обработать его и продолжить работу).
  • Сделать что-то вроде записи в журнал, но получить повторно то же самое исключение, чтобы продолжить его обработку на более высоком уровне.
  • Вызвать другое исключение вместо текущего.

Пропустить исключение

Если вы знаете, как его обработать и как его полностью восстановить, можно пропустить исключение.

Например, если вы получаете входящий файл, который может быть в различных форматах (JSON, YAML), вы можете попробовать проанализировать его с помощью различных средств. Если анализатор JSON создаёт исключение, которое показывает, что файл имеет некорректный формат JSON, вы пропускаете его и пробуете проанализировать через парсер YAML. Если парсер YAML также не справляется с задачей, тогда вы даёте исключению перейти на следующий уровень.

1
import json
2

3
import yaml
4

5

6

7
def parse_file(filename):
8

9
    try:
10

11
        return json.load(open(filename))
12

13
    except json.JSONDecodeError
14

15
        return yaml.load(open(filename))

Обратите внимание, что другие исключения (например, file not found или no read permissions) будут переходить на следующий уровень и не будут обработаны конкретным исключением. Это хорошая тактика в том случае, если вы хотите использовать YAML парсер, когда анализ с помощью JSON парсера не удался. 

Если вы хотите обрабатывать все исключения, тогда используйте except Exception. Например:

1
def print_exception_type(func, *args, **kwargs):
2

3
    try:
4

5
        return func(*args, **kwargs)
6

7
    except Exception as e:
8

9
        print type(e)

Обратите внимание, что, добавляя as e, вы привязываете объект к имении e в вашем исключении.

Перезапуск исключения

Чтобы перезапустить исключение, просто напишите raise без аргументов внутри обработчика. Это позволит выполнить некоторую локальную обработку, но также пропустит исключение для обработки на верхние уровни. Здесь, функция invoke_function() выводит тип исключения в консоль и затем повторно вызывает его.

1
def invoke_function(func, *args, **kwargs):
2

3
    try:
4

5
        return func(*args, **kwargs)
6

7
    except Exception as e:
8

9
        print type(e)
10

11
        raise

Вызов Различных Исключений

Есть несколько случаев, когда вы хотели бы вызвать другое исключение. Иногда вы хотите сгруппировать несколько различных низкоуровневых исключений в одну категорию, которая равномерно обрабатывается на более высоком уровне кода. В других случаях вам нужно преобразовать исключение на уровне пользователя и предоставить контекст конкретного приложения.

Финальное утверждение

Иногда вы хотите убедиться, что код очистки выполняется, даже если где-то по пути возникло исключение. Например, у вас может быть подключение к базе данных, которое требуется закрыть, как только вы закончите. Это неправильный способ сделать это:

1
def fetch_some_data():
2

3
    db = open_db_connection()
4

5
    query(db)
6

7
    close_db_Connection(db)

Если функция query() вызывает исключение, то вызов close_db_connection() никогда не будет выполнен и подключение останется открытым. Утверждение finally всегда выполняется после всех попыток обработчика. Вот как сделать это правильно:

1
def fetch_some_data():
2

3
    db = None
4

5
    try:
6

7
        db = open_db_connection()
8

9
        query(db)
10

11
    finally:
12

13
        if db is not None:
14

15
            close_db_connection(db)

Вызов open_db_connection() может не вернуть подключение или вызвать исключение. В этом случае нет необходимости закрывать соединение.

При использовании finally, вы должны быть осторожны, чтобы не вызвать другие исключения, потому, что они скроют исходное.

Диспетчеров Контекста

Контекстные менеджеры обеспечивают еще один механизм обработки ресурсов, таких как файлы или подключения к БД, которые выполняются автоматически, даже если исключения были вызваны. Вместо блоков try-finally, можно использовать определение with. Вот пример с файлом:

1
def process_file(filename):
2

3
     with open(filename) as f:
4

5
        process(f.read())

Теперь, даже если process() вызывает исключение, этот файл будет закрыт правильно сразу же когда область видимости блока with завершена, независимо от того, было исключение обработано или нет.

Ведение журнала

Ведение журнала обычно требуется в нетривиальных, масштабных системах. Это особенно полезно в веб-приложениях, где вы можете исправить все исключения универсальным способом: Просто записать в журнал исключение и вернуть сообщение об ошибке.

При записи полезно учитывать тип исключения, сообщение и маршрут ошибки. Вся эта информация доступна через объект sys.exc_info, но если вы используете logger.exception() метод в обработчике исключений, Python извлечёт всю необходимую для вас информацию.

Это лучший пример, которую я рекомендую:

1
import logging
2

3
logger = logging.getLogger()
4

5

6

7
def f():
8

9
    try:
10

11
        flaky_func()
12

13
    except Exception:
14

15
        logger.exception()
16

17
        raise

Если вы будете придерживаться этому шаблону,тогда (предполагаю, что вы настроили запись в журнал правильно), независимо от того, что происходит, вы будете иметь очень понятные записи в ваших журналах о что пошло не так, и будете иметь возможность исправить проблему.

Если вы повторно вызываете исключение, убедитесь что вы не записывайте в журнал повторно одну и туже ошибку на разных уровнях. Эту будет бесполезный мусор. который может запутать вас и заставить думать что произошло несколько ошибок, хотя на самом деле одна ошибка была зарегистрирована несколько раз.

Самый простой способ сделать это заключается в том, чтобы позволить всем исключениям переходить дальше (если они могут быть обработаны и пропущены ранее), и затем выполнить запись в журнал на самом верхнем уровне системы/приложения.

Sentry

Ведение журнала это возможность. Наиболее распространенные реализации которой, является использование журнала. Но, для крупномасштабных распределенных систем с сотнями, тысячами или более серверов, это не всегда лучшее решение.

Для отслеживания исключений во всей инфраструктуре, такой сервис как sentry очень полезен. Он централизует все сообщения об исключениях, и в дополнении к маршруту ошибки он добавляет состояние каждого состояния стека (значение переменных в то время, когда было вызвано исключение). Он также предоставляет приятный интерфейс с панелью мониторинга, отчетами и способами получать сообщения по нескольким проектам сразу. Он предоставляется с открытым исходным кодом, так что вы можете запустить свой собственный сервер или оформить подписку на предустановленную версию.

Работа с временной ошибкой

Некоторые ошибки являются временными, в частности при работе с распределенными системами. Система, которая начинает ругаться при первом признаке ошибки не очень полезна.

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

Другой подход заключается в том, чтобы быстро получить ошибку и затем повторить попытку. Преимущество в том, что если цель реагирует быстро, то вам не придется тратить много времени находясь в режиме ожидания и можно реагировать незамедлительно. Но если это сделать не удалось, вы можете повторять запрос несколько раз до тех пор, пока вы не решите, что ресурс действительно недоступен и вызовете исключение. В следующем разделе я расскажу об оформителе, который может сделать это для вас.

Полезные оформители

Два оформителя которые могут помочь в обработке ошибок, это @log_error, который записывает исключение и затем вновь вызывает его и @retry оформитель, который будет повторять вызов функции несколько раз.

Журнал ошибок

Вот пример простой реализации. Оформитель исключает объект logger. Когда он оформляет функцию и функция вызвана, он обработает вызов в блоке try-except, и если там было исключение сделает запись в журнал и наконец повторно вызовет исключение.

1
def log_error(logger)
2

3
    def decorated(f):
4

5
        @functools.wraps(f)
6

7
        def wrapped(*args, **kwargs):
8

9
            try:
10

11
                return f(*args, **kwargs)
12

13
            except Exception as e:
14

15
                if logger:
16

17
                    logger.exception(e)
18

19
                raise
20

21
        return wrapped
22

23
    return decorated

Вот пример, как его использовать:

1
import logging
2

3
logger = logging.getLogger()
4

5

6

7
@log_error(logger)
8

9
def f():
10

11
    raise Exception('I am exceptional')

Retrier

Здесь, очень хорошая реализация @retry оформителя.

1
import time
2

3
import math
4

5

6

7
# Retry decorator with exponential backoff

8

9
def retry(tries, delay=3, backoff=2):
10

11
  '''Retries a function or method until it returns True.

12


13


14


15
  delay sets the initial delay in seconds, and backoff sets the factor by which

16


17
  the delay should lengthen after each failure. backoff must be greater than 1,

18


19
  or else it isn't really a backoff. tries must be at least 0, and delay

20


21
  greater than 0.'''
22

23

24

25
  if backoff <= 1:
26

27
    raise ValueError("backoff must be greater than 1")
28

29

30

31
  tries = math.floor(tries)
32

33
  if tries < 0:
34

35
    raise ValueError("tries must be 0 or greater")
36

37

38

39
  if delay <= 0:
40

41
    raise ValueError("delay must be greater than 0")
42

43

44

45
  def deco_retry(f):
46

47
    def f_retry(*args, **kwargs):
48

49
      mtries, mdelay = tries, delay # make mutable

50

51

52

53
      rv = f(*args, **kwargs) # first attempt

54

55
      while mtries > 0:
56

57
        if rv is True: # Done on success

58

59
          return True
60

61

62

63
        mtries -= 1      # consume an attempt

64

65
        time.sleep(mdelay) # wait...

66

67
        mdelay *= backoff  # make future wait longer

68

69

70

71
        rv = f(*args, **kwargs) # Try again

72

73

74

75
      return False # Ran out of tries :-(

76

77

78

79
    return f_retry # true decorator -> decorated function

80

81
  return deco_retry  # @retry(arg[, ...]) -> true decorator

Заключение

Обработка ошибок имеет решающее значение для пользователей и разработчиков. Python предоставляет отличную поддержку на уровне языка и стандартной библиотеки для обработки ошибок на основе исключений. Следуя рекомендациям старательно, вы можете преодолеть этот аспект, которым так часто пренебрегают.

Put your try/except structure more in-wards. Otherwise when you get an error, it will break all the loops.

Perhaps after the first for-loop, add the try/except. Then if an error is raised, it will continue with the next file.

for infile in listing:
    try:
        if infile.startswith("ABC"):
            fo = open(infile,"r")
            for line in fo:
                if line.startswith("REVIEW"):
                    print infile
            fo.close()
    except:
        pass

This is a perfect example of why you should use a with statement here to open files. When you open the file using open(), but an error is catched, the file will remain open forever. Now is better than never.

for infile in listing:
    try:
        if infile.startswith("ABC"):
            with open(infile,"r") as fo
                for line in fo:
                    if line.startswith("REVIEW"):
                        print infile
    except:
        pass

Now if an error is caught, the file will be closed, as that is what the with statement does.

Понравилась статья? Поделить с друзьями:
  • Как исправить ошибку в поданном исковом заявлении
  • Как исправить ошибку в пещере
  • Как исправить ошибку в повер поинте
  • Как исправить ошибку в персонифицированном учете стажа
  • Как исправить ошибку в повер поинт