feat: added maturin wheel building config as completely separate "project",

feat: added stubs for python glue, bundled in the wheel,
feat: the python glue now uses less stupid names.
This commit is contained in:
cschen 2024-08-06 23:28:09 +02:00
parent f9784e961d
commit bdbd94879b
7 changed files with 170 additions and 13 deletions

3
.gitignore vendored
View file

@ -4,6 +4,9 @@ Cargo.lock
.vscode/ .vscode/
*.sublime-* *.sublime-*
.venv
wheels/
# js # js
node_modules/ node_modules/
package-lock.json package-lock.json

1
dist/py/.python-version vendored Normal file
View file

@ -0,0 +1 @@
3.8

7
dist/py/build.sh vendored Executable file
View file

@ -0,0 +1,7 @@
ROOT_DIR="$(pwd)"
WHEEL_DIR="$ROOT_DIR/wheels"
PYO3_PYTHON="$(pyenv which python)"
TARGET_EXT="$($PYO3_PYTHON -c 'import sysconfig; print(sysconfig.get_config_var("EXT_SUFFIX"))')"
maturin build -i "$PYO3_PYTHON" --out "$WHEEL_DIR"

65
dist/py/codemp.pyi vendored Normal file
View file

@ -0,0 +1,65 @@
from typing import Tuple
def init_logger(debug: bool | None) -> PyLogger: ...
def codemp_init() -> Client: ...
class PyLogger:
async def message(self) -> str | None: ...
class CodempTextChange:
start_incl: int
end_excl: int
content: str
def is_deletion(self) -> bool: ...
def is_addition(self) -> bool: ...
def is_empty(self) -> bool: ...
def apply(self, txt: str) -> str: ...
def from_diff(self, before: str, after: str) -> CodempTextChange: ...
def index_to_rowcol(self, txt: str, index: int) -> Tuple[int, int]: ...
class CodempBufferController:
def content(self) -> str: ...
def send(self, start: int, end: int, txt: str) -> None: ...
async def try_recv(self) -> CodempTextChange | None: ...
async def recv(self) -> CodempTextChange: ...
async def poll(self) -> None: ...
class CodempCursor:
start: Tuple[int, int]
end: Tuple[int, int]
buffer: str
user: str # can be an empty string
class CodempCursorController:
def send(self, path: str, start: Tuple[int, int], end: Tuple[int, int]) -> None: ...
def try_recv(self) -> CodempCursor | None: ...
async def recv(self) -> CodempCursor: ...
async def poll(self) -> None: ...
class CodempWorkspace:
async def create(self, path: str) -> None: ...
async def attach(self, path: str) -> CodempBufferController: ...
async def fetch_buffers(self) -> None: ...
async def fetch_users(self) -> None: ...
async def list_buffer_users(self, path: str) -> list[str]: ...
async def delete(self, path: str) -> None: ...
def id(self) -> str: ...
def cursor(self) -> CodempCursorController: ...
def buffer_by_name(self, path: str) -> CodempBufferController: ...
def filetree(self) -> list[str]: ...
class Client:
def __init__(self) -> None: ...
async def connect(self, host: str) -> None: ...
async def login(self, user: str, password: str, workspace: str | None) -> None: ...
async def join_workspace(self, workspace: str) -> CodempWorkspace: ...
async def get_workspace(self, id: str) -> CodempWorkspace: ...
async def user_id(self) -> str: ...

30
dist/py/pyproject.toml vendored Normal file
View file

@ -0,0 +1,30 @@
[project]
name = "codemp"
version = "0.6.2"
description = "Cooperative multi-player coding"
requires-python = ">=3.8"
license = {file = "../../LICENSE"}
keywords = ["codemp", "ffi", "rust", "python"]
authors = [
{email = "cschen@codemp.dev"}
]
maintainers = [
{name = "Camillo Schenone", email = "cschen@codemp.dev"}
]
classifiers = [
"Programming Language :: Python"
]
dependencies = []
[project.urls]
repository = "github.com/hexedtech/codemp.git"
[build-system]
requires = ["maturin>=1.0,<2.0"]
build-backend = "maturin"
[tool.maturin]
features = ["python"]
manifest-path = "../../Cargo.toml"

View file

@ -4,7 +4,6 @@
//! information about their identity //! information about their identity
use codemp_proto as proto; use codemp_proto as proto;
// use pyo3::prelude::*;
use uuid::Uuid; use uuid::Uuid;
/// user cursor position in a buffer /// user cursor position in a buffer

View file

@ -1,4 +1,4 @@
use pyo3::types::PyList; use pyo3::types::{PyList, PyTuple};
use std::{format, sync::Arc}; use std::{format, sync::Arc};
use tokio::sync::{mpsc, Mutex, RwLock}; use tokio::sync::{mpsc, Mutex, RwLock};
use tracing; use tracing;
@ -73,8 +73,8 @@ impl PyLogger {
// 5. Create a new buffer/attach to an existing one // 5. Create a new buffer/attach to an existing one
#[pyfunction] #[pyfunction]
fn codemp_init<'a>(py: Python<'a>) -> PyResult<Py<PyClient>> { fn codemp_init<'a>(py: Python<'a>) -> PyResult<Py<Client>> {
Ok(Py::new(py, PyClient::default())?) Ok(Py::new(py, Client::default())?)
} }
#[pyfunction] #[pyfunction]
@ -108,22 +108,22 @@ fn init_logger(py: Python<'_>, debug: Option<bool>) -> PyResult<Py<PyLogger>> {
} }
#[pyclass] #[pyclass]
struct PyClient(Arc<RwLock<Option<CodempClient>>>); struct Client(Arc<RwLock<Option<CodempClient>>>);
impl Default for PyClient { impl Default for Client {
fn default() -> Self { fn default() -> Self {
PyClient(Arc::new(RwLock::new(None))) Client(Arc::new(RwLock::new(None)))
} }
} }
impl From<CodempClient> for PyClient { impl From<CodempClient> for Client {
fn from(value: CodempClient) -> Self { fn from(value: CodempClient) -> Self {
PyClient(RwLock::new(Some(value)).into()) Client(RwLock::new(Some(value)).into())
} }
} }
#[pymethods] #[pymethods]
impl PyClient { impl Client {
fn connect<'a>(&'a self, py: Python<'a>, dest: String) -> PyResult<&'a PyAny> { fn connect<'a>(&'a self, py: Python<'a>, dest: String) -> PyResult<&'a PyAny> {
let cli = self.0.clone(); let cli = self.0.clone();
@ -217,6 +217,7 @@ impl PyClient {
#[pymethods] #[pymethods]
impl CodempWorkspace { impl CodempWorkspace {
// join a workspace // join a workspace
#[pyo3(name = "create")]
fn pycreate<'a>(&'a self, py: Python<'a>, path: String) -> PyResult<&'a PyAny> { fn pycreate<'a>(&'a self, py: Python<'a>, path: String) -> PyResult<&'a PyAny> {
let ws = self.clone(); let ws = self.clone();
@ -225,7 +226,7 @@ impl CodempWorkspace {
Ok(()) Ok(())
}) })
} }
#[pyo3(name = "attach")]
fn pyattach<'a>(&'a self, py: Python<'a>, path: String) -> PyResult<&'a PyAny> { fn pyattach<'a>(&'a self, py: Python<'a>, path: String) -> PyResult<&'a PyAny> {
let ws = self.clone(); let ws = self.clone();
@ -235,6 +236,7 @@ impl CodempWorkspace {
}) })
} }
#[pyo3(name = "fetch_buffers")]
fn pyfetch_buffers<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> { fn pyfetch_buffers<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> {
let ws = self.clone(); let ws = self.clone();
@ -244,6 +246,7 @@ impl CodempWorkspace {
}) })
} }
#[pyo3(name = "fetch_users")]
fn pyfetch_users<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> { fn pyfetch_users<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> {
let ws = self.clone(); let ws = self.clone();
@ -253,6 +256,7 @@ impl CodempWorkspace {
}) })
} }
#[pyo3(name = "list_buffer_users")]
fn pylist_buffer_users<'a>(&'a self, py: Python<'a>, path: String) -> PyResult<&'a PyAny> { fn pylist_buffer_users<'a>(&'a self, py: Python<'a>, path: String) -> PyResult<&'a PyAny> {
let ws = self.clone(); let ws = self.clone();
@ -268,6 +272,7 @@ impl CodempWorkspace {
}) })
} }
#[pyo3(name = "delete")]
fn pydelete<'a>(&'a self, py: Python<'a>, path: String) -> PyResult<&'a PyAny> { fn pydelete<'a>(&'a self, py: Python<'a>, path: String) -> PyResult<&'a PyAny> {
let ws = self.clone(); let ws = self.clone();
@ -277,14 +282,17 @@ impl CodempWorkspace {
}) })
} }
#[pyo3(name = "id")]
fn pyid(&self, py: Python<'_>) -> Py<PyString> { fn pyid(&self, py: Python<'_>) -> Py<PyString> {
PyString::new(py, self.id().as_str()).into() PyString::new(py, self.id().as_str()).into()
} }
#[pyo3(name = "cursor")]
fn pycursor(&self, py: Python<'_>) -> PyResult<Py<CodempCursorController>> { fn pycursor(&self, py: Python<'_>) -> PyResult<Py<CodempCursorController>> {
Ok(Py::new(py, CodempCursorController::from(self.cursor()))?) Ok(Py::new(py, CodempCursorController::from(self.cursor()))?)
} }
#[pyo3(name = "buffer_by_name")]
fn pybuffer_by_name( fn pybuffer_by_name(
&self, &self,
py: Python<'_>, py: Python<'_>,
@ -297,6 +305,7 @@ impl CodempWorkspace {
Ok(Some(Py::new(py, CodempBufferController::from(bufctl))?)) Ok(Some(Py::new(py, CodempBufferController::from(bufctl))?))
} }
#[pyo3(name = "filetree")]
fn pyfiletree(&self, py: Python<'_>) -> Py<PyList> { fn pyfiletree(&self, py: Python<'_>) -> Py<PyList> {
PyList::new(py, self.filetree()).into_py(py) PyList::new(py, self.filetree()).into_py(py)
} }
@ -306,6 +315,7 @@ impl CodempWorkspace {
#[pymethods] #[pymethods]
impl CodempCursorController { impl CodempCursorController {
#[pyo3(name = "send")]
fn pysend<'a>(&'a self, path: String, start: (i32, i32), end: (i32, i32)) -> PyResult<()> { fn pysend<'a>(&'a self, path: String, start: (i32, i32), end: (i32, i32)) -> PyResult<()> {
let pos = CodempCursor { let pos = CodempCursor {
start: start.into(), start: start.into(),
@ -317,6 +327,7 @@ impl CodempCursorController {
Ok(self.send(pos)?) Ok(self.send(pos)?)
} }
#[pyo3(name = "try_recv")]
fn pytry_recv(&self, py: Python<'_>) -> PyResult<PyObject> { fn pytry_recv(&self, py: Python<'_>) -> PyResult<PyObject> {
match self.try_recv()? { match self.try_recv()? {
Some(cur_event) => { Some(cur_event) => {
@ -327,6 +338,7 @@ impl CodempCursorController {
} }
} }
#[pyo3(name = "recv")]
fn pyrecv<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> { fn pyrecv<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> {
let rc = self.clone(); let rc = self.clone();
@ -336,6 +348,7 @@ impl CodempCursorController {
}) })
} }
#[pyo3(name = "poll")]
fn pypoll<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> { fn pypoll<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> {
let rc = self.clone(); let rc = self.clone();
@ -343,12 +356,40 @@ impl CodempCursorController {
} }
} }
#[pymethods]
impl CodempCursor {
#[getter(start)]
fn pystart(&self, py: Python<'_>) -> Py<PyTuple> {
self.start.into_py(py)
}
#[getter(end)]
fn pyend(&self, py: Python<'_>) -> Py<PyTuple> {
self.end.into_py(py)
}
#[getter(buffer)]
fn pybuffer(&self, py: Python<'_>) -> Py<PyString> {
PyString::new(py, self.buffer.as_str()).into()
}
#[getter(user)]
fn pyuser(&self, py: Python<'_>) -> Py<PyString> {
match self.user {
Some(user) => PyString::new(py, user.to_string().as_str()).into(),
None => "".into_py(py),
}
}
}
#[pymethods] #[pymethods]
impl CodempBufferController { impl CodempBufferController {
#[pyo3(name = "content")]
fn pycontent<'a>(&self, py: Python<'a>) -> &'a PyString { fn pycontent<'a>(&self, py: Python<'a>) -> &'a PyString {
PyString::new(py, self.content().as_str()) PyString::new(py, self.content().as_str())
} }
#[pyo3(name = "send")]
fn pysend(&self, start: usize, end: usize, txt: String) -> PyResult<()> { fn pysend(&self, start: usize, end: usize, txt: String) -> PyResult<()> {
let op = CodempTextChange { let op = CodempTextChange {
span: start..end, span: start..end,
@ -357,6 +398,7 @@ impl CodempBufferController {
Ok(self.send(op)?) Ok(self.send(op)?)
} }
#[pyo3(name = "try_recv")]
fn pytry_recv(&self, py: Python<'_>) -> PyResult<PyObject> { fn pytry_recv(&self, py: Python<'_>) -> PyResult<PyObject> {
match self.try_recv()? { match self.try_recv()? {
Some(txt_change) => { Some(txt_change) => {
@ -367,6 +409,7 @@ impl CodempBufferController {
} }
} }
#[pyo3(name = "recv")]
fn pyrecv<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> { fn pyrecv<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> {
let rc = self.clone(); let rc = self.clone();
@ -376,6 +419,7 @@ impl CodempBufferController {
}) })
} }
#[pyo3(name = "poll")]
fn pypoll<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> { fn pypoll<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> {
let rc = self.clone(); let rc = self.clone();
@ -386,42 +430,51 @@ impl CodempBufferController {
#[pymethods] #[pymethods]
impl CodempTextChange { impl CodempTextChange {
#[getter] #[getter]
#[pyo3(name = "start_incl")]
fn pystart_incl(&self) -> PyResult<usize> { fn pystart_incl(&self) -> PyResult<usize> {
Ok(self.span.start) Ok(self.span.start)
} }
#[getter] #[getter]
#[pyo3(name = "end_excl")]
fn pyend_excl(&self) -> PyResult<usize> { fn pyend_excl(&self) -> PyResult<usize> {
Ok(self.span.end) Ok(self.span.end)
} }
#[getter] #[getter]
#[pyo3(name = "content")]
fn pycontent(&self) -> PyResult<String> { fn pycontent(&self) -> PyResult<String> {
Ok(self.content.clone()) Ok(self.content.clone())
} }
#[pyo3(name = "is_deletion")]
fn pyis_deletion(&self) -> bool { fn pyis_deletion(&self) -> bool {
self.is_deletion() self.is_deletion()
} }
#[pyo3(name = "is_addition")]
fn pyis_addition(&self) -> bool { fn pyis_addition(&self) -> bool {
self.is_addition() self.is_addition()
} }
#[pyo3(name = "is_empty")]
fn pyis_empty(&self) -> bool { fn pyis_empty(&self) -> bool {
self.is_empty() self.is_empty()
} }
#[pyo3(name = "apply")]
fn pyapply(&self, txt: &str) -> String { fn pyapply(&self, txt: &str) -> String {
self.apply(txt) self.apply(txt)
} }
#[classmethod] #[classmethod]
#[pyo3(name = "from_diff")]
fn pyfrom_diff(_cls: &PyType, before: &str, after: &str) -> CodempTextChange { fn pyfrom_diff(_cls: &PyType, before: &str, after: &str) -> CodempTextChange {
CodempTextChange::from_diff(before, after) CodempTextChange::from_diff(before, after)
} }
#[classmethod] #[classmethod]
#[pyo3(name = "index_to_rowcol")]
fn pyindex_to_rowcol(_cls: &PyType, txt: &str, index: usize) -> (i32, i32) { fn pyindex_to_rowcol(_cls: &PyType, txt: &str, index: usize) -> (i32, i32) {
CodempTextChange::index_to_rowcol(txt, index).into() CodempTextChange::index_to_rowcol(txt, index).into()
} }
@ -432,13 +485,12 @@ impl CodempTextChange {
fn codemp(_py: Python, m: &PyModule) -> PyResult<()> { fn codemp(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(codemp_init, m)?)?; m.add_function(wrap_pyfunction!(codemp_init, m)?)?;
m.add_function(wrap_pyfunction!(init_logger, m)?)?; m.add_function(wrap_pyfunction!(init_logger, m)?)?;
m.add_class::<PyClient>()?; m.add_class::<Client>()?;
m.add_class::<PyLogger>()?; m.add_class::<PyLogger>()?;
m.add_class::<CodempWorkspace>()?; m.add_class::<CodempWorkspace>()?;
m.add_class::<CodempCursorController>()?; m.add_class::<CodempCursorController>()?;
m.add_class::<CodempBufferController>()?; m.add_class::<CodempBufferController>()?;
// m.add_class::<PyId>()?;
m.add_class::<CodempCursor>()?; m.add_class::<CodempCursor>()?;
m.add_class::<CodempTextChange>()?; m.add_class::<CodempTextChange>()?;