refactor pygomx-module -> pygomx

This commit is contained in:
saces 2026-03-09 17:27:43 +01:00
parent e1dfbe218d
commit 23fc397384
11 changed files with 181 additions and 34 deletions

View file

@ -4,40 +4,32 @@ ARG GOLANG_VERSION=1.24
ARG DEBIAN_VERSION=trixie ARG DEBIAN_VERSION=trixie
ARG PYTHON_VERSION=3.14 ARG PYTHON_VERSION=3.14
FROM docker.io/library/golang:${GOLANG_VERSION}-${DEBIAN_VERSION} AS gobuilder
RUN apt update
RUN apt -y install libolm-dev
RUN --mount=type=bind,source=./libmxclient/go.mod,target=/libmxclient/build/go.mod --mount=type=bind,source=./libmxclient/go.sum,target=/libmxclient/build/go.sum <<EOF
cd /libmxclient/build
go mod download
EOF
RUN --mount=type=bind,source=./libmxclient,target=/libmxclient/build <<EOF
# building go lib
set -e
cd /libmxclient/build
CGO_ENABLED=1 GOARCH=${GOARCH} go build -buildmode=c-shared -o /pygomx-build/libmxclient.so .
EOF
FROM docker.io/library/python:${PYTHON_VERSION}-${DEBIAN_VERSION} AS pybuilder FROM docker.io/library/python:${PYTHON_VERSION}-${DEBIAN_VERSION} AS pybuilder
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
ENV PIP_ROOT_USER_ACTION=ignore ENV PIP_ROOT_USER_ACTION=ignore
COPY --from=gobuilder /pygomx-build/libmxclient.so /usr/local/lib/libmxclient.so RUN <<EOF
COPY --from=gobuilder /pygomx-build/libmxclient.h /usr/local/include/libmxclient.h # install packages
set -eu
apt update
apt -y upgrade
apt -y install golang libolm-dev
EOF
RUN pip install cffi setuptools RUN pip install cffi setuptools build
COPY pygomx-module /pygomx #RUN --mount=type=bind,source=.,target=/pygomx <<EOF
COPY libmxclient /pygomx/libmxclient
COPY pygomx /pygomx/pygomx
RUN <<EOF RUN <<EOF
# build py module # build py module
set -e set -eu
cd /pygomx cd /pygomx/pygomx
PYGOMX_BUILD_MODE=shared python3 build_ffi.py python3 -m build --verbose --wheel --outdir /pygomx-wheel
ls -la *.so
ls -la /pygomx-wheel
EOF EOF
FROM docker.io/library/python:${PYTHON_VERSION}-slim-${DEBIAN_VERSION} AS develop FROM docker.io/library/python:${PYTHON_VERSION}-slim-${DEBIAN_VERSION} AS develop
@ -54,9 +46,7 @@ apt -y install libolm3
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
EOF EOF
COPY --from=gobuilder /pygomx-build/libmxclient.so /usr/local/lib/ RUN --mount=type=bind,from=pybuilder,source=/pygomx-wheel,target=/pygomx-wheel pip install /pygomx-wheel/*.whl
COPY --from=pybuilder /pygomx/_pygomx.cpython-314-x86_64-linux-gnu.so /usr/local/lib/python3.14/site-packages/
RUN ldconfig
FROM develop AS demobot FROM develop AS demobot
WORKDIR /smal WORKDIR /smal

View file

@ -1,7 +1,7 @@
monorepos to have the right versions together monorepos to have the right versions together
libmxclient - golang matrix client library libmxclient - golang matrix client library
pygomx-module - python binding package pygomx - python binding package
smal - python matrix lib smal - python matrix lib
@ -20,7 +20,7 @@ the bot follows each invite (autojoin) and have two commands:
binary/package install: binary/package install:
(only linux-amd64 for now) (only linux-amd64 for now)
pip install --index-url https://codeberg.org/api/packages/saces/pypi/simple/ --no-deps pygomx-module pip install --index-url https://codeberg.org/api/packages/saces/pypi/simple/ --no-deps pygomx
pip install git+https://codeberg.org/saces/pygomx.git#subdirectory=smal pip install git+https://codeberg.org/saces/pygomx.git#subdirectory=smal
@ -40,7 +40,7 @@ install from source / develop (venv):
(create and activate a venv) (create and activate a venv)
cd pygomx-module cd pygomx
pip install . pip install .
cd ../smal cd ../smal

View file

@ -1,5 +1,5 @@
pygomx-module pygomx
============= ======
python package with the pygomx binaries python package with the pygomx binaries

View file

@ -3,7 +3,7 @@ requires = ["setuptools>=80", "cffi>=2.0.0"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[project] [project]
name = "pygomx-module" name = "pygomx"
version = "0.0.1" version = "0.0.1"
requires-python = ">=3.10" requires-python = ">=3.10"
description = "python pindings for a golang matrix library" description = "python pindings for a golang matrix library"
@ -15,3 +15,6 @@ dependencies = ["cffi>=2.0.0"]
[project.urls] [project.urls]
homepage = "https://codeberg.org/saces/pygomx" homepage = "https://codeberg.org/saces/pygomx"
heimseite = "https://code.c-base.org/saces/pygomx" heimseite = "https://code.c-base.org/saces/pygomx"
[tool.setuptools.package-dir]
"pygomx" = "src/pygomx"

View file

@ -51,7 +51,7 @@ class CustomCommand(Command):
"-tags", "-tags",
",".join(go_tags), ",".join(go_tags),
"-o", "-o",
f"../pygomx-module/libmxclient{build_mode_ext}", f"../pygomx/libmxclient{build_mode_ext}",
".", ".",
] ]
subprocess.call(go_call, cwd="../libmxclient") subprocess.call(go_call, cwd="../libmxclient")

View file

View file

@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
class APIError(Exception):
"""Exception raised for api usage errors.
Attributes:
message -- explanation of the error
"""
def __init__(self, message):
self.message = message[4:]
super().__init__(self.message)

141
pygomx/src/pygomx/pygomx.py Normal file
View file

@ -0,0 +1,141 @@
# -*- coding: utf-8 -*-
import logging
from _pygomx import lib, ffi
import json
from .errors import APIError
logger = logging.getLogger(__name__)
def checkApiError(cstr):
result = ffi.string(cstr).decode("utf-8")
lib.FreeCString(cstr)
if result.startswith("ERR:"):
raise APIError(result)
if result == "SUCCESS.":
return
logger.debug(result)
result_dict = json.loads(result)
return result_dict
class _MXClient:
"""
core binding
"""
def __init__(self):
super().__init__()
self._createMXClient()
# create a c-handle for self and keep it alive
self._ffi_selfhandle = ffi.new_handle(self)
r = lib.apiv0_set_on_event_handler(
self.client_id, on_event_callback, self._ffi_selfhandle
)
result = ffi.string(r)
lib.FreeCString(r)
if result.startswith(b"ERR:"):
raise APIError(result)
r = lib.apiv0_set_on_message_handler(
self.client_id, on_message_callback, self._ffi_selfhandle
)
result = ffi.string(r)
lib.FreeCString(r)
if result.startswith(b"ERR:"):
raise APIError(result)
r = lib.apiv0_set_on_sys_handler(
self.client_id, on_sys_callback, self._ffi_selfhandle
)
result = ffi.string(r)
lib.FreeCString(r)
if result.startswith(b"ERR:"):
raise APIError(result)
def _createMXClient(self):
r = lib.apiv0_createclient_pass(b".mxpass", b".", b"*", b"*", b"*")
result = ffi.string(r)
lib.FreeCString(r)
if result.startswith(b"ERR:"):
raise APIError(result)
result_dict = json.loads(result)
self.client_id = result_dict["id"]
self.UserID = result_dict["userid"]
self.DeviceID = result_dict["deviceid"]
def _sync(self):
r = lib.apiv0_startclient(self.client_id)
checkApiError(r)
def _stopsync(self):
r = lib.apiv0_stopclient(self.client_id)
checkApiError(r)
def _sendmessage(self, data_dict):
data = json.dumps(data_dict).encode(encoding="utf-8")
r = lib.apiv0_sendmessage(self.client_id, data)
result = checkApiError(r)
return result
def leaveroom(self, roomid):
r = lib.apiv0_leaveroom(self.client_id, roomid.encode(encoding="utf-8"))
checkApiError(r)
def joinedrooms(self):
r = lib.apiv0_joinedrooms(self.client_id)
return checkApiError(r)
def _createroom(self, data_dict):
data = json.dumps(data_dict).encode(encoding="utf-8")
r = lib.apiv0_createroom(self.client_id, data)
return checkApiError(r)
def process_event(self, evt):
if hasattr(self, "on_event") and callable(self.on_event):
self.on_event(evt)
else:
logger.warn(f"got event but on_event not declared: {evt}")
def process_message(self, msg):
if hasattr(self, "on_message") and callable(self.on_message):
self.on_message(msg)
else:
logger.warn(f"got message but on_message not declared: {msg}")
def process_sys(self, ntf):
if hasattr(self, "on_sys") and callable(self.on_sys):
self.on_sys(ntf)
else:
logger.warn(f"got systen notification but on_sys not declared: {ntf}")
@ffi.callback("void(char*, void*)")
def on_event_callback(evt, pobj):
cli = ffi.from_handle(pobj)
e = ffi.string(evt).decode("utf-8")
evt = json.loads(e)
cli.process_event(evt)
@ffi.callback("void(char*, void*)")
def on_message_callback(msg, pobj):
cli = ffi.from_handle(pobj)
m = ffi.string(msg).decode("utf-8")
msg = json.loads(m)
cli.process_message(msg)
@ffi.callback("void(char*, void*)")
def on_sys_callback(msg, pobj):
cli = ffi.from_handle(pobj)
m = ffi.string(msg).decode("utf-8")
sys = json.loads(m)
cli.process_sys(sys)