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

Python client v3 (UASTv2) #128

Merged
merged 53 commits into from
Mar 12, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
9b5b502
use the new libuast; rewrite bindings using cpp layer of libuast
Oct 4, 2018
2788a1a
working bindings prototype
Oct 11, 2018
8e39632
fix string memory management
Oct 16, 2018
d3b6bf9
refactoring: NodeExtType->PyNodeExtType for consistency
bzz Oct 16, 2018
6ce2b11
refactoring: NodeExt->PyNodeExt for consistency
bzz Oct 16, 2018
7bac87f
refactoring: PyUastType->PyContextType for consistency
bzz Oct 16, 2018
8b84e4a
refactoring: PyUast->PyContext for consistency
bzz Oct 16, 2018
1fa1d0d
refactoring: fix comments + fmt after rebase
bzz Oct 17, 2018
9e88733
apply review feedback
bzz Oct 17, 2018
98b3ef8
fix replace
Oct 17, 2018
96abf64
Build fixes, comment out v1 things, some other adjustements
Oct 18, 2018
151e61c
Recover grpc sdk v1 protocol for some grpc objects
Oct 22, 2018
7f583ea
Forward port the aliases refactor by Vadim
Oct 22, 2018
6ba57fc
Forward port travis changes
Oct 24, 2018
24fd7b6
Merge branch 'master' into v3
juanjux Oct 24, 2018
a2ca471
fix pip install
Oct 30, 2018
e308038
update the client to use both protocols
Oct 30, 2018
0d675e1
Remove unused and broken import
Oct 31, 2018
91b798b
Compile the ext module from an static libuast object
Oct 31, 2018
acec219
enable building the client with static libuast
Nov 1, 2018
2fd570c
do not free the query string in filter, it seems to be borrowed
Nov 1, 2018
d05770c
improve the native Python wrappers and update the readme
Nov 1, 2018
272acc9
fix error handling in native extension
Nov 2, 2018
270445b
Explicit cast to char* to avoid nasty warning with latest G++
Nov 8, 2018
1f977e4
PEP8
Nov 8, 2018
20890e0
Renamed PyContext to PythonContext to avoid symbol conflict in 3.7+
Nov 8, 2018
2c983a9
Use same name for Windows an Linux static lib before the extension
Nov 8, 2018
0bcf223
Add several needed static libs for Windows
Nov 8, 2018
8401a1c
Several improvements (see desc)
Nov 15, 2018
bd8c2d5
Several Improvements (II)
Nov 16, 2018
f964c46
Make iterators great (and working) again
Nov 16, 2018
ea7d615
fix usage of parsed string arguments in filter
Dec 5, 2018
1c73766
properly deallocate python objects
Dec 5, 2018
7cb563a
free encoding buffer
Dec 5, 2018
cd1d90d
bump versions
Dec 5, 2018
a55abc4
Unittests and other fixes
Dec 11, 2018
d74a514
Merge branch 'master' into v3
juanjux Dec 11, 2018
75170a6
Uncommented failed test
Dec 11, 2018
c4fd5be
Enabled unnitesting in travis
Dec 11, 2018
753efb4
Run docker and install python driver from travis
Dec 11, 2018
9876503
Commented out the node afected by SDK issue 340
Dec 12, 2018
7098328
Merge branch 'master' into v3
juanjux Dec 12, 2018
7ad8c6f
Remove Python 3.5 from Travis
Dec 12, 2018
a2752b7
Use range for grpcio and grpciotools
Dec 12, 2018
a99f5be
Fixed some of @bzz feedback from review
Dec 12, 2018
b983ea2
add error checks for iterators and clarify comments
Dec 13, 2018
9e3f415
Fixed from @zurk review (thanks!)
Dec 18, 2018
988eb5e
Merge branch 'v3' of https://github.com/dennwc/client-python into v3
Dec 18, 2018
ba93944
Merge branch 'master' into v3
juanjux Dec 18, 2018
d485273
Fixes and improvements from @vmarkovtsev review
Dec 18, 2018
66ccfed
PEP8 fix
Dec 18, 2018
a020666
Changed ModeDict to a Modes enum-like class
Dec 18, 2018
9b094aa
Allow to create Clients with an instanced grpc channel as suggested b…
Dec 18, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,18 @@ Please, read the [getting started](https://doc.bblf.sh/using-babelfish/getting-s
import bblfsh

client = bblfsh.BblfshClient("0.0.0.0:9432")
uast = client.parse("/path/to/file.py").uast
print(uast)
uast = client.parse("/path/to/file.py")
print(uast.load())

# "filter' allows you to use XPath queries to filter on result nodes:
print(bblfsh.filter(uast, "//Import[@roleImport and @roleDeclaration]//alias"))
it = uast.filter("//Import[@role='Import' and @role='Declaration']//alias")
for node in it:
print(node.load())

# filter\_[bool|string|number] must be used when using XPath functions returning
# these types:
print(bblfsh.filter_bool(uast, "boolean(//*[@strtOffset or @endOffset])"))
print(bblfsh.filter_string(uast, "name(//*[1])"))
print(bblfsh.filter_number(uast, "count(//*)"))
# filter must be used when using XPath functions returning these types:
print(uast.filter("boolean(//*[@strtOffset or @endOffset])"))
print(uast.filter("name(//*[1])"))
print(uast.filter("count(//*)"))

# You can also iterate on several tree iteration orders:
it = bblfsh.iterator(uast, bblfsh.TreeOrder.PRE_ORDER)
Expand Down
5 changes: 3 additions & 2 deletions bblfsh/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import grpc

import bblfsh.pyuast
from bblfsh.pyuast import decode as uast_decode

from bblfsh.aliases import ParseRequest, DriverStub, ProtocolServiceStub, VersionRequest, SupportedLanguagesRequest

Expand Down Expand Up @@ -83,7 +83,8 @@ def parse(self, filename, language=None, contents=None, mode=None, raw=False, ti
"""
if raw:
return response.uast
return pyuast.decode(response.uast, 0)
ctx = uast_decode(response.uast, format=0)
return ctx

def supported_languages(self):
sup_response = self._stub_v1.SupportedLanguages(SupportedLanguagesRequest())
Expand Down
41 changes: 28 additions & 13 deletions bblfsh/pyuast.cc
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ class ContextExt {
}

uast::Iterator<NodeHandle> *it = ctx->Filter(unode, query);
delete(query);

return newIter(it, false);
}

Expand Down Expand Up @@ -309,13 +309,22 @@ static PyObject *PyContextExt_root(PyContextExt *self, PyObject *Py_UNUSED(ignor
return self->p->RootNode();
}

// PyContextExt_load returns a root node converted to Python object.
// Returns a new reference.
static PyObject *PyContextExt_load(PyContextExt *self, PyObject *Py_UNUSED(ignored)) {
PyObject* root = PyContextExt_root(self, nullptr);
return PyNodeExt_load((PyNodeExt*)root, nullptr);
}

// PyContextExt_filter filters UAST.
// Returns a new reference.
static PyObject *PyContextExt_filter(PyContextExt *self, PyObject *args) {
PyObject *node = nullptr;
static PyObject *PyContextExt_filter(PyContextExt *self, PyObject *args, PyObject *kwargs) {
char* kwds[] = {"query", "node", NULL};
char *query = nullptr;
if (!PyArg_ParseTuple(args, "Os", &node, &query))
PyObject *node = nullptr;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", kwds, &query, &node))
return nullptr;
// TODO: freeing the query leads to a segfault; need to clarify why
return self->p->Filter(node, query);
}

Expand All @@ -333,7 +342,10 @@ static PyMethodDef PyContextExt_methods[] = {
{"root", (PyCFunction) PyContextExt_root, METH_NOARGS,
"Return the root node attached to this query context"
},
{"filter", (PyCFunction) PyContextExt_filter, METH_VARARGS,
{"load", (PyCFunction) PyContextExt_load, METH_NOARGS,
"Load external node to Python"
},
{"filter", (PyCFunction) PyContextExt_filter, METH_VARARGS | METH_KEYWORDS,
"Filter a provided UAST with XPath"
},
{"encode", (PyCFunction) PyContextExt_encode, METH_VARARGS,
Expand Down Expand Up @@ -869,10 +881,11 @@ static PyObject *PyContext_root(PyContext *self, PyObject *Py_UNUSED(ignored)) {
return self->p->RootNode();
}

static PyObject *PyContext_filter(PyContext *self, PyObject *args) {
PyObject *node = nullptr;
static PyObject *PyContext_filter(PyContext *self, PyObject *args, PyObject *kwargs) {
char* kwds[] = {"query", "node", NULL};
char *query = nullptr;
if (!PyArg_ParseTuple(args, "Os", &node, &query))
PyObject *node = nullptr;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", kwds, &query, &node))
return nullptr;
return self->p->Filter(node, query);
}
Expand All @@ -889,7 +902,7 @@ static PyMethodDef PyContext_methods[] = {
{"root", (PyCFunction) PyContext_root, METH_NOARGS,
"Return the root node attached to this query context"
},
{"filter", (PyCFunction) PyContext_filter, METH_VARARGS,
{"filter", (PyCFunction) PyContext_filter, METH_VARARGS | METH_KEYWORDS,
"Filter a provided UAST with XPath"
},
{"encode", (PyCFunction) PyContext_encode, METH_VARARGS,
Expand Down Expand Up @@ -964,11 +977,12 @@ static PyObject *PyUastIter_new(PyObject *self, PyObject *args) {
return ctx->Iterate(obj, (TreeOrder)order, true);
}

static PyObject *PyContextExt_decode(PyObject *self, PyObject *args) {
static PyObject *PyContextExt_decode(PyObject *self, PyObject *args, PyObject *kwargs) {
char* kwds[] = {"data", "format", NULL};
PyObject *obj = nullptr;
UastFormat format = UAST_BINARY; // TODO: make it a kwarg
UastFormat format = UAST_BINARY; // TODO: make it an enum

if (!PyArg_ParseTuple(args, "Oi", &obj, &format))
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i", kwds, &obj, &format))
return nullptr;

Py_buffer buf;
Expand All @@ -992,6 +1006,7 @@ static PyObject *PyContextExt_decode(PyObject *self, PyObject *args) {
}

static PyObject *PyContext_new(PyObject *self, PyObject *args) {
// TODO: optionally accept root object
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
Expand All @@ -1006,7 +1021,7 @@ static PyObject *PyContext_new(PyObject *self, PyObject *args) {

static PyMethodDef extension_methods[] = {
{"iterator", PyUastIter_new, METH_VARARGS, "Get an iterator over a node"},
{"decode", PyContextExt_decode, METH_VARARGS, "Decode UAST from a byte array"},
{"decode", (PyCFunction)PyContextExt_decode, METH_VARARGS | METH_KEYWORDS, "Decode UAST from a byte array"},
{"uast", PyContext_new, METH_VARARGS, "Creates a new UAST context"},
{nullptr, nullptr, 0, nullptr}
};
Expand Down
5 changes: 2 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from setuptools.command.build_ext import build_ext

VERSION = "3.0.0"
LIBUAST_VERSION = "v3.0.0-rc2"
LIBUAST_VERSION = "v3.0.0-rc3"
LIBUAST_ARCH = "linux-amd64"
SDK_V1_VERSION = "v1.16.1"
SDK_V1_MAJOR = SDK_V1_VERSION.split('.')[0]
Expand All @@ -25,8 +25,7 @@
log = logging.getLogger("setup.py")

# For debugging libuast-client interactions, set to True in production!
# FIXME: change to true
GET_LIBUAST = False
GET_LIBUAST = True
if not GET_LIBUAST:
log.warning("WARNING: not retrieving libuast, using local version")

Expand Down