mirror of
https://github.com/hexedtech/codemp.git
synced 2024-11-22 15:24:48 +01:00
ON_HOLD: pyo3 experimental async is still too experimental, pivoting to blocking behaviour for now.
This commit is contained in:
parent
a45db74722
commit
b09c7a2206
4 changed files with 94 additions and 41 deletions
|
@ -2,21 +2,34 @@ use crate::workspace::Workspace;
|
||||||
use crate::Client;
|
use crate::Client;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
// #[pyfunction]
|
use super::tokio;
|
||||||
// pub fn codemp_init<'a>(py: Python<'a>) -> PyResult<Py<Client>> {
|
|
||||||
// Ok(Py::new(py, Client::default())?)
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl Client {
|
impl Client {
|
||||||
#[new]
|
#[new]
|
||||||
fn __new__(host: String, username: String, password: String) -> crate::Result<Self> {
|
fn __new__(host: String, username: String, password: String) -> crate::Result<Self> {
|
||||||
super::tokio().block_on(async move { Client::new(host, username, password).await })
|
tokio().block_on(Client::new(host, username, password))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn dioboia(&self) {
|
||||||
|
tokio().spawn(async { tracing::info!("dioboia? si dioboia!") });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "join_workspace")]
|
#[pyo3(name = "join_workspace")]
|
||||||
async fn pyjoin_workspace(&self, workspace: String) -> crate::Result<Workspace> {
|
async fn pyjoin_workspace(&self, workspace: String) -> crate::Result<Workspace> {
|
||||||
self.join_workspace(workspace).await
|
// self.join_workspace(workspace).await
|
||||||
|
let rc = self.clone();
|
||||||
|
crate::spawn_future!(rc.join_workspace(workspace))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
// This expands to if spawn_future_allow_threads! is used
|
||||||
|
// tokio()
|
||||||
|
// .spawn(super::AllowThreads(Box::pin(async move {
|
||||||
|
// rc.join_workspace(workspace).await
|
||||||
|
// })))
|
||||||
|
// or if only spawn_future!
|
||||||
|
// tokio()
|
||||||
|
// .spawn(async move { rc.join_workspace(workspace).await })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "leave_workspace")]
|
#[pyo3(name = "leave_workspace")]
|
||||||
|
|
|
@ -5,6 +5,8 @@ use crate::buffer::Controller as BufferController;
|
||||||
use crate::cursor::Controller as CursorController;
|
use crate::cursor::Controller as CursorController;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
use crate::spawn_future;
|
||||||
|
|
||||||
// need to do manually since Controller is a trait implementation
|
// need to do manually since Controller is a trait implementation
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl CursorController {
|
impl CursorController {
|
||||||
|
@ -16,22 +18,26 @@ impl CursorController {
|
||||||
buffer: path,
|
buffer: path,
|
||||||
user: None,
|
user: None,
|
||||||
};
|
};
|
||||||
super::AllowThreads(self.send(pos)).await
|
let rc = self.clone();
|
||||||
|
spawn_future!(rc.send(pos)).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "try_recv")]
|
#[pyo3(name = "try_recv")]
|
||||||
async fn pytry_recv(&self) -> crate::Result<Option<Cursor>> {
|
async fn pytry_recv(&self) -> crate::Result<Option<Cursor>> {
|
||||||
super::AllowThreads(self.try_recv()).await
|
let rc = self.clone();
|
||||||
|
spawn_future!(rc.try_recv()).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "recv")]
|
#[pyo3(name = "recv")]
|
||||||
async fn pyrecv(&self) -> crate::Result<Cursor> {
|
async fn pyrecv(&self) -> crate::Result<Cursor> {
|
||||||
super::AllowThreads(self.recv()).await
|
let rc = self.clone();
|
||||||
|
spawn_future!(rc.recv()).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "poll")]
|
#[pyo3(name = "poll")]
|
||||||
async fn pypoll(&self) -> crate::Result<()> {
|
async fn pypoll(&self) -> crate::Result<()> {
|
||||||
super::AllowThreads(self.poll()).await
|
let rc = self.clone();
|
||||||
|
spawn_future!(rc.poll()).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "stop")]
|
#[pyo3(name = "stop")]
|
||||||
|
@ -45,7 +51,8 @@ impl CursorController {
|
||||||
impl BufferController {
|
impl BufferController {
|
||||||
#[pyo3(name = "content")]
|
#[pyo3(name = "content")]
|
||||||
async fn pycontent(&self) -> crate::Result<String> {
|
async fn pycontent(&self) -> crate::Result<String> {
|
||||||
super::AllowThreads(Box::pin(self.content())).await
|
let rc = self.clone();
|
||||||
|
spawn_future!(rc.content()).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "send")]
|
#[pyo3(name = "send")]
|
||||||
|
@ -56,22 +63,26 @@ impl BufferController {
|
||||||
content: txt,
|
content: txt,
|
||||||
hash: None,
|
hash: None,
|
||||||
};
|
};
|
||||||
super::AllowThreads(self.send(op)).await
|
let rc = self.clone();
|
||||||
|
spawn_future!(rc.send(op)).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "try_recv")]
|
#[pyo3(name = "try_recv")]
|
||||||
async fn pytry_recv(&self) -> crate::Result<Option<TextChange>> {
|
async fn pytry_recv(&self) -> crate::Result<Option<TextChange>> {
|
||||||
super::AllowThreads(self.try_recv()).await
|
let rc = self.clone();
|
||||||
|
spawn_future!(rc.try_recv()).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "recv")]
|
#[pyo3(name = "recv")]
|
||||||
async fn pyrecv(&self) -> crate::Result<TextChange> {
|
async fn pyrecv(&self) -> crate::Result<TextChange> {
|
||||||
super::AllowThreads(self.recv()).await
|
let rc = self.clone();
|
||||||
|
spawn_future!(rc.recv()).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "poll")]
|
#[pyo3(name = "poll")]
|
||||||
async fn pypoll(&self) -> crate::Result<()> {
|
async fn pypoll(&self) -> crate::Result<()> {
|
||||||
super::AllowThreads(self.poll()).await
|
let rc = self.clone();
|
||||||
|
spawn_future!(rc.poll()).await.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,9 +108,6 @@ impl Cursor {
|
||||||
|
|
||||||
#[getter(user)]
|
#[getter(user)]
|
||||||
fn pyuser(&self) -> Option<String> {
|
fn pyuser(&self) -> Option<String> {
|
||||||
match self.user {
|
self.user.map(|user| user.to_string())
|
||||||
Some(user) => Some(user.to_string()),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,10 @@ pub mod client;
|
||||||
pub mod controllers;
|
pub mod controllers;
|
||||||
pub mod workspace;
|
pub mod workspace;
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::{
|
use std::{
|
||||||
future::Future,
|
future::Future,
|
||||||
pin::{pin, Pin},
|
pin::Pin,
|
||||||
|
sync::OnceLock,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -17,10 +17,9 @@ use crate::{
|
||||||
};
|
};
|
||||||
use pyo3::exceptions::{PyConnectionError, PyRuntimeError, PySystemError};
|
use pyo3::exceptions::{PyConnectionError, PyRuntimeError, PySystemError};
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use tokio::sync::{mpsc, Mutex};
|
use tokio::sync::watch;
|
||||||
|
|
||||||
fn tokio() -> &'static tokio::runtime::Runtime {
|
fn tokio() -> &'static tokio::runtime::Runtime {
|
||||||
use std::sync::OnceLock;
|
|
||||||
static RT: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
|
static RT: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
|
||||||
RT.get_or_init(|| {
|
RT.get_or_init(|| {
|
||||||
tokio::runtime::Builder::new_current_thread()
|
tokio::runtime::Builder::new_current_thread()
|
||||||
|
@ -43,14 +42,32 @@ where
|
||||||
{
|
{
|
||||||
type Output = F::Output;
|
type Output = F::Output;
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
let waker = cx.waker();
|
let waker = cx.waker();
|
||||||
Python::with_gil(|gil| {
|
let fut = unsafe { self.map_unchecked_mut(|e| &mut e.0) };
|
||||||
gil.allow_threads(|| pin!(&mut self.0).poll(&mut Context::from_waker(waker)))
|
Python::with_gil(|py| py.allow_threads(|| fut.poll(&mut Context::from_waker(waker))))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! spawn_future_allow_threads {
|
||||||
|
($fut:expr) => {
|
||||||
|
$crate::ffi::python::tokio().spawn($crate::ffi::python::AllowThreads(Box::pin(
|
||||||
|
async move {
|
||||||
|
tracing::info!("running future from rust.");
|
||||||
|
$fut.await
|
||||||
|
},
|
||||||
|
)))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! spawn_future {
|
||||||
|
($fut:expr) => {
|
||||||
|
$crate::ffi::python::tokio().spawn(async move { $fut.await })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
impl From<crate::Error> for PyErr {
|
impl From<crate::Error> for PyErr {
|
||||||
fn from(value: crate::Error) -> Self {
|
fn from(value: crate::Error) -> Self {
|
||||||
match value {
|
match value {
|
||||||
|
@ -75,11 +92,11 @@ impl IntoPy<PyObject> for crate::api::User {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct LoggerProducer(mpsc::Sender<String>);
|
struct LoggerProducer(watch::Sender<String>);
|
||||||
|
|
||||||
impl std::io::Write for LoggerProducer {
|
impl std::io::Write for LoggerProducer {
|
||||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
let _ = self.0.try_send(String::from_utf8_lossy(buf).to_string()); // ignore: logger disconnected or with full buffer
|
let _ = self.0.send(String::from_utf8_lossy(buf).to_string()); // ignore: logger disconnected or with full buffer
|
||||||
Ok(buf.len())
|
Ok(buf.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,13 +106,13 @@ impl std::io::Write for LoggerProducer {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
struct PyLogger(Arc<Mutex<mpsc::Receiver<String>>>);
|
struct PyLogger(watch::Receiver<String>);
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl PyLogger {
|
impl PyLogger {
|
||||||
#[new]
|
#[new]
|
||||||
fn init_logger(debug: bool) -> PyResult<Self> {
|
fn init_logger(debug: bool) -> PyResult<Self> {
|
||||||
let (tx, rx) = mpsc::channel(256);
|
let (tx, mut rx) = watch::channel("logger initialised".to_string());
|
||||||
let level = if debug {
|
let level = if debug {
|
||||||
tracing::Level::DEBUG
|
tracing::Level::DEBUG
|
||||||
} else {
|
} else {
|
||||||
|
@ -120,13 +137,17 @@ impl PyLogger {
|
||||||
.with_writer(std::sync::Mutex::new(LoggerProducer(tx)))
|
.with_writer(std::sync::Mutex::new(LoggerProducer(tx)))
|
||||||
.try_init()
|
.try_init()
|
||||||
{
|
{
|
||||||
Ok(_) => Ok(PyLogger(Arc::new(Mutex::new(rx)))),
|
Ok(_) => Ok(PyLogger(rx)),
|
||||||
Err(_) => Err(PySystemError::new_err("A logger already exists")),
|
Err(_) => Err(PySystemError::new_err("A logger already exists")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn listen(&self) -> Option<String> {
|
async fn listen(&mut self) -> Option<String> {
|
||||||
AllowThreads(Box::pin(self.0.lock().await.recv())).await
|
if self.0.changed().await.is_ok() {
|
||||||
|
return Some(self.0.borrow().clone());
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,17 +3,21 @@ use crate::cursor::Controller as CursorController;
|
||||||
use crate::workspace::Workspace;
|
use crate::workspace::Workspace;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
use crate::spawn_future;
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl Workspace {
|
impl Workspace {
|
||||||
// join a workspace
|
// join a workspace
|
||||||
#[pyo3(name = "create")]
|
#[pyo3(name = "create")]
|
||||||
async fn pycreate(&self, path: String) -> crate::Result<()> {
|
async fn pycreate(&self, path: String) -> crate::Result<()> {
|
||||||
self.create(path.as_str()).await
|
let rc = self.clone();
|
||||||
|
spawn_future!(rc.create(path.as_str())).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "attach")]
|
#[pyo3(name = "attach")]
|
||||||
async fn pyattach(&self, path: String) -> crate::Result<BufferController> {
|
async fn pyattach(&self, path: String) -> crate::Result<BufferController> {
|
||||||
self.attach(path.as_str()).await
|
let rc = self.clone();
|
||||||
|
spawn_future!(rc.attach(path.as_str())).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "detach")]
|
#[pyo3(name = "detach")]
|
||||||
|
@ -27,27 +31,34 @@ impl Workspace {
|
||||||
|
|
||||||
#[pyo3(name = "event")]
|
#[pyo3(name = "event")]
|
||||||
async fn pyevent(&self) -> crate::Result<crate::api::Event> {
|
async fn pyevent(&self) -> crate::Result<crate::api::Event> {
|
||||||
self.event().await
|
let rc = self.clone();
|
||||||
|
spawn_future!(rc.event()).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "fetch_buffers")]
|
#[pyo3(name = "fetch_buffers")]
|
||||||
async fn pyfetch_buffers(&self) -> crate::Result<()> {
|
async fn pyfetch_buffers(&self) -> crate::Result<()> {
|
||||||
self.fetch_buffers().await
|
let rc = self.clone();
|
||||||
|
spawn_future!(rc.fetch_buffers()).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "fetch_users")]
|
#[pyo3(name = "fetch_users")]
|
||||||
async fn pyfetch_users(&self) -> crate::Result<()> {
|
async fn pyfetch_users(&self) -> crate::Result<()> {
|
||||||
self.fetch_users().await
|
let rc = self.clone();
|
||||||
|
spawn_future!(rc.fetch_users()).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "list_buffer_users")]
|
#[pyo3(name = "list_buffer_users")]
|
||||||
async fn pylist_buffer_users(&self, path: String) -> crate::Result<Vec<crate::api::User>> {
|
async fn pylist_buffer_users(&self, path: String) -> crate::Result<Vec<crate::api::User>> {
|
||||||
self.list_buffer_users(path.as_str()).await
|
let rc = self.clone();
|
||||||
|
spawn_future!(rc.list_buffer_users(path.as_str()))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "delete")]
|
#[pyo3(name = "delete")]
|
||||||
async fn pydelete(&self, path: String) -> crate::Result<()> {
|
async fn pydelete(&self, path: String) -> crate::Result<()> {
|
||||||
self.delete(path.as_str()).await
|
let rc = self.clone();
|
||||||
|
spawn_future!(rc.delete(path.as_str())).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "id")]
|
#[pyo3(name = "id")]
|
||||||
|
|
Loading…
Reference in a new issue