从Python访问errno?

我坚持一个相当复杂的Python模块,不返回有用的错误代码(它实际上无声无息地失败)。 但是,它调用的底层C库设置了errno。

通常errno进来OSError属性,但因为我没有例外,我不能得到它。

使用ctypes,libc.errno不起作用,因为errno是GNU libc中的一个macros。 Python 2.6有一些function,但Debian仍然使用Python 2.5。 在我的纯Python程序中插入一个C模块来阅读errno令我厌恶。

有没有办法访问errno? 只有Linux的解决scheme是好的,因为封装的库仅限于Linux。 我也不必担心线程,因为我只能在这个失败的时候运行一个线程。

下面的代码是不可靠的(或全面的,有errno可以定义多种方式),但它应该让你开始(或重新考虑你的位置在一个小的扩展模块(毕竟在Debian的python setup.py installeasy_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 

where 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模块,它什么都不做,但返回errno /是/恶心,但是你正在使用的库。 我会这样做,而不是自己修补python。

通过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重置为从stdin读取。

一旦你知道__errno_location这个魔术字,这看起来就像一个普通的模式。 但是只有errno我很迷茫。

我不确定这是你和Jerub指的是什么,但是你可以写一个非常简短的C扩展,只是输出errno,即用python语言界面 。

否则,我同意你不得不添加这一小部分的编译代码是一个痛苦。

ctypes实际上提供了一个标准的方法来访问python的c实现,它使用errno。 除了我的(linux)系统之外,我还没有测试过这个,但是这应该是非常便携的:

ctypes.c_int.in_dll(ctypes.pythonapi, “错误号”)

它返回一个包含当前值的c_int。