mirror of
https://github.com/ralsina/tartrazine.git
synced 2025-05-24 08:18:52 -03:00
Merge pull request #245 from bzz/initial-cffi-python-bindings
Initial cffi bindings for python
This commit is contained in:
commit
a4c166cc04
7
Makefile
7
Makefile
@ -29,6 +29,7 @@ LINUX_DIR=$(RESOURCES_DIR)/linux-x86-64
|
|||||||
LINUX_SHARED_LIB=$(LINUX_DIR)/libenry.so
|
LINUX_SHARED_LIB=$(LINUX_DIR)/libenry.so
|
||||||
DARWIN_DIR=$(RESOURCES_DIR)/darwin
|
DARWIN_DIR=$(RESOURCES_DIR)/darwin
|
||||||
DARWIN_SHARED_LIB=$(DARWIN_DIR)/libenry.dylib
|
DARWIN_SHARED_LIB=$(DARWIN_DIR)/libenry.dylib
|
||||||
|
STATIC_LIB=$(RESOURCES_DIR)/libenry.a
|
||||||
HEADER_FILE=libenry.h
|
HEADER_FILE=libenry.h
|
||||||
NATIVE_LIB=./shared/enry.go
|
NATIVE_LIB=./shared/enry.go
|
||||||
|
|
||||||
@ -79,4 +80,10 @@ $(LINUX_SHARED_LIB):
|
|||||||
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -buildmode=c-shared -o $(LINUX_SHARED_LIB) $(NATIVE_LIB) && \
|
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -buildmode=c-shared -o $(LINUX_SHARED_LIB) $(NATIVE_LIB) && \
|
||||||
mv $(LINUX_DIR)/$(HEADER_FILE) $(RESOURCES_DIR)/$(HEADER_FILE)
|
mv $(LINUX_DIR)/$(HEADER_FILE) $(RESOURCES_DIR)/$(HEADER_FILE)
|
||||||
|
|
||||||
|
|
||||||
|
static: $(STATIC_LIB)
|
||||||
|
|
||||||
|
$(STATIC_LIB):
|
||||||
|
CGO_ENABLED=1 go build -buildmode=c-archive -o $(STATIC_LIB) $(NATIVE_LIB)
|
||||||
|
|
||||||
.PHONY: benchmarks benchmarks-samples benchmarks-slow
|
.PHONY: benchmarks benchmarks-samples benchmarks-slow
|
||||||
|
@ -59,4 +59,6 @@ package:
|
|||||||
clean:
|
clean:
|
||||||
rm -rf $(JAR)
|
rm -rf $(JAR)
|
||||||
rm -rf $(RESOURCES_DIR)
|
rm -rf $(RESOURCES_DIR)
|
||||||
|
rm -rf $(JNAERATOR_JAR)
|
||||||
|
|
||||||
|
.PHONY: test package clean linux-shared darwin-shared os-shared-lib
|
||||||
|
29
python/README.md
Normal file
29
python/README.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Python bindings for enry
|
||||||
|
|
||||||
|
Python bingings thoug cFFI (API, out-of-line) for calling enr Go functions though CGo wrapper.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```
|
||||||
|
$ make static
|
||||||
|
$ python enry_build.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Will build static library for Cgo wrapper `libenry`, then generate and build `enry.c`
|
||||||
|
- a CPython extension that
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
Example for single exposed API function is provided.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ python enry.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## TODOs
|
||||||
|
- [ ] try ABI mode, to aviod dependency on C compiler on install (+perf test?)
|
||||||
|
- [ ] ready `libenry.h` and generate `ffibuilder.cdef` content
|
||||||
|
- [ ] helpers for sending/recieving Go slices to C
|
||||||
|
- [ ] cover the rest of enry API
|
||||||
|
- [ ] add `setup.py`
|
||||||
|
- [ ] build/release automation on CI (publish on pypi)
|
76
python/enry.py
Normal file
76
python/enry.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
"""
|
||||||
|
Python library calling enry Go implementation trough cFFI (API, out-of-line) and Cgo.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from _c_enry import ffi, lib
|
||||||
|
|
||||||
|
## Helpers
|
||||||
|
|
||||||
|
|
||||||
|
def go_str_to_py(go_str):
|
||||||
|
str_len = go_str.n
|
||||||
|
if str_len > 0:
|
||||||
|
return ffi.unpack(go_str.p, go_str.n).decode()
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def py_str_to_go(py_str):
|
||||||
|
str_bytes = py_str.encode()
|
||||||
|
c_str = ffi.new("char[]", str_bytes)
|
||||||
|
go_str = ffi.new("_GoString_ *", [c_str, len(str_bytes)])
|
||||||
|
return go_str[0]
|
||||||
|
|
||||||
|
|
||||||
|
def go_bool_to_py(go_bool):
|
||||||
|
return go_bool == 1
|
||||||
|
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
|
||||||
|
def language_by_extension(filename: str) -> str:
|
||||||
|
fName = py_str_to_go(filename)
|
||||||
|
guess = lib.GetLanguageByExtension(fName)
|
||||||
|
lang = go_str_to_py(guess.r0)
|
||||||
|
return lang
|
||||||
|
|
||||||
|
|
||||||
|
def language_by_filename(filename: str) -> str:
|
||||||
|
fName = py_str_to_go(filename)
|
||||||
|
guess = lib.GetLanguageByFilename(fName)
|
||||||
|
lang = go_str_to_py(guess.r0)
|
||||||
|
return lang
|
||||||
|
|
||||||
|
|
||||||
|
def is_vendor(filename: str) -> bool:
|
||||||
|
fName = py_str_to_go(filename)
|
||||||
|
guess = lib.IsVendor(fName)
|
||||||
|
return go_bool_to_py(guess)
|
||||||
|
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
files = [
|
||||||
|
"Parse.hs", "some.cpp", "and.go", "type.h", ".bashrc", ".gitignore"
|
||||||
|
]
|
||||||
|
|
||||||
|
print("strategy: extension")
|
||||||
|
for filename in files:
|
||||||
|
lang = language_by_extension(filename)
|
||||||
|
print("file: {:10s} language: '{}'".format(filename, lang))
|
||||||
|
|
||||||
|
print("\nstrategy: filename")
|
||||||
|
for filename in files:
|
||||||
|
lang = language_by_filename(filename)
|
||||||
|
print("file: {:10s} language: '{}'".format(filename, lang))
|
||||||
|
|
||||||
|
print("\ncheck: is vendor?")
|
||||||
|
for filename in files:
|
||||||
|
vendor = is_vendor(filename)
|
||||||
|
print("file: {:10s} vendor: '{}'".format(filename, vendor))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
43
python/enry_build.py
Normal file
43
python/enry_build.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
from cffi import FFI
|
||||||
|
ffibuilder = FFI()
|
||||||
|
|
||||||
|
# cdef() expects a single string declaring the C types, functions and
|
||||||
|
# globals needed to use the shared object. It must be in valid C syntax.
|
||||||
|
ffibuilder.cdef("""
|
||||||
|
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
|
||||||
|
typedef _GoString_ GoString;
|
||||||
|
typedef unsigned char GoUint8;
|
||||||
|
|
||||||
|
/* Return type for GetLanguageByExtension */
|
||||||
|
struct GetLanguageByExtension_return {
|
||||||
|
GoString r0; /* language */
|
||||||
|
GoUint8 r1; /* safe */
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct GetLanguageByExtension_return GetLanguageByExtension(GoString p0);
|
||||||
|
|
||||||
|
/* Return type for GetLanguageByFilename */
|
||||||
|
struct GetLanguageByFilename_return {
|
||||||
|
GoString r0; /* language */
|
||||||
|
GoUint8 r1; /* safe */
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct GetLanguageByFilename_return GetLanguageByFilename(GoString p0);
|
||||||
|
|
||||||
|
extern GoUint8 IsVendor(GoString p0);
|
||||||
|
""")
|
||||||
|
|
||||||
|
# set_source() gives the name of the python extension module to
|
||||||
|
# produce, and some C source code as a string. This C code needs
|
||||||
|
# to make the declarated functions, types and globals available,
|
||||||
|
# so it is often just the "#include".
|
||||||
|
ffibuilder.set_source("_c_enry",
|
||||||
|
"""
|
||||||
|
#include "../.shared/libenry.h" // the C header of the library
|
||||||
|
""",
|
||||||
|
libraries=['enry'],
|
||||||
|
library_dirs=['../.shared'
|
||||||
|
]) # library name, for the linker
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
ffibuilder.compile(verbose=True)
|
4
python/requirements.txt
Normal file
4
python/requirements.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
cffi==1.12.3
|
||||||
|
Click==7.0
|
||||||
|
pycparser==2.19
|
||||||
|
yapf==0.27.0
|
Loading…
x
Reference in New Issue
Block a user