Skip to content

Commit 0663009

Browse files
authored
Merge pull request #27 from benoitc/feature/process-bound-envs
Add process-bound Python environments
2 parents 7576e5b + c2c99f0 commit 0663009

File tree

12 files changed

+1538
-18
lines changed

12 files changed

+1538
-18
lines changed

c_src/py_nif.c

Lines changed: 569 additions & 1 deletion
Large diffs are not rendered by default.

docker/Dockerfile.asan

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Dockerfile for testing with Python ASAN (Address Sanitizer)
2+
FROM erlang:27-slim
3+
4+
# Install build dependencies
5+
RUN apt-get update && apt-get install -y \
6+
build-essential \
7+
cmake \
8+
git \
9+
curl \
10+
wget \
11+
libssl-dev \
12+
zlib1g-dev \
13+
libbz2-dev \
14+
libreadline-dev \
15+
libsqlite3-dev \
16+
libncurses5-dev \
17+
libncursesw5-dev \
18+
xz-utils \
19+
tk-dev \
20+
libffi-dev \
21+
liblzma-dev \
22+
clang \
23+
llvm \
24+
&& rm -rf /var/lib/apt/lists/*
25+
26+
# Build Python 3.13 with ASAN enabled
27+
ARG PYTHON_VERSION=3.13.12
28+
RUN cd /tmp && \
29+
wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz && \
30+
tar xf Python-${PYTHON_VERSION}.tar.xz && \
31+
cd Python-${PYTHON_VERSION} && \
32+
CC=clang CXX=clang++ \
33+
CFLAGS="-fsanitize=address -fno-omit-frame-pointer -g" \
34+
LDFLAGS="-fsanitize=address" \
35+
./configure --prefix=/usr/local \
36+
--with-pydebug \
37+
--with-address-sanitizer \
38+
--enable-shared \
39+
LDFLAGS="-Wl,-rpath,/usr/local/lib -fsanitize=address" && \
40+
make -j$(nproc) && \
41+
make install && \
42+
cd / && rm -rf /tmp/Python-*
43+
44+
# Verify Python installation
45+
RUN python3 --version
46+
47+
# Set environment for rebar3 and ASAN
48+
ENV PYTHON_CONFIG=/usr/local/bin/python3-config
49+
ENV LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
50+
ENV ASAN_OPTIONS=detect_leaks=0:abort_on_error=1:print_stats=1
51+
52+
# Set compiler to clang with ASAN for NIF compilation via CMake variables
53+
ENV CC=clang
54+
ENV CXX=clang++
55+
ENV CMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer -g -O1"
56+
ENV CMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer -g -O1"
57+
ENV CMAKE_EXE_LINKER_FLAGS="-fsanitize=address"
58+
ENV CMAKE_SHARED_LINKER_FLAGS="-fsanitize=address"
59+
60+
# Create working directory
61+
WORKDIR /app
62+
63+
# Copy source
64+
COPY . /app
65+
66+
# Remove problematic plugin
67+
RUN sed -i '/rebar3_ex_doc/d' rebar.config || true
68+
69+
# Set ASAN_BUILD for the build hooks
70+
ENV ASAN_BUILD=1
71+
72+
# Run tests - do_cmake.sh will use ASAN flags via ASAN_BUILD env var
73+
# Preload ASAN runtime to ensure symbol compatibility with ASAN-built Python
74+
CMD export LD_PRELOAD=$(clang -print-file-name=libclang_rt.asan-x86_64.so) && \
75+
rm -rf _build && rebar3 ct --readable=compact

docker/Dockerfile.python312

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Dockerfile for testing with Python 3.12 (subinterpreter support)
2+
# Supports ASAN builds when ASAN_BUILD=1
3+
FROM erlang:27-slim
4+
5+
# Prevent tzdata from asking for input
6+
ENV DEBIAN_FRONTEND=noninteractive
7+
8+
# Build arg for ASAN
9+
ARG ASAN_BUILD=0
10+
11+
# Install build dependencies
12+
RUN apt-get update && apt-get install -y \
13+
build-essential \
14+
cmake \
15+
git \
16+
curl \
17+
wget \
18+
libssl-dev \
19+
zlib1g-dev \
20+
libbz2-dev \
21+
libreadline-dev \
22+
libsqlite3-dev \
23+
libncurses5-dev \
24+
libncursesw5-dev \
25+
xz-utils \
26+
tk-dev \
27+
libffi-dev \
28+
liblzma-dev \
29+
clang \
30+
llvm \
31+
&& rm -rf /var/lib/apt/lists/*
32+
33+
# Build Python 3.12 from source (with ASAN if requested)
34+
ARG PYTHON_VERSION=3.12.8
35+
RUN cd /tmp && \
36+
wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz && \
37+
tar xf Python-${PYTHON_VERSION}.tar.xz && \
38+
cd Python-${PYTHON_VERSION} && \
39+
if [ "$ASAN_BUILD" = "1" ]; then \
40+
CC=clang CXX=clang++ \
41+
CFLAGS="-fsanitize=address -fno-omit-frame-pointer -g" \
42+
LDFLAGS="-fsanitize=address" \
43+
./configure --prefix=/usr/local \
44+
--with-pydebug \
45+
--with-address-sanitizer \
46+
--enable-shared \
47+
LDFLAGS="-Wl,-rpath,/usr/local/lib -fsanitize=address"; \
48+
else \
49+
./configure --prefix=/usr/local \
50+
--enable-optimizations \
51+
--with-lto \
52+
--enable-shared \
53+
LDFLAGS="-Wl,-rpath,/usr/local/lib"; \
54+
fi && \
55+
make -j$(nproc) && \
56+
make install && \
57+
cd / && rm -rf /tmp/Python-*
58+
59+
# Verify Python installation
60+
RUN python3 --version && \
61+
python3 -c "import sys; print('Python version:', sys.version)"
62+
63+
# Set environment for rebar3
64+
ENV PYTHON_CONFIG=/usr/local/bin/python3-config
65+
ENV LD_LIBRARY_PATH=/usr/local/lib:${LD_LIBRARY_PATH:-}
66+
67+
# Set ASAN options
68+
ENV ASAN_OPTIONS=detect_leaks=0:abort_on_error=1:print_stats=1
69+
70+
# Create working directory
71+
WORKDIR /app
72+
73+
# Copy source
74+
COPY . /app
75+
76+
# Remove problematic plugin
77+
RUN sed -i '/rebar3_ex_doc/d' rebar.config || true
78+
79+
# Persist ASAN_BUILD from build arg to env for runtime
80+
ENV ASAN_BUILD=${ASAN_BUILD}
81+
82+
# Run tests - do_cmake.sh will use ASAN flags via ASAN_BUILD env var
83+
CMD rm -rf _build && rebar3 ct --readable=compact

docker/Dockerfile.python314

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Dockerfile for testing with Python 3.14 (free-threading support)
2+
FROM erlang:27-slim
3+
4+
# Install build dependencies
5+
RUN apt-get update && apt-get install -y \
6+
build-essential \
7+
cmake \
8+
git \
9+
curl \
10+
wget \
11+
libssl-dev \
12+
zlib1g-dev \
13+
libbz2-dev \
14+
libreadline-dev \
15+
libsqlite3-dev \
16+
libncurses5-dev \
17+
libncursesw5-dev \
18+
xz-utils \
19+
tk-dev \
20+
libffi-dev \
21+
liblzma-dev \
22+
&& rm -rf /var/lib/apt/lists/*
23+
24+
# Build Python 3.14 with free-threading enabled
25+
ARG PYTHON_VERSION=3.14.3
26+
RUN cd /tmp && \
27+
wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz && \
28+
tar xf Python-${PYTHON_VERSION}.tar.xz && \
29+
cd Python-${PYTHON_VERSION} && \
30+
./configure --prefix=/usr/local \
31+
--enable-optimizations \
32+
--with-lto \
33+
--disable-gil \
34+
--enable-shared \
35+
LDFLAGS="-Wl,-rpath,/usr/local/lib" && \
36+
make -j$(nproc) && \
37+
make install && \
38+
cd / && rm -rf /tmp/Python-*
39+
40+
# Verify Python installation
41+
RUN python3 --version && \
42+
python3 -c "import sys; print('Free-threading:', hasattr(sys, '_is_gil_enabled') and not sys._is_gil_enabled())"
43+
44+
# Set environment for rebar3
45+
ENV PYTHON_CONFIG=/usr/local/bin/python3-config
46+
ENV LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
47+
48+
# Create working directory
49+
WORKDIR /app
50+
51+
# Copy source
52+
COPY . /app
53+
54+
# Remove problematic plugin and build
55+
RUN sed -i '/rebar3_ex_doc/d' rebar.config || true && \
56+
rm -rf _build && \
57+
rebar3 compile
58+
59+
# Run tests
60+
CMD ["rebar3", "ct", "--readable=compact"]

docker/docker-compose.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
services:
2+
# Python 3.14 with free-threading (no GIL)
3+
python314:
4+
build:
5+
context: ..
6+
dockerfile: docker/Dockerfile.python314
7+
environment:
8+
- PYTHON_CONFIG=/usr/local/bin/python3-config
9+
command: rebar3 ct --readable=compact
10+
11+
# Python 3.13 with Address Sanitizer
12+
asan:
13+
build:
14+
context: ..
15+
dockerfile: docker/Dockerfile.asan
16+
environment:
17+
- PYTHON_CONFIG=/usr/local/bin/python3-config
18+
- ASAN_OPTIONS=detect_leaks=0:abort_on_error=1:print_stats=1
19+
- ASAN_BUILD=1
20+
21+
# Python 3.12 with Address Sanitizer
22+
asan312:
23+
build:
24+
context: ..
25+
dockerfile: docker/Dockerfile.python312
26+
args:
27+
- ASAN_BUILD=1
28+
environment:
29+
- PYTHON_CONFIG=/usr/local/bin/python3-config
30+
- ASAN_OPTIONS=detect_leaks=0:abort_on_error=1:print_stats=1
31+
- ASAN_BUILD=1

docker/run-tests.sh

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/bin/bash
2+
# Run tests in Docker with different Python configurations
3+
4+
set -e
5+
6+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7+
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
8+
9+
cd "$SCRIPT_DIR"
10+
11+
usage() {
12+
echo "Usage: $0 [python314|asan|all]"
13+
echo ""
14+
echo "Options:"
15+
echo " python314 - Run tests with Python 3.14 (free-threading)"
16+
echo " asan - Run tests with Python + Address Sanitizer"
17+
echo " all - Run both test configurations"
18+
echo ""
19+
echo "Examples:"
20+
echo " $0 python314"
21+
echo " $0 asan"
22+
echo " $0 all"
23+
}
24+
25+
build_and_run() {
26+
local target=$1
27+
echo "========================================"
28+
echo "Building and running: $target"
29+
echo "========================================"
30+
31+
docker compose build "$target"
32+
docker compose run --rm "$target"
33+
}
34+
35+
case "${1:-all}" in
36+
python314)
37+
build_and_run python314
38+
;;
39+
asan)
40+
build_and_run asan
41+
;;
42+
all)
43+
build_and_run python314
44+
echo ""
45+
build_and_run asan
46+
;;
47+
-h|--help)
48+
usage
49+
exit 0
50+
;;
51+
*)
52+
echo "Error: Unknown option '$1'"
53+
usage
54+
exit 1
55+
;;
56+
esac
57+
58+
echo ""
59+
echo "========================================"
60+
echo "All tests completed successfully!"
61+
echo "========================================"

0 commit comments

Comments
 (0)