如何设置python异常消息使用特殊语言?

时间:2015-09-13 06:15:28

标签: python c++ exception ctypes

我想改变python处理返回错误字符串语言的方式,例如WinError / OSError异常。我正在使用ctypes,WinError被定义为

def WinError(code=None, descr=None):
    if code is None:
        code = GetLastError()
    if descr is None:
        descr = FormatError(code).strip()
    return OSError(None, descr, None, code)

FormatError函数是从.. \ Python34 \ DLLs_ctypes.pyd中提取的,是C ++ FormatMessage函数的python版本。

DWORD WINAPI FormatMessage(
  _In_     DWORD   dwFlags,
  _In_opt_ LPCVOID lpSource,
  _In_     DWORD   dwMessageId,
  _In_     DWORD   dwLanguageId,
  _Out_    LPTSTR  lpBuffer,
  _In_     DWORD   nSize,
  _In_opt_ va_list *Arguments
);

理想情况下,python等价物应具有相同的参数,但FormatError只能有一个参数,这就是FormatError([code])。 我找到了用c ++编写的ctypes的源代码。有一个名为callproc.c的文件,并且FormatError函数定义为

static TCHAR *FormatError(DWORD code)
{
    TCHAR *lpMsgBuf;
    DWORD n;
    n = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
              NULL,
              code,
              MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
              (LPTSTR) &lpMsgBuf,
              0,
              NULL);
    if (n) {
        while (isspace(lpMsgBuf[n-1]))
            --n;
        lpMsgBuf[n] = '\0'; /* rstrip() */
    }
    return lpMsgBuf;
}

LANG_NEUTRAL | SUBLANG_DEFAULT =回退到用户的默认语言。

是否可以通过设置区域设置,环境变量或其他内容来控制错误字符串的语言?

提前致谢!

编辑:我想我发现了一些有趣的东西,但我会稍后测试一下,因为我真的很困。这应该工作与否? https://gist.github.com/EBNull/6135237

1 个答案:

答案 0 :(得分:0)

在Windows Vista及更高版本中,您可以调用SetThreadPreferredUILanguages为当前线程设置最多5种语言的列表,按优先顺序排序。 Windows 7+也有SetProcessPreferredUILanguages来更改整个过程的此设置。

Windows语言包提供所需的多语言用户界面(MUI)资源库。对于系统库,您将在System32的子目录中找到按语言命名的MUI资源。例如,如果安装了西班牙语语言包,则应存在System32\es-ES\kernel32.dll.mui。有关MUI的更多信息,请阅读Understanding MUIMUI Fundamental Concepts Explained

以下是调用SetThreadPreferredUILanguages的示例ctypes定义:

import ctypes
from ctypes import wintypes

kernel32 = ctypes.WinDLL('kernel32')    

MUI_LANGUAGE_ID    = 0x004
MUI_LANGUAGE_NAME  = 0x008
MUI_RESET_FILTERS  = 0x001
MUI_CONSOLE_FILTER = 0x100
MUI_COMPLEX_SCRIPT_FILTER = 0x200

def errcheck_bool(result, func, args):
    if not result:
        raise ctypes.WinError(ctypes.get_last_error())
    return args

kernel32.SetThreadPreferredUILanguages = ctypes.WINFUNCTYPE(
    wintypes.BOOL, 
    wintypes.DWORD,
    wintypes.LPCWSTR,
    wintypes.PULONG,
    use_last_error=True
)(('SetThreadPreferredUILanguages', kernel32),
  ((1, 'flags', MUI_LANGUAGE_NAME),
   (1, 'languages', None),
   (2, 'pulNumLanguages')))

kernel32.SetThreadPreferredUILanguages.errcheck = errcheck_bool

我将pulNumLanguages参数定义为out参数(即类型2),因此ctypes负责将引用传递给临时c_ulong,然后将值作为高级Python返回结果。

C调用的低级BOOL结果由errcheck_bool处理,这可能会基于Windows LastError代码引发异常。为确保此值的准确性,本机调用配置为use_last_error=True,它捕获线程局部存储变量中的LastError值。通过调用ctypes.get_last_error来检索此捕获的错误值。

语言列表作为单个宽字符串传递,每个元素以nul字符(即'\0')终止。建议使用language names而不是旧语言ID,因此我将MUI_LANGUAGE_NAME设置为默认标记值。

示例:

>>> ERROR_FILE_NOT_FOUND = 2
>>> kernel32.SetThreadPreferredUILanguages(languages='es-es\0en-us\0')
2
>>> ctypes.FormatError(ERROR_FILE_NOT_FOUND)
'El sistema no puede encontrar el archivo especificado.'

复位:

>>> kernel32.SetThreadPreferredUILanguages(flags=0)
0
>>> ctypes.FormatError(ERROR_FILE_NOT_FOUND)
'The system cannot find the file specified.'    

不幸的是,这并没有设置C运行时错误消息使用的语言。通常,只要Python依赖于CRT的低级POSIX API,就可以在Windows上使用CRT错误消息。 _open / _wopen

>>> kernel32.SetThreadPreferredUILanguages(languages='es-es\0en-us\0')
2
>>> open('spam')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'spam'

以上ENOENT消息在CRT的_sys_errlist数组中进行了硬编码。例如,这里是Python 3.5中此数组的前10个条目(使用新的通用CRT):

ucrtbase = ctypes.CDLL('ucrtbase')
ucrtbase.__sys_nerr.restype = ctypes.POINTER(ctypes.c_int)
nerr = ucrtbase.__sys_nerr()[0]
ucrtbase.__sys_errlist.restype = ctypes.POINTER(ctypes.c_char_p * nerr)
errlist = ucrtbase.__sys_errlist()[0]

>>> print(*(m.decode() for m in errlist[:10]), sep='\n')
No error
Operation not permitted
No such file or directory
No such process
Interrupted function call
Input/output error
No such device or address
Arg list too long
Exec format error
Bad file descriptor

(实际上并没有使用ctypes。上面只是为了展示实现。在Python中使用os.strerror来获取这些CRT错误消息。)

在这种情况下,您必须通过使用自定义或机器翻译的错误消息重新引发异常来处理异常。