In IPython, I can see that a property of a Cython class is a generator by simply defining it and then calling:
%%cython
cdef class SomeCls:
property x:
def __get__(self):
yield 1
The call looks like
SomeCls().x
# prints <generator at 0x102f61ee8>
I am having trouble testing if that property is a generator:
import types
print(isinstance(SomeCls().x, types.GeneratorType))
# prints False
import inspect
print(inspect.isgeneratorfunction(SomeCls.x))
# prints False
How can I determine whether a property of a Cython class is a generator?
Why doesn't the usual way work?
First, as you already probably know, there is no difference between inspect.isgeneratorfunction(...)
and isinstance(..., types.GeneratorType)
- the inspect-module just calls isinstance(..., types.GeneratorType)
.
On the other hand, types.GeneratorType
is defined as
def _g():
yield 1
GeneratorType = type(_g())
CPython uses PyGenObject (here code, here documentation) for generators, there is no fancy logic for the comparison as for some ABC-classes, so the isinstance
will boil down to comparing the C-object types.
However, Cython returns a __pyx_CoroutineObject
for generators (just check the cythonized code to see)
typedef PyObject *(*__pyx_coroutine_body_t)(PyObject *, PyThreadState *, PyObject *);
typedef struct {
PyObject_HEAD
__pyx_coroutine_body_t body;
PyObject *closure;
...
int resume_label;
char is_running;
} __pyx_CoroutineObject;
which has nothing to do with PyGenObject
as far as isinstance
is concerned - it doesn't really care whether generator
is in the name of the type (but for us humans it can be really puzzling, because type(obj)
says "generator").
So you will have to roll out your own version of isgenerator
, which takes also Cython-"generators" into account. There are many ways, for example
%%cython
def _f():
yield 1
CyGeneratorType = type(_f())
def iscygenerator(o):
return isinstance(o, CyGeneratorType)
and now:
import inspect
def isgenerator(o):
return inspect.isgenerator(o) or iscygenerator(o)
isgenerator(SomeCls().x) # True
iscygenerator(SomeCls().x) # True
inspect.isgenerator(SomeCls().x) # False