about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/greenlet/PyGreenlet.cpp
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/greenlet/PyGreenlet.cpp
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are here HEAD master
Diffstat (limited to '.venv/lib/python3.12/site-packages/greenlet/PyGreenlet.cpp')
-rw-r--r--.venv/lib/python3.12/site-packages/greenlet/PyGreenlet.cpp738
1 files changed, 738 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/greenlet/PyGreenlet.cpp b/.venv/lib/python3.12/site-packages/greenlet/PyGreenlet.cpp
new file mode 100644
index 00000000..29c0bba0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/greenlet/PyGreenlet.cpp
@@ -0,0 +1,738 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
+#ifndef PYGREENLET_CPP
+#define PYGREENLET_CPP
+/*****************
+The Python slot functions for TGreenlet.
+ */
+
+
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+#include "structmember.h" // PyMemberDef
+
+#include "greenlet_internal.hpp"
+#include "TThreadStateDestroy.cpp"
+#include "TGreenlet.hpp"
+// #include "TUserGreenlet.cpp"
+// #include "TMainGreenlet.cpp"
+// #include "TBrokenGreenlet.cpp"
+
+
+#include "greenlet_refs.hpp"
+#include "greenlet_slp_switch.hpp"
+
+#include "greenlet_thread_support.hpp"
+#include "TGreenlet.hpp"
+
+#include "TGreenletGlobals.cpp"
+#include "TThreadStateDestroy.cpp"
+#include "PyGreenlet.hpp"
+// #include "TGreenlet.cpp"
+
+// #include "TExceptionState.cpp"
+// #include "TPythonState.cpp"
+// #include "TStackState.cpp"
+
+using greenlet::LockGuard;
+using greenlet::LockInitError;
+using greenlet::PyErrOccurred;
+using greenlet::Require;
+
+using greenlet::g_handle_exit;
+using greenlet::single_result;
+
+using greenlet::Greenlet;
+using greenlet::UserGreenlet;
+using greenlet::MainGreenlet;
+using greenlet::BrokenGreenlet;
+using greenlet::ThreadState;
+using greenlet::PythonState;
+
+
+
+static PyGreenlet*
+green_new(PyTypeObject* type, PyObject* UNUSED(args), PyObject* UNUSED(kwds))
+{
+    PyGreenlet* o =
+        (PyGreenlet*)PyBaseObject_Type.tp_new(type, mod_globs->empty_tuple, mod_globs->empty_dict);
+    if (o) {
+        new UserGreenlet(o, GET_THREAD_STATE().state().borrow_current());
+        assert(Py_REFCNT(o) == 1);
+    }
+    return o;
+}
+
+
+// green_init is used in the tp_init slot. So it's important that
+// it can be called directly from CPython. Thus, we don't use
+// BorrowedGreenlet and BorrowedObject --- although in theory
+// these should be binary layout compatible, that may not be
+// guaranteed to be the case (32-bit linux ppc possibly).
+static int
+green_init(PyGreenlet* self, PyObject* args, PyObject* kwargs)
+{
+    PyArgParseParam run;
+    PyArgParseParam nparent;
+    static const char* kwlist[] = {
+        "run",
+        "parent",
+        NULL
+    };
+
+    // recall: The O specifier does NOT increase the reference count.
+    if (!PyArg_ParseTupleAndKeywords(
+             args, kwargs, "|OO:green", (char**)kwlist, &run, &nparent)) {
+        return -1;
+    }
+
+    if (run) {
+        if (green_setrun(self, run, NULL)) {
+            return -1;
+        }
+    }
+    if (nparent && !nparent.is_None()) {
+        return green_setparent(self, nparent, NULL);
+    }
+    return 0;
+}
+
+
+
+static int
+green_traverse(PyGreenlet* self, visitproc visit, void* arg)
+{
+    // We must only visit referenced objects, i.e. only objects
+    // Py_INCREF'ed by this greenlet (directly or indirectly):
+    //
+    // - stack_prev is not visited: holds previous stack pointer, but it's not
+    //    referenced
+    // - frames are not visited as we don't strongly reference them;
+    //    alive greenlets are not garbage collected
+    //    anyway. This can be a problem, however, if this greenlet is
+    //    never allowed to finish, and is referenced from the frame: we
+    //    have an uncollectible cycle in that case. Note that the
+    //    frame object itself is also frequently not even tracked by the GC
+    //    starting with Python 3.7 (frames are allocated by the
+    //    interpreter untracked, and only become tracked when their
+    //    evaluation is finished if they have a refcount > 1). All of
+    //    this is to say that we should probably strongly reference
+    //    the frame object. Doing so, while always allowing GC on a
+    //    greenlet, solves several leaks for us.
+
+    Py_VISIT(self->dict);
+    if (!self->pimpl) {
+        // Hmm. I have seen this at interpreter shutdown time,
+        // I think. That's very odd because this doesn't go away until
+        // we're ``green_dealloc()``, at which point we shouldn't be
+        // traversed anymore.
+        return 0;
+    }
+
+    return self->pimpl->tp_traverse(visit, arg);
+}
+
+static int
+green_is_gc(PyObject* _self)
+{
+    BorrowedGreenlet self(_self);
+    int result = 0;
+    /* Main greenlet can be garbage collected since it can only
+       become unreachable if the underlying thread exited.
+       Active greenlets --- including those that are suspended ---
+       cannot be garbage collected, however.
+    */
+    if (self->main() || !self->active()) {
+        result = 1;
+    }
+    // The main greenlet pointer will eventually go away after the thread dies.
+    if (self->was_running_in_dead_thread()) {
+        // Our thread is dead! We can never run again. Might as well
+        // GC us. Note that if a tuple containing only us and other
+        // immutable objects had been scanned before this, when we
+        // would have returned 0, the tuple will take itself out of GC
+        // tracking and never be investigated again. So that could
+        // result in both us and the tuple leaking due to an
+        // unreachable/uncollectible reference. The same goes for
+        // dictionaries.
+        //
+        // It's not a great idea to be changing our GC state on the
+        // fly.
+        result = 1;
+    }
+    return result;
+}
+
+
+static int
+green_clear(PyGreenlet* self)
+{
+    /* Greenlet is only cleared if it is about to be collected.
+       Since active greenlets are not garbage collectable, we can
+       be sure that, even if they are deallocated during clear,
+       nothing they reference is in unreachable or finalizers,
+       so even if it switches we are relatively safe. */
+    // XXX: Are we responsible for clearing weakrefs here?
+    Py_CLEAR(self->dict);
+    return self->pimpl->tp_clear();
+}
+
+/**
+ * Returns 0 on failure (the object was resurrected) or 1 on success.
+ **/
+static int
+_green_dealloc_kill_started_non_main_greenlet(BorrowedGreenlet self)
+{
+    /* Hacks hacks hacks copied from instance_dealloc() */
+    /* Temporarily resurrect the greenlet. */
+    assert(self.REFCNT() == 0);
+    Py_SET_REFCNT(self.borrow(), 1);
+    /* Save the current exception, if any. */
+    PyErrPieces saved_err;
+    try {
+        // BY THE TIME WE GET HERE, the state may actually be going
+        // away
+        // if we're shutting down the interpreter and freeing thread
+        // entries,
+        // this could result in freeing greenlets that were leaked. So
+        // we can't try to read the state.
+        self->deallocing_greenlet_in_thread(
+              self->thread_state()
+              ? static_cast<ThreadState*>(GET_THREAD_STATE())
+              : nullptr);
+    }
+    catch (const PyErrOccurred&) {
+        PyErr_WriteUnraisable(self.borrow_o());
+        /* XXX what else should we do? */
+    }
+    /* Check for no resurrection must be done while we keep
+     * our internal reference, otherwise PyFile_WriteObject
+     * causes recursion if using Py_INCREF/Py_DECREF
+     */
+    if (self.REFCNT() == 1 && self->active()) {
+        /* Not resurrected, but still not dead!
+           XXX what else should we do? we complain. */
+        PyObject* f = PySys_GetObject("stderr");
+        Py_INCREF(self.borrow_o()); /* leak! */
+        if (f != NULL) {
+            PyFile_WriteString("GreenletExit did not kill ", f);
+            PyFile_WriteObject(self.borrow_o(), f, 0);
+            PyFile_WriteString("\n", f);
+        }
+    }
+    /* Restore the saved exception. */
+    saved_err.PyErrRestore();
+    /* Undo the temporary resurrection; can't use DECREF here,
+     * it would cause a recursive call.
+     */
+    assert(self.REFCNT() > 0);
+
+    Py_ssize_t refcnt = self.REFCNT() - 1;
+    Py_SET_REFCNT(self.borrow_o(), refcnt);
+    if (refcnt != 0) {
+        /* Resurrected! */
+        _Py_NewReference(self.borrow_o());
+        Py_SET_REFCNT(self.borrow_o(), refcnt);
+        /* Better to use tp_finalizer slot (PEP 442)
+         * and call ``PyObject_CallFinalizerFromDealloc``,
+         * but that's only supported in Python 3.4+; see
+         * Modules/_io/iobase.c for an example.
+         *
+         * The following approach is copied from iobase.c in CPython 2.7.
+         * (along with much of this function in general). Here's their
+         * comment:
+         *
+         * When called from a heap type's dealloc, the type will be
+         * decref'ed on return (see e.g. subtype_dealloc in typeobject.c). */
+        if (PyType_HasFeature(self.TYPE(), Py_TPFLAGS_HEAPTYPE)) {
+            Py_INCREF(self.TYPE());
+        }
+
+        PyObject_GC_Track((PyObject*)self);
+
+        _Py_DEC_REFTOTAL;
+#ifdef COUNT_ALLOCS
+        --Py_TYPE(self)->tp_frees;
+        --Py_TYPE(self)->tp_allocs;
+#endif /* COUNT_ALLOCS */
+        return 0;
+    }
+    return 1;
+}
+
+
+static void
+green_dealloc(PyGreenlet* self)
+{
+    PyObject_GC_UnTrack(self);
+    BorrowedGreenlet me(self);
+    if (me->active()
+        && me->started()
+        && !me->main()) {
+        if (!_green_dealloc_kill_started_non_main_greenlet(me)) {
+            return;
+        }
+    }
+
+    if (self->weakreflist != NULL) {
+        PyObject_ClearWeakRefs((PyObject*)self);
+    }
+    Py_CLEAR(self->dict);
+
+    if (self->pimpl) {
+        // In case deleting this, which frees some memory,
+        // somehow winds up calling back into us. That's usually a
+        //bug in our code.
+        Greenlet* p = self->pimpl;
+        self->pimpl = nullptr;
+        delete p;
+    }
+    // and finally we're done. self is now invalid.
+    Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+
+
+static OwnedObject
+internal_green_throw(BorrowedGreenlet self, PyErrPieces& err_pieces)
+{
+    PyObject* result = nullptr;
+    err_pieces.PyErrRestore();
+    assert(PyErr_Occurred());
+    if (self->started() && !self->active()) {
+        /* dead greenlet: turn GreenletExit into a regular return */
+        result = g_handle_exit(OwnedObject()).relinquish_ownership();
+    }
+    self->args() <<= result;
+
+    return single_result(self->g_switch());
+}
+
+
+
+PyDoc_STRVAR(
+    green_switch_doc,
+    "switch(*args, **kwargs)\n"
+    "\n"
+    "Switch execution to this greenlet.\n"
+    "\n"
+    "If this greenlet has never been run, then this greenlet\n"
+    "will be switched to using the body of ``self.run(*args, **kwargs)``.\n"
+    "\n"
+    "If the greenlet is active (has been run, but was switch()'ed\n"
+    "out before leaving its run function), then this greenlet will\n"
+    "be resumed and the return value to its switch call will be\n"
+    "None if no arguments are given, the given argument if one\n"
+    "argument is given, or the args tuple and keyword args dict if\n"
+    "multiple arguments are given.\n"
+    "\n"
+    "If the greenlet is dead, or is the current greenlet then this\n"
+    "function will simply return the arguments using the same rules as\n"
+    "above.\n");
+
+static PyObject*
+green_switch(PyGreenlet* self, PyObject* args, PyObject* kwargs)
+{
+    using greenlet::SwitchingArgs;
+    SwitchingArgs switch_args(OwnedObject::owning(args), OwnedObject::owning(kwargs));
+    self->pimpl->may_switch_away();
+    self->pimpl->args() <<= switch_args;
+
+    // If we're switching out of a greenlet, and that switch is the
+    // last thing the greenlet does, the greenlet ought to be able to
+    // go ahead and die at that point. Currently, someone else must
+    // manually switch back to the greenlet so that we "fall off the
+    // end" and can perform cleanup. You'd think we'd be able to
+    // figure out that this is happening using the frame's ``f_lasti``
+    // member, which is supposed to be an index into
+    // ``frame->f_code->co_code``, the bytecode string. However, in
+    // recent interpreters, ``f_lasti`` tends not to be updated thanks
+    // to things like the PREDICT() macros in ceval.c. So it doesn't
+    // really work to do that in many cases. For example, the Python
+    // code:
+    //     def run():
+    //         greenlet.getcurrent().parent.switch()
+    // produces bytecode of len 16, with the actual call to switch()
+    // being at index 10 (in Python 3.10). However, the reported
+    // ``f_lasti`` we actually see is...5! (Which happens to be the
+    // second byte of the CALL_METHOD op for ``getcurrent()``).
+
+    try {
+        //OwnedObject result = single_result(self->pimpl->g_switch());
+        OwnedObject result(single_result(self->pimpl->g_switch()));
+#ifndef NDEBUG
+        // Note that the current greenlet isn't necessarily self. If self
+        // finished, we went to one of its parents.
+        assert(!self->pimpl->args());
+
+        const BorrowedGreenlet& current = GET_THREAD_STATE().state().borrow_current();
+        // It's possible it's never been switched to.
+        assert(!current->args());
+#endif
+        PyObject* p = result.relinquish_ownership();
+
+        if (!p && !PyErr_Occurred()) {
+            // This shouldn't be happening anymore, so the asserts
+            // are there for debug builds. Non-debug builds
+            // crash "gracefully" in this case, although there is an
+            // argument to be made for killing the process in all
+            // cases --- for this to be the case, our switches
+            // probably nested in an incorrect way, so the state is
+            // suspicious. Nothing should be corrupt though, just
+            // confused at the Python level. Letting this propagate is
+            // probably good enough.
+            assert(p || PyErr_Occurred());
+            throw PyErrOccurred(
+                mod_globs->PyExc_GreenletError,
+                "Greenlet.switch() returned NULL without an exception set."
+            );
+        }
+        return p;
+    }
+    catch(const PyErrOccurred&) {
+        return nullptr;
+    }
+}
+
+PyDoc_STRVAR(
+    green_throw_doc,
+    "Switches execution to this greenlet, but immediately raises the\n"
+    "given exception in this greenlet.  If no argument is provided, the "
+    "exception\n"
+    "defaults to `greenlet.GreenletExit`.  The normal exception\n"
+    "propagation rules apply, as described for `switch`.  Note that calling "
+    "this\n"
+    "method is almost equivalent to the following::\n"
+    "\n"
+    "    def raiser():\n"
+    "        raise typ, val, tb\n"
+    "    g_raiser = greenlet(raiser, parent=g)\n"
+    "    g_raiser.switch()\n"
+    "\n"
+    "except that this trick does not work for the\n"
+    "`greenlet.GreenletExit` exception, which would not propagate\n"
+    "from ``g_raiser`` to ``g``.\n");
+
+static PyObject*
+green_throw(PyGreenlet* self, PyObject* args)
+{
+    PyArgParseParam typ(mod_globs->PyExc_GreenletExit);
+    PyArgParseParam val;
+    PyArgParseParam tb;
+
+    if (!PyArg_ParseTuple(args, "|OOO:throw", &typ, &val, &tb)) {
+        return nullptr;
+    }
+
+    assert(typ.borrow() || val.borrow());
+
+    self->pimpl->may_switch_away();
+    try {
+        // Both normalizing the error and the actual throw_greenlet
+        // could throw PyErrOccurred.
+        PyErrPieces err_pieces(typ.borrow(), val.borrow(), tb.borrow());
+
+        return internal_green_throw(self, err_pieces).relinquish_ownership();
+    }
+    catch (const PyErrOccurred&) {
+        return nullptr;
+    }
+}
+
+static int
+green_bool(PyGreenlet* self)
+{
+    return self->pimpl->active();
+}
+
+/**
+ * CAUTION: Allocates memory, may run GC and arbitrary Python code.
+ */
+static PyObject*
+green_getdict(PyGreenlet* self, void* UNUSED(context))
+{
+    if (self->dict == NULL) {
+        self->dict = PyDict_New();
+        if (self->dict == NULL) {
+            return NULL;
+        }
+    }
+    Py_INCREF(self->dict);
+    return self->dict;
+}
+
+static int
+green_setdict(PyGreenlet* self, PyObject* val, void* UNUSED(context))
+{
+    PyObject* tmp;
+
+    if (val == NULL) {
+        PyErr_SetString(PyExc_TypeError, "__dict__ may not be deleted");
+        return -1;
+    }
+    if (!PyDict_Check(val)) {
+        PyErr_SetString(PyExc_TypeError, "__dict__ must be a dictionary");
+        return -1;
+    }
+    tmp = self->dict;
+    Py_INCREF(val);
+    self->dict = val;
+    Py_XDECREF(tmp);
+    return 0;
+}
+
+static bool
+_green_not_dead(BorrowedGreenlet self)
+{
+    // XXX: Where else should we do this?
+    // Probably on entry to most Python-facing functions?
+    if (self->was_running_in_dead_thread()) {
+        self->deactivate_and_free();
+        return false;
+    }
+    return self->active() || !self->started();
+}
+
+
+static PyObject*
+green_getdead(PyGreenlet* self, void* UNUSED(context))
+{
+    if (_green_not_dead(self)) {
+        Py_RETURN_FALSE;
+    }
+    else {
+        Py_RETURN_TRUE;
+    }
+}
+
+static PyObject*
+green_get_stack_saved(PyGreenlet* self, void* UNUSED(context))
+{
+    return PyLong_FromSsize_t(self->pimpl->stack_saved());
+}
+
+
+static PyObject*
+green_getrun(PyGreenlet* self, void* UNUSED(context))
+{
+    try {
+        OwnedObject result(BorrowedGreenlet(self)->run());
+        return result.relinquish_ownership();
+    }
+    catch(const PyErrOccurred&) {
+        return nullptr;
+    }
+}
+
+
+static int
+green_setrun(PyGreenlet* self, PyObject* nrun, void* UNUSED(context))
+{
+    try {
+        BorrowedGreenlet(self)->run(nrun);
+        return 0;
+    }
+    catch(const PyErrOccurred&) {
+        return -1;
+    }
+}
+
+static PyObject*
+green_getparent(PyGreenlet* self, void* UNUSED(context))
+{
+    return BorrowedGreenlet(self)->parent().acquire_or_None();
+}
+
+
+static int
+green_setparent(PyGreenlet* self, PyObject* nparent, void* UNUSED(context))
+{
+    try {
+        BorrowedGreenlet(self)->parent(nparent);
+    }
+    catch(const PyErrOccurred&) {
+        return -1;
+    }
+    return 0;
+}
+
+
+static PyObject*
+green_getcontext(const PyGreenlet* self, void* UNUSED(context))
+{
+    const Greenlet *const g = self->pimpl;
+    try {
+        OwnedObject result(g->context());
+        return result.relinquish_ownership();
+    }
+    catch(const PyErrOccurred&) {
+        return nullptr;
+    }
+}
+
+static int
+green_setcontext(PyGreenlet* self, PyObject* nctx, void* UNUSED(context))
+{
+    try {
+        BorrowedGreenlet(self)->context(nctx);
+        return 0;
+    }
+    catch(const PyErrOccurred&) {
+        return -1;
+    }
+}
+
+
+static PyObject*
+green_getframe(PyGreenlet* self, void* UNUSED(context))
+{
+    const PythonState::OwnedFrame& top_frame = BorrowedGreenlet(self)->top_frame();
+    return top_frame.acquire_or_None();
+}
+
+
+static PyObject*
+green_getstate(PyGreenlet* self)
+{
+    PyErr_Format(PyExc_TypeError,
+                 "cannot serialize '%s' object",
+                 Py_TYPE(self)->tp_name);
+    return nullptr;
+}
+
+static PyObject*
+green_repr(PyGreenlet* _self)
+{
+    BorrowedGreenlet self(_self);
+    /*
+      Return a string like
+      <greenlet.greenlet at 0xdeadbeef [current][active started]|dead main>
+
+      The handling of greenlets across threads is not super good.
+      We mostly use the internal definitions of these terms, but they
+      generally should make sense to users as well.
+     */
+    PyObject* result;
+    int never_started = !self->started() && !self->active();
+
+    const char* const tp_name = Py_TYPE(self)->tp_name;
+
+    if (_green_not_dead(self)) {
+        /* XXX: The otid= is almost useless because you can't correlate it to
+         any thread identifier exposed to Python. We could use
+         PyThreadState_GET()->thread_id, but we'd need to save that in the
+         greenlet, or save the whole PyThreadState object itself.
+
+         As it stands, its only useful for identifying greenlets from the same thread.
+        */
+        const char* state_in_thread;
+        if (self->was_running_in_dead_thread()) {
+            // The thread it was running in is dead!
+            // This can happen, especially at interpreter shut down.
+            // It complicates debugging output because it may be
+            // impossible to access the current thread state at that
+            // time. Thus, don't access the current thread state.
+            state_in_thread = " (thread exited)";
+        }
+        else {
+            state_in_thread = GET_THREAD_STATE().state().is_current(self)
+                ? " current"
+                : (self->started() ? " suspended" : "");
+        }
+        result = PyUnicode_FromFormat(
+            "<%s object at %p (otid=%p)%s%s%s%s>",
+            tp_name,
+            self.borrow_o(),
+            self->thread_state(),
+            state_in_thread,
+            self->active() ? " active" : "",
+            never_started ? " pending" : " started",
+            self->main() ? " main" : ""
+        );
+    }
+    else {
+        result = PyUnicode_FromFormat(
+            "<%s object at %p (otid=%p) %sdead>",
+            tp_name,
+            self.borrow_o(),
+            self->thread_state(),
+            self->was_running_in_dead_thread()
+            ? "(thread exited) "
+            : ""
+            );
+    }
+
+    return result;
+}
+
+
+static PyMethodDef green_methods[] = {
+    {
+      .ml_name="switch",
+      .ml_meth=reinterpret_cast<PyCFunction>(green_switch),
+      .ml_flags=METH_VARARGS | METH_KEYWORDS,
+      .ml_doc=green_switch_doc
+    },
+    {.ml_name="throw", .ml_meth=(PyCFunction)green_throw, .ml_flags=METH_VARARGS, .ml_doc=green_throw_doc},
+    {.ml_name="__getstate__", .ml_meth=(PyCFunction)green_getstate, .ml_flags=METH_NOARGS, .ml_doc=NULL},
+    {.ml_name=NULL, .ml_meth=NULL} /* sentinel */
+};
+
+static PyGetSetDef green_getsets[] = {
+    /* name, getter, setter, doc, context pointer */
+    {.name="__dict__", .get=(getter)green_getdict, .set=(setter)green_setdict},
+    {.name="run", .get=(getter)green_getrun, .set=(setter)green_setrun},
+    {.name="parent", .get=(getter)green_getparent, .set=(setter)green_setparent},
+    {.name="gr_frame", .get=(getter)green_getframe },
+    {
+      .name="gr_context",
+      .get=(getter)green_getcontext,
+      .set=(setter)green_setcontext
+    },
+    {.name="dead", .get=(getter)green_getdead},
+    {.name="_stack_saved", .get=(getter)green_get_stack_saved},
+    {.name=NULL}
+};
+
+static PyMemberDef green_members[] = {
+    {.name=NULL}
+};
+
+static PyNumberMethods green_as_number = {
+  .nb_bool=(inquiry)green_bool,
+};
+
+
+PyTypeObject PyGreenlet_Type = {
+    .ob_base=PyVarObject_HEAD_INIT(NULL, 0)
+    .tp_name="greenlet.greenlet", /* tp_name */
+    .tp_basicsize=sizeof(PyGreenlet),  /* tp_basicsize */
+    /* methods */
+    .tp_dealloc=(destructor)green_dealloc, /* tp_dealloc */
+    .tp_repr=(reprfunc)green_repr,      /* tp_repr */
+    .tp_as_number=&green_as_number,          /* tp_as _number*/
+    .tp_flags=G_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+    .tp_doc="greenlet(run=None, parent=None) -> greenlet\n\n"
+    "Creates a new greenlet object (without running it).\n\n"
+    " - *run* -- The callable to invoke.\n"
+    " - *parent* -- The parent greenlet. The default is the current "
+    "greenlet.",                        /* tp_doc */
+    .tp_traverse=(traverseproc)green_traverse, /* tp_traverse */
+    .tp_clear=(inquiry)green_clear,         /* tp_clear */
+    .tp_weaklistoffset=offsetof(PyGreenlet, weakreflist),  /* tp_weaklistoffset */
+
+    .tp_methods=green_methods,                      /* tp_methods */
+    .tp_members=green_members,                      /* tp_members */
+    .tp_getset=green_getsets,                      /* tp_getset */
+    .tp_dictoffset=offsetof(PyGreenlet, dict),         /* tp_dictoffset */
+    .tp_init=(initproc)green_init,               /* tp_init */
+    .tp_alloc=PyType_GenericAlloc,                  /* tp_alloc */
+    .tp_new=(newfunc)green_new,                          /* tp_new */
+    .tp_free=PyObject_GC_Del,                   /* tp_free */
+    .tp_is_gc=(inquiry)green_is_gc,         /* tp_is_gc */
+};
+
+#endif
+
+// Local Variables:
+// flycheck-clang-include-path: ("/opt/local/Library/Frameworks/Python.framework/Versions/3.8/include/python3.8")
+// End: