Skip to content

PYTHON-5683: Spike: Investigate using Rust for Extension Modules #62

PYTHON-5683: Spike: Investigate using Rust for Extension Modules

PYTHON-5683: Spike: Investigate using Rust for Extension Modules #62

Workflow file for this run

name: Rust Extension Tests
on:
push:
branches: ["master", "v**"]
pull_request:
workflow_dispatch:
concurrency:
group: rust-tests-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: bash -eux {0}
permissions:
contents: read
jobs:
build-and-test:
name: Rust Extension - ${{ matrix.os }} - Python ${{ matrix.python-version }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
exclude:
# Reduce matrix size - test all Python versions on Linux, subset on others
- os: macos-latest
python-version: "3.10"
- os: macos-latest
python-version: "3.11"
- os: windows-latest
python-version: "3.10"
- os: windows-latest
python-version: "3.11"
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@7b1c307e0dcbda6122208f10795a713336a9b35a # stable
with:
toolchain: stable
- name: Cache Rust dependencies
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
bson/_rbson/target/
key: ${{ runner.os }}-cargo-${{ hashFiles('bson/_rbson/Cargo.lock') }}
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install maturin pytest
- name: Build C extension
run: |
pip install setuptools
python _setup.py build_ext -i
- name: Build Rust extension
run: |
cd bson/_rbson
bash build.sh
- name: Verify both extensions are available
run: |
python -c "
import bson
print(f'C extension available: {bson.has_c()}')
print(f'Rust extension available: {bson.has_rust()}')
assert bson.has_c(), 'C extension should be available'
assert bson.has_rust(), 'Rust extension should be available'
"
- name: Smoke test - C extension (default)
run: |
python -c "
import bson
assert bson.get_bson_implementation() == 'c', 'Should default to C'
data = {'test': 'c_extension', 'value': 42}
encoded = bson.encode(data)
decoded = bson.decode(encoded)
assert decoded == data, 'C extension encode/decode failed'
print('C extension smoke test passed')
"
- name: Smoke test - Rust extension
env:
PYMONGO_USE_RUST: "1"
run: |
python -c "
import bson
assert bson.get_bson_implementation() == 'rust', 'Should use Rust'
data = {'test': 'rust_extension', 'value': 99, 'nested': {'key': 'value'}}
encoded = bson.encode(data)
decoded = bson.decode(encoded)
assert decoded == data, 'Rust extension encode/decode failed'
print('Rust extension smoke test passed')
"
- name: Run BSON test suite with C extension (baseline)
run: |
python -m unittest test.test_bson.TestBSON -v
- name: Run BSON test suite with Rust extension
env:
PYMONGO_USE_RUST: "1"
run: |
python -m unittest test.test_bson.TestBSON -v
- name: Test cross-compatibility (C → Rust)
run: |
python -c "
import os
import sys
# Encode with C
import bson as bson_c
assert bson_c.get_bson_implementation() == 'c'
data = {'cross': 'compatibility', 'test': True}
c_encoded = bson_c.encode(data)
# Decode with Rust - preserve extension modules
os.environ['PYMONGO_USE_RUST'] = '1'
# Save extension modules before clearing
_cbson = sys.modules.get('bson._cbson')
_rbson = sys.modules.get('bson._rbson')
# Clear bson modules except extensions
for key in list(sys.modules.keys()):
if key.startswith('bson') and not key.endswith(('_cbson', '_rbson')):
del sys.modules[key]
# Restore extension modules
if _cbson:
sys.modules['bson._cbson'] = _cbson
if _rbson:
sys.modules['bson._rbson'] = _rbson
import bson as bson_rust
assert bson_rust.get_bson_implementation() == 'rust'
rust_decoded = bson_rust.decode(c_encoded)
assert rust_decoded == data, 'Cross-compatibility failed'
print('C to Rust cross-compatibility works')
"
- name: Run performance benchmark
run: |
FASTBENCH=1 python test/performance/perf_test.py TestRustSimpleIntEncodingC TestRustSimpleIntEncodingRust TestRustMixedTypesEncodingC TestRustMixedTypesEncodingRust -v