从Python访问errno?
-
20-08-2019 - |
题
我被一个相当复杂的 Python 模块困住了,它不返回有用的错误代码(实际上它会默默地失败,令人不安)。然而,它调用的底层 C 库设置了 errno。
通常 errno 是通过 OSError 属性出现的,但由于我没有例外,所以我无法理解它。
使用 ctypes,libc.errno 不起作用,因为 errno 是 GNU libc 中的宏。Python 2.6 有一些功能,但 Debian 仍然使用 Python 2.5。仅仅为了读取 errno 就将 C 模块插入到我的纯 Python 程序中,这让我感到恶心。
有什么方法可以访问 errno 吗?仅限 Linux 的解决方案很好,因为所包装的库仅限 Linux。我也不必担心线程,因为在可能失败的时间内我只运行一个线程。
解决方案
更新:在 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())
其他提示
下面是一个代码段,它允许访问errno
:
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
,否则它可能已经被覆盖。
看起来你可以使用这个补丁,将为您提供ctypes.get_errno/set_errno
http://bugs.python.org/issue1798
这是实际应用到库中的补丁:
http://svn.python.org/view?view=rev&revision=63977
另外,添加一个新的C模块,什么都不做,只是返回错误号/是/令人作呕,但这样是你正在使用的库。我会做优先于修补蟒蛇自己。
放弃了,并通过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命令提示,因为它复位错误号从stdin读取工作。
一旦你知道 __errno_location的魔语这看起来像一个共同的模式。但只有错误号我很丢失。
我不知道如果这是你和耶路指的是什么,但你可以写一个很短的C扩展,只是出口错误号,即用的 Python语言接口。
否则,我同意你具有添加的编译代码这个小比特是疼痛。
的ctypes实际上给出了一个标准的方式来访问python的C实现,其通过使用错误号。我还没有比我(Linux)的系统以外的任何其他测试这一点,但是这应该是非常便携:
ctypes.c_int.in_dll(ctypes.pythonapi, “错误号”)
返回包含电流值的c_int的。