Commit 395cc677 authored by Stefan Behnel's avatar Stefan Behnel

Generate short wrappers for special methods like "__next__()" to adapt their...

Generate short wrappers for special methods like "__next__()" to adapt their signature for the PyCFunction entry in PyMethodDef.
Previously, their cast to a two-argument PyCFunction was incorrect.
See #2363.
parent 259da7c9
......@@ -2179,7 +2179,7 @@ class CCodeWriter(object):
if entry.in_closure:
self.put_giveref('Py_None')
def put_pymethoddef(self, entry, term, allow_skip=True):
def put_pymethoddef(self, entry, term, allow_skip=True, wrapper_code_writer=None):
if entry.is_special or entry.name == '__getattribute__':
if entry.name not in special_py_methods:
if entry.name == '__getattr__' and not self.globalstate.directives['fast_getattr']:
......@@ -2189,29 +2189,38 @@ class CCodeWriter(object):
# that's better than ours.
elif allow_skip:
return
from .TypeSlots import method_coexist
if entry.doc:
doc_code = entry.doc_cname
else:
doc_code = 0
method_flags = entry.signature.method_flags()
if method_flags:
if entry.is_special:
method_flags += [method_coexist]
func_ptr = entry.func_cname
# Add required casts, but try not to shadow real warnings.
cast = '__Pyx_PyCFunctionFast' if 'METH_FASTCALL' in method_flags else 'PyCFunction'
if 'METH_KEYWORDS' in method_flags:
cast += 'WithKeywords'
if cast != 'PyCFunction':
func_ptr = '(void*)(%s)%s' % (cast, func_ptr)
self.putln(
'{"%s", (PyCFunction)%s, %s, %s}%s' % (
entry.name,
func_ptr,
"|".join(method_flags),
doc_code,
term))
if not method_flags:
return
if entry.is_special:
from . import TypeSlots
method_flags += [TypeSlots.method_coexist]
func_ptr = wrapper_code_writer.put_pymethoddef_wrapper(entry) if wrapper_code_writer else entry.func_cname
# Add required casts, but try not to shadow real warnings.
cast = '__Pyx_PyCFunctionFast' if 'METH_FASTCALL' in method_flags else 'PyCFunction'
if 'METH_KEYWORDS' in method_flags:
cast += 'WithKeywords'
if cast != 'PyCFunction':
func_ptr = '(void*)(%s)%s' % (cast, func_ptr)
self.putln(
'{"%s", (PyCFunction)%s, %s, %s}%s' % (
entry.name,
func_ptr,
"|".join(method_flags),
entry.doc_cname if entry.doc else '0',
term))
def put_pymethoddef_wrapper(self, entry):
func_cname = entry.func_cname
if entry.is_special:
method_flags = entry.signature.method_flags()
if method_flags and 'METH_NOARGS' in method_flags:
# Special NOARGS methods really take no arguments besides 'self', but PyCFunction expects one.
func_cname = Naming.method_wrapper_prefix + func_cname
self.putln("static PyObject *%s(PyObject *self, CYTHON_UNUSED PyObject *arg) {return %s(self);}" % (
func_cname, entry.func_cname))
return func_cname
# GIL methods
......
......@@ -2144,18 +2144,24 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if env.is_c_class_scope and not env.pyfunc_entries:
return
binding = env.directives['binding']
code.putln("")
wrapper_code_writer = code.insertion_point()
code.putln(
"static PyMethodDef %s[] = {" % (
env.method_table_cname))
for entry in env.pyfunc_entries:
if not entry.fused_cfunction and not (binding and entry.is_overridable):
code.put_pymethoddef(entry, ",")
code.put_pymethoddef(entry, ",", wrapper_code_writer=wrapper_code_writer)
code.putln(
"{0, 0, 0, 0}")
code.putln(
"};")
if wrapper_code_writer.getvalue():
wrapper_code_writer.putln("")
def generate_dict_getter_function(self, scope, code):
dict_attr = scope.lookup_here("__dict__")
if not dict_attr or not dict_attr.is_variable:
......
......@@ -28,6 +28,7 @@ const_prefix = pyrex_prefix + "k_"
py_const_prefix = pyrex_prefix + "kp_"
label_prefix = pyrex_prefix + "L"
pymethdef_prefix = pyrex_prefix + "mdef_"
method_wrapper_prefix = pyrex_prefix + "specialmethod_"
methtab_prefix = pyrex_prefix + "methods_"
memtab_prefix = pyrex_prefix + "members_"
objstruct_prefix = pyrex_prefix + "obj_"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment