pylint: Class 'message' 没有 'startswith' 成员

pylint: Class 'message' has no 'startswith' member

出于某种原因,pylint 1.6.4 (astroid 1.4.9) 不喜欢这样:

try:
    some_package.their_function()
except Exception as ex:
    if ex.message.startswith(...):
        ...

它抱怨:

error (E1101, no-member, feed_sentiment) Class 'message' has no 'startswith' member

我觉得这很奇怪,因为:

>>> type(Exception("foo").message)
<type 'str'>
>>> Exception("foo").message.startswith
<built-in method startswith of str object at 0x10520d360>

我认为这是一个bug in pylint

可是,是不是做错了什么?这里的"pythonic"方法是什么?

PS。是的,我知道正确的方法是定义我自己的异常子类,但我无法控制 some_package.

PPS。是的,我知道我可以用 pylint: disable=no-member.

注释代码

pythonic 方法是将 ex 显式转换为 str,因为这也会将消息转换为字符串:

try:
    some_package.their_function()
except Exception as ex:
    if str(ex).startswith(...):  # or "if something in str(ex)":

Exception.message 的问题是它可能不是 str:

>>> try:
...     raise ValueError(1.2)
... except Exception as ex:
...     print ex
...     print type(ex.message)
...     print repr(str(ex))  # force it to be a string
...     print hasattr(ex.message, 'startswith')
ValueError(1.2,)
<type 'float'>
'1.2'
False

使用 str 作为消息是一种很好的风格,非常可取,但这绝不是保证!

这确实是 astroid 中的一个错误 - pylint 用于构建抽象语法树和值推断的内部库。

import astroid

node = astroid.builder.parse("""
    ex = Exception()
    msg = ex.message
""")
print list(node.locals['msg'][0].infer())

此代码片段的输出是:

[<ClassDef(message) l.0 [exceptions] at 0x34aadd0>, <ClassDef(message) l.0 [exceptions] at 0x3482cb0>]

输出意味着异常实例上的 message 属性被推断为自定义 class 定义,而不是字符串实例。

感谢您提交错误!