Почему Python Introspection не знает исключений в функции? - PullRequest
0 голосов
/ 24 сентября 2018

Когда я обрабатываю какой-то код исключения кода, я удивляюсь, почему python не включил, какое исключение будет вызывать функция в его системе внутреннего анализа. Например, когда мне нужно использовать функцию, ссылаются на многие другие функции, которые будут вызывать различные исключения.Я должен рассмотреть все, что происходит в моей бизнес-логике.Вот так:

def a():
    raise Exception('exception1')

def b():
    a()
    raise Exception('exception2')

def c():
    b()
    raise Exception('exception3')

def business():
    try:
        c()
    except Exception as e:
        pass

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

И, как я знаю, Java явно аннотирует 'Throw' в определении функции, и IDE и программист могут легко узнать, какие исключения следует обрабатывать.

Будет лучше, если я смогу узнать все исключения с помощьюсам объект, например:

all_exception = obj.__exceptions__()

Итак, мой вопрос: почему Python не включает в себя объект-исключение в объекте функции.
Кто может объяснить дизайн Python?

1 Ответ

0 голосов
/ 24 сентября 2018

Python - это динамический язык, и вы не можете заранее знать, какие исключения может генерировать функция.

Возьмите этот пример:

def throw(exception):
    raise exception

Какое исключение будет выполнять эта функцияподнять?Я могу использовать throw(ValueError) или throw(TypeError('foobar')), и оба будут работать и являются действительными Python:

>>> throw(ValueError)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in throw
ValueError
>>> throw(TypeError('foobar'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in throw
TypeError: foobar

Исключениями являются только классы и экземпляры .Текущие версии Python требуют, чтобы класс исключений был производным от BaseException, но в старых версиях Python вы даже можете использовать строки для исключений (raise "Your mother was a hamster").

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

>>> def oops():
...     raise ValueError('Oops')
...
>>> ValueError = TypeError
>>> oops()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in oops
TypeError: Oops

Именно поэтому функции Python не могут раскрыть, какие исключения они вызывают.

Обратите внимание, что никогда не бывает веской причины использовать обычный Exception,Используйте одно из стандартных исключений, если они имеют смысл (ValueError, TypeError, IndexError, KeyError и т. Д.), Или создайте свои собственные специфичные для API исключения, создав подклассы из Exception или более конкретный подкласс исключений.

Затем document Ваш API правильно.Укажите, какие исключения следует ожидать разработчику, где это необходимо.Стандартные исключения не должны быть прописаны;вполне очевидно, что функция, которая работает только со строками, выдаст TypeError, если вместо этого вы передадите файловый объект.

Вы можете использовать иерархию классов исключений в своем бизнес-приложении, если вам нужно перехватить несколько типов:

class BusinessException(Exception):
    """The base exception for all of Business APIs"""

class SpecificBusinessException(BusinessException):
    """Exception that indicates a specific problem occurred"""

class DifferenBusinessException(BusinessException):
    """Exception that indicates a different specific problem occurred"""

затем поднимите подклассовые исключения и поймайте BusinessException для обработки всех или поймайте только определенные подклассы для настройки обработки.

Если вы должны выяснитькакие коды исключений поднимают и принимают риски, связанные с возможностью изменения имен динамическим языком, тогда вы можете использовать анализ абстрактного синтаксического дерева (AST) , чтобы хотя бы найти некоторую информацию оисключения.Для прямых операторов raise Name и raise Name(args..) извлечение этих имен или вызовов путем обхода AST является по меньшей мере относительно простым:

import builtins
import inspect
import ast

class ExceptionExtractor(ast.NodeVisitor):
    def __init__(self):
        self.exceptions = []
    def visit_Raise(self, node):
        if node.exc is None:
            # plain re-raise
            return
        exc_name = node.exc
        if isinstance(exc_name, ast.Call):
            exc_name = exc_name.func
        if not (isinstance(exc_name, ast.Name) and
                isinstance(exc_name.ctx, ast.Load)):
            # not a raise Name or raise Name(...)
            return
        self.exceptions.append(exc_name.id)

def global_exceptions_raised(func):
    """Extract the expressions used in raise statements

    Only supports raise Name and raise Name(...) forms, and
    only if the source can be accessed. No checks are made for the
    scope of the name.

    returns references to those exception names that can be loaded from
    the function globals.

    """
    source = inspect.getsource(func)
    tree = ast.parse(source)
    extractor = ExceptionExtractor()
    extractor.visit(tree)
    fglobals = {**func.__globals__, **vars(builtins)}
    return [fglobals[name] for name in extractor.exceptions if name in fglobals]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...