Подтвердить что ты не робот

Доступ к errno из Python?

Я застрял с довольно сложным модулем Python, который не возвращает полезные коды ошибок (он действительно терпит неудачу молча). Однако основная библиотека C, которую он вызывает, устанавливает errno.

Обычно errno входит в атрибуты OSError, но поскольку у меня нет исключения, я не могу получить его.

Используя ctypes, libc.errno не работает, потому что errno является макросом в GNU libc. Python 2.6 имеет некоторые преимущества, но Debian по-прежнему использует Python 2.5. Вставка модуля C в мою чистую программу Python просто для того, чтобы читать errno отвращает меня.

Есть ли способ доступа к errno? Решение Linux только прекрасно, поскольку обернутая библиотека - это только Linux. Мне также не нужно беспокоиться о потоках, так как я выполняю только один поток за время, в которое это может потерпеть неудачу.

4b9b3361

Ответ 1

Обновление: в Python 2.6 + используйте ctypes.get_errno().

Python 2.5

Неподвижный код не является надежным (или всеобъемлющим, существует возможность выбора путей 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 

Где 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())

Ответ 2

Вот фрагмент кода, который позволяет получить доступ к 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 как можно скорее после вызова функции, иначе она может быть перезаписана.

Ответ 3

Похоже, вы можете использовать этот патч, который предоставит вам ctypes.get_errno/set_errno

http://bugs.python.org/issue1798

Это патч, который был фактически применен к репозиторию:

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

В противном случае добавление нового модуля C, который ничего не делает, кроме возврата errno/is/disgusting, но так же, как и библиотека, которую вы используете. Я бы сделал это, предпочитая исправлять сам python.

Ответ 4

Поднял и проследил через заголовки 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 я был довольно потерян.

Ответ 5

Я не уверен, что это то, о чем вы и Jerub имеете в виду, но вы можете написать очень короткое расширение C, которое просто экспортирует errno, то есть с интерфейс языка python.

В противном случае я согласен с вами в том, что добавить этот маленький бит скомпилированного кода - это боль.

Ответ 6

ctypes фактически предоставляет стандартный способ доступа к реализации python c, который использует errno. Я не тестировал это ни на что иное, кроме моей (linux) системы, но это должно быть очень портативным:

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

который возвращает c_int, содержащий текущее значение.