NEURON
nrn_metaclass.cpp
Go to the documentation of this file.
1 // Derived from
2 // https://github.com/python/cpython/pull/93012#issuecomment-1138876646
3 
4 #include <Python.h>
5 
6 PyObject* nrn_type_from_metaclass(PyTypeObject* metaclass,
7  PyObject* mod,
8  PyType_Spec* spec,
9  PyObject* base) {
10 #if PY_VERSION_HEX >= 0x030C0000
11  // Life is good, PyType_FromMetaclass() is available
12  PyObject* result = PyType_FromMetaclass(metaclass, mod, spec, base);
13  if (!result) {
14  // fail("nanobind::detail::nb_type_new(\"%s\"): type construction failed!", t->name);
15  return result;
16  }
17 #else
18  /* The fallback code below is cursed. It provides an alternative when
19  PyType_FromMetaclass() is not available we are furthermore *not*
20  targeting the stable ABI interface. It calls PyType_FromSpec() to create
21  a tentative type, copies its contents into a larger type with a
22  different metaclass, then lets the original type expire. */
23 
24  PyObject* temp = PyType_FromSpecWithBases(spec, base); // since 3.3
25  Py_XINCREF(temp);
26  PyHeapTypeObject* temp_ht = (PyHeapTypeObject*) temp;
27  PyTypeObject* temp_tp = &temp_ht->ht_type;
28 
29  Py_INCREF(temp_ht->ht_name);
30  Py_INCREF(temp_ht->ht_qualname);
31  Py_INCREF(temp_tp->tp_base);
32  Py_XINCREF(temp_ht->ht_slots);
33 
34  PyObject* result = PyType_GenericAlloc(metaclass, 0);
35  if (!temp || !result) {
36  // fail("nanobind::detail::nb_type_new(\"%s\"): type construction failed!", t->name);
37  return nullptr;
38  }
39  PyHeapTypeObject* ht = (PyHeapTypeObject*) result;
40  PyTypeObject* tp = &ht->ht_type;
41 
42  memcpy(ht, temp_ht, sizeof(PyHeapTypeObject));
43 
44  tp->ob_base.ob_base.ob_type = metaclass;
45  tp->ob_base.ob_base.ob_refcnt = 1;
46  tp->ob_base.ob_size = 0;
47  tp->tp_as_async = &ht->as_async;
48  tp->tp_as_number = &ht->as_number;
49  tp->tp_as_sequence = &ht->as_sequence;
50  tp->tp_as_mapping = &ht->as_mapping;
51  tp->tp_as_buffer = &ht->as_buffer;
52  tp->tp_name = strdup(spec->name); // name_copy;
53  tp->tp_flags = spec->flags | Py_TPFLAGS_HEAPTYPE;
54 
55  tp->tp_dict = tp->tp_bases = tp->tp_mro = tp->tp_cache = tp->tp_subclasses = tp->tp_weaklist =
56  nullptr;
57  ht->ht_cached_keys = nullptr;
58  tp->tp_version_tag = 0;
59 
60  PyType_Ready(tp);
61  Py_DECREF(temp);
62 
63  // PyType_FromMetaclass consistently sets the __module__ to 'hoc' (never 'neuron.hoc')
64  // So rather than use PyModule_GetName or PyObject_GetAttrString(mod, "__name__") just
65  // explicitly set to 'hoc'
66  PyObject* module_name = PyUnicode_FromString("hoc");
67  if (PyObject_SetAttrString(result, "__module__", module_name) < 0) {
68  Py_DECREF(module_name);
69  return nullptr;
70  }
71  Py_DECREF(module_name);
72 #endif
73 
74  return result;
75 }
PyObject * nrn_type_from_metaclass(PyTypeObject *metaclass, PyObject *mod, PyType_Spec *spec, PyObject *base)
_object PyObject
Definition: nrnpy.h:12