Access to errno from Python?

前端 未结 6 1299
没有蜡笔的小新
没有蜡笔的小新 2020-12-29 05:00

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

相关标签:
6条回答
  • 2020-12-29 05:20

    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.

    0 讨论(0)
  • 2020-12-29 05:21

    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.

    0 讨论(0)
  • 2020-12-29 05:29

    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.

    0 讨论(0)
  • 2020-12-29 05:36

    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.

    0 讨论(0)
  • 2020-12-29 05:38

    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.

    0 讨论(0)
  • 2020-12-29 05:39

    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())
    
    0 讨论(0)
提交回复
热议问题