Как предотвратить "слишком широкое исключение" в этом случае?

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

Я выполняю его с чем-то вроде этого:

list_of_functions = [f_a,f_b,f_c]
for current_function in list_of_functions:
    try:
        current_function()
    except Exception:
        print(traceback.format_exc())

Он работает нормально, но не совместим с PEP8:

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

Например, используйте:

try:
    import platform_specific_module
except ImportError:
    platform_specific_module = None

Голый, кроме: предложение будет ловить SystemExit и KeyboardInterrupt исключений, затрудняя прерывание программы с помощью Control-C, и может маскировать другие проблемы. Если вы хотите поймать все исключения что ошибки сигнальной программы, используйте исключение Исключение: (голый кроме эквивалентно исключению BaseException:).

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

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

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

Как это хорошо?

Ответ 1

Руководство PEP8, которое вы цитируете, предполагает, что в вашем случае можно использовать голые исключения, если вы регистрируете ошибки. Я бы подумал, что вы должны покрыть столько исключений, сколько сможете/знаете, как бороться, а затем записывать остальные и pass, например.

import logging

list_of_functions = [f_a,f_b,f_c]
for current_function in list_of_functions:
    try:
        current_function()
    except KnownException:
        raise
    except Exception as e:
        logging.exception(e)

Ответ 2

__author__ = 'xray'
# coding: utf8
from wiki_baike import url_manager, html_downloader, html_parser, html_outputer
import logging

class SpiderMain(object):
    def __init__(self):
        self.url = url_manager.UrlManager()
        self.downloader = html_downloader.HtmlDownloader()
        self.parser = html_parser.HtmlParser()
        self.outputer = html_outputer.HtmlOutputer()

    def craw(self, root_url):
        count = 1
        self.urls.add_new_url(root_url)
        while self.urls.has_new_url():
            try:
                new_url = self.urls.get_new_url()
                print 'craw %d : %s' % (count, new_url)
                html_cont = self.downloader.download(new_url)
                new_urls, new_data = self.parser.parse(new_url, html_cont)
                self.urls.add_new_urls(new_urls)
                self.outputer.collect_data(new_data)
                if count == 1000:
                    break
                count += 1
            except Exception as e:
                logging.exception(e)
                print 'error'
        self.outputer.output_html()

    if __name__=='__main__':
    root_url = 'http://baike.baidu.com/view/21087.html'
    # root_url = 'https://rollbar.com/docs/'
    obj_spider = SpiderMain()
    obj_spider.craw(root_url)

Ответ 3

Из выпуска PY-9715 на yourtrack.jetbrains.com:

"Too broad exception clauses" inspection

От pep-0348:

BaseException

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

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

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

Ответ 4

Возможно, вы имеете в виду, что каждая функция может вызывать разные исключения? Когда вы указываете тип исключения в предложении except, это может быть любое имя, которое ссылается на исключение, а не только на имя класса.

например.

def raise_value_error():
    raise ValueError

def raise_type_error():
    raise TypeError

def raise_index_error():
    doesnt_exist

func_and_exceptions = [(raise_value_error, ValueError), (raise_type_error, TypeError), 
    (raise_index_error, IndexError)]

for function, possible_exception in func_and_exceptions:
   try:
       function()
   except possible_exception as e:
       print("caught", repr(e), "when calling", function.__name__)

печатает:

caught ValueError() when calling raise_value_error
caught TypeError() when calling raise_type_error
Traceback (most recent call last):
  File "run.py", line 14, in <module>
    function()
  File "run.py", line 8, in raise_index_error
    doesnt_exist
NameError: name 'doesnt_exist' is not defined

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

Ответ 5

Я думаю, что в некоторых редких случаях поймать общее исключение просто оправданно, и есть способ обмануть проверку PEP8:

list_of_functions = [f_a,f_b,f_c]
for current_function in list_of_functions:
try:
    current_function()
except (ValueError, Exception):
    print(traceback.format_exc())

Вы можете заменить ValueError любым другим. Это работает для меня (по крайней мере, в PyCharm).