gpt4 book ai didi

python - 返回 OSError 异常类的子类实例的逻辑在哪里?

转载 作者:太空狗 更新时间:2023-10-29 20:25:11 25 4
gpt4 key购买 nike

我一直在寻找一些对某些人来说可能相对愚蠢但对我来说非常有趣的东西! :-)

在 Python 3.3 中,输入和输出错误已与 OSError 合并,因此异常类层次结构发生了变化。内置类 OSError 的一个有趣特性是,它在传递 errnostrerror

时返回它的子类
>>> OSError(2, os.strerror(2))
FileNotFoundError(2, 'No such file or directory')

>>> OSError(2, os.strerror(2)).errno
2
>>> OSError(2, os.strerror(2)).strerror
'No such file or directory'

如您所见,将 errnostrerror 传递给 OSError 的构造函数会返回 FileNotFoundError 实例,它是一个OSError 的子类。

Python 文档:

The constructor often actually returns a subclass of OSError, as described in OS exceptions below. The particular subclass depends on the final errno value. This behaviour only occurs when constructing OSError directly or via an alias, and is not inherited when subclassing.

我想编写一个以这种方式运行的子类。这主要是出于好奇,而不是现实世界的代码。我也想知道,创建子类对象的逻辑在哪里,例如它是否编码在 __new__ 中?如果 __new__ 包含创建子类实例的逻辑,那么从 OSError 继承通常会返回此行为,除非 __new__ 中有某种类型检查:

>>> class A(OSError): pass 
>>> A(2, os.strerror(2))
A(2, 'No such file or directory')

然后必须进行类型检查:

# If passed OSError, returns subclass instance
>>> A.__new__(OSError, 2, os.strerror(2))
FileNotFoundError(2, 'No such file or directory')

# Not OSError? Return instance of A
>>> A.__new__(A, 2, os.strerror(2)
A(2, 'No such file or directory')

我一直在挖掘 C 代码以找出这段代码的确切位置,因为我不是 C 方面的专家,我怀疑这确实是逻辑,而且(坦率地说,我对此持怀疑态度):

exceptions.c

if (myerrno && PyLong_Check(myerrno) &&
errnomap && (PyObject *) type == PyExc_OSError) {
PyObject *newtype;
newtype = PyDict_GetItem(errnomap, myerrno);
if (newtype) {
assert(PyType_Check(newtype));
type = (PyTypeObject *) newtype;
}
else if (PyErr_Occurred())
goto error;
}
}

现在我想知道在不使用 C 代码的情况下从 Python 本身扩展 errnomap 的可能性,以便 OSErro 可以创建用户定义类的实例,如果你问我为什么要那样做?我会说,只是为了好玩。

最佳答案

你是对的,errnomap 是保存从 errno 值到 OSError 子类的映射的变量,但不幸的是它没有导出到 exceptions.c 之外 源文件,因此没有可移植的方式来修改它。


可以使用高度不可移植的 hack 来访问它,我在下面纯粹本着乐趣。这应该适用于任何 x86-64 Linux 系统。

>>> import os, sys
>>> os.system("""gdb -p %d \
-ex 'b PyDict_GetItem if (PyLong_AsLongLong($rsi) == -1 ? \
(PyErr_Clear(), 0) : PyLong_AsLongLong($rsi)) == 0xbaadf00d' \
-ex c \
-ex 'call PySys_SetObject("errnomap", $rdi)' --batch >/dev/null 2>&1 &""" % os.getpid())
0
>>> OSError(0xbaadf00d, '')
OSError(3131961357, '')
>>> sys.errnomap
{32: <class 'BrokenPipeError'>, 1: <class 'PermissionError'> [...]}
>>> class ImATeapotError(OSError):
pass
>>> sys.errnomap[99] = ImATeapotError
>>> OSError(99, "I'm a teapot")
ImATeapotError(99, "I'm a teapot")

快速解释这是如何工作的:

gdb -p %d [...] --batch >/dev/null 2>&1 &

将调试器附加到当前 Python 进程(os.getpid()),在无人值守模式下(--batch),丢弃输出(>/dev/null 2>&1) 并在后台 (&),允许 Python 继续运行。

b PyDict_GetItem if (PyLong_AsLongLong($rsi) == -1 ? (PyErr_Clear(), 0) : PyLong_AsLongLong($rsi)) == 0xbaadf00d

当Python程序accesses any dictionary , 如果键 is an int 中断具有魔法值(稍后用作 OSError(0xbaadf00d, ''));如果它不是 int,我们刚刚引发了 TypeError,所以 suppress it .

调用 PySys_SetObject("errnomap", $rdi)

发生这种情况时,我们知道正在查找的字典是 errnomapstore it as an attribute on the sys module .

关于python - 返回 OSError 异常类的子类实例的逻辑在哪里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39498031/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com