Domanda

Sono bloccato con un modulo Python abbastanza complesso che non restituisce utili codici di errore (in realtà fallisce in modo inquietante silenziosamente). Tuttavia, la libreria C sottostante che chiama imposta errno.

Normalmente errno arriva sugli attributi OSError, ma poiché non ho un'eccezione, non riesco a capirlo.

Usando ctypes, libc.errno non funziona perché errno è una macro in GNU libc. Python 2.6 ha alcuni vantaggi, ma Debian usa ancora Python 2.5. Inserire un modulo C nel mio programma Python puro solo per leggere errno mi fa schifo.

C'è un modo per accedere a errno? Una soluzione solo per Linux va bene, poiché la libreria che viene spostata è solo per Linux. Inoltre, non devo preoccuparmi dei thread, poiché sto eseguendo un solo thread durante il tempo in cui questo può fallire.

È stato utile?

Soluzione

Aggiornamento: su Python 2.6+ , usa ctypes.get_errno() .

Python 2.5

Il codice sottostante non è affidabile (o completo, ci sono molti modi in cui errno potrebbe essere definito) ma dovrebbe iniziare (o riconsiderare la tua posizione su un piccolo modulo di estensione (dopo tutto su Debian python setup.py install o easy_install non dovrebbe avere problemi a crearlo)). Da 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 

Dove 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())

Altri suggerimenti

Ecco uno snippet di codice che consente di accedere 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)

L'importante è che controlli <=> il prima possibile dopo la tua chiamata di funzione, altrimenti potrebbe già essere sovrascritto.

Sembra che tu possa usare questa patch che ti fornirà ctypes.get_errno/set_errno

http://bugs.python.org/issue1798

Questa è la patch che è stata effettivamente applicata al repository:

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

Altrimenti, aggiungendo un nuovo modulo C che non fa altro che restituire errno / is / disgusting, ma lo è anche la libreria che stai usando. Lo farei preferendo patchare Python da solo.

Lasciato e rintracciato attraverso le intestazioni 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)

Non funziona nel prompt dei comandi di Python perché reimposta errno per leggere da stdin.

Quando conosci la parola magica di __errno_location sembra un modello comune . Ma con solo errno ero piuttosto perso.

Non sono sicuro se questo e quello a cui ti riferisci tu e Jerub, ma potresti scrivere un'estensione C molto breve che esporta semplicemente errno, ovvero con l'interfaccia del linguaggio python .

Altrimenti, sono d'accordo con te sul fatto che dover aggiungere questo piccolo pezzo di codice compilato è una seccatura.

ctypes fornisce in realtà un modo standard per accedere all'implementazione c di python, che sta usando errno. Non ho provato questo su qualcosa di diverso dal mio sistema (Linux), ma questo dovrebbe essere molto portatile:

ctypes.c_int.in_dll (ctypes.pythonapi, quot &; & Errno quot;)

che restituisce un c_int contenente il valore corrente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top