index funcion español python python-3.4

funcion - append python español



¿Cómo enumerar todas las excepciones que una función podría generar en Python 3? (2)

¿Existe una forma programática de obtener una lista de todas las excepciones que una función podría generar?

Sé, por ejemplo, que os.makedirs(path[, mode]) puede generar PermissionError (y quizás otros), pero la documentación solo menciona OSError . (Esto es solo un ejemplo, tal vez incluso uno malo; no estoy especialmente interesado en esta función, más en el problema en general).

¿Hay una forma programática de encontrar todas las excepciones posibles cuando no están / mal documentadas? Esto puede ser especialmente útil en bibliotecas de terceros y bibliotecas que no se envían con el código fuente de Python.

La solución presentada en " Python: ¿Cómo puedo saber qué excepciones se pueden generar desde una llamada de método " no funciona en Python 3; no hay paquete compiler


Encontrando una excepción en el código fuente no incorporado:

Como se dijo en el tema Python: ¿Cómo puedo saber qué excepciones se pueden generar a partir de una llamada de método ? Puede obtener el árbol de sintaxis abstracta y buscar excepciones generadas.

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'']

Este método funciona para cada pieza de código que haya escrito.

Encontrar una excepción en los métodos incorporados

No se puede analizar la función os.makedirs como os.makedirs .

Dos alternativas:

  • Puedes echar un vistazo a las pruebas incluidas en tu distribución de python (por ejemplo, con cpython )
  • y si su método de destino ofrece el código fuente de Python, puede analizarlo como anteriormente (el código estaría en /usr/lib/python3/*.py)

Para todos los métodos C nativos, está atascado con la documentación y debe confiar en ella. Cuando os.makedirs dice que solo devuelve OSError , es verdadero, ya que las excepciones PermissionError y FileExistError son subclases de OSError .

Para encontrar errores programáticamente para incorporados puedes usar este ejemplo:

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

Captura todas las excepciones con un nombre que termina con ''Error'', seguramente se puede extender para encontrar todas las excepciones estándar.


No puede obtener resultados confiables para algunas funciones (si no la mayoría). Algunos ejemplos:

  • funciones que ejecutan código arbitrario (ej. exec('')(rorrEeulaV esiar''[::-1]) genera ValueError )

  • Funciones que no están escritas en Python.

  • Funciones que llaman a otras funciones que pueden propagar errores a la persona que llama.

  • Funciones que vuelven a subir excepciones activas en el bloque except:

Lamentablemente, esta lista está incompleta.

Por ejemplo, os.makedirs está escrito en Python y puedes ver su fuente:

... 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 vuelve a elevar la última excepción activa ( OSError o una de sus subclases). Aquí está la jerarquía de clases para OSError :

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

Para obtener los tipos de excepción exactos, deberá consultar mkdir , las funciones a las que llama, las funciones a las que llaman esas funciones, etc.

Por lo tanto, obtener posibles excepciones sin ejecutar la función es muy difícil y realmente no debería hacerlo.

Sin embargo, para casos simples como

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

una combinación de la funcionalidad del módulo ast y inspect.getclosurevars (para obtener clases de excepción, se introdujo en Python 3.3) puede producir resultados bastante precisos:

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)

huellas dactilares

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

Tenga en cuenta que este código solo funciona para funciones escritas en Python.