Skip to content

Commit 66e1f14

Browse files
author
Kevin Vu te Laar
committed
binding wrapper: CI/build fix, add binding stubs
CI: - Python unit and integration tests have their own CI test now. - Unit and integration test pipelines reverted to pre-binding related changes. Binding need workarounds to comply with packaging, CI and building compatibility. - Format fixes. - `binding.py` (binding wrapper) and bindings have stubs now. CMake build: - Naming is more straight forward. - Bindings install into `pythonX.Y/site-packages/villas/node/`. Should be compatible with a potential pypi-package related to the wrapper binding. Signed-off-by: Kevin Vu te Laar <vu.te@rwth-aachen.de>
1 parent 279eb80 commit 66e1f14

12 files changed

Lines changed: 328 additions & 219 deletions

.gitlab-ci.yml

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,34 @@ test:cppcheck:
168168
- cppcheck.log
169169
expose_as: cppcheck
170170

171+
test:python_unit_integration:
172+
stage: test
173+
image: ${DOCKER_IMAGE_DEV}:${DOCKER_TAG}
174+
before_script:
175+
# dependency from node.py, which gets imported because of __init__.py in node/villas/python/node
176+
- pip install requests
177+
script:
178+
# build binding and symlink to villas.node folder
179+
# helps with correct imports and compatiblity for CI, binding wrapper and binding install
180+
- export PYTHONPATH=$PYTHONPATH:${PWD}/python
181+
- cmake --build build ${CMAKE_BUILD_OPTS} --target python_binding
182+
183+
- binding_path=$(find ${PWD}/build/python/binding/ -name "python_binding*.so" | head -n1)
184+
- link_path=${PWD}/python/villas/node/$(basename $python_binding)
185+
- ln -sf $binding_path $link_path
186+
187+
- cmake --build build ${CMAKE_BUILD_OPTS} --target run-python-unit-tests run-python-integration-tests
188+
- rm $node_path
189+
needs:
190+
- job: "build:source: [fedora]"
191+
artifacts: true
192+
171193
test:unit:
172194
stage: test
173195
image: ${DOCKER_IMAGE_DEV}:${DOCKER_TAG}
174196
script:
175197
- cmake -S . -B build ${CMAKE_OPTS}
176-
- export PYTHONPATH=$PYTHONPATH:${PWD}/build/python/binding/:${PWD}/python/villas/node/
177-
- cmake --build build ${CMAKE_BUILD_OPTS} --target run-unit-tests run-unit-tests-common run-python-unit-tests
198+
- cmake --build build ${CMAKE_BUILD_OPTS} --target run-unit-tests run-unit-tests-common
178199
needs:
179200
- job: "build:source: [fedora]"
180201
artifacts: true
@@ -184,8 +205,7 @@ test:integration:
184205
image: ${DOCKER_IMAGE_DEV}:${DOCKER_TAG}
185206
script:
186207
- cmake -S . -B build ${CMAKE_OPTS}
187-
- export PYTHONPATH=$PYTHONPATH:${PWD}/build/python/binding/:${PWD}/python/villas/node/
188-
- cmake --build build ${CMAKE_BUILD_OPTS} --target run-integration-tests run-python-integration-tests
208+
- cmake --build build ${CMAKE_BUILD_OPTS} --target run-integration-tests
189209
artifacts:
190210
name: ${CI_PROJECT_NAME}-integration-tests-${CI_BUILD_REF}
191211
when: always

python/binding/CMakeLists.txt

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,29 @@ if(pybind11_FOUND)
1111
find_package(Python3 REQUIRED COMPONENTS Interpreter Development)
1212

1313
execute_process(
14-
COMMAND "${Python3_EXECUTABLE}" -c "import sysconfig; print(sysconfig.get_path('stdlib') + '/lib-dynload')"
15-
OUTPUT_VARIABLE PYTHON_LIB_DYNLOAD_DIR
14+
COMMAND "${Python3_EXECUTABLE}" -c "import sysconfig; print(sysconfig.get_path('purelib'))"
15+
OUTPUT_VARIABLE PYTHON_SITE_PACKAGES
1616
OUTPUT_STRIP_TRAILING_WHITESPACE
1717
)
1818

1919
message(STATUS "Found Python version: ${Python_VERSION}")
2020
message(STATUS "Python major version: ${Python_VERSION_MAJOR}")
2121
message(STATUS "Python minor version: ${Python_VERSION_MINOR}")
22-
message(STATUS "Python .so install directory: ${PYTHON_LIB_DYNLOAD_DIR}")
2322

24-
pybind11_add_module(python-binding villas-python-binding.cpp)
25-
set_target_properties(python-binding PROPERTIES OUTPUT_NAME villas_node)
26-
target_link_libraries(python-binding PUBLIC villas)
23+
pybind11_add_module(python_binding capi_python_binding.cpp)
24+
target_link_libraries(python_binding PUBLIC villas)
2725

2826
install(
29-
TARGETS python-binding
27+
TARGETS python_binding
3028
COMPONENT lib
31-
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
32-
LIBRARY DESTINATION ${PYTHON_LIB_DYNLOAD_DIR}
29+
LIBRARY DESTINATION ${PYTHON_SITE_PACKAGES}/villas/node/
30+
)
31+
file(WRITE "${CMAKE_BINARY_DIR}/python/villas/__init__.py" "")
32+
file(WRITE "${CMAKE_BINARY_DIR}/python/villas/node/__init__.py" "")
33+
install(
34+
DIRECTORY "${CMAKE_BINARY_DIR}/python/villas"
35+
DESTINATION "${PYTHON_SITE_PACKAGES}"
36+
FILES_MATCHING PATTERN "__init__.py"
3337
)
3438
else()
3539
message(STATUS "pybind11 not found. Skipping Python wrapper build.")
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class Array {
6767
* @param villas_node Name of the module to be bound
6868
* @param m Access variable for modifying the module code
6969
*/
70-
PYBIND11_MODULE(villas_node, m) {
70+
PYBIND11_MODULE(python_binding, m) {
7171
m.def("memory_init", &memory_init);
7272

7373
m.def("node_check", [](void *n) -> int { return node_check((vnode *)n); });

python/villas/node/binding.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import json
1111
import logging
1212

13-
import villas_node as vn
13+
import villas.node.python_binding as vn
1414

1515
logger = logging.getLogger("villas.node")
1616

python/villas/node/binding.pyi

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from _typeshed import Incomplete
2+
from typing import Any, Callable
3+
4+
logger: Incomplete
5+
6+
7+
class SamplesArray:
8+
def __init__(self, length: int) -> None: ...
9+
def __len__(self) -> int: ...
10+
def __getitem__(self, idx: int | slice): ...
11+
def __copy__(self) -> None: ...
12+
def __deepcopy__(self) -> None: ...
13+
14+
15+
def _warn_if_not_implemented(
16+
func: Callable[..., Any],
17+
) -> Callable[..., Any]: ...
18+
def memory_init(*args): ...
19+
def node_check(node): ...
20+
def node_destroy(node): ...
21+
def node_details(node): ...
22+
def node_input_signals_max_cnt(node): ...
23+
def node_is_enabled(node): ...
24+
def node_is_valid_name(name: str): ...
25+
def node_name(node): ...
26+
def node_name_full(node): ...
27+
def node_name_short(node): ...
28+
def node_netem_fds(*args): ...
29+
def node_new(config, uuid: str | None = None): ...
30+
def node_output_signals_max_cnt(node): ...
31+
def node_pause(node): ...
32+
def node_poll_fds(*args): ...
33+
def node_prepare(node): ...
34+
@_warn_if_not_implemented
35+
def node_read(node, samples, sample_length, count): ...
36+
def node_restart(node): ...
37+
def node_resume(node): ...
38+
@_warn_if_not_implemented
39+
def node_reverse(node): ...
40+
def node_start(node): ...
41+
def node_stop(node): ...
42+
def node_to_json(node): ...
43+
def node_to_json_str(node): ...
44+
@_warn_if_not_implemented
45+
def node_write(node, samples, count): ...
46+
def sample_length(*args) -> None: ...
47+
def sample_pack(*args) -> None: ...
48+
def sample_unpack(*args) -> None: ...
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import typing
2+
from typing import Any, Callable, overload
3+
4+
Array = Any
5+
capsule = Any
6+
timespec = Any
7+
8+
9+
class SamplesArray:
10+
def __init__(self, len: int) -> None: ...
11+
def bulk_alloc(self, arg0: int, arg1: int, arg2: int) -> None: ...
12+
def get_block(self, arg0: int) -> capsule: ...
13+
def __getitem__(self, arg0: int) -> capsule: ...
14+
def __iter__(self) -> typing.Iterator[capsule]: ...
15+
def __setitem__(self, arg0: int, arg1: capsule) -> None: ...
16+
17+
18+
def memory_init(arg0: int) -> int: ...
19+
def node_check(arg0: capsule) -> int: ...
20+
def node_destroy(arg0: capsule) -> int: ...
21+
def node_details(arg0: capsule) -> str: ...
22+
def node_input_signals_max_cnt(arg0: capsule) -> int: ...
23+
def node_is_enabled(arg0: capsule) -> bool: ...
24+
def node_is_valid_name(arg0: str) -> bool: ...
25+
def node_name(arg0: capsule) -> str: ...
26+
def node_name_full(arg0: capsule) -> str: ...
27+
def node_name_short(arg0: capsule) -> str: ...
28+
def node_netem_fds(arg0: capsule, arg1: int) -> int: ...
29+
def node_new(arg0: str, arg1: str) -> capsule: ...
30+
def node_output_signals_max_cnt(arg0: capsule) -> int: ...
31+
def node_pause(arg0: capsule) -> int: ...
32+
def node_poll_fds(arg0: capsule, arg1: int) -> int: ...
33+
def node_prepare(arg0: capsule) -> int: ...
34+
@overload
35+
def node_read(arg0: capsule, arg1: Array, arg2: int) -> int: ...
36+
@overload
37+
def node_read(arg0: capsule, arg1: capsule, arg2: int) -> int: ... # type: ignore[overload-cannot-match]
38+
def node_restart(arg0: capsule) -> int: ...
39+
def node_resume(arg0: capsule) -> int: ...
40+
def node_reverse(arg0: capsule) -> int: ...
41+
def node_start(arg0: capsule) -> int: ...
42+
def node_stop(arg0: capsule) -> int: ...
43+
def node_to_json_str(arg0: capsule) -> str: ...
44+
@overload
45+
def node_write(arg0: capsule, arg1: Array, arg2: int) -> int: ...
46+
@overload
47+
def node_write(arg0: capsule, arg1: capsule, arg2: int) -> int: ... # type: ignore[overload-cannot-match]
48+
def sample_alloc(arg0: int) -> capsule: ...
49+
def sample_decref(arg0: capsule) -> None: ...
50+
def sample_length(arg0: capsule) -> int: ...
51+
def sample_pack(
52+
arg0: int, arg1: timespec, arg2: timespec, arg3: int, arg4: float
53+
) -> capsule: ...
54+
def sample_unpack(
55+
arg0: capsule,
56+
arg1: int,
57+
arg2: timespec,
58+
arg3: timespec,
59+
arg4: int,
60+
arg5: int,
61+
arg6: float,
62+
) -> None: ...
63+
def smps_array(arg0: int) -> Array: ...

tests/integration/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ add_custom_target(run-python-integration-tests
2222
COMMAND
2323
python3 -m unittest discover ${CMAKE_CURRENT_SOURCE_DIR}
2424
DEPENDS
25-
python-binding
25+
python_binding
2626
USES_TERMINAL
2727
)
2828

0 commit comments

Comments
 (0)