Как перечислить все исключения, которые функция могла бы повысить в Python 3?

Есть ли программный способ получить список всех исключений, которые могла бы повысить функция?

Я знаю, например, что os.makedirs(path[, mode]) может поднять PermissionError (и, возможно, другие), но в документации упоминается только OSError . (Это всего лишь пример – может быть, даже плохой, меня это особенно не интересует – больше в проблеме вообще).

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

Решение, представленное в « Python: как я могу узнать, какие исключения могут быть выбраны из вызова метода », не работает в Python 3; нет compiler .

  • Скрыть некоторые возможные ошибки Pylint
  • urllib HTTPS запрос: <urlopen error неизвестный тип url: https>
  • многократный импорт python
  • Python не находит библиотеки MagickWand (несмотря на правильное расположение?)
  • Должен ли я использовать два цикла событий asyncio в одной программе?
  • Сериализация члена Enum для JSON
  • Установите django1.7 с помощью Python 3.4 с помощью virtualenv
  • Python 3 datetime.fromtimestamp не работает на 1 микросекунду
  • 2 Solutions collect form web for “Как перечислить все исключения, которые функция могла бы повысить в Python 3?”

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

    • функции, выполняющие произвольный код (например, exec(')(rorrEeulaV esiar'[::-1]) вызывает ValueError )

    • функции, которые не написаны на Python

    • функции, вызывающие другие функции, которые могут распространять ошибки вызывающего

    • функции, восстанавливающие активные исключения в блоке except: block

    К сожалению, этот список неполный.

    Например, os.makedirs написано на Python, и вы можете увидеть его источник:

     ... try: mkdir(name, mode) except OSError as e: if not exist_ok or e.errno != errno.EEXIST or not path.isdir(name): raise 

    Bare raise re-raises последнее активное исключение ( OSError или один из его подклассов). Вот иерархия классов для OSError :

     +-- OSError | +-- BlockingIOError | +-- ChildProcessError | +-- ConnectionError | | +-- BrokenPipeError | | +-- ConnectionAbortedError | | +-- ConnectionRefusedError | | +-- ConnectionResetError | +-- FileExistsError | +-- FileNotFoundError | +-- InterruptedError | +-- IsADirectoryError | +-- NotADirectoryError | +-- PermissionError | +-- ProcessLookupError | +-- TimeoutError 

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

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


    Однако для простых случаев, таких как

     raise Exception # without arguments raise Exception('abc') # with arguments 

    комбинация функциональности модуля ast и inspect.getclosurevars (чтобы получить классы исключений, введенные в Python 3.3), могут дать довольно точные результаты:

     from inspect import getclosurevars, getsource from collections import ChainMap from textwrap import dedent import ast, os class MyException(Exception): pass def g(): raise Exception class A(): def method(): raise OSError def f(x): int() A.method() os.makedirs() g() raise MyException raise ValueError('argument') def get_exceptions(func, ids=set()): try: vars = ChainMap(*getclosurevars(func)[:3]) source = dedent(getsource(func)) except TypeError: return class _visitor(ast.NodeTransformer): def __init__(self): self.nodes = [] self.other = [] def visit_Raise(self, n): self.nodes.append(n.exc) def visit_Expr(self, n): if not isinstance(n.value, ast.Call): return c, ob = n.value.func, None if isinstance(c, ast.Attribute): parts = [] while getattr(c, 'value', None): parts.append(c.attr) c = c.value if c.id in vars: ob = vars[c.id] for name in reversed(parts): ob = getattr(ob, name) elif isinstance(c, ast.Name): if c.id in vars: ob = vars[c.id] if ob is not None and id(ob) not in ids: self.other.append(ob) ids.add(id(ob)) v = _visitor() v.visit(ast.parse(source)) for n in v.nodes: if isinstance(n, (ast.Call, ast.Name)): name = n.id if isinstance(n, ast.Name) else n.func.id if name in vars: yield vars[name] for o in v.other: yield from get_exceptions(o) for e in get_exceptions(f): print(e) 

    печать

     <class '__main__.MyException'> <class 'ValueError'> <class 'OSError'> <class 'Exception'> 

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

    Обнаружение исключения в не встроенном исходном коде:

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

     import ast def find_raise(body): raises = [] for ast_ in body: if isinstance(ast_, ast.Raise): raises.append(ast_) if hasattr(ast_, 'body'): raises += find_raise(ast_.body) return list(set(raises)) test = ''' def f(arg): raise OSError(arg) ''' raises = find_raise(ast.parse(test).body) print [i.type.func.id for i in raises] # print ['OSError'] 

    Этот метод работает для каждого фрагмента кода, который вы написали.


    Обнаружение исключения во встроенных методах

    Вы не можете разобрать встроенную функцию, например os.makedirs .

    Две альтернативы:

    • Вы можете посмотреть тесты, включенные в ваш дистрибутив python ( например, cpython )
    • и если ваш целевой метод предлагает исходный код python, вы можете проанализировать его, как раньше (код будет в /usr/lib/python3/*.py)

    Для всех собственных методов С, вы застряли в документации и должны доверять ей. Когда os.makedirs говорит, что возвращает только OSError , это правда, поскольку исключения PermissionError и FileExistError являются подклассами OSError .

    Чтобы найти Ошибки программно для встроенного, вы можете использовать этот пример:

     >>> import re >>> re.findall(r'\w+Error', open.__doc__) ['IOError', 'FileExistsError', 'ValueError'] >>> re.findall(r'\w+Error', os.makedirs.__doc__) ['OSError'] 

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

    Interesting Posts
    Python - лучший язык программирования в мире.