Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Shell commands and function callbacks #1175

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
486d7ce
Initial callback and shell command implementations
siramok Jul 17, 2023
a62462f
Merge branch 'Alpine-DAV:develop' into develop
siramok Jul 18, 2023
43489d5
Initial work to combine commands and callbacks
siramok Jul 19, 2023
75288a2
Move callback registration and execution into Command
siramok Jul 19, 2023
1ef9834
Catch the case where neither action is defined
siramok Jul 19, 2023
c35d61c
Allow defining multiline shell commands and callbacks
siramok Jul 19, 2023
c816e88
Minor formatting tweaks
siramok Jul 19, 2023
8629395
Initial implementation of triggers with callbacks
siramok Jul 24, 2023
01f06b7
Merge branch 'Alpine-DAV:develop' into develop
siramok Jul 26, 2023
9ccfa0f
Improved organization, more error catching
siramok Jul 26, 2023
3d1dcf7
Make sure that triggers either have a condition or callback
siramok Jul 26, 2023
1dc2aeb
Make void callbacks take conduit node parameters
siramok Jul 31, 2023
1b005dd
Initial attempt at exposing callbacks through ascent-jupyter-bridge
siramok Aug 1, 2023
30333d6
Merge branch 'Alpine-DAV:develop' into develop
siramok Aug 1, 2023
0b3c769
Let void callbacks return arbitrary data via conduit nodes
siramok Aug 3, 2023
95815b8
Merge branch 'Alpine-DAV:develop' into develop
siramok Aug 4, 2023
422b89e
Merge branch 'Alpine-DAV:develop' into develop
siramok Aug 21, 2023
713857b
Disallow anonymous callbacks, add some tests
siramok Aug 21, 2023
a286cab
Add tests for shell commands and callbacks
siramok Aug 22, 2023
156ff58
Refactor callback API into the main API, adjusts tests to reflect the…
siramok Aug 22, 2023
e4f82b2
Minor cleanups that I missed
siramok Aug 22, 2023
7043d5d
Fix minor bug with the trigger + callback test
siramok Aug 22, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/libs/ascent/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ set(ascent_headers
runtimes/flow_filters/ascent_runtime_htg_filters.hpp
runtimes/flow_filters/ascent_runtime_trigger_filters.hpp
runtimes/flow_filters/ascent_runtime_query_filters.hpp
runtimes/flow_filters/ascent_runtime_command_filters.hpp
runtimes/flow_filters/ascent_runtime_vtkh_utils.hpp
runtimes/flow_filters/ascent_runtime_utils.hpp
# utils
Expand Down Expand Up @@ -228,6 +229,7 @@ set(ascent_sources
runtimes/flow_filters/ascent_runtime_htg_filters.cpp
runtimes/flow_filters/ascent_runtime_trigger_filters.cpp
runtimes/flow_filters/ascent_runtime_query_filters.cpp
runtimes/flow_filters/ascent_runtime_command_filters.cpp
runtimes/flow_filters/ascent_runtime_utils.cpp
# utils
utils/ascent_actions_utils.cpp
Expand Down
65 changes: 65 additions & 0 deletions src/libs/ascent/ascent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,71 @@ about(conduit::Node &n)

}

//-----------------------------------------------------------------------------
void
register_callback(const std::string &callback_name,
void (*callback_function)(conduit::Node &, conduit::Node &))
{
if (callback_name == "")
{
ASCENT_ERROR("cannot register an anonymous void callback");
}
else if (m_void_callback_map.count(callback_name) != 0)
{
ASCENT_ERROR("cannot register more than one void callback under the name '" << callback_name << "'");
}
else if (m_bool_callback_map.count(callback_name) != 0)
{
ASCENT_ERROR("cannot register both a void and bool callback under the same name '" << callback_name << "'");
}
m_void_callback_map.insert(std::make_pair(callback_name, callback_function));
}

//-----------------------------------------------------------------------------
void
register_callback(const std::string &callback_name,
bool (*callback_function)(void))
{
if (callback_name == "")
{
ASCENT_ERROR("cannot register an anonymous bool callback");
}
else if (m_bool_callback_map.count(callback_name) != 0)
{
ASCENT_ERROR("cannot register more than one bool callback under the name '" << callback_name << "'");
}
else if (m_void_callback_map.count(callback_name) != 0)
{
ASCENT_ERROR("cannot register both a void and bool callback under the same name '" << callback_name << "'");
}
m_bool_callback_map.insert(std::make_pair(callback_name, callback_function));
}

//-----------------------------------------------------------------------------
void
execute_callback(std::string callback_name,
conduit::Node &params,
conduit::Node &output)
{
if (m_void_callback_map.count(callback_name) != 1)
{
ASCENT_ERROR("requested void callback '" << callback_name << "' was never registered");
}
auto callback_function = m_void_callback_map.at(callback_name);
return callback_function(params, output);
}

//-----------------------------------------------------------------------------
bool
execute_callback(std::string callback_name)
{
if (m_bool_callback_map.count(callback_name) != 1)
{
ASCENT_ERROR("requested bool callback '" << callback_name << "' was never registered");
}
auto callback_function = m_bool_callback_map.at(callback_name);
return callback_function();
}

//-----------------------------------------------------------------------------
// -- end ascent:: --
Expand Down
18 changes: 18 additions & 0 deletions src/libs/ascent/ascent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,31 @@ class ASCENT_API Ascent
conduit::Node m_info;
};

// Callback maps
static std::map<std::string, void (*)(conduit::Node &, conduit::Node &)> m_void_callback_map;
static std::map<std::string, bool (*)(void)> m_bool_callback_map;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should define these in the cpp file, otherwise they could be multiple defined by users calling ascent.


//-----------------------------------------------------------------------------
std::string ASCENT_API about();

//-----------------------------------------------------------------------------
void ASCENT_API about(conduit::Node &node);

//-----------------------------------------------------------------------------
void ASCENT_API register_callback(const std::string &callback_name,
void (*callback_function)(conduit::Node &, conduit::Node &));
//-----------------------------------------------------------------------------
void ASCENT_API register_callback(const std::string &callback_name,
bool (*callback_function)(void));

//-----------------------------------------------------------------------------
void ASCENT_API execute_callback(std::string callback_name,
conduit::Node &params,
conduit::Node &output);

//-----------------------------------------------------------------------------
bool ASCENT_API execute_callback(std::string callback_name);

};
//-----------------------------------------------------------------------------
// -- end ascent:: --
Expand Down
94 changes: 94 additions & 0 deletions src/libs/ascent/python/ascent_module/ascent_mpi_python.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,29 @@ struct PyAscent_MPI_Ascent
Ascent *ascent; // NoteIterator is light weight, we can deal with copies
};

//---------------------------------------------------------------------------//
// Helper that promotes ascent error to python error
//---------------------------------------------------------------------------//
static void
PyAscent_MPI_Ascent_Error_To_PyErr(const conduit::Error &e)
{
std::ostringstream oss;
oss << "Ascent Error: " << e.message();
PyErr_SetString(PyExc_RuntimeError,
oss.str().c_str());
}

//---------------------------------------------------------------------------//
// Helper that promotes ascent error to python error
//---------------------------------------------------------------------------//
static void
PyAscent_MPI_Cpp_Error_To_PyErr(const char *msg)
{
std::ostringstream oss;
oss << "Ascent Error: " << msg;
PyErr_SetString(PyExc_RuntimeError,
oss.str().c_str());
}

//---------------------------------------------------------------------------//
static PyObject *
Expand Down Expand Up @@ -465,6 +488,72 @@ PyAscent_MPI_about()
return (PyObject*)py_node_res;
}

//---------------------------------------------------------------------------//
// ascent::execute_callback
//---------------------------------------------------------------------------//
static PyObject *
PyAscent_MPI_execute_callback(PyObject *self,
PyObject *args)
{
char *callback_name;
PyObject *py_params = NULL;
PyObject *py_output = NULL;

if (!PyArg_ParseTuple(args,
"sOO",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can use s|00 to make all the args optional (maybe to support the bool callback case)

&callback_name,
&py_params,
&py_output))
{
return NULL;
}

try
{
if(py_params != NULL && py_output != NULL)
{
if(!PyConduit_Node_Check(py_params))
{
PyErr_SetString(PyExc_TypeError,
"Ascent::execute_callback 'params' argument must be a "
"conduit::Node");
return NULL;
}
else if (!PyConduit_Node_Check(py_output))
{
PyErr_SetString(PyExc_TypeError,
"Ascent::execute_callback 'output' argument must be a "
"conduit::Node");
return NULL;
}
std::string callback_name_string = callback_name;
Node *params = PyConduit_Node_Get_Node_Ptr(py_params);
Node *output = PyConduit_Node_Get_Node_Ptr(py_output);
ascent::execute_callback(callback_name, *params, *output);
Py_RETURN_NONE;
}
}
catch(conduit::Error e)
{
PyAscent_MPI_Ascent_Error_To_PyErr(e);
return NULL;
}
// also try to bottle other errors, to prevent python
// from crashing due to uncaught exception
catch(std::exception &e)
{
PyAscent_MPI_Cpp_Error_To_PyErr(e.what());
return NULL;
}
catch(...)
{
PyAscent_MPI_Cpp_Error_To_PyErr("unknown cpp exception thrown");
return NULL;
}

Py_RETURN_NONE;
}

//---------------------------------------------------------------------------//
// Python Module Method Defs
//---------------------------------------------------------------------------//
Expand All @@ -476,6 +565,11 @@ static PyMethodDef ascent_mpi_python_funcs[] =
METH_NOARGS,
NULL},
//-----------------------------------------------------------------------//
{"execute_callback",
(PyCFunction)PyAscent_MPI_execute_callback,
METH_VARARGS,
NULL},
//-----------------------------------------------------------------------//
// end ascent methods table
//-----------------------------------------------------------------------//
{NULL, NULL, METH_VARARGS, NULL}
Expand Down
73 changes: 72 additions & 1 deletion src/libs/ascent/python/ascent_module/ascent_python.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ static PyMethodDef PyAscent_Ascent_METHODS[] = {
METH_VARARGS | METH_KEYWORDS,
"{todo}"},
//-----------------------------------------------------------------------//
{"execute",
{"execute",
(PyCFunction)PyAscent_Ascent_execute,
METH_VARARGS | METH_KEYWORDS,
"{todo}"},
Expand Down Expand Up @@ -596,6 +596,72 @@ PyAscent_about()
return (PyObject*)py_node_res;
}

//---------------------------------------------------------------------------//
// ascent::execute_callback
//---------------------------------------------------------------------------//
static PyObject *
PyAscent_execute_callback(PyObject *self,
PyObject *args)
{
char *callback_name;
PyObject *py_params = NULL;
PyObject *py_output = NULL;

if (!PyArg_ParseTuple(args,
"sOO",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also s|OO possible here

&callback_name,
&py_params,
&py_output))
{
return NULL;
}

try
{
if(py_params != NULL && py_output != NULL)
{
if(!PyConduit_Node_Check(py_params))
{
PyErr_SetString(PyExc_TypeError,
"Ascent::execute_callback 'params' argument must be a "
"conduit::Node");
return NULL;
}
else if (!PyConduit_Node_Check(py_output))
{
PyErr_SetString(PyExc_TypeError,
"Ascent::execute_callback 'output' argument must be a "
"conduit::Node");
return NULL;
}
std::string callback_name_string = callback_name;
Node *params = PyConduit_Node_Get_Node_Ptr(py_params);
Node *output = PyConduit_Node_Get_Node_Ptr(py_output);
ascent::execute_callback(callback_name, *params, *output);
Py_RETURN_NONE;
}
}
catch(conduit::Error e)
{
PyAscent_Ascent_Error_To_PyErr(e);
return NULL;
}
// also try to bottle other errors, to prevent python
// from crashing due to uncaught exception
catch(std::exception &e)
{
PyAscent_Cpp_Error_To_PyErr(e.what());
return NULL;
}
catch(...)
{
PyAscent_Cpp_Error_To_PyErr("unknown cpp exception thrown");
return NULL;
}

Py_RETURN_NONE;
}

//---------------------------------------------------------------------------//
// Python Module Method Defs
//---------------------------------------------------------------------------//
Expand All @@ -607,6 +673,11 @@ static PyMethodDef ascent_python_funcs[] =
METH_NOARGS,
NULL},
//-----------------------------------------------------------------------//
{"execute_callback",
(PyCFunction)PyAscent_execute_callback,
METH_VARARGS,
NULL},
//-----------------------------------------------------------------------//
// end ascent methods table
//-----------------------------------------------------------------------//
{NULL, NULL, METH_VARARGS, NULL}
Expand Down
8 changes: 8 additions & 0 deletions src/libs/ascent/python/ascent_module/py_src/ascent.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ def about():
raise ImportError('failed to import ascent_python, was Ascent built with serial support ENABLE_SERIAL=ON ?')
return None

def execute_callback(callback_name, params, output):
try:
from .ascent_python import execute_callback as ascent_execute_callback
return ascent_execute_callback(callback_name, params, output)
except ImportError:
raise ImportError('failed to import ascent_python, was Ascent built with serial support ENABLE_SERIAL=ON ?')
return None

def Ascent():
try:
from .ascent_python import Ascent as ascent_obj
Expand Down
7 changes: 7 additions & 0 deletions src/libs/ascent/python/ascent_module/py_src/mpi/ascent_mpi.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ def about():
raise ImportError('failed to import ascent_mpi_python, was Ascent built with mpi support?')
return None

def execute_callback(callback_name, params, output):
try:
from .ascent_mpi_python import execute_callback as ascent_execute_callback
return ascent_execute_callback(callback_name, params, output)
except ImportError:
raise ImportError('failed to import ascent_mpi_python, was Ascent built with serial support ENABLE_SERIAL=ON ?')
return None

def Ascent():
try:
Expand Down
1 change: 0 additions & 1 deletion src/libs/ascent/runtimes/ascent_empty_runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ EmptyRuntime::Info()
return m_info;
}


//-----------------------------------------------------------------------------
void
EmptyRuntime::Cleanup()
Expand Down
2 changes: 1 addition & 1 deletion src/libs/ascent/runtimes/ascent_flow_runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,11 @@ FlowRuntime::Info()
return m_info;
}


//-----------------------------------------------------------------------------
void
FlowRuntime::Cleanup()
{

}

//-----------------------------------------------------------------------------
Expand Down
Loading
Loading