diff --git a/.github/ci-hpc-config.yml b/.github/ci-hpc-config.yml index 9eb86ed45..19acb3b16 100644 --- a/.github/ci-hpc-config.yml +++ b/.github/ci-hpc-config.yml @@ -30,12 +30,12 @@ gnu-14.2.0: -DENABLE_ALL_TESTS=ON -DBoost_ROOT=/usr/local/apps/boost/1.87.0/GNU/14.2 -DBoost_INCLUDE_DIR=/usr/local/apps/boost/1.87.0/GNU/14.2/include - -DBoost_PYTHON310_LIBRARY_RELEASE=/usr/local/apps/boost/1.87.0/GNU/14.2/lib/libboost_python312.so -DPython3_ROOT_DIR=/usr/local/apps/python3/3.12.9-01 -DPython3_EXECUTABLE=/usr/local/apps/python3/3.12.9-01/bin/python3 -DPython3_INCLUDE_DIRS=/usr/local/apps/python3/3.12.9-01/include/python3.12 -DPython3_LIBRARIES=/usr/local/apps/python3/3.12.9-01/lib64/libpython3.12.so -DPython3_LIBRARY_DIRS=/usr/local/apps/python3/3.12.9-01/lib64 + -DCMAKE_PREFIX_PATH=/usr/local/apps/python3/3.12.11-01/lib/python3.12/site-packages/pybind11/share/cmake/pybind11 ctest_options: # disable s_http* tests - -L nightly -E '(s_http)' @@ -54,15 +54,15 @@ gnu-12.2.0: cmake_options: - >- -DCMAKE_VERBOSE_MAKEFILE=ON - -DENABLE_ALL_TESTS=ON - -DBoost_ROOT=/usr/local/apps/boost/1.81.0/GNU/12.2 - -DBoost_INCLUDE_DIR=/usr/local/apps/boost/1.81.0/GNU/12.2/include - -DBoost_PYTHON310_LIBRARY_RELEASE=/usr/local/apps/boost/1.81.0/GNU/12.2/lib/libboost_python310.so - -DPython3_ROOT_DIR=/usr/local/apps/python3/3.10.10-01 - -DPython3_EXECUTABLE=/usr/local/apps/python3/3.10.10-01/bin/python3 - -DPython3_INCLUDE_DIRS=/usr/local/apps/python3/3.10.10-01/include/python3.10 - -DPython3_LIBRARIES=/usr/local/apps/python3/3.10.10-01/lib64/libpython3.10.so + -DENABLE_ALL_TESTS=ON + -DBoost_ROOT=/usr/local/apps/boost/1.81.0/GNU/12.2 + -DBoost_INCLUDE_DIR=/usr/local/apps/boost/1.81.0/GNU/12.2/include + -DPython3_ROOT_DIR=/usr/local/apps/python3/3.10.10-01 + -DPython3_EXECUTABLE=/usr/local/apps/python3/3.10.10-01/bin/python3 + -DPython3_INCLUDE_DIRS=/usr/local/apps/python3/3.10.10-01/include/python3.10 + -DPython3_LIBRARIES=/usr/local/apps/python3/3.10.10-01/lib64/libpython3.10.so -DPython3_LIBRARY_DIRS=/usr/local/apps/python3/3.10.10-01/lib64 + -DCMAKE_PREFIX_PATH=/usr/local/apps/python3/3.10.10-01/lib/python3.10/site-packages/pybind11/share/cmake/pybind11 ctest_options: # disable s_http* tests - -L nightly -E '(s_http)' @@ -82,14 +82,14 @@ gnu-8.5.0: - >- -DCMAKE_VERBOSE_MAKEFILE=ON -DENABLE_ALL_TESTS=ON - -DBoost_ROOT=/usr/local/apps/boost/1.84.0/GNU/8.5 - -DBoost_INCLUDE_DIR=/usr/local/apps/boost/1.84.0/GNU/8.5/include - -DBoost_PYTHON310_LIBRARY_RELEASE=/usr/local/apps/boost/1.84.0/GNU/8.5/lib/libboost_python311.so + -DBoost_ROOT=/usr/local/apps/boost/1.84.0/GNU/8.5 + -DBoost_INCLUDE_DIR=/usr/local/apps/boost/1.84.0/GNU/8.5/include -DPython3_ROOT_DIR=/usr/local/apps/python3/3.11.8-01 - -DPython3_EXECUTABLE=/usr/local/apps/python3/3.11.8-01/bin/python3 + -DPython3_EXECUTABLE=/usr/local/apps/python3/3.11.8-01/bin/python3 -DPython3_INCLUDE_DIRS=/usr/local/apps/python3/3.11.8-01/include/python3.11 - -DPython3_LIBRARIES=/usr/local/apps/python3/3.11.8-01/lib64/libpython3.11.so + -DPython3_LIBRARIES=/usr/local/apps/python3/3.11.8-01/lib64/libpython3.11.so -DPython3_LIBRARY_DIRS=/usr/local/apps/python3/3.11.8-01/lib64 + -DCMAKE_PREFIX_PATH=/usr/local/apps/python3/3.11.8-01/lib/python3.11/site-packages/pybind11/share/cmake/pybind11 ctest_options: # disable s_http* tests - -L nightly -E '(s_http)' @@ -112,12 +112,12 @@ intel-2025.0.1: -DCMAKE_CXX_COMPILER=icpx -DBoost_ROOT=/usr/local/apps/boost/1.87.0/INTEL/2025.0 -DBoost_INCLUDE_DIR=/usr/local/apps/boost/1.87.0/INTEL/2025.0/include - -DBoost_PYTHON310_LIBRARY_RELEASE=/usr/local/apps/boost/1.87.0/INTEL/2025.0/lib/libboost_python312.so -DPython3_ROOT_DIR=/usr/local/apps/python3/3.12.9-01 -DPython3_EXECUTABLE=/usr/local/apps/python3/3.12.9-01/bin/python3 -DPython3_INCLUDE_DIRS=/usr/local/apps/python3/3.12.9-01/include/python3.12 -DPython3_LIBRARIES=/usr/local/apps/python3/3.12.9-01/lib64/libpython3.12.so -DPython3_LIBRARY_DIRS=/usr/local/apps/python3/3.12.9-01/lib64 + -DCMAKE_PREFIX_PATH=/usr/local/apps/python3/3.12.9-01/lib/python3.12/site-packages/pybind11/share/cmake/pybind11 ctest_options: # disable s_http* tests - -L nightly -E '(s_http)' @@ -140,12 +140,12 @@ intel-2021.4.0: -DCMAKE_CXX_COMPILER=icpx -DBoost_ROOT=/usr/local/apps/boost/1.81.0/INTEL/2021.4 -DBoost_INCLUDE_DIR=/usr/local/apps/boost/1.81.0/INTEL/2021.4/include - -DBoost_PYTHON310_LIBRARY_RELEASE=/usr/local/apps/boost/1.81.0/INTEL/2021.4/lib/libboost_python310.so -DPython3_ROOT_DIR=/usr/local/apps/python3/3.10.10-01 -DPython3_EXECUTABLE=/usr/local/apps/python3/3.10.10-01/bin/python3 -DPython3_INCLUDE_DIRS=/usr/local/apps/python3/3.10.10-01/include/python3.10 -DPython3_LIBRARIES=/usr/local/apps/python3/3.10.10-01/lib64/libpython3.10.so -DPython3_LIBRARY_DIRS=/usr/local/apps/python3/3.10.10-01/lib64 + -DCMAKE_PREFIX_PATH=/usr/local/apps/python3/3.10.10-01/lib/python3.10/site-packages/pybind11/share/cmake/pybind11 ctest_options: - -L nightly -E py3_ @@ -166,12 +166,12 @@ nvidia-24.11: -DENABLE_ALL_TESTS=ON -DBoost_ROOT=/usr/local/apps/boost/1.87.0/NVIDIA/24.11 -DBoost_INCLUDE_DIR=/usr/local/apps/boost/1.87.0/NVIDIA/24.11/include - -DBoost_PYTHON310_LIBRARY_RELEASE=/usr/local/apps/boost/1.87.0/NVIDIA/24.11/lib/libboost_python312.so -DPython3_ROOT_DIR=/usr/local/apps/python3/3.12.9-01 -DPython3_EXECUTABLE=/usr/local/apps/python3/3.12.9-01/bin/python3 -DPython3_INCLUDE_DIRS=/usr/local/apps/python3/3.12.9-01/include/python3.12 -DPython3_LIBRARIES=/usr/local/apps/python3/3.12.9-01/lib64/libpython3.12.so -DPython3_LIBRARY_DIRS=/usr/local/apps/python3/3.12.9-01/lib64 + -DCMAKE_PREFIX_PATH=/usr/local/apps/python3/3.12.9-01/lib/python3.12/site-packages/pybind11/share/cmake/pybind11 ctest_options: # disable all tests - --version @@ -193,12 +193,12 @@ nvidia-22.11: -DENABLE_ALL_TESTS=ON -DBoost_ROOT=/usr/local/apps/boost/1.81.0/NVIDIA/22.11 -DBoost_INCLUDE_DIR=/usr/local/apps/boost/1.81.0/NVIDIA/22.11/include - -DBoost_PYTHON310_LIBRARY_RELEASE=/usr/local/apps/boost/1.81.0/NVIDIA/22.11/lib/libboost_python310.so -DPython3_ROOT_DIR=/usr/local/apps/python3/3.10.10-01 -DPython3_EXECUTABLE=/usr/local/apps/python3/3.10.10-01/bin/python3 -DPython3_INCLUDE_DIRS=/usr/local/apps/python3/3.10.10-01/include/python3.10 -DPython3_LIBRARIES=/usr/local/apps/python3/3.10.10-01/lib64/libpython3.10.so -DPython3_LIBRARY_DIRS=/usr/local/apps/python3/3.10.10-01/lib64 + -DCMAKE_PREFIX_PATH=/usr/local/apps/python3/3.10.10-01/lib/python3.10/site-packages/pybind11/share/cmake/pybind11 ctest_options: # disable all tests - --version @@ -220,12 +220,12 @@ aocc-4.0.0: -DENABLE_ALL_TESTS=ON -DBoost_ROOT=/usr/local/apps/boost/1.81.0/AMD/4.0 -DBoost_INCLUDE_DIR=/usr/local/apps/boost/1.81.0/AMD/4.0/include - -DBoost_PYTHON310_LIBRARY_RELEASE=/usr/local/apps/boost/1.81.0/AMD/4.0/lib/libboost_python310.so -DPython3_ROOT_DIR=/usr/local/apps/python3/3.10.10-01 -DPython3_EXECUTABLE=/usr/local/apps/python3/3.10.10-01/bin/python3 -DPython3_INCLUDE_DIRS=/usr/local/apps/python3/3.10.10-01/include/python3.10 -DPython3_LIBRARIES=/usr/local/apps/python3/3.10.10-01/lib64/libpython3.10.so -DPython3_LIBRARY_DIRS=/usr/local/apps/python3/3.10.10-01/lib64 + -DCMAKE_PREFIX_PATH=/usr/local/apps/python3/3.10.10-01/lib/python3.10/site-packages/pybind11/share/cmake/pybind11 ctest_options: # disable all tests - --version diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 1374efcfc..e09cc8a8f 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -123,6 +123,25 @@ if (ENABLE_PYTHON) endif() +# ========================================================================================= +# pybind11 +# ========================================================================================= +if (ENABLE_PYTHON) + + ecbuild_info( "Locating pybind11" ) + + find_package(pybind11 2.10.3 REQUIRED) + + ecbuild_info( "pybind11 details:" ) + ecbuild_info( " * pybind11_FOUND : ${pybind11_FOUND}" ) + ecbuild_info( " * pybind11_INCLUDE_DIRS : ${pybind11_INCLUDE_DIRS}" ) + ecbuild_info( " * pybind11_VERSION : ${pybind11_VERSION}" ) + ecbuild_info( " * pybind11_VERSION_MAJOR: ${pybind11_VERSION_MAJOR}" ) + ecbuild_info( " * pybind11_VERSION_MINOR: ${pybind11_VERSION_MINOR}" ) + ecbuild_info( " * pybind11_VERSION_PATCH: ${pybind11_VERSION_PATCH}" ) + +endif() + # ========================================================================================= # Boost # ========================================================================================= @@ -174,21 +193,6 @@ if ( Boost_MINOR_VERSION GREATER_EQUAL 86 ) list(APPEND _boost_needed_libs process) endif() -if (ENABLE_PYTHON) - # The following is used to find Boost.python library, as the library name changes with python version - if ( Boost_MINOR_VERSION GREATER 66 ) - # cmake 3.15 - # see: https://gitlab.kitware.com/cmake/cmake/issues/19656 - # INTERFACE_LIBRARY targets may only have whitelisted properties. - set(_python_base_version "${Python3_VERSION_MAJOR}${Python3_VERSION_MINOR}") - else() - set(_python_base_version "${Python3_VERSION_MAJOR}") - endif() - set(ECFLOW_BOOST_PYTHON_COMPONENT "python${_python_base_version}") - - list(APPEND _boost_needed_libs ${ECFLOW_BOOST_PYTHON_COMPONENT}) -endif() - if(HAVE_TESTS) # HAVE_TESTS is defined if ecbuild ENABLE_TESTS is set, (by default this is set) list(APPEND _boost_needed_libs unit_test_framework test_exec_monitor ) endif() diff --git a/libs/attribute/src/ecflow/attribute/CronAttr.hpp b/libs/attribute/src/ecflow/attribute/CronAttr.hpp index ff1ff71be..f5d115d5e 100644 --- a/libs/attribute/src/ecflow/attribute/CronAttr.hpp +++ b/libs/attribute/src/ecflow/attribute/CronAttr.hpp @@ -77,14 +77,10 @@ class CronAttr { const TimeSeries& time() const { return timeSeries_; } const TimeSeries& time_series() const { return timeSeries_; } bool last_day_of_month() const { return last_day_of_month_; } - std::vector::const_iterator week_days_begin() const { return weekDays_.begin(); } - std::vector::const_iterator week_days_end() const { return weekDays_.end(); } - std::vector::const_iterator last_week_days_of_month_begin() const { return last_week_days_of_month_.begin(); } - std::vector::const_iterator last_week_days_end_of_month_end() const { return last_week_days_of_month_.end(); } - std::vector::const_iterator days_of_month_begin() const { return daysOfMonth_.begin(); } - std::vector::const_iterator days_of_month_end() const { return daysOfMonth_.end(); } - std::vector::const_iterator months_begin() const { return months_.begin(); } - std::vector::const_iterator months_end() const { return months_.end(); } + const std::vector& week_days() const { return weekDays_; } + const std::vector& last_week_days_of_month() const { return last_week_days_of_month_; } + const std::vector& days_of_month() const { return daysOfMonth_; } + const std::vector& months() const { return months_; } std::string name() const; // for display/gui std::string toString() const; diff --git a/libs/client/src/ecflow/client/ClientInvoker.hpp b/libs/client/src/ecflow/client/ClientInvoker.hpp index 74ad2ec21..c02628539 100644 --- a/libs/client/src/ecflow/client/ClientInvoker.hpp +++ b/libs/client/src/ecflow/client/ClientInvoker.hpp @@ -458,6 +458,8 @@ class ClientInvoker { */ bool is_not_retrying(const ClientToServerCmd& cmd) const; + auto& changed_node_paths() { return server_reply_.changed_nodes(); } + private: /** * @return 1 when command is selected; 0 if no command is selected (e.g. --help) diff --git a/libs/pyext/CMakeLists.txt b/libs/pyext/CMakeLists.txt index d47781a0b..2a57dd6b4 100644 --- a/libs/pyext/CMakeLists.txt +++ b/libs/pyext/CMakeLists.txt @@ -55,6 +55,7 @@ set(srcs ../src/ecflow/python/ClientDoc.hpp ../src/ecflow/python/DefsDoc.hpp ../src/ecflow/python/Edit.hpp + ../src/ecflow/python/ExportCollections.hpp ../src/ecflow/python/GlossaryDoc.hpp ../src/ecflow/python/NodeAttrDoc.hpp ../src/ecflow/python/NodeUtil.hpp @@ -67,6 +68,7 @@ set(srcs ../src/ecflow/python/EcfExt.cpp ../src/ecflow/python/Edit.cpp ../src/ecflow/python/ExportClient.cpp + ../src/ecflow/python/ExportCollections.cpp ../src/ecflow/python/ExportCore.cpp ../src/ecflow/python/ExportDefs.cpp ../src/ecflow/python/ExportNode.cpp @@ -127,6 +129,6 @@ set(s_tests s_TestSslSetup ) -if (Python3_FOUND AND (Boost_PYTHON3_FOUND OR Boost_PYTHON${Python3_VERSION_MAJOR}${Python3_VERSION_MINOR}_FOUND)) +if (Python3_FOUND AND pybind11_FOUND) add_subdirectory( python3 ) endif() diff --git a/libs/pyext/python3/CMakeLists.txt b/libs/pyext/python3/CMakeLists.txt index a4a5bb4c0..497a16698 100644 --- a/libs/pyext/python3/CMakeLists.txt +++ b/libs/pyext/python3/CMakeLists.txt @@ -32,7 +32,7 @@ ecbuild_add_library( ecflow_all libsimulator Python3::Module - Boost::${ECFLOW_BOOST_PYTHON_COMPONENT} + pybind11::headers $<$:OpenSSL::SSL> CXXFLAGS $<$:-Wno-macro-redefined> diff --git a/libs/pyext/script.py b/libs/pyext/script.py deleted file mode 100644 index ee6fbbe5b..000000000 --- a/libs/pyext/script.py +++ /dev/null @@ -1,11 +0,0 @@ -## Copyright 2009- ECMWF. -## This software is licensed under the terms of the Apache Licence version 2.0 -## which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -## In applying this licence, ECMWF does not waive the privileges and immunities -## granted to it by virtue of its status as an intergovernmental organisation -## nor does it submit to any jurisdiction. - -# -# -print 'Hello World !' -number = 42 diff --git a/libs/pyext/src/ecflow/python/EcfExt.cpp b/libs/pyext/src/ecflow/python/EcfExt.cpp index 46d36fd0d..228b1e6db 100644 --- a/libs/pyext/src/ecflow/python/EcfExt.cpp +++ b/libs/pyext/src/ecflow/python/EcfExt.cpp @@ -8,31 +8,35 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/core/Version.hpp" #include "ecflow/python/PythonBinding.hpp" -void export_Core(); -void export_NodeAttr(); -void export_Node(); -void export_Task(); -void export_SuiteAndFamily(); -void export_Defs(); -void export_Client(); +void export_Collections(py::module& m); +void export_Core(py::module& m); +void export_NodeAttr(py::module& m); +void export_Node(py::module& m); +void export_Task(py::module& m); +void export_SuiteAndFamily(py::module& m); +void export_Defs(py::module& m); +void export_Client(py::module& m); -// See: http://wiki.python.org/moin/boost.python/HowTo#boost.function_objects -BOOST_PYTHON_MODULE(ecflow) { - py::docstring_options doc_options(true, // show the docstrings from here - true, // show Python signatures. - false // Don't mention the C++ method signatures in the generated docstrings - ); - py::scope().attr("__doc__") = - "The ecflow module provides the python bindings/api for creating definition structure " - "and communicating with the server."; +PYBIND11_MODULE(ecflow, m) { + py::options options; + options.enable_user_defined_docstrings(); // show the docstrings from here + options.enable_function_signatures(); // show Python signatures. + options.enable_enum_members_docstring(); - export_Core(); - export_NodeAttr(); - export_Node(); - export_Task(); - export_SuiteAndFamily(); - export_Defs(); - export_Client(); + m.doc() = "The ecflow module provides the python bindings/api for creating definition structure " + "and communicating with the server."; + + m.attr("__version__") = ecf::Version::base(); + + export_Collections(m); + export_Core(m); + export_NodeAttr(m); + export_Node(m); + export_Task(m); + export_SuiteAndFamily(m); + export_Defs(m); + export_Client(m); } diff --git a/libs/pyext/src/ecflow/python/Edit.cpp b/libs/pyext/src/ecflow/python/Edit.cpp index ce95e16a6..842103d24 100644 --- a/libs/pyext/src/ecflow/python/Edit.cpp +++ b/libs/pyext/src/ecflow/python/Edit.cpp @@ -17,23 +17,12 @@ Edit::Edit(const py::dict& dict) { pyutil_dict_to_str_vec(dict, vec_); } -Edit::Edit(const py::dict& dict, const py::dict& dict2) { - pyutil_dict_to_str_vec(dict, vec_); - pyutil_dict_to_str_vec(dict2, vec_); + +Edit::Edit(const py::kwargs& kwargs) { + pyutil_dict_to_str_vec(kwargs, vec_); } -py::object Edit::init(py::tuple args, py::dict kw) { - // cout << "Edit::init args: " << len(args) << " kwargs " << len(kw) << "\n"; - // args[0] is Edit(i.e self) - for (int i = 1; i < len(args); ++i) { - if (py::extract(args[i]).check()) { - py::dict d = py::extract(args[i]); - return args[0].attr("__init__")(d, kw); // calls -> .def(init() -> Edit(dict,dict) - } - else { - throw std::runtime_error("Edit::Edit: only accepts dictionary and key word arguments"); - } - } - py::tuple rest(args.slice(1, py::_)); - return args[0].attr("__init__")(kw); // calls -> .def(init() -> Edit(const py::dict& dict) +Edit::Edit(const py::dict& dict, const py::kwargs& kwargs) { + pyutil_dict_to_str_vec(dict, vec_); + pyutil_dict_to_str_vec(kwargs, vec_); } diff --git a/libs/pyext/src/ecflow/python/Edit.hpp b/libs/pyext/src/ecflow/python/Edit.hpp index 2b1ff3d6f..54aece0bd 100644 --- a/libs/pyext/src/ecflow/python/Edit.hpp +++ b/libs/pyext/src/ecflow/python/Edit.hpp @@ -19,10 +19,12 @@ class Edit { public: explicit Edit(const py::dict& dict); - Edit(const py::dict& dict, const py::dict& dict2); + explicit Edit(const py::kwargs& kw); + Edit(const py::dict& dict, const py::kwargs& kw); + const std::vector& variables() const { return vec_; } + static std::string to_string() { return "edit"; } - static py::object init(py::tuple args, py::dict kw); private: std::vector vec_; diff --git a/libs/pyext/src/ecflow/python/ExportClient.cpp b/libs/pyext/src/ecflow/python/ExportClient.cpp index 20e4857cd..d6c3028ff 100644 --- a/libs/pyext/src/ecflow/python/ExportClient.cpp +++ b/libs/pyext/src/ecflow/python/ExportClient.cpp @@ -25,59 +25,75 @@ #include "ecflow/python/PythonBinding.hpp" #include "ecflow/python/PythonUtil.hpp" -// See: http://wiki.python.org/moin/boost.python/HowTo#boost.function_objects +namespace { -void set_host_port(ClientInvoker* self, const std::string& host, int port) { +/** + * This class acts as a scope guard, ensuring that the ClientInvoker CLI is set to a desired state within the scope. + * + * Upon entering the scope, the current state is recorded and the then set to the desired value (default is true). + * On scope exit, the state is set back to its original value. + */ +class ClientInvokerScopeState { +public: + explicit ClientInvokerScopeState(ClientInvoker* self, bool desired = true) + : _self(self), + previous_(self->cli()), // record the previous state + desired_(desired) { + self->set_cli(desired_); // set the desired state + } + ~ClientInvokerScopeState() { _self->set_cli(previous_); } // restore the previous state + +private: + ClientInvoker* _self; + bool previous_{}; + bool desired_{}; +}; + +void ClientInvoker_set_host_port(ClientInvoker* self, const std::string& host, int port) { self->set_host_port(host, ecf::convert_to(port)); } -void set_retry_connection_period(ClientInvoker* self, int seconds) { +void ClientInvoker_set_retry_connection_period(ClientInvoker* self, int seconds) { self->set_retry_connection_period(std::chrono::seconds(seconds)); } -std::string version(ClientInvoker* self) { +std::string ClientInvoker_version(ClientInvoker* self) { return ecf::Version::full(); } -std::string server_version(ClientInvoker* self) { +std::string ClientInvoker_server_version(ClientInvoker* self) { self->server_version(); return self->get_string(); } -const std::string& query(ClientInvoker* self, - const std::string& query_type, - const std::string& path_to_attribute, - const std::string& attribute) { +const std::string& ClientInvoker_query(ClientInvoker* self, + const std::string& query_type, + const std::string& path_to_attribute, + const std::string& attribute) { self->query(query_type, path_to_attribute, attribute); return self->get_string(); } -const std::string& query1(ClientInvoker* self, const std::string& query_type, const std::string& path_to_attribute) { - self->query(query_type, path_to_attribute, ""); - return self->get_string(); -} -// const std::string& get_log(ClientInvoker* self) { self->getLog(); return self->get_string();} - -const std::string& get_log(ClientInvoker* self, int lastLines) { +const std::string& ClientInvoker_get_log(ClientInvoker* self, int lastLines) { self->getLog(lastLines); return self->get_string(); } -const std::string& edit_script_edit(ClientInvoker* self, const std::string& absNodePath) { +const std::string& ClientInvoker_edit_script_edit(ClientInvoker* self, const std::string& absNodePath) { self->edit_script_edit(absNodePath); return self->get_string(); } -const std::string& edit_script_preprocess(ClientInvoker* self, const std::string& absNodePath) { +const std::string& ClientInvoker_edit_script_preprocess(ClientInvoker* self, const std::string& absNodePath) { self->edit_script_preprocess(absNodePath); return self->get_string(); } -int edit_script_submit(ClientInvoker* self, - const std::string& absNodePath, - const py::list& name_values, - const py::list& lines, - bool alias = false, - bool run = true) { +int ClientInvoker_edit_script_submit(ClientInvoker* self, + const std::string& absNodePath, + const py::list& name_values, + const py::list& lines, + bool alias = false, + bool run = true) { std::vector file_contents; pyutil_list_to_str_vec(lines, file_contents); @@ -92,59 +108,43 @@ int edit_script_submit(ClientInvoker* self, return self->edit_script_submit(absNodePath, used_variables, file_contents, alias, run); } -namespace /* __ANONYMOUS__ */ { - -py::object convert_to_pyobject(const std::string& s, bool as_bytes) { - py::object result; - if (as_bytes) { - result = py::object(py::handle<>(PyBytes_FromObject( - PyMemoryView_FromMemory(const_cast(s.data()), static_cast(s.size()), PyBUF_READ)))); - } - else { - result = py::object(py::handle<>(PyUnicode_FromStringAndSize(s.data(), static_cast(s.size())))); - } - return result; -} - -} // namespace - -py::object get_file(ClientInvoker* self, - const std::string& absNodePath, - const std::string& file_type = "script", - const std::string& max_lines = "10000", - bool as_bytes = false) { +py::object ClientInvoker_get_file(ClientInvoker* self, + const std::string& absNodePath, + const std::string& file_type = "script", + const std::string& max_lines = "10000", + bool as_bytes = false) { self->file(absNodePath, file_type, max_lines); const std::string& s = self->get_string(); - return convert_to_pyobject(s, as_bytes); -} - -/// Set the CLI to enable output to standard out -class CliSetter { -public: - explicit CliSetter(ClientInvoker* self) - : _self(self) { - self->set_cli(true); - } - ~CliSetter() { _self->set_cli(false); } + auto convert_string_to_pyobject = [](const std::string& s, bool as_bytes) -> py::object { + PyObject* content = nullptr; + if (as_bytes) { + PyObject* memory = + PyMemoryView_FromMemory(const_cast(s.data()), static_cast(s.size()), PyBUF_READ); + content = PyBytes_FromObject(memory); + } + else { + content = PyUnicode_FromStringAndSize(s.data(), static_cast(s.size())); + } + return py::reinterpret_steal(content); + }; -private: - ClientInvoker* _self; -}; + return convert_string_to_pyobject(s, as_bytes); +} -const std::string& stats(ClientInvoker* self, bool to_stdout = true) { +const std::string& ClientInvoker_stats(ClientInvoker* self, bool to_stdout = true) { self->stats(); if (to_stdout) { std::cout << self->server_reply().get_string() << std::endl; } return self->server_reply().get_string(); } -BOOST_PYTHON_FUNCTION_OVERLOADS(stats_overloads, stats, 1, 2) -void stats_reset(ClientInvoker* self) { +void ClientInvoker_stats_reset(ClientInvoker* self) { self->stats_reset(); } -py::list suites(ClientInvoker* self) { + +py::list ClientInvoker_suites(ClientInvoker* self) { self->suites(); const std::vector& the_suites = self->server_reply().get_string_vec(); py::list list; @@ -155,233 +155,271 @@ py::list suites(ClientInvoker* self) { return list; } -bool news_local(ClientInvoker* self) { +bool ClientInvoker_news_local(ClientInvoker* self) { self->news_local(); return self->get_news(); } -void free_trigger_dep(ClientInvoker* self, const std::string& path) { +void ClientInvoker_free_trigger_dep(ClientInvoker* self, const std::string& path) { self->freeDep(path, true /*trigger*/, false /*all*/, false /*date*/, false /*time*/); } -void free_date_dep(ClientInvoker* self, const std::string& path) { + +void ClientInvoker_free_date_dep(ClientInvoker* self, const std::string& path) { self->freeDep(path, false /*trigger*/, false /*all*/, true /*date*/, false /*time*/); } -void free_time_dep(ClientInvoker* self, const std::string& path) { + +void ClientInvoker_free_time_dep(ClientInvoker* self, const std::string& path) { self->freeDep(path, false /*trigger*/, false /*all*/, false /*date*/, true /*time*/); } -void free_all_dep(ClientInvoker* self, const std::string& path) { + +void ClientInvoker_free_all_dep(ClientInvoker* self, const std::string& path) { self->freeDep(path, false /*trigger*/, true /*all*/, false /*date*/, false /*time*/); } -void free_trigger_dep1(ClientInvoker* self, const py::list& list) { + +void ClientInvoker_free_trigger_dep1(ClientInvoker* self, const py::list& list) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->freeDep(paths, true /*trigger*/, false /*all*/, false /*date*/, false /*time*/); } -void free_date_dep1(ClientInvoker* self, const py::list& list) { + +void ClientInvoker_free_date_dep1(ClientInvoker* self, const py::list& list) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->freeDep(paths, false /*trigger*/, false /*all*/, true /*date*/, false /*time*/); } -void free_time_dep1(ClientInvoker* self, const py::list& list) { + +void ClientInvoker_free_time_dep1(ClientInvoker* self, const py::list& list) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->freeDep(paths, false /*trigger*/, false /*all*/, false /*date*/, true /*time*/); } -void free_all_dep1(ClientInvoker* self, const py::list& list) { + +void ClientInvoker_free_all_dep1(ClientInvoker* self, const py::list& list) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->freeDep(paths, false /*trigger*/, true /*all*/, false /*date*/, false /*time*/); } -void force_state(ClientInvoker* self, const std::string& path, NState::State state) { +void ClientInvoker_force_state(ClientInvoker* self, const std::string& path, NState::State state) { self->force(path, NState::toString(state), false); } -void force_states(ClientInvoker* self, const py::list& list, NState::State state) { + +void ClientInvoker_force_states(ClientInvoker* self, const py::list& list, NState::State state) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->force(paths, NState::toString(state), false); } -void force_state_recursive(ClientInvoker* self, const std::string& path, NState::State state) { + +void ClientInvoker_force_state_recursive(ClientInvoker* self, const std::string& path, NState::State state) { self->force(path, NState::toString(state), true); } -void force_states_recursive(ClientInvoker* self, const py::list& list, NState::State state) { + +void ClientInvoker_force_states_recursive(ClientInvoker* self, const py::list& list, NState::State state) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->force(paths, NState::toString(state), true); } -void force_event(ClientInvoker* self, const std::string& path, const std::string& set_or_clear) { + +void ClientInvoker_force_event(ClientInvoker* self, const std::string& path, const std::string& set_or_clear) { self->force(path, set_or_clear); } -void force_events(ClientInvoker* self, const py::list& list, const std::string& set_or_clear) { + +void ClientInvoker_force_events(ClientInvoker* self, const py::list& list, const std::string& set_or_clear) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->force(paths, set_or_clear); } -void run(ClientInvoker* self, const std::string& path, bool force) { +void ClientInvoker_run(ClientInvoker* self, const std::string& path, bool force) { self->run(path, force); } -void runs(ClientInvoker* self, const py::list& list, bool force) { + +void ClientInvoker_runs(ClientInvoker* self, const py::list& list, bool force) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->run(paths, force); } -void requeue(ClientInvoker* self, std::string path, const std::string& option) { + +void ClientInvoker_requeue(ClientInvoker* self, std::string path, const std::string& option) { self->requeue(path, option); } -void requeues(ClientInvoker* self, const py::list& list, const std::string& option) { + +void ClientInvoker_requeue_s(ClientInvoker* self, const py::list& list, const std::string& option) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->requeue(paths, option); } -void suspend(ClientInvoker* self, const std::string& path) { + +void ClientInvoker_suspend(ClientInvoker* self, const std::string& path) { self->suspend(path); } -void suspends(ClientInvoker* self, const py::list& list) { + +void ClientInvoker_suspend_s(ClientInvoker* self, const py::list& list) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->suspend(paths); } -void resume(ClientInvoker* self, const std::string& path) { + +void ClientInvoker_resume(ClientInvoker* self, const std::string& path) { self->resume(path); } -void resumes(ClientInvoker* self, const py::list& list) { + +void ClientInvoker_resume_s(ClientInvoker* self, const py::list& list) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->resume(paths); } -void archive(ClientInvoker* self, const std::string& path) { +void ClientInvoker_archive(ClientInvoker* self, const std::string& path) { self->archive(path); } -void archives(ClientInvoker* self, const py::list& list) { + +void ClientInvoker_archive_s(ClientInvoker* self, const py::list& list) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->archive(paths); } -void restore(ClientInvoker* self, const std::string& path) { + +void ClientInvoker_restore(ClientInvoker* self, const std::string& path) { self->restore(path); } -void restores(ClientInvoker* self, const py::list& list) { + +void ClientInvoker_restore_s(ClientInvoker* self, const py::list& list) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->restore(paths); } -void the_status(ClientInvoker* self, const std::string& path) { +void ClientInvoker_status(ClientInvoker* self, const std::string& path) { self->status(path); } -void statuss(ClientInvoker* self, const py::list& list) { + +void ClientInvoker_status_s(ClientInvoker* self, const py::list& list) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->status(paths); } -void do_kill(ClientInvoker* self, const std::string& path) { + +void ClientInvoker_kill(ClientInvoker* self, const std::string& path) { self->kill(path); } -void do_kills(ClientInvoker* self, const py::list& list) { + +void ClientInvoker_kill_s(ClientInvoker* self, const py::list& list) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->kill(paths); } -const std::string& check(ClientInvoker* self, const std::string& node_path) { + +const std::string& ClientInvoker_check(ClientInvoker* self, const std::string& node_path) { self->check(node_path); return self->get_string(); } -const std::string& checks(ClientInvoker* self, const py::list& list) { + +const std::string& ClientInvoker_check_s(ClientInvoker* self, const py::list& list) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->check(paths); return self->get_string(); } -void delete_node(ClientInvoker* self, const py::list& list, bool force) { +void ClientInvoker_delete_node_s(ClientInvoker* self, const py::list& list, bool force) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->delete_nodes(paths, force); } -void ch_suites(ClientInvoker* self) { - CliSetter cli(self); +void ClientInvoker_ch_suites(ClientInvoker* self) { + ClientInvokerScopeState cli(self); self->ch_suites(); } -void ch_register(ClientInvoker* self, bool auto_add_new_suites, const py::list& list) { + +void ClientInvoker_ch_register(ClientInvoker* self, bool auto_add_new_suites, const py::list& list) { std::vector suites; pyutil_list_to_str_vec(list, suites); self->ch_register(auto_add_new_suites, suites); } -void ch_add(ClientInvoker* self, int client_handle, const py::list& list) { +void ClientInvoker_ch_add(ClientInvoker* self, int client_handle, const py::list& list) { std::vector suites; pyutil_list_to_str_vec(list, suites); self->ch_add(client_handle, suites); } -void ch1_add(ClientInvoker* self, const py::list& list) { + +void ClientInvoker_ch1_add(ClientInvoker* self, const py::list& list) { std::vector suites; pyutil_list_to_str_vec(list, suites); self->ch1_add(suites); } -void ch_remove(ClientInvoker* self, int client_handle, const py::list& list) { +void ClientInvoker_ch_remove(ClientInvoker* self, int client_handle, const py::list& list) { std::vector suites; pyutil_list_to_str_vec(list, suites); self->ch_remove(client_handle, suites); } -void ch1_remove(ClientInvoker* self, const py::list& list) { + +void ClientInvoker_ch1_remove(ClientInvoker* self, const py::list& list) { std::vector suites; pyutil_list_to_str_vec(list, suites); self->ch1_remove(suites); } -/// Need to provide override since the boolean argument is optional. -/// This saves on client python code, on having to specify the optional arg -void replace_1(ClientInvoker* self, const std::string& absNodePath, defs_ptr client_defs) { +void ClientInvoker_replace_1(ClientInvoker* self, const std::string& absNodePath, defs_ptr client_defs) { + // This ClientInvoker function wrapper avoids exposing the optional boolean arguments to the Python API. self->replace_1(absNodePath, client_defs); } -void replace_2(ClientInvoker* self, const std::string& absNodePath, const std::string& path_to_client_defs) { + +void ClientInvoker_replace_2(ClientInvoker* self, + const std::string& absNodePath, + const std::string& path_to_client_defs) { + // This ClientInvoker function wrapper avoids exposing the optional boolean arguments to the Python API. self->replace(absNodePath, path_to_client_defs); } -void order(ClientInvoker* self, const std::string& absNodePath, const std::string& the_order) { +void ClientInvoker_order(ClientInvoker* self, const std::string& absNodePath, const std::string& the_order) { self->order(absNodePath, the_order); } -void alters(ClientInvoker* self, - const py::list& list, - const std::string& alterType, /* one of [ add | change | delete | set_flag | clear_flag ] */ - const std::string& attrType, - const std::string& name = "", - const std::string& value = "") { +void ClientInvoker_alter(ClientInvoker* self, + const std::string& path, + const std::string& alterType, /* one of [ add | change | delete | set_flag | clear_flag ] */ + const std::string& attrType, + const std::string& name = "", + const std::string& value = "") { + self->alter(path, alterType, attrType, name, value); +} + +void ClientInvoker_alter_s(ClientInvoker* self, + const py::list& list, + const std::string& alterType, /* one of [ add | change | delete | set_flag | clear_flag ] */ + const std::string& attrType, + const std::string& name = "", + const std::string& value = "") { std::vector paths; pyutil_list_to_str_vec(list, paths); self->check(paths); self->alter(paths, alterType, attrType, name, value); } -void alter(ClientInvoker* self, - const std::string& path, - const std::string& alterType, /* one of [ add | change | delete | set_flag | clear_flag ] */ - const std::string& attrType, - const std::string& name = "", - const std::string& value = "") { - self->alter(path, alterType, attrType, name, value); + +void ClientInvoker_alter_sort(ClientInvoker* self, + const std::string& path, + const std::string& attribute_name, + bool recursive = true) { + self->alter_sort(std::vector(1, path), attribute_name, recursive); } -void alter_sorts(ClientInvoker* self, const py::list& list, const std::string& attribute_name, bool recursive = true) { +void ClientInvoker_alter_sort_s(ClientInvoker* self, + const py::list& list, + const std::string& attribute_name, + bool recursive = true) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->check(paths); self->alter_sort(paths, attribute_name, recursive); } -void alter_sort(ClientInvoker* self, - const std::string& path, - const std::string& attribute_name, - bool recursive = true) { - self->alter_sort(std::vector(1, path), attribute_name, recursive); -} -void set_child_pid(ClientInvoker* self, int pid) { +void ClientInvoker_set_child_pid(ClientInvoker* self, int pid) { self->set_child_pid(ecf::convert_to(pid)); } -void set_child_init_add_vars(ClientInvoker* self, const py::dict& dict) { +void ClientInvoker_set_child_init_add_vars(ClientInvoker* self, const py::dict& dict) { std::vector> vars; pyutil_dict_to_str_vec(dict, vars); @@ -394,94 +432,78 @@ void set_child_init_add_vars(ClientInvoker* self, const py::dict& dict) { self->set_child_init_add_vars(vec); } -void set_child_init_add_vars2(ClientInvoker* self, const py::list& dict) { +void ClientInvoker_set_child_init_add_vars2(ClientInvoker* self, const py::list& dict) { std::vector vec; pyutil_list_to_str_vec(dict, vec); self->set_child_init_add_vars(vec); } -void set_child_complete_del_vars(ClientInvoker* self, const py::list& dict) { +void ClientInvoker_set_child_complete_del_vars(ClientInvoker* self, const py::list& dict) { std::vector vars; pyutil_list_to_str_vec(dict, vars); self->set_child_complete_del_vars(vars); } -// Context mgr. The expression is evaluated and should result in an object called a ``context manager'' -// with expression [as variable]: -// with-block -// -// . The context manager must have __enter__() and __exit__() methods. -// . The context manager's __enter__() method is called. -// The value returned is assigned to VAR. -// If no 'as VAR' clause is present, the value is simply discarded. -// . The code in BLOCK is executed. -// . If BLOCK raises an exception, the __exit__(type, value, traceback) -// is called with the exception details, the same values returned by sys.exc_info(). -// The method's return value controls whether the exception is re-raised: -// any false value re-raises the exception, and True will result in suppressing it. -// You'll only rarely want to suppress the exception, because if you do the author -// of the code containing the 'with' statement will never realize anything went wrong. -// . If BLOCK didn't raise an exception, the __exit__() method is still called, but type, value, and traceback are all -// None. -// -std::shared_ptr client_enter(std::shared_ptr self) { +std::shared_ptr ClientInvoker_enter(std::shared_ptr self) { return self; } -bool client_exit(std::shared_ptr self, - const py::object& type, - const py::object& value, - const py::object& traceback) { + +bool ClientInvoker_exit(std::shared_ptr self, + const py::object& type, + const py::object& value, + const py::object& traceback) { self->ch1_drop(); return false; } -const std::vector& zombieGet(ClientInvoker* self, int pid) { +const std::vector& ClientInvoker_zombieGet(ClientInvoker* self, int pid) { self->zombieGet(); return self->server_reply().zombies(); } -void zombieFobCli(ClientInvoker* self, const py::list& list) { +void ClientInvoker_zombieFobCli(ClientInvoker* self, const py::list& list) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->zombieFobCliPaths(paths); } -void zombieFailCli(ClientInvoker* self, const py::list& list) { +void ClientInvoker_zombieFailCli(ClientInvoker* self, const py::list& list) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->zombieFailCliPaths(paths); } -void zombieAdoptCli(ClientInvoker* self, const py::list& list) { +void ClientInvoker_zombieAdoptCli(ClientInvoker* self, const py::list& list) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->zombieAdoptCliPaths(paths); } -void zombieBlockCli(ClientInvoker* self, const py::list& list) { +void ClientInvoker_zombieBlockCli(ClientInvoker* self, const py::list& list) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->zombieBlockCliPaths(paths); } -void zombieRemoveCli(ClientInvoker* self, const py::list& list) { +void ClientInvoker_zombieRemoveCli(ClientInvoker* self, const py::list& list) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->zombieRemoveCliPaths(paths); } -void zombieKillCli(ClientInvoker* self, const py::list& list) { +void ClientInvoker_zombieKillCli(ClientInvoker* self, const py::list& list) { std::vector paths; pyutil_list_to_str_vec(list, paths); self->zombieKillCliPaths(paths); } -/// -/// @brief Creates a new instance of ClientInvoker with the specified arguments. -/// -/// This ensures that the ClientInvoker instance is properly initialized considering -/// the environment variable ECF_SSL. -/// -/// @tparam ARGS -/// @param args -/// @return the newly created ClientInvoker instance. +/** + * @brief Creates a new instance of ClientInvoker with the specified arguments. + * + * This ensures that the ClientInvoker instance is properly initialized considering + * the environment variable ECF_SSL. + * + * @tparam ARGS + * @param args + * @return the newly created ClientInvoker instance. + */ template -std::shared_ptr client_invoker_make(const ARGS&... args) { +std::shared_ptr ClientInvoker_make(const ARGS&... args) { // (1) Create a new instance of ClientInvoker auto ci = std::make_shared(args...); @@ -500,7 +522,7 @@ std::shared_ptr client_invoker_make(const ARGS&... args) { } #if defined(ECF_OPENSSL) -void client_invoker_enable_ssl(ClientInvoker* self) { +void ClientInvoker_enable_ssl(ClientInvoker* self) { if (auto ecf_ssl = ::getenv("ECF_SSL"); ecf_ssl) { self->enable_ssl_if_defined(); } @@ -510,279 +532,409 @@ void client_invoker_enable_ssl(ClientInvoker* self) { } #endif -void export_Client() { +} // namespace + +void export_Client(py::module& m) { + + static const char* ClientInvoker_version_doc = "Returns the current client version"; + + static const char* ClientInvoker_server_version_doc = + "Returns the server version, can throw for old servers, that did not implement this request."; + + static const char* ClientInvoker_set_user_name_doc = + "Set user name. A password must be provided in the file ..ecf.custom_passwd"; + + static const char* ClientInvoker_get_host_doc = + "Returns the host, assume set_host_port() has been set, otherwise return localhost"; + + static const char* ClientInvoker_get_port_doc = + "Returns the port, assume set_host_port() has been set, otherwise returns 3141"; + + static const char* ClientInvoker_set_auto_sync_doc = + "If true automatically sync with local definition after each call."; + + static const char* ClientInvoker_enable_http_doc = "Enable HTTP communication"; + + static const char* ClientInvoker_enable_https_doc = "Enable HTTPS communication"; + + static const char* ClientInvoker_set_zombie_child_timeout_doc = + "Set timeout for zombie child commands,that cannot connect to server, default is 24 hours. " + "The input is required to be in seconds"; + + static const char* ClientInvoker_debug_server_on_doc = + "Enable server debug, Will dump to standard out on server host."; + + static const char* ClientInvoker_debug_server_off_doc = "Disable server debug"; + + static const char* ClientInvoker_debug_doc = "enable/disable client api debug"; + + static const char* ClientInvoker_child_init_doc = "Child command,notify server job has started"; + + static const char* ClientInvoker_child_abort_doc = + "Child command,notify server job has aborted, can provide an optional reason"; + + static const char* ClientInvoker_child_event_doc = + "Child command,notify server event occurred, requires the event name"; + + static const char* ClientInvoker_child_meter_doc = + "Child command,notify server meter changed, requires meter name and value"; + + static const char* ClientInvoker_child_label_doc = + "Child command,notify server label changed, requires label name, and new value"; + + static const char* ClientInvoker_child_wait_doc = "Child command,wait for expression to come true"; + + static const char* ClientInvoker_child_queue_doc = + "Child command,active:return current step as string, then increment index," + "requires queue name, and optionally path to node with the queue"; + + static const char* ClientInvoker_child_complete_doc = "Child command,notify server job has complete"; + // Need std::shared_ptr, to add support for with( __enter__,__exit__) - py::class_, boost::noncopyable>("Client", ClientDoc::class_client()) - .def("__init__", py::make_constructor(client_invoker_make<>)) - .def("__init__", py::make_constructor(client_invoker_make)) - .def("__init__", py::make_constructor(client_invoker_make)) - .def("__init__", py::make_constructor(client_invoker_make)) - .def("__enter__", &client_enter) // allow with statement - .def("__exit__", &client_exit) // allow with statement, remove last handle - .def("version", &version, "Returns the current client version") - .def("set_user_name", - &ClientInvoker::set_user_name, - "set user name. A password must be provided in the file ..ecf.custom_passwd") - .def("server_version", - &server_version, - "Returns the server version, can throw for old servers, that did not implement this request.") + py::class_>(m, "Client", ClientDoc::class_client()) + + .def(py::init(&ClientInvoker_make<>)) + .def(py::init(&ClientInvoker_make)) + .def(py::init(&ClientInvoker_make)) + .def(py::init(&ClientInvoker_make)) + + // *** Context Manager *** + + .def("__enter__", &ClientInvoker_enter) + .def("__exit__", &ClientInvoker_exit) + + // *** User commands *** + + .def("version", &ClientInvoker_version, ClientInvoker_version_doc) + + .def("server_version", &ClientInvoker_server_version, ClientInvoker_server_version_doc) + + .def("set_user_name", &ClientInvoker::set_user_name, ClientInvoker_set_user_name_doc) .def("set_host_port", &ClientInvoker::set_host_port, ClientDoc::set_host_port()) .def("set_host_port", &ClientInvoker::set_hostport) - .def("set_host_port", &set_host_port) - .def("get_host", - &ClientInvoker::host, - py::return_value_policy(), - "Return the host, assume set_host_port() has been set, otherwise return localhost") - .def("get_port", - &ClientInvoker::port, - py::return_value_policy(), - "Return the port, assume set_host_port() has been set. otherwise returns 3141") - .def("set_retry_connection_period", set_retry_connection_period, ClientDoc::set_retry_connection_period()) + .def("set_host_port", &ClientInvoker_set_host_port) + .def("get_host", &ClientInvoker::host, py::return_value_policy::reference, ClientInvoker_get_host_doc) + .def("get_port", &ClientInvoker::port, py::return_value_policy::reference, ClientInvoker_get_port_doc) + + .def("set_retry_connection_period", + &ClientInvoker_set_retry_connection_period, + ClientDoc::set_retry_connection_period()) + .def("set_connection_attempts", &ClientInvoker::set_connection_attempts, ClientDoc::set_connection_attempts()) - .def("set_auto_sync", - &ClientInvoker::set_auto_sync, - "If true automatically sync with local definition after each call.") + + .def("set_auto_sync", &ClientInvoker::set_auto_sync, ClientInvoker_set_auto_sync_doc) + .def("is_auto_sync_enabled", &ClientInvoker::is_auto_sync_enabled, "Returns true if automatic syncing enabled") + .def("get_defs", &ClientInvoker::defs, ClientDoc::get_defs()) + .def("reset", &ClientInvoker::reset, "reset client definition, and handle number") + .def("in_sync", &ClientInvoker::in_sync, ClientDoc::in_sync()) - .def("get_log", &get_log, py::return_value_policy(), ClientDoc::get_log()) + .def("edit_script_edit", - &edit_script_edit, - py::return_value_policy(), + &ClientInvoker_edit_script_edit, + py::return_value_policy::reference, ClientDoc::edit_script_edit()) .def("edit_script_preprocess", - &edit_script_preprocess, - py::return_value_policy(), + &ClientInvoker_edit_script_preprocess, + py::return_value_policy::reference, ClientDoc::edit_script_preprocess()) - .def("edit_script_submit", &edit_script_submit, ClientDoc::edit_script_submit()) - .def("new_log", &ClientInvoker::new_log, (py::arg("path") = ""), ClientDoc::new_log()) + .def("edit_script_submit", &ClientInvoker_edit_script_submit, ClientDoc::edit_script_submit()) + + .def("get_log", &ClientInvoker_get_log, py::return_value_policy::reference, ClientDoc::get_log()) + .def("new_log", &ClientInvoker::new_log, py::arg("path") = "", ClientDoc::new_log()) .def("clear_log", &ClientInvoker::clearLog, ClientDoc::clear_log()) .def("flush_log", &ClientInvoker::flushLog, ClientDoc::flush_log()) .def("log_msg", &ClientInvoker::logMsg, ClientDoc::log_msg()) + .def("restart_server", &ClientInvoker::restartServer, ClientDoc::restart_server()) .def("halt_server", &ClientInvoker::haltServer, ClientDoc::halt_server()) .def("shutdown_server", &ClientInvoker::shutdownServer, ClientDoc::shutdown_server()) .def("terminate_server", &ClientInvoker::terminateServer, ClientDoc::terminate_server()) + .def("wait_for_server_reply", &ClientInvoker::wait_for_server_reply, - (py::arg("time_out") = 60), + py::arg("time_out") = 60, ClientDoc::wait_for_server_reply()) + .def("load", &ClientInvoker::loadDefs, - (py::arg("path_to_defs"), - py::arg("force") = false, - py::arg("check_only") = false, - py::arg("print") = false, - py::arg("stats") = false), + py::arg("path_to_defs"), + py::arg("force") = false, + py::arg("check_only") = false, + py::arg("print") = false, + py::arg("stats") = false, ClientDoc::load_defs()) - .def("load", &ClientInvoker::load, (py::arg("defs"), py::arg("force") = false), ClientDoc::load()) + .def("load", &ClientInvoker::load, py::arg("defs"), py::arg("force") = false, ClientDoc::load()) + .def("get_server_defs", &ClientInvoker::getDefs, ClientDoc::get_server_defs()) - .def("sync_local", &ClientInvoker::sync_local, (py::arg("sync_suite_clock") = false), ClientDoc::sync()) - .def("news_local", &news_local, ClientDoc::news()) - .add_property("changed_node_paths", - py::range(&ClientInvoker::changed_node_paths_begin, &ClientInvoker::changed_node_paths_end), - ClientDoc::changed_node_paths()) - .def("suites", &suites, ClientDoc::suites()) - .def("ch_register", &ch_register, ClientDoc::ch_register()) - .def("ch_suites", &ch_suites, ClientDoc::ch_suites()) + + .def("sync_local", &ClientInvoker::sync_local, py::arg("sync_suite_clock") = false, ClientDoc::sync()) + + .def("news_local", &ClientInvoker_news_local, ClientDoc::news()) + + .def_property_readonly( + "changed_node_paths", &ClientInvoker::changed_node_paths, ClientDoc::changed_node_paths()) + + .def("suites", &ClientInvoker_suites, ClientDoc::suites()) + + .def("ch_register", &ClientInvoker_ch_register, ClientDoc::ch_register()) + .def("ch_suites", &ClientInvoker_ch_suites, ClientDoc::ch_suites()) .def("ch_handle", &ClientInvoker::client_handle, ClientDoc::ch_register()) .def("ch_drop", &ClientInvoker::ch_drop, ClientDoc::ch_drop()) .def("ch_drop", &ClientInvoker::ch1_drop) .def("ch_drop_user", &ClientInvoker::ch_drop_user, ClientDoc::ch_drop_user()) - .def("ch_add", &ch_add, ClientDoc::ch_add()) - .def("ch_add", &ch1_add) - .def("ch_remove", &ch_remove, ClientDoc::ch_remove()) - .def("ch_remove", &ch1_remove) + .def("ch_add", &ClientInvoker_ch_add, ClientDoc::ch_add()) + .def("ch_add", &ClientInvoker_ch1_add) + .def("ch_remove", &ClientInvoker_ch_remove, ClientDoc::ch_remove()) + .def("ch_remove", &ClientInvoker_ch1_remove) .def("ch_auto_add", &ClientInvoker::ch_auto_add, ClientDoc::ch_auto_add()) .def("ch_auto_add", &ClientInvoker::ch1_auto_add) + .def("checkpt", &ClientInvoker::checkPtDefs, - (py::arg("mode") = ecf::CheckPt::UNDEFINED, - py::arg("check_pt_interval") = 0, - py::arg("check_pt_save_alarm_time") = 0), + py::arg("mode") = ecf::CheckPt::UNDEFINED, + py::arg("check_pt_interval") = 0, + py::arg("check_pt_save_alarm_time") = 0, ClientDoc::checkpt()) .def("restore_from_checkpt", &ClientInvoker::restoreDefsFromCheckPt, ClientDoc::restore_from_checkpt()) + .def("reload_wl_file", &ClientInvoker::reloadwsfile, ClientDoc::reload_wl_file()) .def("reload_passwd_file", &ClientInvoker::reloadpasswdfile, "reload the passwd file. ..ecf.passwd") .def("reload_custom_passwd_file", &ClientInvoker::reloadcustompasswdfile, "reload the custom passwd file. ..ecf.custom_passwd. For users using ECF_USER or --user or " "set_user_name()") - .def("requeue", &requeue, (py::arg("abs_node_path"), py::arg("option") = ""), ClientDoc::requeue()) - .def("requeue", &requeues, (py::arg("paths"), py::arg("option") = "")) - .def("free_trigger_dep", &free_trigger_dep, ClientDoc::free_trigger_dep()) - .def("free_trigger_dep", &free_trigger_dep1) - .def("free_date_dep", &free_date_dep, ClientDoc::free_date_dep()) - .def("free_date_dep", &free_date_dep1) - .def("free_time_dep", &free_time_dep, ClientDoc::free_time_dep()) - .def("free_time_dep", &free_time_dep1) - .def("free_all_dep", &free_all_dep, ClientDoc::free_all_dep()) - .def("free_all_dep", &free_all_dep1) + + .def("requeue", &ClientInvoker_requeue, py::arg("abs_node_path"), py::arg("option") = "", ClientDoc::requeue()) + .def("requeue", &ClientInvoker_requeue_s, py::arg("paths"), py::arg("option") = "") + + .def("free_trigger_dep", &ClientInvoker_free_trigger_dep, ClientDoc::free_trigger_dep()) + .def("free_trigger_dep", &ClientInvoker_free_trigger_dep1) + + .def("free_date_dep", &ClientInvoker_free_date_dep, ClientDoc::free_date_dep()) + .def("free_date_dep", &ClientInvoker_free_date_dep1) + + .def("free_time_dep", &ClientInvoker_free_time_dep, ClientDoc::free_time_dep()) + .def("free_time_dep", &ClientInvoker_free_time_dep1) + + .def("free_all_dep", &ClientInvoker_free_all_dep, ClientDoc::free_all_dep()) + .def("free_all_dep", &ClientInvoker_free_all_dep1) + .def("ping", &ClientInvoker::pingServer, ClientDoc::ping()) + .def("stats", - &stats, - stats_overloads(py::args("to_stdout"), - ClientDoc::stats())[py::return_value_policy()]) - .def("stats_reset", &stats_reset, ClientDoc::stats_reset()) + &ClientInvoker_stats, // This prints to stdout, so we need to use a call guard to redirect output + py::arg("to_stdout") = true, + py::call_guard(), + ClientDoc::stats()) + + .def("stats_reset", &ClientInvoker_stats_reset, ClientDoc::stats_reset()) + .def("get_file", - &get_file, - (py::arg("task"), py::arg("type") = "script", py::arg("max_lines") = "10000", py::arg("as_bytes") = false), + &ClientInvoker_get_file, + py::arg("task"), + py::arg("type") = "script", + py::arg("max_lines") = "10000", + py::arg("as_bytes") = false, ClientDoc::get_file()) + .def("plug", &ClientInvoker::plug, ClientDoc::plug()) - .def("query", &query, py::return_value_policy(), ClientDoc::query()) - .def("query", &query1, py::return_value_policy(), ClientDoc::query()) + + .def("query", + &ClientInvoker_query, + py::arg("query_type"), + py::arg("path_to_attribute"), + py::arg("attribute") = std::string{}, + py::return_value_policy::reference, + ClientDoc::query()) + .def("alter", - &alters, - (py::arg("paths"), - py::arg("alter_type"), - py::arg("attribute_type"), - py::arg("name") = "", - py::arg("value") = ""), + &ClientInvoker_alter_s, + py::arg("paths"), + py::arg("alter_type"), + py::arg("attribute_type"), + py::arg("name") = "", + py::arg("value") = "", ClientDoc::alter()) .def("alter", - &alter, - (py::arg("abs_node_path"), - py::arg("alter_type"), - py::arg("attribute_type"), - py::arg("name") = "", - py::arg("value") = "")) + &ClientInvoker_alter, + py::arg("abs_node_path"), + py::arg("alter_type"), + py::arg("attribute_type"), + py::arg("name") = "", + py::arg("value") = "") + + .def("sort_attributes", + &ClientInvoker_alter_sort, + py::arg("abs_node_path"), + py::arg("attribute_name"), + py::arg("recursive") = true) .def("sort_attributes", - &alter_sort, - (py::arg("abs_node_path"), py::arg("attribute_name"), py::arg("recursive") = true)) - .def( - "sort_attributes", &alter_sorts, (py::arg("paths"), py::arg("attribute_name"), py::arg("recursive") = true)) - .def("force_event", &force_event, ClientDoc::force_event()) - .def("force_event", &force_events) - .def("force_state", &force_state, ClientDoc::force_state()) - .def("force_state", &force_states) - .def("force_state_recursive", &force_state_recursive, ClientDoc::force_state_recursive()) - .def("force_state_recursive", &force_states_recursive) + &ClientInvoker_alter_sort_s, + py::arg("paths"), + py::arg("attribute_name"), + py::arg("recursive") = true) + + .def("force_event", &ClientInvoker_force_event, ClientDoc::force_event()) + .def("force_event", &ClientInvoker_force_events) + + .def("force_state", &ClientInvoker_force_state, ClientDoc::force_state()) + .def("force_state", &ClientInvoker_force_states) + + .def("force_state_recursive", &ClientInvoker_force_state_recursive, ClientDoc::force_state_recursive()) + .def("force_state_recursive", &ClientInvoker_force_states_recursive) + .def("replace", &ClientInvoker::replace, ClientDoc::replace()) .def("replace", &ClientInvoker::replace_1) - .def("replace", &replace_1) - .def("replace", &replace_2) - .def("order", &order, ClientDoc::order()) + .def("replace", &ClientInvoker_replace_1) + .def("replace", &ClientInvoker_replace_2) + + .def("order", &ClientInvoker_order, ClientDoc::order()) + .def("group", &ClientInvoker::group, ClientDoc::group()) + .def("begin_suite", &ClientInvoker::begin, - (py::arg("suite_name"), py::arg("force") = false), + py::arg("suite_name"), + py::arg("force") = false, ClientDoc::begin_suite()) - .def("begin_all_suites", &ClientInvoker::begin_all_suites, (py::arg("force") = false), ClientDoc::begin_all()) + + .def("begin_all_suites", &ClientInvoker::begin_all_suites, py::arg("force") = false, ClientDoc::begin_all()) + .def("job_generation", &ClientInvoker::job_gen, ClientDoc::job_gen()) - .def("run", &run, ClientDoc::run()) - .def("run", &runs) - .def("check", &check, py::return_value_policy(), ClientDoc::check()) - .def("check", &checks, py::return_value_policy()) - .def("kill", &do_kill, ClientDoc::kill()) - .def("kill", &do_kills) - .def("status", &the_status, ClientDoc::status()) - .def("status", &statuss) - .def("suspend", &suspend, ClientDoc::suspend()) - .def("suspend", &suspends) - .def("resume", &resume, ClientDoc::resume()) - .def("resume", &resumes) - .def("archive", &archive, ClientDoc::archive()) - .def("archive", &archives) - .def("restore", &restore, ClientDoc::restore()) - .def("restore", &restores) + + .def("run", &ClientInvoker_run, ClientDoc::run()) + .def("run", &ClientInvoker_runs) + + .def("check", &ClientInvoker_check, py::return_value_policy::reference, ClientDoc::check()) + .def("check", &ClientInvoker_check_s, py::return_value_policy::reference) + + .def("kill", &ClientInvoker_kill, ClientDoc::kill()) + .def("kill", &ClientInvoker_kill_s) + + .def("status", &ClientInvoker_status, ClientDoc::status()) + .def("status", &ClientInvoker_status_s) + + .def("suspend", &ClientInvoker_suspend, ClientDoc::suspend()) + .def("suspend", &ClientInvoker_suspend_s) + + .def("resume", &ClientInvoker_resume, ClientDoc::resume()) + .def("resume", &ClientInvoker_resume_s) + + .def("archive", &ClientInvoker_archive, ClientDoc::archive()) + .def("archive", &ClientInvoker_archive_s) + + .def("restore", &ClientInvoker_restore, ClientDoc::restore()) + .def("restore", &ClientInvoker_restore_s) + .def("delete", &ClientInvoker::delete_node, - (py::arg("abs_node_path"), py::arg("force") = false), + py::arg("abs_node_path"), + py::arg("force") = false, ClientDoc::delete_node()) - .def("delete", &delete_node, (py::arg("paths"), py::arg("force") = false)) - .def("delete_all", &ClientInvoker::delete_all, (py::arg("force") = false), ClientDoc::delete_all()) - .def("debug_server_on", - &ClientInvoker::debug_server_on, - "Enable server debug, Will dump to standard out on server host.") - .def("debug_server_off", &ClientInvoker::debug_server_off, "Disable server debug") + .def("delete", &ClientInvoker_delete_node_s, py::arg("paths"), py::arg("force") = false) + + .def("delete_all", &ClientInvoker::delete_all, py::arg("force") = false, ClientDoc::delete_all()) + + .def("debug_server_on", &ClientInvoker::debug_server_on, ClientInvoker_debug_server_on_doc) + .def("debug_server_off", &ClientInvoker::debug_server_off, ClientInvoker_debug_server_off_doc) - .def("debug", &ClientInvoker::debug, "enable/disable client api debug") + .def("debug", &ClientInvoker::debug, ClientInvoker_debug_doc) #ifdef ECF_OPENSSL - .def("enable_ssl", client_invoker_enable_ssl, ClientDoc::enable_ssl()) + .def("enable_ssl", ClientInvoker_enable_ssl, ClientDoc::enable_ssl()) .def("disable_ssl", &ClientInvoker::disable_ssl, ClientDoc::disable_ssl()) .def("get_certificate", &ClientInvoker::get_certificate, ClientDoc::get_certificate()) #endif - .def("enable_http", &ClientInvoker::enable_http, "Enable HTTP communication") - .def("enable_https", &ClientInvoker::enable_https, "Enable HTTPS communication") + .def("enable_http", &ClientInvoker::enable_http, ClientInvoker_enable_http_doc) + .def("enable_https", &ClientInvoker::enable_https, ClientInvoker_enable_https_doc) - .def("zombie_get", &zombieGet, py::return_value_policy()) + .def("zombie_get", &ClientInvoker_zombieGet, py::return_value_policy::reference) .def("zombie_fob", &ClientInvoker::zombieFobCli) .def("zombie_fail", &ClientInvoker::zombieFailCli) .def("zombie_adopt", &ClientInvoker::zombieAdoptCli) .def("zombie_block", &ClientInvoker::zombieBlockCli) .def("zombie_remove", &ClientInvoker::zombieRemoveCli) .def("zombie_kill", &ClientInvoker::zombieKillCli) - .def("zombie_fob", &zombieFobCli) - .def("zombie_fail", &zombieFailCli) - .def("zombie_adopt", &zombieAdoptCli) - .def("zombie_block", &zombieBlockCli) - .def("zombie_remove", &zombieRemoveCli) - .def("zombie_kill", &zombieKillCli) + .def("zombie_fob", &ClientInvoker_zombieFobCli) + .def("zombie_fail", &ClientInvoker_zombieFailCli) + .def("zombie_adopt", &ClientInvoker_zombieAdoptCli) + .def("zombie_block", &ClientInvoker_zombieBlockCli) + .def("zombie_remove", &ClientInvoker_zombieRemoveCli) + .def("zombie_kill", &ClientInvoker_zombieKillCli) .def("set_child_path", &ClientInvoker::set_child_path, ClientDoc::set_child_path()) .def("set_child_password", &ClientInvoker::set_child_password, ClientDoc::set_child_password()) .def("set_child_pid", &ClientInvoker::set_child_pid, ClientDoc::set_child_pid()) - .def("set_child_pid", &set_child_pid, ClientDoc::set_child_pid()) + .def("set_child_pid", &ClientInvoker_set_child_pid, ClientDoc::set_child_pid()) .def("set_child_try_no", &ClientInvoker::set_child_try_no, ClientDoc::set_child_try_no()) .def("set_child_timeout", &ClientInvoker::set_child_timeout, ClientDoc::set_child_timeout()) - .def("set_child_init_add_vars", &set_child_init_add_vars, ClientDoc::set_child_init_add_vars()) - .def("set_child_init_add_vars", &set_child_init_add_vars2, ClientDoc::set_child_init_add_vars()) - .def("set_child_complete_del_vars", &set_child_complete_del_vars, ClientDoc::set_child_complete_del_vars()) + .def("set_child_init_add_vars", &ClientInvoker_set_child_init_add_vars, ClientDoc::set_child_init_add_vars()) + .def("set_child_init_add_vars", &ClientInvoker_set_child_init_add_vars2, ClientDoc::set_child_init_add_vars()) + .def("set_child_complete_del_vars", + &ClientInvoker_set_child_complete_del_vars, + ClientDoc::set_child_complete_del_vars()) .def("set_zombie_child_timeout", &ClientInvoker::set_zombie_child_timeout, - "Set timeout for zombie child commands,that cannot connect to server, default is 24 hours. The input is " - "required to be in seconds") - .def("child_init", &ClientInvoker::child_init, "Child command,notify server job has started") - .def("child_abort", - &ClientInvoker::child_abort, - (py::arg("reason") = ""), - "Child command,notify server job has aborted, can provide an optional reason") + ClientInvoker_set_zombie_child_timeout_doc) + + // *** Task (or Child) commands *** + + .def("child_init", &ClientInvoker::child_init, ClientInvoker_child_init_doc) + + .def("child_abort", &ClientInvoker::child_abort, py::arg("reason") = "", ClientInvoker_child_abort_doc) + .def("child_event", &ClientInvoker::child_event, - (py::arg("event_name"), py::arg("value") = true), - "Child command,notify server event occurred, requires the event name") - .def("child_meter", - &ClientInvoker::child_meter, - "Child command,notify server meter changed, requires meter name and value") - .def("child_label", - &ClientInvoker::child_label, - "Child command,notify server label changed, requires label name, and new value") - .def("child_wait", &ClientInvoker::child_wait, "Child command,wait for expression to come true") + py::arg("event_name"), + py::arg("value") = true, + ClientInvoker_child_event_doc) + + .def("child_meter", &ClientInvoker::child_meter, ClientInvoker_child_meter_doc) + + .def("child_label", &ClientInvoker::child_label, ClientInvoker_child_label_doc) + + .def("child_wait", &ClientInvoker::child_wait, ClientInvoker_child_wait_doc) + .def("child_queue", &ClientInvoker::child_queue, - (py::arg("queue_name"), py::arg("action"), py::arg("step") = "", py::arg("path_to_node_with_queue") = ""), - "Child command,active:return current step as string, then increment index, requires queue name, and " - "optionally path to node with the queue") - .def("child_complete", &ClientInvoker::child_complete, "Child command,notify server job has complete"); - - py::class_("WhyCmd", - "The why command reports, the reason why a node is not running.\n\n" - "It needs the definition structure and the path to node\n" - "\nConstructor::\n\n" - " WhyCmd(defs, node_path)\n" - " defs_ptr defs : pointer to a definition structure\n" - " string node_path : The node path\n\n" - "\nExceptions:\n\n" - "- raises RuntimeError if the definition is empty\n" - "- raises RuntimeError if the node path is empty\n" - "- raises RuntimeError if the node path cannot be found in the definition\n" - "\nUsage::\n\n" - " try:\n" - " ci = Client()\n" - " ci.sync_local()\n" - " ask = WhyCmd(ci.get_defs(),'/suite/family')\n" - " print(ask.why())\n" - " except RuntimeError, e:\n" - " print(str(e))\n\n", - py::init()) + py::arg("queue_name"), + py::arg("action"), + py::arg("step") = "", + py::arg("path_to_node_with_queue") = "", + ClientInvoker_child_queue_doc) + + .def("child_complete", &ClientInvoker::child_complete, ClientInvoker_child_complete_doc); + + constexpr const char* WhyCmd_doc = "The why command reports, the reason why a node is not running.\n\n" + "It needs the definition structure and the path to node\n" + "\nConstructor::\n\n" + " WhyCmd(defs, node_path)\n" + " defs_ptr defs : pointer to a definition structure\n" + " string node_path : The node path\n\n" + "\nExceptions:\n\n" + "- raises RuntimeError if the definition is empty\n" + "- raises RuntimeError if the node path is empty\n" + "- raises RuntimeError if the node path cannot be found in the definition\n" + "\nUsage::\n\n" + " try:\n" + " ci = Client()\n" + " ci.sync_local()\n" + " ask = WhyCmd(ci.get_defs(),'/suite/family')\n" + " print(ask.why())\n" + " except RuntimeError, e:\n" + " print(str(e))\n\n"; + + py::class_(m, "WhyCmd", WhyCmd_doc) + + .def(py::init()) .def("why", &WhyCmd::why, "returns a '/n' separated string, with reasons why node is not running"); - py::class_( - "UrlCmd", + constexpr const char* UrlCmd_doc = "Executes a command ECF_URL_CMD to display a url.\n\n" "It needs the definition structure and the path to node.\n" "\nConstructor::\n\n" @@ -811,7 +963,10 @@ void export_Client() { " url = UrlCmd(ci.get_defs(),'/suite/family/task')\n" " print(url.execute())\n" " except RuntimeError, e:\n" - " print(str(e))\n\n", - py::init()) + " print(str(e))\n\n"; + + py::class_(m, "UrlCmd", UrlCmd_doc) + + .def(py::init()) .def("execute", &UrlCmd::execute, "Displays url in the chosen browser"); } diff --git a/libs/pyext/src/ecflow/python/ExportCollections.cpp b/libs/pyext/src/ecflow/python/ExportCollections.cpp new file mode 100644 index 000000000..79c2d06c1 --- /dev/null +++ b/libs/pyext/src/ecflow/python/ExportCollections.cpp @@ -0,0 +1,29 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "ecflow/python/ExportCollections.hpp" + +void export_Collections(py::module& m) { + // Export the vector of Variable + py::bind_vector>(m, "VariableList"); + + // Export the vector of node_ptr + py::bind_vector>( + m, "NodeVec", "Hold a list of Nodes (i.e `suite`_, `family`_ or `task`_\\ s)"); + + // Export the vector of suite_ptr + py::bind_vector>(m, "SuiteVec"); + + // Export the vector of family_ptr + py::bind_vector>(m, "FamilyVec"); + + // Export the vector of task_ptr + py::bind_vector>(m, "TaskVec"); +} diff --git a/libs/pyext/src/ecflow/python/ExportCollections.hpp b/libs/pyext/src/ecflow/python/ExportCollections.hpp new file mode 100644 index 000000000..ce26e23f2 --- /dev/null +++ b/libs/pyext/src/ecflow/python/ExportCollections.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_python_ExportCollections_HPP +#define ecflow_python_ExportCollections_HPP + +#include + +#include "ecflow/attribute/Variable.hpp" +#include "ecflow/node/Family.hpp" +#include "ecflow/node/Suite.hpp" +#include "ecflow/node/Task.hpp" +#include "ecflow/python/PythonBinding.hpp" + +PYBIND11_MAKE_OPAQUE(std::vector) + +PYBIND11_MAKE_OPAQUE(std::vector) +PYBIND11_MAKE_OPAQUE(std::vector) +PYBIND11_MAKE_OPAQUE(std::vector) +PYBIND11_MAKE_OPAQUE(std::vector) + +#endif /* ecflow_python_ExportCollections_HPP */ diff --git a/libs/pyext/src/ecflow/python/ExportCore.cpp b/libs/pyext/src/ecflow/python/ExportCore.cpp index ef8cfee07..fcb97e358 100644 --- a/libs/pyext/src/ecflow/python/ExportCore.cpp +++ b/libs/pyext/src/ecflow/python/ExportCore.cpp @@ -21,17 +21,6 @@ #include "ecflow/python/PythonBinding.hpp" #include "ecflow/python/PythonUtil.hpp" -// See: http://wiki.python.org/moin/boost.python/HowTo#boost.function_objects -template -struct pair_to_tuple -{ - using converter = pair_to_tuple; - using ctype = std::pair; - - static PyObject* convert(ctype const& v) { return py::incref(py::make_tuple(v.first, v.second).ptr()); } - static void register_to_python() { py::to_python_converter(); } -}; - bool debug_build() { #ifdef NDEBUG return false; @@ -40,34 +29,29 @@ bool debug_build() { #endif } -void export_Core() { - // For use in test only - py::def("debug_build", debug_build); - - // see: https://github.com/boostorg/python/blob/master/test/raw_ctor.cpp - // Uses a raw constructor approach to support pass arbitrary number arguments on the python side. - // using no_init postpones defining __init__ function until after raw_function for proper overload resolution order, - // since later defs get higher priority. - py::class_("Edit", NodeAttrDoc::variable_doc(), py::no_init) - .def("__init__", raw_function(&Edit::init, 0)) // raw_constructor -> will call -> def(init() ) - .def(py::init()) // - .def(py::init()) // - .def("__str__", &Edit::to_string) // __str__ - ; - - py::class_("File", "Utility class, Used in test only.", py::no_init) - .def("find_server", &ecf::File::find_ecf_server_path, "Provides pathname to the server") - .staticmethod("find_server") - .def("find_client", &ecf::File::find_ecf_client_path, "Provides pathname to the client") - .staticmethod("find_client") - .def("source_dir", &ecf::File::root_source_dir, "Path name to ecflow source directory") - .staticmethod("source_dir") - .def("build_dir", &ecf::File::root_build_dir, "Path name to ecflow build directory") - .staticmethod("build_dir"); - - py::enum_( - "Style", - "Style is used to control printing output for the definition\n\n" +void export_Core(py::module& m) { + // The following is used, in tests only, to detect a debug build + m.def("debug_build", debug_build); + + py::class_(m, "Edit", NodeAttrDoc::variable_doc()) + + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def("__str__", &Edit::to_string); + + constexpr const char* file_docs = "Utility class, Used in test only."; + + py::class_(m, "File", file_docs) + + .def_static("find_server", &ecf::File::find_ecf_server_path, "Provides pathname to the server") + .def_static("find_client", &ecf::File::find_ecf_client_path, "Provides pathname to the client") + .def_static("source_dir", &ecf::File::root_source_dir, "Path name to ecflow source directory") + .def_static("build_dir", &ecf::File::root_build_dir, "Path name to ecflow build directory"); + + constexpr const char* printstyle_type_docs = + "Style is used to control printing output for the definition\n" + "\n" "- DEFS: This style outputs the definition file in a format that is parse-able.\n" " and can be re-loaded back into the server.\n" " Externs are automatically added.\n" @@ -75,7 +59,8 @@ void export_Core() { "- STATE: The output includes additional state information for debug\n" " This excludes the edit history\n" "- MIGRATE: Output includes structure and state, allow migration to future ecflow versions\n" - " This includes edit history. If file is reloaded no checking is done\n\n" + " This includes edit history. If file is reloaded no checking is done\n" + "\n" "The following shows a summary of the features associated with each choice\n" "\n" " ===================== ==== ===== =======\n" @@ -85,55 +70,57 @@ void export_Core() { " Checking on reload Yes Yes No\n" " Edit History No No Yes\n" " Show trigger AST No Yes No\n" - " ===================== ==== ===== =======\n") + " ===================== ==== ===== =======\n"; + + py::enum_(m, "Style", printstyle_type_docs) + .value("NOTHING", PrintStyle::NOTHING) .value("DEFS", PrintStyle::DEFS) .value("STATE", PrintStyle::STATE) .value("MIGRATE", PrintStyle::MIGRATE); - py::class_( - "PrintStyle", + constexpr const char* printstyle_docs = "Singleton used to control the print Style. See :py:class:`ecflow.Style`\n\n" "\nUsage::\n\n" " old_style = PrintStyle.get_style()\n" " PrintStyle.set_style(PrintStyle.STATE)\n" " ...\n" " print(defs) # show the node state\n" - " PrintStyle.set_style(old_style) # reset previous style\n", - py::no_init) - .def("get_style", &PrintStyleHolder::getStyle, "Returns the style, static method") - .staticmethod("get_style") - .def("set_style", &PrintStyleHolder::setStyle, "Set the style, static method") - .staticmethod("set_style"); - - py::enum_( - "CheckPt", + " PrintStyle.set_style(old_style) # reset previous style\n"; + + py::class_(m, "PrintStyle", printstyle_docs) + + .def_static("get_style", &PrintStyleHolder::getStyle, "Returns the style, static method") + .def_static("set_style", &PrintStyleHolder::setStyle, "Set the style, static method"); + + constexpr const char* checkpt_mode_docs = "CheckPt is enum that is used to control check pointing in the `ecflow_server`_\n\n" "- NEVER : Switches of check pointing\n" "- ON_TIME: `check point`_ file is saved periodically, specified by checkPtInterval. This is the default.\n" "- ALWAYS : `check point`_ file is saved after any state change, *not* recommended for large definitions\n" - "- UNDEFINED : None of the the above, used to provide default argument\n") + "- UNDEFINED : None of the the above, used to provide default argument\n"; + + py::enum_(m, "CheckPt", checkpt_mode_docs) + .value("NEVER", ecf::CheckPt::NEVER) .value("ON_TIME", ecf::CheckPt::ON_TIME) .value("ALWAYS", ecf::CheckPt::ALWAYS) .value("UNDEFINED", ecf::CheckPt::UNDEFINED); - py::class_("Ecf", "Singleton used to control ecf debugging\n\n", py::no_init) - .def("debug_equality", &Ecf::debug_equality, "Returns true if debugging of equality is enabled") - .staticmethod("debug_equality") - .def("set_debug_equality", &Ecf::set_debug_equality, "Set debugging for equality") - .staticmethod("set_debug_equality") - .def("debug_level", - &Ecf::debug_level, - "Returns integer showing debug level. debug_level > 0 will disable some warning messages") - .staticmethod("debug_level") - .def("set_debug_level", - &Ecf::set_debug_level, - "Set debug level. debug_level > 0 will disable some warning messages") - .staticmethod("set_debug_level"); - - py::enum_( - "State", + constexpr const char* ecf_docs = "Singleton used to control ecf debugging\n\n"; + + py::class_(m, "Ecf", ecf_docs) + + .def_static("debug_equality", &Ecf::debug_equality, "Returns true if debugging of equality is enabled") + .def_static("set_debug_equality", &Ecf::set_debug_equality, "Set debugging for equality") + .def_static("debug_level", + &Ecf::debug_level, + "Returns integer showing debug level. debug_level > 0 will disable some warning messages") + .def_static("set_debug_level", + &Ecf::set_debug_level, + "Set debug level. debug_level > 0 will disable some warning messages"); + + constexpr const char* nstate_state_docs = "Each `node`_ can have a status, which reflects the life cycle of a node.\n\n" "It varies as follows:\n\n" "- When the definition file is loaded into the `ecflow_server`_ the `task`_ status is `unknown`_\n" @@ -145,7 +132,10 @@ void export_Core() { "- On a successful submission the task is placed into the `active`_ state\n" "- Before a job ends, it may send other message to the server such as:\n" " Set an `event`_, Change a `meter`_, Change a `label`_, send a message to log file\n\n" - "Jobs end by becoming either `complete`_ or `aborted`_") + "Jobs end by becoming either `complete`_ or `aborted`_"; + + py::enum_(m, "State", nstate_state_docs) + .value("unknown", NState::UNKNOWN) .value("complete", NState::COMPLETE) .value("queued", NState::QUEUED) @@ -153,20 +143,22 @@ void export_Core() { .value("submitted", NState::SUBMITTED) .value("active", NState::ACTIVE); - py::enum_("DState", - "A DState is like a ecflow.State, except for the addition of SUSPENDED\n\n" - "Suspended stops job generation, and hence is an attribute of a Node.\n" - "DState can be used for setting the default state of node when it is\n" - "begun or re queued. DState is used for defining `defstatus`_.\n" - "See :py:class:`ecflow.Node.add_defstatus` and :py:class:`ecflow.Defstatus`\n" - "The default state of a `node`_ is `queued`_.\n" - "\nUsage::\n\n" - " task = ecflow.Task('t1')\n" - " task.add_defstatus(ecflow.DState.complete)" - " task = ecflow.Task('t2')\n" - " task += Defstatus('complete')\n" - " task = Task('t3',\n" - " Defstatus('complete')) # create in place\n") + constexpr const char* dstate_docs = "A DState is like a ecflow.State, except for the addition of SUSPENDED\n\n" + "Suspended stops job generation, and hence is an attribute of a Node.\n" + "DState can be used for setting the default state of node when it is\n" + "begun or re queued. DState is used for defining `defstatus`_.\n" + "See :py:class:`ecflow.Node.add_defstatus` and :py:class:`ecflow.Defstatus`\n" + "The default state of a `node`_ is `queued`_.\n" + "\nUsage::\n\n" + " task = ecflow.Task('t1')\n" + " task.add_defstatus(ecflow.DState.complete)" + " task = ecflow.Task('t2')\n" + " task += Defstatus('complete')\n" + " task = Task('t3',\n" + " Defstatus('complete')) # create in place\n"; + + py::enum_(m, "DState", dstate_docs) + .value("unknown", DState::UNKNOWN) .value("complete", DState::COMPLETE) .value("queued", DState::QUEUED) @@ -175,47 +167,49 @@ void export_Core() { .value("suspended", DState::SUSPENDED) .value("active", DState::ACTIVE); - py::class_("Defstatus", - "A `node`_ can be set with a default status other the `queued`_\n\n" - "The default state of a `node`_ is `queued`_.\n" - "This defines the state to take at 'begin' or 're-queue' time\n" - "See :py:class:`ecflow.Node.add_defstatus` and :py:class:`ecflow.DState`\n", - py::init()) + constexpr const char* defstatus_docs = "A `node`_ can be set with a default status other the `queued`_\n\n" + "The default state of a `node`_ is `queued`_.\n" + "This defines the state to take at 'begin' or 're-queue' time\n" + "See :py:class:`ecflow.Node.add_defstatus` and :py:class:`ecflow.DState`\n"; + + py::class_(m, "Defstatus", defstatus_docs) + + .def(py::init()) .def(py::init()) // constructor .def("state", &Defstatus::state) - .def("__str__", &Defstatus::to_string) // __str__ - ; + .def("__str__", &Defstatus::to_string); + + constexpr const char* sstate_state_docs = "A SState holds the `ecflow_server`_ state\n\n" + "See `server states`_"; + + py::enum_(m, "SState", sstate_state_docs) - py::enum_("SState", - "A SState holds the `ecflow_server`_ state\n\n" - "See `server states`_") .value("HALTED", SState::HALTED) .value("SHUTDOWN", SState::SHUTDOWN) .value("RUNNING", SState::RUNNING); - py::class_("TimeSlot", - "Represents a time slot.\n\n" - "It is typically used as an argument to a :py:class:`TimeSeries` or\n" - "other time dependent attributes of a node.\n" - "\n" - "\nConstructor::\n\n" - " TimeSlot(hour,min)\n" - " int hour: represent an hour:\n" - " int minute: represents a minute:\n" - "\nUsage::\n\n" - " ts = TimeSlot(10,11)\n", - py::init()) - .def("__str__", &ecf::TimeSlot::toString) // __str__ + constexpr const char* timeslot_docs = "Represents a time slot.\n\n" + "It is typically used as an argument to a :py:class:`TimeSeries` or\n" + "other time dependent attributes of a node.\n" + "\n" + "\nConstructor::\n\n" + " TimeSlot(hour,min)\n" + " int hour: represent an hour:\n" + " int minute: represents a minute:\n" + "\nUsage::\n\n" + " ts = TimeSlot(10,11)\n"; + + py::class_(m, "TimeSlot", timeslot_docs) + + .def(py::init()) + .def("__str__", &ecf::TimeSlot::toString) .def("__copy__", pyutil_copy_object) // __copy__ uses copy constructor - .def(py::self == py::self) // __eq__ - .def("hour", &ecf::TimeSlot::hour) // return int - .def("minute", &ecf::TimeSlot::minute) // return int - .def("empty", &ecf::TimeSlot::isNULL) // return bool - ; + .def(py::self == py::self) + .def("hour", &ecf::TimeSlot::hour) + .def("minute", &ecf::TimeSlot::minute) + .def("empty", &ecf::TimeSlot::isNULL); - // single slot, | start, finish, incr, bool relative to suite start - py::class_( - "TimeSeries", + constexpr const char* timeseries_docs = "A TimeSeries can hold a single time slot or a series.\n\n" "Time series can be created relative to the `suite`_ start or start of a repeating node.\n" "A Time series can be used as argument to the :py:class:`ecflow.Time`, :py:class:`ecflow.Today` and " @@ -243,10 +237,18 @@ void export_Core() { "\nExceptions:\n\n" "- Raises IndexError when an invalid time series is specified\n" "\nUsage::\n\n" - " time_series = TimeSeries(TimeSlot(10,11),False)\n", - py::init>()) - .def(py::init>()) - .def(py::init>()) + " time_series = TimeSeries(TimeSlot(10,11),False)\n"; + + // single slot, | start, finish, incr, bool relative to suite start + py::class_(m, "TimeSeries", timeseries_docs) + + .def(py::init(), py::arg("ts"), py::arg("relative") = false) + .def(py::init(), py::arg("hour"), py::arg("minute"), py::arg("relative") = false) + .def(py::init(), + py::arg("start"), + py::arg("finish"), + py::arg("increment"), + py::arg("relative") = false) .def(py::self == py::self) // __eq__ .def("__str__", &ecf::TimeSeries::toString) // __str__ .def("__copy__", pyutil_copy_object) // __copy__ uses copy constructor @@ -256,20 +258,18 @@ void export_Core() { // slot .def("start", &ecf::TimeSeries::start, - py::return_value_policy(), + py::return_value_policy::reference, "returns the start time") // returns a time slot .def("finish", &ecf::TimeSeries::finish, - py::return_value_policy(), + py::return_value_policy::reference, "returns the finish time if time series specified, else returns a NULL time slot") // returns a time slot .def("incr", &ecf::TimeSeries::incr, - py::return_value_policy(), + py::return_value_policy::reference, " returns the increment time if time series specified, else returns a NULL time slot") // returns a time // slot .def("relative", &ecf::TimeSeries::relative, "returns a boolean where true means that the time series is relative"); - - pair_to_tuple::register_to_python(); } diff --git a/libs/pyext/src/ecflow/python/ExportDefs.cpp b/libs/pyext/src/ecflow/python/ExportDefs.cpp index e485e2406..122d2df07 100644 --- a/libs/pyext/src/ecflow/python/ExportDefs.cpp +++ b/libs/pyext/src/ecflow/python/ExportDefs.cpp @@ -20,14 +20,17 @@ #include "ecflow/node/formatter/DefsWriter.hpp" #include "ecflow/python/DefsDoc.hpp" #include "ecflow/python/Edit.hpp" +#include "ecflow/python/ExportCollections.hpp" #include "ecflow/python/GlossaryDoc.hpp" #include "ecflow/python/PythonBinding.hpp" #include "ecflow/python/PythonUtil.hpp" #include "ecflow/simulator/Simulator.hpp" -// See: http://wiki.python.org/moin/boost.python/HowTo#boost.function_objects +namespace { -void save_as_defs(const Defs& theDefs, const std::string& filename, PrintStyle::Type_t the_style_enum) { +void Defs_save_as_defs_with_given_style(const Defs& theDefs, + const std::string& filename, + PrintStyle::Type_t the_style_enum) { std::string file_creation_error_msg; if (!ecf::File::create(filename, ecf::as_string(theDefs, the_style_enum), file_creation_error_msg)) { std::string error = "save_as_defs failed: "; @@ -36,30 +39,17 @@ void save_as_defs(const Defs& theDefs, const std::string& filename, PrintStyle:: } } -void save_as_defs_1(const Defs& theDefs, const std::string& filename) { - save_as_defs(theDefs, filename, PrintStyle::DEFS); +void Defs_save_as_defs_with_default_style(const Defs& theDefs, const std::string& filename) { + Defs_save_as_defs_with_given_style(theDefs, filename, PrintStyle::DEFS); } -std::string convert_to_string(const Defs& theDefs) { +std::string Defs_str(const Defs& theDefs) { std::string buffer; ecf::write_t(buffer, theDefs, PrintStyleHolder::getStyle()); return buffer; } -static defs_ptr create_defs(const std::string& file_name) { - defs_ptr defs = Defs::create(); - - std::string errorMsg, warningMsg; - if (!defs->restore(file_name, errorMsg, warningMsg)) { - throw std::runtime_error(errorMsg); - } - if (!warningMsg.empty()) { - std::cerr << warningMsg; - } - return defs; -} - -std::string check_defs(defs_ptr defs) { +std::string Defs_check(defs_ptr defs) { std::string error_msg; std::string warning_msg; if (defs.get() && !defs->check(error_msg, warning_msg)) { @@ -70,11 +60,11 @@ std::string check_defs(defs_ptr defs) { return warning_msg; } -void restore_from_checkpt(defs_ptr defs, const std::string& file_name) { +void Defs_restore(defs_ptr defs, const std::string& file_name) { defs->restore(file_name); } -std::string simulate(defs_ptr defs) { +std::string Defs_simulate(defs_ptr defs) { if (defs.get()) { // name output file after name of the first suite std::string defs_filename = "pyext.def"; @@ -91,33 +81,32 @@ std::string simulate(defs_ptr defs) { return std::string(); } -SState::State get_server_state(defs_ptr self) { +SState::State Defs_get_server_state(defs_ptr self) { return self->server_state().get_state(); } -/// Since we don't pass in a child pos, the nodes are added to the end -suite_ptr add_suite(defs_ptr self, suite_ptr s) { +suite_ptr Defs_add_suite(defs_ptr self, suite_ptr s) { + // Since we don't pass in a child pos, the nodes are added to the end self->addSuite(s); return s; } -std::vector get_all_tasks(defs_ptr self) { +std::vector Defs_get_all_tasks(defs_ptr self) { return ecf::get_all_tasks_ptr(*self); } - -std::vector get_all_nodes(defs_ptr self) { +std::vector Defs_get_all_nodes(defs_ptr self) { return ecf::get_all_nodes_ptr(*self); } -// Context management, Only used to provide indentation -defs_ptr defs_enter(defs_ptr self) { +defs_ptr Defs_enter(defs_ptr self) { return self; } -bool defs_exit(defs_ptr self, const py::object& type, const py::object& value, const py::object& traceback) { + +bool Defs_exit(defs_ptr self, const py::object& type, const py::object& value, const py::object& traceback) { return false; } -std::string check_job_creation(defs_ptr defs, bool throw_on_error, bool verbose) { +std::string Defs_check_job_creation(defs_ptr defs, bool throw_on_error, bool verbose) { job_creation_ctrl_ptr jobCtrl = std::make_shared(); if (verbose) { jobCtrl->set_verbose(verbose); @@ -129,20 +118,22 @@ std::string check_job_creation(defs_ptr defs, bool throw_on_error, bool verbose) return jobCtrl->get_error_msg(); } -// Add server user variables -defs_ptr add_variable(defs_ptr self, const std::string& name, const std::string& value) { +defs_ptr Defs_add_variable_string(defs_ptr self, const std::string& name, const std::string& value) { self->server_state().add_or_update_user_variables(name, value); return self; } -defs_ptr add_variable_int(defs_ptr self, const std::string& name, int value) { + +defs_ptr Defs_add_variable_int(defs_ptr self, const std::string& name, int value) { self->server_state().add_or_update_user_variables(name, ecf::convert_to(value)); return self; } -defs_ptr add_variable_var(defs_ptr self, const Variable& var) { + +defs_ptr Defs_add_variable_variable(defs_ptr self, const Variable& var) { self->server_state().add_or_update_user_variables(var.name(), var.theValue()); return self; } -defs_ptr add_variable_dict(defs_ptr self, const py::dict& dict) { + +defs_ptr Defs_add_variable_dict(defs_ptr self, const py::dict& dict) { std::vector> vec; pyutil_dict_to_str_vec(dict, vec); std::vector>::iterator i; @@ -152,23 +143,12 @@ defs_ptr add_variable_dict(defs_ptr self, const py::dict& dict) { } return self; } -void delete_variable(defs_ptr self, const std::string& name) { - self->server_state().delete_user_variable(name); -} -void sort_attributes(defs_ptr self, ecf::Attr::Type attr) { - self->sort_attributes(attr); -} -void sort_attributes1(defs_ptr self, ecf::Attr::Type attr, bool recurse) { - self->sort_attributes(attr, recurse); -} -void sort_attributes2(defs_ptr self, ecf::Attr::Type attr, bool recurse, const py::list& list) { - std::vector no_sort; - pyutil_list_to_str_vec(list, no_sort); - self->sort_attributes(attr, recurse, no_sort); +void Defs_delete_variable(defs_ptr self, const std::string& name) { + self->server_state().delete_user_variable(name); } -void sort_attributes3(defs_ptr self, const std::string& attribute_name, bool recursive, const py::list& list) { +void Defs_sort_attributes(defs_ptr self, const std::string& attribute_name, bool recursive, const py::list& list) { std::string attribute = attribute_name; boost::algorithm::to_lower(attribute); ecf::Attr::Type attr = ecf::Attr::to_attr(attribute_name); @@ -180,196 +160,234 @@ void sort_attributes3(defs_ptr self, const std::string& attribute_name, bool rec self->sort_attributes(attr, recursive, no_sort); } -// Support sized and Container protocol -size_t defs_len(defs_ptr self) { +size_t Defs_len(defs_ptr self) { return self->suiteVec().size(); } -bool defs_container(defs_ptr self, const std::string& name) { + +bool Defs_container(defs_ptr self, const std::string& name) { return (self->findSuite(name)) ? true : false; } -static py::object do_add(defs_ptr self, const py::object& arg) { - // std::cout << "defs::do_add \n"; - if (arg.ptr() == py::object().ptr()) { - return py::object(self); // *IGNORE* None +py::object Defs_add(defs_ptr self, const py::handle& arg) { + // When arg is None, there is nothing to do... + if (arg == py::none()) { + return py::cast(self); + } + + if (auto found = py_extract(arg); found) { + self->addSuite(found.value()); } - else if (py::extract(arg).check()) { - self->addSuite(py::extract(arg)); + else if (auto found = py_extract(arg); found) { + auto suite = std::make_shared(found.value()); + self->addSuite(suite); } - else if (py::extract(arg).check()) { - add_variable_dict(self, py::extract(arg)); + else if (auto found = py_extract(arg); found) { + Defs_add_variable_dict(self, found.value()); } - else if (py::extract(arg).check()) { - Edit edit = py::extract(arg); + else if (auto found = py_extract(arg); found) { + Edit edit = found.value(); + const std::vector& vec = edit.variables(); for (const auto& i : vec) { self->server_state().add_or_update_user_variables(i.name(), i.theValue()); } } - else if (py::extract(arg).check()) { - py::list the_list = py::extract(arg); + else if (auto found = py_extract(arg); found) { + py::list the_list = found.value(); + int the_list_size = len(the_list); for (int i = 0; i < the_list_size; ++i) { - (void)do_add(self, the_list[i]); // recursive + (void)Defs_add(self, the_list[i]); // recursive } } - else if (py::extract(arg).check()) { - Variable var = py::extract(arg); + else if (auto found = py_extract(arg); found) { + Variable var = found.value(); self->server_state().add_or_update_user_variables(var.name(), var.theValue()); } else { throw std::runtime_error("ExportDefs::add : Unknown type"); } - return py::object(self); + return py::cast(self); } -static py::object add(py::tuple args, py::dict kwargs) { - int the_list_size = len(args); - defs_ptr self = py::extract(args[0]); // self - if (!self) { - throw std::runtime_error("ExportDefs::add() : first argument is not a Defs"); +py::object Defs_add_args_kwargs(defs_ptr self, const py::args& args, const py::kwargs& kwargs) { + for (auto arg : args) { + Defs_add(self, arg); } + Defs_add_variable_dict(self, kwargs); - for (int i = 1; i < the_list_size; ++i) { - (void)do_add(self, args[i]); - } - (void)add_variable_dict(self, kwargs); - - return py::object(self); // return defs as python object, relies class_... for type registration + return py::cast(self); // return defs as python object, relies class_... for type registration } -static py::object defs_iadd(defs_ptr self, const py::list& list) { - // std::cout << "defs_iadd list " << self->name() << "\n"; +py::object Defs_iadd(defs_ptr self, const py::list& list) { int the_list_size = len(list); for (int i = 0; i < the_list_size; ++i) { - (void)do_add(self, list[i]); + (void)Defs_add(self, list[i]); } - return py::object(self); // return node_ptr as python object, relies class_... for type registration + return py::cast(self); // return defs_ptr as python object, relies class_... for type registration } -static py::object defs_getattr(defs_ptr self, const std::string& attr) { - // cout << " defs_getattr self.name() : " << self->name() << " attr " << attr << "\n"; +py::object Defs_getattr(defs_ptr self, const std::string& attr) { suite_ptr child = self->findSuite(attr); if (child) { - return py::object(child); + return py::cast(child); } Variable var = self->server_state().findVariable(attr); if (!var.empty()) { - return py::object(var); + return py::cast(var); } throw std::runtime_error(MESSAGE("ExportDefs::defs_getattr : function of name '" << attr << "' does not exist *OR* suite or defs variable")); } -py::object defs_raw_constructor(py::tuple args, py::dict kw) { - // cout << "defs_raw_constructor len(args):" << len(args) << endl; - // args[0] is Defs(i.e self) - py::list the_list; - std::string name; - for (int i = 1; i < len(args); ++i) { - if (py::extract(args[i]).check()) { - name = py::extract(args[i]); - } - else { - the_list.append(args[i]); - } +defs_ptr Defs_make(const std::string& filename) { + defs_ptr defs = Defs::create(); + + std::string errorMsg, warningMsg; + if (!defs->restore(filename, errorMsg, warningMsg)) { + throw std::runtime_error(errorMsg); } - if (!name.empty() && len(the_list) > 0) { - throw std::runtime_error("defs_raw_constructor: Can't mix string with other arguments. String argument " - "specifies a path(loads a definition from disk)"); + if (!warningMsg.empty()) { + std::cerr << warningMsg; } - return args[0].attr("__init__")(the_list, kw); // calls -> init(list attr, dict kw) + return defs; } -defs_ptr defs_init(py::list the_list, py::dict kw) { - // cout << " defs_init: the_list: " << len(the_list) << " dict: " << len(kw) << endl; +defs_ptr Defs_init(const py::args& args, const py::kwargs& kw) { defs_ptr defs = Defs::create(); - (void)add_variable_dict(defs, kw); - (void)defs_iadd(defs, the_list); + Defs_add_args_kwargs(defs, args, kw); return defs; } -void export_Defs() { - py::class_("Defs", DefsDoc::add_definition_doc(), py::init<>("Create a empty Defs")) - .def("__init__", py::raw_function(&defs_raw_constructor, 0)) // will call -> task_init - .def("__init__", py::make_constructor(&defs_init)) - .def("__init__", py::make_constructor(&create_defs), DefsDoc::add_definition_doc()) - .def(py::self == py::self) // __eq__ - .def("__copy__", pyutil_copy_object) // __copy__ uses copy constructor - .def("__str__", convert_to_string) // __str__ - .def("__enter__", &defs_enter) // allow with statement, hence indentation support - .def("__exit__", &defs_exit) // allow with statement, hence indentation support - .def("__len__", &defs_len) // Sized protocol - .def("__contains__", &defs_container) // Container protocol - .def("__iter__", py::range(&Defs::suite_begin, &Defs::suite_end)) // iterable protocol - .def("__getattr__", &defs_getattr) /* Any attempt to resolve a property, method, or field name that doesn't - actually exist on the object itself will be passed to __getattr__*/ - .def("__iadd__", &defs_iadd) // defs += [ Suite('s1'), Edit(var='value'), Variable('a','b') [ Suite('t2') ] ] - .def("__iadd__", &do_add) // defs += Suite("s1") - .def("__add__", &do_add) - .def("add", raw_function(add, 1), DefsDoc::add()) - .def("add_suite", &add_suite, DefsDoc::add_suite_doc()) +} // namespace + +void export_Defs(py::module& m) { + + py::class_(m, "Defs", py::dynamic_attr(), DefsDoc::add_definition_doc()) + + .def(py::init<>(), "Create an empty Defs") + .def(py::init(&Defs_make), DefsDoc::add_definition_doc()) + .def(py::init(&Defs_init)) + + .def(py::self == py::self) + .def("__copy__", pyutil_copy_object) + .def("__str__", Defs_str) + + // *** Iteration *** + + .def( + "__iter__", + [](const Defs& s) { return py::make_iterator(s.suiteVec().begin(), s.suiteVec().end()); }, + py::keep_alive<0, 1>()) + .def("__len__", &Defs_len) + .def("__contains__", &Defs_container) + + // *** Operators *** + + .def("__add__", &Defs_add) + .def("__iadd__", &Defs_iadd) // defs += [ Suite('s1'), Edit(var='value'), Variable('a','b') [ Suite('t2') ] ] + .def("__iadd__", &Defs_add) // defs += Suite("s1") + + // *** Context Manager *** + + .def("__enter__", &Defs_enter) // allow with statement, hence indentation support + .def("__exit__", &Defs_exit) // allow with statement, hence indentation support + + // *** Dynamic Attributes *** + + .def("__getattr__", &Defs_getattr) + + .def("add", &Defs_add_args_kwargs, DefsDoc::add()) + + .def("add_suite", &Defs_add_suite, DefsDoc::add_suite_doc()) .def("add_suite", &Defs::add_suite, GlossaryDoc::list()) + .def("add_extern", &Defs::add_extern, DefsDoc::add_extern_doc()) + .def("auto_add_externs", &Defs::auto_add_externs, DefsDoc::add_extern_doc()) - .def("add_variable", &add_variable, DefsDoc::add_variable_doc()) - .def("add_variable", &add_variable_int) - .def("add_variable", &add_variable_var) - .def("add_variable", &add_variable_dict) - .def("sort_attributes", &sort_attributes) - .def("sort_attributes", &sort_attributes1) - .def("sort_attributes", &sort_attributes2) + + .def("add_variable", &Defs_add_variable_string, DefsDoc::add_variable_doc()) + .def("add_variable", &Defs_add_variable_int) + .def("add_variable", &Defs_add_variable_variable) + .def("add_variable", &Defs_add_variable_dict) + + .def("sort_attributes", + &Defs_sort_attributes, + py::arg("attribute_type"), + py::arg("recursive") = true, + py::arg("no_sort") = py::list()) .def("sort_attributes", - &sort_attributes3, - (py::arg("attribute_type"), py::arg("recursive") = true, py::arg("no_sort") = py::list())) - .def("sort_attributes", &Defs::sort_attributes, (py::arg("attribute_type"), py::arg("recursive") = true)) - .def("delete_variable", &delete_variable, "An empty string will delete all user variables") + &Defs::sort_attributes, + py::arg("attribute_type"), + py::arg("recursive") = true, + py::arg("no_sort") = py::list()) + + .def("delete_variable", &Defs_delete_variable, "An empty string will delete all user variables") + .def("find_suite", &Defs::findSuite, "Given a name, find the corresponding `suite`_") + .def("find_abs_node", &Defs::findAbsNode, "Given a path, find the the `node`_") + .def("find_node_path", &Defs::find_node_path, "Given a type(suite,family,task) and a name, return path of the first match, otherwise return an empty " "string") + .def("find_node", &Defs::find_node, "Given a type(suite,family,task) and a path to a node, return the node.") - .def("get_all_nodes", &get_all_nodes, "Returns all the `node`_\\ s in the definition") - .def("get_all_tasks", &get_all_tasks, "Returns all the `task`_ nodes") + + .def("get_all_nodes", &Defs_get_all_nodes, "Returns all the `node`_\\ s in the definition") + + .def("get_all_tasks", &Defs_get_all_tasks, "Returns all the `task`_ nodes") + .def("has_time_dependencies", &Defs::hasTimeDependencies, "returns True if the `suite definition`_ has any time `dependencies`_") + .def("save_as_checkpt", &Defs::write_to_checkpt_file, "Save the in memory `suite definition`_ as a `check point`_ file. This includes all node state.") + .def("restore_from_checkpt", - &restore_from_checkpt, + &Defs_restore, "Restore the `suite definition`_ from a `check point`_ file stored on disk") + .def("save_as_defs", - &save_as_defs, + &Defs_save_as_defs_with_given_style, "Save the in memory `suite definition`_ into a file. The file name must be passed as an argument\n\n") + .def("save_as_defs", - &save_as_defs_1, + &Defs_save_as_defs_with_default_style, "Save the in memory `suite definition`_ into a file. The file name must be passed as an argument\n\n") - .def("check", &check_defs, DefsDoc::check()) - .def("simulate", &simulate, DefsDoc::simulate()) + + .def("check", &Defs_check, DefsDoc::check()) + + .def("simulate", &Defs_simulate, DefsDoc::simulate()) + .def("check_job_creation", - &check_job_creation, - (py::arg("throw_on_error") = false, py::arg("verbose") = false), + &Defs_check_job_creation, + py::arg("throw_on_error") = false, + py::arg("verbose") = false, DefsDoc::check_job_creation_doc()) + .def("check_job_creation", &Defs::check_job_creation) + .def("generate_scripts", &Defs::generate_scripts, DefsDoc::generate_scripts_doc()) + .def("get_state", &Defs::state) - .def("get_server_state", &get_server_state, DefsDoc::get_server_state()) - .add_property("suites", py::range(&Defs::suite_begin, &Defs::suite_end), "Returns a list of `suite`_\\ s") - .add_property("externs", py::range(&Defs::extern_begin, &Defs::extern_end), "Returns a list of `extern`_\\ s") - .add_property("user_variables", - py::range(&Defs::user_variables_begin, &Defs::user_variables_end), - "Returns a list of user defined `variable`_\\ s") - .add_property("server_variables", - py::range(&Defs::server_variables_begin, &Defs::server_variables_end), - "Returns a list of server `variable`_\\ s"); - -#if ECF_ENABLE_PYTHON_PTR_REGISTER - py::register_ptr_to_python(); // needed for mac and boost 1.6 -#endif + + .def("get_server_state", &Defs_get_server_state, DefsDoc::get_server_state()) + + .def_property_readonly( + "suites", + py::cpp_function([](const Defs& s) { return py::make_iterator(s.suiteVec().begin(), s.suiteVec().end()); }), + "Returns a list of `suite`_\\ s") + + .def_property_readonly("externs", &Defs::externs, "Returns a list of `extern`_\\ s") + + .def_property_readonly( + "user_variables", &Defs::user_variables, "Returns a list of user defined `variable`_\\ s") + + .def_property_readonly("server_variables", &Defs::server_variables, "Returns a list of server `variable`_\\ s"); } diff --git a/libs/pyext/src/ecflow/python/ExportNode.cpp b/libs/pyext/src/ecflow/python/ExportNode.cpp index 2186219f7..8391174cb 100644 --- a/libs/pyext/src/ecflow/python/ExportNode.cpp +++ b/libs/pyext/src/ecflow/python/ExportNode.cpp @@ -25,65 +25,82 @@ #include "ecflow/node/NodeAlgorithms.hpp" #include "ecflow/node/NodeContainer.hpp" #include "ecflow/python/DefsDoc.hpp" +#include "ecflow/python/Edit.hpp" +#include "ecflow/python/ExportCollections.hpp" #include "ecflow/python/NodeAttrDoc.hpp" #include "ecflow/python/NodeUtil.hpp" #include "ecflow/python/PythonBinding.hpp" #include "ecflow/python/PythonUtil.hpp" -// See: http://wiki.python.org/moin/boost.python/HowTo#boost.function_objects +namespace { -Defs* get_defs(node_ptr self) { +Defs* Node_get(node_ptr self) { return self->defs(); } -node_ptr add_variable(node_ptr self, const std::string& name, const std::string& value) { +node_ptr Node_add_variable_string(node_ptr self, const std::string& name, const std::string& value) { self->add_variable(name, value); return self; } -node_ptr add_variable_int(node_ptr self, const std::string& name, int value) { + +node_ptr Node_add_variable_int(node_ptr self, const std::string& name, int value) { self->add_variable_int(name, value); return self; } -node_ptr add_variable_var(node_ptr self, const Variable& var) { + +node_ptr Node_add_variable_variable(node_ptr self, const Variable& var) { self->addVariable(var); return self; } -node_ptr add_event(node_ptr self, const Event& e) { +node_ptr Node_add_variable_edit(node_ptr self, const Edit& edit) { + for (const auto& var : edit.variables()) { + self->addVariable(var); + } + return self; +} + +node_ptr Node_add_event(node_ptr self, const Event& e) { self->addEvent(e); return self; } -node_ptr add_event_1(node_ptr self, int number) { + +node_ptr Node_add_event_int(node_ptr self, int number) { self->addEvent(Event(number)); return self; } -node_ptr add_event_2(node_ptr self, int number, const std::string& name) { - self->addEvent(Event(number, name)); + +node_ptr Node_add_event_string(node_ptr self, const std::string& name) { + self->addEvent(Event(name)); return self; } -node_ptr add_event_3(node_ptr self, const std::string& name) { - self->addEvent(Event(name)); + +node_ptr Node_add_event_int_string(node_ptr self, int number, const std::string& name) { + self->addEvent(Event(number, name)); return self; } -node_ptr add_meter(node_ptr self, const Meter& m) { +node_ptr Node_add_meter(node_ptr self, const Meter& m) { self->addMeter(m); return self; } -node_ptr add_meter_1(node_ptr self, const std::string& meter_name, int min, int max, int color_change) { + +node_ptr Node_add_meter_1(node_ptr self, const std::string& meter_name, int min, int max, int color_change) { self->addMeter(Meter(meter_name, min, max, color_change)); return self; } -node_ptr add_meter_2(node_ptr self, const std::string& meter_name, int min, int max) { + +node_ptr Node_add_meter_2(node_ptr self, const std::string& meter_name, int min, int max) { self->addMeter(Meter(meter_name, min, max)); return self; } -node_ptr add_queue(node_ptr self, const QueueAttr& m) { +node_ptr Node_add_queue(node_ptr self, const QueueAttr& m) { self->add_queue(m); return self; } -node_ptr add_queue1(node_ptr self, const std::string& name, const py::list& list) { + +node_ptr Node_add_queue1(node_ptr self, const std::string& name, const py::list& list) { std::vector vec; pyutil_list_to_str_vec(list, vec); QueueAttr queue_attr(name, vec); @@ -91,11 +108,12 @@ node_ptr add_queue1(node_ptr self, const std::string& name, const py::list& list return self; } -node_ptr add_generic(node_ptr self, const GenericAttr& m) { +node_ptr Node_add_generic(node_ptr self, const GenericAttr& m) { self->add_generic(m); return self; } -node_ptr add_generic1(node_ptr self, const std::string& name, const py::list& list) { + +node_ptr Node_add_generic1(node_ptr self, const std::string& name, const py::list& list) { std::vector vec; pyutil_list_to_str_vec(list, vec); GenericAttr attr(name, vec); @@ -103,156 +121,182 @@ node_ptr add_generic1(node_ptr self, const std::string& name, const py::list& li return self; } -node_ptr add_label(node_ptr self, const std::string& name, const std::string& value) { +node_ptr Node_add_label(node_ptr self, const std::string& name, const std::string& value) { self->addLabel(Label(name, value)); return self; } -node_ptr add_label_1(node_ptr self, const Label& label) { + +node_ptr Node_add_label_1(node_ptr self, const Label& label) { self->addLabel(label); return self; } -node_ptr add_aviso(node_ptr self, const ecf::AvisoAttr& attr) { +node_ptr Node_add_aviso(node_ptr self, const ecf::AvisoAttr& attr) { self->addAviso(attr); return self; } -node_ptr add_mirror(node_ptr self, const ecf::MirrorAttr& attr) { +node_ptr Node_add_mirror(node_ptr self, const ecf::MirrorAttr& attr) { self->addMirror(attr); return self; } -node_ptr add_limit(node_ptr self, const std::string& name, int limit) { +node_ptr Node_add_limit(node_ptr self, const std::string& name, int limit) { self->addLimit(Limit(name, limit)); return self; } -node_ptr add_limit_1(node_ptr self, const Limit& limit) { + +node_ptr Node_add_limit_1(node_ptr self, const Limit& limit) { self->addLimit(limit); return self; } -node_ptr add_in_limit(node_ptr self, - const std::string& name, - const std::string& pathToNode, - int tokens, - bool limit_this_node_only) { + +node_ptr Node_add_in_limit(node_ptr self, + const std::string& name, + const std::string& pathToNode, + int tokens, + bool limit_this_node_only) { self->addInLimit(InLimit(name, pathToNode, tokens, limit_this_node_only)); return self; } -node_ptr add_in_limit_1(node_ptr self, const InLimit& inlimit) { +node_ptr Node_add_in_limit_1(node_ptr self, const InLimit& inlimit) { self->addInLimit(inlimit); return self; } -node_ptr add_time(node_ptr self, int hour, int minute) { + +node_ptr Node_add_time(node_ptr self, int hour, int minute) { self->addTime(ecf::TimeAttr(hour, minute)); return self; } -node_ptr add_time_1(node_ptr self, int hour, int minute, bool relative) { + +node_ptr Node_add_time_1(node_ptr self, int hour, int minute, bool relative) { self->addTime(ecf::TimeAttr(hour, minute, relative)); return self; } -node_ptr add_time_2(node_ptr self, const std::string& ts) { + +node_ptr Node_add_time_2(node_ptr self, const std::string& ts) { self->addTime(ecf::TimeAttr(ecf::TimeSeries::create(ts))); return self; } -node_ptr add_time_3(node_ptr self, const ecf::TimeAttr& ts) { + +node_ptr Node_add_time_3(node_ptr self, const ecf::TimeAttr& ts) { self->addTime(ts); return self; } -node_ptr add_today(node_ptr self, int hour, int minute) { + +node_ptr Node_add_today(node_ptr self, int hour, int minute) { self->addToday(ecf::TodayAttr(hour, minute)); return self; } -node_ptr add_today_1(node_ptr self, int hour, int minute, bool relative) { + +node_ptr Node_add_today_1(node_ptr self, int hour, int minute, bool relative) { self->addToday(ecf::TodayAttr(hour, minute, relative)); return self; } -node_ptr add_today_2(node_ptr self, const std::string& ts) { + +node_ptr Node_add_today_2(node_ptr self, const std::string& ts) { self->addToday(ecf::TodayAttr(ecf::TimeSeries::create(ts))); return self; } -node_ptr add_today_3(node_ptr self, const ecf::TodayAttr& ts) { + +node_ptr Node_add_today_3(node_ptr self, const ecf::TodayAttr& ts) { self->addToday(ts); return self; } -node_ptr add_date(node_ptr self, int day, int month, int year) { + +node_ptr Node_add_date(node_ptr self, int day, int month, int year) { self->addDate(DateAttr(day, month, year)); return self; } -node_ptr add_date_1(node_ptr self, const DateAttr& d) { + +node_ptr Node_add_date_1(node_ptr self, const DateAttr& d) { self->addDate(d); return self; } -node_ptr add_day(node_ptr self, DayAttr::Day_t day) { + +node_ptr Node_add_day(node_ptr self, DayAttr::Day_t day) { self->addDay(DayAttr(day)); return self; } -node_ptr add_day_1(node_ptr self, const std::string& day) { + +node_ptr Node_add_day_1(node_ptr self, const std::string& day) { self->addDay(DayAttr(DayAttr::getDay(day))); return self; } -node_ptr add_day_2(node_ptr self, const DayAttr& day) { + +node_ptr Node_add_day_2(node_ptr self, const DayAttr& day) { self->addDay(day); return self; } -node_ptr add_autocancel(node_ptr self, int days) { +node_ptr Node_add_autocancel(node_ptr self, int days) { self->addAutoCancel(ecf::AutoCancelAttr(days)); return self; } -node_ptr add_autocancel_1(node_ptr self, int hour, int min, bool relative) { + +node_ptr Node_add_autocancel_1(node_ptr self, int hour, int min, bool relative) { self->addAutoCancel(ecf::AutoCancelAttr(hour, min, relative)); return self; } -node_ptr add_autocancel_2(node_ptr self, const ecf::TimeSlot& ts, bool relative) { + +node_ptr Node_add_autocancel_2(node_ptr self, const ecf::TimeSlot& ts, bool relative) { self->addAutoCancel(ecf::AutoCancelAttr(ts, relative)); return self; } -node_ptr add_autocancel_3(node_ptr self, const ecf::AutoCancelAttr& attr) { + +node_ptr Node_add_autocancel_3(node_ptr self, const ecf::AutoCancelAttr& attr) { self->addAutoCancel(attr); return self; } -node_ptr add_autoarchive(node_ptr self, int days, bool idle) { + +node_ptr Node_add_autoarchive(node_ptr self, int days, bool idle) { self->add_autoarchive(ecf::AutoArchiveAttr(days, idle)); return self; } -node_ptr add_autoarchive_1(node_ptr self, int hour, int min, bool relative, bool idle) { + +node_ptr Node_add_autoarchive_1(node_ptr self, int hour, int min, bool relative, bool idle) { self->add_autoarchive(ecf::AutoArchiveAttr(hour, min, relative, idle)); return self; } -node_ptr add_autoarchive_2(node_ptr self, const ecf::TimeSlot& ts, bool relative, bool idle) { + +node_ptr Node_add_autoarchive_2(node_ptr self, const ecf::TimeSlot& ts, bool relative, bool idle) { self->add_autoarchive(ecf::AutoArchiveAttr(ts, relative, idle)); return self; } -node_ptr add_autoarchive_3(node_ptr self, const ecf::AutoArchiveAttr& attr) { + +node_ptr Node_add_autoarchive_3(node_ptr self, const ecf::AutoArchiveAttr& attr) { self->add_autoarchive(attr); return self; } -node_ptr add_zombie(node_ptr self, const ZombieAttr& attr) { +node_ptr Node_add_zombie(node_ptr self, const ZombieAttr& attr) { self->addZombie(attr); return self; } -node_ptr add_autorestore(node_ptr self, const ecf::AutoRestoreAttr& attr) { +node_ptr Node_add_autorestore(node_ptr self, const ecf::AutoRestoreAttr& attr) { self->add_autorestore(attr); return self; } -node_ptr add_autorestore1(node_ptr self, const py::list& list) { + +node_ptr Node_add_autorestore1(node_ptr self, const py::list& list) { std::vector vec; pyutil_list_to_str_vec(list, vec); self->add_autorestore(ecf::AutoRestoreAttr(vec)); return self; } -node_ptr add_cron(node_ptr self, const ecf::CronAttr& attr) { +node_ptr Node_add_cron(node_ptr self, const ecf::CronAttr& attr) { self->addCron(attr); return self; } -node_ptr add_late(node_ptr self, const ecf::LateAttr& attr) { + +node_ptr Node_add_late(node_ptr self, const ecf::LateAttr& attr) { self->addLate(attr); return self; } -std::string get_state_change_time(node_ptr self, const std::string& format) { + +std::string Node_get_state_change_time(node_ptr self, const std::string& format) { if (format == "iso_extended") { return to_iso_extended_string(self->state_change_time()); } @@ -262,48 +306,56 @@ std::string get_state_change_time(node_ptr self, const std::string& format) { return to_simple_string(self->state_change_time()); } -node_ptr add_repeat_date(node_ptr self, const RepeatDate& d) { +node_ptr Node_add_repeat_date(node_ptr self, const RepeatDate& d) { self->addRepeat(d); return self; } -node_ptr add_repeat_datetime(node_ptr self, const RepeatDateTime& d) { + +node_ptr Node_add_repeat_datetime(node_ptr self, const RepeatDateTime& d) { self->addRepeat(d); return self; } -node_ptr add_repeat_date_list(node_ptr self, const RepeatDateList& d) { + +node_ptr Node_add_repeat_date_list(node_ptr self, const RepeatDateList& d) { self->addRepeat(d); return self; } -node_ptr add_repeat_integer(node_ptr self, const RepeatInteger& d) { + +node_ptr Node_add_repeat_integer(node_ptr self, const RepeatInteger& d) { self->addRepeat(d); return self; } -node_ptr add_repeat_string(node_ptr self, const RepeatString& d) { + +node_ptr Node_add_repeat_string(node_ptr self, const RepeatString& d) { self->addRepeat(d); return self; } -node_ptr add_repeat_enum(node_ptr self, const RepeatEnumerated& d) { + +node_ptr Node_add_repeat_enum(node_ptr self, const RepeatEnumerated& d) { self->addRepeat(d); return self; } -node_ptr add_repeat_day(node_ptr self, const RepeatDay& d) { + +node_ptr Node_add_repeat_day(node_ptr self, const RepeatDay& d) { self->addRepeat(d); return self; } -void sort_attributes(node_ptr self, ecf::Attr::Type attr) { +void Node_sort_attributes(node_ptr self, ecf::Attr::Type attr) { self->sort_attributes(attr); } -void sort_attributes1(node_ptr self, ecf::Attr::Type attr, bool recursive) { + +void Node_sort_attributes1(node_ptr self, ecf::Attr::Type attr, bool recursive) { self->sort_attributes(attr, recursive); } -void sort_attributes2(node_ptr self, ecf::Attr::Type attr, bool recursive, const py::list& list) { + +void Node_sort_attributes2(node_ptr self, ecf::Attr::Type attr, bool recursive, const py::list& list) { std::vector no_sort; pyutil_list_to_str_vec(list, no_sort); self->sort_attributes(attr, recursive, no_sort); } -void sort_attributes3(node_ptr self, const std::string& attribute_name, bool recursive, const py::list& list) { +void Node_sort_attributes3(node_ptr self, const std::string& attribute_name, bool recursive, const py::list& list) { std::string attribute = attribute_name; boost::algorithm::to_lower(attribute); ecf::Attr::Type attr = ecf::Attr::to_attr(attribute_name); @@ -315,58 +367,69 @@ void sort_attributes3(node_ptr self, const std::string& attribute_name, bool rec self->sort_attributes(attr, recursive, no_sort); } -std::vector get_all_nodes(node_ptr self) { +std::vector Node_get_all_nodes(node_ptr self) { return ecf::get_all_nodes_ptr(self); } -node_ptr add_trigger(node_ptr self, const std::string& expr) { +node_ptr Node_add_trigger(node_ptr self, const std::string& expr) { self->add_trigger(expr); return self; } -node_ptr add_trigger_expr(node_ptr self, const Expression& expr) { + +node_ptr Node_add_trigger_expr(node_ptr self, const Expression& expr) { self->add_trigger_expr(expr); return self; } -node_ptr add_complete(node_ptr self, const std::string& expr) { + +node_ptr Node_add_complete(node_ptr self, const std::string& expr) { self->add_complete(expr); return self; } -node_ptr add_complete_expr(node_ptr self, const Expression& expr) { + +node_ptr Node_add_complete_expr(node_ptr self, const Expression& expr) { self->add_complete_expr(expr); return self; } -node_ptr add_part_trigger(node_ptr self, const PartExpression& expr) { + +node_ptr Node_add_part_trigger(node_ptr self, const PartExpression& expr) { self->add_part_trigger(PartExpression(expr)); return self; } -node_ptr add_part_trigger_1(node_ptr self, const std::string& expression) { + +node_ptr Node_add_part_trigger_1(node_ptr self, const std::string& expression) { self->add_part_trigger(PartExpression(expression)); return self; } -node_ptr add_part_trigger_2(node_ptr self, const std::string& expression, bool and_expr) { + +node_ptr Node_add_part_trigger_2(node_ptr self, const std::string& expression, bool and_expr) { self->add_part_trigger(PartExpression(expression, and_expr)); return self; } -node_ptr add_part_complete(node_ptr self, const PartExpression& expr) { + +node_ptr Node_add_part_complete(node_ptr self, const PartExpression& expr) { self->add_part_complete(PartExpression(expr)); return self; } -node_ptr add_part_complete_1(node_ptr self, const std::string& expression) { + +node_ptr Node_add_part_complete_1(node_ptr self, const std::string& expression) { self->add_part_complete(PartExpression(expression)); return self; } -node_ptr add_part_complete_2(node_ptr self, const std::string& expression, bool and_expr) { + +node_ptr Node_add_part_complete_2(node_ptr self, const std::string& expression, bool and_expr) { self->add_part_complete(PartExpression(expression, and_expr)); return self; } -bool evaluate_trigger(node_ptr self) { + +bool Node_evaluate_trigger(node_ptr self) { Ast* t = self->triggerAst(); if (t) { return t->evaluate(); } return false; } -bool evaluate_complete(node_ptr self) { + +bool Node_evaluate_complete(node_ptr self) { Ast* t = self->completeAst(); if (t) { return t->evaluate(); @@ -374,16 +437,16 @@ bool evaluate_complete(node_ptr self) { return false; } -node_ptr add_defstatus(node_ptr self, DState::State s) { +node_ptr Node_add_defstatus(node_ptr self, DState::State s) { self->addDefStatus(s); return self; } -node_ptr add_defstatus1(node_ptr self, const Defstatus& ds) { +node_ptr Node_add_defstatus1(node_ptr self, const Defstatus& ds) { self->addDefStatus(ds.state()); return self; } -py::list generated_variables_using_python_list(node_ptr self) { +py::list Node_generated_variables_using_python_list(node_ptr self) { py::list list; std::vector vec; self->gen_variables(vec); @@ -393,22 +456,29 @@ py::list generated_variables_using_python_list(node_ptr self) { return list; } -void generated_variables_using_variablelist(node_ptr self, std::vector& vec) { +void Node_generated_variables_using_variable_list(node_ptr self, std::vector& vec) { self->gen_variables(vec); } -///////////////////////////////////////////////////////////////////////////////////////// +py::object Node_add(node_ptr self, const py::object& arg) { + NodeUtil::add(*self, arg); + return py::cast(self); +} -static py::object do_rshift(node_ptr self, const py::object& arg) { - // std::cout << "do_rshift\n"; - (void)NodeUtil::do_add(self, arg); +py::object Node_iadd(node_ptr self, const py::object& arg) { + NodeUtil::add(*self, arg); + return py::cast(self); +} - if (py::extract(arg).check()) { - NodeContainer* nc = self->isNodeContainer(); - if (!nc) { - throw std::runtime_error("ExportNode::do_rshift() : Can only add a child to Suite or Family"); - } - node_ptr child = py::extract(arg); +py::object Node_rshift(node_ptr self, const py::object& arg) { + NodeContainer* nc = self->isNodeContainer(); + if (!nc) { + throw std::runtime_error("ExportNode::do_rshift() : Can only add a child to Suite or Family"); + } + + if (auto found = py_extract(arg); found) { + node_ptr child = found.value(); + self->addChild(child); std::vector children; nc->immediateChildren(children); @@ -429,19 +499,21 @@ static py::object do_rshift(node_ptr self, const py::object& arg) { } } } - return py::object(self); + else { + throw std::runtime_error("ExportNode::do_rshift() : Argument must be a node_ptr"); + } + return py::cast(self); } -static py::object do_lshift(node_ptr self, const py::object& arg) { - // std::cout << "do_lshift : " << self->name() << "\n"; cout << flush; - (void)NodeUtil::do_add(self, arg); - if (py::extract(arg).check()) { +py::object Node_lshift(node_ptr self, const py::object& arg) { + NodeContainer* nc = self->isNodeContainer(); + if (!nc) { + throw std::runtime_error("ExportNode::do_lshift() : Can only add a child to Suite or Family"); + } - NodeContainer* nc = self->isNodeContainer(); - if (!nc) { - throw std::runtime_error("ExportNode::do_lshift() : Can only add a child to Suite or Family"); - } - node_ptr child = py::extract(arg); + if (auto found = py_extract(arg); found) { + node_ptr child = found.value(); + self->addChild(child); std::vector children; nc->immediateChildren(children); @@ -466,363 +538,483 @@ static py::object do_lshift(node_ptr self, const py::object& arg) { } } } - return py::object(self); -} - -static py::object add(py::tuple args, py::dict kwargs) { - int the_list_size = len(args); - node_ptr self = py::extract(args[0]); // self - if (!self) { - throw std::runtime_error("ExportNode::add() : first argument is not a node"); - } - - for (int i = 1; i < the_list_size; ++i) { - (void)NodeUtil::do_add(self, args[i]); + else { + throw std::runtime_error("ExportNode::do_lshift() : Argument must be a node_ptr"); } + return py::cast(self); +} - // key word arguments are use for adding variable only - (void)NodeUtil::add_variable_dict(self, kwargs); - - return py::object(self); // return node_ptr as python object, relies class_... for type registration +py::object Node_add_args_kwargs(node_ptr self, const py::args& args, const py::kwargs& kwargs) { + NodeUtil::add(*self, args); + NodeUtil::add(*self, kwargs); + return py::cast(self); } -static py::object node_getattr(node_ptr self, const std::string& attr) { - // cout << " node_getattr self.name() : " << self->name() << " attr " << attr << "\n"; - size_t pos = 0; - node_ptr child = self->findImmediateChild(attr, pos); - if (child) { - return py::object(child); +py::object Node_getattr(node_ptr self, const std::string& attr) { + + size_t pos = 0; + if (node_ptr child = self->findImmediateChild(attr, pos); child) { + return py::cast(child); } - const Variable& var = self->findVariable(attr); - if (!var.empty()) { - return py::object(var); + if (const Variable& var = self->findVariable(attr); !var.empty()) { + return py::cast(var); } - const Variable& gvar = self->findGenVariable(attr); - if (!gvar.empty()) { - return py::object(gvar); + if (const Variable& gvar = self->findGenVariable(attr); !gvar.empty()) { + return py::cast(gvar); } - const Event& event = self->findEventByNameOrNumber(attr); - if (!event.empty()) { - return py::object(event); + if (const Event& event = self->findEventByNameOrNumber(attr); !event.empty()) { + return py::cast(event); } - const Meter& meter = self->findMeter(attr); - if (!meter.empty()) { - return py::object(meter); + if (const Meter& meter = self->findMeter(attr); !meter.empty()) { + return py::cast(meter); } - limit_ptr limit = self->find_limit(attr); - if (limit.get()) { - return py::object(limit); + if (limit_ptr limit = self->find_limit(attr); limit.get()) { + return py::cast(limit); } - throw std::runtime_error(MESSAGE( - "ExportNode::node_getattr: function of name '" - << attr << "' does not exist *OR* child node,variable,meter,event or limit on node " << self->absNodePath())); + throw std::runtime_error(MESSAGE("ExportNode::node_getattr: function of name '" + << attr + << "' does not exist *OR* child node, variable, meter, event or limit on node " + << self->absNodePath())); return py::object(); } -////////////////////////////////////////////////////////////////////////////////////////////////////////////// -struct null_deleter -{ - void operator()(void const*) const {} -}; -void do_replace_on_server(node_ptr self, ClientInvoker& theClient, bool suspend_node_first, bool force_replace) { - // cout << "ExportNode: do_replace_on_server " << self->absNodePath() << "\n"; - // cout << " suspend_node_first " << suspend_node_first << "\n"; - // cout << " force_replace " << force_replace << "\n"; +void Node_replace_on_server_basic(node_ptr self, + ClientInvoker& theClient, + bool suspend_node_first, + bool force_replace) { + struct null_deleter + { + void operator()(void const*) const {} + }; + + // Hack! Create a std::shared_ptr from a Defs*, avoiding double delete by using null_deleter + defs_ptr defs = defs_ptr(self->defs(), null_deleter()); - // Need to make a defs_ptr from a Defs* to avoid double delete use null_deletor - defs_ptr defs = defs_ptr(self->defs(), null_deleter()); bool create_parents_as_required = true; if (suspend_node_first) { theClient.suspend(self->absNodePath()); } theClient.replace_1(self->absNodePath(), defs, create_parents_as_required, force_replace); // this can throw } -void replace_on_server(node_ptr self, bool suspend_node_first, bool force_replace) { +void Node_replace_on_server(node_ptr self, bool suspend_node_first, bool force_replace) { ClientInvoker theClient; // assume HOST and PORT found from environment - do_replace_on_server(self, theClient, suspend_node_first, force_replace); + Node_replace_on_server_basic(self, theClient, suspend_node_first, force_replace); } -void replace_on_server1(node_ptr self, - const std::string& host, - const std::string& port, - bool suspend_node_first, - bool force_replace) { +void Node_replace_on_server1(node_ptr self, + const std::string& host, + const std::string& port, + bool suspend_node_first, + bool force_replace) { ClientInvoker theClient(host, port); - do_replace_on_server(self, theClient, suspend_node_first, force_replace); + Node_replace_on_server_basic(self, theClient, suspend_node_first, force_replace); } -void replace_on_server2(node_ptr self, const std::string& host_port, bool suspend_node_first, bool force_replace) { +void Node_replace_on_server2(node_ptr self, const std::string& host_port, bool suspend_node_first, bool force_replace) { ClientInvoker theClient(host_port); - do_replace_on_server(self, theClient, suspend_node_first, force_replace); + Node_replace_on_server_basic(self, theClient, suspend_node_first, force_replace); } -////////////////////////////////////////////////////////////////////////////////////////////////////////////// -const ecf::LateAttr* get_late_attr(node_ptr self) { +const ecf::LateAttr* Node_get_late(node_ptr self) { return self->get_late(); } -const ecf::AutoArchiveAttr* get_autoarchive_attr(node_ptr self) { +const ecf::AutoArchiveAttr* Node_get_autoarchive(node_ptr self) { return self->get_autoarchive(); } -const ecf::AutoCancelAttr* get_autocancel_attr(node_ptr self) { +const ecf::AutoCancelAttr* Node_get_autocancel(node_ptr self) { return self->get_autocancel(); } -const ecf::AutoRestoreAttr* get_autorestore_attr(node_ptr self) { +const ecf::AutoRestoreAttr* Node_get_autorestore(node_ptr self) { return self->get_autorestore(); } -const ecf::Flag& get_flag_attr(node_ptr self) { +const ecf::Flag& Node_get_flag(node_ptr self) { return self->get_flag(); } -void export_Node() { - // Turn off proxies by passing true as the NoProxy template parameter. - // shared_ptrs don't need proxies because calls on one a copy of the - // shared_ptr will affect all of them (duh!). - py::class_>("NodeVec", "Hold a list of Nodes (i.e `suite`_, `family`_ or `task`_\\ s)") - .def(py::vector_indexing_suite, true>()); - - py::class_("Node", DefsDoc::node_doc(), py::no_init) - .def("name", &Node::name, py::return_value_policy()) - .def("add", raw_function(add, 1), DefsDoc::add()) // a.add(b) & a.add([b]) - .def(py::self < py::self) // __lt__ - .def("__add__", &NodeUtil::do_add, DefsDoc::add()) // a + b - .def("__rshift__", - &do_rshift) // nc >> a >> b >> c a + (b.add(Trigger('a==complete')) + (c.add(Trigger('b==complete'))) - .def("__lshift__", - &do_lshift) // nc << a << b << c (a.add(Trigger('b==complete')) + (b.add(Trigger('c==complete'))) + c - .def("__iadd__", &NodeUtil::do_add) // a += b - .def("__iadd__", &NodeUtil::node_iadd) // a += [ b ] - .def("__getattr__", &node_getattr) /* Any attempt to resolve a property, method, or field name that doesn't - actually exist on the object itself will be passed to __getattr__*/ +} // namespace + +void export_Node(py::module& m) { + + py::class_>(m, "Node", py::dynamic_attr(), DefsDoc::node_doc()) + + // + // The '__lt__' is defined, instead of py::self < py::self, to avoid problems in older + // compilers (e.g. gcc 10) which complain about Node being an abstract class and thus not + // being able to determine the correct operator< overload + // + .def("__lt__", [](const Node& self, const Node& other) { return self < other; }) + + // *** Operators *** + + .def("__add__", &Node_add, DefsDoc::add()) + .def("__iadd__", &Node_iadd) + + // *** Shift operators *** + + .def("__rshift__", &Node_rshift) + // nc >> a >> b >> c a + (b.add(Trigger('a==complete')) + (c.add(Trigger('b==complete'))) + + .def("__lshift__", &Node_lshift) + // nc << a << b << c (a.add(Trigger('b==complete')) + (b.add(Trigger('c==complete'))) + c + + // *** Dynamic attributes *** + + .def("__getattr__", &Node_getattr) + + .def("name", &Node::name, py::return_value_policy::reference) + + .def("add", &Node_add_args_kwargs, DefsDoc::add()) + .def("remove", &Node::remove, "Remove the node from its parent. and returns it") - .def("add_trigger", &add_trigger, DefsDoc::add_trigger_doc()) - .def("add_trigger", &add_trigger_expr) - .def("add_complete", &add_complete, DefsDoc::add_trigger_doc()) - .def("add_complete", &add_complete_expr) - .def("add_part_trigger", &add_part_trigger, DefsDoc::add_trigger_doc()) - .def("add_part_trigger", &add_part_trigger_1) - .def("add_part_trigger", &add_part_trigger_2) - .def("add_part_complete", &add_part_complete, DefsDoc::add_trigger_doc()) - .def("add_part_complete", &add_part_complete_1) - .def("add_part_complete", &add_part_complete_2) - .def("evaluate_trigger", &evaluate_trigger, "evaluate trigger expression") - .def("evaluate_complete", &evaluate_complete, "evaluate complete expression") - .def("add_variable", &add_variable, DefsDoc::add_variable_doc()) - .def("add_variable", &add_variable_int) - .def("add_variable", &add_variable_var) - .def("add_variable", &NodeUtil::add_variable_dict) - .def("add_label", &add_label, DefsDoc::add_label_doc()) - .def("add_label", &add_label_1) - .def("add_aviso", &add_aviso, DefsDoc::add_aviso_doc()) - .def("add_mirror", &add_mirror, DefsDoc::add_mirror_doc()) - .def("add_limit", &add_limit, DefsDoc::add_limit_doc()) - .def("add_limit", &add_limit_1) + + .def("add_trigger", &Node_add_trigger, DefsDoc::add_trigger_doc()) + .def("add_trigger", &Node_add_trigger_expr) + + .def("add_complete", &Node_add_complete, DefsDoc::add_trigger_doc()) + .def("add_complete", &Node_add_complete_expr) + + .def("add_part_trigger", &Node_add_part_trigger, DefsDoc::add_trigger_doc()) + .def("add_part_trigger", &Node_add_part_trigger_1) + .def("add_part_trigger", &Node_add_part_trigger_2) + + .def("add_part_complete", &Node_add_part_complete, DefsDoc::add_trigger_doc()) + .def("add_part_complete", &Node_add_part_complete_1) + .def("add_part_complete", &Node_add_part_complete_2) + + .def("evaluate_trigger", &Node_evaluate_trigger, "evaluate trigger expression") + + .def("evaluate_complete", &Node_evaluate_complete, "evaluate complete expression") + + .def("add_variable", &Node_add_variable_string, DefsDoc::add_variable_doc()) + .def("add_variable", &Node_add_variable_int) + .def("add_variable", &Node_add_variable_variable) + .def("add_variable", &Node_add_variable_edit) + .def("add_variable", &NodeUtil::add1) + + .def("add_label", &Node_add_label, DefsDoc::add_label_doc()) + .def("add_label", &Node_add_label_1) + + .def("add_aviso", &Node_add_aviso, DefsDoc::add_aviso_doc()) + + .def("add_mirror", &Node_add_mirror, DefsDoc::add_mirror_doc()) + + .def("add_limit", &Node_add_limit, DefsDoc::add_limit_doc()) + .def("add_limit", &Node_add_limit_1) + .def("add_inlimit", - &add_in_limit, - (py::arg("limit_name"), - py::arg("path_to_node_containing_limit") = "", - py::arg("tokens") = 1, - py::arg("limit_this_node_only") = false), + &Node_add_in_limit, + py::arg("limit_name"), + py::arg("path_to_node_containing_limit") = "", + py::arg("tokens") = 1, + py::arg("limit_this_node_only") = false, DefsDoc::add_inlimit_doc()) - .def("add_inlimit", &add_in_limit_1) - .def("add_event", &add_event, DefsDoc::add_event_doc()) - .def("add_event", &add_event_1) - .def("add_event", &add_event_2) - .def("add_event", &add_event_3) - .def("add_meter", &add_meter, DefsDoc::add_meter_doc()) - .def("add_meter", &add_meter_1) - .def("add_meter", &add_meter_2) - .def("add_queue", &add_queue) - .def("add_queue", &add_queue1) - .def("add_generic", &add_generic) - .def("add_generic", &add_generic1) - .def("add_date", &add_date, DefsDoc::add_date_doc()) - .def("add_date", &add_date_1) - .def("add_day", &add_day, DefsDoc::add_day_doc()) - .def("add_day", &add_day_1) - .def("add_day", &add_day_2) - .def("add_today", &add_today, DefsDoc::add_today_doc()) - .def("add_today", &add_today_1) - .def("add_today", &add_today_2) - .def("add_today", &add_today_3) - .def("add_time", &add_time, DefsDoc::add_time_doc()) - .def("add_time", &add_time_1) - .def("add_time", &add_time_2) - .def("add_time", &add_time_3) - .def("add_cron", &add_cron, DefsDoc::add_cron_doc()) - .def("add_late", &add_late, DefsDoc::add_late_doc()) - .def("add_autocancel", &add_autocancel, DefsDoc::add_autocancel_doc()) - .def("add_autocancel", &add_autocancel_1) - .def("add_autocancel", &add_autocancel_2) - .def("add_autocancel", &add_autocancel_3) + .def("add_inlimit", &Node_add_in_limit_1) + + .def("add_event", &Node_add_event, DefsDoc::add_event_doc()) + .def("add_event", &Node_add_event_int) + .def("add_event", &Node_add_event_string) + .def("add_event", &Node_add_event_int_string) + + .def("add_meter", &Node_add_meter, DefsDoc::add_meter_doc()) + .def("add_meter", &Node_add_meter_1) + .def("add_meter", &Node_add_meter_2) + + .def("add_queue", &Node_add_queue) + .def("add_queue", &Node_add_queue1) + + .def("add_generic", &Node_add_generic) + .def("add_generic", &Node_add_generic1) + + .def("add_date", &Node_add_date, DefsDoc::add_date_doc()) + .def("add_date", &Node_add_date_1) + + .def("add_day", &Node_add_day, DefsDoc::add_day_doc()) + .def("add_day", &Node_add_day_1) + .def("add_day", &Node_add_day_2) + + .def("add_today", &Node_add_today, DefsDoc::add_today_doc()) + .def("add_today", &Node_add_today_1) + .def("add_today", &Node_add_today_2) + .def("add_today", &Node_add_today_3) + + .def("add_time", &Node_add_time, DefsDoc::add_time_doc()) + .def("add_time", &Node_add_time_1) + .def("add_time", &Node_add_time_2) + .def("add_time", &Node_add_time_3) + + .def("add_cron", &Node_add_cron, DefsDoc::add_cron_doc()) + + .def("add_late", &Node_add_late, DefsDoc::add_late_doc()) + + .def("add_autocancel", &Node_add_autocancel, DefsDoc::add_autocancel_doc()) + .def("add_autocancel", &Node_add_autocancel_1) + .def("add_autocancel", &Node_add_autocancel_2) + .def("add_autocancel", &Node_add_autocancel_3) + .def("add_autoarchive", - &add_autoarchive, - (py::arg("days"), py::arg("idle") = false), + &Node_add_autoarchive, + py::arg("days"), + py::arg("idle") = false, DefsDoc::add_autoarchive_doc()) .def("add_autoarchive", - &add_autoarchive_1, - (py::arg("hour"), py::arg("min"), py::arg("relative"), py::arg("idle") = false)) - .def("add_autoarchive", &add_autoarchive_2, (py::arg("TimeSlot"), py::arg("relative"), py::arg("idle") = false)) - .def("add_autoarchive", &add_autoarchive_3) - .def("add_autorestore", &add_autorestore, DefsDoc::add_autorestore_doc()) - .def("add_autorestore", &add_autorestore1) + &Node_add_autoarchive_1, + py::arg("hour"), + py::arg("min"), + py::arg("relative"), + py::arg("idle") = false) + .def("add_autoarchive", + &Node_add_autoarchive_2, + py::arg("TimeSlot"), + py::arg("relative"), + py::arg("idle") = false) + .def("add_autoarchive", &Node_add_autoarchive_3) + + .def("add_autorestore", &Node_add_autorestore, DefsDoc::add_autorestore_doc()) + .def("add_autorestore", &Node_add_autorestore1) + .def("add_verify", &Node::addVerify, DefsDoc::add_verify_doc()) - .def("add_repeat", &add_repeat_date, DefsDoc::add_repeat_date_doc()) - .def("add_repeat", &add_repeat_datetime, DefsDoc::add_repeat_datetime_doc()) - .def("add_repeat", &add_repeat_date_list, DefsDoc::add_repeat_date_list_doc()) - .def("add_repeat", &add_repeat_integer, DefsDoc::add_repeat_integer_doc()) - .def("add_repeat", &add_repeat_string, DefsDoc::add_repeat_string_doc()) - .def("add_repeat", &add_repeat_enum, DefsDoc::add_repeat_enumerated_doc()) - .def("add_repeat", &add_repeat_day, DefsDoc::add_repeat_day_doc()) - .def("add_defstatus", &add_defstatus, DefsDoc::add_defstatus_doc()) - .def("add_defstatus", &add_defstatus1, DefsDoc::add_defstatus_doc()) - .def("add_zombie", &add_zombie, NodeAttrDoc::zombie_doc()) + + .def("add_repeat", &Node_add_repeat_date, DefsDoc::add_repeat_date_doc()) + .def("add_repeat", &Node_add_repeat_datetime, DefsDoc::add_repeat_datetime_doc()) + .def("add_repeat", &Node_add_repeat_date_list, DefsDoc::add_repeat_date_list_doc()) + .def("add_repeat", &Node_add_repeat_integer, DefsDoc::add_repeat_integer_doc()) + .def("add_repeat", &Node_add_repeat_string, DefsDoc::add_repeat_string_doc()) + .def("add_repeat", &Node_add_repeat_enum, DefsDoc::add_repeat_enumerated_doc()) + .def("add_repeat", &Node_add_repeat_day, DefsDoc::add_repeat_day_doc()) + + .def("add_defstatus", &Node_add_defstatus, DefsDoc::add_defstatus_doc()) + .def("add_defstatus", &Node_add_defstatus1, DefsDoc::add_defstatus_doc()) + + .def("add_zombie", &Node_add_zombie, NodeAttrDoc::zombie_doc()) + .def("delete_variable", &Node::deleteVariable) + .def("delete_event", &Node::deleteEvent) + .def("delete_meter", &Node::deleteMeter) + .def("delete_label", &Node::deleteLabel) + .def("delete_queue", &Node::delete_queue) + .def("delete_generic", &Node::delete_generic) + .def("delete_trigger", &Node::deleteTrigger) + .def("delete_complete", &Node::deleteComplete) + .def("delete_repeat", &Node::deleteRepeat) + .def("delete_limit", &Node::deleteLimit) + .def("delete_inlimit", &Node::deleteInlimit) + .def("delete_time", &Node::deleteTime) .def("delete_time", &Node::delete_time) + .def("delete_today", &Node::deleteToday) .def("delete_today", &Node::delete_today) + .def("delete_date", &Node::deleteDate) .def("delete_date", &Node::delete_date) + .def("delete_day", &Node::deleteDay) .def("delete_day", &Node::delete_day) + .def("delete_cron", &Node::deleteCron) .def("delete_cron", &Node::delete_cron) + .def("delete_zombie", &Node::deleteZombie) .def("delete_zombie", &Node::delete_zombie) + .def("change_trigger", &Node::changeTrigger) + .def("change_complete", &Node::changeComplete) - .def("sort_attributes", &sort_attributes) - .def("sort_attributes", &sort_attributes1) - .def("sort_attributes", &sort_attributes2) + + .def("sort_attributes", &Node_sort_attributes) + .def("sort_attributes", &Node_sort_attributes1) + .def("sort_attributes", &Node_sort_attributes2) .def("sort_attributes", - &sort_attributes3, - (py::arg("attribute_type"), py::arg("recursive") = true, py::arg("no_sort") = py::list())) - .def("sort_attributes", &Node::sort_attributes, (py::arg("attribute_type"), py::arg("recursive") = true)) + &Node_sort_attributes3, + py::arg("attribute_type"), + py::arg("recursive") = true, + py::arg("no_sort") = py::list()) + .def("sort_attributes", + &Node::sort_attributes, + py::arg("attribute_type"), + py::arg("recursive") = true, + py::arg("no_sort") = py::list()) + .def("get_abs_node_path", &Node::absNodePath, DefsDoc::abs_node_path_doc()) + .def("has_time_dependencies", &Node::hasTimeDependencies) + .def("update_generated_variables", &Node::update_generated_variables) + .def("get_generated_variables", - &generated_variables_using_python_list, + &Node_generated_variables_using_python_list, "Returns the list of generated variables.") .def("get_generated_variables", - &generated_variables_using_variablelist, + &Node_generated_variables_using_variable_list, "Retrieves the list of generated variables. Pass in ecflow.VariableList as argument to hold variables.") + .def("is_suspended", &Node::isSuspended, "Returns true if the `node`_ is in a `suspended`_ state") + .def("find_variable", &Node::findVariable, - py::return_value_policy(), + py::return_value_policy::reference, "Find user variable on the node only. Returns an object") + .def("find_gen_variable", &Node::findGenVariable, - py::return_value_policy(), + py::return_value_policy::reference, "Find generated variable on the node only. Returns an object") + .def("find_parent_variable", &Node::find_parent_variable, - py::return_value_policy(), + py::return_value_policy::reference, "Find user variable variable up the parent hierarchy. Returns an object") + .def("find_parent_variable_sub_value", &Node::find_parent_variable_sub_value, "Find user variable *up* node tree, then variable substitute the value, otherwise return empty string") + .def("find_meter", &Node::findMeter, - py::return_value_policy(), + py::return_value_policy::reference, "Find the `meter`_ on the node only. Returns an object") + .def("find_event", &Node::findEventByNameOrNumber, - py::return_value_policy(), + py::return_value_policy::reference, "Find the `event`_ on the node only. Returns a object") + .def("find_label", &Node::find_label, - py::return_value_policy(), + py::return_value_policy::reference, "Find the `label`_ on the node only. Returns a object") + .def("find_queue", &Node::find_queue, - py::return_value_policy(), + py::return_value_policy::reference, "Find the queue on the node only. Returns a queue object") + .def("find_generic", &Node::find_generic, - py::return_value_policy(), + py::return_value_policy::reference, "Find the `generic`_ on the node only. Returns a Generic object") + .def("find_limit", &Node::find_limit, "Find the `limit`_ on the node only. returns a limit ptr") + .def("find_node_up_the_tree", &Node::find_node_up_the_tree, "Search immediate node, then up the node hierarchy") + .def("get_state", &Node::state, "Returns the state of the node. This excludes the suspended state") + .def("get_state_change_time", - &get_state_change_time, - (py::arg("format") = "iso_extended"), + &Node_get_state_change_time, + py::arg("format") = "iso_extended", "Returns the time of the last state change as a string. Default format is iso_extended, (iso_extended, " "iso, simple)") + .def("get_dstate", &Node::dstate, "Returns the state of node. This will include suspended state") + .def("get_defstatus", &Node::defStatus) - .def("get_repeat", &Node::repeat, py::return_value_policy()) - .def("get_late", &get_late_attr, py::return_internal_reference<>()) - .def("get_autocancel", &get_autocancel_attr, py::return_internal_reference<>()) - .def("get_autoarchive", &get_autoarchive_attr, py::return_internal_reference<>()) - .def("get_autorestore", &get_autorestore_attr, py::return_internal_reference<>()) - .def("get_trigger", &Node::get_trigger, py::return_internal_reference<>()) - .def("get_complete", &Node::get_complete, py::return_internal_reference<>()) - .def("get_defs", get_defs, py::return_internal_reference<>()) - .def("get_parent", &Node::parent, py::return_internal_reference<>()) - .def("get_all_nodes", &get_all_nodes, "Returns all the child nodes") + + .def("get_repeat", &Node::repeat, py::return_value_policy::reference) + + .def("get_late", &Node_get_late, py::return_value_policy::reference_internal) + + .def("get_autocancel", &Node_get_autocancel, py::return_value_policy::reference_internal) + + .def("get_autoarchive", &Node_get_autoarchive, py::return_value_policy::reference_internal) + + .def("get_autorestore", &Node_get_autorestore, py::return_value_policy::reference_internal) + + .def("get_trigger", &Node::get_trigger, py::return_value_policy::reference_internal) + + .def("get_complete", &Node::get_complete, py::return_value_policy::reference_internal) + + .def("get_defs", Node_get, py::return_value_policy::reference_internal) + + .def("get_parent", &Node::parent, py::return_value_policy::reference_internal) + + .def("get_all_nodes", &Node_get_all_nodes, "Returns all the child nodes") + .def("get_flag", - &get_flag_attr, - py::return_value_policy(), + &Node_get_flag, + py::return_value_policy::reference, "Return additional state associated with a node.") + .def("replace_on_server", - &replace_on_server, - (py::arg("suspend_node_first") = true, py::arg("force") = true), + &Node_replace_on_server, + py::arg("suspend_node_first") = true, + py::arg("force") = true, "replace node on the server.") .def("replace_on_server", - &replace_on_server1, - (py::arg("suspend_node_first") = true, py::arg("force") = true), + &Node_replace_on_server1, + py::arg("host"), + py::arg("port"), + py::arg("suspend_node_first") = true, + py::arg("force") = true, "replace node on the server.") .def("replace_on_server", - &replace_on_server2, - (py::arg("suspend_node_first") = true, py::arg("force") = true), + &Node_replace_on_server2, + py::arg("host_port"), + py::arg("suspend_node_first") = true, + py::arg("force") = true, "replace node on the server.") .def("replace_on_server", - &do_replace_on_server, - (py::arg("suspend_node_first") = true, py::arg("force") = true), + &Node_replace_on_server_basic, + py::arg("client"), + py::arg("suspend_node_first") = true, + py::arg("force") = true, "replace node on the server.") - .add_property("meters", py::range(&Node::meter_begin, &Node::meter_end), "Returns a list of `meter`_\\ s") - .add_property("events", py::range(&Node::event_begin, &Node::event_end), "Returns a list of `event`_\\ s") - .add_property("variables", - py::range(&Node::variable_begin, &Node::variable_end), - "Returns a list of user defined `variable`_\\ s") - .add_property("labels", py::range(&Node::label_begin, &Node::label_end), "Returns a list of `label`_\\ s") - .add_property("avisos", py::range(&Node::aviso_begin, &Node::aviso_end), "Returns a list of `aviso`_\\ s") - .add_property("mirrors", py::range(&Node::mirror_begin, &Node::mirror_end), "Returns a list of `mirror`_\\ s") - .add_property("limits", py::range(&Node::limit_begin, &Node::limit_end), "Returns a list of `limit`_\\ s") - .add_property( - "inlimits", py::range(&Node::inlimit_begin, &Node::inlimit_end), "Returns a list of `inlimit`_\\ s") - .add_property("verifies", py::range(&Node::verify_begin, &Node::verify_end), "Returns a list of Verify's") - .add_property("times", py::range(&Node::time_begin, &Node::time_end), "Returns a list of `time`_\\ s") - .add_property("todays", py::range(&Node::today_begin, &Node::today_end), "Returns a list of `today`_\\ s") - .add_property("dates", py::range(&Node::date_begin, &Node::date_end), "Returns a list of `date`_\\ s") - .add_property("days", py::range(&Node::day_begin, &Node::day_end), "Returns a list of `day`_\\ s") - .add_property("crons", py::range(&Node::cron_begin, &Node::cron_end), "Returns a list of `cron`_\\ s") - .add_property("zombies", py::range(&Node::zombie_begin, &Node::zombie_end), "Returns a list of `zombie`_\\ s") - .add_property("queues", py::range(&Node::queue_begin, &Node::queue_end), "Returns a list of `queue`_\\ s") - .add_property( - "generics", py::range(&Node::generic_begin, &Node::generic_end), "Returns a list of `generic`_\\ s"); -#if ECF_ENABLE_PYTHON_PTR_REGISTER - py::register_ptr_to_python(); // needed for mac and boost 1.6 -#endif + + .def_property_readonly("meters", &Node::meters, "Returns a list of `meter`_\\ s") + + .def_property_readonly("events", &Node::events, "Returns a list of `event`_\\ s") + + .def_property_readonly("variables", &Node::variables, "Returns a list of user defined `variable`_\\ s") + + .def_property_readonly("labels", &Node::labels, "Returns a list of `label`_\\ s") + + .def_property_readonly("avisos", + static_cast& (Node::*)() const>(&Node::avisos), + py::return_value_policy::reference_internal, + "Returns a list of `aviso`_\\ s") + + .def_property_readonly("mirrors", + static_cast& (Node::*)() const>(&Node::mirrors), + py::return_value_policy::reference_internal, + "Returns a list of `mirror`_\\ s") + + .def_property_readonly("limits", &Node::limits, "Returns a list of `limit`_\\ s") + + .def_property_readonly("inlimits", &Node::inlimits, "Returns a list of `inlimit`_\\ s") + + .def_property_readonly("verifies", &Node::verifys, "Returns a list of Verify's") + + .def_property_readonly("times", &Node::timeVec, "Returns a list of `time`_\\ s") + + .def_property_readonly("todays", &Node::todayVec, "Returns a list of `today`_\\ s") + + .def_property_readonly("dates", &Node::dates, "Returns a list of `date`_\\ s") + + .def_property_readonly("days", &Node::days, "Returns a list of `day`_\\ s") + + .def_property_readonly("crons", &Node::crons, "Returns a list of `cron`_\\ s") + + .def_property_readonly("zombies", &Node::zombies, "Returns a list of `zombie`_\\ s") + + .def_property_readonly("queues", &Node::queues, "Returns a list of `queue`_\\ s") + + .def_property_readonly("generics", &Node::generics, "Returns a list of `generic`_\\ s"); } diff --git a/libs/pyext/src/ecflow/python/ExportNodeAttr.cpp b/libs/pyext/src/ecflow/python/ExportNodeAttr.cpp index 4243de339..aa51b21ab 100644 --- a/libs/pyext/src/ecflow/python/ExportNodeAttr.cpp +++ b/libs/pyext/src/ecflow/python/ExportNodeAttr.cpp @@ -37,115 +37,200 @@ #include "ecflow/node/Limit.hpp" #include "ecflow/node/MirrorAttr.hpp" #include "ecflow/python/DefsDoc.hpp" +#include "ecflow/python/ExportCollections.hpp" #include "ecflow/python/NodeAttrDoc.hpp" #include "ecflow/python/PythonBinding.hpp" #include "ecflow/python/PythonUtil.hpp" #include "ecflow/python/Trigger.hpp" -// See: http://wiki.python.org/moin/boost.python/HowTo#boost.function_objects -/////////////////////////////////////////////////////////////////////////////////////////////////// -py::object late_raw_constructor(py::tuple args, py::dict kw) { - // cout << "late_raw_constructor len(args):" << len(args) << endl; - // args[0] is Late(i.e self) - if (len(args) > 1) { - throw std::runtime_error("late_raw_constructor: Late only expects keyword arguments, ie. " - "Late(submitted='00:20',active='15:00',complete='+30:00')"); +namespace { + +// JobCreationCtrl + +job_creation_ctrl_ptr JobCreationCtrl_make() { + return std::make_shared(); +} + +// ZombieAttr + +ZombieAttr ZombieAttr_make(ecf::Child::ZombieType zt, const py::list& list, ecf::ZombieCtrlAction uc) { + + int the_list_size = len(list); + + std::vector vec; + vec.reserve(the_list_size); + + for (int i = 0; i < the_list_size; ++i) { + vec.push_back(list[i].cast()); } - return args[0].attr("__init__")(kw); // calls -> late_init(dict kw) + return ZombieAttr(zt, vec, uc); } -static void extract_late_keyword_arguments(std::shared_ptr late, py::dict& dict) { - py::list keys = dict.keys(); - const int no_of_keys = len(keys); - for (int i = 0; i < no_of_keys; ++i) { - if (py::extract(keys[i]).check()) { - std::string first = py::extract(keys[i]); - if (py::extract(dict[keys[i]]).check()) { - std::string second = py::extract(dict[keys[i]]); - int hour = 0; - int min = 0; - bool relative = ecf::TimeSeries::getTime(second, hour, min); - if (first == "submitted") { - late->add_submitted(hour, min); - } - else if (first == "active") { - late->add_active(hour, min); - } - else if (first == "complete") { - late->add_complete(hour, min, relative); - } - else { - throw std::runtime_error( - "extract_late_keyword_arguments: keyword arguments, expected [submitted | active | complete]"); - } - } - else { - throw std::runtime_error("extract_late_keyword_arguments: expected keyword arguments to be a string, " - "ie Late(submitted='00:20',active='15:00',complete='+30:00')"); - } - } +ZombieAttr ZombieAttr_make_lifetime(ecf::Child::ZombieType zt, + const py::list& list, + ecf::ZombieCtrlAction uc, + int life_time_in_server) { + + int the_list_size = len(list); + + std::vector vec; + vec.reserve(the_list_size); + + for (int i = 0; i < the_list_size; ++i) { + vec.push_back(list[i].cast()); } + return ZombieAttr(zt, vec, uc, life_time_in_server); } -static std::shared_ptr late_init(py::dict& dict) { - std::shared_ptr late = std::make_shared(); - extract_late_keyword_arguments(late, dict); - return late; +// Limit + +py::list Limit_node_paths(Limit* limit) { + py::list list; + const std::set& paths = limit->paths(); + for (std::string path : paths) { + list.append(path); + } + return list; } -static std::shared_ptr late_create() { - return std::make_shared(); +// Queue + +QueueAttr Queue_make(const std::string& name, const py::list& list) { + std::vector vec; + pyutil_list_to_str_vec(list, vec); + return QueueAttr(name, vec); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -py::object cron_raw_constructor(py::tuple args, py::dict kw) { - // cout << "cron_raw_constructor len(args):" << len(args) << endl; - // args[0] is Cron(i.e self) args[1] is string name - for (int i = 1; i < len(args); ++i) { - if (py::extract(args[i]).check()) { - std::string time_series = py::extract(args[i]); - if (time_series.empty()) { - throw std::runtime_error("cron_raw_constructor: Empty string, please pass a valid time, i.e '12:30'"); - } - return args[0].attr("__init__")(time_series, kw); // calls -> init(const std::string& ts, dict kw) +// Generic + +GenericAttr Generic_make(const std::string& name, const py::list& list) { + std::vector vec; + pyutil_list_to_str_vec(list, vec); + return GenericAttr(name, vec); +} + +// Late + +void Late_extract_from_kwargs(std::shared_ptr late, const py::kwargs& kwargs) { + + for (const auto& entry : kwargs) { + // 1. Extract the keywork argument name + std::string first; + if (auto found = py_extract(entry.first); found) { + first = found.value(); + } + else if (auto found = py_extract(entry.first); found) { + first = found.value(); } - if (py::extract(args[i]).check()) { - ecf::TimeSeries time_series = py::extract(args[i]); - return args[0].attr("__init__")(time_series, kw); // calls -> init(const ecf::TimeSeries& ts, dict kw) + else { + throw std::runtime_error("extract_late_keyword_arguments: keyword argument name expected to be a string"); + } + + // 1.a Ensure the keyword argument is an expected value + static const std::vector valid_keywords = {"submitted", "active", "complete"}; + if (find(valid_keywords.begin(), valid_keywords.end(), first) == valid_keywords.end()) { + throw std::runtime_error("extract_late_keyword_arguments: keyword argument expected to be one of " + "[submitted | active | complete]"); + } + + // 2. Extract keyword argument value + std::string second; + if (auto found = py_extract(entry.second); found) { + second = found.value(); + } + else if (auto found = py_extract(entry.second); found) { + second = found.value(); } else { - throw std::runtime_error("cron_raw_constructor: expects string | TimeSeries and keyword arguments"); + throw std::runtime_error("extract_late_keyword_arguments: keyword argument value expected to be a string, " + "ie Late(submitted='00:20',active='15:00',complete='+30:00')"); + } + + // 3. Convert the value to hour/min/relative, and set the Late attributes + int hour = 0; + int min = 0; + bool relative = ecf::TimeSeries::getTime(second, hour, min); + if (first == "submitted") { + late->add_submitted(hour, min); + } + else if (first == "active") { + late->add_active(hour, min); + } + else if (first == "complete") { + late->add_complete(hour, min, relative); } } - throw std::runtime_error("cron_raw_constructor: expects string | TimeSeries and keyword arguments !!"); - return py::object(); } -static void extract_cron_keyword_arguments(std::shared_ptr cron, py::dict& dict) { - py::list keys = dict.keys(); - const int no_of_keys = len(keys); - for (int i = 0; i < no_of_keys; ++i) { +std::shared_ptr Late_make_default() { + return std::make_shared(); +} + +std::shared_ptr Late_make(const py::args& args, const py::kwargs& kwargs) { + int arg_count = len(args); + if (arg_count > 0) { + throw std::runtime_error("late_init: Late only expects keyword arguments, i.e. " + "Late(submitted='00:20',active='15:00',complete='+30:00')"); + } + auto late = std::make_shared(); + Late_extract_from_kwargs(late, kwargs); + return late; +} + +// AutoRestoreAttr + +ecf::AutoRestoreAttr AutoRestore_make(const py::list& list) { + std::vector vec; + pyutil_list_to_str_vec(list, vec); + return ecf::AutoRestoreAttr(vec); +} + +// RepeatDateList + +RepeatDateList RepeatDateList_make(const std::string& name, const py::list& list) { + std::vector vec; + pyutil_list_to_int_vec(list, vec); + return RepeatDateList(name, vec); +} + +// RepeatEnumerated + +RepeatEnumerated RepeatEnumerated_make(const std::string& name, const py::list& list) { + std::vector vec; + pyutil_list_to_str_vec(list, vec); + return RepeatEnumerated(name, vec); +} + +// RepeatString + +RepeatString RepeatString_make(const std::string& name, const py::list& list) { + std::vector vec; + pyutil_list_to_str_vec(list, vec); + return RepeatString(name, vec); +} - if (py::extract(keys[i]).check()) { - std::string first = py::extract(keys[i]); - if (py::extract(dict[keys[i]]).check()) { +// Cron - py::list second = py::extract(dict[keys[i]]); - std::vector int_vec; - pyutil_list_to_int_vec(second, int_vec); +void extract_cron_keyword_arguments(std::shared_ptr cron, py::dict& dict) { + for (auto entry : dict) { + if (auto found_key = py_extract(entry.first); found_key) { + std::string key = found_key.value(); - // expected keywords are: days_of_week,last_week_days_ofThe_month, days_of_month, months - if (first == "days_of_week") { - cron->addWeekDays(int_vec); + if (auto found_value = py_extract(entry.second); found_value) { + std::vector value; + pyutil_list_to_int_vec(found_value.value(), value); + + if (key == "days_of_week") { + cron->addWeekDays(value); } - else if (first == "days_of_month") { - cron->addDaysOfMonth(int_vec); + else if (key == "days_of_month") { + cron->addDaysOfMonth(value); } - else if (first == "months") { - cron->addMonths(int_vec); + else if (key == "months") { + cron->addMonths(value); } - else if (first == "last_week_days_of_the_month") { - cron->add_last_week_days_of_month(int_vec); + else if (key == "last_week_days_of_the_month") { + cron->add_last_week_days_of_month(value); } else { throw std::runtime_error( @@ -153,8 +238,8 @@ static void extract_cron_keyword_arguments(std::shared_ptr cron, "last_week_days_of_the_month | days_of_month | months | last_day_of_the_month"); } } - else if (py::extract(dict[keys[i]]).check()) { - if (first == "last_day_of_the_month") { + else if (auto found_value = py_extract(entry.second); found_value) { + if (key == "last_day_of_the_month" && found_value.value()) { cron->add_last_day_of_month(); } else { @@ -170,285 +255,143 @@ static void extract_cron_keyword_arguments(std::shared_ptr cron, } } -static std::shared_ptr cron_init(const std::string& ts, py::dict& dict) { +std::shared_ptr Cron_make() { + return std::make_shared(); +} + +std::shared_ptr cron_make_string_kwargs(const std::string& ts, py::kwargs& kwargs) { std::shared_ptr cron = std::make_shared(ts); - extract_cron_keyword_arguments(cron, dict); + extract_cron_keyword_arguments(cron, kwargs); return cron; } -static std::shared_ptr cron_init1(const ecf::TimeSeries& ts, py::dict& dict) { +std::shared_ptr cron_make_timeseries_kwargs(const ecf::TimeSeries& ts, py::kwargs& kwargs) { std::shared_ptr cron = std::make_shared(ts); - extract_cron_keyword_arguments(cron, dict); + extract_cron_keyword_arguments(cron, kwargs); return cron; } -static std::shared_ptr cron_create() { - return std::make_shared(); -} -static std::shared_ptr cron_create2(const ecf::TimeSeries& ts) { +std::shared_ptr cron_make_timeseries(const ecf::TimeSeries& ts) { return std::make_shared(ts); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -void add_time_series_3(ecf::CronAttr* self, const std::string& ts) { - self->addTimeSeries(ecf::TimeSeries::create(ts)); -} - -void set_week_days(ecf::CronAttr* cron, const py::list& list) { +void Cron_set_week_days(ecf::CronAttr* cron, const py::list& list) { std::vector int_vec; pyutil_list_to_int_vec(list, int_vec); cron->addWeekDays(int_vec); } -void set_last_week_days_of_month(ecf::CronAttr* cron, const py::list& list) { + +void Cron_set_last_week_days_of_month(ecf::CronAttr* cron, const py::list& list) { std::vector int_vec; pyutil_list_to_int_vec(list, int_vec); cron->add_last_week_days_of_month(int_vec); } -void set_days_of_month(ecf::CronAttr* cron, const py::list& list) { +void Cron_set_days_of_month(ecf::CronAttr* cron, const py::list& list) { std::vector int_vec; pyutil_list_to_int_vec(list, int_vec); cron->addDaysOfMonth(int_vec); } -void set_last_day_of_the_month(ecf::CronAttr* cron) { + +void Cron_set_last_day_of_the_month(ecf::CronAttr* cron) { cron->add_last_day_of_month(); } -void set_months(ecf::CronAttr* cron, const py::list& list) { +void Cron_set_months(ecf::CronAttr* cron, const py::list& list) { std::vector int_vec; pyutil_list_to_int_vec(list, int_vec); cron->addMonths(int_vec); } -// Create as shared because: we want to pass a Python list as part of the constructor, -// *AND* the only way make_constructor works is with a pointer. -// The Node::add function seem to cope with this, some boost python magic,must do a conversion -// from shared_ptr to pass by reference -static std::shared_ptr create_RepeatEnumerated(const std::string& name, const py::list& list) { - std::vector vec; - pyutil_list_to_str_vec(list, vec); - return std::make_shared(name, vec); -} -static std::shared_ptr create_RepeatDateList(const std::string& name, const py::list& list) { - std::vector vec; - pyutil_list_to_int_vec(list, vec); - return std::make_shared(name, vec); -} -static std::shared_ptr create_RepeatString(const std::string& name, const py::list& list) { - std::vector vec; - pyutil_list_to_str_vec(list, vec); - return std::make_shared(name, vec); -} -static std::shared_ptr create_AutoRestoreAttr(const py::list& list) { - std::vector vec; - pyutil_list_to_str_vec(list, vec); - return std::make_shared(vec); -} - -static std::shared_ptr create_queue(const std::string& name, const py::list& list) { - std::vector vec; - pyutil_list_to_str_vec(list, vec); - return std::make_shared(name, vec); -} - -static std::shared_ptr create_generic(const std::string& name, const py::list& list) { - std::vector vec; - pyutil_list_to_str_vec(list, vec); - return std::make_shared(name, vec); -} - -static std::shared_ptr -create_ZombieAttr(ecf::Child::ZombieType zt, const py::list& list, ecf::ZombieCtrlAction uc, int life_time_in_server) { - std::vector vec; - int the_list_size = len(list); - vec.reserve(the_list_size); - for (int i = 0; i < the_list_size; ++i) { - vec.push_back(py::extract(list[i])); - } - return std::make_shared(zt, vec, uc, life_time_in_server); -} - -static std::shared_ptr -create_ZombieAttr1(ecf::Child::ZombieType zt, const py::list& list, ecf::ZombieCtrlAction uc) { - std::vector vec; - int the_list_size = len(list); - vec.reserve(the_list_size); - for (int i = 0; i < the_list_size; ++i) { - vec.push_back(py::extract(list[i])); - } - return std::make_shared(zt, vec, uc); -} - -static py::list wrap_set_of_strings(Limit* limit) { - py::list list; - const std::set& paths = limit->paths(); - for (std::string path : paths) { - list.append(path); - } - return list; -} - -static job_creation_ctrl_ptr makeJobCreationCtrl() { - return std::make_shared(); -} - -static std::shared_ptr aviso_init(const std::string& name, - const std::string& listener, - const std::string& url = ecf::AvisoAttr::default_url, - const std::string& schema = ecf::AvisoAttr::default_schema, - const std::string& polling = ecf::AvisoAttr::default_polling, - const std::string& auth = ecf::AvisoAttr::default_auth) { - auto attr = std::make_shared(nullptr, name, listener, url, schema, polling, 0, auth, ""); - return attr; -} - -static std::shared_ptr aviso_init_defaults_0(const std::string& name, const std::string& listener) { - return aviso_init(name, listener); -} - -static std::shared_ptr -aviso_init_defaults_1(const std::string& name, const std::string& listener, const std::string& url) { - return aviso_init(name, listener, url); -} - -static std::shared_ptr aviso_init_defaults_2(const std::string& name, - const std::string& listener, - const std::string& url, - const std::string& schema) { - return aviso_init(name, listener, url, schema); +void Cron_set_time_series(ecf::CronAttr* self, const std::string& ts) { + self->addTimeSeries(ecf::TimeSeries::create(ts)); } -static std::shared_ptr aviso_init_defaults_3(const std::string& name, - const std::string& listener, - const std::string& url, - const std::string& schema, - const std::string& polling) { - return aviso_init(name, listener, url, schema, polling); -} +// Aviso -static std::shared_ptr mirror_init(const std::string& name, - const std::string& path, - const std::string& host = ecf::MirrorAttr::default_remote_host, - const std::string& port = ecf::MirrorAttr::default_remote_port, - const std::string& polling = ecf::MirrorAttr::default_polling, - bool ssl = false, - const std::string& auth = ecf::MirrorAttr::default_remote_auth) { - auto attr = std::make_shared(nullptr, name, path, host, port, polling, ssl, auth, ""); - return attr; +ecf::AvisoAttr Aviso_make(const std::string& name, + const std::string& listener, + const std::string& url = ecf::AvisoAttr::default_url, + const std::string& schema = ecf::AvisoAttr::default_schema, + const std::string& polling = ecf::AvisoAttr::default_polling, + const std::string& auth = ecf::AvisoAttr::default_auth) { + return ecf::AvisoAttr(nullptr, name, listener, url, schema, polling, 0, auth, ""); } -static std::shared_ptr mirror_init_defaults_0(const std::string& name, const std::string& path) { - return mirror_init(name, path); +std::string Aviso_str(const ecf::AvisoAttr& aviso) { + return ecf::to_python_string(aviso); } -static std::shared_ptr -mirror_init_defaults_1(const std::string& name, const std::string& path, const std::string& host) { - return mirror_init(name, path, host); -} +// Mirror -static std::shared_ptr mirror_init_defaults_2(const std::string& name, - const std::string& path, - const std::string& host, - const std::string& port) { - return mirror_init(name, path, host, port); +ecf::MirrorAttr Mirror_make(const std::string& name, + const std::string& path, + const std::string& host = ecf::MirrorAttr::default_remote_host, + const std::string& port = ecf::MirrorAttr::default_remote_port, + const std::string& polling = ecf::MirrorAttr::default_polling, + bool ssl = false, + const std::string& auth = ecf::MirrorAttr::default_remote_auth) { + return ecf::MirrorAttr(nullptr, name, path, host, port, polling, ssl, auth, ""); } -static std::shared_ptr mirror_init_defaults_3(const std::string& name, - const std::string& path, - const std::string& host, - const std::string& port, - const std::string& polling) { - return mirror_init(name, path, host, port, polling); +std::string Mirror_str(const ecf::MirrorAttr& mirror) { + return ecf::to_python_string(mirror); } -static std::shared_ptr mirror_init_defaults_4(const std::string& name, - const std::string& path, - const std::string& host, - const std::string& port, - const std::string& polling, - bool ssl) { - return mirror_init(name, path, host, port, polling, ssl); -} +} // namespace -static std::string to_python_string_aviso(const ecf::AvisoAttr& aviso) { - return ecf::to_python_string(aviso); -} +void export_NodeAttr(py::module& m) { -static std::string to_python_string_mirror(const ecf::MirrorAttr& mirror) { - return ecf::to_python_string(mirror); -} + // Trigger & Complete are a thin wrapper over Expression, allowing to call: + // + // Task("a").add(Trigger("a=1"),Complete("b=1")) + // + py::class_>(m, "Trigger", DefsDoc::trigger()) -void export_NodeAttr() { - // Trigger & Complete thin wrapper over Expression, allows us to call: Task("a").add(Trigger("a=1"),Complete("b=1")) - py::class_>("Trigger", DefsDoc::trigger(), py::init()) + .def(py::init()) .def(py::init()) .def(py::init()) .def(py::init()) - .def(py::self == py::self) // __eq__ - .def("__str__", &Trigger::expression) // __str__ + .def(py::self == py::self) + .def("__str__", &Trigger::expression) .def("get_expression", &Trigger::expression, "returns the trigger expression as a string"); - py::class_>("Complete", DefsDoc::trigger(), py::init()) + py::class_>(m, "Complete", DefsDoc::trigger()) + + .def(py::init()) .def(py::init()) .def(py::init()) .def(py::init()) - .def(py::self == py::self) // __eq__ - .def("__str__", &Complete::expression) // __str__ + .def(py::self == py::self) + .def("__str__", &Complete::expression) .def("get_expression", &Complete::expression, "returns the complete expression as a string"); - // mimic PartExpression(const std::string& expression ) - // mimic PartExpression(const std::string& expression, bool andExpr /* true means AND , false means OR */ ) - // Use to adding large trigger and complete expressions - py::class_("PartExpression", DefsDoc::part_expression_doc(), py::init()) + py::class_(m, "PartExpression", DefsDoc::part_expression_doc()) + + .def(py::init()) .def(py::init()) - .def(py::self == py::self) // __eq__ + .def(py::self == py::self) .def("get_expression", &PartExpression::expression, - py::return_value_policy(), + py::return_value_policy::reference, "returns the part expression as a string") .def("and_expr", &PartExpression::andExpr) .def("or_expr", &PartExpression::orExpr); - py::class_>( - "Expression", DefsDoc::expression_doc(), py::init()) + py::class_>(m, "Expression", DefsDoc::expression_doc()) + + .def(py::init()) .def(py::init()) - .def(py::self == py::self) // __eq__ - .def("__str__", &Expression::expression) // __str__ + .def(py::self == py::self) + .def("__str__", &Expression::expression) .def("get_expression", &Expression::expression, "returns the complete expression as a string") .def("add", &Expression::add, "Add a part expression, the second and subsequent part expressions must have 'and/or' set") - .add_property( - "parts", py::range(&Expression::part_begin, &Expression::part_end), "Returns a list of PartExpression's"); - - py::enum_( - "FlagType", - "Flags store state associated with a node\n\n" - "- FORCE_ABORT - Node* do not run when try_no > ECF_TRIES, and task aborted by user\n" - "- USER_EDIT - task\n" - "- TASK_ABORTED - task*\n" - "- EDIT_FAILED - task*\n" - "- JOBCMD_FAILED - task*\n" - "- KILLCMD_FAILED - task*\n" - "- STATUSCMD_FAILED - task*\n" - "- NO_SCRIPT - task*\n" - "- KILLED - task* do not run when try_no > ECF_TRIES, and task killed by user\n" - "- STATUS - task* indicates that the status command has been run\n" - "- LATE - Node attribute, Task is late, or Defs checkpt takes to long\n" - "- MESSAGE - Node\n" - "- BYRULE - Node*, set if node is set to complete by complete trigger expression\n" - "- QUEUELIMIT - Node\n" - "- WAIT - task* \n" - "- LOCKED - Server\n" - "- ZOMBIE - task*\n" - "- NO_REQUE - task\n" - "- ARCHIVED - Suite/Family\n" - "- RESTORED - Family/Family\n" - "- THRESHOLD - task\n" - "- SIGTERM - Defs, records that server received a SIGTERM signal\n" - "- LOG_ERROR - Error in opening or writing to log file\n" - "- CHECKPT_ERROR - Error in opening or writing to checkpt file \n" - "- NOT_SET\n") + .def("parts", &Expression::expr, "Returns a list of PartExpression's"); + + py::enum_(m, "FlagType", NodeAttrDoc::flag_type_doc()) + .value("force_abort", ecf::Flag::FORCE_ABORT) .value("user_edit", ecf::Flag::USER_EDIT) .value("task_aborted", ecf::Flag::TASK_ABORTED) @@ -476,23 +419,23 @@ void export_NodeAttr() { .value("checkpt_error", ecf::Flag::CHECKPT_ERROR) .value("remote_error", ecf::Flag::REMOTE_ERROR); - py::class_("Flag", "Represents additional state associated with a Node.\n\n", py::init<>()) - .def("__str__", &ecf::Flag::to_string) // __str__ - .def(py::self == py::self) // __eq__ + py::class_(m, "Flag", "Represents additional state associated with a Node.\n\n") + + .def(py::init<>()) + .def("__str__", &ecf::Flag::to_string) + .def(py::self == py::self) .def("is_set", &ecf::Flag::is_set, "Queries if a given flag is set") .def("set", &ecf::Flag::set, "Sets the given flag. Used in test only") .def("clear", &ecf::Flag::clear, "Clear the given flag. Used in test only") .def("reset", &ecf::Flag::reset, "Clears all flags. Used in test only") - .def("list", &ecf::Flag::list, "Returns the list of all flag types. returns FlagTypeVec. Used in test only") - .staticmethod("list") - .def("type_to_string", &ecf::Flag::enum_to_string, "Convert type to a string. Used in test only") - .staticmethod("type_to_string"); + .def_static("list", &ecf::Flag::list, "Returns the list of all flag types. returns FlagTypeVec. Tests only") + .def_static("type_to_string", &ecf::Flag::enum_to_string, "Convert type to a string. Tests only"); - py::class_>("FlagTypeVec", "Hold a list of flag types") - .def(py::vector_indexing_suite, true>()); + py::class_>(m, "FlagTypeVec", "Hold a list of flag types"); - py::class_("JobCreationCtrl", DefsDoc::jobgenctrl_doc()) - .def("__init__", py::make_constructor(makeJobCreationCtrl), DefsDoc::jobgenctrl_doc()) + py::class_>(m, "JobCreationCtrl", DefsDoc::jobgenctrl_doc()) + + .def(py::init(&JobCreationCtrl_make), DefsDoc::jobgenctrl_doc()) .def("set_node_path", &JobCreationCtrl::set_node_path, "The node we want to check job creation for. If no node specified check all tasks") @@ -502,18 +445,19 @@ void export_NodeAttr() { .def("set_verbose", &JobCreationCtrl::set_verbose, "Output each task as its being checked.") .def("get_dir_for_job_creation", &JobCreationCtrl::dir_for_job_creation, - py::return_value_policy(), + py::return_value_policy::reference, "Returns the directory set for job creation") - .def( - "generate_temp_dir", - &JobCreationCtrl::generate_temp_dir, - "Automatically generated temporary directory for job creation. Directory written to stdout for information") + .def("generate_temp_dir", + &JobCreationCtrl::generate_temp_dir, + "Automatically generated temporary directory for job creation. Directory written to stdout for " + "information") .def("get_error_msg", &JobCreationCtrl::get_error_msg, - py::return_value_policy(), + py::return_value_policy::reference, "Returns an error message generated during checking of job creation"); - py::enum_("ZombieType", NodeAttrDoc::zombie_type_doc()) + py::enum_(m, "ZombieType", NodeAttrDoc::zombie_type_doc()) + .value("ecf", ecf::Child::ECF) .value("ecf_pid", ecf::Child::ECF_PID) .value("ecf_pid_passwd", ecf::Child::ECF_PID_PASSWD) @@ -521,7 +465,8 @@ void export_NodeAttr() { .value("user", ecf::Child::USER) .value("path", ecf::Child::PATH); - py::enum_("ZombieUserActionType", NodeAttrDoc::zombie_user_action_type_doc()) + py::enum_(m, "ZombieUserActionType", NodeAttrDoc::zombie_user_action_type_doc()) + .value("fob", ecf::ZombieCtrlAction::FOB) .value("fail", ecf::ZombieCtrlAction::FAIL) .value("remove", ecf::ZombieCtrlAction::REMOVE) @@ -529,7 +474,8 @@ void export_NodeAttr() { .value("block", ecf::ZombieCtrlAction::BLOCK) .value("kill", ecf::ZombieCtrlAction::KILL); - py::enum_("ChildCmdType", NodeAttrDoc::child_cmd_type_doc()) + py::enum_(m, "ChildCmdType", NodeAttrDoc::child_cmd_type_doc()) + .value("init", ecf::Child::INIT) .value("event", ecf::Child::EVENT) .value("meter", ecf::Child::METER) @@ -539,8 +485,8 @@ void export_NodeAttr() { .value("abort", ecf::Child::ABORT) .value("complete", ecf::Child::COMPLETE); - py::enum_("AttrType", - "Sortable attribute type, currently [event | meter | label | limit | variable | all ]") + py::enum_(m, "AttrType", NodeAttrDoc::sortable_attribute_type_doc()) + .value("event", ecf::Attr::EVENT) .value("meter", ecf::Attr::METER) .value("label", ecf::Attr::LABEL) @@ -548,29 +494,32 @@ void export_NodeAttr() { .value("variable", ecf::Attr::VARIABLE) .value("all", ecf::Attr::ALL); - py::class_("ZombieAttr", NodeAttrDoc::zombie_doc()) - .def("__init__", make_constructor(&create_ZombieAttr)) - .def("__init__", make_constructor(&create_ZombieAttr1)) - .def("__str__", &ZombieAttr::toString) // __str__ - .def("__copy__", pyutil_copy_object) // __copy__ uses copy constructor - .def(py::self == py::self) // __eq__ + py::class_(m, "ZombieAttr", NodeAttrDoc::zombie_doc()) + + .def(py::init(&ZombieAttr_make)) + .def(py::init(&ZombieAttr_make_lifetime)) + .def("__str__", &ZombieAttr::toString) + .def("__copy__", pyutil_copy_object) + .def(py::self == py::self) .def("empty", &ZombieAttr::empty, "Return true if the attribute is empty") .def("zombie_type", &ZombieAttr::zombie_type, "Returns the `zombie type`_") .def("user_action", &ZombieAttr::action, "The automated action to invoke, when zombies arise") .def("zombie_lifetime", &ZombieAttr::zombie_lifetime, "Returns the lifetime in seconds of `zombie`_ in the server") - .add_property("child_cmds", - py::range(&ZombieAttr::child_begin, &ZombieAttr::child_end), - "The list of child commands. If empty action applies to all child cmds"); + .def("child_cmds", + &ZombieAttr::child_cmds, + "The list of child commands. If empty action applies to all child cmds"); + + constexpr const char* zombievec_doc = "Hold a list of zombies"; + + py::class_>(m, "ZombieVec", zombievec_doc); - py::class_>("ZombieVec", "Hold a list of zombies") - .def(py::vector_indexing_suite, true>()); + py::class_(m, "Zombie", NodeAttrDoc::plain_zombie_doc()) - py::class_("Zombie", "Represent a zombie process stored by the server") - .def("__str__", &Zombie::to_string) // __str__ - .def("__copy__", pyutil_copy_object) // __copy__ uses copy constructor - .def(py::self == py::self) // __eq__ + .def("__str__", &Zombie::to_string) + .def("__copy__", pyutil_copy_object) + .def(py::self == py::self) .def("empty", &Zombie::empty) .def("manual_user_action", &Zombie::manual_user_action) .def("fob", &Zombie::fob) @@ -582,104 +531,74 @@ void export_NodeAttr() { .def("type", &Zombie::type) .def("type_str", &Zombie::type_str) .def("last_child_cmd", &Zombie::last_child_cmd) - .def("attr", &Zombie::attr, py::return_value_policy()) + .def("attr", &Zombie::attr, py::return_value_policy::reference) .def("calls", &Zombie::calls) - .def("jobs_password", &Zombie::jobs_password, py::return_value_policy()) - .def("path_to_task", &Zombie::path_to_task, py::return_value_policy()) - .def("process_or_remote_id", &Zombie::process_or_remote_id, py::return_value_policy()) - .def("user_cmd", &Zombie::user_cmd, py::return_value_policy()) + .def("jobs_password", &Zombie::jobs_password, py::return_value_policy::reference) + .def("path_to_task", &Zombie::path_to_task, py::return_value_policy::reference) + .def("process_or_remote_id", &Zombie::process_or_remote_id, py::return_value_policy::reference) + .def("user_cmd", &Zombie::user_cmd, py::return_value_policy::reference) .def("try_no", &Zombie::try_no) .def("duration", &Zombie::duration) .def("user_action", &Zombie::user_action) .def("user_action_str", &Zombie::user_action_str) - .def("host", &Zombie::host, py::return_value_policy()) + .def("host", &Zombie::host, py::return_value_policy::reference) .def("allowed_age", &Zombie::allowed_age); - py::class_("Variable", NodeAttrDoc::variable_doc(), py::init()) - .def("__str__", &Variable::toString) // __str__ - .def("__copy__", pyutil_copy_object) // __copy__ uses copy constructor - .def(py::self < py::self) // __lt__ - .def(py::self == py::self) // __eq__ - .def("name", - &Variable::name, - py::return_value_policy(), - "Return the variable name as string") - .def("value", - &Variable::theValue, - py::return_value_policy(), - "Return the variable value as a string") + py::class_(m, "Variable", NodeAttrDoc::variable_doc()) + + .def(py::init()) + .def(py::self == py::self) + .def(py::self < py::self) + .def("__str__", &Variable::toString) + .def("__copy__", pyutil_copy_object) + .def("name", &Variable::name, py::return_value_policy::reference, "Return the variable name as string") + .def("value", &Variable::theValue, py::return_value_policy::reference, "Return the variable value as a string") .def("empty", &Variable::empty, "Return true if the variable is empty. Used when returning a Null variable, from a find"); - // .add_property("value", make_function(&Variable::theValue ,py::return_value_policy()), - // &Variable::set_value, "get/set the value as a property") - // The following works v = Variable('name','fred') - // print v.value - // But it will then break v.value() - - // We need to return pass a list of Variable as arguments, to retrieve the generated variables - py::class_>("VariableList", "Hold a list of Variables") - .def(py::vector_indexing_suite>()); - - py::class_