从Python访问errno?

发布于 2024-07-16 06:41:19 字数 393 浏览 8 评论 0原文

我被一个相当复杂的 Python 模块困住了,它不返回有用的错误代码(它实际上默默地失败了,令人不安)。 然而,它调用的底层 C 库设置了 errno。

通常 errno 是通过 OSError 属性出现的,但由于我没有例外,所以我无法理解它。

使用 ctypes,libc.errno 不起作用,因为 errno 是 GNU libc 中的宏。 Python 2.6 有一些功能,但 Debian 仍然使用 Python 2.5。 仅仅为了读取 errno 就将 C 模块插入到我的纯 Python 程序中,这让我感到恶心。

有什么方法可以访问 errno 吗? 仅限 Linux 的解决方案很好,因为所包装的库仅限 Linux。 我也不必担心线程,因为在可能失败的时间内我只运行一个线程。

I am stuck with a fairly complex Python module that does not return useful error codes (it actually fails disturbingly silently). However, the underlying C library it calls sets errno.

Normally errno comes in over OSError attributes, but since I don't have an exception, I can't get at it.

Using ctypes, libc.errno doesn't work because errno is a macro in GNU libc. Python 2.6 has some affordances but Debian still uses Python 2.5. Inserting a C module into my pure Python program just to read errno disgusts me.

Is there some way to access errno? A Linux-only solution is fine, since the library being wrapped is Linux-only. I also don't have to worry about threads, as I'm only running one thread during the time in which this can fail.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

小霸王臭丫头 2024-07-23 06:41:19

以下是允许访问 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,否则它可能已经被覆盖。

Here is a snippet of code that allows to access 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)

The important thing is that you check errno as soon as possible after your function call, otherwise it may already be overwritten.

青朷 2024-07-23 06:41:19

更新:在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 

其中标准_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())

Update: On Python 2.6+, use ctypes.get_errno().

Python 2.5

Belowed code is not reliable (or comprehensive, there are a plefora of ways errno could be defined) but it should get you started (or reconsider your position on a tiny extension module (after all on Debian python setup.py install or easy_install should have no problem to build it)). From 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())
贩梦商人 2024-07-23 06:41:19

看起来您可以使用此补丁,它将为您提供 ctypes.get_errno/set_errno

http: //bugs.python.org/issue1798

这是实际应用于存储库的补丁:

http://svn.python.org/view?view=rev&revision=63977

否则,添加一个新的 C 模块除了返回 errno /is/ 之外什么也不做,令人厌恶,但也是如此您正在使用的库。 我会这样做而不是自己修补 python。

It looks like you can use this patch that will provide you with ctypes.get_errno/set_errno

http://bugs.python.org/issue1798

This is the patch that was actually applied to the repository:

http://svn.python.org/view?view=rev&revision=63977

Otherwise, adding a new C module that does nothing but return errno /is/ disgusting, but so is the library that you're using. I would do that in preference to patching python myself.

℉絮湮 2024-07-23 06:41:19

放弃并通过 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 我就迷失了方向。

Gave up and tracked through the C headers.

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)

It doesn't work in the python command prompt because it resets errno to read from stdin.

Once you know the magic word of __errno_location this looks like a common pattern. But with just errno I was pretty lost.

奈何桥上唱咆哮 2024-07-23 06:41:19

我不确定这是否是您和 Jerub 所指的,但您可以编写一个非常短的 C 扩展,仅导出 errno,即 Python 语言接口

否则,我同意你的观点,必须添加这一小段编译代码是一件痛苦的事情。

I'm not sure if this is what you and Jerub are referring to, but you could write a very short C extension that just exports errno, i.e. with the python language interface.

Otherwise, I agree with you that having to add this small bit of compiled code is a pain.

面犯桃花 2024-07-23 06:41:19

ctypes 实际上提供了一种访问 python 的 c 实现的标准方法,即使用 errno。 我没有在我的(linux)系统以外的任何系统上测试过这个,但这应该是非常可移植的:

ctypes.c_int.in_dll(ctypes.pythonapi,"errno")

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

ctypes actually gives a standard way to access python's c implementation, which is using errno. I haven't tested this on anything other than my (linux) system, but this should be very portable:

ctypes.c_int.in_dll(ctypes.pythonapi,"errno")

which returns a c_int containing the current value.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文