pylint、协程、装饰器和类型推断 [英] pylint, coroutines, decorators and type inferencing

查看:38
本文介绍了pylint、协程、装饰器和类型推断的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在处理一个 Google AppEngine 项目,我最近将我的 pylint 版本升级到:

I'm working on a Google AppEngine project and I recently upgraded my pylint version to:

No config file found, using default configuration
pylint 1.5.6, 
astroid 1.4.6
Python 2.7.10 (default, Oct 23 2015, 19:19:21)

这似乎破坏了某种类型推断.具体来说,GAE 的 ndb使用装饰器和生成器函数返回未来"对象,如下所示:

This seems to have broken some type inferencing. Specifically, GAE's ndb uses a decorator and a generator function to return a "Future" object like this:

@ndb.tasklet
def coroutine_like(item_id):
    # do something here...
    item = yield EntityType.get_by_id_async(item_id)
    raise ndb.Return(item)

我可以这样称呼它:

future = coroutine_like('12345')
# Do other stuff
entity = future.get_result()

以前,我对这里的 linter 没有任何问题.现在我得到:

Previously, I didn't have any problems with the linter here. Now I'm getting:

E: 42,17: Generator 'generator' has no 'get_result' member (no-member)
E: 48,17: Generator 'generator' has no 'get_result' member (no-member)
E: 60,25: Generator 'generator' has no 'get_result' member (no-member)
E: 74, 8: Generator 'generator' has no 'wait' member (no-member)
E: 88, 8: Generator 'generator' has no 'wait' member (no-member)
E: 95,17: Generator 'generator' has no 'get_result' member (no-member)

我意识到我可以单独# pylint: disable=no-member 那些行,但这会很麻烦.我还意识到我可以通过在模块级别添加抑制代码来抑制模块级别的警告,并且我可以通过修改我的 pylintrc 文件来全局抑制警告.我真的不想做这些事情.我更愿意(以某种方式)告诉 pylint 用 @ndb.tasklet 装饰器装饰的东西返回 ndb.Future 实例.我已经看到有注册类型推断助手的方法1 用于 pylint,但我不知道如何让它们与我的生成器函数装饰器一起工作.

I realize that I can # pylint: disable=no-member those lines individually but that would be cumbersome. I also realize that I can suppress that warning at the module level by adding the suppression code at the module level and I can globally suppress the warning by modifying my pylintrc file. I don't really want to do those things. I would much rather (somehow) tell pylint that things decorated with the @ndb.tasklet decorator return ndb.Future instances. I've seen that there are ways to register type-inferencing helpers1 for pylint, but I'm not sure how to make them work with my decorator of a generator function.

1请注意,这是一篇相当古老的博客文章...我认为 logilab.astng 不再使用,现在您可以使用 astroid 相反,但这并没有让我更接近我正在寻找的答案......

1Note that is a pretty old blog post... I think that logilab.astng is no longer in use and now you would use astroid instead, but that doesn't get me too much closer to the answer that I'm looking for...

推荐答案

那篇博文确实很老了,现在已经有一段时间了.

That blog post is definitely very old, things have changed for a while now.

你可以看看astroid的大脑模块是如何实现的(https://github.com/PyCQA/astroid/tree/master/astroid/brain).它们通常是 AST 转换器,应用于特定的 AST,提供修改以便 pylint 了解您的代码究竟发生了什么.

You might take a look at the way how astroid's brain modules are implemented (https://github.com/PyCQA/astroid/tree/master/astroid/brain). They usually are AST transformers, which are applied to particular ASTs, providing modifications in order for pylint to understand what exactly is happening with your code.

变换通常是一个函数,它接收一个节点并应该返回一个新节点或修改过的相同节点(但请注意,将来我们将取消对修改相同节点的支持,它们将变得不可变)

A transform is usually a function, which receives a node and is supposed to return a new node or the same node modified (be warned though that in the future, we will remove support for modifying the same node, they will become immutable)

您可以通过注册一个

astroid.MANAGER.register_transform(type_of_node, transform_function)

但通常可以为 register_transform 提供过滤器,以便它仅应用于您感兴趣的特定节点.过滤器是 register_transform 的第三个参数,它是一个接收节点并应返回一个函数布尔值,如果节点应该被转换,则为真,否则为假.您也可以将此转换作为推理提示,通过将第二个参数包装在 astroid.inference_tip(...) 中来代替正常的推理机制.这可能就是您想要的,因为您想帮助 pylint 正确推断此函数,而不是向 AST 本身添加构造.在这种特殊情况下,转换可以返回一个 ndb.Return 实例,并使用函数中的屈服点进行初始化.另外,请注意,您可以从字符串构建 AST,仅使用代码表示,如下所示:

but is usually okay to provide a filter to register_transform, so that it would be applied only to particular nodes you are interested in. The filter is the third argument of register_transform and it is a function that receives a node and should return a boolean, true if the node should be transformed, false otherwise. You can also this transform as an inference tip, that would be used instead of the normal inference mechanism, by wrapping the second argument in astroid.inference_tip(...). This is probably what you want, since you want to help pylint infer this function properly, rather than adding constructs to the AST itself. In this particular case, the transform could return an instance of ndb.Return, initialized with the yield points you have in your function. Also, note that you can build the AST from a string, with only the code representation, as in:

ast = astroid.parse('''...'''
return ast

但是如果你想要更细粒度的方法,你可以自己构建 AST(粗略的例子):

But if you want a more fine grained approach, you can build the AST yourself (crude example):

from astroid import MANAGER
module = MANAGER.ast_from_module_name('ndb')
cls = next(module.igetattr('Return'))
instance = cls.instantiate_class()
node = astroid.Return(...)
node.value = ... node
return node

另外,请注意,创建新节点会随着最新版本而改变,通过使用适当的构造方法来构建它们,而不是手动添加属性.

Also, note though that creating new nodes will change with the newest release, by using proper constructor methods for building them, instead of adding attributes manually.

希望这会有所帮助.

这篇关于pylint、协程、装饰器和类型推断的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆