Skip to content

Commit 0d15d6c

Browse files
committed
Switch WAMP ubjson serializer from py-ubjson to bjdata (#1849)
Fixes #1849: py-ubjson is unmaintained and ships no wheels, so `pip install --only-binary :all:` for autobahn failed. The WAMP "ubjson" serializer is now backed by bjdata (Binary JData), a maintained, wheel-shipping successor. - serializer.py / message.py: import bjdata as the "ubjson" backend (serializer id unchanged for transport negotiation). - pyproject.toml: drop the unconditional py-ubjson dependency; bjdata is an OPTIONAL dependency in the `serialization` extra (it pulls in numpy, which we keep out of a minimal install). A minimal `pip install autobahn` now installs cleanly from wheels only. - PyPy: set PYBJDATA_NO_EXTENSION=1 in the test recipes and CI (serdes) to use bjdata's JIT-friendly pure-Python path (also avoids its numpy-ABI-fragile C extension). WIRE-LEVEL CHANGE: bjdata's octet encoding is NOT identical to the prior py-ubjson/UBJSON bytes (unsigned-int markers, little-endian). The wamp-proto UBJSON test vectors will be regenerated in a follow-up wamp-proto PR after the next autobahn-python release; until then the serdes byte-vector conformance suite excludes "ubjson" (round-trip + cross-serializer coverage is retained via test_wamp_serializer.py). See the changelog. Note: This work was completed with AI assistance (Claude Code).
1 parent 3d973e6 commit 0d15d6c

8 files changed

Lines changed: 75 additions & 15 deletions

File tree

.github/workflows/main.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ jobs:
190190
# Create output directory for test results
191191
mkdir -p test-results/serdes-${{ matrix.python-env }}
192192
193+
# On PyPy, use bjdata's pure-Python path (JIT-friendly; avoids the
194+
# numpy-ABI-fragile C extension). See autobahn #1849.
195+
if [[ "${{ matrix.python-env }}" == pypy* ]]; then export PYBJDATA_NO_EXTENSION=1; fi
196+
193197
# Run tests and generate reports
194198
VENV_PYTHON=$(just --quiet _get-venv-python ${{ matrix.python-env }})
195199
${VENV_PYTHON} -m pytest -v \

docs/changelog.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ Changelog
88
26.6.1
99
------
1010

11+
**WAMP Serialization**
12+
13+
* The WAMP ``ubjson`` serializer is now backed by ``bjdata`` (Binary JData) instead of the unmaintained, wheel-less ``py-ubjson``. ``py-ubjson`` is removed as a dependency, which fixes installation via wheels only (``pip install --only-binary :all:``) (#1849)
14+
* ⚠️ **Wire-level change to watch out for:** bjdata's octet-level encoding is NOT identical to the previous py-ubjson/UBJSON bytes (different integer markers, little-endian). The WAMP serializer id remains ``ubjson`` for transport negotiation. The ``wamp-proto`` UBJSON test vectors will be regenerated in a follow-up PR after this release; until then the ``ubjson`` serializer is excluded from the byte-vector conformance suite (round-trip and cross-serializer coverage retained) (#1849)
15+
* ``bjdata`` is an OPTIONAL dependency (it pulls in numpy): the ``ubjson`` serializer now requires the ``autobahn[serialization]`` extra, keeping numpy out of a minimal install. On PyPy, set ``PYBJDATA_NO_EXTENSION=1`` to use the JIT-friendly pure-Python path (#1849)
16+
1117
**FlatBuffers**
1218

1319
* Bump vendored FlatBuffers from v25.9.23 to v25.12.19, restoring the version-sync with zlmdb 26.6.1 (#1853)

examples/serdes/tests/conftest.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,21 @@
1212
from .utils import load_test_vector, get_serializer_ids
1313

1414

15+
# The WAMP "ubjson" serializer is now backed by bjdata (autobahn #1849). Every
16+
# test in this conformance suite is built on the wamp-proto canonical byte vectors
17+
# (serialize-to / deserialize-from / cross-serializer all reference the stored
18+
# ``bytes_hex``). bjdata's octet-level encoding intentionally differs from the
19+
# legacy UBJSON bytes in those vectors, so "ubjson" is excluded from the byte-vector
20+
# conformance suite until the wamp-proto UBJSON vectors are regenerated (a follow-up
21+
# wamp-proto PR after the next autobahn-python release). bjdata round-trip
22+
# correctness is covered by src/autobahn/wamp/test/test_wamp_serializer.py.
23+
_VECTOR_EXCLUDED_SERIALIZERS = ("ubjson",)
24+
25+
26+
def _conformance_serializer_ids():
27+
return [s for s in get_serializer_ids() if s not in _VECTOR_EXCLUDED_SERIALIZERS]
28+
29+
1530
@pytest.fixture(scope="session")
1631
def wamp_test_vector_publish():
1732
"""Load PUBLISH test vector"""
@@ -48,12 +63,12 @@ def pytest_generate_tests(metafunc):
4863
This generates test parameters for serializer_id based on available serializers.
4964
"""
5065
if "serializer_id" in metafunc.fixturenames:
51-
serializer_ids = get_serializer_ids()
66+
serializer_ids = _conformance_serializer_ids()
5267
metafunc.parametrize("serializer_id", serializer_ids)
5368

5469
if "serializer_pair" in metafunc.fixturenames:
5570
# Generate all unique pairs of serializers for cross-serializer tests
56-
serializer_ids = get_serializer_ids()
71+
serializer_ids = _conformance_serializer_ids()
5772
pairs = []
5873
for i, ser1 in enumerate(serializer_ids):
5974
for ser2 in serializer_ids[i + 1 :]:

justfile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,10 @@ check-coverage-twisted venv="" use_nvx="": (install-tools venv) (install-dev ven
746746
VENV_PATH="{{ VENV_DIR }}/${VENV_NAME}"
747747
VENV_PYTHON=$(just --quiet _get-venv-python "${VENV_NAME}")
748748
749+
# On PyPy, use bjdata's pure-Python path (JIT-friendly; avoids the
750+
# numpy-ABI-fragile C extension). See autobahn #1849.
751+
if [[ "${VENV_NAME}" == pypy* ]]; then export PYBJDATA_NO_EXTENSION=1; fi
752+
749753
# Handle NVX configuration
750754
USE_NVX="{{ use_nvx }}"
751755
if [ "${USE_NVX}" = "1" ]; then
@@ -1093,6 +1097,10 @@ test-twisted venv="" use_nvx="": (install-tools venv) (install-dev venv)
10931097
VENV_PATH="{{ VENV_DIR }}/${VENV_NAME}"
10941098
VENV_PYTHON=$(just --quiet _get-venv-python "${VENV_NAME}")
10951099
1100+
# On PyPy, use bjdata's pure-Python path (JIT-friendly; avoids the
1101+
# numpy-ABI-fragile C extension). See autobahn #1849.
1102+
if [[ "${VENV_NAME}" == pypy* ]]; then export PYBJDATA_NO_EXTENSION=1; fi
1103+
10961104
# Handle NVX configuration
10971105
USE_NVX="{{ use_nvx }}"
10981106
if [ "${USE_NVX}" = "1" ]; then
@@ -1134,6 +1142,10 @@ test-asyncio venv="" use_nvx="": (install-tools venv) (install-dev venv)
11341142
VENV_PATH="{{ VENV_DIR }}/${VENV_NAME}"
11351143
VENV_PYTHON=$(just --quiet _get-venv-python "${VENV_NAME}")
11361144
1145+
# On PyPy, use bjdata's pure-Python path (JIT-friendly; avoids the
1146+
# numpy-ABI-fragile C extension). See autobahn #1849.
1147+
if [[ "${VENV_NAME}" == pypy* ]]; then export PYBJDATA_NO_EXTENSION=1; fi
1148+
11371149
# Handle NVX configuration
11381150
USE_NVX="{{ use_nvx }}"
11391151
if [ "${USE_NVX}" = "1" ]; then
@@ -1162,6 +1174,10 @@ test-serdes venv="": (install-tools venv) (install-dev venv)
11621174
fi
11631175
VENV_PYTHON=$(just --quiet _get-venv-python "${VENV_NAME}")
11641176
1177+
# On PyPy, use bjdata's pure-Python path (JIT-friendly; avoids the
1178+
# numpy-ABI-fragile C extension). See autobahn #1849.
1179+
if [[ "${VENV_NAME}" == pypy* ]]; then export PYBJDATA_NO_EXTENSION=1; fi
1180+
11651181
echo "==> Running WAMP message serdes conformance tests in ${VENV_NAME}..."
11661182
echo "==> Test vectors loaded from: wamp-proto/testsuite/"
11671183
${VENV_PYTHON} -m pytest -v \

pyproject.toml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ dependencies = [
5252
"u-msgpack-python>=2.1; platform_python_implementation != 'CPython'",
5353
"ujson>=4.0.2", # Binary wheels for both CPython and PyPy
5454
"cbor2>=5.2.0", # Binary wheels + pure Python fallback
55-
"py-ubjson>=0.16.1", # Pure Python implementation (set PYUBJSON_NO_EXTENSION=1 to skip C extension build)
55+
# NOTE: the WAMP "ubjson" serializer (bjdata) is an OPTIONAL extra, NOT a base
56+
# dependency: bjdata pulls in numpy, which we don't want in a minimal install.
57+
# See the [serialization] extra below. This also fixes #1849 (the old, wheel-less
58+
# py-ubjson made `pip install --only-binary :all:` fail).
5659
# flatbuffers is vendored - no external dependency needed
5760
]
5861

@@ -78,9 +81,14 @@ compress = [
7881
# Users who need snappy: pip install python-snappy
7982
]
8083

81-
# Backwards compatibility - serialization now included by default in base install
82-
# All WAMP serializers (JSON, MessagePack, CBOR, UBJSON, Flatbuffers) are always available
83-
serialization = []
84+
# Optional binary-JSON ("ubjson") WAMP serializer, backed by bjdata.
85+
# JSON, MessagePack, CBOR and (vendored) FlatBuffers are always available in the
86+
# base install; the "ubjson" serializer requires this extra because bjdata pulls
87+
# in numpy, which we keep out of a minimal autobahn install.
88+
# On PyPy, set PYBJDATA_NO_EXTENSION=1 to use the JIT-friendly pure-Python path.
89+
serialization = [
90+
"bjdata>=0.6.0",
91+
]
8492

8593
# TLS transport encryption, WAMP-cryptosign end-to-end encryption and authentication
8694
encryption = [

src/autobahn/wamp/message.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -774,9 +774,9 @@ def _deserialize_payload(self, data_bytes, ser_id):
774774
# msgpack supports memoryview (zero-copy)
775775
return msgpack.unpackb(data_bytes)
776776
elif ser_id == "ubjson":
777-
import ubjson
777+
# The WAMP "ubjson" serializer is backed by bjdata (see serializer.py)
778+
import bjdata as ubjson
778779

779-
# ubjson supports memoryview (zero-copy)
780780
return ubjson.loadb(data_bytes)
781781
else:
782782
# Fallback to CBOR for unknown serializers

src/autobahn/wamp/serializer.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -909,11 +909,20 @@ def __init__(self, batched=False):
909909
__all__.append("CBORSerializer")
910910

911911

912-
# UBJSON serialization depends on the `py-ubjson` package being available
913-
# https://pypi.python.org/pypi/py-ubjson
914-
# https://github.com/Iotic-Labs/py-ubjson
912+
# The WAMP "ubjson" serializer is backed by the `bjdata` package (Binary JData),
913+
# a maintained, wheel-shipping successor of the (unmaintained, wheel-less)
914+
# `py-ubjson` package.
915+
# https://pypi.org/project/bjdata/ https://github.com/NeuroJSON/pybj
916+
#
917+
# NOTE: bjdata's on-the-wire (octet-level) encoding is NOT identical to the older
918+
# py-ubjson/UBJSON bytes (e.g. unsigned-integer markers, little-endian). The WAMP
919+
# serializer id remains "ubjson" for transport negotiation. See the changelog.
920+
#
921+
# `bjdata` is an OPTIONAL dependency (it pulls in numpy): install it via the
922+
# `autobahn[serialization]` extra. On PyPy, set PYBJDATA_NO_EXTENSION=1 to use the
923+
# JIT-friendly pure-Python path instead of the C extension.
915924
try:
916-
import ubjson
925+
import bjdata as ubjson
917926
except ImportError:
918927
pass
919928
else:

src/autobahn/wamp/test/test_wamp_serializer.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,11 @@ def create_serializers(decimal_support=False):
229229
_serializers.append(serializer.MsgPackSerializer())
230230
_serializers.append(serializer.MsgPackSerializer(batched=True))
231231

232-
# roundtrip error
233-
_serializers.append(serializer.UBJSONSerializer())
234-
_serializers.append(serializer.UBJSONSerializer(batched=True))
232+
# UBJSON (bjdata-backed) is optional: only present when the
233+
# `autobahn[serialization]` extra (bjdata) is installed.
234+
if hasattr(serializer, "UBJSONSerializer"):
235+
_serializers.append(serializer.UBJSONSerializer())
236+
_serializers.append(serializer.UBJSONSerializer(batched=True))
235237

236238
# FIXME: implement full FlatBuffers serializer for WAMP
237239
# WAMP-FlatBuffers currently only supports Python 3

0 commit comments

Comments
 (0)