Skip to content

Commit

Permalink
Resolve the RuntimeWarning from Cython about PyFrameObject changing s…
Browse files Browse the repository at this point in the history
…ize.

By simply not typing it at the Cython level at all. No need since we're directing through functions now.
  • Loading branch information
jamadden committed Oct 7, 2022
1 parent 47feb0b commit 7327657
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 16 deletions.
16 changes: 14 additions & 2 deletions src/gevent/_compat.h
Expand Up @@ -65,15 +65,27 @@ static PyObject* PyFrame_GetCode(PyFrameObject* frame)
straightforward. Still, it is critical that the cython declaration of
this function use ``object`` as its return type.)
*/
static PyObject* Gevent_PyFrame_GetBack(PyFrameObject* frame)
static PyObject* Gevent_PyFrame_GetBack(PyObject* frame)
{
PyObject* back = (PyObject*)PyFrame_GetBack(frame);
PyObject* back = (PyObject*)PyFrame_GetBack((PyFrameObject*)frame);
if (back) {
return back;
}
Py_RETURN_NONE;
}

/* These are just for typing purposes to appease the compiler. */

static int Gevent_PyFrame_GetLineNumber(PyObject* o)
{
return PyFrame_GetLineNumber((PyFrameObject*)o);
}

static PyObject* Gevent_PyFrame_GetCode(PyObject* o)
{
return (PyObject*)PyFrame_GetCode((PyFrameObject*)o);
}

#ifdef __cplusplus
}
#ifdef __clang__
Expand Down
29 changes: 21 additions & 8 deletions src/gevent/_gevent_cgreenlet.pxd
Expand Up @@ -53,17 +53,30 @@ cdef inline void greenlet_init():
_greenlet_imported = True

ctypedef object CodeType
ctypedef object FrameType

cdef extern from "_compat.h":
int PyFrame_GetLineNumber(FrameType frame)
CodeType PyFrame_GetCode(FrameType frame)
int Gevent_PyFrame_GetLineNumber(FrameType frame)
CodeType Gevent_PyFrame_GetCode(FrameType frame)
object Gevent_PyFrame_GetBack(FrameType frame)
ctypedef class types.FrameType [object PyFrameObject]:
pass
# We don't do this:
#
# ctypedef class types.FrameType [object PyFrameObject]:
# pass
#
# to avoid "RuntimeWarning: types.FrameType size changed, may
# indicate binary incompatibility. Expected 56 from C header, got
# 120 from PyObject" on Python 3.11. That makes the functions that
# really require that kind of object not safe and capable of crashing the
# interpreter.
#
# However, as of cython 3.0a11, that results in a failure to compile if
# we have a local variable typed as FrameType, so we can't do that.
#
# Also, it removes a layer of type checking and makes it possible to crash
# the interpreter if you call these functions with something that's not a PyFrameObject.
# Don't do that.

@cython.nonecheck(False)
cdef inline object get_f_back(FrameType frame):
return Gevent_PyFrame_GetBack(frame)

cdef void _init()

Expand All @@ -89,7 +102,7 @@ cdef class _Frame:


@cython.final
@cython.locals(frame=FrameType,
@cython.locals(# frame=FrameType, # See above about why we cannot do this
newest_Frame=_Frame,
newer_Frame=_Frame,
older_Frame=_Frame)
Expand Down
12 changes: 6 additions & 6 deletions src/gevent/greenlet.py
Expand Up @@ -56,9 +56,9 @@
locals()['get_generic_parent'] = lambda s: s.parent

# Frame access
locals()['PyFrame_GetCode'] = lambda frame: frame.f_code
locals()['PyFrame_GetLineNumber'] = lambda frame: frame.f_lineno
locals()['get_f_back'] = lambda frame: frame.f_back
locals()['Gevent_PyFrame_GetCode'] = lambda frame: frame.f_code
locals()['Gevent_PyFrame_GetLineNumber'] = lambda frame: frame.f_lineno
locals()['Gevent_PyFrame_GetBack'] = lambda frame: frame.f_back


if _PYPY:
Expand Down Expand Up @@ -159,15 +159,15 @@ def _extract_stack(limit):
# Arguments are always passed to the constructor as Python objects,
# meaning we wind up boxing the f_lineno just to unbox it if we pass it.
# It's faster to simply assign once the object is created.
older_Frame.f_code = PyFrame_GetCode(frame) # pylint:disable=undefined-variable
older_Frame.f_lineno = PyFrame_GetLineNumber(frame) # pylint:disable=undefined-variable
older_Frame.f_code = Gevent_PyFrame_GetCode(frame) # pylint:disable=undefined-variable
older_Frame.f_lineno = Gevent_PyFrame_GetLineNumber(frame) # pylint:disable=undefined-variable
if newer_Frame is not None:
newer_Frame.f_back = older_Frame
newer_Frame = older_Frame
if newest_Frame is None:
newest_Frame = newer_Frame

frame = get_f_back(frame) # pylint:disable=undefined-variable
frame = Gevent_PyFrame_GetBack(frame) # pylint:disable=undefined-variable

return newest_Frame

Expand Down

0 comments on commit 7327657

Please sign in to comment.