You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

python.rs 4.1 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. use super::{OperatorEvent, OperatorInput};
  2. use eyre::{bail, eyre, Context};
  3. use pyo3::{pyclass, types::IntoPyDict, Python};
  4. use std::{
  5. panic::{catch_unwind, AssertUnwindSafe},
  6. path::Path,
  7. thread,
  8. };
  9. use tokio::sync::mpsc::{Receiver, Sender};
  10. pub fn spawn(
  11. path: &Path,
  12. events_tx: Sender<OperatorEvent>,
  13. mut inputs: Receiver<OperatorInput>,
  14. ) -> eyre::Result<()> {
  15. if !path.exists() {
  16. bail!("No python file exists at {}", path.display());
  17. }
  18. let path = path
  19. .canonicalize()
  20. .wrap_err_with(|| format!("no file found at `{}`", path.display()))?;
  21. let path_cloned = path.clone();
  22. let send_output = SendOutputCallback {
  23. events_tx: events_tx.clone(),
  24. };
  25. let python_runner = move |py: pyo3::Python| {
  26. if let Some(parent_path) = path.parent() {
  27. let parent_path = parent_path
  28. .to_str()
  29. .ok_or_else(|| eyre!("module path is not valid utf8"))?;
  30. let sys = py.import("sys").wrap_err("failed to import `sys` module")?;
  31. let sys_path = sys
  32. .getattr("path")
  33. .wrap_err("failed to import `sys.path` module")?;
  34. let sys_path_append = sys_path
  35. .getattr("append")
  36. .wrap_err("`sys.path.append` was not found")?;
  37. sys_path_append
  38. .call1((parent_path,))
  39. .wrap_err("failed to append module path to python search path")?;
  40. }
  41. let module_name = path
  42. .file_stem()
  43. .ok_or_else(|| eyre!("module path has no file stem"))?
  44. .to_str()
  45. .ok_or_else(|| eyre!("module file stem is not valid utf8"))?;
  46. let module = py
  47. .import(module_name)
  48. .wrap_err("failed to import Python module")?;
  49. let operator_class = module
  50. .getattr("Operator")
  51. .wrap_err("no `Operator` class found in module")?;
  52. let locals = [("Operator", operator_class)].into_py_dict(py);
  53. let operator = py
  54. .eval("Operator()", None, Some(locals))
  55. .wrap_err("failed to create Operator instance")?;
  56. while let Some(input) = inputs.blocking_recv() {
  57. operator
  58. .call_method1(
  59. "on_input",
  60. (input.id.to_string(), input.value, send_output.clone()),
  61. )
  62. .wrap_err("on_input failed")?;
  63. }
  64. if operator
  65. .hasattr("drop_operator")
  66. .wrap_err("failed to look for drop_operator")?
  67. {
  68. operator
  69. .call_method0("drop_operator")
  70. .wrap_err("drop_operator failed")?;
  71. }
  72. Result::<_, eyre::Report>::Ok(())
  73. };
  74. thread::spawn(move || {
  75. let closure = AssertUnwindSafe(|| {
  76. let result = Python::with_gil(python_runner);
  77. result.wrap_err_with(|| format!("error in Python module at {}", path_cloned.display()))
  78. });
  79. match catch_unwind(closure) {
  80. Ok(Ok(())) => {}
  81. Ok(Err(err)) => {
  82. let _ = events_tx.blocking_send(OperatorEvent::Error(err));
  83. }
  84. Err(panic) => {
  85. let _ = events_tx.blocking_send(OperatorEvent::Panic(panic));
  86. }
  87. }
  88. });
  89. Ok(())
  90. }
  91. #[pyclass]
  92. #[derive(Clone)]
  93. struct SendOutputCallback {
  94. events_tx: Sender<OperatorEvent>,
  95. }
  96. #[allow(unsafe_op_in_unsafe_fn)]
  97. mod callback_impl {
  98. use super::SendOutputCallback;
  99. use crate::operator::OperatorEvent;
  100. use pyo3::{pymethods, PyResult};
  101. #[pymethods]
  102. impl SendOutputCallback {
  103. fn __call__(&mut self, output: &str, data: &[u8]) -> PyResult<()> {
  104. println!("RUNTIME received python output `{output}` with value `{data:?}`");
  105. let result = self.events_tx.blocking_send(OperatorEvent::Output {
  106. id: output.to_owned().into(),
  107. value: data.to_owned(),
  108. });
  109. result
  110. .map_err(|_| eyre::eyre!("channel to dora runtime was closed unexpectedly").into())
  111. }
  112. }
  113. }

DORA (Dataflow-Oriented Robotic Architecture) is middleware designed to streamline and simplify the creation of AI-based robotic applications. It offers low latency, composable, and distributed datafl