From 3487e3780cedf9f13589e6da53089ba9e76ec18d Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 18 Oct 2023 17:01:49 -0400 Subject: [PATCH] initial implementation --- CMakeLists.txt | 44 ++++++++++ README.md | 61 +++++++++++++ cmake/xo_pyreactorConfig.cmake.in | 4 + include/README.md | 1 + src/pyreactor/CMakeLists.txt | 8 ++ src/pyreactor/pyreactor.cpp | 141 ++++++++++++++++++++++++++++++ src/pyreactor/pyreactor.hpp.in | 25 ++++++ 7 files changed, 284 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 cmake/xo_pyreactorConfig.cmake.in create mode 100644 include/README.md create mode 100644 src/pyreactor/CMakeLists.txt create mode 100644 src/pyreactor/pyreactor.cpp create mode 100644 src/pyreactor/pyreactor.hpp.in diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..aec8d379 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,44 @@ +# xo-pyreactor/CMakeLists.txt + +cmake_minimum_required(VERSION 3.10) + +project(xo_pyreactor VERSION 1.0) +enable_language(CXX) + +# common XO cmake macros (see github.com:Rconybea/xo-cmake) +include(xo_macros/xo_cxx) +include(xo_macros/code-coverage) + +# ---------------------------------------------------------------- +# unit test setup + +enable_testing() +# activate code coverage for all executables + libraries (when configured with -DCODE_COVERAGE=ON) +add_code_coverage() +# 1. assuming that /nix/store/ prefixes .hpp files belonging to gcc, catch2 etc. +# we're not interested in code coverage for these sources. +# 2. exclude the utest/ subdir, we don't need coverage on the unit tests themselves; +# rather, want coverage on the code that the unit tests exercise. +# +# NOTE: this seems to work only with the 'ccov-all' target. In particular, doesn't seem to do anything with the 'ccov' target +# +add_code_coverage_all_targets(EXCLUDE /nix/store/* ${PROJECT_SOURCE_DIR}/utest/* ${PROJECT_BINARY_DIR}/local/* ${PROJECT_SOURCE_DIR}/repo/*) + +# ---------------------------------------------------------------- +# c++ settings (usually temporary) + +set(PROJECT_CXX_FLAGS "") +add_definitions(${PROJECT_CXX_FLAGS}) + +xo_toplevel_compile_options() + +# ---------------------------------------------------------------- +# sources + +add_subdirectory(src/pyreactor) +#add_subdirectory(utest) + +# ---------------------------------------------------------------- +# provide find_package() support + +xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) diff --git a/README.md b/README.md new file mode 100644 index 00000000..f618c641 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# python bindings for c++ reactor library (xo-reactor) + +## Getting Started + +### build + install dependencies + +- [github/Rconybea/xo-reactor](https://github.com/Rconybea/xo-reactor) +- [github/Rconybea/xo-pyutil](https://github.com/Rconybea/xo-pyutil) +- [github/Rconybea/xo-pyreflect](https://github.com/Rconybea/xo-pyreflect) +- [github/Rconybea/xo-pyprintjson](https://github.com/Rconybea/xo-pyprintjson) + +### build + install + +``` +$ cd xo-pyreactor +$ 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) + +## Development + +### build for unit test coverage +``` +$ cd xo-pyreactor +$ 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-pyreactor +$ 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-pyreactor/build +$ cmake -LAH +``` diff --git a/cmake/xo_pyreactorConfig.cmake.in b/cmake/xo_pyreactorConfig.cmake.in new file mode 100644 index 00000000..9c15f36a --- /dev/null +++ b/cmake/xo_pyreactorConfig.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/include/README.md b/include/README.md new file mode 100644 index 00000000..4a0ad1c1 --- /dev/null +++ b/include/README.md @@ -0,0 +1 @@ +placeholder for future pyreactor #include files diff --git a/src/pyreactor/CMakeLists.txt b/src/pyreactor/CMakeLists.txt new file mode 100644 index 00000000..ad752e96 --- /dev/null +++ b/src/pyreactor/CMakeLists.txt @@ -0,0 +1,8 @@ +# xo_pyreactor/src/pyreactor/CMakeLists.txt + +set(SELF_LIB pyreactor) +set(SELF_SRCS pyreactor.cpp) + +xo_pybind11_library(${SELF_LIB} ${PROJECT_NAME}Targets ${SELF_SRCS}) + +xo_pybind11_dependency(${SELF_LIB} reactor) diff --git a/src/pyreactor/pyreactor.cpp b/src/pyreactor/pyreactor.cpp new file mode 100644 index 00000000..5f8cf77b --- /dev/null +++ b/src/pyreactor/pyreactor.cpp @@ -0,0 +1,141 @@ +/* @file ReactorPy.cpp */ + +#include "pyreactor.hpp" +#include "xo/pyprintjson/pyprintjson.hpp" +#include "xo/pyreflect/pyreflect.hpp" + +#include "xo/reactor/Reactor.hpp" +#include "xo/reactor/ReactorSource.hpp" +#include "xo/reactor/EventStore.hpp" +#include "xo/reactor/Sink.hpp" +#include "xo/webutil/StreamEndpointDescr.hpp" +//#include "time/Time.hpp" + +//#include "xo/pyutil/pytime.hpp" +#include "xo/pyutil/pyutil.hpp" + +//#include +//#include +#include + +namespace xo { + using xo::json::PrintJsonSingleton; + using xo::fn::CallbackId; + using xo::ref::Refcount; + using xo::ref::rp; + using xo::time::utc_nanos; + using xo::tostr; + namespace py = pybind11; + + namespace reactor { + PYBIND11_MODULE(PYREACTOR_MODULE_NAME(), m) { + /* e.g. for TypeDescr */ + PYREFLECT_IMPORT_MODULE(); //py::module_::import("pyreflect"); + PYPRINTJSON_IMPORT_MODULE(); //py::module_::import("pyprintjson"); + + /* module docstring */ + m.doc() = "pybind11 plugin for xo.reactor"; + + m.def("time2str", [](utc_nanos tm) { return tostr(tm); }); + + /* TODO: if we write pycallback/, then CallbackId wrapper belongs there */ + py::class_(m, "CallbackId"); + + py::class_>(m, "AbstractEventProcessor") + .def_property("name", + &AbstractEventProcessor::name, + &AbstractEventProcessor::set_name) + .def("reference_counter", [](AbstractEventProcessor const & x) { return x.reference_counter(); }) + .def("memory_address", [](AbstractEventProcessor const & x) { return (void*)&x; }) + .def("map_network", [](AbstractEventProcessor & x) { return AbstractEventProcessor::map_network(&x); }) + .def("__repr__", &AbstractEventProcessor::display_string); + + py::class_>(m, "AbstractSource") + .def_property_readonly("source_ev_type", &AbstractSource::source_ev_type) + .def_property_readonly("is_volatile", &AbstractSource::is_volatile) + .def_property_readonly("n_out_ev", &AbstractSource::n_out_ev) + .def_property_readonly("n_queued_out_ev", &AbstractSource::n_queued_out_ev) + .def("attach_sink", &AbstractSource::attach_sink) + .def("detach_sink", &AbstractSource::detach_sink) + /* editor bait: websock_endpoint_descr */ + .def("stream_endpoint_descr", &AbstractSource::stream_endpoint_descr) + .def("deliver_one", &AbstractSource::deliver_one) + .def("deliver_n", &AbstractSource::deliver_n, + py::arg("n")); + + py::class_>(m, "AbstractSink") + //.cdef("__repr__", &AbstractSink::display_string) + .def_property_readonly("sink_ev_type", &AbstractSink::sink_ev_type) + .def_property_readonly("n_in_ev", &AbstractSink::n_in_ev) + .def("attach_source", &AbstractSink::attach_source); + + py::class_> + (m, "ReactorSource") + .def_property_readonly("is_empty", &ReactorSource::is_empty) + .def_property_readonly("is_nonempty", &ReactorSource::is_nonempty) + .def_property_readonly("is_exhausted", &ReactorSource::is_exhausted) + .def_property_readonly("sim_current_tm", &ReactorSource::sim_current_tm) + .def_property("debug_sim_flag", + &ReactorSource::debug_sim_flag, + &ReactorSource::set_debug_sim_flag); + + py::class_> + (m, "AbstractEventStore") + .def_property_readonly("empty", &AbstractEventStore::empty) + .def_property_readonly("size", &AbstractEventStore::size) + .def("http_snapshot", + [](AbstractEventStore & self) { + std::stringstream ss; + self.http_snapshot(PrintJsonSingleton::instance(), &ss); + return ss.str(); + }) + .def("http_endpoint_descr", + [](AbstractEventStore & self, std::string const & url_prefix) { + return self.http_endpoint_descr(PrintJsonSingleton::instance(), url_prefix); + }, + py::arg("url_prefix")) + .def("clear", + &AbstractEventStore::clear); + + py::class_> + (m, "Reactor") + .def("add_source", + [](Reactor & self, rp src) { + return self.add_source(src.borrow()); + }) + .def("remove_source", + [](Reactor & self, rp src) { + return self.remove_source(src.borrow()); + }) + .def("run_one", &Reactor::run_one) + .def("run_n", &Reactor::run_n, py::arg("n")); + +#ifdef NOT_IN_USE // trying removed code in ProcessPy.cpp instead for now + /* prints + * std::pair + * pairs + */ + m.def("make_realization_printer", + [] + { + return new SinkToConsole>(); + }); + + py::class_>, + AbstractSink, + xo::ref::rp>>> + (m, "SinkToConsole"); +#endif + } /*pyreactor*/ + } /*namespace reactor*/ +} /*namespace xo*/ + +/* end ReactorPy.cpp */ diff --git a/src/pyreactor/pyreactor.hpp.in b/src/pyreactor/pyreactor.hpp.in new file mode 100644 index 00000000..edbb2e78 --- /dev/null +++ b/src/pyreactor/pyreactor.hpp.in @@ -0,0 +1,25 @@ +/* @file pyreactor.hpp + * + * automatically generated from src/pyreflect/pyreactor.hpp.in + * see src/pyreactor/CMakeLists.txt + */ + +/* python requires module name = library name + * example: + * PYBIND11_MODULE(PYREACTOR_MODULE_NAME(), m) { ... } + */ +#define PYREACTOR_MODULE_NAME() @SELF_LIBRARY_NAME@ + +/* example: + * py::module_::import(PYREACTOR_MODULE_NAME_STR) + */ +#define PYREACTOR_MODULE_NAME_STR "@SELF_LIBRARY_NAME@" + +/* example: + * PYREACTOR_IMPORT_MODULE() + * replaces + * py::module_::import("pyreactor") + */ +#define PYREACTOR_IMPORT_MODULE() py::module_::import("@SELF_LIBRARY_NAME@") + +/* end pyreactor.hpp */