Обработка ошибок: как правильно расставить приоритеты исключений - PullRequest
0 голосов
/ 09 апреля 2020

Существует две функции: одна загружает файл Excel (ExcelFileUploadView(APIView)), а другая обрабатывает загруженный файл (def parse_excel_rfi_sheet). Функция parse_excel_rfi_sheet вызывается внутри ExcelFileUploadView(APIView)

class ExcelFileUploadView(APIView):
    parser_classes = (MultiPartParser, FormParser)
    permission_classes = (permissions.AllowAny,)

    def put(self, request, format=None):
        if 'file' not in request.data:
            raise ParseError("Empty content")
        f = request.data['file']
        filename = f.name
        if filename.endswith('.xlsx'):
            try:
                file = default_storage.save(filename, f)
                r = parse_excel_rfi_sheet(file)
                status = 200
            except:
                raise Exception({"general_errors": ["Error during file upload"]})
            finally:
                default_storage.delete(file)
        else:
            status = 406
            r = {"general_errors": ["Please upload only xlsx files"]}
        return Response(r, status=status) 

def parse_excel_rfi_sheet(file):
    workbook = load_workbook(filename=file)
    sheet = workbook["RFI"]
    curent_module_coordinate = []
    try:
        ....
        curent_module_coordinate.append(sheet['E688'].value)  
        curent_module_coordinate.append(sheet['E950'].value)  
        if check_exel_rfi_template_structure(structure=curent_module_coordinate):
            file_status = True
        else:
            file_status = False
    except:
        raise Exception({"general_errors": ["Error during excel file parsing. Unknown module cell"]})

Проблема заключается в том, что при возникновении ошибки внутри parse_excel_rfi_sheet я не вижу вызова {"general_errors": ["Error during excel file parsing. Unknown module cell"]} Вместо этого я всегда вижу вызов

{"general_errors": ["Error during file upload"]}

Вот почему я не могу понять, на каком этапе произошла ошибка: в момент загрузки файла или в момент обработки. Как это изменить?

Ответы [ 3 ]

1 голос
/ 09 апреля 2020

Ваша проблема заключается в перехвате абсолютно всех исключений, сначала в parse_excel_rfi_sheet, затем еще раз в вашем put методе. Оба голых, за исключением предложения (except: whatever_code_here) и большие try блоки являются антипаттернами - вы хотите отлавливать только те исключения, которые ожидаете в данный момент (используя except (SomeExceptionType, AnotherExceptionType, ...) as e:, и иметь как можно меньше кода в вашем try блоков, так что вы уверены, что знаете, откуда исходит исключение.

Единственное исключение (без каламбура) из этого правила - это случай с обработчиками «catch all» на более высоком уровне, которые используются для отлавливать неожиданные ошибки, регистрировать их (чтобы вы могли увидеть, что произошло) и представлять пользователю дружественное сообщение об ошибке - но даже в этом случае вам не нужен только пункт «исключение», кроме except Exception as e.

TL; DR: никогда не предполагайте ничего о том, какое исключение было возбуждено, где и почему, и никогда не передавайте исключения молча (по крайней мере, регистрируйте их - и проверяйте свои журналы).

1 голос
/ 09 апреля 2020

Поскольку вы вызываете parse_excel_rfi_sheet из ExcelFileUploadView всякий раз, когда исключение {"general_errors": ["Error during excel file parsing. Unknown module cell"]} вызывается из parse_excel_rfi_sheet, функция try, блок из ExcelFileUploadView завершается сбоем и приходит к except и вызывает исключение {"general_errors": ["Error during file upload"]}.

Это можно проверить, напечатав исключение, вызванное функцией ExcelFileUploadView. Измените блок try на следующее:

try:
    file = default_storage.save(filename, f)
    r = parse_excel_rfi_sheet(file)
    status = 200
except Exception as e:
    print("Exception raised ", e)
    raise Exception({"general_errors": ["Error during file upload"]})
0 голосов
/ 09 апреля 2020

raise Exception(...) генерирует новый экземпляр Exception и вызывает его. Это означает, что try ... except в put эффективно выбрасывает исключение, которое оно перехватило, и заменяет его новым сообщением «Ошибка при загрузке файла», поэтому вы всегда видите одно и то же сообщение.

Чистым способом справиться с этим было бы определение пользовательского подкласса Exception (например, InvalidFormatException) и повышение этого подкласса в parse_excel_rfi_sheet, с двумя различными except падежами в put:

class InvalidFormatException(Exception):
    pass

[...]

def parse_excel_rfi_sheet(file):
    workbook = load_workbook(filename=file)
    sheet = workbook["RFI"]
    curent_module_coordinate = []
    try:
        ....
        curent_module_coordinate.append(sheet['E688'].value)  
        curent_module_coordinate.append(sheet['E950'].value)  
        if check_exel_rfi_template_structure(structure=curent_module_coordinate):
            file_status = True
        else:
            file_status = False
    except:
        raise InvalidFormatException({"general_errors": ["Error during excel file parsing. Unknown module cell"]})

Ваш put затем становится:

    def put(self, request, format=None):
        if 'file' not in request.data:
            raise ParseError("Empty content")
        f = request.data['file']
        filename = f.name
        if filename.endswith('.xlsx'):
            try:
                file = default_storage.save(filename, f)
                r = parse_excel_rfi_sheet(file)
                status = 200
            except InvalidFormatException:
                raise # pass on the exception
            except:
                raise Exception({"general_errors": ["Error during file upload"]})
            finally:
                default_storage.delete(file)
        else:
            status = 406
            r = {"general_errors": ["Please upload only xlsx files"]}
        return Response(r, status=status)

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

...