diff -up src/sage/cpython/cython_metaclass.h.orig src/sage/cpython/cython_metaclass.h --- src/sage/cpython/cython_metaclass.h.orig 2022-09-19 16:38:18.000000000 -0600 +++ src/sage/cpython/cython_metaclass.h 2023-01-15 19:52:42.818659170 -0700 @@ -66,7 +66,7 @@ static CYTHON_INLINE int Sage_PyType_Rea } /* Now, set t.__class__ to metaclass */ - Py_TYPE(t) = metaclass; + Py_SET_TYPE(t, metaclass); PyType_Modified(t); } else diff -up src/sage/cpython/dict_del_by_value.pyx.orig src/sage/cpython/dict_del_by_value.pyx --- src/sage/cpython/dict_del_by_value.pyx.orig 2022-09-19 16:38:18.000000000 -0600 +++ src/sage/cpython/dict_del_by_value.pyx 2023-01-15 19:52:42.819659153 -0700 @@ -25,7 +25,7 @@ from weakref import KeyedRef from cpython.list cimport PyList_New from cpython cimport Py_XINCREF, Py_XDECREF -from libc.stdint cimport int8_t, int16_t, int32_t, int64_t +from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, uint8_t, uint32_t cdef extern from "Python.h": ctypedef struct PyDictKeysObject @@ -45,8 +45,6 @@ cdef extern from "Python.h": #(this file is not exported from CPython, so we need to be #careful the definitions are in step with what happens there. -ctypedef void* dict_lookup_func # Precise definition not needed - ctypedef union IndexBlock: int8_t as_1[8] int16_t as_2[4] @@ -55,8 +53,10 @@ ctypedef union IndexBlock: ctypedef struct MyPyDictKeysObject: Py_ssize_t dk_refcnt - Py_ssize_t dk_size - dict_lookup_func dk_lookup + uint8_t dk_log2_size + uint8_t dk_log2_index_bytes + uint8_t dk_kind + uint32_t dk_version Py_ssize_t dk_usable Py_ssize_t dk_nentries IndexBlock dk_indices @@ -66,46 +66,65 @@ ctypedef struct PyDictKeyEntry: PyObject * me_key PyObject * me_value +ctypedef struct PyDictUnicodeEntry: + PyObject * me_key + PyObject * me_value + cdef Py_ssize_t DKIX_EMPTY = -1 cdef Py_ssize_t DKIX_DUMMY = -2 cdef Py_ssize_t DKIX_ERROR = -3 +cdef Py_ssize_t DKIX_KEY_CHANGED = -4 + +ctypedef enum DictKeysKind: + DICT_KEYS_GENERAL = 0, + DICT_KEYS_UNICODE = 1, + DICT_KEYS_SPLIT = 2 ##### #These routines are copied from CPython's Object/dictobject.c #in order to access PyDictKeysObject fields +cdef inline Py_ssize_t DK_SIZE(MyPyDictKeysObject *keys): + return 1L << keys.dk_log2_size + cdef inline int DK_IXSIZE(MyPyDictKeysObject *keys): - cdef Py_ssize_t s = keys.dk_size - if s <= 0xff: + cdef uint8_t s = keys.dk_log2_size + if s <= 7: return 1 - elif s <= 0xffff: + if s <= 15: return 2 - elif s <= 0xffffffff: + if sizeof(void *) <= 4 or s <= 31: return 4 else: return 8 cdef inline PyDictKeyEntry * DK_ENTRIES(MyPyDictKeysObject *keys): - return &(keys.dk_indices.as_1[keys.dk_size * DK_IXSIZE(keys)]) + return &(keys.dk_indices.as_1[1UL << keys.dk_log2_index_bytes]) + +cdef inline PyDictUnicodeEntry * DK_UNICODE_ENTRIES(MyPyDictKeysObject *keys): + return &(keys.dk_indices.as_1[1UL << keys.dk_log2_index_bytes]) + +cdef inline bint DK_IS_UNICODE(MyPyDictKeysObject *keys): + return keys.dk_kind != DICT_KEYS_GENERAL cdef inline Py_ssize_t dk_get_index(MyPyDictKeysObject *keys, Py_ssize_t i): - cdef Py_ssize_t s = keys.dk_size - if s <= 0xff: + cdef uint8_t s = keys.dk_log2_size + if s < 8: return keys.dk_indices.as_1[i] - elif s <= 0xffff: + elif s < 16: return keys.dk_indices.as_2[i] - elif s <= 0xffffffff: + elif sizeof(void *) <= 4 or s < 32: return keys.dk_indices.as_4[i] else: return keys.dk_indices.as_8[i] cdef inline void dk_set_index(MyPyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix): - cdef Py_ssize_t s = keys.dk_size - if s <= 0xff: + cdef uint8_t s = keys.dk_log2_size + if s < 8: keys.dk_indices.as_1[i] = ix - elif s <= 0xffff: + elif s < 16: keys.dk_indices.as_2[i] = ix - elif s <= 0xffffffff: + elif sizeof(void *) <= 4 or s < 32: keys.dk_indices.as_4[i] = ix else: keys.dk_indices.as_8[i] = ix @@ -113,22 +132,7 @@ cdef inline void dk_set_index(MyPyDictKe #End of replication of Object/dictobject.c ###### -cdef dict_lookup_func lookdict - -cdef dict_lookup_func DK_LOOKUP(PyDictObject *mp): - return ((mp.ma_keys)).dk_lookup - -def init_lookdict(): - global lookdict - # A dict which a non-string key uses the generic "lookdict" - # as lookup function - cdef object D = {} - D[0] = 0 - lookdict = DK_LOOKUP(D) - -init_lookdict() - -cdef int del_dictitem_by_exact_value(PyDictObject *mp, PyObject *value, Py_hash_t hash) except -1: +cdef int del_dictitem_by_exact_value(PyDictObject *mp, PyObject *value, Py_hash_t hashval) except -1: """ This is used in callbacks for the weak values of :class:`WeakValueDictionary`. @@ -179,47 +183,72 @@ cdef int del_dictitem_by_exact_value(PyD """ keys = (mp.ma_keys) cdef size_t perturb - cdef size_t mask = keys.dk_size-1 - cdef PyDictKeyEntry *entries = DK_ENTRIES(keys) + cdef size_t mask = DK_SIZE(keys)-1 + cdef PyDictUnicodeEntry *uc_entries + cdef PyDictUnicodeEntry *uc_ep + cdef PyDictKeyEntry *entries cdef PyDictKeyEntry *ep - + if mp.ma_values is not NULL: raise TypeError("del_dictitem_by_exact_value cannot be applied to a shared key dict") - cdef size_t i = hash & mask + cdef size_t i = hashval & mask ix = dk_get_index(keys, i) if ix == DKIX_EMPTY: # key not found return 0 - ep = &(entries[ix]) - perturb = hash - while (ep.me_value != value or ep.me_hash != hash): - perturb = perturb >> 5 #this is the value of PERTURB_SHIFT - i = mask & (i * 5 + perturb + 1) - ix = dk_get_index(keys, i) - if ix == DKIX_EMPTY: - # key not found - return 0 - ep = &(entries[ix]) + if DK_IS_UNICODE(keys): + uc_entries = DK_UNICODE_ENTRIES(keys) + uc_ep = &(uc_entries[ix]) + perturb = hashval + while (uc_ep.me_value != value or hash(uc_ep.me_key) != hashval): + perturb = perturb >> 5 #this is the value of PERTURB_SHIFT + i = mask & (i * 5 + perturb + 1) + ix = dk_get_index(keys, i) + if ix == DKIX_EMPTY: + # key not found + return 0 + uc_ep = &(uc_entries[ix]) - # We need the lookup function to be the generic lookdict, otherwise - # deletions may not work correctly - keys.dk_lookup = lookdict + T = PyList_New(2) + PyList_SetItem(T, 0, uc_ep.me_key) + PyList_SetItem(T, 1, uc_ep.me_value) + uc_ep.me_key = NULL + uc_ep.me_value = NULL + mp.ma_used -= 1 + dk_set_index(keys, i, DKIX_DUMMY) + #We have transferred the to-be-deleted references to the list T + #we now delete the list so that the actual decref happens through a + #deallocation routine that uses the Python Trashcan macros to + #avoid stack overflow in deleting deep structures. + del T + else: + entries = DK_ENTRIES(keys) + ep = &(entries[ix]) + perturb = hashval + while (ep.me_value != value or ep.me_hash != hashval): + perturb = perturb >> 5 #this is the value of PERTURB_SHIFT + i = mask & (i * 5 + perturb + 1) + ix = dk_get_index(keys, i) + if ix == DKIX_EMPTY: + # key not found + return 0 + ep = &(entries[ix]) - T = PyList_New(2) - PyList_SetItem(T, 0, ep.me_key) - PyList_SetItem(T, 1, ep.me_value) - ep.me_key = NULL - ep.me_value = NULL - mp.ma_used -= 1 - dk_set_index(keys, i, DKIX_DUMMY) - #We have transferred the to-be-deleted references to the list T - #we now delete the list so that the actual decref happens through a - #deallocation routine that uses the Python Trashcan macros to - #avoid stack overflow in deleting deep structures. - del T + T = PyList_New(2) + PyList_SetItem(T, 0, ep.me_key) + PyList_SetItem(T, 1, ep.me_value) + ep.me_key = NULL + ep.me_value = NULL + mp.ma_used -= 1 + dk_set_index(keys, i, DKIX_DUMMY) + #We have transferred the to-be-deleted references to the list T + #we now delete the list so that the actual decref happens through a + #deallocation routine that uses the Python Trashcan macros to + #avoid stack overflow in deleting deep structures. + del T def test_del_dictitem_by_exact_value(D, value, h): """ diff -up src/sage/libs/gmp/pylong.pyx.orig src/sage/libs/gmp/pylong.pyx --- src/sage/libs/gmp/pylong.pyx.orig 2022-09-19 16:38:18.000000000 -0600 +++ src/sage/libs/gmp/pylong.pyx 2023-01-15 19:52:42.819659153 -0700 @@ -33,6 +33,7 @@ from .mpz cimport * cdef extern from *: Py_ssize_t* Py_SIZE_PTR "&Py_SIZE"(object) + void __Pyx_SET_SIZE(object, Py_ssize_t) int hash_bits """ #ifdef _PyHASH_BITS _PyHASH_BITS /* Python 3 */ @@ -57,10 +58,8 @@ cdef mpz_get_pylong_large(mpz_srcptr z): mpz_export(L.ob_digit, NULL, -1, sizeof(digit), 0, PyLong_nails, z) if mpz_sgn(z) < 0: - # Set correct size (use a pointer to hack around Cython's - # non-support for lvalues). - sizeptr = Py_SIZE_PTR(L) - sizeptr[0] = -pylong_size + # Set correct size + __Pyx_SET_SIZE(L, -pylong_size) return L diff -up src/sage/misc/decorators.py.orig src/sage/misc/decorators.py --- src/sage/misc/decorators.py.orig 2022-09-19 16:38:19.000000000 -0600 +++ src/sage/misc/decorators.py 2023-01-15 19:52:42.819659153 -0700 @@ -31,8 +31,7 @@ from functools import (partial, update_w from copy import copy from sage.misc.sageinspect import (sage_getsource, sage_getsourcelines, - sage_getargspec) -from inspect import ArgSpec + sage_getargspec, ArgSpec) def sage_wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES): diff -up src/sage/misc/sageinspect.py.orig src/sage/misc/sageinspect.py --- src/sage/misc/sageinspect.py.orig 2023-01-15 17:07:04.838843913 -0700 +++ src/sage/misc/sageinspect.py 2023-01-15 19:53:47.194510597 -0700 @@ -119,12 +119,15 @@ import functools import os import tokenize import re +from collections import namedtuple try: import importlib.machinery as import_machinery except ImportError: pass +ArgSpec = namedtuple('ArgSpec', 'args varargs keywords defaults') + def is_function_or_cython_function(obj): """ @@ -359,7 +362,7 @@ def _extract_embedded_signature(docstrin docstring = L[1] if len(L) > 1 else '' # Remove first line, keep the rest def_string = "def " + name + signature + ": pass" try: - return docstring, inspect.ArgSpec(*_sage_getargspec_cython(def_string)) + return docstring, ArgSpec(*_sage_getargspec_cython(def_string)) except SyntaxError: docstring = os.linesep.join(L) return docstring, None @@ -1100,7 +1103,7 @@ def _sage_getargspec_from_ast(source): OUTPUT: - - an instance of :obj:`inspect.ArgSpec`, i.e., a named tuple + - an instance of :obj:`ArgSpec`, i.e., a named tuple EXAMPLES:: @@ -1132,8 +1135,7 @@ def _sage_getargspec_from_ast(source): vararg = getattr(ast_args.vararg, 'arg', None) kwarg = getattr(ast_args.kwarg, 'arg', None) - return inspect.ArgSpec(args, vararg, kwarg, - tuple(defaults) if defaults else None) + return ArgSpec(args, vararg, kwarg, tuple(defaults) if defaults else None) def _sage_getargspec_cython(source): @@ -1149,7 +1151,7 @@ def _sage_getargspec_cython(source): OUTPUT: - - an instance of :obj:`inspect.ArgSpec`, i.e., a named tuple + - an instance of :obj:`ArgSpec`, i.e., a named tuple EXAMPLES:: @@ -1659,11 +1661,11 @@ def sage_getargspec(obj): return sage_getargspec(obj.__call__) if isinstance(obj, (lazy_attribute, AbstractMethod)): source = sage_getsource(obj) - return inspect.ArgSpec(*_sage_getargspec_cython(source)) + return ArgSpec(*_sage_getargspec_cython(source)) if not callable(obj): raise TypeError("obj is not a code object") try: - return inspect.ArgSpec(*obj._sage_argspec_()) + return ArgSpec(*obj._sage_argspec_()) except (AttributeError, TypeError): pass # If we are lucky, the function signature is embedded in the docstring. @@ -1679,7 +1681,7 @@ def sage_getargspec(obj): # Note that this may give a wrong result for the constants! try: args, varargs, varkw = inspect.getargs(obj.__code__) - return inspect.ArgSpec(args, varargs, varkw, obj.__defaults__) + return ArgSpec(args, varargs, varkw, obj.__defaults__) except (TypeError, AttributeError): pass if isclassinstance(obj): @@ -1714,7 +1716,7 @@ def sage_getargspec(obj): except TypeError: # happens for Python builtins source = '' if source: - return inspect.ArgSpec(*_sage_getargspec_cython(source)) + return ArgSpec(*_sage_getargspec_cython(source)) else: func_obj = obj @@ -1727,7 +1729,7 @@ def sage_getargspec(obj): except TypeError: # arg is not a code object # The above "hopefully" was wishful thinking: try: - return inspect.ArgSpec(*_sage_getargspec_cython(sage_getsource(obj))) + return ArgSpec(*_sage_getargspec_cython(sage_getsource(obj))) except TypeError: # This happens for Python builtins # The best we can do is to return a generic argspec args = [] @@ -1737,7 +1739,7 @@ def sage_getargspec(obj): defaults = func_obj.__defaults__ except AttributeError: defaults = None - return inspect.ArgSpec(args, varargs, varkw, defaults) + return ArgSpec(args, varargs, varkw, defaults) def formatannotation(annotation, base_module=None): @@ -1808,19 +1810,7 @@ def sage_formatargspec(args, varargs=Non :func:`sage_getargspec`. Since :func:`sage_getargspec` works for Cython functions while Python's inspect module does not, it makes sense to keep this function for formatting instances of - ``inspect.ArgSpec``. - - EXAMPLES:: - - sage: from sage.misc.sageinspect import sage_formatargspec - sage: from inspect import formatargspec # deprecated in Python 3 - sage: args = ['a', 'b', 'c'] - sage: defaults = [3] - sage: sage_formatargspec(args, defaults=defaults) - '(a, b, c=3)' - sage: import warnings; warnings.simplefilter('ignore') # ignore DeprecationWarning - sage: formatargspec(args, defaults=defaults) == sage_formatargspec(args, defaults=defaults) - True + ``ArgSpec``. """ def formatargandannotation(arg): result = formatarg(arg) diff -up src/sage/symbolic/ginac/numeric.cpp.orig src/sage/symbolic/ginac/numeric.cpp --- src/sage/symbolic/ginac/numeric.cpp.orig 2022-09-19 16:38:19.000000000 -0600 +++ src/sage/symbolic/ginac/numeric.cpp 2023-01-15 19:52:42.820659135 -0700 @@ -52,7 +52,6 @@ #define register #define PY_SSIZE_T_CLEAN #include -#include #include "flint/fmpz.h" #include "flint/fmpz_factor.h"