从Python访问errno?
我遇到了一个相当复杂的Python模块,该模块不返回有用的错误代码(它实际上以令人不安的方式失败)。但是,它调用的底层C库设置errno 通常情况下,errno会超过OSError属性,但因为我没有异常,所以我无法处理它 使用ctypes,libc.errno不起作用,因为errno是GNULIBC中的宏。Python2.6有一些启示,但Debian仍然使用Python2.5。在纯Python程序中插入C模块只是为了读取errno,这让我很反感从Python访问errno?,python,linux,python-2.5,errno,Python,Linux,Python 2.5,Errno,我遇到了一个相当复杂的Python模块,该模块不返回有用的错误代码(它实际上以令人不安的方式失败)。但是,它调用的底层C库设置errno 通常情况下,errno会超过OSError属性,但因为我没有异常,所以我无法处理它 使用ctypes,libc.errno不起作用,因为errno是GNULIBC中的宏。Python2.6有一些启示,但Debian仍然使用Python2.5。在纯Python程序中插入C模块只是为了读取errno,这让我很反感 有没有办法访问errno?只使用Linux的解决方
有没有办法访问errno?只使用Linux的解决方案是可以的,因为要包装的库是只使用Linux的。我也不必担心线程,因为在这可能失败的时间内,我只运行一个线程。看起来您可以使用此修补程序为您提供
ctypes。get\u errno/set\u errno
这是实际应用于存储库的修补程序:
否则,添加一个新的C模块,它只返回errno/is/厌恶,但您正在使用的库也是如此。我宁愿这样做,也不愿自己修补python。我不确定这是否是您和Jerub所指的,但您可以编写一个非常短的C扩展,只导出errno,即使用
否则,我同意您的观点,必须添加这一小段编译代码是一件痛苦的事情。放弃并通过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读取
一旦你知道了答案,这看起来就像是一个常见的模式。但是,仅仅使用它我就迷路了。更新:在Python 2.6+上,使用 Python 2.5 下面的代码不可靠(或者说不全面,有很多方法可以定义
errno
),但它应该可以让您开始(或者重新考虑您在一个小型扩展模块上的位置(毕竟在Debianpython setup.py安装
或easy_install
上构建它应该没有问题)。从
其中标准库:
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())
def get_libc_name():
如果sys.platform==“win32”:
#解析sys.version并推断编译器的版本
导入distutils.msvccompiler
version=distutils.msvccompiler.get\u build\u version()
如果版本为“无”:
#这种逻辑适用于Python的官方版本。
如果系统版本信息<(2,4):
clibname='msvcrt'
其他:
clibname='msvcr71'
其他:
如果版本这里有一段代码,允许访问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
,否则它可能已经被覆盖。ctypes实际上提供了一种访问python的c实现的标准方法,即使用errno。除了我的(linux)系统之外,我还没有在任何其他系统上测试过这一点,但这应该是非常可移植的:
ctypes.c_int.in_dll(ctypes.pythonapi,“errno”)
它返回一个包含当前值的c_int。这是一个不错的最后手段,但要求模块用户修补自己的Python副本有点苛刻。我希望有人知道一个神奇的咒语,让2.5的ctypes达到errno,即使它不是100%洁食。errcheck界面很酷,谢谢你指出。不幸的是,J.F.塞巴斯蒂安(J.F.Sebastian)也因为告诉我如何在BSD上这样做而获胜。+1:因为他明确指出,errno
可能会在你阅读之前被更改;为了实现通过errcheck
来阻止它,下面将正确设置OSError异常上的errno和strerror属性:raise OSError(e,errno.errorcode[e])我发现以下内容对于检查返回值是否为-1或NULL非常有用:如果不是ret或ctypes.cast(ret,ctypes.POINTER(ctypes.c_int))==-1:
事实上,现在适当的方法是使用ctypes.get_errno()。啊,这对我很有帮助。在浏览CTypesAPI时,我从来没有看到过这一点。如果您的目标是2.6或更高版本,ctypes.get\u 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)