我坚持一个相当复杂的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 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
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。