summaryrefslogtreecommitdiff
path: root/Cython/Compiler/TypeSlots.py
diff options
context:
space:
mode:
Diffstat (limited to 'Cython/Compiler/TypeSlots.py')
-rw-r--r--Cython/Compiler/TypeSlots.py742
1 files changed, 490 insertions, 252 deletions
diff --git a/Cython/Compiler/TypeSlots.py b/Cython/Compiler/TypeSlots.py
index 0ef17ede6..2c891f648 100644
--- a/Cython/Compiler/TypeSlots.py
+++ b/Cython/Compiler/TypeSlots.py
@@ -9,6 +9,8 @@ from . import Naming
from . import PyrexTypes
from .Errors import error
+import copy
+
invisible = ['__cinit__', '__dealloc__', '__richcmp__',
'__nonzero__', '__bool__']
@@ -23,6 +25,7 @@ class Signature(object):
# fixed_arg_format string
# ret_format string
# error_value string
+ # use_fastcall boolean
#
# The formats are strings made up of the following
# characters:
@@ -49,6 +52,7 @@ class Signature(object):
# '*' rest of args passed as generic Python
# arg tuple and kw dict (must be last
# char in format string)
+ # '?' optional object arg (currently for pow only)
format_map = {
'O': PyrexTypes.py_object_type,
@@ -68,6 +72,7 @@ class Signature(object):
'S': PyrexTypes.c_char_ptr_ptr_type,
'r': PyrexTypes.c_returncode_type,
'B': PyrexTypes.c_py_buffer_ptr_type,
+ '?': PyrexTypes.py_object_type
# 'T', '-' and '*' are handled otherwise
# and are not looked up in here
}
@@ -86,20 +91,27 @@ class Signature(object):
'z': "-1",
}
- def __init__(self, arg_format, ret_format):
- self.has_dummy_arg = 0
- self.has_generic_args = 0
+ # Use METH_FASTCALL instead of METH_VARARGS
+ use_fastcall = False
+
+ def __init__(self, arg_format, ret_format, nogil=False):
+ self.has_dummy_arg = False
+ self.has_generic_args = False
+ self.optional_object_arg_count = 0
if arg_format[:1] == '-':
- self.has_dummy_arg = 1
+ self.has_dummy_arg = True
arg_format = arg_format[1:]
if arg_format[-1:] == '*':
- self.has_generic_args = 1
+ self.has_generic_args = True
arg_format = arg_format[:-1]
+ if arg_format[-1:] == '?':
+ self.optional_object_arg_count += 1
self.fixed_arg_format = arg_format
self.ret_format = ret_format
self.error_value = self.error_value_map.get(ret_format, None)
self.exception_check = ret_format != 'r' and self.error_value is not None
self.is_staticmethod = False
+ self.nogil = nogil
def __repr__(self):
return '<Signature[%s(%s%s)]>' % (
@@ -107,7 +119,10 @@ class Signature(object):
', '.join(self.fixed_arg_format),
'*' if self.has_generic_args else '')
- def num_fixed_args(self):
+ def min_num_fixed_args(self):
+ return self.max_num_fixed_args() - self.optional_object_arg_count
+
+ def max_num_fixed_args(self):
return len(self.fixed_arg_format)
def is_self_arg(self, i):
@@ -135,7 +150,7 @@ class Signature(object):
def function_type(self, self_arg_override=None):
# Construct a C function type descriptor for this signature
args = []
- for i in range(self.num_fixed_args()):
+ for i in range(self.max_num_fixed_args()):
if self_arg_override is not None and self.is_self_arg(i):
assert isinstance(self_arg_override, PyrexTypes.CFuncTypeArg)
args.append(self_arg_override)
@@ -149,7 +164,8 @@ class Signature(object):
exc_value = self.exception_value()
return PyrexTypes.CFuncType(
ret_type, args, exception_value=exc_value,
- exception_check=self.exception_check)
+ exception_check=self.exception_check,
+ nogil=self.nogil)
def method_flags(self):
if self.ret_format == "O":
@@ -157,17 +173,50 @@ class Signature(object):
if self.has_dummy_arg:
full_args = "O" + full_args
if full_args in ["O", "T"]:
- if self.has_generic_args:
- return [method_varargs, method_keywords]
- else:
+ if not self.has_generic_args:
return [method_noargs]
+ elif self.use_fastcall:
+ return [method_fastcall, method_keywords]
+ else:
+ return [method_varargs, method_keywords]
elif full_args in ["OO", "TO"] and not self.has_generic_args:
return [method_onearg]
if self.is_staticmethod:
- return [method_varargs, method_keywords]
+ if self.use_fastcall:
+ return [method_fastcall, method_keywords]
+ else:
+ return [method_varargs, method_keywords]
return None
+ def method_function_type(self):
+ # Return the C function type
+ mflags = self.method_flags()
+ kw = "WithKeywords" if (method_keywords in mflags) else ""
+ for m in mflags:
+ if m == method_noargs or m == method_onearg:
+ return "PyCFunction"
+ if m == method_varargs:
+ return "PyCFunction" + kw
+ if m == method_fastcall:
+ return "__Pyx_PyCFunction_FastCall" + kw
+ return None
+
+ def with_fastcall(self):
+ # Return a copy of this Signature with use_fastcall=True
+ sig = copy.copy(self)
+ sig.use_fastcall = True
+ return sig
+
+ @property
+ def fastvar(self):
+ # Used to select variants of functions, one dealing with METH_VARARGS
+ # and one dealing with __Pyx_METH_FASTCALL
+ if self.use_fastcall:
+ return "FASTCALL"
+ else:
+ return "VARARGS"
+
class SlotDescriptor(object):
# Abstract base class for type slot descriptors.
@@ -178,15 +227,26 @@ class SlotDescriptor(object):
# py3 Indicates presence of slot in Python 3
# py2 Indicates presence of slot in Python 2
# ifdef Full #ifdef string that slot is wrapped in. Using this causes py3, py2 and flags to be ignored.)
+ # used_ifdef Full #ifdef string that the slot value is wrapped in (otherwise it is assigned NULL)
+ # Unlike "ifdef" the slot is defined and this just controls if it receives a value
def __init__(self, slot_name, dynamic=False, inherited=False,
- py3=True, py2=True, ifdef=None):
+ py3=True, py2=True, ifdef=None, is_binop=False,
+ used_ifdef=None):
self.slot_name = slot_name
self.is_initialised_dynamically = dynamic
self.is_inherited = inherited
self.ifdef = ifdef
+ self.used_ifdef = used_ifdef
self.py3 = py3
self.py2 = py2
+ self.is_binop = is_binop
+
+ def slot_code(self, scope):
+ raise NotImplementedError()
+
+ def spec_value(self, scope):
+ return self.slot_code(scope)
def preprocessor_guard_code(self):
ifdef = self.ifdef
@@ -194,13 +254,30 @@ class SlotDescriptor(object):
py3 = self.py3
guard = None
if ifdef:
- guard = ("#if %s" % ifdef)
+ guard = "#if %s" % ifdef
elif not py3 or py3 == '<RESERVED>':
- guard = ("#if PY_MAJOR_VERSION < 3")
+ guard = "#if PY_MAJOR_VERSION < 3"
elif not py2:
- guard = ("#if PY_MAJOR_VERSION >= 3")
+ guard = "#if PY_MAJOR_VERSION >= 3"
return guard
+ def generate_spec(self, scope, code):
+ if self.is_initialised_dynamically:
+ return
+ value = self.spec_value(scope)
+ if value == "0":
+ return
+ preprocessor_guard = self.preprocessor_guard_code()
+ if not preprocessor_guard:
+ if self.py3 and self.slot_name.startswith('bf_'):
+ # The buffer protocol requires Limited API 3.11, so check if the spec slots are available.
+ preprocessor_guard = "#if defined(Py_%s)" % self.slot_name
+ if preprocessor_guard:
+ code.putln(preprocessor_guard)
+ code.putln("{Py_%s, (void *)%s}," % (self.slot_name, value))
+ if preprocessor_guard:
+ code.putln("#endif")
+
def generate(self, scope, code):
preprocessor_guard = self.preprocessor_guard_code()
if preprocessor_guard:
@@ -215,7 +292,7 @@ class SlotDescriptor(object):
# PyPy currently has a broken PyType_Ready() that fails to
# inherit some slots. To work around this, we explicitly
# set inherited slots here, but only in PyPy since CPython
- # handles this better than we do.
+ # handles this better than we do (except for buffer slots in type specs).
inherited_value = value
current_scope = scope
while (inherited_value == "0"
@@ -225,12 +302,20 @@ class SlotDescriptor(object):
current_scope = current_scope.parent_type.base_type.scope
inherited_value = self.slot_code(current_scope)
if inherited_value != "0":
- code.putln("#if CYTHON_COMPILING_IN_PYPY")
+ # we always need inherited buffer slots for the type spec
+ is_buffer_slot = int(self.slot_name in ("bf_getbuffer", "bf_releasebuffer"))
+ code.putln("#if CYTHON_COMPILING_IN_PYPY || %d" % is_buffer_slot)
code.putln("%s, /*%s*/" % (inherited_value, self.slot_name))
code.putln("#else")
end_pypy_guard = True
+ if self.used_ifdef:
+ code.putln("#if %s" % self.used_ifdef)
code.putln("%s, /*%s*/" % (value, self.slot_name))
+ if self.used_ifdef:
+ code.putln("#else")
+ code.putln("NULL, /*%s*/" % self.slot_name)
+ code.putln("#endif")
if end_pypy_guard:
code.putln("#endif")
@@ -248,14 +333,20 @@ class SlotDescriptor(object):
def generate_dynamic_init_code(self, scope, code):
if self.is_initialised_dynamically:
- value = self.slot_code(scope)
- if value != "0":
- code.putln("%s.%s = %s;" % (
- scope.parent_type.typeobj_cname,
- self.slot_name,
- value
- )
- )
+ self.generate_set_slot_code(
+ self.slot_code(scope), scope, code)
+
+ def generate_set_slot_code(self, value, scope, code):
+ if value == "0":
+ return
+
+ if scope.parent_type.typeptr_cname:
+ target = "%s->%s" % (scope.parent_type.typeptr_cname, self.slot_name)
+ else:
+ assert scope.parent_type.typeobj_cname
+ target = "%s.%s" % (scope.parent_type.typeobj_cname, self.slot_name)
+
+ code.putln("%s = %s;" % (target, value))
class FixedSlot(SlotDescriptor):
@@ -285,8 +376,8 @@ class MethodSlot(SlotDescriptor):
# method_name string The __xxx__ name of the method
# alternatives [string] Alternative list of __xxx__ names for the method
- def __init__(self, signature, slot_name, method_name, fallback=None,
- py3=True, py2=True, ifdef=None, inherited=True):
+ def __init__(self, signature, slot_name, method_name, method_name_to_slot,
+ fallback=None, py3=True, py2=True, ifdef=None, inherited=True):
SlotDescriptor.__init__(self, slot_name, py3=py3, py2=py2,
ifdef=ifdef, inherited=inherited)
self.signature = signature
@@ -360,30 +451,61 @@ class GCClearReferencesSlot(GCDependentSlot):
class ConstructorSlot(InternalMethodSlot):
# Descriptor for tp_new and tp_dealloc.
- def __init__(self, slot_name, method, **kargs):
+ def __init__(self, slot_name, method=None, **kargs):
InternalMethodSlot.__init__(self, slot_name, **kargs)
self.method = method
- def slot_code(self, scope):
- entry = scope.lookup_here(self.method)
- if (self.slot_name != 'tp_new'
- and scope.parent_type.base_type
+ def _needs_own(self, scope):
+ if (scope.parent_type.base_type
and not scope.has_pyobject_attrs
and not scope.has_memoryview_attrs
- and not scope.has_cpp_class_attrs
- and not (entry and entry.is_special)):
+ and not scope.has_cpp_constructable_attrs
+ and not (self.slot_name == 'tp_new' and scope.parent_type.vtabslot_cname)):
+ entry = scope.lookup_here(self.method) if self.method else None
+ if not (entry and entry.is_special):
+ return False
+ # Unless we can safely delegate to the parent, all types need a tp_new().
+ return True
+
+ def _parent_slot_function(self, scope):
+ parent_type_scope = scope.parent_type.base_type.scope
+ if scope.parent_scope is parent_type_scope.parent_scope:
+ entry = scope.parent_scope.lookup_here(scope.parent_type.base_type.name)
+ if entry.visibility != 'extern':
+ return self.slot_code(parent_type_scope)
+ return None
+
+ def slot_code(self, scope):
+ if not self._needs_own(scope):
# if the type does not have object attributes, it can
# delegate GC methods to its parent - iff the parent
# functions are defined in the same module
- parent_type_scope = scope.parent_type.base_type.scope
- if scope.parent_scope is parent_type_scope.parent_scope:
- entry = scope.parent_scope.lookup_here(scope.parent_type.base_type.name)
- if entry.visibility != 'extern':
- return self.slot_code(parent_type_scope)
- if entry and not entry.is_special:
- return "0"
+ slot_code = self._parent_slot_function(scope)
+ return slot_code or '0'
return InternalMethodSlot.slot_code(self, scope)
+ def spec_value(self, scope):
+ slot_function = self.slot_code(scope)
+ if self.slot_name == "tp_dealloc" and slot_function != scope.mangle_internal("tp_dealloc"):
+ # Not used => inherit from base type.
+ return "0"
+ return slot_function
+
+ def generate_dynamic_init_code(self, scope, code):
+ if self.slot_code(scope) != '0':
+ return
+ # If we don't have our own slot function and don't know the
+ # parent function statically, copy it dynamically.
+ base_type = scope.parent_type.base_type
+ if base_type.typeptr_cname:
+ src = '%s->%s' % (base_type.typeptr_cname, self.slot_name)
+ elif base_type.is_extension_type and base_type.typeobj_cname:
+ src = '%s.%s' % (base_type.typeobj_cname, self.slot_name)
+ else:
+ return
+
+ self.generate_set_slot_code(src, scope, code)
+
class SyntheticSlot(InternalMethodSlot):
# Type slot descriptor for a synthesized method which
@@ -404,6 +526,20 @@ class SyntheticSlot(InternalMethodSlot):
else:
return self.default_value
+ def spec_value(self, scope):
+ return self.slot_code(scope)
+
+
+class BinopSlot(SyntheticSlot):
+ def __init__(self, signature, slot_name, left_method, method_name_to_slot, **kargs):
+ assert left_method.startswith('__')
+ right_method = '__r' + left_method[2:]
+ SyntheticSlot.__init__(
+ self, slot_name, [left_method, right_method], "0", is_binop=True, **kargs)
+ # MethodSlot causes special method registration.
+ self.left_slot = MethodSlot(signature, "", left_method, method_name_to_slot, **kargs)
+ self.right_slot = MethodSlot(signature, "", right_method, method_name_to_slot, **kargs)
+
class RichcmpSlot(MethodSlot):
def slot_code(self, scope):
@@ -432,8 +568,16 @@ class TypeFlagsSlot(SlotDescriptor):
value += "|Py_TPFLAGS_BASETYPE"
if scope.needs_gc():
value += "|Py_TPFLAGS_HAVE_GC"
+ if scope.may_have_finalize():
+ value += "|Py_TPFLAGS_HAVE_FINALIZE"
+ if scope.parent_type.has_sequence_flag:
+ value += "|Py_TPFLAGS_SEQUENCE"
return value
+ def generate_spec(self, scope, code):
+ # Flags are stored in the PyType_Spec, not in a PyType_Slot.
+ return
+
class DocStringSlot(SlotDescriptor):
# Descriptor for the docstring slot.
@@ -444,7 +588,7 @@ class DocStringSlot(SlotDescriptor):
return "0"
if doc.is_unicode:
doc = doc.as_utf8_string()
- return doc.as_c_string_literal()
+ return "PyDoc_STR(%s)" % doc.as_c_string_literal()
class SuiteSlot(SlotDescriptor):
@@ -452,7 +596,7 @@ class SuiteSlot(SlotDescriptor):
#
# sub_slots [SlotDescriptor]
- def __init__(self, sub_slots, slot_type, slot_name, ifdef=None):
+ def __init__(self, sub_slots, slot_type, slot_name, substructures, ifdef=None):
SlotDescriptor.__init__(self, slot_name, ifdef=ifdef)
self.sub_slots = sub_slots
self.slot_type = slot_type
@@ -487,7 +631,9 @@ class SuiteSlot(SlotDescriptor):
if self.ifdef:
code.putln("#endif")
-substructures = [] # List of all SuiteSlot instances
+ def generate_spec(self, scope, code):
+ for slot in self.sub_slots:
+ slot.generate_spec(scope, code)
class MethodTableSlot(SlotDescriptor):
# Slot descriptor for the method table.
@@ -503,8 +649,42 @@ class MemberTableSlot(SlotDescriptor):
# Slot descriptor for the table of Python-accessible attributes.
def slot_code(self, scope):
+ # Only used in specs.
return "0"
+ def get_member_specs(self, scope):
+ return [
+ get_slot_by_name("tp_dictoffset", scope.directives).members_slot_value(scope),
+ #get_slot_by_name("tp_weaklistoffset").spec_value(scope),
+ ]
+
+ def is_empty(self, scope):
+ for member_entry in self.get_member_specs(scope):
+ if member_entry:
+ return False
+ return True
+
+ def substructure_cname(self, scope):
+ return "%s%s_%s" % (Naming.pyrex_prefix, self.slot_name, scope.class_name)
+
+ def generate_substructure_spec(self, scope, code):
+ if self.is_empty(scope):
+ return
+ from .Code import UtilityCode
+ code.globalstate.use_utility_code(UtilityCode.load_cached("IncludeStructmemberH", "ModuleSetupCode.c"))
+
+ code.putln("static struct PyMemberDef %s[] = {" % self.substructure_cname(scope))
+ for member_entry in self.get_member_specs(scope):
+ if member_entry:
+ code.putln(member_entry)
+ code.putln("{NULL, 0, 0, 0, NULL}")
+ code.putln("};")
+
+ def spec_value(self, scope):
+ if self.is_empty(scope):
+ return "0"
+ return self.substructure_cname(scope)
+
class GetSetSlot(SlotDescriptor):
# Slot descriptor for the table of attribute get & set methods.
@@ -520,13 +700,13 @@ class BaseClassSlot(SlotDescriptor):
# Slot descriptor for the base class slot.
def __init__(self, name):
- SlotDescriptor.__init__(self, name, dynamic = 1)
+ SlotDescriptor.__init__(self, name, dynamic=True)
def generate_dynamic_init_code(self, scope, code):
base_type = scope.parent_type.base_type
if base_type:
- code.putln("%s.%s = %s;" % (
- scope.parent_type.typeobj_cname,
+ code.putln("%s->%s = %s;" % (
+ scope.parent_type.typeptr_cname,
self.slot_name,
base_type.typeptr_cname))
@@ -551,10 +731,11 @@ class DictOffsetSlot(SlotDescriptor):
else:
return "0"
-
-# The following dictionary maps __xxx__ method names to slot descriptors.
-
-method_name_to_slot = {}
+ def members_slot_value(self, scope):
+ dict_offset = self.slot_code(scope)
+ if dict_offset == "0":
+ return None
+ return '{"__dictoffset__", T_PYSSIZET, %s, READONLY, NULL},' % dict_offset
## The following slots are (or could be) initialised with an
## extern function pointer.
@@ -569,17 +750,6 @@ method_name_to_slot = {}
#
#------------------------------------------------------------------------------------------
-def get_special_method_signature(name):
- # Given a method name, if it is a special method,
- # return its signature, else return None.
- slot = method_name_to_slot.get(name)
- if slot:
- return slot.signature
- elif name in richcmp_special_methods:
- return ibinaryfunc
- else:
- return None
-
def get_property_accessor_signature(name):
# Return signature of accessor for an extension type
@@ -592,7 +762,7 @@ def get_base_slot_function(scope, slot):
# This is useful for enabling the compiler to optimize calls
# that recursively climb the class hierarchy.
base_type = scope.parent_type.base_type
- if scope.parent_scope is base_type.scope.parent_scope:
+ if base_type and scope.parent_scope is base_type.scope.parent_scope:
parent_slot = slot.slot_code(base_type.scope)
if parent_slot != '0':
entry = scope.parent_scope.lookup_here(scope.parent_type.base_type.name)
@@ -613,16 +783,16 @@ def get_slot_function(scope, slot):
return None
-def get_slot_by_name(slot_name):
+def get_slot_by_name(slot_name, compiler_directives):
# For now, only search the type struct, no referenced sub-structs.
- for slot in slot_table:
+ for slot in get_slot_table(compiler_directives).slot_table:
if slot.slot_name == slot_name:
return slot
assert False, "Slot not found: %s" % slot_name
def get_slot_code_by_name(scope, slot_name):
- slot = get_slot_by_name(slot_name)
+ slot = get_slot_by_name(slot_name, scope.directives)
return slot.slot_code(scope)
def is_reverse_number_slot(name):
@@ -634,8 +804,8 @@ def is_reverse_number_slot(name):
"""
if name.startswith("__r") and name.endswith("__"):
forward_name = name.replace("r", "", 1)
- for meth in PyNumberMethods:
- if getattr(meth, "method_name", None) == forward_name:
+ for meth in get_slot_table(None).PyNumberMethods:
+ if hasattr(meth, "right_slot"):
return True
return False
@@ -668,8 +838,8 @@ pyfunction_onearg = Signature("-O", "O")
unaryfunc = Signature("T", "O") # typedef PyObject * (*unaryfunc)(PyObject *);
binaryfunc = Signature("OO", "O") # typedef PyObject * (*binaryfunc)(PyObject *, PyObject *);
ibinaryfunc = Signature("TO", "O") # typedef PyObject * (*binaryfunc)(PyObject *, PyObject *);
-ternaryfunc = Signature("OOO", "O") # typedef PyObject * (*ternaryfunc)(PyObject *, PyObject *, PyObject *);
-iternaryfunc = Signature("TOO", "O") # typedef PyObject * (*ternaryfunc)(PyObject *, PyObject *, PyObject *);
+powternaryfunc = Signature("OO?", "O") # typedef PyObject * (*ternaryfunc)(PyObject *, PyObject *, PyObject *);
+ipowternaryfunc = Signature("TO?", "O") # typedef PyObject * (*ternaryfunc)(PyObject *, PyObject *, PyObject *);
callfunc = Signature("T*", "O") # typedef PyObject * (*ternaryfunc)(PyObject *, PyObject *, PyObject *);
inquiry = Signature("T", "i") # typedef int (*inquiry)(PyObject *);
lenfunc = Signature("T", "z") # typedef Py_ssize_t (*lenfunc)(PyObject *);
@@ -682,7 +852,7 @@ ssizessizeargfunc = Signature("Tzz", "O") # typedef PyObject *(*ssizessizeargfu
intobjargproc = Signature("TiO", 'r') # typedef int(*intobjargproc)(PyObject *, int, PyObject *);
ssizeobjargproc = Signature("TzO", 'r') # typedef int(*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *);
intintobjargproc = Signature("TiiO", 'r') # typedef int(*intintobjargproc)(PyObject *, int, int, PyObject *);
-ssizessizeobjargproc = Signature("TzzO", 'r') # typedef int(*ssizessizeobjargproc)(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *);
+ssizessizeobjargproc = Signature("TzzO", 'r') # typedef int(*ssizessizeobjargproc)(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *);
intintargproc = Signature("Tii", 'r')
ssizessizeargproc = Signature("Tzz", 'r')
@@ -732,103 +902,8 @@ property_accessor_signatures = {
'__del__': Signature("T", 'r')
}
-#------------------------------------------------------------------------------------------
-#
-# Descriptor tables for the slots of the various type object
-# substructures, in the order they appear in the structure.
-#
-#------------------------------------------------------------------------------------------
-PyNumberMethods_Py3_GUARD = "PY_MAJOR_VERSION < 3 || (CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX < 0x03050000)"
-
-PyNumberMethods = (
- MethodSlot(binaryfunc, "nb_add", "__add__"),
- MethodSlot(binaryfunc, "nb_subtract", "__sub__"),
- MethodSlot(binaryfunc, "nb_multiply", "__mul__"),
- MethodSlot(binaryfunc, "nb_divide", "__div__", ifdef = PyNumberMethods_Py3_GUARD),
- MethodSlot(binaryfunc, "nb_remainder", "__mod__"),
- MethodSlot(binaryfunc, "nb_divmod", "__divmod__"),
- MethodSlot(ternaryfunc, "nb_power", "__pow__"),
- MethodSlot(unaryfunc, "nb_negative", "__neg__"),
- MethodSlot(unaryfunc, "nb_positive", "__pos__"),
- MethodSlot(unaryfunc, "nb_absolute", "__abs__"),
- MethodSlot(inquiry, "nb_nonzero", "__nonzero__", py3 = ("nb_bool", "__bool__")),
- MethodSlot(unaryfunc, "nb_invert", "__invert__"),
- MethodSlot(binaryfunc, "nb_lshift", "__lshift__"),
- MethodSlot(binaryfunc, "nb_rshift", "__rshift__"),
- MethodSlot(binaryfunc, "nb_and", "__and__"),
- MethodSlot(binaryfunc, "nb_xor", "__xor__"),
- MethodSlot(binaryfunc, "nb_or", "__or__"),
- EmptySlot("nb_coerce", ifdef = PyNumberMethods_Py3_GUARD),
- MethodSlot(unaryfunc, "nb_int", "__int__", fallback="__long__"),
- MethodSlot(unaryfunc, "nb_long", "__long__", fallback="__int__", py3 = "<RESERVED>"),
- MethodSlot(unaryfunc, "nb_float", "__float__"),
- MethodSlot(unaryfunc, "nb_oct", "__oct__", ifdef = PyNumberMethods_Py3_GUARD),
- MethodSlot(unaryfunc, "nb_hex", "__hex__", ifdef = PyNumberMethods_Py3_GUARD),
-
- # Added in release 2.0
- MethodSlot(ibinaryfunc, "nb_inplace_add", "__iadd__"),
- MethodSlot(ibinaryfunc, "nb_inplace_subtract", "__isub__"),
- MethodSlot(ibinaryfunc, "nb_inplace_multiply", "__imul__"),
- MethodSlot(ibinaryfunc, "nb_inplace_divide", "__idiv__", ifdef = PyNumberMethods_Py3_GUARD),
- MethodSlot(ibinaryfunc, "nb_inplace_remainder", "__imod__"),
- MethodSlot(ibinaryfunc, "nb_inplace_power", "__ipow__"), # actually ternaryfunc!!!
- MethodSlot(ibinaryfunc, "nb_inplace_lshift", "__ilshift__"),
- MethodSlot(ibinaryfunc, "nb_inplace_rshift", "__irshift__"),
- MethodSlot(ibinaryfunc, "nb_inplace_and", "__iand__"),
- MethodSlot(ibinaryfunc, "nb_inplace_xor", "__ixor__"),
- MethodSlot(ibinaryfunc, "nb_inplace_or", "__ior__"),
-
- # Added in release 2.2
- # The following require the Py_TPFLAGS_HAVE_CLASS flag
- MethodSlot(binaryfunc, "nb_floor_divide", "__floordiv__"),
- MethodSlot(binaryfunc, "nb_true_divide", "__truediv__"),
- MethodSlot(ibinaryfunc, "nb_inplace_floor_divide", "__ifloordiv__"),
- MethodSlot(ibinaryfunc, "nb_inplace_true_divide", "__itruediv__"),
-
- # Added in release 2.5
- MethodSlot(unaryfunc, "nb_index", "__index__"),
-
- # Added in release 3.5
- MethodSlot(binaryfunc, "nb_matrix_multiply", "__matmul__", ifdef="PY_VERSION_HEX >= 0x03050000"),
- MethodSlot(ibinaryfunc, "nb_inplace_matrix_multiply", "__imatmul__", ifdef="PY_VERSION_HEX >= 0x03050000"),
-)
-
-PySequenceMethods = (
- MethodSlot(lenfunc, "sq_length", "__len__"),
- EmptySlot("sq_concat"), # nb_add used instead
- EmptySlot("sq_repeat"), # nb_multiply used instead
- SyntheticSlot("sq_item", ["__getitem__"], "0"), #EmptySlot("sq_item"), # mp_subscript used instead
- MethodSlot(ssizessizeargfunc, "sq_slice", "__getslice__"),
- EmptySlot("sq_ass_item"), # mp_ass_subscript used instead
- SyntheticSlot("sq_ass_slice", ["__setslice__", "__delslice__"], "0"),
- MethodSlot(cmpfunc, "sq_contains", "__contains__"),
- EmptySlot("sq_inplace_concat"), # nb_inplace_add used instead
- EmptySlot("sq_inplace_repeat"), # nb_inplace_multiply used instead
-)
-
-PyMappingMethods = (
- MethodSlot(lenfunc, "mp_length", "__len__"),
- MethodSlot(objargfunc, "mp_subscript", "__getitem__"),
- SyntheticSlot("mp_ass_subscript", ["__setitem__", "__delitem__"], "0"),
-)
-
-PyBufferProcs = (
- MethodSlot(readbufferproc, "bf_getreadbuffer", "__getreadbuffer__", py3 = False),
- MethodSlot(writebufferproc, "bf_getwritebuffer", "__getwritebuffer__", py3 = False),
- MethodSlot(segcountproc, "bf_getsegcount", "__getsegcount__", py3 = False),
- MethodSlot(charbufferproc, "bf_getcharbuffer", "__getcharbuffer__", py3 = False),
-
- MethodSlot(getbufferproc, "bf_getbuffer", "__getbuffer__"),
- MethodSlot(releasebufferproc, "bf_releasebuffer", "__releasebuffer__")
-)
-
-PyAsyncMethods = (
- MethodSlot(unaryfunc, "am_await", "__await__"),
- MethodSlot(unaryfunc, "am_aiter", "__aiter__"),
- MethodSlot(unaryfunc, "am_anext", "__anext__"),
- EmptySlot("am_send", ifdef="PY_VERSION_HEX >= 0x030A00A3"),
-)
+PyNumberMethods_Py2only_GUARD = "PY_MAJOR_VERSION < 3 || (CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX < 0x03050000)"
#------------------------------------------------------------------------------------------
#
@@ -836,100 +911,262 @@ PyAsyncMethods = (
# top-level type slots, beginning with tp_dealloc, in the order they
# appear in the type object.
#
+# It depends on some compiler directives (currently c_api_binop_methods), so the
+# slot tables for each set of compiler directives are generated lazily and put in
+# the _slot_table_dict
+#
#------------------------------------------------------------------------------------------
-slot_table = (
- ConstructorSlot("tp_dealloc", '__dealloc__'),
- EmptySlot("tp_print", ifdef="PY_VERSION_HEX < 0x030800b4"),
- EmptySlot("tp_vectorcall_offset", ifdef="PY_VERSION_HEX >= 0x030800b4"),
- EmptySlot("tp_getattr"),
- EmptySlot("tp_setattr"),
-
- # tp_compare (Py2) / tp_reserved (Py3<3.5) / tp_as_async (Py3.5+) is always used as tp_as_async in Py3
- MethodSlot(cmpfunc, "tp_compare", "__cmp__", ifdef="PY_MAJOR_VERSION < 3"),
- SuiteSlot(PyAsyncMethods, "__Pyx_PyAsyncMethodsStruct", "tp_as_async", ifdef="PY_MAJOR_VERSION >= 3"),
-
- MethodSlot(reprfunc, "tp_repr", "__repr__"),
-
- SuiteSlot(PyNumberMethods, "PyNumberMethods", "tp_as_number"),
- SuiteSlot(PySequenceMethods, "PySequenceMethods", "tp_as_sequence"),
- SuiteSlot(PyMappingMethods, "PyMappingMethods", "tp_as_mapping"),
-
- MethodSlot(hashfunc, "tp_hash", "__hash__", inherited=False), # Py3 checks for __richcmp__
- MethodSlot(callfunc, "tp_call", "__call__"),
- MethodSlot(reprfunc, "tp_str", "__str__"),
-
- SyntheticSlot("tp_getattro", ["__getattr__","__getattribute__"], "0"), #"PyObject_GenericGetAttr"),
- SyntheticSlot("tp_setattro", ["__setattr__", "__delattr__"], "0"), #"PyObject_GenericSetAttr"),
-
- SuiteSlot(PyBufferProcs, "PyBufferProcs", "tp_as_buffer"),
-
- TypeFlagsSlot("tp_flags"),
- DocStringSlot("tp_doc"),
-
- GCDependentSlot("tp_traverse"),
- GCClearReferencesSlot("tp_clear"),
-
- RichcmpSlot(richcmpfunc, "tp_richcompare", "__richcmp__", inherited=False), # Py3 checks for __hash__
-
- EmptySlot("tp_weaklistoffset"),
-
- MethodSlot(getiterfunc, "tp_iter", "__iter__"),
- MethodSlot(iternextfunc, "tp_iternext", "__next__"),
+class SlotTable(object):
+ def __init__(self, old_binops):
+ # The following dictionary maps __xxx__ method names to slot descriptors.
+ method_name_to_slot = {}
+ self._get_slot_by_method_name = method_name_to_slot.get
+ self.substructures = [] # List of all SuiteSlot instances
+
+ bf = binaryfunc if old_binops else ibinaryfunc
+ ptf = powternaryfunc if old_binops else ipowternaryfunc
+
+ # Descriptor tables for the slots of the various type object
+ # substructures, in the order they appear in the structure.
+ self.PyNumberMethods = (
+ BinopSlot(bf, "nb_add", "__add__", method_name_to_slot),
+ BinopSlot(bf, "nb_subtract", "__sub__", method_name_to_slot),
+ BinopSlot(bf, "nb_multiply", "__mul__", method_name_to_slot),
+ BinopSlot(bf, "nb_divide", "__div__", method_name_to_slot,
+ ifdef = PyNumberMethods_Py2only_GUARD),
+ BinopSlot(bf, "nb_remainder", "__mod__", method_name_to_slot),
+ BinopSlot(bf, "nb_divmod", "__divmod__", method_name_to_slot),
+ BinopSlot(ptf, "nb_power", "__pow__", method_name_to_slot),
+ MethodSlot(unaryfunc, "nb_negative", "__neg__", method_name_to_slot),
+ MethodSlot(unaryfunc, "nb_positive", "__pos__", method_name_to_slot),
+ MethodSlot(unaryfunc, "nb_absolute", "__abs__", method_name_to_slot),
+ MethodSlot(inquiry, "nb_bool", "__bool__", method_name_to_slot,
+ py2 = ("nb_nonzero", "__nonzero__")),
+ MethodSlot(unaryfunc, "nb_invert", "__invert__", method_name_to_slot),
+ BinopSlot(bf, "nb_lshift", "__lshift__", method_name_to_slot),
+ BinopSlot(bf, "nb_rshift", "__rshift__", method_name_to_slot),
+ BinopSlot(bf, "nb_and", "__and__", method_name_to_slot),
+ BinopSlot(bf, "nb_xor", "__xor__", method_name_to_slot),
+ BinopSlot(bf, "nb_or", "__or__", method_name_to_slot),
+ EmptySlot("nb_coerce", ifdef = PyNumberMethods_Py2only_GUARD),
+ MethodSlot(unaryfunc, "nb_int", "__int__", method_name_to_slot, fallback="__long__"),
+ MethodSlot(unaryfunc, "nb_long", "__long__", method_name_to_slot,
+ fallback="__int__", py3 = "<RESERVED>"),
+ MethodSlot(unaryfunc, "nb_float", "__float__", method_name_to_slot),
+ MethodSlot(unaryfunc, "nb_oct", "__oct__", method_name_to_slot,
+ ifdef = PyNumberMethods_Py2only_GUARD),
+ MethodSlot(unaryfunc, "nb_hex", "__hex__", method_name_to_slot,
+ ifdef = PyNumberMethods_Py2only_GUARD),
+
+ # Added in release 2.0
+ MethodSlot(ibinaryfunc, "nb_inplace_add", "__iadd__", method_name_to_slot),
+ MethodSlot(ibinaryfunc, "nb_inplace_subtract", "__isub__", method_name_to_slot),
+ MethodSlot(ibinaryfunc, "nb_inplace_multiply", "__imul__", method_name_to_slot),
+ MethodSlot(ibinaryfunc, "nb_inplace_divide", "__idiv__", method_name_to_slot,
+ ifdef = PyNumberMethods_Py2only_GUARD),
+ MethodSlot(ibinaryfunc, "nb_inplace_remainder", "__imod__", method_name_to_slot),
+ MethodSlot(ptf, "nb_inplace_power", "__ipow__", method_name_to_slot),
+ MethodSlot(ibinaryfunc, "nb_inplace_lshift", "__ilshift__", method_name_to_slot),
+ MethodSlot(ibinaryfunc, "nb_inplace_rshift", "__irshift__", method_name_to_slot),
+ MethodSlot(ibinaryfunc, "nb_inplace_and", "__iand__", method_name_to_slot),
+ MethodSlot(ibinaryfunc, "nb_inplace_xor", "__ixor__", method_name_to_slot),
+ MethodSlot(ibinaryfunc, "nb_inplace_or", "__ior__", method_name_to_slot),
+
+ # Added in release 2.2
+ # The following require the Py_TPFLAGS_HAVE_CLASS flag
+ BinopSlot(bf, "nb_floor_divide", "__floordiv__", method_name_to_slot),
+ BinopSlot(bf, "nb_true_divide", "__truediv__", method_name_to_slot),
+ MethodSlot(ibinaryfunc, "nb_inplace_floor_divide", "__ifloordiv__", method_name_to_slot),
+ MethodSlot(ibinaryfunc, "nb_inplace_true_divide", "__itruediv__", method_name_to_slot),
+
+ # Added in release 2.5
+ MethodSlot(unaryfunc, "nb_index", "__index__", method_name_to_slot),
+
+ # Added in release 3.5
+ BinopSlot(bf, "nb_matrix_multiply", "__matmul__", method_name_to_slot,
+ ifdef="PY_VERSION_HEX >= 0x03050000"),
+ MethodSlot(ibinaryfunc, "nb_inplace_matrix_multiply", "__imatmul__", method_name_to_slot,
+ ifdef="PY_VERSION_HEX >= 0x03050000"),
+ )
+
+ self.PySequenceMethods = (
+ MethodSlot(lenfunc, "sq_length", "__len__", method_name_to_slot),
+ EmptySlot("sq_concat"), # nb_add used instead
+ EmptySlot("sq_repeat"), # nb_multiply used instead
+ SyntheticSlot("sq_item", ["__getitem__"], "0"), #EmptySlot("sq_item"), # mp_subscript used instead
+ MethodSlot(ssizessizeargfunc, "sq_slice", "__getslice__", method_name_to_slot),
+ EmptySlot("sq_ass_item"), # mp_ass_subscript used instead
+ SyntheticSlot("sq_ass_slice", ["__setslice__", "__delslice__"], "0"),
+ MethodSlot(cmpfunc, "sq_contains", "__contains__", method_name_to_slot),
+ EmptySlot("sq_inplace_concat"), # nb_inplace_add used instead
+ EmptySlot("sq_inplace_repeat"), # nb_inplace_multiply used instead
+ )
+
+ self.PyMappingMethods = (
+ MethodSlot(lenfunc, "mp_length", "__len__", method_name_to_slot),
+ MethodSlot(objargfunc, "mp_subscript", "__getitem__", method_name_to_slot),
+ SyntheticSlot("mp_ass_subscript", ["__setitem__", "__delitem__"], "0"),
+ )
+
+ self.PyBufferProcs = (
+ MethodSlot(readbufferproc, "bf_getreadbuffer", "__getreadbuffer__", method_name_to_slot,
+ py3 = False),
+ MethodSlot(writebufferproc, "bf_getwritebuffer", "__getwritebuffer__", method_name_to_slot,
+ py3 = False),
+ MethodSlot(segcountproc, "bf_getsegcount", "__getsegcount__", method_name_to_slot,
+ py3 = False),
+ MethodSlot(charbufferproc, "bf_getcharbuffer", "__getcharbuffer__", method_name_to_slot,
+ py3 = False),
+
+ MethodSlot(getbufferproc, "bf_getbuffer", "__getbuffer__", method_name_to_slot),
+ MethodSlot(releasebufferproc, "bf_releasebuffer", "__releasebuffer__", method_name_to_slot)
+ )
+
+ self.PyAsyncMethods = (
+ MethodSlot(unaryfunc, "am_await", "__await__", method_name_to_slot),
+ MethodSlot(unaryfunc, "am_aiter", "__aiter__", method_name_to_slot),
+ MethodSlot(unaryfunc, "am_anext", "__anext__", method_name_to_slot),
+ EmptySlot("am_send", ifdef="PY_VERSION_HEX >= 0x030A00A3"),
+ )
+
+ self.slot_table = (
+ ConstructorSlot("tp_dealloc", '__dealloc__'),
+ EmptySlot("tp_print", ifdef="PY_VERSION_HEX < 0x030800b4"),
+ EmptySlot("tp_vectorcall_offset", ifdef="PY_VERSION_HEX >= 0x030800b4"),
+ EmptySlot("tp_getattr"),
+ EmptySlot("tp_setattr"),
+
+ # tp_compare (Py2) / tp_reserved (Py3<3.5) / tp_as_async (Py3.5+) is always used as tp_as_async in Py3
+ MethodSlot(cmpfunc, "tp_compare", "__cmp__", method_name_to_slot, ifdef="PY_MAJOR_VERSION < 3"),
+ SuiteSlot(self. PyAsyncMethods, "__Pyx_PyAsyncMethodsStruct", "tp_as_async",
+ self.substructures, ifdef="PY_MAJOR_VERSION >= 3"),
+
+ MethodSlot(reprfunc, "tp_repr", "__repr__", method_name_to_slot),
+
+ SuiteSlot(self.PyNumberMethods, "PyNumberMethods", "tp_as_number", self.substructures),
+ SuiteSlot(self.PySequenceMethods, "PySequenceMethods", "tp_as_sequence", self.substructures),
+ SuiteSlot(self.PyMappingMethods, "PyMappingMethods", "tp_as_mapping", self.substructures),
+
+ MethodSlot(hashfunc, "tp_hash", "__hash__", method_name_to_slot,
+ inherited=False), # Py3 checks for __richcmp__
+ MethodSlot(callfunc, "tp_call", "__call__", method_name_to_slot),
+ MethodSlot(reprfunc, "tp_str", "__str__", method_name_to_slot),
+
+ SyntheticSlot("tp_getattro", ["__getattr__","__getattribute__"], "0"), #"PyObject_GenericGetAttr"),
+ SyntheticSlot("tp_setattro", ["__setattr__", "__delattr__"], "0"), #"PyObject_GenericSetAttr"),
+
+ SuiteSlot(self.PyBufferProcs, "PyBufferProcs", "tp_as_buffer", self.substructures),
+
+ TypeFlagsSlot("tp_flags"),
+ DocStringSlot("tp_doc"),
+
+ GCDependentSlot("tp_traverse"),
+ GCClearReferencesSlot("tp_clear"),
+
+ RichcmpSlot(richcmpfunc, "tp_richcompare", "__richcmp__", method_name_to_slot,
+ inherited=False), # Py3 checks for __hash__
+
+ EmptySlot("tp_weaklistoffset"),
+
+ MethodSlot(getiterfunc, "tp_iter", "__iter__", method_name_to_slot),
+ MethodSlot(iternextfunc, "tp_iternext", "__next__", method_name_to_slot),
+
+ MethodTableSlot("tp_methods"),
+ MemberTableSlot("tp_members"),
+ GetSetSlot("tp_getset"),
+
+ BaseClassSlot("tp_base"), #EmptySlot("tp_base"),
+ EmptySlot("tp_dict"),
+
+ SyntheticSlot("tp_descr_get", ["__get__"], "0"),
+ SyntheticSlot("tp_descr_set", ["__set__", "__delete__"], "0"),
+
+ DictOffsetSlot("tp_dictoffset", ifdef="!CYTHON_USE_TYPE_SPECS"), # otherwise set via "__dictoffset__" member
+
+ MethodSlot(initproc, "tp_init", "__init__", method_name_to_slot),
+ EmptySlot("tp_alloc"), #FixedSlot("tp_alloc", "PyType_GenericAlloc"),
+ ConstructorSlot("tp_new", "__cinit__"),
+ EmptySlot("tp_free"),
+
+ EmptySlot("tp_is_gc"),
+ EmptySlot("tp_bases"),
+ EmptySlot("tp_mro"),
+ EmptySlot("tp_cache"),
+ EmptySlot("tp_subclasses"),
+ EmptySlot("tp_weaklist"),
+ EmptySlot("tp_del"),
+ EmptySlot("tp_version_tag"),
+ SyntheticSlot("tp_finalize", ["__del__"], "0", ifdef="PY_VERSION_HEX >= 0x030400a1",
+ used_ifdef="CYTHON_USE_TP_FINALIZE"),
+ EmptySlot("tp_vectorcall", ifdef="PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)"),
+ EmptySlot("tp_print", ifdef="__PYX_NEED_TP_PRINT_SLOT == 1"),
+ EmptySlot("tp_watched", ifdef="PY_VERSION_HEX >= 0x030C0000"),
+ # PyPy specific extension - only here to avoid C compiler warnings.
+ EmptySlot("tp_pypy_flags", ifdef="CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000"),
+ )
+
+ #------------------------------------------------------------------------------------------
+ #
+ # Descriptors for special methods which don't appear directly
+ # in the type object or its substructures. These methods are
+ # called from slot functions synthesized by Cython.
+ #
+ #------------------------------------------------------------------------------------------
+
+ MethodSlot(initproc, "", "__cinit__", method_name_to_slot)
+ MethodSlot(destructor, "", "__dealloc__", method_name_to_slot)
+ MethodSlot(destructor, "", "__del__", method_name_to_slot)
+ MethodSlot(objobjargproc, "", "__setitem__", method_name_to_slot)
+ MethodSlot(objargproc, "", "__delitem__", method_name_to_slot)
+ MethodSlot(ssizessizeobjargproc, "", "__setslice__", method_name_to_slot)
+ MethodSlot(ssizessizeargproc, "", "__delslice__", method_name_to_slot)
+ MethodSlot(getattrofunc, "", "__getattr__", method_name_to_slot)
+ MethodSlot(getattrofunc, "", "__getattribute__", method_name_to_slot)
+ MethodSlot(setattrofunc, "", "__setattr__", method_name_to_slot)
+ MethodSlot(delattrofunc, "", "__delattr__", method_name_to_slot)
+ MethodSlot(descrgetfunc, "", "__get__", method_name_to_slot)
+ MethodSlot(descrsetfunc, "", "__set__", method_name_to_slot)
+ MethodSlot(descrdelfunc, "", "__delete__", method_name_to_slot)
+
+ def get_special_method_signature(self, name):
+ # Given a method name, if it is a special method,
+ # return its signature, else return None.
+ slot = self._get_slot_by_method_name(name)
+ if slot:
+ return slot.signature
+ elif name in richcmp_special_methods:
+ return ibinaryfunc
+ else:
+ return None
- MethodTableSlot("tp_methods"),
- MemberTableSlot("tp_members"),
- GetSetSlot("tp_getset"),
+ def get_slot_by_method_name(self, method_name):
+ # For now, only search the type struct, no referenced sub-structs.
+ return self._get_slot_by_method_name(method_name)
- BaseClassSlot("tp_base"), #EmptySlot("tp_base"),
- EmptySlot("tp_dict"),
+ def __iter__(self):
+ # make it easier to iterate over all the slots
+ return iter(self.slot_table)
- SyntheticSlot("tp_descr_get", ["__get__"], "0"),
- SyntheticSlot("tp_descr_set", ["__set__", "__delete__"], "0"),
- DictOffsetSlot("tp_dictoffset"),
+_slot_table_dict = {}
- MethodSlot(initproc, "tp_init", "__init__"),
- EmptySlot("tp_alloc"), #FixedSlot("tp_alloc", "PyType_GenericAlloc"),
- InternalMethodSlot("tp_new"),
- EmptySlot("tp_free"),
+def get_slot_table(compiler_directives):
+ if not compiler_directives:
+ # fetch default directives here since the builtin type classes don't have
+ # directives set
+ from .Options import get_directive_defaults
+ compiler_directives = get_directive_defaults()
- EmptySlot("tp_is_gc"),
- EmptySlot("tp_bases"),
- EmptySlot("tp_mro"),
- EmptySlot("tp_cache"),
- EmptySlot("tp_subclasses"),
- EmptySlot("tp_weaklist"),
- EmptySlot("tp_del"),
- EmptySlot("tp_version_tag"),
- EmptySlot("tp_finalize", ifdef="PY_VERSION_HEX >= 0x030400a1"),
- EmptySlot("tp_vectorcall", ifdef="PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)"),
- EmptySlot("tp_print", ifdef="PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000"),
- # PyPy specific extension - only here to avoid C compiler warnings.
- EmptySlot("tp_pypy_flags", ifdef="CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000"),
-)
+ old_binops = compiler_directives['c_api_binop_methods']
+ key = (old_binops,)
+ if key not in _slot_table_dict:
+ _slot_table_dict[key] = SlotTable(old_binops=old_binops)
+ return _slot_table_dict[key]
-#------------------------------------------------------------------------------------------
-#
-# Descriptors for special methods which don't appear directly
-# in the type object or its substructures. These methods are
-# called from slot functions synthesized by Cython.
-#
-#------------------------------------------------------------------------------------------
-MethodSlot(initproc, "", "__cinit__")
-MethodSlot(destructor, "", "__dealloc__")
-MethodSlot(objobjargproc, "", "__setitem__")
-MethodSlot(objargproc, "", "__delitem__")
-MethodSlot(ssizessizeobjargproc, "", "__setslice__")
-MethodSlot(ssizessizeargproc, "", "__delslice__")
-MethodSlot(getattrofunc, "", "__getattr__")
-MethodSlot(getattrofunc, "", "__getattribute__")
-MethodSlot(setattrofunc, "", "__setattr__")
-MethodSlot(delattrofunc, "", "__delattr__")
-MethodSlot(descrgetfunc, "", "__get__")
-MethodSlot(descrsetfunc, "", "__set__")
-MethodSlot(descrdelfunc, "", "__delete__")
+# Populate "special_method_names" based on the default directives (so it can always be accessed quickly).
+special_method_names = set(get_slot_table(compiler_directives=None))
# Method flags for python-exposed methods.
@@ -937,5 +1174,6 @@ MethodSlot(descrdelfunc, "", "__delete__")
method_noargs = "METH_NOARGS"
method_onearg = "METH_O"
method_varargs = "METH_VARARGS"
+method_fastcall = "__Pyx_METH_FASTCALL" # Actually VARARGS on versions < 3.7
method_keywords = "METH_KEYWORDS"
method_coexist = "METH_COEXIST"