diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9b73933..ab178a3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,7 +19,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - python-version: ['3.10', '3.11'] + python-version: ['3.9', '3.10', '3.11', '3.12'] pari-version: ['pari-2.11.4', 'pari-2.13.0', 'pari-2.15.4'] env: LC_ALL: C diff --git a/autogen/paths.py b/autogen/paths.py index 3e8194f..e879464 100644 --- a/autogen/paths.py +++ b/autogen/paths.py @@ -15,12 +15,13 @@ from __future__ import absolute_import, unicode_literals import os +import shutil + from glob import glob -from distutils.spawn import find_executable -# find_executable() returns None if nothing was found -gppath = find_executable("gp") +gppath = shutil.which("gp") + if gppath is None: # This almost certainly won't work, but we need to put something here prefix = "." diff --git a/cypari2/Py_SET_SIZE.h b/cypari2/Py_SET_SIZE.h deleted file mode 100644 index 5f18ab0..0000000 --- a/cypari2/Py_SET_SIZE.h +++ /dev/null @@ -1,8 +0,0 @@ -#include "Python.h" - -#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION < 9) -// The function Py_SET_SIZE is defined starting with python 3.9. -void Py_SET_SIZE(PyVarObject *o, Py_ssize_t size){ - Py_SIZE(o) = size; -} -#endif diff --git a/cypari2/convert.pyx b/cypari2/convert.pyx index 7c2ed24..8d39c3c 100644 --- a/cypari2/convert.pyx +++ b/cypari2/convert.pyx @@ -54,14 +54,8 @@ from libc.math cimport INFINITY from .paridecl cimport * from .stack cimport new_gen, reset_avma from .string_utils cimport to_string, to_bytes - -cdef extern from *: - ctypedef struct PyLongObject: - digit* ob_digit - -cdef extern from "Py_SET_SIZE.h": - void Py_SET_SIZE(py_long o, Py_ssize_t size) - +from .pycore_long cimport (ob_digit, _PyLong_IsZero, _PyLong_IsNegative, + _PyLong_IsPositive, _PyLong_DigitCount, _PyLong_SetSignAndDigitCount) ######################################################################## # Conversion PARI -> Python @@ -424,7 +418,7 @@ cdef PyLong_FromINT(GEN g): cdef Py_ssize_t sizedigits_final = 0 cdef py_long x = _PyLong_New(sizedigits) - cdef digit* D = x.ob_digit + cdef digit* D = ob_digit(x) cdef digit d cdef ulong w @@ -452,10 +446,7 @@ cdef PyLong_FromINT(GEN g): sizedigits_final = i+1 # Set correct size - if signe(g) > 0: - Py_SET_SIZE(x, sizedigits_final) - else: - Py_SET_SIZE(x, -sizedigits_final) + _PyLong_SetSignAndDigitCount(x, signe(g), sizedigits_final) return x @@ -465,18 +456,18 @@ cdef PyLong_FromINT(GEN g): ######################################################################## cdef GEN PyLong_AS_GEN(py_long x): - cdef const digit* D = x.ob_digit + cdef const digit* D = ob_digit(x) # Size of the input cdef size_t sizedigits cdef long sgn - if Py_SIZE(x) == 0: + if _PyLong_IsZero(x): return gen_0 - elif Py_SIZE(x) > 0: - sizedigits = Py_SIZE(x) + elif _PyLong_IsPositive(x): + sizedigits = _PyLong_DigitCount(x) sgn = evalsigne(1) else: - sizedigits = -Py_SIZE(x) + sizedigits = _PyLong_DigitCount(x) sgn = evalsigne(-1) # Size of the output, in bits and in words diff --git a/cypari2/pycore_long.h b/cypari2/pycore_long.h new file mode 100644 index 0000000..ff1a73d --- /dev/null +++ b/cypari2/pycore_long.h @@ -0,0 +1,98 @@ +#include "Python.h" +#include + +#if PY_VERSION_HEX >= 0x030C00A5 +#define ob_digit(o) (((PyLongObject*)o)->long_value.ob_digit) +#else +#define ob_digit(o) (((PyLongObject*)o)->ob_digit) +#endif + +#if PY_VERSION_HEX >= 0x030C00A7 +// taken from cpython:Include/internal/pycore_long.h @ 3.12 + +/* Long value tag bits: + * 0-1: Sign bits value = (1-sign), ie. negative=2, positive=0, zero=1. + * 2: Reserved for immortality bit + * 3+ Unsigned digit count + */ +#define SIGN_MASK 3 +#define SIGN_ZERO 1 +#define SIGN_NEGATIVE 2 +#define NON_SIZE_BITS 3 + +static inline bool +_PyLong_IsZero(const PyLongObject *op) +{ + return (op->long_value.lv_tag & SIGN_MASK) == SIGN_ZERO; +} + +static inline bool +_PyLong_IsNegative(const PyLongObject *op) +{ + return (op->long_value.lv_tag & SIGN_MASK) == SIGN_NEGATIVE; +} + +static inline bool +_PyLong_IsPositive(const PyLongObject *op) +{ + return (op->long_value.lv_tag & SIGN_MASK) == 0; +} + +static inline Py_ssize_t +_PyLong_DigitCount(const PyLongObject *op) +{ + assert(PyLong_Check(op)); + return op->long_value.lv_tag >> NON_SIZE_BITS; +} + +#define TAG_FROM_SIGN_AND_SIZE(sign, size) ((1 - (sign)) | ((size) << NON_SIZE_BITS)) + +static inline void +_PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size) +{ + assert(size >= 0); + assert(-1 <= sign && sign <= 1); + assert(sign != 0 || size == 0); + op->long_value.lv_tag = TAG_FROM_SIGN_AND_SIZE(sign, (size_t)size); +} + +#else +// fallback for < 3.12 + +static inline bool +_PyLong_IsZero(const PyLongObject *op) +{ + return Py_SIZE(op) == 0; +} + +static inline bool +_PyLong_IsNegative(const PyLongObject *op) +{ + return Py_SIZE(op) < 0; +} + +static inline bool +_PyLong_IsPositive(const PyLongObject *op) +{ + return Py_SIZE(op) > 0; +} + +static inline Py_ssize_t +_PyLong_DigitCount(const PyLongObject *op) +{ + Py_ssize_t size = Py_SIZE(op); + return size < 0 ? -size : size; +} + +static inline void +_PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size) +{ +#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION < 9) +// The function Py_SET_SIZE is defined starting with python 3.9. + Py_SIZE(o) = size; +#else + Py_SET_SIZE(op, sign < 0 ? -size : size); +#endif +} + +#endif diff --git a/cypari2/pycore_long.pxd b/cypari2/pycore_long.pxd new file mode 100644 index 0000000..41de637 --- /dev/null +++ b/cypari2/pycore_long.pxd @@ -0,0 +1,9 @@ +from cpython.longintrepr cimport py_long, digit + +cdef extern from "pycore_long.h": + digit* ob_digit(py_long o) + bint _PyLong_IsZero(py_long o) + bint _PyLong_IsNegative(py_long o) + bint _PyLong_IsPositive(py_long o) + Py_ssize_t _PyLong_DigitCount(py_long o) + void _PyLong_SetSignAndDigitCount(py_long o, int sign, Py_ssize_t size)