Skip to content

Can someone please help me how to fix this error? #49

@AriBermeki

Description

@AriBermeki
use pyo3::types::PyString;
use serde::Serialize;
use serde_json::{to_value, Value};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use tokio::runtime::Runtime;
use tokio::sync::{mpsc, oneshot};
use tokio::sync::Mutex as AsyncMutex;
use uuid::Uuid;

use pyo3::{prelude::*, IntoPyObjectExt};


#[derive(Debug, Clone)]
pub struct PythonData {
    pub id: String,
    pub method: String,
    pub args: Value,
}

#[derive(Debug)]
pub struct APIRequest {
    pub data: PythonData,
    pub responder: oneshot::Sender<Value>,
}

#[derive(Clone, Debug)]
pub struct APIConnector {
    sender: mpsc::Sender<APIRequest>,
    _pending: Arc<Mutex<HashMap<String, oneshot::Sender<Value>>>>,
    receiver: Arc<AsyncMutex<mpsc::Receiver<APIRequest>>>,
    runtime: Arc<Runtime>,
}

impl APIConnector {
    pub fn new() -> Self {
        let runtime = Arc::new(Runtime::new().unwrap());
        let (tx, rx) = mpsc::channel::<APIRequest>(100000);
        let receiver = Arc::new(AsyncMutex::new(rx));

        Self {
            sender: tx,
            _pending: Arc::new(Mutex::new(HashMap::new())),
            receiver,
            runtime,
        }
    }

    pub fn send_request<S: Serialize>(&self, method: &str, args: S) -> oneshot::Receiver<Value> {
        let id = Uuid::new_v4().to_string();
        let (tx, rx) = oneshot::channel();
        let args_value = to_value(args).expect("Failed to serialize arguments");

        let request = APIRequest {
            data: PythonData {
                id: id.clone(),
                method: method.to_string(),
                args: args_value,
            },
            responder: tx,
        };


        let _ = self.sender.blocking_send(request);

        rx
    }

    pub fn on(&self, proxy:&tao::event_loop::EventLoopProxy<crate::FrameEvent>) {
        let rx_clone = self.receiver.clone();
        let main_proxy = proxy.clone();
        self.runtime.spawn(async move {
            let mut rx = rx_clone.lock().await;

            while let Some(req) = rx.recv().await {
                if let Err(e) =  main_proxy.send_event(crate::FrameEvent::APIRequest {
                    data: req.data,
                    responder: Some(req.responder),
                }){
            eprintln!("Event Loop already closed: {:?}", e);
        }

                tokio::time::sleep(std::time::Duration::from_millis(200)).await;

            }
        });
    }
}


#[pyclass]
pub struct WindowAPI {
    api: Option<APIConnector>,
}

#[pymethods]
impl WindowAPI {
    #[new]
    pub fn new() -> Self {
        Self { api: None }
    }

#[pyo3(name = "emit_event")]
fn emit_event_py<'py>(&self, py: Python<'py>, event:String, args:String) -> PyResult<Bound<'py, PyAny>> {
    let Some(api) = &self.api else {
        return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
            "API Connector not connected",
        ));
    };

    let event = event.to_string();
    let args_value: Value = serde_json::from_str(&args)
        .map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(e.to_string()))?;

    let api_clone = api.clone();

    let fut = async move {
        let rx = api_clone.send_request(&event, args_value);
        let response = rx.await.map_err(|e| {
            PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string())
        })?;

        Python::with_gil(|py| {
            let py_value:Bound<'_, PyString> = match response {
                Value::String(s) => PyString::new(py, &s).into(),
                _ => {
                    let json = serde_json::to_string(&response)
                        .unwrap_or_else(|_| "null".to_string());
                    PyString::new(py, &json).into()
                }
            };
            let payload = py_value.into_any();
            Ok(payload)
        })
    };

    let d = pyo3_async_runtimes::tokio::future_into_py(py, fut)?.into_bound_py_any(py)?;
    Ok(d)
}



}


impl WindowAPI {
    pub fn conect_api(&mut self, api:APIConnector){
        self.api = Some(api)
    }
}




#[pyfunction]
pub fn create_webframe(window_connector:&mut WindowAPI)->anyhow::Result<()>{
    let proxy= todo!();
    let api = APIConnector::new();
    window_connector.conect_api(api);
    loop {
        api.on(proxy);
    }
}

it should actually look something like this in python domain

import json
from webruntime import create_webframe


class WindowAPI:
    def __init__(self):
        pass

    async def emit_event(self, event:str, args:str):...



window_api = WindowAPI()


async def some_window_command():
    args = json.dumps({})
    command_result = await window_api.emit_event("window.setTitle", args)
    print(command_result)




if __name__ == "__main__":
    create_webframe(window_api)

My goal is to implement something similar to this

import asyncio
import uuid
from typing import Optional, Any, Dict

class WindowHandel:
    def __init__(self):
        self.sio = None
        self.state_scope = asyncio.Queue()
        self.methods_scope = asyncio.Queue()
        self._pending_responses: Dict[str, asyncio.Future] = {}

    def runtime_scope(self, sio):
        """Connect or update the socket.io server instance."""
        self.sio = sio

    async def handle_window_response(self, response: dict):
        req_id = response.get("id")
        if req_id and req_id in self._pending_responses:
            future = self._pending_responses.pop(req_id)
            if "error" in response:
                future.set_exception(Exception(response["error"]))
            else:
                future.set_result(response.get("result"))

    async def endless_state_loop(self):
        while True:
            task = None
            queue_name = None
            queue = None
            if not self.state_scope.empty():
                queue = self.state_scope
                queue_name = "state_scope"
            elif not self.methods_scope.empty():
                queue = self.methods_scope
                queue_name = "methods_scope"

            if queue:
                task = await queue.get()

            if task is None:
                await asyncio.sleep(0.01)
                continue

            future = task.pop("future", None)
            data = task.get("data", {})

            try:
                if hasattr(self, "sio") and self.sio:
                    await self.sio.emit("window_request", data)
            except Exception as e:
                print(f"[SocketIO Error in {queue_name}] {e}")
                if future:
                    future.set_exception(e)

    async def _request(self, method: str, args: dict, scope: bool = True) -> Any:
        req_id = str(uuid.uuid4())
        future = asyncio.get_event_loop().create_future()
        self._pending_responses[req_id] = future
        queue = self.state_scope if scope == True else self.methods_scope
        await queue.put({
            "event": "window_request",
            "data": {
                "id": req_id,
                "method": method,
                "args": args
            },
            "future": future
        })
        return await future

    async def current(self) -> Any:
        return await self._request("window.current", {})

    async def close(self, id: Optional[int] = None) -> Any:
        return await self._request("window.close", {"id": id}, scope=False)

    async def list(self) -> Any:
        return await self._request("window.list", {})

    async def sendMessage(self, message: str, id: int) -> Any:
        return await self._request("window.sendMessage", {"message": message, "id": id}, scope=False)

    async def setMenu(self, options: Optional[dict] = None, id: Optional[int] = None) -> Any:
        return await self._request("window.setMenu", {"options": options, "id": id}, scope=False)

    async def hideMenu(self, id: Optional[int] = None) -> Any:
        return await self._request("window.hideMenu", {"id": id}, scope=False)

    async def showMenu(self, id: Optional[int] = None) -> Any:
        return await self._request("window.showMenu", {"id": id}, scope=False)

    async def isMenuVisible(self, id: Optional[int] = None) -> Any:
        return await self._request("window.isMenuVisible", {"id": id})

    async def scaleFactor(self, id: Optional[int] = None) -> Any:
        return await self._request("window.scaleFactor", {"id": id})
lifetime may not live long enough
returning this value requires that `'1` must outlive `'2`rustc[Click for full compiler diagnostic](rust-analyzer-diagnostics-view:/diagnostic%20message%20[1]?1#file:///c%3A/Users/MalekAli/Desktop/simple_loop/cha/src/runtime_handel.rs)
runtime_handel.rs(123, 27): has type `pyo3::Python<'1>`
runtime_handel.rs(123, 29): return type of closure is Result<pyo3::Bound<'2, pyo3::PyAny>, pyo3::PyErr>
let payload: Bound<'_, PyAny>
Go to [Bound](vscode-file://vscode-app/c:/Users/MalekAli/AppData/Local/Programs/Microsoft%20VS%20Code/resources/app/out/vs/code/electron-sandbox/workbench/workbench.html) | [PyAny](vscode-file://vscode-app/c:/Users/MalekAli/AppData/Local/Programs/Microsoft%20VS%20Code/resources/app/out/vs/code/electron-sandbox/workbench/workbench.html)
implementation of `IntoPyObject` is not general enough
`IntoPyObject<'0>` would have to be implemented for the type `pyo3::Bound<'_, pyo3::PyAny>`, for any lifetime `'0`...
...but `IntoPyObject<'1>` is actually implemented for the type `pyo3::Bound<'1, pyo3::PyAny>`, for some specific lifetime `'1`rustc[Click for full compiler diagnostic](rust-analyzer-diagnostics-view:/diagnostic%20message%20[4]?4#file:///c%3A/Users/MalekAli/Desktop/simple_loop/cha/src/runtime_handel.rs)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions