25

私は、有用なエラー コードを返さないかなり複雑な Python モジュールに悩まされています (実際には、不穏なほど静かに失敗します)。ただし、それが呼び出す基になる C ライブラリは errno を設定します。

通常、errno は OSError 属性を超えて入ってきますが、例外がないため、取得できません。

ctypes を使用すると、errno は GNU libc のマクロであるため、libc.errno は機能しません。Python 2.6 にはいくつかのアフォーダンスがありますが、Debian はまだ Python 2.5 を使用しています。errno を読み取るためだけに純粋な Python プログラムに C モジュールを挿入すると、うんざりします。

errno にアクセスする方法はありますか? ラップされるライブラリは Linux 専用であるため、Linux 専用のソリューションで問題ありません。スレッドについても心配する必要はありません。スレッドが失敗する可能性がある間は 1 つのスレッドしか実行していないからです。

4

6 に答える 6

18

へのアクセスを許可するコードのスニペットを次に示します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重要なことは、関数呼び出し後できるだけ早くチェックすることです。そうしないと、既に上書きされている可能性があります。

于 2009-03-19T07:20:16.067 に答える
14

更新: Python 2.6+では、 を使用しますctypes.get_errno()

パイソン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())
于 2009-03-19T07:19:50.483 に答える
9

あきらめて、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)

errno を stdin から読み取るようにリセットするため、python コマンド プロンプトでは機能しません。

__errno_locationの魔法の言葉がわかれば、これはよくあるパターンのように見えます。しかし、errnoだけでかなり迷ってしまいました。

于 2009-03-19T07:18:23.450 に答える
9

提供するこのパッチを使用できるようですctypes.get_errno/set_errno

http://bugs.python.org/issue1798

これは、実際にリポジトリに適用されたパッチです。

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

それ以外の場合、errno を返すだけの新しい C モジュールを追加するのは /is/ 嫌ですが、使用しているライブラリも同様です。自分でpythonにパッチを当てるよりも、そうするでしょう。

于 2009-03-19T04:10:16.990 に答える
1

ctypesは、実際には、errnoを使用しているpythonのc実装にアクセスするための標準的な方法を提供します。私はこれを私の(Linux)システム以外でテストしていませんが、これは非常に移植性が高いはずです:

ctypes.c_int.in_dll(ctypes.pythonapi、 "errno")

これは、現在の値を含むc_intを返します。

于 2011-05-29T21:47:21.410 に答える
1

これがあなたとJerubが参照しているものであるかどうかはわかりませんが、errnoをエクスポートするだけの非常に短いC拡張機能を作成できます。つまり、Python言語インターフェイスを使用します。

そうでなければ、コンパイルされたコードのこの小さなビットを追加しなければならないのは苦痛であることに同意します。

于 2009-03-19T05:30:18.640 に答える