Skip to content
This repository was archived by the owner on Mar 8, 2020. It is now read-only.

Commit 375fd7e

Browse files
authored
Merge pull request #128 from dennwc/v3
Python client v3 (UASTv2)
2 parents 3daf5c4 + 9b094aa commit 375fd7e

17 files changed

Lines changed: 1970 additions & 844 deletions

.travis.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
11
language: python
22
sudo: true
33
dist: xenial
4+
env:
5+
- BBLFSHD_VERSION=v2.9.1 BBLFSH_PYTHON_VERSION=v2.3.0
46
services:
57
- docker
68
cache:
79
directories:
810
- $HOME/.cache/pip
911
python:
10-
- "3.5"
1112
- "3.6"
1213
- "3.7"
1314
install:
15+
- docker run --privileged -d -p 9432:9432 --name bblfshd bblfsh/bblfshd:$BBLFSHD_VERSION
16+
- docker exec bblfshd bblfshctl driver install bblfsh/python-driver:$BBLFSH_PYTHON_VERSION
1417
- wget https://github.com/bblfsh/client-python/releases/download/v2.2.1/protobuf-python_3.4.1-1_amd64.deb
1518
- sudo dpkg -i protobuf-python_3.4.1-1_amd64.deb
1619
- pip3 install --upgrade pip
1720
- pip3 install -r requirements.txt
1821
- python3 setup.py --getdeps --log
1922
- pip3 install . --upgrade
23+
- cd bblfsh && python3 -m unittest discover
2024
- if [[ -z "$TRAVIS_TAG" ]]; then exit 0; fi
2125
- if [[ $TRAVIS_PYTHON_VERSION != '3.6' ]]; then exit 0; fi # disable double uploads to pypi
2226
- echo "[distutils]" > .pypirc
@@ -28,6 +32,5 @@ install:
2832
- HOME=. python setup.py sdist upload
2933
script:
3034
- python3 setup.py build_ext -i
31-
- python3 -m unittest discover .
3235
notifications:
3336
email: false

MANIFEST.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ include Makefile
77
include github.com/gogo/protobuf/gogoproto/gogo.proto
88
include gopkg.in/bblfsh/sdk.v1/protocol/generated.proto
99
include gopkg.in/bblfsh/sdk.v1/uast/generated.proto
10-
include bblfsh/memtracker.h
10+
include bblfsh/libuast/libuast.hpp
1111
prune bblfsh/libuast

README.md

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,18 @@ pip install bblfsh
1818
```bash
1919
git clone https://github.com/bblfsh/client-python.git
2020
cd client-python
21+
pip install -r requirements.txt
22+
python setup.py --getdeps
2123
python setup.py install
24+
# or: pip install .
2225
```
2326

2427
### Dependencies
2528

26-
You need to install `libxml2` and its header files. You also will need a `curl` cli tool to dowload `libuast`, and a `g++` for building [libtuast Python bindings](https://github.com/bblfsh/client-python/blob/0037d762563ab49b3daac8a7577f7103a5628fc6/setup.py#L17).
29+
You also will need a `curl` cli tool to dowload `libuast`, and a `g++` for building [libtuast Python bindings](https://github.com/bblfsh/client-python/blob/0037d762563ab49b3daac8a7577f7103a5628fc6/setup.py#L17).
2730
The command for Debian and derived distributions would be:
2831

2932
```bash
30-
sudo apt install libxml2-dev
3133
sudo apt install curl
3234
sudo apt install build-essential
3335
```
@@ -49,21 +51,49 @@ Please, read the [getting started](https://doc.bblf.sh/using-babelfish/getting-s
4951
import bblfsh
5052

5153
client = bblfsh.BblfshClient("0.0.0.0:9432")
52-
uast = client.parse("/path/to/file.py").uast
53-
print(uast)
54-
# "filter' allows you to use XPath queries to filter on result nodes:
55-
print(bblfsh.filter(uast, "//Import[@roleImport and @roleDeclaration]//alias"))
56-
57-
# filter\_[bool|string|number] must be used when using XPath functions returning
58-
# these types:
59-
print(bblfsh.filter_bool(uast, "boolean(//*[@strtOffset or @endOffset])"))
60-
print(bblfsh.filter_string(uast, "name(//*[1])"))
61-
print(bblfsh.filter_number(uast, "count(//*)"))
54+
ctx = client.parse("/path/to/file.py")
55+
print(ctx)
56+
# or to get the results in a dictionary:
57+
resdict = ctx.get_all()
6258

63-
# You can also iterate on several tree iteration orders:
64-
it = bblfsh.iterator(uast, bblfsh.TreeOrder.PRE_ORDER)
59+
# "filter' allows you to use XPath queries to filter on result nodes:
60+
it = ctx.filter("//python:Call")
6561
for node in it:
66-
print(node.internal_type)
62+
print(node)
63+
# or:
64+
doSomething(node.get())
65+
66+
# filter must be used when using XPath functions returning these types:
67+
# XPath queries can return different types (dicts, int, float, bool or str),
68+
# calling get() with an item will return the right type, but if you must ensure
69+
# that you are getting the expected type (to avoid errors in the queries) there
70+
# are alterative typed versions:
71+
x = next(ctx.filter("boolean(//*[@strtOffset or @endOffset])").get_bool()
72+
y = next(ctx.filter("name(//*[1])")).get_str()
73+
z = next(ctx.filter("count(//*)").get_int() # or get_float()
74+
75+
# You can also iterate using iteration orders different than the
76+
# default preorder using the `iterate` method on `parse` result or node objects:
77+
78+
# Directly over parse results
79+
it = client.parse("/path/to/file.py").iterate(bblfsh.TreeOrder.POST_ORDER)
80+
for i in it: ...
81+
82+
# Over filter results (which by default are already iterators with PRE_ORDER):
83+
ctx = client.parse("file.py")
84+
newiter = ctx.filter("//python:Call").iterate(bblfsh.TreeOrder.LEVEL_ORDER)
85+
for i in newiter: ...
86+
87+
# Over individual node objects to change the iteration order of
88+
# a specific subtree:
89+
ctx = client.parse("file.py")
90+
first_node = next(ctx)
91+
newiter = first_node.iterate(bblfsh.TreeOrder.POSITION_ORDER)
92+
for i in newiter: ...
93+
94+
# You can also get the non semantic UAST or native AST:
95+
ctx = client.parse("file.py", mode=bblfsh.ModeDict["NATIVE"])
96+
# Possible values for ModeDict: DEFAULT_MODE, NATIVE, PREPROCESSED, ANNOTATED, SEMANTIC
6797
```
6898

6999
Please read the [Babelfish clients](https://doc.bblf.sh/using-babelfish/clients.html)

bblfsh/__init__.py

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,26 @@
11
from bblfsh.client import BblfshClient
2-
from bblfsh.pyuast import filter, filter_bool, filter_number, filter_string, iterator
2+
from bblfsh.pyuast import decode, iterator, uast
3+
from bblfsh.tree_order import TreeOrder
34
from bblfsh.aliases import *
45

5-
class TreeOrder:
6-
PRE_ORDER = 0
7-
POST_ORDER = 1
8-
LEVEL_ORDER = 2
9-
POSITION_ORDER = 3
10-
11-
# "in" is a reserved keyword in Python thus can't be used as package name, so
12-
# we import by string
136

147
class RoleSearchException(Exception):
158
pass
169

1710

18-
def role_id(role_name: str) -> int:
11+
def role_id(rname: str) -> int:
1912
try:
20-
name = DESCRIPTOR.enum_types_by_name["Role"].values_by_name[role_name].number
13+
name = DESCRIPTOR.enum_types_by_name["Role"].values_by_name[rname].number
2114
except KeyError:
22-
raise RoleSearchException("Role with name '{}' not found".format(role_name))
15+
raise RoleSearchException("Role with name '{}' not found".format(rname))
2316

2417
return name
2518

2619

27-
def role_name(role_id: int) -> str:
20+
def role_name(rid: int) -> str:
2821
try:
29-
id_ = DESCRIPTOR.enum_types_by_name["Role"].values_by_number[role_id].name
22+
id_ = DESCRIPTOR.enum_types_by_name["Role"].values_by_number[rid].name
3023
except KeyError:
31-
raise RoleSearchException("Role with ID '{}' not found".format(role_id))
24+
raise RoleSearchException("Role with ID '{}' not found".format(rid))
3225

3326
return id_

bblfsh/__main__.py

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,62 @@
11
import argparse
2+
import pprint
23
import sys
34

4-
import bblfsh
5-
from bblfsh.pyuast import filter
6-
75
from bblfsh.client import BblfshClient
86
from bblfsh.launcher import ensure_bblfsh_is_running
97

108

11-
def setup():
9+
def setup() -> argparse.Namespace:
1210
parser = argparse.ArgumentParser(
1311
description="Query for a UAST to Babelfish and dump it to stdout."
1412
)
1513
parser.add_argument("-e", "--endpoint", default="0.0.0.0:9432",
16-
help="bblfsh gRPC endpoint.")
14+
help="bblfsh gRPC endpoint.", type=str)
1715
parser.add_argument("-f", "--file", required=True,
18-
help="File to parse.")
16+
help="File to parse.", type=str)
1917
parser.add_argument("-l", "--language", default=None,
20-
help="File's language. The default is to autodetect.")
18+
help="File's language. The default is to autodetect.", type=str)
2119
parser.add_argument("--disable-bblfsh-autorun", action="store_true",
2220
help="Do not automatically launch Babelfish server "
2321
"if it is not running.")
2422

25-
parser.add_argument("-q", "--query", default="", help="xpath query")
26-
parser.add_argument("-m", "--mapn", default="", help="transform function of the results (n)")
27-
parser.add_argument("-a", "--array", help='print results as an array', action='store_true')
23+
parser.add_argument("-q", "--query", default="", help="xpath query", type=str)
24+
parser.add_argument("-a", "--array", help='print results as a parseable Python array', action='store_true')
2825

29-
args = parser.parse_args()
30-
return args
26+
return parser.parse_args()
3127

32-
def run_query(root: bblfsh.Node, query: str, mapn: str, as_array: bool) -> None:
33-
result = list(filter(root, query))
3428

35-
if not result:
29+
def run_query(uast, query: str, array: bool) -> None:
30+
result_iter = uast.filter(query)
31+
if not result_iter:
3632
print("Nothing found")
3733

38-
else:
39-
if mapn:
40-
result = [eval(mapn) for n in result]
34+
result_list = [x.load() for x in result_iter]
4135

42-
if as_array:
43-
print("results[{}] = {}".format(len(result), result))
44-
else:
45-
print("Running xpath query: {}".format(query))
46-
print("FOUND {} roots".format(len(result)))
36+
if array:
37+
pprint.pprint(result_list)
38+
else:
39+
print("%d Results:" % len(result_list))
40+
for i, node in enumerate(result_list):
41+
print("== {} ==================================".format(i+1))
42+
print(node)
4743

48-
for i, node in enumerate(result):
49-
print("== {} ==================================".format(i+1))
50-
print(node)
5144

52-
def main():
45+
def main() -> int:
5346
args = setup()
5447
if not args.disable_bblfsh_autorun:
5548
ensure_bblfsh_is_running()
5649

5750
client = BblfshClient(args.endpoint)
58-
response = client.parse(args.file, args.language)
59-
root = response.uast
60-
if len(response.errors):
61-
sys.stderr.write("\n".join(response.errors) + "\n")
62-
query = args.query
63-
if query:
64-
run_query(root, query, args.mapn, args.array)
51+
ctx = client.parse(args.file, args.language)
52+
53+
if args.query:
54+
run_query(ctx, args.query, array=args.array)
6555
else:
66-
print(root)
56+
pprint.pprint(ctx.load())
57+
58+
return 0
59+
6760

6861
if __name__ == "__main__":
6962
sys.exit(main())

bblfsh/aliases.py

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,39 @@
1-
__all__ = ["DESCRIPTOR", "Node", "Position", "ParseResponse", "NativeParseResponse",
2-
"ParseRequest", "NativeParseRequest", "VersionRequest", "ProtocolServiceStub"]
3-
41
import importlib
5-
6-
from bblfsh.sdkversion import VERSION
2+
import google
73

84
# "in" is a reserved keyword in Python thus can't be used as package name, so
95
# we import by string
10-
uast_module = importlib.import_module(
11-
"bblfsh.gopkg.in.bblfsh.sdk.%s.uast.generated_pb2" % VERSION)
12-
protocol_module = importlib.import_module(
13-
"bblfsh.gopkg.in.bblfsh.sdk.%s.protocol.generated_pb2" % VERSION)
14-
protocol_grpc_module = importlib.import_module(
15-
"bblfsh.gopkg.in.bblfsh.sdk.%s.protocol.generated_pb2_grpc" % VERSION)
6+
uast_v2_module = importlib.import_module(
7+
"bblfsh.gopkg.in.bblfsh.sdk.v2.uast.generated_pb2")
8+
protocol_v2_module = importlib.import_module(
9+
"bblfsh.gopkg.in.bblfsh.sdk.v2.protocol.generated_pb2")
10+
protocol_grpc_v2_module = importlib.import_module(
11+
"bblfsh.gopkg.in.bblfsh.sdk.v2.protocol.generated_pb2_grpc")
12+
protocol_v1_module = importlib.import_module(
13+
"bblfsh.gopkg.in.bblfsh.sdk.v1.protocol.generated_pb2")
14+
protocol_grpc_v1_module = importlib.import_module(
15+
"bblfsh.gopkg.in.bblfsh.sdk.v1.protocol.generated_pb2_grpc")
16+
17+
DESCRIPTOR = uast_v2_module.DESCRIPTOR
18+
ParseRequest = protocol_v2_module.ParseRequest
19+
ParseResponse = protocol_v2_module.ParseResponse
20+
ParseError = protocol_v2_module.ParseError
21+
Mode = protocol_v2_module.Mode
22+
ModeType = google.protobuf.internal.enum_type_wrapper.EnumTypeWrapper
23+
24+
25+
class Modes:
26+
pass
27+
28+
# Current values: {'DEFAULT_MODE': 0, 'NATIVE': 1, 'PREPROCESSED': 2, 'ANNOTATED': 4, 'SEMANTIC': 8}
29+
for k, v in Mode.DESCRIPTOR.values_by_name.items():
30+
setattr(Modes, k, v.number)
31+
32+
DriverStub = protocol_grpc_v2_module.DriverStub
33+
DriverServicer = protocol_grpc_v2_module.DriverServicer
1634

17-
DESCRIPTOR = uast_module.DESCRIPTOR
18-
Node = uast_module.Node
19-
Position = uast_module.Position
20-
ParseResponse = protocol_module.ParseResponse
21-
NativeParseResponse = protocol_module.NativeParseResponse
22-
ParseRequest = protocol_module.ParseRequest
23-
NativeParseRequest = protocol_module.NativeParseRequest
24-
VersionRequest = protocol_module.VersionRequest
25-
SupportedLanguagesRequest = protocol_module.SupportedLanguagesRequest
26-
SupportedLanguagesResponse = protocol_module.SupportedLanguagesResponse
27-
ProtocolServiceStub = protocol_grpc_module.ProtocolServiceStub
35+
VersionRequest = protocol_v1_module.VersionRequest
36+
VersionResponse = protocol_v1_module.VersionResponse
37+
SupportedLanguagesRequest = protocol_v1_module.SupportedLanguagesRequest
38+
SupportedLanguagesResponse = protocol_v1_module.SupportedLanguagesResponse
39+
ProtocolServiceStub = protocol_grpc_v1_module.ProtocolServiceStub

0 commit comments

Comments
 (0)