use std::process::Stdio;
use tokio::process::Command;
use tracing::{error, info};
use crate::error::{Result, TaskFlowError};
use crate::task::{Task, TaskHandler, TaskResult};
pub struct PythonTaskHandler;
impl PythonTaskHandler {
pub fn new() -> Self {
Self
}
}
#[async_trait::async_trait]
impl TaskHandler for PythonTaskHandler {
fn task_type(&self) -> &'static str {
"python_script"
}
async fn execute(&self, task: &Task) -> Result<TaskResult> {
let script_content = task
.definition
.payload
.get("script")
.and_then(|v| v.as_str())
.ok_or_else(|| {
TaskFlowError::InvalidConfiguration("Missing script content".to_string())
})?;
let args = task
.definition
.payload
.get("args")
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.filter_map(|v| v.as_str().map(|s| s.to_string()))
.collect::<Vec<String>>()
})
.unwrap_or_default();
info!(
"Executing Python script: {} with args: {:?}",
task.definition.id, args
);
let mut cmd = Command::new("python3");
cmd.arg("-c").arg(script_content);
cmd.args(&args);
cmd.stdin(Stdio::null());
cmd.stdout(Stdio::piped());
cmd.stderr(Stdio::piped());
let output = cmd
.output()
.await
.map_err(|e| TaskFlowError::ExecutionError(e.to_string()))?;
let success = output.status.success();
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
if !success {
error!("Python script execution failed: {}", stderr);
}
Ok(TaskResult {
success,
output: Some(stdout),
error: if stderr.is_empty() {
None
} else {
Some(stderr)
},
execution_time_ms: 0,
metadata: Default::default(),
})
}
}