Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support PyPy #544

Merged
merged 4 commits into from
Jan 6, 2025
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
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
Loading