diff --git a/xo-pywebsock/.gitignore b/xo-pywebsock/.gitignore new file mode 100644 index 00000000..13c0afb7 --- /dev/null +++ b/xo-pywebsock/.gitignore @@ -0,0 +1,6 @@ +# clangd working space (see emacs+lsp) +.cache +# typical cmake build directory (source-tree-nephew) +.build* +# symlink to builddir/compile_commands.json; should be set manually in dev sandbox +compile_commands.json diff --git a/xo-pywebsock/CMakeLists.txt b/xo-pywebsock/CMakeLists.txt new file mode 100644 index 00000000..56e9f2eb --- /dev/null +++ b/xo-pywebsock/CMakeLists.txt @@ -0,0 +1,12 @@ +# xo-pywebsock/CMakeLists.txt + +cmake_minimum_required(VERSION 3.10) +project(xo_pywebsock VERSION 1.0) + +include(cmake/xo-bootstrap-macros.cmake) + +xo_cxx_toplevel_options() + +add_subdirectory(src/pywebsock) + +xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) diff --git a/xo-pywebsock/README.md b/xo-pywebsock/README.md new file mode 100644 index 00000000..468c605d --- /dev/null +++ b/xo-pywebsock/README.md @@ -0,0 +1,71 @@ +# python bindings for c++ websocket library (xo-websock) + +## Getting Started + +### build + install dependencies + +- [github/Rconybea/xo-websock](https://github.com/Rconybea/xo-websock) +- [github/Rconybea/xo-pyutil](https://github.com/Rconybea/xo-pyutil) +- [github/Rconybea/xo-pyreactor](https://github.com/Rconybea/xo-pyreactor) + +### build + install + +``` +$ cd xo-pywebsock +$ mkdir build +$ cd build +$ INSTALL_PREFIX=/usr/local # or wherever you prefer, e.g. ~/local +$ cmake \ + -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake \ + -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} \ + -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} .. +$ make +$ make install +``` +(also see .github/workflows/main.yml) + +## Examples + +Assumes `xo-pywebsock` installed to `~/local2/lib` +``` +PYTHONPATH=~/local2/lib:$PYTHONPATH python +>>> import xo_pywebsock +>>> dir(xo_pywebsock) +['Runstate', 'Webserver', 'WebserverConfig', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'make_webserver'] +>>> +``` + +## Development + +### build for unit test coverage +``` +$ cd xo-pywebsock +$ mkdir build-ccov +$ cd build-ccov +$ cmake \ + -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake \ + -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} \ + -DCODE_COVERAGE=ON \ + -DCMAKE_BUILD_TYPE=Debug .. +``` + +### LSP (language server) support + +LSP looks for compile commands in the root of the source tree; +while Cmake creates them in the root of its build directory. + +``` +$ cd xo-pywebsock +$ ln -s build/compile_commands.json # supply compile commands to LSP +``` + +### display cmake variables + +- `-L` list variables +- `-A` include 'advanced' variables +- `-H` include help text + +``` +$ cd xo-pywebsock/build +$ cmake -LAH +``` diff --git a/xo-pywebsock/cmake/xo-bootstrap-macros.cmake b/xo-pywebsock/cmake/xo-bootstrap-macros.cmake new file mode 100644 index 00000000..96592216 --- /dev/null +++ b/xo-pywebsock/cmake/xo-bootstrap-macros.cmake @@ -0,0 +1,14 @@ +if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL "prefix")) + # default to typical install location for xo-project-macros + set(CMAKE_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/share/cmake) +endif() + +if (NOT XO_SUBMODULE_BUILD) + message("-- CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") + message("-- CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") +endif() + +# needs to have been installed somewhere on CMAKE_MODULE_PATH, +# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) +# +include(xo_macros/xo-project-macros) diff --git a/xo-pywebsock/cmake/xo_pywebsockConfig.cmake.in b/xo-pywebsock/cmake/xo_pywebsockConfig.cmake.in new file mode 100644 index 00000000..9c15f36a --- /dev/null +++ b/xo-pywebsock/cmake/xo_pywebsockConfig.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/xo-pywebsock/include/README.md b/xo-pywebsock/include/README.md new file mode 100644 index 00000000..1c13f62a --- /dev/null +++ b/xo-pywebsock/include/README.md @@ -0,0 +1 @@ +placeholder. needed at install for generated .hpp file diff --git a/xo-pywebsock/src/pywebsock/CMakeLists.txt b/xo-pywebsock/src/pywebsock/CMakeLists.txt new file mode 100644 index 00000000..1e40a064 --- /dev/null +++ b/xo-pywebsock/src/pywebsock/CMakeLists.txt @@ -0,0 +1,9 @@ +# xo_pywebsock/src/pywebsock/CMakeLists.txt + +set(SELF_LIB xo_pywebsock) +set(SELF_SRCS pywebsock.cpp) + +xo_pybind11_library(${SELF_LIB} ${PROJECT_NAME}Targets ${SELF_SRCS}) +xo_pybind11_dependency(${SELF_LIB} websock) +xo_pybind11_header_dependency(${SELF_LIB} xo_pywebutil) +xo_pybind11_dependency(${SELF_LIB} xo_pyutil) diff --git a/xo-pywebsock/src/pywebsock/pywebsock.cpp b/xo-pywebsock/src/pywebsock/pywebsock.cpp new file mode 100644 index 00000000..bbaa19c1 --- /dev/null +++ b/xo-pywebsock/src/pywebsock/pywebsock.cpp @@ -0,0 +1,63 @@ +/* @file pywebsock.cpp */ + +#include "pywebsock.hpp" +#include "xo/pywebutil/pywebutil.hpp" +#include "xo/websock/Webserver.hpp" +#include "xo/printjson/PrintJson.hpp" +//#include "web_util/EndpointDescr.hpp" +#include "xo/pyutil/pyutil.hpp" +#include + +namespace xo { + using xo::web::WebserverConfig; + using xo::web::Webserver; + using xo::web::Runstate; + using xo::json::PrintJsonSingleton; + using xo::rp; + namespace py = pybind11; + + namespace web { + PYBIND11_MODULE(PYWEBSOCK_MODULE_NAME(), m) { + PYWEBUTIL_IMPORT_MODULE(); // = py::module_::import("pywebutil") + + /* module docstring */ + m.doc() = "pybind11 plugin for xo.websock"; + + py::enum_(m, "Runstate") + .value("stopped", Runstate::stopped) + .value("stop_requested", Runstate::stop_requested) + .value("running", Runstate::running); + + py::class_(m, "WebserverConfig") + .def(py::init(), + py::arg("port"), + py::arg("tls_flag"), + py::arg("host_check_flag"), + py::arg("use_retry_flag")) + .def_property_readonly("port", &WebserverConfig::port) + .def_property_readonly("tls_flag", &WebserverConfig::tls_flag) + .def_property_readonly("host_check_flag", &WebserverConfig::host_check_flag) + .def_property_readonly("use_retry_flag", &WebserverConfig::use_retry_flag); + + py::class_>(m, "Webserver") + .def_static("make", + [](WebserverConfig const & ws_config) + { + return Webserver::make(ws_config, + PrintJsonSingleton::instance()); + }) + .def_property_readonly("state", &Webserver::state) + .def("register_http_endpoint", &Webserver::register_http_endpoint) + .def("register_stream_endpoint", &Webserver::register_stream_endpoint) + .def("start_webserver", &Webserver::start_webserver) + .def("stop_webserver", &Webserver::stop_webserver) + .def("join_webserver", &Webserver::join_webserver) + .def("__repr__", &Webserver::display_string); + + m.def("make_webserver", + &Webserver::make); + } /*pywebsock*/ + } /*web*/ +} /*namespace xo*/ + +/* end pywebsock.cpp */ diff --git a/xo-pywebsock/src/pywebsock/pywebsock.hpp.in b/xo-pywebsock/src/pywebsock/pywebsock.hpp.in new file mode 100644 index 00000000..4d3ec777 --- /dev/null +++ b/xo-pywebsock/src/pywebsock/pywebsock.hpp.in @@ -0,0 +1,25 @@ +/* @file pywebsock.hpp + * + * automatically generated from src/pywebsock/pywebsock.hpp.in + * see src/pywebsock/CMakeLists.txt + */ + +/* python requires module name = library name + * example: + * PYBIND11_MODULE(PYWEBSOCK_MODULE_NAME(), m) { ... } + */ +#define PYWEBSOCK_MODULE_NAME() @SELF_LIB@ + +/* example: + * py::module_::import(PYWEBSOCK_MODULE_NAME_STR) + */ +#define PYWEBSOCK_MODULE_NAME_STR "@SELF_LIB@" + +/* example: + * PYWEBSOCK_IMPORT_MODULE() + * replaces + * py::module_::import("pywebsock") + */ +#define PYWEBSOCK_IMPORT_MODULE() py::module_::import("@SELF_LIB@") + +/* end pywebsock.hpp */