2016-06-28 23 views
2

私は、Google AppEngineのプロジェクトに取り組んでいると私は最近、私のpylintのバージョンをアップグレードする:pylint、コルーチン、デコレータと型推論

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 uses a decorator and a generator function to return a "Future" objectこのような:

@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() 

、私はここリンターに問題を持っていませんでした。今私は取得しています:

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ファイルを変更して警告をグローバルに抑制することができます。私は本当にそれらのことをしたくありません。私は幾分(何らかの形で)@ndb.taskletデコレータリターンndb.Futureインスタンスで装飾されたものがpylintに伝えます。私は、ways to register type-inferencing helpers がpylintであることを見てきましたが、ジェネレータ関数のデコレータでそれらを動作させる方法がわかりません。かなり古いブログ記事です

注...私はlogilab.astngが使用されなくなったと思いませんし、今は代わりにastroidを使用しますが、それは私にかなり近いすぎを得ることはありません私が探していると答えて...

答えて

1

ブログの投稿は間違いなく非常に古く、物事はしばらく変わりました。

アストロイドの脳のモジュールがどのように実装されているかを見てみましょう(https://github.com/PyCQA/astroid/tree/master/astroid/brain)。それらは通常、特定のASTに適用されるASTトランスフォーマーであり、パイリントがコードで正確に何が起こっているのかを理解するための変更を行います。

通常、トランスフォームはノードを受け取り、新しいノードまたは同じノードを返すことになっている関数です(将来は同じノードを変更するサポートが削除されますが、不変)

あなたは

astroid.MANAGER.register_transform(type_of_node, transform_function) 

介して1を登録するが、それはあなただけに興味がある特定のノードに適用されるように、register_transformするフィルタを提供するために、通常は大丈夫ですができます。フィルターは三番目の引数でありますregister_transformの関数であり、ノードを受け取ってブール値を返す関数であり、th eノードを変換する必要があります。この変換は、推論のヒントとして使用することもできます。これは、通常の推論メカニズムの代わりに、第2引数をastroid.inference_tip(...)にラップすることによって使用されます。これはおそらくあなたが望むものです。なぜなら、AST自体に構造体を追加するのではなく、この関数を適切に推論することを助けるためです。 この特定のケースでは、トランスフォームは関数内にある降伏点で初期化されたndb.Returnのインスタンスを返すことができます。また、あなたがのように、唯一のコード表現で、文字列からASTを構築することができることに注意してください。

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

しかし、あなたはよりきめの細かいアプローチをしたい場合は、AST自分(粗製の一例)を構築することができます:

また、新しいノードの作成は、手動で属性を追加するのではなく、新しいノードを構築するための適切なコンストラクタメソッドを使用して変更されます。

これが役に立ちます。

上記PCManticoreからのアドバイス、私は一緒にこれをハックすることができましたと
+0

この回答ありがとうございます。私はまだここで私が持っている余分な瞬間にそれを消化しています。どこかのサンプルのリポジトリがありますか(おそらく、pylintのコードベースにありますか?)私はこれらのことがどのように行われているか見てみることができますか? – mgilson

+0

残念なことに、いくつかの構造化されたサンプルを見つける最良の場所は、まだアストロイドの脳(https://github.com/PyCQA/astroid/tree/master/astroid/brain)であり、一般的にアストロイドのコードベースを抜けています。 #pylint-dev(freenode) – PCManticore

+0

Awesomeでも同様の結果が得られるかもしれません。これらの例をありがとう。私は一緒に働くことができるものをハックすることができました(以下の別の答えとして掲示)。もしあなたがそこで怪しいと思われるものを見たら、私に知らせてください。すべてが合理的に見える場合、私は 'ndb'と' pylint'をもっとうまく使い、OSSコミュニティの残りの部分に別のレポを作成しようとしているかもしれません。 – mgilson

0

:、これは理想的である場合、私は知らないか、このアプローチにはすべての主要な欠点がある場合は、しかし

"""Brains for helping silence pylint errors/warnings from ndb.""" 
import astroid 


def _is_tasklet(node): 
    """Check whether a FunctionDef node is decorated with ndb.tasklet.""" 
    if not node.decorators: 
     return False 
    return 'google.appengine.ext.ndb.tasklets.tasklet' in node.decoratornames() 

@astroid.inference_tip 
def _infer_tasklet(node, context=None): # pylint: disable=unused-argument 
    """Infer the type of tasklets.""" 

    # Does the name of the function matter? Should it be global? 
    module = astroid.parse(""" 
    import google.appengine.ext.ndb.tasklets 
    def tasklet_function(*args, **kwargs): 
     return google.appengine.ext.ndb.tasklets.Future() 
    """) 
    tasklet_function = next(
     module.igetattr('tasklet_function', context=context)) 
    return iter([tasklet_function]) 


astroid.MANAGER.register_transform(
    astroid.FunctionDef, 
    _infer_tasklet, 
    _is_tasklet) 

def register(linter): # pylint: disable=unused-argument 
    """Register the plugin with the linter.""" 

パスが適切に設定されていることを前提とします。上記のスクリプトは、/path/to/pylint_helpers/ndb_brain.pyで(現在)であると私は/usr/local/google_appengineにインストールdev_appserver.py)と、以下のpylint-configファイルがあります。

[MASTER] 
init-hook='import sys; sys.path[0:0] = ("/usr/local/google_appengine", "/path/to/pylint_helpers"); import dev_appserver; dev_appserver.fix_sys_path()' 
load-plugins=ndb_brain 

タスクレット警告を黙らているようだが(woohoo)!。基本的な考え方は、ndb.taskletで装飾されているすべての関数に明示的な推論ヒントを追加することです。推論のヒントは、基本的には、pylintに、ジェネレータ関数として動作するのではなく、ndb.Futureを返すように伝えます。 I これは、(ノードを変換することによって)ASTの書き換えではなく、推論のヒントであるため、pylintに関する限り、他の有害な影響はないはずであると考えています。

+0

これはかなり素晴らしいです!コンテキストをigetattrの呼び出し '' igetattr( 'tasklet_function'、context = context) ''に渡すことを忘れないでください。コンテキストは、推論された推論パスを用いてローカルコンテクストを齲蝕するので、ほとんどの場合、推論を改善することができる。私は、なぜ、init-hookの内部でndb_brainを渡さなければならないのか分かりません。通常、これは '' pylint --load-plugins = pylint_plugin''で行います。 'plugin'は、グローバルスコープhttps://github.com/PyCQA/pylint/blob/master/pylint/extensions/bad_builtin.py#L61でこの機能を持つ必要がありますが、あなたの場合は空にすることができます。 – PCManticore

+0

@PCManticore - ここに助けてくれてありがとう。私は更新しました。 – mgilson