Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
570 changes: 569 additions & 1 deletion c_src/py_nif.c

Large diffs are not rendered by default.

75 changes: 75 additions & 0 deletions docker/Dockerfile.asan
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Dockerfile for testing with Python ASAN (Address Sanitizer)
FROM erlang:27-slim

# Install build dependencies
RUN apt-get update && apt-get install -y \
build-essential \
cmake \
git \
curl \
wget \
libssl-dev \
zlib1g-dev \
libbz2-dev \
libreadline-dev \
libsqlite3-dev \
libncurses5-dev \
libncursesw5-dev \
xz-utils \
tk-dev \
libffi-dev \
liblzma-dev \
clang \
llvm \
&& rm -rf /var/lib/apt/lists/*

# Build Python 3.13 with ASAN enabled
ARG PYTHON_VERSION=3.13.12
RUN cd /tmp && \
wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz && \
tar xf Python-${PYTHON_VERSION}.tar.xz && \
cd Python-${PYTHON_VERSION} && \
CC=clang CXX=clang++ \
CFLAGS="-fsanitize=address -fno-omit-frame-pointer -g" \
LDFLAGS="-fsanitize=address" \
./configure --prefix=/usr/local \
--with-pydebug \
--with-address-sanitizer \
--enable-shared \
LDFLAGS="-Wl,-rpath,/usr/local/lib -fsanitize=address" && \
make -j$(nproc) && \
make install && \
cd / && rm -rf /tmp/Python-*

# Verify Python installation
RUN python3 --version

# Set environment for rebar3 and ASAN
ENV PYTHON_CONFIG=/usr/local/bin/python3-config
ENV LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
ENV ASAN_OPTIONS=detect_leaks=0:abort_on_error=1:print_stats=1

# Set compiler to clang with ASAN for NIF compilation via CMake variables
ENV CC=clang
ENV CXX=clang++
ENV CMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer -g -O1"
ENV CMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer -g -O1"
ENV CMAKE_EXE_LINKER_FLAGS="-fsanitize=address"
ENV CMAKE_SHARED_LINKER_FLAGS="-fsanitize=address"

# Create working directory
WORKDIR /app

# Copy source
COPY . /app

# Remove problematic plugin
RUN sed -i '/rebar3_ex_doc/d' rebar.config || true

# Set ASAN_BUILD for the build hooks
ENV ASAN_BUILD=1

# Run tests - do_cmake.sh will use ASAN flags via ASAN_BUILD env var
# Preload ASAN runtime to ensure symbol compatibility with ASAN-built Python
CMD export LD_PRELOAD=$(clang -print-file-name=libclang_rt.asan-x86_64.so) && \
rm -rf _build && rebar3 ct --readable=compact
83 changes: 83 additions & 0 deletions docker/Dockerfile.python312
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Dockerfile for testing with Python 3.12 (subinterpreter support)
# Supports ASAN builds when ASAN_BUILD=1
FROM erlang:27-slim

# Prevent tzdata from asking for input
ENV DEBIAN_FRONTEND=noninteractive

# Build arg for ASAN
ARG ASAN_BUILD=0

# Install build dependencies
RUN apt-get update && apt-get install -y \
build-essential \
cmake \
git \
curl \
wget \
libssl-dev \
zlib1g-dev \
libbz2-dev \
libreadline-dev \
libsqlite3-dev \
libncurses5-dev \
libncursesw5-dev \
xz-utils \
tk-dev \
libffi-dev \
liblzma-dev \
clang \
llvm \
&& rm -rf /var/lib/apt/lists/*

# Build Python 3.12 from source (with ASAN if requested)
ARG PYTHON_VERSION=3.12.8
RUN cd /tmp && \
wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz && \
tar xf Python-${PYTHON_VERSION}.tar.xz && \
cd Python-${PYTHON_VERSION} && \
if [ "$ASAN_BUILD" = "1" ]; then \
CC=clang CXX=clang++ \
CFLAGS="-fsanitize=address -fno-omit-frame-pointer -g" \
LDFLAGS="-fsanitize=address" \
./configure --prefix=/usr/local \
--with-pydebug \
--with-address-sanitizer \
--enable-shared \
LDFLAGS="-Wl,-rpath,/usr/local/lib -fsanitize=address"; \
else \
./configure --prefix=/usr/local \
--enable-optimizations \
--with-lto \
--enable-shared \
LDFLAGS="-Wl,-rpath,/usr/local/lib"; \
fi && \
make -j$(nproc) && \
make install && \
cd / && rm -rf /tmp/Python-*

# Verify Python installation
RUN python3 --version && \
python3 -c "import sys; print('Python version:', sys.version)"

# Set environment for rebar3
ENV PYTHON_CONFIG=/usr/local/bin/python3-config
ENV LD_LIBRARY_PATH=/usr/local/lib:${LD_LIBRARY_PATH:-}

# Set ASAN options
ENV ASAN_OPTIONS=detect_leaks=0:abort_on_error=1:print_stats=1

# Create working directory
WORKDIR /app

# Copy source
COPY . /app

# Remove problematic plugin
RUN sed -i '/rebar3_ex_doc/d' rebar.config || true

# Persist ASAN_BUILD from build arg to env for runtime
ENV ASAN_BUILD=${ASAN_BUILD}

# Run tests - do_cmake.sh will use ASAN flags via ASAN_BUILD env var
CMD rm -rf _build && rebar3 ct --readable=compact
60 changes: 60 additions & 0 deletions docker/Dockerfile.python314
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Dockerfile for testing with Python 3.14 (free-threading support)
FROM erlang:27-slim

# Install build dependencies
RUN apt-get update && apt-get install -y \
build-essential \
cmake \
git \
curl \
wget \
libssl-dev \
zlib1g-dev \
libbz2-dev \
libreadline-dev \
libsqlite3-dev \
libncurses5-dev \
libncursesw5-dev \
xz-utils \
tk-dev \
libffi-dev \
liblzma-dev \
&& rm -rf /var/lib/apt/lists/*

# Build Python 3.14 with free-threading enabled
ARG PYTHON_VERSION=3.14.3
RUN cd /tmp && \
wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz && \
tar xf Python-${PYTHON_VERSION}.tar.xz && \
cd Python-${PYTHON_VERSION} && \
./configure --prefix=/usr/local \
--enable-optimizations \
--with-lto \
--disable-gil \
--enable-shared \
LDFLAGS="-Wl,-rpath,/usr/local/lib" && \
make -j$(nproc) && \
make install && \
cd / && rm -rf /tmp/Python-*

# Verify Python installation
RUN python3 --version && \
python3 -c "import sys; print('Free-threading:', hasattr(sys, '_is_gil_enabled') and not sys._is_gil_enabled())"

# Set environment for rebar3
ENV PYTHON_CONFIG=/usr/local/bin/python3-config
ENV LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH

# Create working directory
WORKDIR /app

# Copy source
COPY . /app

# Remove problematic plugin and build
RUN sed -i '/rebar3_ex_doc/d' rebar.config || true && \
rm -rf _build && \
rebar3 compile

# Run tests
CMD ["rebar3", "ct", "--readable=compact"]
31 changes: 31 additions & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
services:
# Python 3.14 with free-threading (no GIL)
python314:
build:
context: ..
dockerfile: docker/Dockerfile.python314
environment:
- PYTHON_CONFIG=/usr/local/bin/python3-config
command: rebar3 ct --readable=compact

# Python 3.13 with Address Sanitizer
asan:
build:
context: ..
dockerfile: docker/Dockerfile.asan
environment:
- PYTHON_CONFIG=/usr/local/bin/python3-config
- ASAN_OPTIONS=detect_leaks=0:abort_on_error=1:print_stats=1
- ASAN_BUILD=1

# Python 3.12 with Address Sanitizer
asan312:
build:
context: ..
dockerfile: docker/Dockerfile.python312
args:
- ASAN_BUILD=1
environment:
- PYTHON_CONFIG=/usr/local/bin/python3-config
- ASAN_OPTIONS=detect_leaks=0:abort_on_error=1:print_stats=1
- ASAN_BUILD=1
61 changes: 61 additions & 0 deletions docker/run-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/bin/bash
# Run tests in Docker with different Python configurations

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"

cd "$SCRIPT_DIR"

usage() {
echo "Usage: $0 [python314|asan|all]"
echo ""
echo "Options:"
echo " python314 - Run tests with Python 3.14 (free-threading)"
echo " asan - Run tests with Python + Address Sanitizer"
echo " all - Run both test configurations"
echo ""
echo "Examples:"
echo " $0 python314"
echo " $0 asan"
echo " $0 all"
}

build_and_run() {
local target=$1
echo "========================================"
echo "Building and running: $target"
echo "========================================"

docker compose build "$target"
docker compose run --rm "$target"
}

case "${1:-all}" in
python314)
build_and_run python314
;;
asan)
build_and_run asan
;;
all)
build_and_run python314
echo ""
build_and_run asan
;;
-h|--help)
usage
exit 0
;;
*)
echo "Error: Unknown option '$1'"
usage
exit 1
;;
esac

echo ""
echo "========================================"
echo "All tests completed successfully!"
echo "========================================"
Loading
Loading