diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 0bc7180..5ee34da 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -17,16 +17,20 @@ jobs: python-version: ${{ matrix.python-version }} - run: | sudo apt-get update - sudo apt-get install libgmp-dev sudo apt-get install lcov + - run: bash scripts/cibw_before_all.sh - run: pip install --upgrade pip setuptools setuptools_scm - run: pip --verbose install --editable .[tests] - - run: | + - name: Build and run coverage tests + run: | python setup.py clean - CFLAGS="-coverage" python setup.py develop + python setup.py develop pytest --hypothesis-profile=default lcov --capture --directory . --no-external \ --output-file build/coverage-${{ matrix.python-version }}.info + env: + CFLAGS: -coverage + LD_LIBRARY_PATH: .local/lib/ - uses: actions/upload-artifact@v4 with: name: coverage-${{ matrix.python-version }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9dc72e6..8a5aad9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,6 @@ jobs: python-version: [3.9, '3.10', pypy3.10-nightly, 3.11, 3.12, 3.13, 3.14] runs-on: ubuntu-24.04 env: - CFLAGS: -Wpedantic -Werror -std=c17 PYTEST_ADDOPTS: --verbose steps: - uses: actions/checkout@v4 @@ -27,12 +26,14 @@ jobs: with: python-version: ${{ matrix.python-version }} allow-prereleases: true - - run: | - sudo apt-get update - sudo apt-get install libgmp-dev + - run: bash scripts/cibw_before_all.sh - run: pip install --upgrade pip - run: pip --verbose install --editable .[tests] + env: + CFLAGS: -Wpedantic -Werror -std=c17 - run: pytest + env: + LD_LIBRARY_PATH: .local/lib/ macos: needs: - linux diff --git a/.gitignore b/.gitignore index 808f830..a3a4518 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ venv/ *.egg-info/ *.swp build/ +.local/ diff --git a/README.rst b/README.rst index 7a7d998..e9e234d 100644 --- a/README.rst +++ b/README.rst @@ -10,6 +10,15 @@ also few functions (factorial, gcd and isqrt), compatible with the stdlib's module math. +Warning on alloca +----------------- + +Most GMP packages enable using alloca() for temporary workspace allocation. +This module can't prevent a crash in case of a stack overflow. To avoid this, +you should compile the GMP library with '--disable-alloca' configure option to +use rather the heap for all temporary allocations. + + Motivation ---------- diff --git a/scripts/cibw_before_all.sh b/scripts/cibw_before_all.sh index a9f57ae..43b4b0f 100644 --- a/scripts/cibw_before_all.sh +++ b/scripts/cibw_before_all.sh @@ -19,6 +19,7 @@ rm config.guess && mv configfsf.guess config.guess && chmod +x config.guess --enable-shared \ --disable-static \ --with-pic \ + --disable-alloca \ --prefix=$PREFIX -q make -j6 -s make -s install diff --git a/tests/test_functions.py b/tests/test_functions.py index 18eb0ff..db9a232 100644 --- a/tests/test_functions.py +++ b/tests/test_functions.py @@ -1,5 +1,6 @@ import math import platform +import resource import pytest from gmp import factorial, gcd, isqrt, mpz @@ -25,22 +26,18 @@ def test_factorial(x): reason="FIXME: setrlimit fails with ValueError on MacOS") @pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="XXX: bug in PyNumber_ToBase()?") -def test_factorial_outofmemory(): - import random - import resource - - for _ in range(100): - soft, hard = resource.getrlimit(resource.RLIMIT_AS) - resource.setrlimit(resource.RLIMIT_AS, (1024*64*1024, hard)) - a = random.randint(12811, 24984) - a = mpz(a) - while True: - try: - factorial(a) - a *= 2 - except MemoryError: - break - resource.setrlimit(resource.RLIMIT_AS, (soft, hard)) +@given(integers(min_value=12811, max_value=24984)) +def test_factorial_outofmemory(x): + soft, hard = resource.getrlimit(resource.RLIMIT_AS) + resource.setrlimit(resource.RLIMIT_AS, (1024*32*1024, hard)) + a = mpz(x) + while True: + try: + factorial(a) + a *= 2 + except MemoryError: + break + resource.setrlimit(resource.RLIMIT_AS, (soft, hard)) @given(integers(), integers()) diff --git a/tests/test_mpz.py b/tests/test_mpz.py index add9c87..cb9d209 100644 --- a/tests/test_mpz.py +++ b/tests/test_mpz.py @@ -2,6 +2,7 @@ import operator import pickle import platform +import resource import string import sys import warnings @@ -776,8 +777,6 @@ def test_pickle(protocol, x): @example(249846727467293) @example(1292734994793) def test_outofmemory(x): - import resource - soft, hard = resource.getrlimit(resource.RLIMIT_AS) resource.setrlimit(resource.RLIMIT_AS, (1024*32*1024, hard)) mx = mpz(x)