Metadata-Version: 2.4
Name: eveo7-mumbleserver-ice
Version: 0.1.0
Summary: Python bindings for interacting with Mumble server using ZeroC's ICE technology.
Project-URL: Source, https://gitlab.com/eveo7/eveo7-mumbleserver-ice
Project-URL: Issues, https://gitlab.com/eveo7/eveo7-mumbleserver-ice/-/issues
Project-URL: Changelog, https://gitlab.com/eveo7/eveo7-mumbleserver-ice/-/blob/main/CHANGELOG.md
Author-email: Boris Talovikov <boris.t.66@gmail.com>
Maintainer-email: Boris Talovikov <boris.t.66@gmail.com>
License-Expression: BSD-3-Clause
License-File: NOTICE.md
License-File: NOTICE.ru.md
Keywords: ice,mumble,murmur,slice2py,zeroc
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Communications :: Chat
Classifier: Topic :: Communications :: Conferencing
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: <3.15,>=3.11
Requires-Dist: zeroc-ice<3.8,>=3.7.10.1
Description-Content-Type: text/markdown

# eveo7-mumbleserver-ice

Python bindings for [Mumble](https://www.mumble.info/)'s Murmur server,
generated from upstream's `MumbleServer.ice` Slice IDL via
[ZeroC Ice](https://zeroc.com/) `slice2py`. This package is the
distribution layer — the IDL lives upstream, `slice2py` produces the
binding, this repo packages it as a Python wheel with type stubs.

```sh
pip install eveo7-mumbleserver-ice
```

## Compatibility

| Component | Supported                                                                |
|-----------|--------------------------------------------------------------------------|
| Mumble    | **1.5.x, 1.6.x** — see the Mumble 1.5+ note below                        |
| Python    | CPython **3.11 – 3.14**                                                  |
| OS        | Linux, macOS, Windows (Linux always builds from source — see below)      |

> [!IMPORTANT]
> Bindings target Mumble's `::MumbleServer` Slice contract, introduced
> in Mumble **1.5**. The module was renamed from `::Murmur` upstream,
> so attaching to a 1.4.x Murmur server fails at
> `MetaPrx.checkedCast(...)` returning `None`. For pre-1.5 servers,
> use a binding generated against the old IDL.

## Quick start

Connect to a running Mumble server's Ice endpoint and exercise a
representative operation:

```python
import Ice
import eveo7_mumbleserver_ice as pkg

communicator = Ice.initialize()
try:
    base = communicator.stringToProxy("Meta:tcp -h 127.0.0.1 -p 6502")
    meta = pkg.MetaPrx.checkedCast(base)
    if meta is None:
        raise RuntimeError("server is not exposing ::MumbleServer::Meta")

    major, minor, patch, label = meta.getVersion()
    print(f"Mumble {major}.{minor}.{patch} ({label})")

    for server in meta.getAllServers():
        if server.isRunning():
            print(f"  server #{server.id()}: {len(server.getUsers())} users")
finally:
    communicator.destroy()
```

## asyncio

A thin asyncio adapter lives at `eveo7_mumbleserver_ice.aio`. Two entry
points: `wrap()` bridges a single `<op>Async()` future onto an awaitable
`asyncio.Future`, and `AsyncProxy` reflectively forwards attribute
access so each operation can be awaited directly.

> [!TIP]
> The adapter's value is **concurrent fan-out**: `gather(*wrap(...))`
> over many proxies. Single-call overhead is ~50× the sync path, so
> don't reach for `aio` for one-shot queries. Realistic speedup
> against a real Mumble server: ~4× for I/O-bound ops over serial
> sync calls.

```python
import asyncio
from eveo7_mumbleserver_ice.aio import AsyncProxy, wrap

# Assumes ``meta`` was obtained as in Quick start above.
async def report(meta):
    # Single bridged call:
    major, minor, patch, label = await wrap(meta.getVersionAsync())
    print(f"Mumble {major}.{minor}.{patch} ({label})")

    # AsyncProxy — every attribute resolves to ``<name>Async()``
    # under the hood and returns an awaitable:
    ameta = AsyncProxy(meta)
    uptime = await ameta.getUptime()
    print(f"uptime: {uptime}s")

    # Concurrent fan-out — the adapter's primary use case:
    running = [s for s in meta.getAllServers() if s.isRunning()]
    user_lists = await asyncio.gather(
        *(wrap(s.getUsersAsync()) for s in running)
    )
    total = sum(len(u) for u in user_lists)
    print(f"online across {len(running)} servers: {total} users")
```

`Ice.InvocationFuture` is **not** a `concurrent.futures.Future`
subclass — `asyncio.wrap_future` rejects it — so the adapter
hand-rolls the bridge via `add_done_callback` + `call_soon_threadsafe`.
asyncio is stdlib, no extra install needed; `wrap()` is always
available.

## Installation notes

> [!NOTE]
> On **Linux**, no `manylinux` wheel is published for `zeroc-ice`
> upstream — every install compiles the C extension from source.
> Modern GCC's default `-std=gnu23` breaks the upstream sources, so
> `CFLAGS=-std=gnu17` must be set at build time. `uv sync` forwards
> this automatically via the `[tool.uv].extra-build-variables` block
> in this repo's `pyproject.toml`; a plain `pip install` requires
> the flag in the environment.

macOS / Windows installs pull pre-built `zeroc-ice` wheels: cp311 /
cp312 (from 3.7.10.1) and cp313 / cp314 (from 3.7.11). No local
build is required.

Type stubs ship at `eveo7_mumbleserver_ice/__init__.pyi` alongside a
PEP 561 `py.typed` marker, so mypy / pyright / basedpyright resolve
`from eveo7_mumbleserver_ice import ServerPrx` to a concrete typed
class rather than `Any`.

## Architecture

The generated surface — five files, regenerated atomically:

- `MumbleServer.ice` — vendored Slice snapshot pulled from upstream
  `mumble-voip/mumble@master`.
- `.mumble-server-ice.sha256` — sha256sum-format lockfile pinning the
  snapshot's byte content. Verified on every regen; only bumped when
  an upstream diff is reviewed and accepted (`update_ice -- --force`).
- `eveo7_mumbleserver_ice/MumbleServer_ice.py` — `slice2py` output.
  ~7k lines, marked auto-generated.
- `eveo7_mumbleserver_ice/__init__.py` + `__init__.pyi` — generated by
  this repo's pipeline (`_nox/update_ice.py`, `_nox/stub_gen/`); the
  former re-exports every public class from the slice2py module, the
  latter renders honest stubs from the IDL AST.

Hand-written modules (the asyncio adapter at
`eveo7_mumbleserver_ice/aio.py`) live alongside and are not touched
by regeneration.

## Development

All dev tasks run through nox sessions; see `noxfile.py` for the full
list. Headline ones:

```sh
uv sync                                                 # install dev deps
uv run nox -s lint typecheck tests                      # default gates
uv run nox -s preflight                                 # full per-push CI parity
uv run nox -s release_check                             # pre-tag (incl. install matrix)
uv run nox -s postregen                                 # post-update_ice verifier
uv run nox -s update_ice                                # regenerate bindings
uv run nox -s check_drift                               # codegen drift gate
uv run nox -s stubtest                                  # stubs ↔ runtime parity
uv run nox -s integration_real_mumble                   # docker Mumble matrix
uv run nox -s benchmark                                 # marshaling perf
uv run nox -s slice_diff -- old.ice new.ice             # diff Slice snapshots
```

The integration matrix exercises the last release of each Mumble
minor line — 1.4.x (the legacy `::Murmur` module: the test detects
the pre-rename surface and reports a clean skip-with-reason instead
of a hard failure), 1.5.x and 1.6.x (current `::MumbleServer` API
that the bindings target).

> [!IMPORTANT]
> Regeneration (`nox -s update_ice`) must run on Python **3.11 or
> 3.12** — the dev group pins `zeroc-ice==3.7.10.1` for byte-stable
> output, and that release's C extension references CPython symbols
> removed in 3.13. Downstream installs on 3.13 / 3.14 still work via
> the 3.7.11 wheel.

After cloning, install the pre-push hook once so a forgotten regen is
caught locally:

```sh
uv run pre-commit install --hook-type pre-push
```

## See also

- [`CHANGELOG.md`](CHANGELOG.md) — release history
  (Russian: [`CHANGELOG.ru.md`](CHANGELOG.ru.md)).
- [Mumble](https://github.com/mumble-voip/mumble) — upstream server
  and the source of truth for `MumbleServer.ice`.
- [ZeroC Ice — Python mapping](https://doc.zeroc.com/ice/latest/language-mappings/python-mapping)
  — Slice → Python contract this binding implements.
- [`README.ru.md`](README.ru.md) — Russian translation of this file.

## License

[BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause), declared
as an SPDX expression in `pyproject.toml`. Mumble's own
[upstream license](https://www.mumble.info/LICENSE) is textually
identical BSD-3-Clause, and applies to the `MumbleServer.ice` snapshot
vendored here and to the `slice2py` output derived from it. Full
upstream attribution and the verbatim Mumble license text live in
[`NOTICE.md`](NOTICE.md) (Russian sibling: [`NOTICE.ru.md`](NOTICE.ru.md))
— shipped alongside the source distribution and at
`dist-info/licenses/` in the wheel.
