Getting started with Lookout Python SDK

March 21, 2019 ยท View on GitHub

This is the guide on how to develop a Lookout Analyzer in Python.

Environment

  • Linux or macOS
  • 3.5 <= Python <= 3.7
  • Babelfish
  • Lookout Server

Install the SDK:

pip install lookout-style

Install and run bblfshd.

Download the lookout binary. The version must match the submodule revision.

Create the configuration config.yml:

server: 0.0.0.0:2000
db: sqlite:////tmp/lookout.sqlite
fs: /tmp

Clone a sample project to test on:

git clone https://github.com/src-d/go-git /tmp/go-git

Sample code

Create my_analyzer.py:

import logging
from typing import Any, Dict, Iterable

from bblfsh import Node

from lookout.core.analyzer import Analyzer, AnalyzerModel, ReferencePointer
from lookout.core.api.service_analyzer_pb2 import Comment
from lookout.core.api.service_data_pb2 import Change, File
from lookout.core.data_requests import DataService, \
    with_changed_uasts_and_contents, with_uasts_and_contents


class MyModel(AnalyzerModel):
    NAME = "my-model"
    VENDOR = "source{d}"

    def _load_tree(self, tree: dict) -> None:
        self.node_counts = tree["node_counts"]

    def _generate_tree(self) -> dict:
        return {"node_counts": self.node_counts}


class MyAnalyzer(Analyzer):
    model_type = MyModel
    version = 1
    name = "examples.MyAnalyzer"
    description = "Reports the changes in UAST node counts."
    _log = logging.getLogger("MyAnalyzer")

    @with_changed_uasts_and_contents(unicode=False)
    def analyze(self, ptr_from: ReferencePointer, ptr_to: ReferencePointer,
                data_service: DataService, changes: Iterable[Change]) -> [Comment]:
        self._log.info("analyze %s %s", ptr_from.commit, ptr_to.commit)
        comments = []
        for change in changes:
            comment = Comment()
            comment.file = change.head.path
            comment.text = "%s %d > %d" % (change.head.language,
                                           self.model.node_counts.get(change.base.path, 0),
                                           self.count_nodes(change.head.uast))
            comment.line = 0
            comment.confidence = 100
            comments.append(comment)
        return comments

    @classmethod
    @with_uasts_and_contents(unicode=False)
    def train(cls, ptr: ReferencePointer, config: Dict[str, Any], data_service: DataService,
              files: Iterable[File]) -> AnalyzerModel:
        cls._log.info("train %s %s", ptr.url, ptr.commit)
        model = cls.construct_model(ptr)
        model.node_counts = {}
        for file in files:
            model.node_counts[file.path] = cls.count_nodes(file.uast)
        return model

    @staticmethod
    def count_nodes(uast: Node):
        stack = [uast]
        count = 0
        while stack:
            node = stack.pop()
            count += 1
            stack.extend(node.children)
        return count


analyzer_class = MyAnalyzer

@with_changed_uasts_and_contents(unicode=False) and @with_uasts_and_contents(unicode=False) populate 'changes' and 'files' objects accordingly with UASTs and file contents. unicode argument determines whether you need Unicode content and UAST with positions in Unicode characters (True) or byte content with positions in bytes (False). We don't care in our case, hence set it to False for performance reasons. position information and we set False. Thus we keep lookout response unchanged and save computational time.

If you need only the UASTs, decorate with @with_changed_uasts and @with_uasts respectively.

Running

There are two ways to test an analyzer: local debug run with lookout-sdk tool and GitHub run.

Local debug

Launch the analyzer:

analyzer run my_analyzer -c config.yml

Train the model:

lookout push ipv4://localhost:2000 --git-dir /tmp/go-git \
    --from defd0b861ca79845c8f06f7c826c769012404bbd \
    --to 4397264e391b45a8eac147cc7373189d55c640cc

You should have /tmp/tmp/go-git/my_analyzer.MyAnalyzer_1.asdf.

Run the model:

lookout review ipv4://localhost:2000 --git-dir /tmp/go-git \
    --from 4397264e391b45a8eac147cc7373189d55c640cc \
    --to 9f00789688d26191a987fdec8bc2678362ec4453

You should see the comments in the log:

[2018-08-30T12:17:52.877425809+02:00]  INFO posting analysis app=lookout comments=1 head=HEAD provider= repository=file:///tmp/go-git
[2018-08-30T12:17:52.877452788+02:00]  INFO file comment app=lookout file=.travis.yml text=YAML 1 > 1
[2018-08-30T12:17:52.877494168+02:00]  INFO file comment app=lookout file=_examples/branch/main.go text=Go 163 > 163
[2018-08-30T12:17:52.877512614+02:00]  INFO file comment app=lookout file=blame.go text=Go 1322 > 1346
[2018-08-30T12:17:52.877529521+02:00]  INFO file comment app=lookout file=blame_test.go text=Go 987 > 1017
[2018-08-30T12:17:52.87754568+02:00]  INFO file comment app=lookout file=config/branch.go text=Go 0 > 313
[2018-08-30T12:17:52.877562436+02:00]  INFO file comment app=lookout file=config/branch_test.go text=Go 0 > 322
[2018-08-30T12:17:52.877579607+02:00]  INFO file comment app=lookout file=config/config.go text=Go 1488 > 1804
[2018-08-30T12:17:52.877610958+02:00]  INFO file comment app=lookout file=config/config_test.go text=Go 804 > 1096
[2018-08-30T12:17:52.877627674+02:00]  INFO file comment app=lookout file=config/modules.go text=Go 599 > 608
[2018-08-30T12:17:52.877647039+02:00]  INFO file comment app=lookout file=config/modules_test.go text=Go 367 > 426
[2018-08-30T12:17:52.877666745+02:00]  INFO file comment app=lookout file=config/refspec.go text=Go 679 > 686
[2018-08-30T12:17:52.877683412+02:00]  INFO file comment app=lookout file=config/refspec_test.go text=Go 717 > 865
[2018-08-30T12:17:52.877700949+02:00]  INFO file comment app=lookout file=example_test.go text=Go 416 > 444
[2018-08-30T12:17:52.877715779+02:00]  INFO file comment app=lookout file=options.go text=Go 1437 > 1509
[2018-08-30T12:17:52.87773132+02:00]  INFO global comment app=lookout text= 0 > 1
[2018-08-30T12:17:52.877746694+02:00]  INFO file comment app=lookout file=plumbing/format/gitignore/dir.go text=Go 265 > 613
[2018-08-30T12:17:52.877766641+02:00]  INFO file comment app=lookout file=plumbing/format/gitignore/dir_test.go text=Go 308 > 1560
[2018-08-30T12:17:52.877782725+02:00]  INFO file comment app=lookout file=plumbing/format/idxfile/decoder.go text=Go 725 > 724
[2018-08-30T12:17:52.877797847+02:00]  INFO file comment app=lookout file=plumbing/format/packfile/common.go text=Go 264 > 259
[2018-08-30T12:17:52.877812875+02:00]  INFO file comment app=lookout file=plumbing/format/packfile/delta_test.go text=Go 623 > 709
[2018-08-30T12:17:52.877830362+02:00]  INFO file comment app=lookout file=plumbing/format/packfile/diff_delta.go text=Go 943 > 947
[2018-08-30T12:17:52.877845029+02:00]  INFO file comment app=lookout file=plumbing/format/packfile/encoder_advanced_test.go text=Go 610 > 640
[2018-08-30T12:17:52.87786115+02:00]  INFO file comment app=lookout file=plumbing/format/packfile/index.go text=Go 420 > 746
[2018-08-30T12:17:52.877876907+02:00]  INFO file comment app=lookout file=plumbing/format/packfile/index_test.go text=Go 790 > 821
[2018-08-30T12:17:52.877893931+02:00]  INFO file comment app=lookout file=plumbing/format/packfile/scanner.go text=Go 2085 > 2093
[2018-08-30T12:17:52.877909046+02:00]  INFO file comment app=lookout file=plumbing/format/pktline/encoder.go text=Go 516 > 523
[2018-08-30T12:17:52.87792569+02:00]  INFO file comment app=lookout file=plumbing/format/pktline/scanner.go text=Go 568 > 568
[2018-08-30T12:17:52.877940972+02:00]  INFO file comment app=lookout file=plumbing/format/pktline/scanner_test.go text=Go 1221 > 1318
[2018-08-30T12:17:52.877957964+02:00]  INFO file comment app=lookout file=plumbing/object/blob.go text=Go 612 > 613
[2018-08-30T12:17:52.877972981+02:00]  INFO file comment app=lookout file=plumbing/object/change.go text=Go 641 > 734
[2018-08-30T12:17:52.877988784+02:00]  INFO file comment app=lookout file=plumbing/object/change_test.go text=Go 1961 > 2488
[2018-08-30T12:17:52.878003888+02:00]  INFO file comment app=lookout file=plumbing/object/commit.go text=Go 1899 > 1950
[2018-08-30T12:17:52.87802217+02:00]  INFO file comment app=lookout file=plumbing/object/commit_test.go text=Go 1783 > 2077
[2018-08-30T12:17:52.878037803+02:00]  INFO file comment app=lookout file=plumbing/object/commit_walker_bfs.go text=Go 0 > 419
[2018-08-30T12:17:52.878054739+02:00]  INFO file comment app=lookout file=plumbing/object/commit_walker_ctime.go text=Go 0 > 416
[2018-08-30T12:17:52.878069504+02:00]  INFO file comment app=lookout file=plumbing/object/commit_walker_test.go text=Go 616 > 1096
[2018-08-30T12:17:52.878085501+02:00]  INFO file comment app=lookout file=plumbing/object/difftree.go text=Go 109 > 170
[2018-08-30T12:17:52.878100405+02:00]  INFO file comment app=lookout file=plumbing/object/file.go text=Go 608 > 610
[2018-08-30T12:17:52.878117623+02:00]  INFO file comment app=lookout file=plumbing/object/patch.go text=Go 1378 > 1499
[2018-08-30T12:17:52.878132839+02:00]  INFO file comment app=lookout file=plumbing/object/tag.go text=Go 1599 > 1607
[2018-08-30T12:17:52.878151205+02:00]  INFO file comment app=lookout file=plumbing/object/tree.go text=Go 2085 > 2291
[2018-08-30T12:17:52.878166392+02:00]  INFO file comment app=lookout file=plumbing/object/tree_test.go text=Go 13488 > 13531
[2018-08-30T12:17:52.878182526+02:00]  INFO file comment app=lookout file=plumbing/protocol/packp/advrefs.go text=Go 553 > 922
[2018-08-30T12:17:52.878198767+02:00]  INFO file comment app=lookout file=plumbing/protocol/packp/advrefs_test.go text=Go 1356 > 1869
[2018-08-30T12:17:52.878214409+02:00]  INFO file comment app=lookout file=plumbing/storer/object.go text=Go 1259 > 1251
[2018-08-30T12:17:52.878230549+02:00]  INFO file comment app=lookout file=plumbing/transport/common.go text=Go 1161 > 1171
[2018-08-30T12:17:52.878246578+02:00]  INFO file comment app=lookout file=plumbing/transport/common_test.go text=Go 1342 > 1406
[2018-08-30T12:17:52.878262076+02:00]  INFO file comment app=lookout file=plumbing/transport/http/common.go text=Go 1106 > 1223
[2018-08-30T12:17:52.878278812+02:00]  INFO file comment app=lookout file=plumbing/transport/http/common_test.go text=Go 1030 > 1121
[2018-08-30T12:17:52.878295402+02:00]  INFO file comment app=lookout file=plumbing/transport/internal/common/common.go text=Go 1994 > 2009
[2018-08-30T12:17:52.878311716+02:00]  INFO file comment app=lookout file=plumbing/transport/internal/common/common_test.go text=Go 0 > 369
[2018-08-30T12:17:52.878327364+02:00]  INFO file comment app=lookout file=plumbing/transport/server/server.go text=Go 2138 > 2085
[2018-08-30T12:17:52.878343609+02:00]  INFO file comment app=lookout file=plumbing/transport/test/receive_pack.go text=Go 2438 > 2611
[2018-08-30T12:17:52.878358777+02:00]  INFO file comment app=lookout file=remote.go text=Go 4507 > 4739
[2018-08-30T12:17:52.878374509+02:00]  INFO file comment app=lookout file=remote_test.go text=Go 4519 > 4878
[2018-08-30T12:17:52.878390517+02:00]  INFO file comment app=lookout file=repository.go text=Go 5300 > 5962
[2018-08-30T12:17:52.878407408+02:00]  INFO file comment app=lookout file=repository_test.go text=Go 9445 > 11072
[2018-08-30T12:17:52.878422685+02:00]  INFO file comment app=lookout file=storage/filesystem/config.go text=Go 235 > 238
[2018-08-30T12:17:52.878439569+02:00]  INFO file comment app=lookout file=storage/filesystem/config_test.go text=Go 266 > 266
[2018-08-30T12:17:52.878455319+02:00]  INFO file comment app=lookout file=storage/filesystem/dotgit/dotgit.go text=Go 0 > 4094
[2018-08-30T12:17:52.878470885+02:00]  INFO file comment app=lookout file=storage/filesystem/dotgit/dotgit_rewrite_packed_refs.go text=Go 0 > 345
[2018-08-30T12:17:52.878486858+02:00]  INFO file comment app=lookout file=storage/filesystem/dotgit/dotgit_setref.go text=Go 0 > 155
[2018-08-30T12:17:52.878504272+02:00]  INFO file comment app=lookout file=storage/filesystem/dotgit/dotgit_setref_norwfs.go text=Go 0 > 185
[2018-08-30T12:17:52.878518505+02:00]  INFO file comment app=lookout file=storage/filesystem/dotgit/dotgit_test.go text=Go 0 > 4225
[2018-08-30T12:17:52.87853998+02:00]  INFO file comment app=lookout file=storage/filesystem/dotgit/writers.go text=Go 0 > 1413
[2018-08-30T12:17:52.878555832+02:00]  INFO file comment app=lookout file=storage/filesystem/dotgit/writers_test.go text=Go 0 > 884
[2018-08-30T12:17:52.878571671+02:00]  INFO file comment app=lookout file=storage/filesystem/index.go text=Go 186 > 189
[2018-08-30T12:17:52.878586346+02:00]  INFO global comment app=lookout text= 4082 > 1
[2018-08-30T12:17:52.87860415+02:00]  INFO global comment app=lookout text= 64 > 1
[2018-08-30T12:17:52.87861766+02:00]  INFO global comment app=lookout text= 117 > 1
[2018-08-30T12:17:52.878632606+02:00]  INFO global comment app=lookout text= 156 > 1
[2018-08-30T12:17:52.87864729+02:00]  INFO global comment app=lookout text= 154 > 1
[2018-08-30T12:17:52.878662021+02:00]  INFO global comment app=lookout text= 185 > 1
[2018-08-30T12:17:52.878675667+02:00]  INFO global comment app=lookout text= 4149 > 1
[2018-08-30T12:17:52.878690719+02:00]  INFO global comment app=lookout text= 1413 > 1
[2018-08-30T12:17:52.878705198+02:00]  INFO global comment app=lookout text= 884 > 1
[2018-08-30T12:17:52.878721451+02:00]  INFO file comment app=lookout file=storage/filesystem/module.go text=Go 72 > 72
[2018-08-30T12:17:52.878738932+02:00]  INFO file comment app=lookout file=storage/filesystem/object.go text=Go 2779 > 3037
[2018-08-30T12:17:52.878755606+02:00]  INFO file comment app=lookout file=storage/filesystem/object_test.go text=Go 817 > 817
[2018-08-30T12:17:52.878770143+02:00]  INFO file comment app=lookout file=storage/filesystem/reference.go text=Go 244 > 244
[2018-08-30T12:17:52.878786408+02:00]  INFO file comment app=lookout file=storage/filesystem/shallow.go text=Go 219 > 228
[2018-08-30T12:17:52.878801769+02:00]  INFO file comment app=lookout file=storage/filesystem/storage.go text=Go 207 > 207
[2018-08-30T12:17:52.878817991+02:00]  INFO file comment app=lookout file=submodule_test.go text=Go 1136 > 1292
[2018-08-30T12:17:52.878832824+02:00]  INFO file comment app=lookout file=utils/diff/diff.go text=Go 200 > 200
[2018-08-30T12:17:52.8788491+02:00]  INFO file comment app=lookout file=utils/merkletrie/difftree.go text=Go 931 > 1014
[2018-08-30T12:17:52.878869283+02:00]  INFO file comment app=lookout file=utils/merkletrie/difftree_test.go text=Go 1796 > 2197
[2018-08-30T12:17:52.87888543+02:00]  INFO file comment app=lookout file=worktree.go text=Go 3938 > 4080
[2018-08-30T12:17:52.878900631+02:00]  INFO file comment app=lookout file=worktree_bsd.go text=Go 0 > 128
[2018-08-30T12:17:52.878916457+02:00]  INFO global comment app=lookout text= 112 > 1
[2018-08-30T12:17:52.878933051+02:00]  INFO file comment app=lookout file=worktree_linux.go text=Go 112 > 128
[2018-08-30T12:17:52.878949529+02:00]  INFO file comment app=lookout file=worktree_status.go text=Go 3193 > 3215
[2018-08-30T12:17:52.878967755+02:00]  INFO file comment app=lookout file=worktree_test.go text=Go 11255 > 11711
[2018-08-30T12:17:52.878997694+02:00]  INFO file comment app=lookout file=worktree_windows.go text=Go 89 > 157
[2018-08-30T12:17:52.879017207+02:00]  INFO status: success app=lookout

GitHub

You need to generate a GitHub personal access token with repo permissions. You should also decide which GitHub repository to use for your assisted code review tests. Create a sample pull request in that repository. Then execute:

analyzer package my_analyzer -u your_user -t your_token -r your/repo -y

You should eventually see the comments to your PR on GitHub authored by your user.