¿Acceso a errno desde Python?


Estoy atascado con un módulo Python bastante complejo que no devuelve códigos de error útiles (en realidad falla inquietantemente en silencio). Sin embargo, la biblioteca C subyacente a la que llama establece errno.

Normalmente errno viene por encima de los atributos OSError, pero como no tengo una excepción, no puedo llegar a ella.

Usando ctypes, libc.errno no funciona porque errno es una macro en GNU libc. Python 2.6 tiene algunas ventajas, pero Debian todavía usa Python 2.5. Insertar un módulo C en mi pure Programa Python solo para leer errno me disgusta.

¿Hay alguna forma de acceder a errno? Una solución solo para Linux está bien, ya que la biblioteca que se está empaquetando es solo para Linux. Tampoco tengo que preocuparme por los hilos, ya que solo estoy ejecutando un hilo durante el tiempo en el que esto puede fallar.

Author: Adam Smith, 2009-03-19

6 answers

Actualización: En Python 2.6 + , usar ctypes.get_errno().

Python 2.5

El código abajo no es confiable (o completo, hay una plefora de formas errno que se podrían definir), pero debería ayudarlo a comenzar (o reconsiderar su posición sobre un pequeño módulo de extensión (después de todo en Debian python setup.py install o easy_install no debería tener ningún problema para compilarlo)). De 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 

Donde 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())
 14
Author: jfs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-03 06:57:26

Aquí hay un fragmento de código que permite acceder a 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)

Lo importante es que compruebe errno tan pronto como sea posible después de su llamada a la función, de lo contrario ya puede ser sobrescrito.

 17
Author: theller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-19 07:20:16

Parece que puede usar este parche que le proporcionará ctypes.get_errno/set_errno

Http://bugs.python.org/issue1798

Este es el parche que realmente se aplicó al repositorio:

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

De lo contrario, agregar un nuevo módulo C que no hace más que devolver errno /es/ desagradable, pero también lo es la biblioteca que está utilizando. Lo haría en lugar de parchear Python yo mismo.

 9
Author: Jerub,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-19 04:10:16

Se rindió y rastreó a través de las cabeceras 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)

No funciona en el símbolo del sistema de python porque restablece errno para leer desde stdin.

Una vez que conoces la palabra mágica de __errno_ubicación esto parece un patrón común. Pero con just errno estaba bastante perdido.

 9
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-19 07:18:23

No estoy seguro de si esto es a lo que tú y Jerub se refieren, pero podrías escribir una extensión C muy corta que solo exporte errno, es decir, con la interfaz del lenguaje python.

De lo contrario, estoy de acuerdo con usted en que tener que agregar este pequeño trozo de código compilado es un dolor.

 1
Author: Dana the Sane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-19 05:30:18

Ctypes en realidad da una forma estándar de acceder a la implementación c de python, que está usando errno. No he probado esto en nada más que mi sistema (linux), pero esto debería ser muy portátil:

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

Que devuelve un c_int que contiene el valor actual.

 0
Author: Lee McCuller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-29 21:47:21