Skip to content

Commit

Permalink
Merge pull request #544 from skirpichev/work-onpypy
Browse files Browse the repository at this point in the history
Support PyPy
  • Loading branch information
casevh authored Jan 6, 2025
2 parents 6e7a351 + b6bd13b commit 532970f
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pip_install_gmpy2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [3.8, 3.11]
python-version: [3.8, 3.11, pypy3.10-nightly]
os: [ubuntu-22.04]
runs-on: ${{ matrix.os }}
steps:
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ classifiers = ['Development Status :: 5 - Production/Stable',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'Programming Language :: Python :: Implementation :: CPython',
"Programming Language :: Python :: Implementation :: PyPy",
'Topic :: Scientific/Engineering :: Mathematics',
'Topic :: Software Development :: Libraries :: Python Modules']
requires-python = '>=3.8'
Expand Down Expand Up @@ -56,5 +57,5 @@ gmpy2 = ['*.pxd', '*.h', '*.dll', '*.lib']

[tool.pytest.ini_options]
addopts = "--durations=10"
norecursedirs = ['build', '.eggs', '.git']
norecursedirs = ['build', '.eggs', '.git', '.hypothesis']
xfail_strict = true
8 changes: 8 additions & 0 deletions src/gmpy2.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ LGPL 3 or later.";
/* The following global structures are used by gmpy_cache.c.
*/

#ifndef PYPY_VERSION
#define CACHE_SIZE (100)
#else
#define CACHE_SIZE (0)
#endif
#define MAX_CACHE_MPZ_LIMBS (64)
#define MAX_CACHE_MPFR_BITS (1024)

Expand Down Expand Up @@ -177,13 +181,15 @@ static PyObject *GMPyExc_Overflow = NULL;
static PyObject *GMPyExc_Underflow = NULL;
static PyObject *GMPyExc_Erange = NULL;

#ifndef PYPY_VERSION
/*
* Parameters of Python’s internal representation of integers.
*/


size_t int_digit_size, int_nails, int_bits_per_digit;
int int_digits_order, int_endianness;
#endif


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Expand Down Expand Up @@ -603,6 +609,7 @@ PyMODINIT_FUNC PyInit_gmpy2(void)
PyObject* xmpz = NULL;
PyObject* limb_size = NULL;

#ifndef PYPY_VERSION
/* Query parameters of Python’s internal representation of integers. */
const PyLongLayout *layout = PyLong_GetNativeLayout();

Expand All @@ -611,6 +618,7 @@ PyMODINIT_FUNC PyInit_gmpy2(void)
int_bits_per_digit = layout->bits_per_digit;
int_nails = int_digit_size*8 - int_bits_per_digit;
int_endianness = layout->digit_endianness;
#endif

#ifndef STATIC
static void *GMPy_C_API[GMPy_API_pointers];
Expand Down
36 changes: 32 additions & 4 deletions src/gmpy2_binary.c
Original file line number Diff line number Diff line change
Expand Up @@ -646,11 +646,39 @@ GMPy_MPC_To_Binary(MPC_Object *obj)
Py_DECREF((PyObject*)real);
Py_DECREF((PyObject*)imag);

PyBytes_AS_STRING(result)[0] = 0x05;
PyBytes_AS_STRING(temp)[0] = 0x05;
Py_ssize_t result_size, temp_size;
char *result_str, *temp_str;

PyBytes_ConcatAndDel(&result, temp);
return result;
if ((PyBytes_AsStringAndSize(result, &result_str, &result_size) < 0)
|| (PyBytes_AsStringAndSize(temp, &temp_str, &temp_size) < 0))
{
/* LCOV_EXCL_START */
Py_DECREF(result);
Py_DECREF(temp);
return NULL;
/* LCOV_EXCL_STOP */
}
result_str[0] = 0x05;
temp_str[0] = 0x05;

char *buf = PyMem_Malloc(result_size + temp_size);

if (!buf) {
/* LCOV_EXCL_START */
Py_DECREF(result);
Py_DECREF(temp);
return NULL;
/* LCOV_EXCL_STOP */
}
memcpy(buf, result_str, result_size);
memcpy(buf + result_size, temp_str, temp_size);
Py_DECREF(result);
Py_DECREF(temp);

PyObject *ret = PyBytes_FromStringAndSize(buf, result_size + temp_size);

PyMem_Free(buf);
return ret;
}

PyDoc_STRVAR(doc_from_binary,
Expand Down
2 changes: 1 addition & 1 deletion src/gmpy2_cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ GMPy_XMPZ_New(CTXT_Object *context)
if (result == NULL) {
return NULL;
}
mpz_init(result->z);
mpz_init(result->z);
}
return result;
}
Expand Down
54 changes: 54 additions & 0 deletions src/gmpy2_convert_gmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
static int
mpz_set_PyLong(mpz_t z, PyObject *obj)
{
#ifndef PYPY_VERSION
static PyLongExport long_export;

if (PyLong_Export(obj, &long_export) < 0) {
Expand Down Expand Up @@ -77,6 +78,44 @@ mpz_set_PyLong(mpz_t z, PyObject *obj)
}
}
return 0;
#else
int overflow;
long value = PyLong_AsLongAndOverflow(obj, &overflow);
if (!overflow) {
mpz_set_si(z, value);
return 0;
}

PyObject *s = PyNumber_ToBase(obj, 16);

if (!s) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}

const char *str = PyUnicode_AsUTF8(s), *p = str;

if (!str) {
/* LCOV_EXCL_START */
Py_DECREF(s);
return -1;
/* LCOV_EXCL_STOP */
}

int negative = (str[0] == '-');

p += 2;
if (negative) {
p++;
}
mpz_init_set_str(z, p, 16);
Py_DECREF(s);
if (negative) {
mpz_neg(z, z);
}
return 0;
#endif
}

static MPZ_Object *
Expand Down Expand Up @@ -148,6 +187,7 @@ GMPy_PyLong_From_MPZ(MPZ_Object *obj, CTXT_Object *context)
return PyLong_FromLong(mpz_get_si(obj->z));
}

#ifndef PYPY_VERSION
size_t size = (mpz_sizeinbase(obj->z, 2) +
int_bits_per_digit - 1) / int_bits_per_digit;
void *digits;
Expand All @@ -163,6 +203,20 @@ GMPy_PyLong_From_MPZ(MPZ_Object *obj, CTXT_Object *context)
int_endianness, int_nails, obj->z);

return PyLongWriter_Finish(writer);
#else
PyObject *str = GMPy_PyStr_From_MPZ(obj, 16, 0, NULL);

if (!str) {
/* LCOV_EXCL_START */
return NULL;
/* LCOV_EXCL_STOP */
}

PyObject *res = PyLong_FromUnicodeObject(str, 16);

Py_DECREF(str);
return res;
#endif
}

static PyObject *
Expand Down
2 changes: 1 addition & 1 deletion src/gmpy2_xmpz.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ static PyGetSetDef GMPy_XMPZ_getseters[] =
"the denominator of a rational number in lowest terms", NULL },
{ "real", (getter)GMPy_XMPZ_Attrib_GetReal, NULL,
"the real part of a complex number", NULL },
{ "denominator", (getter)GMPy_XMPZ_Attrib_GetImag, NULL,
{ "imag", (getter)GMPy_XMPZ_Attrib_GetImag, NULL,
"the imaginary part of a complex number", NULL },
{NULL}
};
Expand Down
5 changes: 5 additions & 0 deletions test/test_misc.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import platform
import sys

import pytest

import gmpy2


Expand All @@ -17,6 +20,8 @@ def test_misc():
'under LGPL 3 or later.')


@pytest.mark.skipif(platform.python_implementation() == "PyPy",
reason="sys.getsizeof raises TypeError")
def test_sizeof():
assert sys.getsizeof(gmpy2.mpz(10)) > 0
assert sys.getsizeof(gmpy2.mpfr('1.0')) > 0

0 comments on commit 532970f

Please sign in to comment.