English 中文(简体)
如何从Python访问errno?
原标题:
  • 时间:2009-03-19 04:00:33
  •  标签:

我卡在一个相当复杂的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 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 

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://bugs.python.org/issue1798

这是实际应用于代码库的补丁:

将此翻译成中文:http://svn.python.org/view?view=rev&revision=63977

否则,添加一个什么都不做,仅返回errno的C模块是令人厌恶的,但你使用的库也是如此。我宁愿这样做,而不是自己修补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,"errno") ctypes.c_int.in_dll(ctypes.pythonapi,"errno")

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





相关问题
热门标签