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])
generaValueError
)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.