问题描述
在我工作的地方,我们广泛使用 SQLAlchemy。
随着时间的推移,我们为我们的模型开发了一个适合我们需要的基类。
但是当需要对我们的代码进行 lint 时,我们总是被我们知道可以忽略的警告所淹没。
但到目前为止,我们只能通过generated-members
指令在全局范围内做到这一点,该指令往往会隐藏问题。
所以我开始想: “我怎么能教 pylint 呢?”
这是情况:
from sqlalchemy.ext.declarative import declarative_base
class CustomBaseModel(object):
def feature(self):
pass
Model = declarative_base(cls=CustomBaseModel)
class Thing(Model):
id = Column(Integer, primary_key=True)
label = Column(String(64))
t = Thing()
t.feature() # Pylint says Thing has not `feature()` method.
所以我想做的是告诉pylint Model 实际上或多或少是CustomBaseModel。
因此,它看起来更像是我应该对declarative_base()
调用的返回值使用 。
但我不确定如何进行。
看起来 API 会随着时间而改变,我不会去任何地方。
我研究的另一个策略是将在CustomBaseModel
上找到的属性复制到模型。
但它不起作用。
事实上,Pylint 模型似乎只是一个名字……它忘记了它是什么,也不知道它是一个类。
任何提示将不胜感激...
1楼
如果你替换这个:
Model = declarative_base(cls=CustomBaseModel)
像这样:
def base_decorator(cls):
return declarative_base(cls = cls)
@base_decorator
class Model(CustomBaseModel):
pass
这将导致类似于以下执行顺序的内容:
class Model(CustomBaseModel):
pass
Model = declarative_base(cls = Model)
这在功能上一样的,你在你的代码示例有直接调用,但它给pylint
一个线索, Model
源自CustomBaseModel
。
2楼
这是我敢说的与我的案例和 SQLAlchemy 最相关的答案。 仍然归功于 LeoK 为我指明了正确的方向。
如果你像这样重写代码:
from sqlalchemy import as_declarative
@as_declarative
class Model(object):
# ...
def feature():
pass
class Thing(Model):
pass
t = Thing()
t.feature() # No more complain !
这将产生与之前完全相同的Model
类,但不需要中间的CustomBaseModel
类。
由于类装饰器应返回一个类,因此 Pylint 的意图更明确。 它不再失去对类属性的跟踪。
请注意,没有什么可以阻止您将类与装饰器完全混淆。 虽然 Pylint 可以应付其中的一些,而且也不是那么容易被愚弄。 不过,我猜大多数元编程都是有障碍的。
以下是一些可以玩的示例:
def class_breaker(cls):
# Try some of those:
# E: 37, 0: Assigning to function call which only returns None (assignment-from-none)
# return None # return None too obvious
# E: 47,21: BrokenClass is not callable (not-callable)
# cls = None # Confuses Pylint a bit. Hard to reconcile the message with the issue (IMHO) but correct.
# return cls
# No warnings ! return value is a type
cls = type('Broken', (cls, ), {})
return cls
@class_breaker
class ClassToBreak(object):
def __init__(self, name):
self._name = name
@property
def name(self):
"""Retrieve the name"""
return self._name
class OtherClassToBreak(object):
def __init__(self, name):
"""Init."""
self._name = name
@property
def name(self):
"""Retrieve the name"""
return self._name
BrokenClass = class_breaker(OtherClassToBreak)
def main():
instance = ClassToBreak(name='foo')
print instance.name
other_instance = BrokenClass(name='foo')
print other_instance.name
if __name__ == '__main__':
main()