diff options
-rw-r--r-- | Doc/api/concrete.tex | 31 | ||||
-rw-r--r-- | Include/ceval.h | 1 | ||||
-rw-r--r-- | Include/genobject.h | 33 | ||||
-rw-r--r-- | Makefile.pre.in | 2 | ||||
-rw-r--r-- | Misc/NEWS | 3 | ||||
-rw-r--r-- | Objects/genobject.c | 129 | ||||
-rw-r--r-- | Python/ceval.c | 146 |
7 files changed, 207 insertions, 138 deletions
diff --git a/Doc/api/concrete.tex b/Doc/api/concrete.tex index e602528..62084bd 100644 --- a/Doc/api/concrete.tex +++ b/Doc/api/concrete.tex @@ -2603,3 +2603,34 @@ when accessed. Cell objects are not likely to be useful elsewhere. reference counts are adjusted, and no checks are made for safety; \var{cell} must be non-\NULL{} and must be a cell object. \end{cfuncdesc} + + +\subsection{Generator Objects \label{gen-objects}} + +Generator objects are what Python uses to implement generator iterators. +They are normally created by iterating over a function that yields values, +rather than explicitly calling \cfunction{PyGen_New}. + +\begin{ctypedesc}{PyGenObject} + The C structure used for generator objects. +\end{ctypedesc} + +\begin{cvardesc}{PyTypeObject}{PyGen_Type} + The type object corresponding to generator objects +\end{cvardesc} + +\begin{cfuncdesc}{int}{PyGen_Check}{ob} + Return true if \var{ob} is a generator object; \var{ob} must not be + \NULL. +\end{cfuncdesc} + +\begin{cfuncdesc}{int}{PyGen_CheckExact}{ob} + Return true if \var{ob}'s type is \var{PyGen_Type} + is a generator object; \var{ob} must not be + \NULL. +\end{cfuncdesc} + +\begin{cfuncdesc}{PyObject*}{PyGen_New}{PyFrameObject *frame} + Create and return a new generator object based on the \var{frame} object. + The parameter must not be \NULL. +\end{cfuncdesc} diff --git a/Include/ceval.h b/Include/ceval.h index dc3864b..ae6de3a 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -64,6 +64,7 @@ PyAPI_FUNC(char *) PyEval_GetFuncName(PyObject *); PyAPI_FUNC(char *) PyEval_GetFuncDesc(PyObject *); PyAPI_FUNC(PyObject *) PyEval_GetCallStats(PyObject *); +PyAPI_FUNC(PyObject *) PyEval_EvaluateFrame(PyObject *); /* this used to be handled on a per-thread basis - now just two globals */ PyAPI_DATA(volatile int) _Py_Ticker; diff --git a/Include/genobject.h b/Include/genobject.h new file mode 100644 index 0000000..c9b7c19 --- /dev/null +++ b/Include/genobject.h @@ -0,0 +1,33 @@ + +/* Generator object interface */ + +#ifndef Py_GENOBJECT_H +#define Py_GENOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + PyObject_HEAD + /* The gi_ prefix is intended to remind of generator-iterator. */ + + PyFrameObject *gi_frame; + + /* True if generator is being executed. */ + int gi_running; + + /* List of weak reference. */ + PyObject *gi_weakreflist; +} PyGenObject; + +PyAPI_DATA(PyTypeObject) PyGen_Type; + +#define PyGen_Check(op) PyObject_TypeCheck(op, &PyGen_Type) +#define PyGen_CheckExact(op) ((op)->ob_type == &PyGen_Type) + +PyAPI_FUNC(PyObject *) PyGen_New(PyFrameObject *); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_GENOBJECT_H */ diff --git a/Makefile.pre.in b/Makefile.pre.in index 9a50039..074fba4 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -261,6 +261,7 @@ OBJECT_OBJS= \ Objects/complexobject.o \ Objects/descrobject.o \ Objects/enumobject.o \ + Objects/genobject.o \ Objects/fileobject.o \ Objects/floatobject.o \ Objects/frameobject.o \ @@ -478,6 +479,7 @@ PYTHON_HEADERS= \ Include/descrobject.h \ Include/dictobject.h \ Include/enumobject.h \ + Include/genobject.h \ Include/fileobject.h \ Include/floatobject.h \ Include/funcobject.h \ @@ -497,6 +497,9 @@ Build C API ----- +- New public functions PyEval_EvaluateFrame and PyGen_New to expose + generator objects. + - New public functions Py_IncRef() and Py_DecRef(), exposing the functionality of the Py_XINCREF() and Py_XDECREF macros. Useful for runtime dynamic embedding of Python. See patch #938302, by Bob diff --git a/Objects/genobject.c b/Objects/genobject.c new file mode 100644 index 0000000..0f7373a --- /dev/null +++ b/Objects/genobject.c @@ -0,0 +1,129 @@ +/* Generator object implementation */ + +#include "Python.h" +#include "frameobject.h" +#include "genobject.h" +#include "ceval.h" +#include "structmember.h" + +static int +gen_traverse(PyGenObject *gen, visitproc visit, void *arg) +{ + return visit((PyObject *)gen->gi_frame, arg); +} + +static void +gen_dealloc(PyGenObject *gen) +{ + _PyObject_GC_UNTRACK(gen); + if (gen->gi_weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *) gen); + Py_DECREF(gen->gi_frame); + PyObject_GC_Del(gen); +} + +static PyObject * +gen_iternext(PyGenObject *gen) +{ + PyThreadState *tstate = PyThreadState_GET(); + PyFrameObject *f = gen->gi_frame; + PyObject *result; + + if (gen->gi_running) { + PyErr_SetString(PyExc_ValueError, + "generator already executing"); + return NULL; + } + if (f->f_stacktop == NULL) + return NULL; + + /* Generators always return to their most recent caller, not + * necessarily their creator. */ + Py_XINCREF(tstate->frame); + assert(f->f_back == NULL); + f->f_back = tstate->frame; + + gen->gi_running = 1; + result = PyEval_EvaluateFrame((PyObject *)f); + gen->gi_running = 0; + + /* Don't keep the reference to f_back any longer than necessary. It + * may keep a chain of frames alive or it could create a reference + * cycle. */ + Py_XDECREF(f->f_back); + f->f_back = NULL; + + /* If the generator just returned (as opposed to yielding), signal + * that the generator is exhausted. */ + if (result == Py_None && f->f_stacktop == NULL) { + Py_DECREF(result); + result = NULL; + } + + return result; +} + +static PyObject * +gen_getiter(PyObject *gen) +{ + Py_INCREF(gen); + return gen; +} + +static PyMemberDef gen_memberlist[] = { + {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), RO}, + {"gi_running", T_INT, offsetof(PyGenObject, gi_running), RO}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyGen_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "generator", /* tp_name */ + sizeof(PyGenObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)gen_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + 0, /* tp_doc */ + (traverseproc)gen_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + offsetof(PyGenObject, gi_weakreflist), /* tp_weaklistoffset */ + (getiterfunc)gen_getiter, /* tp_iter */ + (iternextfunc)gen_iternext, /* tp_iternext */ + 0, /* tp_methods */ + gen_memberlist, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ +}; + +PyObject * +PyGen_New(PyFrameObject *f) +{ + PyGenObject *gen = PyObject_GC_New(PyGenObject, &PyGen_Type); + if (gen == NULL) { + Py_DECREF(f); + return NULL; + } + gen->gi_frame = f; + gen->gi_running = 0; + gen->gi_weakreflist = NULL; + _PyObject_GC_TRACK(gen); + return (PyObject *)gen; +} diff --git a/Python/ceval.c b/Python/ceval.c index c103f14..2cebf44 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -10,6 +10,7 @@ #include "compile.h" #include "frameobject.h" +#include "genobject.h" #include "eval.h" #include "opcode.h" #include "structmember.h" @@ -139,143 +140,6 @@ PyEval_GetCallStats(PyObject *self) } #endif -static PyTypeObject gentype; - -typedef struct { - PyObject_HEAD - /* The gi_ prefix is intended to remind of generator-iterator. */ - - PyFrameObject *gi_frame; - - /* True if generator is being executed. */ - int gi_running; - - /* List of weak reference. */ - PyObject *gi_weakreflist; -} genobject; - -static PyObject * -gen_new(PyFrameObject *f) -{ - genobject *gen = PyObject_GC_New(genobject, &gentype); - if (gen == NULL) { - Py_DECREF(f); - return NULL; - } - gen->gi_frame = f; - gen->gi_running = 0; - gen->gi_weakreflist = NULL; - _PyObject_GC_TRACK(gen); - return (PyObject *)gen; -} - -static int -gen_traverse(genobject *gen, visitproc visit, void *arg) -{ - return visit((PyObject *)gen->gi_frame, arg); -} - -static void -gen_dealloc(genobject *gen) -{ - _PyObject_GC_UNTRACK(gen); - if (gen->gi_weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *) gen); - Py_DECREF(gen->gi_frame); - PyObject_GC_Del(gen); -} - -static PyObject * -gen_iternext(genobject *gen) -{ - PyThreadState *tstate = PyThreadState_GET(); - PyFrameObject *f = gen->gi_frame; - PyObject *result; - - if (gen->gi_running) { - PyErr_SetString(PyExc_ValueError, - "generator already executing"); - return NULL; - } - if (f->f_stacktop == NULL) - return NULL; - - /* Generators always return to their most recent caller, not - * necessarily their creator. */ - Py_XINCREF(tstate->frame); - assert(f->f_back == NULL); - f->f_back = tstate->frame; - - gen->gi_running = 1; - result = eval_frame(f); - gen->gi_running = 0; - - /* Don't keep the reference to f_back any longer than necessary. It - * may keep a chain of frames alive or it could create a reference - * cycle. */ - Py_XDECREF(f->f_back); - f->f_back = NULL; - - /* If the generator just returned (as opposed to yielding), signal - * that the generator is exhausted. */ - if (result == Py_None && f->f_stacktop == NULL) { - Py_DECREF(result); - result = NULL; - } - - return result; -} - -static PyObject * -gen_getiter(PyObject *gen) -{ - Py_INCREF(gen); - return gen; -} - -static PyMemberDef gen_memberlist[] = { - {"gi_frame", T_OBJECT, offsetof(genobject, gi_frame), RO}, - {"gi_running", T_INT, offsetof(genobject, gi_running), RO}, - {NULL} /* Sentinel */ -}; - -static PyTypeObject gentype = { - PyObject_HEAD_INIT(&PyType_Type) - 0, /* ob_size */ - "generator", /* tp_name */ - sizeof(genobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)gen_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ - 0, /* tp_doc */ - (traverseproc)gen_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(genobject, gi_weakreflist), /* tp_weaklistoffset */ - (getiterfunc)gen_getiter, /* tp_iter */ - (iternextfunc)gen_iternext, /* tp_iternext */ - 0, /* tp_methods */ - gen_memberlist, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ -}; - #ifdef WITH_THREAD @@ -2673,7 +2537,7 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals, /* Create a new generator that owns the ready to run frame * and return that as the value. */ - return gen_new(f); + return PyGen_New(f); } retval = eval_frame(f); @@ -3407,6 +3271,12 @@ PyEval_GetFuncDesc(PyObject *func) } } +PyObject * +PyEval_EvaluateFrame(PyObject *fo) +{ + return eval_frame((PyFrameObject *)fo); +} + #define EXT_POP(STACK_POINTER) (*--(STACK_POINTER)) static void |