从 Python 访问 errno?
Posted
技术标签:
【中文标题】从 Python 访问 errno?【英文标题】:Access to errno from Python? 【发布时间】:2010-10-14 05:53:15 【问题描述】:我被一个相当复杂的 Python 模块困住了,它不返回有用的错误代码(它实际上默默地失败了,令人不安)。但是,它调用的底层 C 库设置了 errno。
通常 errno 是通过 OSError 属性来的,但是由于我没有异常,所以我无法处理它。
使用 ctypes,libc.errno 不起作用,因为 errno 是 GNU libc 中的宏。 Python 2.6 有一些功能,但 Debian 仍然使用 Python 2.5。将 C 模块插入到我的纯 Python 程序中只是为了读取 errno 让我感到厌恶。
有没有办法访问errno?仅限 Linux 的解决方案很好,因为被包装的库是仅限 Linux 的。我也不必担心线程,因为在这可能失败的时间内我只运行一个线程。
【问题讨论】:
请注意,这个问题专门针对 Python 2.5。 如果您的目标是 2.6 或更高版本,ctypes.get_errno
可能就是您想要的。
【参考方案1】:
这是一段允许访问errno
的sn-p代码:
from ctypes import *
libc = CDLL("libc.so.6")
get_errno_loc = libc.__errno_location
get_errno_loc.restype = POINTER(c_int)
def errcheck(ret, func, args):
if ret == -1:
e = get_errno_loc()[0]
raise OSError(e)
return ret
copen = libc.open
copen.errcheck = errcheck
print copen("nosuchfile", 0)
重要的是你在函数调用后尽快检查errno
,否则它可能已经被覆盖了。
【讨论】:
errcheck 界面很酷,感谢指出。不幸的是,J.F. Sebastian 也因为告诉我如何在 BSD 上做到这一点而获胜。 +1: 明确指出errno
可能在您阅读之前被更改;并通过errcheck
实施以试图阻止它。
下面会正确设置OSError异常的errno和strerror属性:raise OSError(e, errno.errorcode[e])
我发现以下对于检查 -1 或 NULL 的返回值很有用:if not ret or ctypes.cast(ret,ctypes.POINTER(ctypes.c_int)) == -1:
【参考方案2】:
更新:在 Python 2.6+ 上,使用ctypes.get_errno()
。
Python 2.5
下面的代码不可靠(或不全面,有很多方法可以定义errno
)但它应该让你开始(或者重新考虑你在一个微小的扩展模块上的位置(毕竟在 Debian 上python setup.py install
或easy_install
构建它应该没有问题))。来自http://codespeak.net/pypy/dist/pypy/rpython/lltypesystem/ll2ctypes.py
if not hasattr(ctypes, 'get_errno'):
# Python 2.5 or older
if sys.platform == 'win32':
standard_c_lib._errno.restype = ctypes.POINTER(ctypes.c_int)
def _where_is_errno():
return standard_c_lib._errno()
elif sys.platform in ('linux2', 'freebsd6'):
standard_c_lib.__errno_location.restype = ctypes.POINTER(ctypes.c_int)
def _where_is_errno():
return standard_c_lib.__errno_location()
elif sys.platform in ('darwin', 'freebsd7'):
standard_c_lib.__error.restype = ctypes.POINTER(ctypes.c_int)
def _where_is_errno():
return standard_c_lib.__error()
ctypes.get_errno = lambda: _where_is_errno().contents.value
在哪里standard_c_lib
:
def get_libc_name():
if sys.platform == 'win32':
# Parses sys.version and deduces the version of the compiler
import distutils.msvccompiler
version = distutils.msvccompiler.get_build_version()
if version is None:
# This logic works with official builds of Python.
if sys.version_info < (2, 4):
clibname = 'msvcrt'
else:
clibname = 'msvcr71'
else:
if version <= 6:
clibname = 'msvcrt'
else:
clibname = 'msvcr%d' % (version * 10)
# If python was built with in debug mode
import imp
if imp.get_suffixes()[0][0] == '_d.pyd':
clibname += 'd'
return clibname+'.dll'
else:
return ctypes.util.find_library('c')
# Make sure the name is determined during import, not at runtime
libc_name = get_libc_name()
standard_c_lib = ctypes.cdll.LoadLibrary(get_libc_name())
【讨论】:
【参考方案3】:看起来你可以使用这个补丁来为你提供ctypes.get_errno/set_errno
http://bugs.python.org/issue1798
这是实际应用到存储库的补丁:
http://svn.python.org/view?view=rev&revision=63977
否则,添加一个新的 C 模块,它只会返回 errno /is/ 恶心,但您正在使用的库也是如此。我会这样做,而不是自己修补 python。
【讨论】:
这是一个不错的最后手段,但是要求我的模块的用户修补他们自己的 Python 副本有点苛刻。我希望那里有人知道一个魔法咒语,可以让 2.5 的 ctypes 获得 errno,即使它不是 100% kosher。【参考方案4】:放弃并通过 C 标头进行跟踪。
import ctypes
c = ctypes.CDLL("libc.so.6")
c.__errno_location.restype = ctypes.POINTER(ctypes.c_int)
c.write(5000, "foo", 4)
print c.__errno_location().contents # -> c_long(9)
它在 python 命令提示符下不起作用,因为它会将 errno 重置为从标准输入读取。
一旦您知道magic word of __errno_location,这看起来就是一种常见的模式。但是只有errno 我很迷茫。
【讨论】:
【参考方案5】:我不确定这是否是您和 Jerub 所指的,但您可以编写一个非常短的 C 扩展,只导出 errno,即使用 the python language interface。
否则,我同意你的观点,不得不添加这一小段编译代码是一件很痛苦的事情。
【讨论】:
【参考方案6】:ctypes 实际上提供了一种访问 python 的 c 实现的标准方法,它使用 errno。除了我的 (linux) 系统之外,我还没有在任何其他系统上测试过这个,但这应该是非常便携的:
ctypes.c_int.in_dll(ctypes.pythonapi,"errno")
返回一个包含当前值的 c_int。
【讨论】:
其实合适的方式现在是ctypes.get_errno()。 啊,这对我很有帮助。我在使用 ctypes API 时从未见过。以上是关于从 Python 访问 errno?的主要内容,如果未能解决你的问题,请参考以下文章
socket.error: [Errno 10013] 试图以访问权限禁止的方式访问套接字
socket.error:[Errno 10013]尝试以其访问权限禁止的方式访问套接字
我在尝试从我的 Flutter 应用程序访问 AWS EC2 服务器时收到 [SocketException: OS Error: Connection denied, errno = 111]