Доступ к errno из Python? - PullRequest
22 голосов
/ 19 марта 2009

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

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

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

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

Ответы [ 6 ]

17 голосов
/ 19 марта 2009

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

14 голосов
/ 19 марта 2009

Обновление: Вкл. 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())
9 голосов
/ 19 марта 2009

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

Как только вы узнаете волшебное слово __errno_location , это выглядит как обычный шаблон. Но только с errno я был довольно потерян.

9 голосов
/ 19 марта 2009

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

http://bugs.python.org/issue1798

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

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

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

1 голос
/ 19 марта 2009

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

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

0 голосов
/ 30 мая 2011

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

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

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

...