NEURON
nrnpy_hoc.cpp
Go to the documentation of this file.
1 #include "cabcode.h"
2 #include "../oc/code.h"
3 #include "ivocvect.h"
5 #include "neuron/unique_cstr.hpp"
6 #include "nrniv_mf.h"
7 #include "nrn_pyhocobject.h"
8 #include "nrnoc2iv.h"
9 #include "nrnpy.h"
10 #include "nrnpy_utils.h"
11 #include "nrnpython.h"
13 
14 #include "nrnwrap_dlfcn.h"
15 #include "ocfile.h"
16 #include "ocjump.h"
17 #include "oclist.h"
18 #include "shapeplt.h"
19 #include "seclist.h" // lvappendsec_and_ref, seclist_size
20 
21 #include <cstdint>
22 #include <vector>
23 #include <sstream>
24 #include <unordered_map>
25 
26 #include <nanobind/nanobind.h>
27 namespace nb = nanobind;
28 
29 extern PyTypeObject* psection_type;
30 extern std::vector<const char*> py_exposed_classes;
31 
32 #include "parse.hpp"
33 extern void (*nrnpy_sectionlist_helper_)(void*, Object*);
34 extern void* (*nrnpy_get_pyobj)(Object* obj);
35 extern void (*nrnpy_restore_savestate)(int64_t, char*);
36 extern void (*nrnpy_store_savestate)(char** save_data, uint64_t* save_data_size);
37 extern void (*nrnpy_decref)(void* pyobj);
38 extern double (*nrnpy_call_func)(Object*, double);
39 extern void hoc_pushs(Symbol*);
40 extern double cable_prop_eval(Symbol* sym);
43 extern Inst* hoc_pc;
44 extern void hoc_push_string();
45 extern char** hoc_strpop();
46 extern void hoc_objectvar();
47 extern Object* hoc_newobj1(Symbol*, int);
48 extern int ivoc_list_count(Object*);
49 extern Object** hoc_objpop();
51 extern void hoc_object_component();
52 extern int nrn_inpython_;
53 extern int hoc_stack_type();
54 extern void hoc_call();
56 extern void hoc_tobj_unref(Object**);
57 extern void hoc_unref_defer();
58 extern void sec_access_push();
59 extern bool hoc_valid_stmt(const char*, Object*);
65 extern int section_object_seen;
66 extern Symbol* nrn_child_sym;
67 extern int nrn_secref_nchild(Section*);
68 static void pyobject_in_objptr(Object**, PyObject*);
69 static double nrnpy_call_func_(Object*, double);
70 extern IvocVect* (*nrnpy_vec_from_python_p_)(void*);
71 extern Object** (*nrnpy_vec_to_python_p_)(void*);
72 extern Object** (*nrnpy_vec_as_numpy_helper_)(int, double*);
73 extern Object* (*nrnpy_rvp_rxd_to_callable)(Object*);
74 extern Symbol* ivoc_alias_lookup(const char* name, Object* ob);
75 class NetCon;
76 extern int nrn_netcon_weight(NetCon*, double**);
77 extern int nrn_matrix_dim(void*, int);
78 
79 extern PyObject* pmech_types; // Python map for name to Mechanism
80 extern PyObject* rangevars_; // Python map for name to Symbol
81 
82 extern int hoc_max_builtin_class_id;
83 
87 
88 static std::unordered_map<Symbol*, PyTypeObject*> sym_to_type_map;
89 static std::unordered_map<PyTypeObject*, Symbol*> type_to_sym_map;
90 static std::vector<std::string> exposed_py_type_names;
91 
92 // typestr returned by Vector.__array_interface__
93 // byteorder (first element) is modified at import time
94 // to reflect the system byteorder
95 // Allocate one extra character space in case we have a two character integer of
96 // bytes per double
97 // i.e. 16
98 static char array_interface_typestr[5] = "|f8";
99 
100 // static pointer to neurons.doc.get_docstring function initialized at import
101 // time
102 static PyObject* pfunc_get_docstring = nullptr;
103 
104 // Methods unique to the HocTopLevelInterpreter type of HocObject
105 // follow the add_methods implementation of python3.6.2 in typeobject.c
106 // and the GenericGetAttr implementation in object.c
107 static PyObject* topmethdict = nullptr;
108 static void add2topdict(PyObject*);
109 
110 static const char* hocobj_docstring = "class neuron.hoc.HocObject - Hoc Object wrapper";
111 
112 #if 1
113 #include "hoccontext.h"
114 #else
115 extern Object* hoc_thisobject;
116 #define HocTopContextSet \
117  if (hoc_thisobject) { \
118  abort(); \
119  } \
120  assert(hoc_thisobject == 0);
121 #define HocContextRestore /**/
122 #endif
123 
124 static PyObject* rvp_plot = nullptr;
125 static PyObject* plotshape_plot = nullptr;
126 static PyObject* cpp2refstr(char** cpp);
127 static PyObject* get_mech_object_ = nullptr;
129 
130 PyTypeObject* hocobject_type;
131 
132 static PyObject* hocobj_call(PyHocObject* self, PyObject* args, PyObject* kwrds);
133 static PyObject* hocclass_getattro(PyObject* self, PyObject* pyname);
134 
135 struct hocclass {
136  PyTypeObject head;
138 };
139 
140 static int hocclass_init(hocclass* cls, PyObject* args, PyObject* kwds) {
141  if (PyType_Type.tp_init((PyObject*) cls, args, kwds) < 0) {
142  return -1;
143  }
144  return 0;
145 }
146 
147 // Returns a new reference.
148 static PyObject* hocclass_getitem(PyObject* self, Py_ssize_t ix) {
149  hocclass* hclass = (hocclass*) self;
150  Symbol* sym = hclass->sym;
151  assert(sym);
152 
153  assert(sym->type == TEMPLATE);
154  hoc_Item *q, *ql = sym->u.ctemplate->olist;
155  Object* ob;
156  ITERATE(q, ql) {
157  ob = OBJ(q);
158  if (ob->index == ix) {
159  return nrnpy_ho2po(ob);
160  }
161  }
162  PyErr_Format(PyExc_IndexError, "%s[%ld] instance does not exist", sym->name, ix);
163  return nullptr;
164 }
165 
166 // Note use of slots was informed by nanobind (search for nb_meta)
167 
168 static PyType_Slot hocclass_slots[] = {{Py_tp_base, nullptr}, // &PyType_Type : not obvious why it
169  // must be set at runtime
170  {Py_tp_init, (void*) hocclass_init},
171  {Py_tp_getattro, (void*) hocclass_getattro},
172  {Py_sq_item, (void*) hocclass_getitem},
173  {0, nullptr}};
174 
175 static PyType_Spec hocclass_spec = {"hoc.HocClass",
176  0, // .basicsize fill later
177  0, // .itemsize
178  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE,
180 
181 
183  if (pd) {
184  return true;
185  }
186  PyErr_SetString(PyExc_ValueError, "Invalid data_handle");
187  return false;
188 }
189 
190 /** @brief if hoc_evalpointer calls hoc_execerror, return 1
191  **/
192 static int hoc_evalpointer_err() {
193  try {
194  hoc_evalpointer();
195  } catch (std::exception const& e) {
196  std::ostringstream oss;
197  oss << "subscript out of range (array size or number of dimensions changed?)";
198  PyErr_SetString(PyExc_IndexError, oss.str().c_str());
199  return 1;
200  }
201  return 0;
202 }
203 
204 // Returns a new reference.
205 static PyObject* nrnexec(PyObject* self, PyObject* args) {
206  const char* cmd;
207  if (!PyArg_ParseTuple(args, "s", &cmd)) {
208  return nullptr;
209  }
210  bool b = hoc_valid_stmt(cmd, 0);
211  return PyBool_FromLong(b);
212 }
213 
214 static PyObject* nrnexec_safe(PyObject* self, PyObject* args) {
215  return nrn::convert_cxx_exceptions(nrnexec, self, args);
216 }
217 
218 static PyObject* hoc_ac(PyObject* self, PyObject* args) {
219  PyArg_ParseTuple(args, "|d", &hoc_ac_);
220  return Py_BuildValue("d", hoc_ac_);
221 }
222 
223 static PyObject* hoc_ac_safe(PyObject* self, PyObject* args) {
224  return nrn::convert_cxx_exceptions(hoc_ac, self, args);
225 }
226 
227 static PyMethodDef HocMethods[] = {
228  {"execute",
229  nrnexec_safe,
230  METH_VARARGS,
231  "Execute a hoc command, return True on success, False on failure."},
232  {"hoc_ac", hoc_ac_safe, METH_VARARGS, "Get (or set) the scalar hoc_ac_."},
233  {nullptr, nullptr, 0, nullptr}};
234 
235 static void hocobj_dealloc(PyHocObject* self) {
236  // printf("hocobj_dealloc %p\n", self);
237  if (self->ho_) {
238  hoc_obj_unref(self->ho_);
239  }
240  if (self->type_ == PyHoc::HocRefStr && self->u.s_) {
241  // delete [] self->u.s_;
242  free(self->u.s_);
243  }
244  if (self->type_ == PyHoc::HocRefObj && self->u.ho_) {
245  hoc_obj_unref(self->u.ho_);
246  }
247  if (self->indices_) {
248  delete[] self->indices_;
249  }
250  if (self->type_ == PyHoc::HocRefPStr && self->u.pstr_) {
251  // nothing deleted
252  }
253  ((PyObject*) self)->ob_type->tp_free((PyObject*) self);
254 
255  // Deferred deletion of HOC Objects is unnecessary when a HocObject is
256  // destroyed. And we would like to have prompt deletion if this HocObject
257  // wrapped a HOC Object whose refcount was 1.
258  hoc_unref_defer();
259 }
260 
261 // Returns a new reference.
262 static PyObject* hocobj_new(PyTypeObject* subtype, PyObject* args, PyObject* kwds) {
263  PyObject* base;
264  PyHocObject* hbase = nullptr;
265 
266  auto subself = nb::steal(subtype->tp_alloc(subtype, 0));
267  // printf("hocobj_new %s %p %p\n", subtype->tp_name, subtype, subself.ptr());
268  if (!subself) {
269  return nullptr;
270  }
271  PyHocObject* self = (PyHocObject*) subself.ptr();
272  self->ho_ = nullptr;
273  self->u.x_ = 0.;
274  self->sym_ = nullptr;
275  self->indices_ = nullptr;
276  self->nindex_ = 0;
277  self->type_ = PyHoc::HocTopLevelInterpreter;
278  self->iteritem_ = 0;
279 
280  // if subtype is a subclass of some NEURON class, then one of its
281  // tp_mro's is in sym_to_type_map
282  for (Py_ssize_t i = 0; i < PyTuple_Size(subtype->tp_mro); i++) {
283  PyObject* item = PyTuple_GetItem(subtype->tp_mro, i);
284  auto symbol_result = type_to_sym_map.find((PyTypeObject*) item);
285  if (symbol_result != type_to_sym_map.end()) {
286  hbase = (PyHocObject*) hocobj_new(hocobject_type, 0, 0);
287  hbase->type_ = PyHoc::HocFunction;
288  hbase->sym_ = symbol_result->second;
289  break;
290  }
291  }
292 
293  if (kwds && PyDict_Check(kwds) && (base = PyDict_GetItemString(kwds, "hocbase"))) {
294  if (PyObject_TypeCheck(base, hocobject_type)) {
295  hbase = (PyHocObject*) base;
296  } else {
297  PyErr_SetString(PyExc_TypeError, "HOC base class not valid");
298  return nullptr;
299  }
300  PyDict_DelItemString(kwds, "hocbase");
301  }
302 
303  if (hbase and hbase->type_ == PyHoc::HocFunction && hbase->sym_->type == TEMPLATE) {
304  // printf("hocobj_new base %s\n", hbase->sym_->name);
305  // remove the hocbase keyword since hocobj_call only allows
306  // the "sec" keyword argument
307  auto r = nb::steal(hocobj_call(hbase, args, kwds));
308  if (!r) {
309  return nullptr;
310  }
311  PyHocObject* rh = (PyHocObject*) r.ptr();
312  self->type_ = rh->type_;
313  self->ho_ = rh->ho_;
314  hoc_obj_ref(self->ho_);
315  }
316 
317  return subself.release().ptr();
318 }
319 
320 static int hocobj_init(PyObject* subself, PyObject* args, PyObject* kwds) {
321 // printf("hocobj_init %s %p\n",
322 // ((PyTypeObject*)PyObject_Type(subself))->tp_name, subself);
323 #if 0
324  if (subself) {
325  PyHocObject* self = (PyHocObject*)subself;
326  if (self->ho_) { hoc_obj_unref(self->ho_); }
327  self->ho_ = nullptr;
328  self->u.x_ = 0.;
329  self->sym_ = nullptr;
330  self->indices_ = nullptr;
331  self->nindex_ = 0;
332  self->type_ = 0;
333  }
334 #endif
335  return 0;
336 }
337 
338 static void pyobject_in_objptr(Object** op, PyObject* po) {
339  Object* o = nrnpy_pyobject_in_obj(po);
340  if (*op) {
341  hoc_obj_unref(*op);
342  }
343  *op = o;
344 }
345 
346 static PyObject* hocobj_name(PyObject* pself, PyObject* args) {
347  auto* const self = reinterpret_cast<PyHocObject*>(pself);
348  std::string cp;
349  if (self->type_ == PyHoc::HocObject) {
350  cp = hoc_object_name(self->ho_);
351  } else if (self->type_ == PyHoc::HocFunction || self->type_ == PyHoc::HocArray) {
352  if (self->ho_) {
353  cp.append(hoc_object_name(self->ho_));
354  cp.append(1, '.');
355  }
356  cp.append(self->sym_->name);
357  if (self->type_ == PyHoc::HocArray) {
358  for (int i = 0; i < self->nindex_; ++i) {
359  cp.append(1, '[');
360  cp.append(std::to_string(self->indices_[i]));
361  cp.append(1, ']');
362  }
363  cp.append("[?]");
364  } else {
365  cp.append("()");
366  }
367  } else if (self->type_ == PyHoc::HocRefNum) {
368  cp.append("<hoc ref value ");
369  cp.append(std::to_string(self->u.x_));
370  cp.append(1, '>');
371  } else if (self->type_ == PyHoc::HocRefStr) {
372  cp.append("<hoc ref str \"");
373  cp.append(self->u.s_);
374  cp.append("\">");
375  } else if (self->type_ == PyHoc::HocRefPStr) {
376  cp.append("<hoc ref pstr \"");
377  cp.append(*self->u.pstr_);
378  cp.append("\">");
379  } else if (self->type_ == PyHoc::HocRefObj) {
380  cp.append("<hoc ref value \"");
381  cp.append(hoc_object_name(self->u.ho_));
382  cp.append("\">");
383  } else if (self->type_ == PyHoc::HocForallSectionIterator) {
384  cp.append("<all section iterator next>");
385  } else if (self->type_ == PyHoc::HocSectionListIterator) {
386  cp.append("<SectionList iterator>");
387  } else if (self->type_ == PyHoc::HocScalarPtr) {
388  std::ostringstream oss;
389  oss << self->u.px_;
390  cp = std::move(oss).str();
391  } else if (self->type_ == PyHoc::HocArrayIncomplete) {
392  cp.append("<incomplete pointer to hoc array ");
393  cp.append(self->sym_->name);
394  cp.append(1, '>');
395  } else {
396  cp.append("<TopLevelHocInterpreter>");
397  }
398  return Py_BuildValue("s", cp.c_str());
399 }
400 
401 static PyObject* hocobj_name_safe(PyObject* pself, PyObject* args) {
402  return nrn::convert_cxx_exceptions(hocobj_name, pself, args);
403 }
404 
406  return hocobj_name(p, nullptr);
407 }
408 
409 static Inst* save_pc(Inst* newpc) {
410  Inst* savpc = hoc_pc;
411  hoc_pc = newpc;
412  return savpc;
413 }
414 
415 // also called from nrnpy_nrn.cpp
416 int hocobj_pushargs(PyObject* args, std::vector<neuron::unique_cstr>& s2free) {
417  const nb::tuple tup(args);
418  for (int i = 0; i < tup.size(); ++i) {
419  nb::object po(tup[i]);
420  if (nrnpy_numbercheck(po.ptr())) {
421  hoc_pushx(nb::cast<double>(po));
422  } else if (is_python_string(po.ptr())) {
423  char** ts = hoc_temp_charptr();
424  auto str = Py2NRNString::as_ascii(po.ptr());
425  if (!str.is_valid()) {
426  // Since Python error has been set, need to clear, or hoc_execerror
427  // printing with Printf will generate a
428  // Exception ignored on calling ctypes callback function.
429  // So get the message, clear, and make the message
430  // part of the execerror.
431  auto err = Py2NRNString::get_pyerr();
432  hoc_execerr_ext("python string arg cannot decode into c_str. Pyerr message: %s",
433  err.c_str());
434  }
435  *ts = str.c_str();
436  s2free.push_back(std::move(str));
437  hoc_pushstr(ts);
438  } else if (PyObject_TypeCheck(po.ptr(), hocobject_type)) {
439  // The PyObject_TypeCheck above used to be PyObject_IsInstance. The
440  // problem with the latter is that it calls the __class__ method of
441  // the object which can raise an error for nrn.Section, nrn.Segment,
442  // etc. if the internal Section is invalid (Section.prop == NULL).
443  // That, in consequence, will generate an
444  // Exception ignored on calling ctypes callback function: <function Printf
445  // thus obscuring the actual error, such as
446  // nrn.Segment associated with deleted internal Section.
447  PyHocObject* pho = (PyHocObject*) po.ptr();
448  PyHoc::ObjectType tp = pho->type_;
449  if (tp == PyHoc::HocObject) {
450  hoc_push_object(pho->ho_);
451  } else if (tp == PyHoc::HocRefNum) {
452  hoc_pushpx(&pho->u.x_);
453  } else if (tp == PyHoc::HocRefStr) {
454  hoc_pushstr(&pho->u.s_);
455  } else if (tp == PyHoc::HocRefObj) {
456  hoc_pushobj(&pho->u.ho_);
457  } else if (tp == PyHoc::HocScalarPtr) {
458  if (!pho->u.px_) {
459  hoc_execerr_ext("Invalid pointer (arg %d)", i);
460  }
461  hoc_push(pho->u.px_);
462  } else if (tp == PyHoc::HocRefPStr) {
463  hoc_pushstr(pho->u.pstr_);
464  } else {
465  // make a hoc python object and push that
466  Object* ob = nullptr;
467  pyobject_in_objptr(&ob, po.ptr());
468  hoc_push_object(ob);
469  hoc_obj_unref(ob);
470  }
471  } else { // make a hoc PythonObject and push that?
472  Object* ob = nullptr;
473  if (!po.is_none()) {
474  pyobject_in_objptr(&ob, po.ptr());
475  }
476  hoc_push_object(ob);
477  hoc_obj_unref(ob);
478  }
479  }
480  return tup.size();
481 }
482 
483 static Symbol* getsym(char* name, Object* ho, int fail) {
484  Symbol* sym = 0;
485  if (ho) {
487  if (!sym && strcmp(name, "delay") == 0) {
488  sym = hoc_table_lookup("del", ho->ctemplate->symtable);
489  } else if (!sym && ho->aliases) {
490  sym = ivoc_alias_lookup(name, ho);
491  }
492  } else {
494  if (!sym) {
496  }
497  }
498  if (sym && sym->type == UNDEF) {
499  sym = 0;
500  }
501  if (!sym && fail) {
502  PyErr_Format(PyExc_LookupError, "'%s' is not a defined hoc variable name.", name);
503  }
504  return sym;
505 }
506 
507 // on entry the stack order is indices, args, object
508 // on exit all that is popped and the result is on the stack
509 // returns hoc_return_is_int if called on a builtin (i.e. 2 if bool, 1 if int, 0 otherwise)
511  Inst fc[6];
514  fc[0].sym = po->sym_;
515  fc[1].i = 0;
516  fc[2].i = 0;
517  fc[5].i = 0;
518  int stk_offset = 0; // scalar
519  if (po->type_ == PyHoc::HocFunction) {
520  fc[2].i = po->nindex_;
521  fc[5].i = 1;
522  stk_offset = po->nindex_;
523  } else if (po->type_ == PyHoc::HocArray || po->type_ == PyHoc::HocArrayIncomplete) {
524  fc[1].i = po->nindex_;
525  stk_offset = po->nindex_ + 1; // + 1 because of stack_ndim_datum
526  }
527  Object* stack_value = hoc_obj_look_inside_stack(stk_offset);
528  assert(stack_value == po->ho_);
529  fc[3].i = po->ho_->ctemplate->id;
530  fc[4].sym = po->sym_;
531  Inst* pcsav = save_pc(fc);
533  hoc_pc = pcsav;
534  // only return a type if we're calling a built-in (these are all registered first)
535  if (po->ho_->ctemplate->id <= hoc_max_builtin_class_id) {
536  var_type = hoc_return_type_code;
537  }
539  return var_type;
540 }
541 
543  // PyNumber_Check can return 1 for things that should not reasonably
544  // be cast to float but should stay as python objects.
545  // e.g. numpy.arange(1,5) and 5J
546  // The complexity here is partly due to SAGE having its own
547  // number system, e.g. type(1) is <type 'sage.rings.integer.Integer'>
548  int rval = PyNumber_Check(po);
549  // but do not allow sequences
550  if (rval == 1 && po->ob_type->tp_as_sequence) {
551  rval = 0;
552  }
553  // or things that fail when float(po) fails. ARGGH! This
554  // is a lot more expensive than I would like.
555  if (rval == 1) {
556  nb::float_ tmp(po);
557  if (!tmp) {
558  PyErr_Clear();
559  rval = 0;
560  }
561  }
562  return rval;
563 }
564 
565 // Returns a new reference.
567  // o may be NULLobject, or encapsulate a Python object (via
568  // the PythonObject class in hoc (see Py2Nrn in nrnpy_p2h.cpp),
569  // or be a native hoc class instance such as Graph.
570  // The return value is None, or the encapsulated PyObject or
571  // an encapsulating PyHocObject
572  nb::object po;
573  if (!o) {
574  po = nb::none();
575  } else if (o->ctemplate->sym == nrnpy_pyobj_sym_) {
576  po = nb::borrow(nrnpy_hoc2pyobject(o));
577  } else {
578  po = nb::steal(hocobj_new(hocobject_type, 0, 0));
579  ((PyHocObject*) po.ptr())->ho_ = o;
580  ((PyHocObject*) po.ptr())->type_ = PyHoc::HocObject;
581  auto location = sym_to_type_map.find(o->ctemplate->sym);
582  if (location != sym_to_type_map.end()) {
583  Py_INCREF(location->second);
584  po.ptr()->ob_type = location->second;
585  }
586  hoc_obj_ref(o);
587  }
588  return po.release().ptr();
589 }
590 
591 // not static because it's used in nrnpy_nrn.cpp
593  // po may be None, or encapsulate a hoc object (via the
594  // PyHocObject, or be a native Python instance such as [1,2,3]
595  // The return value is None, or the encapsulated hoc object,
596  // or a hoc object of type PythonObject that encapsulates the
597  // po.
598  Object* o;
599  if (po == Py_None) {
600  o = nullptr;
601  } else if (PyObject_TypeCheck(po, hocobject_type)) {
602  PyHocObject* pho = (PyHocObject*) po;
603  if (pho->type_ == PyHoc::HocObject) {
604  o = pho->ho_;
605  hoc_obj_ref(o);
606  } else if (pho->type_ == PyHoc::HocRefObj) {
607  o = pho->u.ho_;
608  hoc_obj_ref(o);
609  } else {
610  // all the rest encapsulate
611  o = nrnpy_pyobject_in_obj(po);
612  }
613  } else { // even if Python string or number
614  o = nrnpy_pyobject_in_obj(po);
615  }
616  return o;
617 }
618 
619 // Returns a new reference.
620 PyObject* nrnpy_hoc_pop(const char* mes) {
621  nb::object result;
622  Object* ho;
623  Object** d;
624  switch (hoc_stack_type()) {
625  case STRING:
626  result = nb::steal(Py_BuildValue("s", *hoc_strpop()));
627  break;
628  case VAR: {
629  // remove mes arg when test coverage development completed
630  // printf("VAR nrnpy_hoc_pop %s\n", mes);
631  auto const px = hoc_pop_handle<double>();
632  if (nrn_chk_data_handle(px)) {
633  // unfortunately, this is nonsense if NMODL POINTER is pointing
634  // to something other than a double.
635  result = nb::steal(Py_BuildValue("d", *px));
636  }
637  } break;
638  case NUMBER:
639  result = nb::steal(Py_BuildValue("d", hoc_xpop()));
640  break;
641  case OBJECTVAR:
642  case OBJECTTMP:
643  d = hoc_objpop();
644  ho = *d;
645  // printf("Py2Nrn %p %p\n", ho->ctemplate->sym, nrnpy_pyobj_sym_);
646  result = nb::steal(nrnpy_ho2po(ho));
647  hoc_tobj_unref(d);
648  break;
649  default:
650  printf("nrnpy_hoc_pop error: stack type = %d\n", hoc_stack_type());
651  }
652  return result.release().ptr();
653 }
654 
655 static int set_final_from_stk(PyObject* po) {
656  int err = 0;
657  switch (hoc_stack_type()) {
658  case STRING:
659  char* s;
660  if (PyArg_Parse(po, "s", &s) == 1) {
662  } else {
663  err = 1;
664  }
665  break;
666  case VAR: {
667  if (double x; PyArg_Parse(po, "d", &x) == 1) {
668  auto px = hoc_pop_handle<double>();
669  if (px) {
670  // This is a future crash if NMODL POINTER is pointing
671  // to something other than a double.
672  *px = x;
673  } else {
674  PyErr_SetString(PyExc_AttributeError, "POINTER is NULL");
675  return -1;
676  }
677  } else {
678  err = 1;
679  }
680  } break;
681  case OBJECTVAR:
682  PyHocObject* pho;
683  if (PyArg_Parse(po, "O!", hocobject_type, &pho) == 1) {
684  Object** pobj = hoc_objpop();
685  if (pho->sym_) {
686  PyErr_SetString(PyExc_TypeError, "argument cannot be a hoc object intermediate");
687  return -1;
688  }
689  Object* ob = *pobj;
690  hoc_obj_ref(pho->ho_);
691  hoc_obj_unref(ob);
692  *pobj = pho->ho_;
693  } else {
694  err = 1;
695  }
696  break;
697  default:
698  printf("set_final_from_stk() error: stack type = %d\n", hoc_stack_type());
699  err = 1;
700  break;
701  }
702  return err;
703 }
704 
705 
706 // Returns a new reference.
707 static void* nrnpy_hoc_int_pop() {
708  return (void*) Py_BuildValue("i", (long) hoc_xpop());
709 }
710 
711 // Returns a new reference.
712 static void* nrnpy_hoc_bool_pop() {
713  return (void*) PyBool_FromLong((long) hoc_xpop());
714 }
715 
716 // Returns a new reference.
717 static void* fcall(void* vself, void* vargs) {
718  PyHocObject* self = (PyHocObject*) vself;
719  if (self->ho_) {
720  hoc_push_object(self->ho_);
721  }
722 
723  std::vector<neuron::unique_cstr> strings_to_free;
724  int narg = hocobj_pushargs((PyObject*) vargs, strings_to_free);
725  if (self->ho_) {
726  self->nindex_ = narg;
727  HocReturnType var_type = component(self);
728  switch (var_type) {
730  return nrnpy_hoc_bool_pop();
732  return nrnpy_hoc_int_pop();
733  default:
734  // No callable hoc function returns a data handle.
735  return nrnpy_hoc_pop("self->ho_ fcall");
736  }
737  }
738  if (self->sym_->type == BLTIN) {
739  if (narg != 1) {
740  hoc_execerror("must be one argument for", self->sym_->name);
741  }
742  double d = hoc_call_func(self->sym_, 1);
743  hoc_pushx(d);
744  } else if (self->sym_->type == TEMPLATE) {
745  Object* ho = hoc_newobj1(self->sym_, narg);
746  auto result = nb::steal(hocobj_new(hocobject_type, 0, 0));
747  auto* pho = (PyHocObject*) result.ptr();
748  pho->ho_ = ho;
749  pho->type_ = PyHoc::HocObject;
750  // Note: I think the only reason we're not using ho2po here is because we don't have to
751  // hocref ho since it was created by hoc_newobj1... but it would be better if we did
752  // so we could avoid repetitive code
753  auto location = sym_to_type_map.find(ho->ctemplate->sym);
754  if (location != sym_to_type_map.end()) {
755  Py_INCREF(location->second);
756  result.ptr()->ob_type = location->second;
757  }
758 
759  return result.release().ptr();
760  } else {
761  auto interp = HocTopContextManager();
762  Inst fc[4];
763  // ugh. so a potential call of hoc_get_last_pointer_symbol will return nullptr.
764  fc[0].in = STOP;
765  fc[1].sym = self->sym_;
766  fc[2].i = narg;
767  fc[3].in = STOP;
768  Inst* pcsav = save_pc(fc + 1);
769  hoc_call();
770  hoc_pc = pcsav;
771  }
772 
773  return nrnpy_hoc_pop("laststatement fcall");
774 }
775 
777 
779  return PyTuple_GetItem(curargs_, i);
780 }
781 
782 static PyObject* hocobj_call(PyHocObject* self, PyObject* args, PyObject* kwrds) {
783  // Hack to allow some python only methods to get the python args.
784  // without losing info about type bool, int, etc.
785  // eg pc.py_broadcast, pc.py_gather, pc.py_allgather
786  PyObject* prevargs_ = curargs_;
787  curargs_ = args;
788 
789  PyObject* section = nullptr;
790  nb::object result;
791  if (kwrds && PyDict_Check(kwrds)) {
792 #if 0
793  PyObject* keys = PyDict_Keys(kwrds);
794  assert(PyList_Check(keys));
795  int n = PyList_Size(keys);
796  for (int i = 0; i < n; ++i) {
797  PyObject* key = PyList_GetItem(keys, i);
798  PyObject* value = PyDict_GetItem(kwrds, key);
799  printf("%s %s\n", PyUnicode_AsUTF8(key), PyUnicode_AsUTF8(PyObject_Str(value)));
800  }
801 #endif
802  section = PyDict_GetItemString(kwrds, "sec");
803  int num_kwargs = PyDict_Size(kwrds);
804  if (num_kwargs > 1) {
805  PyErr_SetString(PyExc_RuntimeError, "invalid keyword argument");
806  curargs_ = prevargs_;
807  return nullptr;
808  }
809  if (section) {
810  if (PyObject_TypeCheck(section, psection_type)) {
811  Section* sec = ((NPySecObj*) section)->sec_;
812  if (!sec->prop) {
814  curargs_ = prevargs_;
815  return nullptr;
816  }
817  nrn_pushsec(sec);
818  } else {
819  PyErr_SetString(PyExc_TypeError, "sec is not a Section");
820  curargs_ = prevargs_;
821  return nullptr;
822  }
823  } else {
824  if (num_kwargs) {
825  PyErr_SetString(PyExc_RuntimeError, "invalid keyword argument");
826  curargs_ = prevargs_;
827  return nullptr;
828  }
829  }
830  }
831  if (self->type_ == PyHoc::HocTopLevelInterpreter) {
832  result = nb::steal(nrnexec((PyObject*) self, args));
833  } else if (self->type_ == PyHoc::HocFunction) {
834  try {
835  result = nb::steal(static_cast<PyObject*>(OcJump::fpycall(fcall, self, args)));
836  } catch (std::exception const& e) {
837  std::ostringstream oss;
838  oss << "hocobj_call error: " << e.what();
839  PyErr_SetString(PyExc_RuntimeError, oss.str().c_str());
840  }
841  hoc_unref_defer();
842  } else {
843  PyErr_SetString(PyExc_TypeError, "object is not callable");
844  curargs_ = prevargs_;
845  return nullptr;
846  }
847  if (section) {
848  nrn_popsec();
849  }
850  curargs_ = prevargs_;
851  return result.release().ptr();
852 }
853 
854 static Arrayinfo* hocobj_aray(Symbol* sym, Object* ho) {
855  if (!sym->arayinfo) {
856  return nullptr;
857  }
858  if (ho) { // objectdata or not?
859  int cplus = (ho->ctemplate->sym->subtype & (CPLUSOBJECT | JAVAOBJECT));
860  if (cplus) {
861  return sym->arayinfo;
862  } else {
863  return ho->u.dataspace[sym->u.oboff + 1].arayinfo;
864  }
865  } else {
866  if (sym->type == VAR &&
867  (sym->subtype == USERDOUBLE || sym->subtype == USERINT || sym->subtype == USERFLOAT)) {
868  return sym->arayinfo;
869  } else {
870  return hoc_top_level_data[sym->u.oboff + 1].arayinfo;
871  }
872  }
873 }
874 
875 // Returns a new reference.
876 static PyHocObject* intermediate(PyHocObject* po, Symbol* sym, int ix) {
877  auto ponew_guard = nb::steal(hocobj_new(hocobject_type, 0, 0));
878  PyHocObject* ponew = (PyHocObject*) ponew_guard.ptr();
879  if (po->ho_) {
880  ponew->ho_ = po->ho_;
881  hoc_obj_ref(po->ho_);
882  }
883  if (ix > -1) { // array, increase dimension by one
884  int j;
885  assert(po->sym_ == sym);
887  ponew->sym_ = sym;
888  ponew->nindex_ = po->nindex_ + 1;
889  ponew->type_ = po->type_;
890  ponew->indices_ = new int[ponew->nindex_];
891  for (j = 0; j < po->nindex_; ++j) {
892  ponew->indices_[j] = po->indices_[j];
893  }
894  ponew->indices_[po->nindex_] = ix;
895  } else { // make it an array (no indices yet)
896  ponew->sym_ = sym;
897  ponew->type_ = PyHoc::HocArray;
898  }
899  return (PyHocObject*) ponew_guard.release().ptr();
900 }
901 
902 // when called, nindex is 1 less than reality
903 static void hocobj_pushtop(PyHocObject* po, Symbol* sym, int ix) {
904  int i;
905  int n = po->nindex_++;
906  // printf("hocobj_pushtop n=%d", po->nindex_);
907  for (i = 0; i < n; ++i) {
908  hoc_pushx((double) po->indices_[i]);
909  // printf(" %d", po->indices_[i]);
910  }
911  hoc_pushx((double) ix);
912  // printf(" %d\n", ix);
913  hoc_push_ndim(n + 1);
914  if (sym) {
915  hoc_pushs(sym);
916  }
917 }
918 
919 static int hocobj_objectvar(Symbol* sym) {
920  int err{0};
921  try {
922  Inst fc;
923  fc.sym = sym;
924  Inst* pcsav = save_pc(&fc);
925  hoc_objectvar();
926  hoc_pc = pcsav;
927  } catch (std::exception const& e) {
928  std::ostringstream oss;
929  oss << "number of dimensions error:" << e.what();
930  PyErr_SetString(PyExc_IndexError, oss.str().c_str());
931  err = 1;
932  }
933  return err;
934 }
935 
936 // Return a new reference.
938  Inst fc;
939  fc.sym = sym;
940  Inst* pcsav = save_pc(&fc);
941  sec_access_push();
942  hoc_pc = pcsav;
943  nb::object result = nb::steal(nrnpy_cas(0, 0));
944  nrn_popsec();
945  return result.release().ptr();
946 }
947 
948 // leave pointer on stack ready for get/set final
949 static void eval_component(PyHocObject* po, int ix) {
950  hoc_push_object(po->ho_);
951  hocobj_pushtop(po, 0, ix);
952  component(po);
953  --po->nindex_;
954 }
955 
956 // Returns a new reference.
958  auto result = nb::steal(hocobj_new(hocobject_type, 0, 0));
959  auto* const po = reinterpret_cast<PyHocObject*>(result.ptr());
961  po->u.px_ = d;
962  return result.release().ptr();
963 }
964 
965 // Returns a new reference.
966 extern "C" NRN_EXPORT PyObject* nrn_hocobj_ptr(double* pd) {
968 }
969 
971  int ret = 0;
972  if (PyObject_TypeCheck(po, hocobject_type)) {
973  auto* const hpo = reinterpret_cast<PyHocObject*>(po);
974  if (hpo->type_ == PyHoc::HocScalarPtr) {
975  pd = hpo->u.px_;
976  ret = 1;
977  }
978  }
979  return ret;
980 }
981 
982 static void symlist2dict(Symlist* sl, PyObject* dict) {
983  auto nn = nb::steal(Py_BuildValue(""));
984  for (Symbol* s = sl->first; s; s = s->next) {
985  if (s->type == UNDEF) {
986  continue;
987  }
988  if (s->cpublic == 1 || sl == hoc_built_in_symlist || sl == hoc_top_level_symlist) {
989  if (strcmp(s->name, "del") == 0) {
990  PyDict_SetItemString(dict, "delay", nn.ptr());
991  } else {
992  PyDict_SetItemString(dict, s->name, nn.ptr());
993  }
994  }
995  }
996 }
997 
998 static int setup_doc_system() {
999  PyObject* pdoc;
1000  if (pfunc_get_docstring) {
1001  return 1;
1002  }
1003  pdoc = PyImport_ImportModule("neuron.doc");
1004  if (!pdoc) {
1005  PyErr_SetString(PyExc_ImportError, "Failed to import neuron.doc documentation module.");
1006  return 0;
1007  }
1008  pfunc_get_docstring = PyObject_GetAttrString(pdoc, "get_docstring");
1009 
1010  if (!pfunc_get_docstring) {
1011  PyErr_SetString(PyExc_AttributeError,
1012  "neuron.doc module does not have attribute 'get_docstring'!");
1013  return 0;
1014  }
1015  return 1;
1016 }
1017 
1018 // Returns a new reference.
1019 static PyObject* hocclass_getattro(PyObject* self, PyObject* pyname) {
1020  hocclass* hclass = (hocclass*) self;
1021  auto name = Py2NRNString::as_ascii(pyname);
1022  const auto n = name.c_str();
1023  if (!n) {
1024  Py2NRNString::set_pyerr(PyExc_TypeError, "attribute name must be a string");
1025  return nullptr;
1026  }
1027 
1028  if (strcmp(n, "__doc__") == 0) {
1029  if (setup_doc_system()) {
1030  nb::object docobj;
1031  if (hclass->sym) {
1032  // For class types, pass the class name and empty string for symbol
1033  docobj = nb::make_tuple("", hclass->sym->name);
1034  } else {
1035  // Fallback
1036  docobj = nb::make_tuple("", "");
1037  }
1038 
1039  nb::object result = nb::steal(PyObject_CallObject(pfunc_get_docstring, docobj.ptr()));
1040  return result.release().ptr();
1041  } else {
1042  return nullptr;
1043  }
1044  }
1045 
1046  // Fall back to the base type's getattro
1047  return PyType_Type.tp_getattro(self, pyname);
1048 }
1049 
1050 // Most likely returns a new reference.
1051 PyObject* toplevel_get(PyObject* subself, const char* n) {
1052  PyHocObject* self = (PyHocObject*) subself;
1053  if (self->type_ == PyHoc::HocTopLevelInterpreter) {
1054  auto descr = nb::borrow(PyDict_GetItemString(topmethdict, n));
1055  if (descr) {
1056  descrgetfunc f = descr.ptr()->ob_type->tp_descr_get;
1057  assert(f);
1058  return f(descr.ptr(), subself, (PyObject*) Py_TYPE(subself));
1059  }
1060  }
1061  return nullptr;
1062 }
1063 
1064 // Returns a new reference.
1065 static PyObject* hocobj_getattr(PyObject* subself, PyObject* pyname) {
1066  // TODO: This function needs refactoring; there are too many exit points
1067  PyHocObject* self = (PyHocObject*) subself;
1068  if (self->type_ == PyHoc::HocObject && !self->ho_) {
1069  PyErr_SetString(PyExc_TypeError, "not a compound type");
1070  return nullptr;
1071  }
1072 
1073  nb::object result;
1074  int isptr = 0;
1075  auto name = Py2NRNString::as_ascii(pyname);
1076  char* n = name.c_str();
1077  if (!n) {
1078  Py2NRNString::set_pyerr(PyExc_TypeError, "attribute name must be a string");
1079  return nullptr;
1080  }
1081 
1082  Symbol* sym = getsym(n, self->ho_, 0);
1083  // Return well known types right away
1084  auto location = sym_to_type_map.find(sym);
1085  if (location != sym_to_type_map.end()) {
1086  Py_INCREF(location->second);
1087  return (PyObject*) location->second;
1088  }
1089 
1090  if (!sym) {
1091  if (self->type_ == PyHoc::HocObject && self->ho_->ctemplate->sym == nrnpy_pyobj_sym_) {
1092  PyObject* p = nrnpy_hoc2pyobject(self->ho_);
1093  return PyObject_GenericGetAttr(p, pyname);
1094  }
1095  if (self->type_ == PyHoc::HocTopLevelInterpreter) {
1096  result = nb::steal(toplevel_get(subself, n));
1097  if (result) {
1098  return result.release().ptr();
1099  }
1100  }
1101  if (strcmp(n, "__dict__") == 0) {
1102  // all the public names
1103  Symlist* sl = nullptr;
1104  if (self->ho_) {
1105  sl = self->ho_->ctemplate->symtable;
1106  } else if (self->sym_ && self->sym_->type == TEMPLATE) {
1107  sl = self->sym_->u.ctemplate->symtable;
1108  }
1109  auto dict = nb::steal(PyDict_New());
1110  if (sl) {
1111  symlist2dict(sl, dict.ptr());
1112  } else {
1113  symlist2dict(hoc_built_in_symlist, dict.ptr());
1114  symlist2dict(hoc_top_level_symlist, dict.ptr());
1115  add2topdict(dict.ptr());
1116  }
1117 
1118  // Is the self->ho_ a Vector? If so, add the __array_interface__ symbol
1119 
1120  if (is_obj_type(self->ho_, "Vector")) {
1121  PyDict_SetItemString(dict.ptr(), "__array_interface__", Py_None);
1122  } else if (is_obj_type(self->ho_, "RangeVarPlot") ||
1123  is_obj_type(self->ho_, "PlotShape")) {
1124  PyDict_SetItemString(dict.ptr(), "plot", Py_None);
1125  }
1126  return dict.release().ptr();
1127  } else if (strncmp(n, "_ref_", 5) == 0) {
1128  if (self->type_ > PyHoc::HocObject) {
1129  PyErr_SetString(PyExc_TypeError, "not a HocTopLevelInterpreter or HocObject");
1130  return nullptr;
1131  }
1132  sym = getsym(n + 5, self->ho_, 0);
1133  if (!sym) {
1134  return PyObject_GenericGetAttr((PyObject*) subself, pyname);
1135  }
1136  if (sym->type == STRING) {
1138  if (self->type_ == PyHoc::HocTopLevelInterpreter) {
1140  } else if (self->type_ == PyHoc::HocObject && !self->ho_->ctemplate->constructor) {
1141  hoc_objectdata = self->ho_->u.dataspace;
1142  } else {
1144  assert(0);
1145  return nullptr;
1146  }
1147  char** cpp = OPSTR(sym);
1149  result = nb::steal(cpp2refstr(cpp));
1150  return result.release().ptr();
1151  } else if (sym->type != VAR && sym->type != RANGEVAR && sym->type != VARALIAS) {
1152  PyErr_Format(
1153  PyExc_TypeError,
1154  "Hoc pointer error, %s is not a hoc variable or range variable or strdef",
1155  sym->name);
1156  return nullptr;
1157  } else {
1158  isptr = 1;
1159  }
1160  } else if (is_obj_type(self->ho_, "Vector") && strcmp(n, "__array_interface__") == 0) {
1161  // return __array_interface__
1162  // printf("building array interface\n");
1163  Vect* v = (Vect*) self->ho_->u.this_pointer;
1164  int size = v->size();
1165  double* x = vector_vec(v);
1166 
1167  return Py_BuildValue("{s:(i),s:s,s:i,s:(N,O)}",
1168  "shape",
1169  size,
1170  "typestr",
1172  "version",
1173  3,
1174  "data",
1175  PyLong_FromVoidPtr(x),
1176  Py_True);
1177 
1178  } else if (is_obj_type(self->ho_, "RangeVarPlot") && strcmp(n, "plot") == 0) {
1179  return PyObject_CallFunctionObjArgs(rvp_plot, (PyObject*) self, nullptr);
1180  } else if (is_obj_type(self->ho_, "PlotShape") && strcmp(n, "plot") == 0) {
1181  return PyObject_CallFunctionObjArgs(plotshape_plot, (PyObject*) self, nullptr);
1182  } else if (strcmp(n, "__doc__") == 0) {
1183  if (setup_doc_system()) {
1184  nb::object docobj;
1185  if (self->ho_) {
1186  docobj = nb::make_tuple(self->ho_->ctemplate->sym->name,
1187  self->sym_ ? self->sym_->name : "");
1188  } else if (self->sym_) {
1189  // Symbol
1190  docobj = nb::make_tuple("", self->sym_->name);
1191  } else {
1192  // Base HocObject
1193  docobj = nb::make_tuple("", "");
1194  }
1195 
1196  result = nb::steal(PyObject_CallObject(pfunc_get_docstring, docobj.ptr()));
1197  return result.release().ptr();
1198  } else {
1199  return nullptr;
1200  }
1201  } else if (self->type_ == PyHoc::HocTopLevelInterpreter &&
1202  strncmp(n, "__nrnsec_0x", 11) == 0) {
1204  if (!sec) {
1205  PyErr_SetString(PyExc_NameError, n);
1206  } else if (sec && sec->prop && sec->prop->dparam[PROP_PY_INDEX].get<void*>()) {
1207  result = nb::borrow(
1208  static_cast<PyObject*>(sec->prop->dparam[PROP_PY_INDEX].get<void*>()));
1209  } else {
1210  nrn_pushsec(sec);
1211  result = nb::steal(nrnpy_cas(nullptr, nullptr));
1212  nrn_popsec();
1213  }
1214  return result.release().ptr();
1215  } else if (self->type_ == PyHoc::HocTopLevelInterpreter && strncmp(n, "__pysec_", 8) == 0) {
1217  if (!sec) {
1218  PyErr_SetString(PyExc_NameError, n);
1219  } else if (sec && sec->prop && sec->prop->dparam[PROP_PY_INDEX].get<void*>()) {
1220  result = nb::borrow(
1221  static_cast<PyObject*>(sec->prop->dparam[PROP_PY_INDEX].get<void*>()));
1222  } else {
1223  nrn_pushsec(sec);
1224  result = nb::steal(nrnpy_cas(nullptr, nullptr));
1225  nrn_popsec();
1226  }
1227  return result.release().ptr();
1228  } else {
1229  // ipython wants to know if there is a __getitem__
1230  // even though it does not use it.
1231  return PyObject_GenericGetAttr((PyObject*) subself, pyname);
1232  }
1233  }
1234  // printf("%s type=%d nindex=%d %s\n", self->sym_?self->sym_->name:"noname",
1235  // self->type_, self->nindex_, sym->name);
1236  // no hoc component for a hoc function
1237  // ie the sym has to be a component for the object returned by the function
1238  if (self->type_ == PyHoc::HocFunction) {
1239  PyErr_SetString(PyExc_TypeError,
1240  "No hoc method for a callable. Missing parentheses before the '.'?");
1241  return nullptr;
1242  }
1243  if (self->type_ == PyHoc::HocArray) {
1244  PyErr_SetString(PyExc_TypeError, "Missing array index");
1245  return nullptr;
1246  }
1247  if (self->ho_) { // use the component fork.
1248  // We use the convention that `ret_ho_` own the Python object,
1249  // and `po` is just a (casted) pointer/view.
1250  auto ret_ho_ = nb::steal(hocobj_new(hocobject_type, 0, 0));
1251  PyHocObject* po = (PyHocObject*) ret_ho_.ptr();
1252  po->ho_ = self->ho_;
1253  hoc_obj_ref(po->ho_);
1254  po->sym_ = sym;
1255  // evaluation deferred unless VAR,STRING,OBJECTVAR and not
1256  // an array
1257  int t = sym->type;
1258  if (t == VAR || t == STRING || t == OBJECTVAR || t == RANGEVAR || t == SECTION ||
1259  t == SECTIONREF || t == VARALIAS || t == OBJECTALIAS || t == RANGEOBJ) {
1260  if (sym != nrn_child_sym && !is_array(*sym)) {
1261  hoc_push_object(po->ho_);
1262  nrn_inpython_ = 1;
1263  component(po);
1264  if (nrn_inpython_ == 2) { // error in component
1265  nrn_inpython_ = 0;
1266  PyErr_SetString(PyExc_TypeError, "No value");
1267  return nullptr;
1268  }
1269  nrn_inpython_ = 0;
1270  if (t == SECTION || t == SECTIONREF) {
1271  section_object_seen = 0;
1272  auto ret = nb::steal(nrnpy_cas(0, 0));
1273  nrn_popsec();
1274  return ret.release().ptr();
1275  } else {
1276  if (isptr) {
1277  auto handle = hoc_pop_handle<double>();
1279  } else {
1280  return nrnpy_hoc_pop("use-the-component-fork hocobj_getattr");
1281  }
1282  }
1283  } else {
1284  if (isptr) {
1286  } else {
1287  po->type_ = PyHoc::HocArray;
1288  }
1289  return ret_ho_.release().ptr();
1290  }
1291  } else {
1292  po->type_ = PyHoc::HocFunction;
1293  return ret_ho_.release().ptr();
1294  }
1295  }
1296  // top level interpreter fork
1297  auto interp = HocTopContextManager();
1298  switch (sym->type) {
1299  case VAR: // double*
1300  if (!is_array(*sym)) {
1301  if (sym->subtype == USERINT) {
1302  result = nb::steal(Py_BuildValue("i", *(sym->u.pvalint)));
1303  break;
1304  }
1305  if (sym->subtype == USERPROPERTY) {
1306  if (!nrn_noerr_access()) {
1307  PyErr_SetString(PyExc_TypeError, "Section access unspecified");
1308  break;
1309  }
1310  if (!isptr) {
1311  if (sym->u.rng.type == CABLESECTION) {
1312  result = nb::steal(Py_BuildValue("d", cable_prop_eval(sym)));
1313  } else {
1314  result = nb::steal(Py_BuildValue("i", int(cable_prop_eval(sym))));
1315  }
1316  break;
1317  } else if (sym->u.rng.type != CABLESECTION) {
1318  PyErr_SetString(PyExc_TypeError, "Cannot be a reference");
1319  break;
1320  }
1321  }
1322  hoc_pushs(sym);
1323  hoc_evalpointer();
1324  if (isptr) {
1325  result = nb::steal(nrn_hocobj_ptr(hoc_pxpop()));
1326  } else {
1327  result = nb::steal(Py_BuildValue("d", *hoc_pxpop()));
1328  }
1329  } else {
1330  result = nb::steal((PyObject*) intermediate(self, sym, -1));
1331  if (isptr) {
1332  ((PyHocObject*) result.ptr())->type_ = PyHoc::HocArrayIncomplete;
1333  } else {
1334  }
1335  }
1336  break;
1337  case STRING: // char*
1338  {
1339  Inst fc, *pcsav;
1340  fc.sym = sym;
1341  pcsav = save_pc(&fc);
1342  hoc_push_string();
1343  hoc_pc = pcsav;
1344  result = nb::steal(Py_BuildValue("s", *hoc_strpop()));
1345  } break;
1346  case OBJECTVAR: // Object*
1347  if (!is_array(*sym)) {
1348  Inst fc, *pcsav;
1349  fc.sym = sym;
1350  pcsav = save_pc(&fc);
1351  hoc_objectvar();
1352  hoc_pc = pcsav;
1353  Object* ho = *hoc_objpop();
1354  result = nb::steal(nrnpy_ho2po(ho));
1355  } else {
1356  result = nb::steal((PyObject*) intermediate(self, sym, -1));
1357  }
1358  break;
1359  case SECTION:
1360  if (!is_array(*sym)) {
1361  result = nb::steal(hocobj_getsec(sym));
1362  } else {
1363  result = nb::steal((PyObject*) intermediate(self, sym, -1));
1364  }
1365  break;
1366  case PROCEDURE:
1367  case FUNCTION:
1368  case FUN_BLTIN:
1369  case BLTIN:
1370  case HOCOBJFUNCTION:
1371  case STRINGFUNC:
1372  case TEMPLATE:
1373  case OBJECTFUNC: {
1374  result = nb::steal(hocobj_new(hocobject_type, 0, 0));
1375  PyHocObject* po = (PyHocObject*) result.ptr();
1376  if (self->ho_) {
1377  po->ho_ = self->ho_;
1378  hoc_obj_ref(po->ho_);
1379  }
1380  po->type_ = PyHoc::HocFunction;
1381  po->sym_ = sym;
1382  // printf("function %s\n", po->sym_->name);
1383  break;
1384  }
1385  case SETPOINTERKEYWORD:
1386  result = nb::steal(toplevel_get(subself, n));
1387  break;
1388  default: // otherwise
1389  {
1390  if (PyDict_GetItemString(pmech_types, n)) {
1391  result = nb::steal(PyObject_CallFunction(get_mech_object_, "s", n));
1392  break;
1393  } else if (PyDict_GetItemString(rangevars_, n)) {
1394  PyErr_Format(PyExc_TypeError,
1395  "Cannot access %s directly; it is a range variable and may be accessed "
1396  "via a section or segment.",
1397  n);
1398  } else {
1399  PyErr_Format(PyExc_TypeError,
1400  "Cannot access %s (NEURON type %d) directly.",
1401  n,
1402  sym->type);
1403  break;
1404  }
1405  }
1406  }
1407  return result.release().ptr();
1408 }
1409 
1410 static PyObject* hocobj_baseattr(PyObject* subself, PyObject* args) {
1411  PyObject* name;
1412  if (!PyArg_ParseTuple(args, "O", &name)) {
1413  return nullptr;
1414  }
1415  return hocobj_getattr(subself, name);
1416 }
1417 
1419  return nrn::convert_cxx_exceptions(hocobj_baseattr, subself, args);
1420 }
1421 
1422 static int refuse_to_look;
1424  // Check for __doc__ attribute - handle Python subclasses with custom __doc__
1425  auto name_str = Py2NRNString::as_ascii(name);
1426  if (name_str.c_str() && strcmp(name_str.c_str(), "__doc__") == 0) {
1427  // Try generic attribute lookup first in case the Python object has its own __doc__
1428  nb::object result = nb::steal(PyObject_GenericGetAttr(subself, name));
1429  if (result && result.ptr() != Py_None) {
1430  // Python object has a non-None __doc__ attribute, use it
1431  return result.release().ptr();
1432  } else {
1433  PyErr_Clear();
1434  // Fall back to HOC documentation system
1435  return hocobj_getattr(subself, name);
1436  }
1437  }
1438 
1439  if ((PyTypeObject*) PyObject_Type(subself) != hocobject_type) {
1440  // printf("try generic %s\n", PyString_AsString(name));
1441  nb::object result = nb::steal(PyObject_GenericGetAttr(subself, name));
1442  if (result) {
1443  // printf("found generic %s\n", PyString_AsString(name));
1444  return result.release().ptr();
1445  } else {
1446  PyErr_Clear();
1447  }
1448  }
1449  if (!refuse_to_look) {
1450  return hocobj_getattr(subself, name);
1451  }
1452  return nullptr;
1453 }
1454 
1455 static int hocobj_setattro(PyObject* subself, PyObject* pyname, PyObject* value) {
1456  int err = 0;
1457  Inst* pcsav;
1458  Inst fc;
1459 
1460  int issub = ((PyTypeObject*) PyObject_Type(subself) != hocobject_type);
1461  if (issub) {
1462  // printf("try hasattr %s\n", PyString_AsString(name));
1463  refuse_to_look = 1;
1464  if (PyObject_HasAttr(subself, pyname)) {
1465  refuse_to_look = 0;
1466  // printf("found hasattr for %s\n", PyString_AsString(name));
1467  return PyObject_GenericSetAttr(subself, pyname, value);
1468  }
1469  refuse_to_look = 0;
1470  }
1471 
1472  PyHocObject* self = (PyHocObject*) subself;
1473  PyHocObject* po;
1474 
1475  if (self->type_ == PyHoc::HocObject && !self->ho_) {
1476  return 1;
1477  }
1478  auto name = Py2NRNString::as_ascii(pyname);
1479  char* n = name.c_str();
1480  if (!n) {
1481  Py2NRNString::set_pyerr(PyExc_TypeError, "attribute name must be a string");
1482  return -1;
1483  }
1484  // printf("hocobj_setattro %s\n", n);
1485  Symbol* sym = getsym(n, self->ho_, 0);
1486  if (!sym) {
1487  if (issub) {
1488  return PyObject_GenericSetAttr(subself, pyname, value);
1489  } else if (!sym && self->type_ == PyHoc::HocObject &&
1490  self->ho_->ctemplate->sym == nrnpy_pyobj_sym_) {
1491  PyObject* p = nrnpy_hoc2pyobject(self->ho_);
1492  return PyObject_GenericSetAttr(p, pyname, value);
1493  } else if (strncmp(n, "_ref_", 5) == 0) {
1494  Symbol* rvsym = getsym(n + 5, self->ho_, 0);
1495  if (rvsym && rvsym->type == RANGEVAR) {
1496  Prop* prop = ob2pntproc_0(self->ho_)->prop;
1497  if (!prop) {
1498  PyErr_SetString(PyExc_TypeError, "Point_process not located in a section");
1499  return -1;
1500  }
1501  err = nrn_pointer_assign(prop, rvsym, value);
1502  return err;
1503  }
1504  sym = getsym(n, self->ho_, 1);
1505  } else {
1506  sym = getsym(n, self->ho_, 1);
1507  }
1508  }
1509  if (!sym) {
1510  return -1;
1511  }
1512  if (self->ho_) { // use the component fork.
1513  // Convention: `result` owns the Python object, and `po` is
1514  // just a (casted) pointer/view.
1515  auto result = nb::steal(hocobj_new(hocobject_type, 0, 0));
1516  auto* po = (PyHocObject*) result.ptr();
1517  po->ho_ = self->ho_;
1518  hoc_obj_ref(po->ho_);
1519  po->sym_ = sym;
1520  // evaluation deferred unless VAR,STRING,OBJECTVAR and not
1521  // an array
1522  int t = sym->type;
1523  if (t == VAR || t == STRING || t == OBJECTVAR || t == RANGEVAR || t == VARALIAS ||
1524  t == OBJECTALIAS) {
1525  if (!is_array(*sym)) {
1526  hoc_push_object(po->ho_);
1527  nrn_inpython_ = 1;
1528  component(po);
1529  if (nrn_inpython_ == 2) { // error in component
1530  nrn_inpython_ = 0;
1531  PyErr_SetString(PyExc_TypeError, "No value");
1532  return -1;
1533  }
1534  return set_final_from_stk(value);
1535  } else {
1536  PyErr_Format(PyExc_TypeError, "'%s' requires subscript for assignment", n);
1537  return -1;
1538  }
1539  } else {
1540  PyErr_SetString(PyExc_TypeError, "not assignable");
1541  return -1;
1542  }
1543  }
1544  auto interp = HocTopContextManager();
1545  switch (sym->type) {
1546  case VAR: // double*
1547  if (is_array(*sym)) {
1548  PyErr_SetString(PyExc_TypeError, "Wrong number of subscripts");
1549  err = -1;
1550  } else {
1551  if (sym->subtype == USERINT) {
1552  err = PyArg_Parse(value, "i", sym->u.pvalint) == 0;
1553  } else if (sym->subtype == USERPROPERTY) {
1554  if (!nrn_noerr_access()) {
1555  PyErr_SetString(PyExc_TypeError, "Section access unspecified");
1556  err = -1;
1557  break;
1558  }
1559  double x;
1560  if (sym->u.rng.type != CABLESECTION) {
1561  int i;
1562  if (PyArg_Parse(value, "i", &i) != 0 && i > 0 && i <= 32767) {
1563  x = double(i);
1564  } else {
1565  PyErr_SetString(PyExc_ValueError,
1566  "nseg must be an integer in range 1 to 32767");
1567  err = -1;
1568  }
1569  } else {
1570  err = PyArg_Parse(value, "d", &x) == 0;
1571  }
1572  if (!err) {
1573  cable_prop_assign(sym, &x, 0);
1574  }
1575  } else {
1576  hoc_pushs(sym);
1577  if (hoc_evalpointer_err()) { // not possible to raise error.
1578  return -1;
1579  }
1580  err = PyArg_Parse(value, "d", hoc_pxpop()) == 0;
1581  }
1582  }
1583  break;
1584  case STRING: // char*
1585  fc.sym = sym;
1586  pcsav = save_pc(&fc);
1587  hoc_push_string();
1588  hoc_pc = pcsav;
1589  char* s;
1590  if (PyArg_Parse(value, "s", &s) == 1) {
1592  } else {
1593  err = 1;
1594  }
1595  break;
1596  case OBJECTVAR: // Object*
1597  {
1598  err = hocobj_objectvar(sym);
1599  if (err) {
1600  break;
1601  }
1602  Object** op;
1603  op = hoc_objpop();
1604  PyObject* po;
1605  PyHocObject* pho;
1606  if (PyArg_Parse(value, "O", &po) == 1) {
1607  if (po == Py_None) {
1608  hoc_obj_unref(*op);
1609  *op = 0;
1610  } else if (PyObject_TypeCheck(po, hocobject_type)) {
1611  pho = (PyHocObject*) po;
1612  if (pho->sym_) {
1613  PyErr_SetString(PyExc_TypeError,
1614  "argument cannot be a hoc object intermediate");
1615  err = -1;
1616  } else {
1617  hoc_obj_ref(pho->ho_);
1618  hoc_obj_unref(*op);
1619  *op = pho->ho_;
1620  }
1621  } else { // it is a PythonObject in hoc
1622  pyobject_in_objptr(op, po);
1623  }
1624  } else {
1625  err = 1;
1626  }
1627  break;
1628  }
1629  default:
1630  PyErr_SetString(PyExc_TypeError, "not assignable");
1631  err = -1;
1632  break;
1633  }
1634  return err;
1635 }
1636 
1640 
1641 static int araylen(Arrayinfo* a, PyHocObject* po) {
1642  int nsub = a ? a->nsub : 0;
1643  if (nsub <= po->nindex_) {
1644  std::ostringstream oss;
1645  oss << "Too many subscripts (Redeclared the array?), hoc var " << po->sym_->name
1646  << " now has " << nsub << " but trying to access dimension " << (po->nindex_);
1647  PyErr_SetString(PyExc_TypeError, oss.str().c_str());
1648  return -1;
1649  }
1650  int n = 0;
1651  // Hoc Vector and Matrix are special cases because the sub[]
1652  // do not get filled in til just before hoc_araypt is called.
1653  // at least check the vector
1654  if (po->sym_ == sym_vec_x) {
1656  } else if (po->sym_ == sym_netcon_weight) {
1657  double* w;
1658  n = nrn_netcon_weight(static_cast<NetCon*>(po->ho_->u.this_pointer), &w);
1659  } else if (po->sym_ == nrn_child_sym) {
1661  } else if (po->sym_ == sym_mat_x) {
1662  n = nrn_matrix_dim(po->ho_->u.this_pointer, po->nindex_);
1663  } else {
1664  n = a->sub[po->nindex_];
1665  }
1666  return n;
1667 }
1668 
1669 static int araychk(Arrayinfo* a, PyHocObject* po, int ix) {
1670  int n = araylen(a, po);
1671  if (n < 0) {
1672  return -1;
1673  }
1674  if (ix < 0 || n <= ix) {
1675  // printf("ix=%d nsub=%d nindex=%d sub[nindex]=%d\n", ix, a->nsub,
1676  // po->nindex_, a->sub[po->nindex_]);
1677  PyErr_Format(PyExc_IndexError,
1678  "%s%s%s",
1679  po->ho_ ? hoc_object_name(po->ho_) : "",
1680  (po->ho_ && po->sym_) ? "." : "",
1681  po->sym_ ? po->sym_->name : "");
1682  return -1;
1683  }
1684  return 0;
1685 }
1686 
1687 static Py_ssize_t seclist_count(Object* ho) {
1689  return static_cast<Py_ssize_t>(seclist_size(static_cast<hoc_List*>(ho->u.this_pointer)));
1690 }
1691 
1692 static Py_ssize_t hocobj_len(PyObject* self) {
1693  PyHocObject* po = (PyHocObject*) self;
1694  if (po->type_ == PyHoc::HocObject) {
1695  if (po->ho_->ctemplate == hoc_vec_template_) {
1696  return vector_capacity((Vect*) po->ho_->u.this_pointer);
1697  } else if (po->ho_->ctemplate == hoc_list_template_) {
1698  return ivoc_list_count(po->ho_);
1699  } else if (po->ho_->ctemplate == hoc_sectionlist_template_) {
1700  return seclist_count(po->ho_);
1701  }
1702  } else if (po->type_ == PyHoc::HocArray) {
1703  Arrayinfo* a = hocobj_aray(po->sym_, po->ho_);
1704  return araylen(a, po);
1705  } else if (po->sym_ && po->sym_->type == TEMPLATE) {
1706  return po->sym_->u.ctemplate->count;
1707  } else if (po->type_ == PyHoc::HocForallSectionIterator) {
1708  PyErr_SetString(PyExc_TypeError, "hoc all section iterator() has no len()");
1709  return -1;
1710  } else if (po->type_ == PyHoc::HocSectionListIterator) {
1711  PyErr_SetString(PyExc_TypeError, "hoc SectionList iterator() has no len()");
1712  return -1;
1713  }
1714  PyErr_SetString(PyExc_TypeError, "Most HocObject have no len()");
1715  return -1;
1716 }
1717 
1718 static int hocobj_nonzero(PyObject* self) {
1719  // printf("hocobj_nonzero\n");
1720  PyHocObject* po = (PyHocObject*) self;
1721  int b = 1;
1722  if (po->type_ == PyHoc::HocObject) {
1723  if (po->ho_->ctemplate == hoc_vec_template_) {
1724  b = vector_capacity((Vect*) po->ho_->u.this_pointer) > 0;
1725  } else if (po->ho_->ctemplate == hoc_list_template_) {
1726  b = ivoc_list_count(po->ho_) > 0;
1727  } else if (po->ho_->ctemplate == hoc_sectionlist_template_) {
1728  b = seclist_count(po->ho_) > 0;
1729  }
1730  } else if (po->type_ == PyHoc::HocArray) {
1731  Arrayinfo* a = hocobj_aray(po->sym_, po->ho_);
1732  int i = araylen(a, po);
1733  if (i < 0) {
1734  return -1;
1735  }
1736  b = i > 0;
1737  } else if (po->sym_ && po->sym_->type == TEMPLATE) {
1738  b = 1; // prior behavior: po->sym_->u.ctemplate->count > 0;
1739  }
1740  return b;
1741 }
1742 
1744  auto po = nb::steal(hocobj_new(hocobject_type, 0, 0));
1745  PyHocObject* pho = (PyHocObject*) po.ptr();
1747  pho->u.its_ = PyHoc::Begin;
1748  pho->iteritem_ = section_list;
1749  return po.release().ptr();
1750 }
1751 
1753  return nrn::convert_cxx_exceptions(nrnpy_forall, self, args);
1754 }
1755 
1756 // Returns a new reference.
1757 static PyObject* hocobj_iter(PyObject* raw_self) {
1758  // printf("hocobj_iter %p\n", self);
1759 
1760  nb::object self = nb::borrow(raw_self);
1761  PyHocObject* po = (PyHocObject*) self.ptr();
1763  if (po->ho_->ctemplate == hoc_vec_template_) {
1764  return PySeqIter_New(self.ptr());
1765  } else if (po->ho_->ctemplate == hoc_list_template_) {
1766  return PySeqIter_New(self.ptr());
1767  } else if (po->ho_->ctemplate == hoc_sectionlist_template_) {
1768  // need a clone of self so nested loops do not share iteritem_
1769  // The HocSectionListIter arm of the outer 'if' became necessary
1770  // at Python-3.13.1 upon which the following body is executed
1771  // twice. See https://github.com/python/cpython/issues/127682
1772  auto po2 = nb::steal(nrnpy_ho2po(po->ho_));
1773  PyHocObject* pho2 = (PyHocObject*) po2.ptr();
1775  pho2->u.its_ = PyHoc::Begin;
1776  pho2->iteritem_ = ((hoc_Item*) po->ho_->u.this_pointer);
1777  return po2.release().ptr();
1778  }
1779  } else if (po->type_ == PyHoc::HocForallSectionIterator) {
1780  po->iteritem_ = section_list;
1781  po->u.its_ = PyHoc::Begin;
1782  return self.release().ptr();
1783  } else if (po->type_ == PyHoc::HocArray) {
1784  return PySeqIter_New(self.ptr());
1785  } else if (po->sym_ && po->sym_->type == TEMPLATE) {
1786  po->iteritem_ = po->sym_->u.ctemplate->olist->next;
1787  return self.release().ptr();
1788  }
1789  PyErr_SetString(PyExc_TypeError, "Not an iterable HocObject");
1790  return nullptr;
1791 }
1792 
1794  hoc_Item* nextnext;
1795  hoc_Item* next;
1796  for (next = q->next; next != ql; next = nextnext) {
1797  nextnext = next->next;
1798  Section* sec = next->element.sec;
1799  if (sec->prop) { // valid
1800  break;
1801  }
1802  hoc_l_delete(next);
1803  section_unref(sec);
1804  }
1805  return next;
1806 }
1807 
1808 // Returns a new reference.
1810  // Note that the longstanding behavior of changing the currently
1811  // accessed section during iteration no longer takes place because
1812  // we cannot guarantee that an iterate will complete with state
1813  // PyHoc::Last and so the previous Section would have been left on the
1814  // hoc section stack.
1815 
1816  // Primarily the Section is pushed and the currently accessed python
1817  // Section is returned during sequential iteration over the list ql.
1818  // On re-entry here, the previous Section is popped.
1819  // The complexity is due to the possibility that the returned nrn_Section
1820  // may be deleted with h.delete_section(sec=nrn_Section). This would
1821  // invalidate the po->iteritem_ (point to freed memory) if it were
1822  // the iteritem of the current Section. Thus we choose to store
1823  // the next iteritem pointer in the list. Although not 100% safe, since
1824  // the user body of the iterator is allowed to delete_section an arbitary
1825  // subset of sections, the obvious work around, making a copy of ql,
1826  // is considered not worth it.
1827 
1828  // In this implementation, the first call to internext_sl starts out
1829  // in state PyHoc::Begin with po->iteritem_ == ql. If there is no valid
1830  // item, it sets po->iteritem_ = nullptr and returns nullptr. If there is
1831  // a valid item, it moves to state PyHoc::Last or PyHoc::NextNotLast
1832  // depending on whether there is a valid secitem following the current
1833  // item. The current section is pushed and currently accessed python
1834  // section is returned.
1835 
1836  // Thereafter, re-entry with po->iteritem_ == nullptr, immediately
1837  // returns nullptr.
1838 
1839  // Re-entry in state PyHoc::NextNotLast, pops the previously pushed Section,
1840  // sets sec to the current Section from po->iteritem_, and
1841  // sets po->iteritem_ to the next_valid_section. If there is no next_valid
1842  // section, then move to state PyHoc::Last.
1843  // Push the current section and return currently accessed python Section.
1844 
1845  // Re-entry in state PyHoc::Last just pops the current section, sets
1846  // po->iteritem_ = nullptr, and returns nullptr.
1847 
1848  if (!po->iteritem_) {
1849  return nullptr;
1850  }
1851 
1852  if (po->u.its_ == PyHoc::Begin) {
1853  assert(po->iteritem_ == ql);
1854  hoc_Item* curitem = next_valid_secitem((hoc_Item*) (po->iteritem_), ql);
1855  if (curitem != ql) { // typical case, a valid current item
1856  Section* sec = curitem->element.sec;
1857  assert(sec->prop);
1858  // sec could be delete_section before return to internext.sl
1859  // which would invalidate curitem.
1860  // Not perfectly safe but leave po->iteritem_ as next valid
1861  // secitem after curitem.
1862  po->iteritem_ = next_valid_secitem(curitem, ql);
1863  if (po->iteritem_ == ql) {
1864  po->u.its_ = PyHoc::Last;
1865  } else {
1866  po->u.its_ = PyHoc::NextNotLast;
1867  }
1868  return (PyObject*) newpysechelp(sec);
1869  } else { // no valid current item so stop
1870  po->iteritem_ = nullptr;
1871  return nullptr;
1872  }
1873  } else if (po->u.its_ == PyHoc::NextNotLast) {
1874  // it would be a bug if po->iteritem_ (now the curitem) has been delete_section
1875  Section* sec = ((hoc_Item*) (po->iteritem_))->element.sec;
1876  if (!sec->prop) {
1877  // Handle an edge case where sec is a Python
1878  // Section and happens to have had as its only reference the
1879  // iteration variable on the previous iteration.
1880  // i.e. when sec is returned from the previous iteration, it will
1881  // be referenced by the iteration variable and the previous reference
1882  // will go to 0, thus invalidating po->iteritem_->element.sec->prop
1883  po->iteritem_ = next_valid_secitem((hoc_Item*) (po->iteritem_), ql);
1884  if (po->iteritem_ == ql) {
1885  po->u.its_ = PyHoc::Last;
1886  po->iteritem_ = nullptr;
1887  return nullptr;
1888  } else {
1889  sec = ((hoc_Item*) (po->iteritem_))->element.sec;
1890  }
1891  }
1892  assert(sec->prop);
1893  po->iteritem_ = next_valid_secitem((hoc_Item*) (po->iteritem_), ql);
1894  if (po->iteritem_ == ql) {
1895  po->u.its_ = PyHoc::Last;
1896  }
1897  return (PyObject*) newpysechelp(sec);
1898  } else if (po->u.its_ == PyHoc::Last) {
1899  po->iteritem_ = nullptr;
1900  return nullptr;
1901  }
1902  return nullptr; // never get here as po->u.its_ is always a defined state.
1903 }
1904 
1905 // Returns a new reference.
1907  // printf("hocobj_iternext %p\n", self);
1908  PyHocObject* po = (PyHocObject*) self;
1909  if (po->type_ == PyHoc::HocSectionListIterator) {
1910  hoc_Item* ql = (hoc_Item*) po->ho_->u.this_pointer;
1911  return iternext_sl(po, ql);
1912  } else if (po->type_ == PyHoc::HocForallSectionIterator) {
1913  return iternext_sl(po, section_list);
1914  } else if (po->sym_->type == TEMPLATE) {
1915  hoc_Item* q = (hoc_Item*) po->iteritem_;
1916  if (q != po->sym_->u.ctemplate->olist) {
1917  po->iteritem_ = q->next;
1918  return nrnpy_ho2po(OBJ(q));
1919  }
1920  }
1921  return nullptr;
1922 }
1923 
1924 /*
1925 Had better be an array. But the same ambiguity as with getattro
1926 in that we may return the final value or an intermediate (in the
1927 case where there is more than one dimension.) At least for now we
1928 only have to handle the OBJECTVAR and VAR case as a component and
1929 at the top level.
1930 
1931 Returns a new reference.
1932 */
1933 static PyObject* hocobj_getitem(PyObject* self, Py_ssize_t ix) {
1934  PyHocObject* po = (PyHocObject*) self;
1935  if (po->type_ > PyHoc::HocArray && po->type_ != PyHoc::HocArrayIncomplete) {
1936  if (ix != 0 && po->type_ != PyHoc::HocScalarPtr) {
1937  PyErr_SetString(PyExc_IndexError, "index for hoc ref must be 0");
1938  return nullptr;
1939  }
1940 
1941  nb::object result;
1942  if (po->type_ == PyHoc::HocScalarPtr) {
1943  try {
1944  auto const h = po->u.px_.next_array_element(ix);
1945  if (nrn_chk_data_handle(h)) {
1946  result = nb::steal(Py_BuildValue("d", *h));
1947  }
1948  } catch (std::exception const& e) {
1949  // next_array_element throws if ix is invalid
1950  PyErr_SetString(PyExc_IndexError, e.what());
1951  return nullptr;
1952  }
1953  } else if (po->type_ == PyHoc::HocRefNum) {
1954  result = nb::steal(Py_BuildValue("d", po->u.x_));
1955  } else if (po->type_ == PyHoc::HocRefStr) {
1956  result = nb::steal(Py_BuildValue("s", po->u.s_));
1957  } else if (po->type_ == PyHoc::HocRefPStr) {
1958  result = nb::steal(Py_BuildValue("s", *po->u.pstr_));
1959  } else {
1960  result = nb::steal(nrnpy_ho2po(po->u.ho_));
1961  }
1962  return result.release().ptr();
1963  }
1964  if (po->type_ == PyHoc::HocObject) { // might be in an iterator context
1965  if (po->ho_->ctemplate == hoc_vec_template_) {
1966  Vect* hv = (Vect*) po->ho_->u.this_pointer;
1967  if (ix < 0) {
1968  ix += vector_capacity(hv);
1969  }
1970  if (ix < 0 || ix >= vector_capacity(hv)) {
1971  PyErr_Format(PyExc_IndexError, "%s", hoc_object_name(po->ho_));
1972  return nullptr;
1973  } else {
1974  return PyFloat_FromDouble(vector_vec(hv)[ix]);
1975  }
1976  } else if (po->ho_->ctemplate == hoc_list_template_) {
1977  OcList* hl = (OcList*) po->ho_->u.this_pointer;
1978  if (ix < 0) {
1979  ix += hl->count();
1980  }
1981  if (ix < 0 || ix >= hl->count()) {
1982  PyErr_Format(PyExc_IndexError, "%s", hoc_object_name(po->ho_));
1983  return nullptr;
1984  } else {
1985  return nrnpy_ho2po(hl->object(ix));
1986  }
1987  } else {
1988  PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
1989  return nullptr;
1990  }
1991  }
1992  if (!po->sym_) {
1993  // printf("unsubscriptable %s %d type=%d\n", hoc_object_name(po->ho_), ix,
1994  // po->type_);
1995  PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
1996  return nullptr;
1997  } else if (po->sym_->type == TEMPLATE) {
1998  hoc_Item *q, *ql = po->sym_->u.ctemplate->olist;
1999  Object* ob;
2000  ITERATE(q, ql) {
2001  ob = OBJ(q);
2002  if (ob->index == ix) {
2003  return nrnpy_ho2po(ob);
2004  }
2005  }
2006  PyErr_Format(PyExc_IndexError, "%s[%ld] instance does not exist", po->sym_->name, ix);
2007  return nullptr;
2008  }
2009  if (po->type_ != PyHoc::HocArray && po->type_ != PyHoc::HocArrayIncomplete) {
2010  PyErr_Format(PyExc_TypeError, "unsubscriptable object, type %d\n", po->type_);
2011  return nullptr;
2012  }
2013  Arrayinfo* a = hocobj_aray(po->sym_, po->ho_);
2014  if (araychk(a, po, ix)) {
2015  return nullptr;
2016  }
2017 
2018  nb::object result;
2019  if (a->nsub - 1 > po->nindex_) { // another intermediate
2020  result = nb::steal((PyObject*) intermediate(po, po->sym_, ix));
2021  } else { // ready to evaluate
2022  if (po->ho_) {
2023  eval_component(po, ix);
2024  if (po->sym_->type == SECTION || po->sym_->type == SECTIONREF) {
2025  section_object_seen = 0;
2026  result = nb::steal(nrnpy_cas(0, 0));
2027  nrn_popsec();
2028  return result.release().ptr();
2029  } else {
2030  if (po->type_ == PyHoc::HocArrayIncomplete) {
2031  result = nb::steal(nrn_hocobj_ptr(hoc_pxpop()));
2032  } else {
2033  result = nb::steal(nrnpy_hoc_pop("po->ho_ hocobj_getitem"));
2034  }
2035  }
2036  } else { // must be a top level intermediate
2037  auto interp = HocTopContextManager();
2038  switch (po->sym_->type) {
2039  case VAR:
2040  hocobj_pushtop(po, po->sym_, ix);
2041  if (hoc_evalpointer_err()) {
2042  --po->nindex_;
2043  return nullptr;
2044  }
2045  --po->nindex_;
2046  if (po->type_ == PyHoc::HocArrayIncomplete) {
2047  assert(!po->u.px_);
2048  result = nb::steal(nrn_hocobj_ptr(hoc_pxpop()));
2049  } else {
2050  result = nb::steal(Py_BuildValue("d", *hoc_pxpop()));
2051  }
2052  break;
2053  case OBJECTVAR:
2054  hocobj_pushtop(po, 0, ix);
2055  if (hocobj_objectvar(po->sym_)) {
2056  break;
2057  }
2058  --po->nindex_;
2059  result = nb::steal(nrnpy_ho2po(*hoc_objpop()));
2060  break;
2061  case SECTION:
2062  hocobj_pushtop(po, 0, ix);
2063  result = nb::steal(hocobj_getsec(po->sym_));
2064  --po->nindex_;
2065  break;
2066  }
2067  }
2068  }
2069  return result.release().ptr();
2070 }
2071 
2072 // Returns a new reference.
2074  // Non slice indexing still uses original function
2075  if (!PySlice_Check(slice)) {
2076  return hocobj_getitem(self, PyLong_AsLong(slice));
2077  }
2078  auto* po = (PyHocObject*) self;
2079  if (!po->ho_) {
2080  PyErr_SetString(PyExc_TypeError, "Obj is NULL");
2081  return nullptr;
2082  }
2083  if (po->type_ != PyHoc::HocObject || po->ho_->ctemplate != hoc_vec_template_) {
2084  PyErr_SetString(PyExc_TypeError, "sequence index must be integer, not 'slice'");
2085  return nullptr;
2086  }
2087  auto* v = (Vect*) po->ho_->u.this_pointer;
2088  Py_ssize_t start = 0;
2089  Py_ssize_t end = 0;
2090  Py_ssize_t step = 0;
2091  Py_ssize_t slicelen = 0;
2092  Py_ssize_t len = vector_capacity(v);
2093  PySlice_GetIndicesEx(slice, len, &start, &end, &step, &slicelen);
2094  if (step == 0) {
2095  PyErr_SetString(PyExc_ValueError, "slice step cannot be zero");
2096  return nullptr;
2097  }
2098  Object** obj = new_vect(v, slicelen, start, step);
2099  return nrnpy_ho2po(*obj);
2100 }
2101 
2102 static int hocobj_setitem(PyObject* self, Py_ssize_t i, PyObject* arg) {
2103  int err = -1;
2104  PyHocObject* po = (PyHocObject*) self;
2105  if (po->type_ > PyHoc::HocArray) {
2106  if (po->type_ == PyHoc::HocArrayIncomplete) {
2107  PyErr_SetString(PyExc_TypeError, "incomplete hoc pointer");
2108  return -1;
2109  }
2110  if (i != 0 && po->type_ != PyHoc::HocScalarPtr) {
2111  PyErr_SetString(PyExc_IndexError, "index for hoc ref must be 0");
2112  return -1;
2113  }
2114  if (po->type_ == PyHoc::HocScalarPtr) {
2115  try {
2116  auto const h = po->u.px_.next_array_element(i);
2117  if (nrn_chk_data_handle(h)) {
2118  PyArg_Parse(arg, "d", static_cast<double const*>(h));
2119  } else {
2120  return -1;
2121  }
2122  } catch (std::exception const& e) {
2123  // next_array_element throws if ix is invalid
2124  PyErr_SetString(PyExc_IndexError, e.what());
2125  return -1;
2126  }
2127  } else if (po->type_ == PyHoc::HocRefNum) {
2128  PyArg_Parse(arg, "d", &po->u.x_);
2129  } else if (po->type_ == PyHoc::HocRefStr) {
2130  char* ts;
2131  PyArg_Parse(arg, "s", &ts);
2132  hoc_assign_str(&po->u.s_, ts);
2133  } else if (po->type_ == PyHoc::HocRefPStr) {
2134  char* ts;
2135  PyArg_Parse(arg, "s", &ts);
2136  hoc_assign_str(po->u.pstr_, ts);
2137  } else {
2138  PyObject* tp;
2139  PyArg_Parse(arg, "O", &tp);
2140  po->u.ho_ = nrnpy_po2ho(tp);
2141  }
2142  return 0;
2143  }
2144  if (po->ho_) {
2145  if (po->ho_->ctemplate == hoc_vec_template_) {
2146  Vect* vec = (Vect*) po->ho_->u.this_pointer;
2147  int vec_size = vector_capacity(vec);
2148  // allow Python style negative indices
2149  if (i < 0) {
2150  i += vec_size;
2151  }
2152  if (i >= vec_size || i < 0) {
2153  PyErr_SetString(PyExc_IndexError, "index out of bounds");
2154  return -1;
2155  }
2156  PyArg_Parse(arg, "d", vector_vec(vec) + i);
2157  return 0;
2158  }
2159  }
2160  if (!po->sym_ || po->type_ != PyHoc::HocArray) {
2161  PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
2162  return -1;
2163  }
2164  Arrayinfo* a = hocobj_aray(po->sym_, po->ho_);
2165  if (!a || a->nsub - 1 != po->nindex_) {
2166  int nsub = a ? a->nsub : 0;
2167  std::ostringstream oss;
2168  oss << "Wrong number of subscripts, hoc var " << po->sym_->name << " has " << nsub
2169  << " but compiled with " << (po->nindex_ + 1);
2170  PyErr_SetString(PyExc_TypeError, oss.str().c_str());
2171  return -1;
2172  }
2173  if (araychk(a, po, i)) {
2174  return -1;
2175  }
2176  if (po->ho_) {
2177  if (po->sym_->type == SECTION) {
2178  PyErr_SetString(PyExc_TypeError, "not assignable");
2179  } else {
2180  eval_component(po, i);
2181  err = set_final_from_stk(arg);
2182  }
2183  } else { // must be a top level intermediate
2184  auto interp = HocTopContextManager();
2185  switch (po->sym_->type) {
2186  case VAR:
2187  hocobj_pushtop(po, po->sym_, i);
2188  if (hoc_evalpointer_err()) {
2189  --po->nindex_;
2190  return -1;
2191  }
2192  --po->nindex_;
2193  err = PyArg_Parse(arg, "d", hoc_pxpop()) != 1;
2194  break;
2195  case OBJECTVAR: {
2196  hocobj_pushtop(po, 0, i);
2197  err = hocobj_objectvar(po->sym_);
2198  if (err) {
2199  break; // can't reach because of earlier array_chk
2200  }
2201  --po->nindex_;
2202  Object** op;
2203  op = hoc_objpop();
2204  PyObject* pyo;
2205  if (PyArg_Parse(arg, "O", &pyo) == 1) {
2206  Object* ho = nrnpy_po2ho(pyo);
2207  hoc_obj_unref(*op);
2208  *op = ho;
2209  err = 0;
2210  } else {
2211  err = 1;
2212  }
2213  break;
2214  }
2215  default:
2216  PyErr_SetString(PyExc_TypeError, "not assignable");
2217  break;
2218  }
2219  }
2220  return err;
2221 }
2222 
2223 static int hocobj_slice_setitem(PyObject* self, PyObject* slice, PyObject* arg) {
2224  // Non slice indexing still uses original function
2225  if (!PySlice_Check(slice)) {
2226  return hocobj_setitem(self, PyLong_AsLong(slice), arg);
2227  }
2228  auto* po = (PyHocObject*) self;
2229  if (!po->ho_) {
2230  PyErr_SetString(PyExc_TypeError, "Obj is NULL");
2231  return -1;
2232  }
2233  if (po->type_ != PyHoc::HocObject || po->ho_->ctemplate != hoc_vec_template_) {
2234  PyErr_SetString(PyExc_TypeError, "sequence index must be integer, not 'slice'");
2235  return -1;
2236  }
2237  auto v = (Vect*) po->ho_->u.this_pointer;
2238  Py_ssize_t start = 0;
2239  Py_ssize_t end = 0;
2240  Py_ssize_t step = 0;
2241  Py_ssize_t slicelen = 0;
2242  Py_ssize_t cap = vector_capacity(v);
2243  PySlice_GetIndicesEx(slice, cap, &start, &end, &step, &slicelen);
2244  // Slice index assignment requires a list of the same size as the slice
2245  auto iter = nb::steal(PyObject_GetIter(arg));
2246  if (!iter) {
2247  PyErr_SetString(PyExc_TypeError, "can only assign an iterable");
2248  return -1;
2249  }
2250  for (Py_ssize_t i = 0; i < slicelen; ++i) {
2251  auto val = nb::steal(PyIter_Next(iter.ptr()));
2252  if (!val) {
2253  PyErr_SetString(PyExc_IndexError,
2254  "iterable object must have the same length as slice (it's too short)");
2255  return -1;
2256  }
2257  PyArg_Parse(val.ptr(), "d", vector_vec(v) + (i * step + start));
2258  }
2259  auto val = nb::steal(PyIter_Next(iter.ptr()));
2260  if (val) {
2261  PyErr_SetString(PyExc_IndexError,
2262  "iterable object must have the same length as slice (it's too long)");
2263  return -1;
2264  }
2265  return 0;
2266 }
2267 
2268 static PyObject* mkref(PyObject* self, PyObject* args) {
2269  PyObject* pa;
2270  if (PyArg_ParseTuple(args, "O", &pa) == 1) {
2271  auto result_guard = nb::steal(hocobj_new(hocobject_type, 0, 0));
2272  PyHocObject* result = (PyHocObject*) result_guard.ptr();
2273  if (nrnpy_numbercheck(pa)) {
2274  result->type_ = PyHoc::HocRefNum;
2275  auto pn = nb::steal(PyNumber_Float(pa));
2276  result->u.x_ = PyFloat_AsDouble(pn.ptr());
2277  } else if (is_python_string(pa)) {
2278  result->type_ = PyHoc::HocRefStr;
2279  result->u.s_ = 0;
2280  auto str = Py2NRNString::as_ascii(pa);
2281  if (!str.is_valid()) {
2282  Py2NRNString::set_pyerr(PyExc_TypeError,
2283  "string arg must have only ascii characters");
2284  return nullptr;
2285  }
2286  char* cpa = str.c_str();
2287  hoc_assign_str(&result->u.s_, cpa);
2288  } else {
2289  result->type_ = PyHoc::HocRefObj;
2290  result->u.ho_ = nrnpy_po2ho(pa);
2291  }
2292  return result_guard.release().ptr();
2293  }
2294  PyErr_SetString(PyExc_TypeError, "single arg must be number, string, or Object");
2295  return nullptr;
2296 }
2297 
2298 static PyObject* mkref_safe(PyObject* self, PyObject* args) {
2299  return nrn::convert_cxx_exceptions(mkref, self, args);
2300 }
2301 
2302 // Returns a new reference.
2303 static PyObject* cpp2refstr(char** cpp) {
2304  // If cpp is from a hoc_temp_charptr (see src/oc/code.cpp) then create a
2305  // HocRefStr and copy *cpp. Otherwise, assume it is from a hoc strdef
2306  // or a HocRefStr which is persistent over the life time of this returned
2307  // PyObject so that it is safe to create a HocRefPStr such that
2308  // u.pstr_ = cpp and it is not needed
2309  // for the HocRefPStr destructor to delete either u.pstr_ or *u.pstr_.
2310 
2311  assert(cpp && *cpp); // not really sure about the *cpp
2312  auto result_guard = nb::steal(hocobj_new(hocobject_type, 0, 0));
2313  auto* result = (PyHocObject*) result_guard.ptr();
2314  if (hoc_is_temp_charptr(cpp)) { // return HocRefStr HocObject.
2315  result->type_ = PyHoc::HocRefStr;
2316  result->u.s_ = 0;
2317  hoc_assign_str(&result->u.s_, *cpp);
2318  } else {
2319  result->type_ = PyHoc::HocRefPStr;
2320  result->u.pstr_ = cpp;
2321  }
2322  return result_guard.release().ptr();
2323 }
2324 
2325 static PyObject* setpointer(PyObject* self, PyObject* args) {
2326  PyObject *ref, *name, *pp;
2327  if (PyArg_ParseTuple(args, "O!OO", hocobject_type, &ref, &name, &pp) == 1) {
2328  PyHocObject* href = (PyHocObject*) ref;
2329  if (href->type_ != PyHoc::HocScalarPtr) {
2330  goto done;
2331  }
2333  if (PyObject_TypeCheck(pp, hocobject_type)) {
2334  PyHocObject* hpp = (PyHocObject*) pp;
2335  if (hpp->type_ != PyHoc::HocObject) {
2336  goto done;
2337  }
2338  auto str = Py2NRNString::as_ascii(name);
2339  char* n = str.c_str();
2340  if (!str.is_valid()) {
2341  Py2NRNString::set_pyerr(PyExc_TypeError,
2342  "POINTER name can contain only ascii characters");
2343  return nullptr;
2344  }
2345  Symbol* sym = getsym(n, hpp->ho_, 0);
2346  if (!sym || sym->type != RANGEVAR || sym->subtype != NRNPOINTER) {
2347  goto done;
2348  }
2349  Prop* prop = ob2pntproc_0(hpp->ho_)->prop;
2350  if (!prop) {
2351  PyErr_SetString(PyExc_TypeError, "Point_process not located in a section");
2352  return nullptr;
2353  }
2354  gh = &(prop->dparam[sym->u.rng.index]);
2355  } else {
2356  gh = nrnpy_setpointer_helper(name, pp);
2357  if (!gh) {
2358  goto done;
2359  }
2360  }
2362  Py_RETURN_NONE;
2363  }
2364 done:
2365  PyErr_SetString(PyExc_TypeError,
2366  "setpointer(_ref_hocvar, 'POINTER_name', point_process or "
2367  "nrn.Mechanism))");
2368  return nullptr;
2369 }
2370 
2371 
2373  return nrn::convert_cxx_exceptions(setpointer, self, args);
2374 }
2375 
2376 static PyObject* hocobj_vptr(PyObject* pself, PyObject* args) {
2377  Object* ho = ((PyHocObject*) pself)->ho_;
2378  PyObject* po = nullptr;
2379  if (ho) {
2380  po = Py_BuildValue("O", PyLong_FromVoidPtr(ho));
2381  }
2382  if (!po) {
2383  PyErr_SetString(PyExc_TypeError, "HocObject does not wrap a Hoc Object");
2384  }
2385  return po;
2386 }
2387 
2388 static PyObject* hocobj_vptr_safe(PyObject* pself, PyObject* args) {
2389  return nrn::convert_cxx_exceptions(hocobj_vptr, pself, args);
2390 }
2391 
2392 static long hocobj_hash(PyHocObject* self) {
2393  return castptr2long self->ho_;
2394 }
2395 
2396 PyObject* nrn_ptr_richcmp(void* self_ptr, void* other_ptr, int op) {
2397  bool result = false;
2398  switch (op) {
2399  case Py_LT:
2400  result = self_ptr < other_ptr;
2401  break;
2402  case Py_LE:
2403  result = self_ptr <= other_ptr;
2404  break;
2405  case Py_EQ:
2406  result = self_ptr == other_ptr;
2407  break;
2408  case Py_NE:
2409  result = self_ptr != other_ptr;
2410  break;
2411  case Py_GT:
2412  result = self_ptr > other_ptr;
2413  break;
2414  case Py_GE:
2415  result = self_ptr >= other_ptr;
2416  break;
2417  }
2418  return PyBool_FromLong(result);
2419 }
2420 
2421 // TODO: unfortunately, this duplicates code from hocobj_same; consolidate?
2422 static PyObject* hocobj_richcmp(PyHocObject* self, PyObject* other, int op) {
2423  auto* pyhoc_other = reinterpret_cast<PyHocObject*>(other);
2424  void* self_ptr = self->ho_;
2425  void* other_ptr = other;
2426  bool are_equal = true;
2427  if (PyObject_TypeCheck(other, hocobject_type)) {
2428  if (pyhoc_other->type_ == self->type_) {
2429  switch (self->type_) {
2430  case PyHoc::HocRefNum:
2431  case PyHoc::HocRefStr:
2432  case PyHoc::HocRefObj:
2433  case PyHoc::HocRefPStr:
2434  /* only same objects can point to same h.ref */
2435  self_ptr = (void*) self;
2436  break;
2437  case PyHoc::HocFunction:
2438  if (self->ho_ != pyhoc_other->ho_) {
2439  if (op == Py_NE) {
2440  Py_RETURN_TRUE;
2441  } else if (op == Py_EQ) {
2442  Py_RETURN_FALSE;
2443  }
2444  /* different classes, comparing < or > doesn't make sense */
2445  PyErr_SetString(PyExc_TypeError, "this comparison is undefined");
2446  return nullptr;
2447  }
2448  self_ptr = self->sym_;
2449  other_ptr = pyhoc_other->sym_;
2450  break;
2451  case PyHoc::HocScalarPtr:
2452  // this seems rather dubious
2453  self_ptr = static_cast<double*>(self->u.px_);
2454  other_ptr = static_cast<double*>(pyhoc_other->u.px_);
2455  break;
2457  case PyHoc::HocArray:
2458  if (op != Py_EQ && op != Py_NE) {
2459  /* comparing partial arrays doesn't make sense */
2460  PyErr_SetString(PyExc_TypeError, "this comparison is undefined");
2461  return nullptr;
2462  }
2463  if (self->ho_ != pyhoc_other->ho_) {
2464  /* different objects */
2465  other_ptr = pyhoc_other->ho_;
2466  break;
2467  }
2468  if (self->nindex_ != pyhoc_other->nindex_ || self->sym_ != pyhoc_other->sym_) {
2469  return PyBool_FromLong(op == Py_NE);
2470  }
2471  for (int i = 0; i < self->nindex_; i++) {
2472  if (self->indices_[i] != pyhoc_other->indices_[i]) {
2473  are_equal = false;
2474  }
2475  }
2476  return PyBool_FromLong(are_equal == (op == Py_EQ));
2477  default:
2478  other_ptr = pyhoc_other->ho_;
2479  }
2480  } else {
2481  if (op == Py_EQ) {
2482  Py_RETURN_FALSE;
2483  } else if (op == Py_NE) {
2484  Py_RETURN_TRUE;
2485  }
2486  /* different NEURON object types are incomperable besides for (in)equality */
2487  PyErr_SetString(PyExc_TypeError, "this comparison is undefined");
2488  return nullptr;
2489  }
2490  }
2491  return nrn_ptr_richcmp(self_ptr, other_ptr, op);
2492 }
2493 
2494 static PyObject* hocobj_same(PyHocObject* pself, PyObject* args) {
2495  PyObject* po;
2496  if (PyArg_ParseTuple(args, "O", &po)) {
2497  return PyBool_FromLong(PyObject_TypeCheck(po, hocobject_type) &&
2498  ((PyHocObject*) po)->ho_ == pself->ho_);
2499  }
2500  return nullptr;
2501 }
2502 
2504  return nrn::convert_cxx_exceptions(hocobj_same, pself, args);
2505 }
2506 
2507 static char* double_array_interface(PyObject* po, long& stride) {
2508  void* data = 0;
2509  if (PyObject_HasAttrString(po, "__array_interface__")) {
2510  auto ai = nb::steal(PyObject_GetAttrString(po, "__array_interface__"));
2511  auto typestr = Py2NRNString::as_ascii(PyDict_GetItemString(ai.ptr(), "typestr"));
2512  if (strcmp(typestr.c_str(), array_interface_typestr) == 0) {
2513  data = PyLong_AsVoidPtr(PyTuple_GetItem(PyDict_GetItemString(ai.ptr(), "data"), 0));
2514  // printf("double_array_interface idata = %ld\n", idata);
2515  if (PyErr_Occurred()) {
2516  data = 0;
2517  }
2518  PyObject* pstride = PyDict_GetItemString(ai.ptr(), "strides");
2519  if (pstride == Py_None) {
2520  stride = 8;
2521  } else if (PyTuple_Check(pstride)) {
2522  if (PyTuple_Size(pstride) == 1) {
2523  PyObject* psize = PyTuple_GetItem(pstride, 0);
2524  if (PyLong_Check(psize)) {
2525  stride = PyLong_AsLong(psize);
2526  } else if (PyInt_Check(psize)) {
2527  stride = PyInt_AS_LONG(psize);
2528 
2529  } else {
2530  PyErr_SetString(PyExc_TypeError,
2531  "array_interface stride element of invalid type.");
2532  data = 0;
2533  }
2534 
2535  } else
2536  data = 0; // don't handle >1 dimensions
2537  } else {
2538  PyErr_SetString(PyExc_TypeError, "array_interface stride object of invalid type.");
2539  data = 0;
2540  }
2541  }
2542  }
2543  return static_cast<char*>(data);
2544 }
2545 
2546 
2547 inline double pyobj_to_double_or_fail(PyObject* obj, long obj_id) {
2548  if (!PyNumber_Check(obj)) {
2549  char buf[50];
2550  Sprintf(buf, "item %d is not a valid number", obj_id);
2551  hoc_execerror(buf, 0);
2552  }
2553  return PyFloat_AsDouble(obj);
2554 }
2555 
2557  Vect* hv = (Vect*) v;
2558  // printf("%s.from_array\n", hoc_object_name(hv->obj_));
2559  Object* ho = *hoc_objgetarg(1);
2560  if (ho->ctemplate->sym != nrnpy_pyobj_sym_) {
2561  hoc_execerror(hoc_object_name(ho), " is not a PythonObject");
2562  }
2563  // We borrow the list, so there's an INCREF and all items are alive
2564  nb::object po = nb::borrow(nrnpy_hoc2pyobject(ho));
2565 
2566  // If it's not a sequence, try iterating over it
2567  if (!PySequence_Check(po.ptr())) {
2568  if (!PyIter_Check(po.ptr())) {
2570  " does not support the Python Sequence or Iterator protocol");
2571  }
2572  long i = 0;
2573  for (nb::handle item: po) {
2574  hv->push_back(pyobj_to_double_or_fail(item.ptr(), i));
2575  i++;
2576  }
2577  return hv;
2578  }
2579 
2580  int size = nb::len(po);
2581  hv->resize(size);
2582  double* x = vector_vec(hv);
2583 
2584  // If sequence provides __array_interface__ use it
2585  long stride;
2586  char* array_interface_ptr = double_array_interface(po.ptr(), stride);
2587  if (array_interface_ptr) {
2588  for (int i = 0, j = 0; i < size; ++i, j += stride) {
2589  x[i] = *(double*) (array_interface_ptr + j);
2590  }
2591  return hv;
2592  }
2593 
2594  // If it's a normal list, convert to the good type so operator[] is more efficient
2595  if (PyList_Check(po.ptr())) {
2596  nb::list list_obj{std::move(po)};
2597  for (long i = 0; i < size; ++i) {
2598  x[i] = pyobj_to_double_or_fail(list_obj[i].ptr(), i);
2599  }
2600  } else {
2601  for (long i = 0; i < size; ++i) {
2602  x[i] = pyobj_to_double_or_fail(po[i].ptr(), i);
2603  }
2604  }
2605  return hv;
2606 }
2607 
2608 static PyObject* (*vec_as_numpy)(int, double*);
2609 extern "C" NRN_EXPORT int nrnpy_set_vec_as_numpy(PyObject* (*p)(int, double*) ) {
2610  vec_as_numpy = p;
2611  return 0;
2612 }
2613 
2614 static PyObject* store_savestate_ = nullptr;
2615 static PyObject* restore_savestate_ = nullptr;
2616 
2617 
2618 static void nrnpy_store_savestate_(char** save_data, uint64_t* save_data_size) {
2619  if (store_savestate_) {
2620  // call store_savestate_ with no arguments to get a byte array that we can write out
2621  nb::bytearray result(PyObject_CallNoArgs(store_savestate_));
2622  if (!result) {
2623  hoc_execerror("SaveState:", "Data store failure.");
2624  }
2625  // free any old data and make a copy
2626  if (*save_data) {
2627  delete[](*save_data);
2628  }
2629  *save_data_size = result.size();
2630  *save_data = new char[*save_data_size];
2631  memcpy(*save_data, result.c_str(), *save_data_size);
2632  } else {
2633  *save_data_size = 0;
2634  }
2635 }
2636 
2637 static void nrnpy_restore_savestate_(int64_t size, char* data) {
2638  if (restore_savestate_) {
2639  nb::bytearray py_data(data, size);
2640  if (!py_data) {
2641  hoc_execerror("SaveState:", "Data restore failure.");
2642  }
2643  auto result = nb::steal(PyObject_CallOneArg(restore_savestate_, py_data.ptr()));
2644  if (!result) {
2645  hoc_execerror("SaveState:", "Data restore failure.");
2646  }
2647  } else {
2648  if (size) {
2649  hoc_execerror("SaveState:", "Missing data restore function.");
2650  }
2651  }
2652 }
2653 
2655  PyObject* plotshape_plot0,
2656  PyObject* get_mech_object_0,
2657  PyObject* store_savestate,
2658  PyObject* restore_savestate) {
2659  rvp_plot = rvp_plot0;
2660  plotshape_plot = plotshape_plot0;
2661  get_mech_object_ = get_mech_object_0;
2662  store_savestate_ = store_savestate;
2663  restore_savestate_ = restore_savestate;
2666  return 0;
2667 }
2668 
2669 static PyObject* gui_callback = nullptr;
2670 extern "C" NRN_EXPORT int nrnpy_set_gui_callback(PyObject* new_gui_callback) {
2671  gui_callback = new_gui_callback;
2672  return 0;
2673 }
2674 
2675 static double object_to_double_(Object* obj) {
2676  auto pyobj = nb::steal(nrnpy_ho2po(obj));
2677  return PyFloat_AsDouble(pyobj.ptr());
2678 }
2679 
2680 // Returns a new reference.
2681 static void* nrnpy_get_pyobj_(Object* obj) {
2682  // returns something wrapping a PyObject if it is a PyObject else NULL
2683  if (obj->ctemplate->sym == nrnpy_pyobj_sym_) {
2684  return (void*) nrnpy_ho2po(obj);
2685  }
2686  return nullptr;
2687 }
2688 
2689 static void nrnpy_decref_(void* pyobj) {
2690  // note: this assumes that pyobj is really a PyObject
2691  if (pyobj) {
2692  Py_DECREF((PyObject*) pyobj);
2693  }
2694 }
2695 
2696 static PyObject* gui_helper_3_helper_(const char* name, Object* obj, int handle_strptr) {
2697  int narg = 1;
2698  while (ifarg(narg)) {
2699  narg++;
2700  }
2701  narg--;
2702  auto args = nb::steal(PyTuple_New(narg + 3));
2703  auto pyname = nb::steal(PyString_FromString(name));
2704  PyTuple_SetItem(args.ptr(), 0, pyname.release().ptr());
2705  for (int iarg = 0; iarg < narg; iarg++) {
2706  const int iiarg = iarg + 1;
2707  if (hoc_is_object_arg(iiarg)) {
2708  auto active_obj = nb::steal(nrnpy_ho2po(*hoc_objgetarg(iiarg)));
2709  PyTuple_SetItem(args.ptr(), iarg + 3, active_obj.release().ptr());
2710  } else if (hoc_is_pdouble_arg(iiarg)) {
2711  PyHocObject* ptr_nrn = (PyHocObject*) hocobj_new(hocobject_type, 0, 0);
2712  ptr_nrn->type_ = PyHoc::HocScalarPtr;
2713  ptr_nrn->u.px_ = hoc_hgetarg<double>(iiarg);
2714  PyObject* py_ptr = (PyObject*) ptr_nrn;
2715  Py_INCREF(py_ptr);
2716  PyTuple_SetItem(args.ptr(), iarg + 3, py_ptr);
2717  } else if (hoc_is_str_arg(iiarg)) {
2718  if (handle_strptr > 0) {
2719  char** str_arg = hoc_pgargstr(iiarg);
2720  PyObject* py_ptr = cpp2refstr(str_arg);
2721  Py_INCREF(py_ptr);
2722  PyTuple_SetItem(args.ptr(), iarg + 3, py_ptr);
2723  } else {
2724  auto py_str = nb::steal(PyString_FromString(gargstr(iiarg)));
2725  PyTuple_SetItem(args.ptr(), iarg + 3, py_str.release().ptr());
2726  }
2727  } else if (hoc_is_double_arg(iiarg)) {
2728  auto py_double = nb::steal(PyFloat_FromDouble(*getarg(iiarg)));
2729  PyTuple_SetItem(args.ptr(), iarg + 3, py_double.release().ptr());
2730  }
2731  }
2732  nb::object my_obj;
2733  if (obj) {
2734  // there's a problem with this: if obj is intrinisically a PyObject, then this is increasing
2735  // it's refcount and that's
2736  my_obj = nb::steal(nrnpy_ho2po(obj));
2737  } else {
2738  my_obj = nb::none();
2739  }
2740  PyTuple_SetItem(args.ptr(), 1, my_obj.release().ptr()); // steals a reference
2741  nb::object my_obj2;
2742  if (hoc_thisobject && name[0] != '~') {
2743  my_obj2 = nb::steal(nrnpy_ho2po(hoc_thisobject)); // in the case of a HOC object, such as
2744  // happens with List.browser, the ref
2745  // count will be 1
2746  } else {
2747  my_obj2 = nb::none();
2748  }
2749 
2750  PyTuple_SetItem(args.ptr(), 2, my_obj2.release().ptr()); // steals a reference to my_obj2
2751  auto po = nb::steal(PyObject_CallObject(gui_callback, args.ptr()));
2752  if (PyErr_Occurred()) {
2753  // if there was an error, display it and return 0.
2754  // It's not a great solution, but it beats segfaulting
2755  PyErr_Print();
2756  po = nb::steal(PyLong_FromLong(0));
2757  }
2758  return po.release().ptr();
2759 }
2760 
2761 static Object** gui_helper_3_(const char* name, Object* obj, int handle_strptr) {
2762  if (gui_callback) {
2763  auto po = nb::steal(gui_helper_3_helper_(name, obj, handle_strptr));
2764  // TODO: something that allows None (currently nrnpy_po2ho returns NULL if po == Py_None)
2765  Object* ho = nrnpy_po2ho(po.release().ptr());
2766  if (ho) {
2767  --ho->refcount;
2768  }
2769  return hoc_temp_objptr(ho);
2770  }
2771  return nullptr;
2772 }
2773 
2774 static char** gui_helper_3_str_(const char* name, Object* obj, int handle_strptr) {
2775  if (gui_callback) {
2776  auto po = nb::steal(gui_helper_3_helper_(name, obj, handle_strptr));
2777  char** ts = hoc_temp_charptr();
2778  *ts = Py2NRNString::as_ascii(po.ptr()).release();
2779  // TODO: is there a memory leak here? do I need to: s2free.push_back(*ts);
2780  return ts;
2781  }
2782  return nullptr;
2783 }
2784 
2785 
2786 static Object** gui_helper_(const char* name, Object* obj) {
2787  return gui_helper_3_(name, obj, 0);
2788 }
2789 
2790 static Object** vec_as_numpy_helper(int size, double* data) {
2791  if (vec_as_numpy) {
2792  auto po = nb::steal((*vec_as_numpy)(size, data));
2793  if (!po.is_none()) {
2794  Object* ho = nrnpy_po2ho(po.release().ptr());
2795  --ho->refcount;
2796  return hoc_temp_objptr(ho);
2797  }
2798  }
2799  hoc_execerror("Vector.as_numpy() error", 0);
2800  return nullptr;
2801 }
2802 
2803 static Object** nrnpy_vec_to_python(void* v) {
2804  Vect* hv = (Vect*) v;
2805  int size = hv->size();
2806  double* x = vector_vec(hv);
2807  // printf("%s.to_array\n", hoc_object_name(hv->obj_));
2808  nb::object po;
2809  Object* ho = nullptr;
2810 
2811  // as_numpy_array=True is the case where this function is being called by the
2812  // ivocvect __array__ member
2813  // as such perhaps we should check here that no arguments were passed
2814  // although this should be the case unless the function is erroneously called
2815  // by the user.
2816 
2817  if (ifarg(1)) {
2818  ho = *hoc_objgetarg(1);
2819  if (ho->ctemplate->sym != nrnpy_pyobj_sym_) {
2820  hoc_execerror(hoc_object_name(ho), " is not a PythonObject");
2821  }
2822  po = nb::borrow(nrnpy_hoc2pyobject(ho));
2823  if (!PySequence_Check(po.ptr())) {
2824  hoc_execerror(hoc_object_name(ho), " is not a Python Sequence");
2825  }
2826  if (size != PySequence_Size(po.ptr())) {
2827  hoc_execerror(hoc_object_name(ho), "Python Sequence not same size as Vector");
2828  }
2829  } else {
2830  if (!(po = nb::steal(PyList_New(size)))) {
2831  hoc_execerror("Could not create new Python List with correct size.", 0);
2832  }
2833 
2834  ho = nrnpy_po2ho(po.ptr());
2835  --ho->refcount;
2836  }
2837  // printf("size = %d\n", size);
2838  long stride;
2839  char* y = double_array_interface(po.ptr(), stride);
2840  if (y) {
2841  for (int i = 0, j = 0; i < size; ++i, j += stride) {
2842  *(double*) (y + j) = x[i];
2843  }
2844  } else if (PyList_Check(po.ptr())) { // PySequence_SetItem does DECREF of old items
2845  for (int i = 0; i < size; ++i) {
2846  auto pn = nb::steal(PyFloat_FromDouble(x[i]));
2847  if (!pn || PyList_SetItem(po.ptr(), i, pn.release().ptr()) == -1) {
2848  char buf[50];
2849  Sprintf(buf, "%d of %d", i, size);
2850  hoc_execerror("Could not set a Python Sequence item", buf);
2851  }
2852  }
2853  } else { // assume PySequence_SetItem works
2854  for (int i = 0; i < size; ++i) {
2855  auto pn = nb::steal(PyFloat_FromDouble(x[i]));
2856  if (!pn || PySequence_SetItem(po.ptr(), i, pn.ptr()) == -1) {
2857  char buf[50];
2858  Sprintf(buf, "%d of %d", i, size);
2859  hoc_execerror("Could not set a Python Sequence item", buf);
2860  }
2861  }
2862  }
2863 
2864  // The HOC reference throughout most of this function is 0 (preventing the need to decrement on
2865  // error paths).
2866  //
2867  // Because the dtor of `po` will decrease the reference count of the `ho` (if it contains one),
2868  // the order in which the decrements happen matter, or else `ho` can be deallocated.
2869  //
2870  // To avoid the situation described, we must briefly acquire a reference to the HOC object (by
2871  // bumping its reference count) and then decrement the reference count again.
2872  ++ho->refcount;
2873  po.dec_ref();
2874  po.release();
2875  --ho->refcount;
2876  return hoc_temp_objptr(ho);
2877 }
2878 
2880  if (obj) {
2881  auto py_obj = nb::steal(nrnpy_ho2po(obj));
2882  auto result = nb::steal(
2883  PyObject_CallFunctionObjArgs(nrnpy_rvp_pyobj_callback, py_obj.ptr(), nullptr));
2884  return nrnpy_po2ho(result.ptr());
2885  } else {
2886  return 0;
2887  }
2888 }
2889 
2890 
2892  nanobind::gil_scoped_acquire lock{};
2893  PyHocObject* pho = (PyHocObject*) sp;
2894  ShapePlotInterface* spi;
2895  if (!is_obj_type(pho->ho_, "PlotShape")) {
2896  PyErr_SetString(PyExc_TypeError, "get_plotshape_variable only takes PlotShape objects");
2897  return nullptr;
2898  }
2899  void* that = pho->ho_->u.this_pointer;
2900 #if HAVE_IV
2901  if (hoc_usegui) {
2902  spi = ((ShapePlot*) that);
2903  } else {
2904  spi = ((ShapePlotData*) that);
2905  }
2906 #else
2907  spi = ((ShapePlotData*) that);
2908 #endif
2909  Object* sl = spi->neuron_section_list();
2910  auto py_sl = nb::steal(nrnpy_ho2po(sl));
2911  auto py_obj = nb::borrow((PyObject*) spi->varobj());
2912  if (!py_obj) {
2913  py_obj = nb::none();
2914  }
2915  // NOte: O increases the reference count; N does not
2916  return Py_BuildValue("sNffN",
2917  spi->varname(),
2918  py_obj.release().ptr(),
2919  spi->low(),
2920  spi->high(),
2921  py_sl.release().ptr());
2922 }
2923 
2924 // poorly follows __reduce__ and __setstate__
2925 // from numpy/core/src/multiarray/methods.c
2927  // printf("hocpickle_reduce\n");
2928  PyHocObject* pho = (PyHocObject*) self;
2929  if (!is_obj_type(pho->ho_, "Vector")) {
2930  PyErr_SetString(PyExc_TypeError, "HocObject: Only Vector instance can be pickled");
2931  return nullptr;
2932  }
2933  Vect* vec = (Vect*) pho->ho_->u.this_pointer;
2934 
2935  // neuron module has a _pkl method that returns h.Vector(0)
2936 
2937  nb::module_ mod = nb::module_::import_("neuron");
2938  if (!mod) {
2939  return nullptr;
2940  }
2941  nb::object obj = mod.attr("_pkl");
2942  if (!obj) {
2943  PyErr_SetString(PyExc_Exception, "neuron module has no _pkl method.");
2944  return nullptr;
2945  }
2946 
2947  // see numpy implementation if more ret[1] stuff needed in case we
2948  // pickle anything but a hoc Vector. I don't think ret[1] can be None.
2949 
2950  // Fill object's state. Tuple with 4 args:
2951  // pickle version, endianness sentinel,
2952  // vector size, string data
2953  //
2954  // To be able to read data on a system with different endianness, a sentinel is added, the
2955  // convention is that the value of the sentinel is `2.0` (when cast to a double). Therefore, if
2956  // the machine reads the sentinel and it's not `2.0` it know that it needs to swap the bytes of
2957  // all doubles in the payload.
2958  double x = 2.0;
2959  nb::bytes byte_order((const void*) (&x), sizeof(double));
2960  nb::bytes vec_data(vec->data(), vec->size() * sizeof(double));
2961  nb::tuple state = nb::make_tuple(1, byte_order, vec->size(), vec_data);
2962 
2963  return nb::make_tuple(obj, nb::make_tuple(0), state).release().ptr();
2964 }
2965 
2967  return nrn::convert_cxx_exceptions(hocpickle_reduce, self, args);
2968 }
2969 
2970 // following copied (except for nrn_need_byteswap line) from NEURON ivocvect.cpp
2971 #define BYTEHEADER \
2972  uint32_t _II__; \
2973  char* _IN__; \
2974  char _OUT__[16]; \
2975  int BYTESWAP_FLAG = 0;
2976 #define BYTESWAP(_X__, _TYPE__) \
2977  if (BYTESWAP_FLAG == 1) { \
2978  _IN__ = (char*) &(_X__); \
2979  for (_II__ = 0; _II__ < sizeof(_TYPE__); _II__++) { \
2980  _OUT__[_II__] = _IN__[sizeof(_TYPE__) - _II__ - 1]; \
2981  } \
2982  (_X__) = *((_TYPE__*) &_OUT__); \
2983  }
2984 
2986  BYTEHEADER
2987  int version = -1;
2988  int size = 0;
2989  nb::object endian_data;
2990  nb::object rawdata;
2991  PyHocObject* pho = (PyHocObject*) self;
2992  // printf("hocpickle_setstate %s\n", hoc_object_name(pho->ho_));
2993  Vect* vec = (Vect*) pho->ho_->u.this_pointer;
2994  {
2995  PyObject* pendian_data;
2996  PyObject* prawdata;
2997  if (!PyArg_ParseTuple(args, "(iOiO)", &version, &pendian_data, &size, &prawdata)) {
2998  return nullptr;
2999  }
3000 
3001  rawdata = nb::borrow(prawdata);
3002  endian_data = nb::borrow(pendian_data);
3003  }
3004  // printf("hocpickle version=%d size=%d\n", version, size);
3005  vector_resize(vec, size);
3006  if (!PyBytes_Check(rawdata.ptr()) || !PyBytes_Check(endian_data.ptr())) {
3007  PyErr_SetString(PyExc_TypeError, "pickle not returning string");
3008  return nullptr;
3009  }
3010  char* two;
3011  Py_ssize_t len;
3012  if (PyBytes_AsStringAndSize(endian_data.ptr(), &two, &len) < 0) {
3013  return nullptr;
3014  }
3015  if (len != sizeof(double)) {
3016  PyErr_SetString(PyExc_ValueError, "endian_data size is not sizeof(double)");
3017  return nullptr;
3018  }
3019  BYTESWAP_FLAG = 0;
3020  if (*((double*) two) != 2.0) {
3021  BYTESWAP_FLAG = 1;
3022  }
3023  // printf("byteswap = %d\n", BYTESWAP_FLAG);
3024  char* str;
3025  if (PyBytes_AsStringAndSize(rawdata.ptr(), &str, &len) < 0) {
3026  return nullptr;
3027  }
3028  if (len != Py_ssize_t(size * sizeof(double))) {
3029  PyErr_SetString(PyExc_ValueError, "buffer size does not match array size");
3030  return nullptr;
3031  }
3032  if (BYTESWAP_FLAG) {
3033  double* x = (double*) str;
3034  for (int i = 0; i < size; ++i) {
3035  BYTESWAP(x[i], double)
3036  }
3037  }
3038  memcpy((char*) vector_vec(vec), str, len);
3039 
3040  Py_RETURN_NONE;
3041 }
3042 
3044  return nrn::convert_cxx_exceptions(hocpickle_setstate, self, args);
3045 }
3046 
3047 static PyObject* libpython_path(PyObject* self, PyObject* args) {
3048 #if defined(HAVE_DLFCN_H) && !defined(MINGW)
3049  Dl_info info;
3050  int rval = dladdr((const void*) Py_Initialize, &info);
3051  if (!rval) {
3052  PyErr_SetString(PyExc_Exception,
3053  "dladdr: Py_Initialize could not be matched to a shared object");
3054  return nullptr;
3055  }
3056  if (!info.dli_fname) {
3057  PyErr_SetString(PyExc_Exception,
3058  "dladdr: No symbol matching Py_Initialize could be found.");
3059  return nullptr;
3060  }
3061  return Py_BuildValue("s", info.dli_fname);
3062 #else
3063  Py_RETURN_NONE;
3064 #endif
3065 }
3066 
3068  return nrn::convert_cxx_exceptions(libpython_path, self, args);
3069 }
3070 
3071 // available for every HocObject
3072 static PyMethodDef hocobj_methods[] = {
3073  {"baseattr", hocobj_baseattr_safe, METH_VARARGS, "To allow use of an overrided base method"},
3074  {"hocobjptr", hocobj_vptr_safe, METH_NOARGS, "Hoc Object pointer as a long int"},
3075  {"same",
3076  (PyCFunction) hocobj_same_safe,
3077  METH_VARARGS,
3078  "o1.same(o2) return True if o1 and o2 wrap the same internal HOC Object"},
3079  {"hname", hocobj_name_safe, METH_NOARGS, "More specific than __str__() or __attr__()."},
3080  {"__reduce__", hocpickle_reduce_safe, METH_VARARGS, "pickle interface"},
3081  {"__setstate__", hocpickle_setstate_safe, METH_VARARGS, "pickle interface"},
3082  {nullptr, nullptr, 0, nullptr}};
3083 
3084 // only for a HocTopLevelInterpreter type HocObject
3085 static PyMethodDef toplevel_methods[] = {
3086  {"ref", mkref_safe, METH_VARARGS, "Wrap to allow call by reference in a hoc function"},
3087  {"cas", nrnpy_cas_safe, METH_VARARGS, "Return the currently accessed section."},
3088  {"allsec", nrnpy_forall_safe, METH_VARARGS, "Return iterator over all sections."},
3089  {"Section",
3090  (PyCFunction) nrnpy_newsecobj_safe,
3091  METH_VARARGS | METH_KEYWORDS,
3092  "Return a new Section"},
3093  {"setpointer", setpointer_safe, METH_VARARGS, "Assign hoc variable address to NMODL POINTER"},
3094  {"libpython_path",
3096  METH_NOARGS,
3097  "Return full path to file that contains Py_Initialize()"},
3098  {nullptr, nullptr, 0, nullptr}};
3099 
3100 static void add2topdict(PyObject* dict) {
3101  for (PyMethodDef* meth = toplevel_methods; meth->ml_name; meth++) {
3102  int err;
3103  auto nn = nb::steal(Py_BuildValue("s", meth->ml_doc));
3104  if (!nn) {
3105  return;
3106  }
3107  err = PyDict_SetItemString(dict, meth->ml_name, nn.ptr());
3108  if (err) {
3109  return;
3110  }
3111  }
3112 }
3113 
3114 static PyObject* nrnpy_vec_math = nullptr;
3115 
3116 extern "C" NRN_EXPORT int nrnpy_vec_math_register(PyObject* callback) {
3117  nrnpy_vec_math = callback;
3118  return 0;
3119 }
3120 
3122  nrnpy_rvp_pyobj_callback = callback;
3123  return 0;
3124 }
3125 
3126 static bool pyobj_is_vector(PyObject* obj) {
3127  if (PyObject_TypeCheck(obj, hocobject_type)) {
3128  PyHocObject* obj_h = (PyHocObject*) obj;
3129  if (obj_h->type_ == PyHoc::HocObject) {
3130  // this is an object (e.g. Vector) not a function
3131  if (obj_h->ho_->ctemplate == hoc_vec_template_) {
3132  return true;
3133  }
3134  }
3135  }
3136  return false;
3137 }
3138 
3139 static PyObject* py_hocobj_math(const char* op, PyObject* obj1, PyObject* obj2) {
3140  bool potentially_valid = false;
3141  int reversed = 0;
3142  if (pyobj_is_vector(obj1)) {
3143  potentially_valid = true;
3144  } else if (pyobj_is_vector(obj2)) {
3145  potentially_valid = true;
3146  reversed = 1;
3147  }
3148  if (!potentially_valid) {
3149  Py_INCREF(Py_NotImplemented);
3150  return Py_NotImplemented;
3151  }
3152  char buf[8];
3153  return PyObject_CallFunction(nrnpy_vec_math, strcpy(buf, "siOO"), op, reversed, obj1, obj2);
3154 }
3155 
3156 static PyObject* py_hocobj_math_unary(const char* op, PyObject* obj) {
3157  if (pyobj_is_vector(obj)) {
3158  char buf[8];
3159  return PyObject_CallFunction(nrnpy_vec_math, strcpy(buf, "siO"), op, 2, obj);
3160  }
3161  Py_INCREF(Py_NotImplemented);
3162  return Py_NotImplemented;
3163 }
3164 
3165 static PyObject* py_hocobj_add(PyObject* obj1, PyObject* obj2) {
3166  return py_hocobj_math("add", obj1, obj2);
3167 }
3168 
3170  return py_hocobj_math_unary("uabs", obj);
3171 }
3172 
3174  return py_hocobj_math_unary("uneg", obj);
3175 }
3176 
3178  return py_hocobj_math_unary("upos", obj);
3179 }
3180 
3181 static PyObject* py_hocobj_sub(PyObject* obj1, PyObject* obj2) {
3182  return py_hocobj_math("sub", obj1, obj2);
3183 }
3184 
3185 static PyObject* py_hocobj_mul(PyObject* obj1, PyObject* obj2) {
3186  return py_hocobj_math("mul", obj1, obj2);
3187 }
3188 
3189 static PyObject* py_hocobj_div(PyObject* obj1, PyObject* obj2) {
3190  return py_hocobj_math("div", obj1, obj2);
3191 }
3192 
3193 #include "nrnpy_hoc.h"
3194 
3195 // Figure out the endian-ness of the system, and return
3196 // 0 (error), '<' (little endian) or '>' (big endian)
3198  char endian_character = 0;
3199 
3200  auto psys = nb::steal(PyImport_ImportModule("sys"));
3201  if (!psys) {
3202  PyErr_SetString(PyExc_ImportError, "Failed to import sys to determine system byteorder.");
3203  return 0;
3204  }
3205 
3206  auto pbo = nb::steal(PyObject_GetAttrString(psys.ptr(), "byteorder"));
3207  if (!pbo) {
3208  PyErr_SetString(PyExc_AttributeError, "sys module does not have attribute 'byteorder'!");
3209  return 0;
3210  }
3211 
3212  auto byteorder = Py2NRNString::as_ascii(pbo.ptr());
3213  if (!byteorder.is_valid()) {
3214  return 0;
3215  }
3216 
3217  if (strcmp(byteorder.c_str(), "little") == 0) {
3218  endian_character = '<';
3219  } else if (strcmp(byteorder.c_str(), "big") == 0) {
3220  endian_character = '>';
3221  } else {
3222  PyErr_SetString(PyExc_RuntimeError, "Unknown system native byteorder.");
3223  return 0;
3224  }
3225  return endian_character;
3226 }
3227 
3228 static void sectionlist_helper_(void* sl, Object* args) {
3229  if (!args || args->ctemplate->sym != nrnpy_pyobj_sym_) {
3230  hoc_execerror("argument must be a Python iterable", "");
3231  }
3232  PyObject* pargs = nrnpy_hoc2pyobject(args);
3233 
3234  auto iterator = nb::steal(PyObject_GetIter(pargs));
3235 
3236  if (!iterator) {
3237  PyErr_Clear();
3238  hoc_execerror("argument must be an iterable", "");
3239  }
3240 
3241  nb::object item;
3242  while ((item = nb::steal(PyIter_Next(iterator.ptr())))) {
3243  if (!PyObject_TypeCheck(item.ptr(), psection_type)) {
3244  hoc_execerror("iterable must contain only Section objects", 0);
3245  }
3246  NPySecObj* pysec = (NPySecObj*) item.ptr();
3247  lvappendsec_and_ref(sl, pysec->sec_);
3248  }
3249 
3250  if (PyErr_Occurred()) {
3251  PyErr_Clear();
3252  hoc_execerror("argument must be a Python iterable", "");
3253  }
3254 }
3255 
3256 /// value of neuron.coreneuron.enable as 0, 1 (-1 if error)
3257 extern int (*nrnpy_nrncore_enable_value_p_)();
3258 
3259 /// value of neuron.coreneuron.file_mode as 0, 1 (-1 if error)
3260 extern int (*nrnpy_nrncore_file_mode_value_p_)();
3261 
3262 /*
3263  * Helper function to inspect value of int/boolean option
3264  * under coreneuron module.
3265  *
3266  * \todo : seems like this could be generalized so that
3267  * additional cases would require less code.
3268  */
3269 static int get_nrncore_opt_value(const char* option) {
3270  PyObject* modules = PyImport_GetModuleDict();
3271  if (modules) {
3272  PyObject* module = PyDict_GetItemString(modules, "neuron.coreneuron");
3273  if (module) {
3274  auto val = nb::steal(PyObject_GetAttrString(module, option));
3275  if (val) {
3276  long enable = PyLong_AsLong(val.ptr());
3277  if (enable != -1) {
3278  return enable;
3279  }
3280  }
3281  }
3282  }
3283  if (PyErr_Occurred()) {
3284  PyErr_Print();
3285  return -1;
3286  }
3287  return 0;
3288 }
3289 
3290 /// return value of neuron.coreneuron.enable
3291 static int nrncore_enable_value() {
3292  return get_nrncore_opt_value("enable");
3293 }
3294 
3295 /// return value of neuron.coreneuron.file_mode
3297  return get_nrncore_opt_value("file_mode");
3298 }
3299 
3300 /** Gets the python string returned by neuron.coreneuron.nrncore_arg(tstop)
3301  return a strdup() copy of the string which should be free when the caller
3302  finishes with it. Return NULL if error or bool(neuron.coreneuron.enable)
3303  is False.
3304 */
3305 extern char* (*nrnpy_nrncore_arg_p_)(double tstop);
3306 static char* nrncore_arg(double tstop) {
3307  PyObject* modules = PyImport_GetModuleDict();
3308  if (modules) {
3309  PyObject* module = PyDict_GetItemString(modules, "neuron.coreneuron");
3310  if (module) {
3311  auto callable = nb::steal(PyObject_GetAttrString(module, "nrncore_arg"));
3312  if (callable) {
3313  auto ts = nb::steal(Py_BuildValue("(d)", tstop));
3314  if (ts) {
3315  auto arg = nb::steal(PyObject_CallObject(callable.ptr(), ts.ptr()));
3316  if (arg) {
3317  auto str = Py2NRNString::as_ascii(arg.ptr());
3318  if (!str.is_valid()) {
3320  PyExc_TypeError,
3321  "neuron.coreneuron.nrncore_arg() must return an ascii string");
3322  return nullptr;
3323  }
3324  if (strlen(str.c_str()) > 0) {
3325  return strdup(str.c_str());
3326  }
3327  }
3328  }
3329  }
3330  }
3331  }
3332  if (PyErr_Occurred()) {
3333  PyErr_Print();
3334  }
3335  return nullptr;
3336 }
3337 
3338 
3339 static PyType_Spec obj_spec_from_name(const char* name) {
3340  return {
3341  name,
3342  sizeof(PyHocObject),
3343  0,
3344  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
3346  };
3347 }
3348 
3349 extern PyObject* nrn_type_from_metaclass(PyTypeObject* meta,
3350  PyObject* mod,
3351  PyType_Spec* spec,
3352  PyObject* bases);
3353 
3355  PyObject* m;
3356  PyTypeObject* pto;
3357  PyType_Spec spec;
3369  nanobind::gil_scoped_acquire lock{};
3370 
3371  char endian_character = 0;
3372 
3373  int err = 0;
3374  PyObject* modules = PyImport_GetModuleDict();
3375  if ((m = PyDict_GetItemString(modules, "hoc")) && PyModule_Check(m)) {
3376  return m;
3377  }
3378  m = PyModule_Create(&hocmodule);
3379  assert(m);
3380 
3381  Symbol* s = nullptr;
3382  spec = obj_spec_from_name("hoc.HocObject");
3383  hocobject_type = (PyTypeObject*) nrn_type_from_metaclass(&PyType_Type, m, &spec, nullptr);
3384  if (!hocobject_type) {
3385  return nullptr;
3386  }
3387  if (PyModule_AddObject(m, "HocObject", (PyObject*) hocobject_type) < 0) {
3388  return nullptr;
3389  }
3390 
3391  hocclass_slots[0].pfunc = (PyObject*) &PyType_Type;
3392  // I have no idea what is going on here. If use
3393  // hocclass_spec.basicsize = sizeof(hocclass);
3394  // then get error
3395  // TypeError: tp_basicsize for type 'hoc.HocClass' (424) is
3396  // too small for base 'type' (920)
3397 
3398 #if 1
3399  hocclass_spec.basicsize = PyType_Type.tp_basicsize + sizeof(Symbol*);
3400  // and what about alignment?
3401  // recommended by chatgpt
3402  size_t alignment = alignof(Symbol*);
3403  size_t remainder = hocclass_spec.basicsize % alignment;
3404  if (remainder != 0) {
3405  hocclass_spec.basicsize += alignment - remainder;
3406  // printf("aligned hocclass_spec.basicsize = %d\n", hocclass_spec.basicsize);
3407  }
3408 #else
3409  // chatgpt agrees that the following suggestion
3410  // https://github.com/neuronsimulator/nrn/pull/2862/files#r1749797713
3411  // is equivalent to the '#if 1' fragment above. However the above
3412  // may "ensures portability and correctness across different architectures."
3413  hocclass_spec.basicsize = PyType_Type.tp_basicsize + sizeof(hocclass) - sizeof(PyTypeObject);
3414 #endif
3415 
3416  PyObject* custom_hocclass = PyType_FromSpec(&hocclass_spec);
3417  if (!custom_hocclass) {
3418  return nullptr;
3419  }
3420  if (PyModule_AddObject(m, "HocClass", custom_hocclass) < 0) {
3421  return nullptr;
3422  }
3423 
3424  auto bases = nb::steal(PyTuple_Pack(1, hocobject_type));
3425  for (auto name: py_exposed_classes) {
3426  // TODO: obj_spec_from_name needs a hoc. prepended
3427  exposed_py_type_names.push_back(std::string("hoc.") + name);
3428  spec = obj_spec_from_name(exposed_py_type_names.back().c_str());
3429  pto = (PyTypeObject*)
3430  nrn_type_from_metaclass((PyTypeObject*) custom_hocclass, m, &spec, bases.ptr());
3431  hocclass* hclass = (hocclass*) pto;
3432  hclass->sym = hoc_lookup(name);
3433  // printf("%s hocclass pto->tp_basicsize = %zd sizeof(*pto)=%zd\n",
3434  // hclass->sym->name, pto->tp_basicsize, sizeof(*pto));
3435  sym_to_type_map[hclass->sym] = pto;
3436  type_to_sym_map[pto] = hclass->sym;
3437  if (PyType_Ready(pto) < 0) {
3438  return nullptr;
3439  }
3440  if (PyModule_AddObject(m, name, (PyObject*) pto) < 0) {
3441  return nullptr;
3442  }
3443  }
3444 
3445  topmethdict = PyDict_New();
3446  for (PyMethodDef* meth = toplevel_methods; meth->ml_name; meth++) {
3447  int err;
3448  auto descr = nb::steal(PyDescr_NewMethod(hocobject_type, meth));
3449  assert(descr);
3450  err = PyDict_SetItemString(topmethdict, meth->ml_name, descr.ptr());
3451  if (err < 0) {
3452  return nullptr;
3453  }
3454  }
3455 
3456  s = hoc_lookup("Vector");
3457  assert(s);
3458  hoc_vec_template_ = s->u.ctemplate;
3459  sym_vec_x = hoc_table_lookup("x", s->u.ctemplate->symtable);
3460  assert(sym_vec_x);
3461  s = hoc_lookup("List");
3462  assert(s);
3463  hoc_list_template_ = s->u.ctemplate;
3464  s = hoc_lookup("SectionList");
3465  assert(s);
3466  hoc_sectionlist_template_ = s->u.ctemplate;
3467  s = hoc_lookup("Matrix");
3468  assert(s);
3469  sym_mat_x = hoc_table_lookup("x", s->u.ctemplate->symtable);
3470  assert(sym_mat_x);
3471  s = hoc_lookup("NetCon");
3472  assert(s);
3473  sym_netcon_weight = hoc_table_lookup("weight", s->u.ctemplate->symtable);
3475 
3476  nrnpy_nrn();
3477  endian_character = get_endian_character();
3478  if (endian_character == 0) {
3479  return nullptr;
3480  }
3481  array_interface_typestr[0] = endian_character;
3482 
3483  // Setup bytesize in typestr
3484  snprintf(array_interface_typestr + 2, 3, "%ld", sizeof(double));
3485  err = PyDict_SetItemString(modules, "hoc", m);
3486  assert(err == 0);
3487  // Py_DECREF(m);
3488  return m;
3489 }
3490 
3492  ptrs->gui_helper = gui_helper_;
3493  ptrs->gui_helper3 = gui_helper_3_;
3496 }
3497 
3498 static double nrnpy_call_func_(Object* obj, double x) {
3499  if (obj->ctemplate->sym != nrnpy_pyobj_sym_) {
3500  hoc_execerror(hoc_object_name(obj), " is not a Python Object");
3501  }
3503  if (!PyCallable_Check(func)) {
3504  hoc_execerror("Object is not callable", nullptr);
3505  }
3506  auto result = nb::steal(PyObject_CallFunction(func, "d", x));
3507  if (!result) {
3508  PyErr_Clear();
3509  hoc_execerror("Python function call raised exception", nullptr);
3510  }
3511  if (!PyNumber_Check(result.ptr())) {
3512  hoc_execerror("Expected a numeric result from Python function", nullptr);
3513  }
3514  double value = PyFloat_AsDouble(result.ptr());
3515  if (PyErr_Occurred()) {
3516  hoc_execerror("Failed to convert result to float", nullptr);
3517  }
3518  return value;
3519 }
#define STRING
Definition: bbslsrv.cpp:9
void nrn_pushsec(Section *sec)
Definition: cabcode.cpp:130
void cable_prop_assign(Symbol *sym, double *pd, int op)
Definition: cabcode.cpp:1512
Section * nrn_noerr_access(void)
return 0 if no accessed section
Definition: cabcode.cpp:474
void nrn_popsec(void)
Definition: cabcode.cpp:154
double const * data() const
Definition: ivocvect.h:34
size_t size() const
Definition: ivocvect.h:42
void resize(size_t n)
Definition: ivocvect.h:46
void push_back(double v)
Definition: ivocvect.h:80
Definition: netcon.h:87
Definition: oclist.h:11
long count()
Definition: oclist.cpp:175
Object * object(long)
Definition: oclist.cpp:219
static neuron::unique_cstr as_ascii(PyObject *python_string)
Definition: nrnpy_utils.cpp:17
static neuron::unique_cstr get_pyerr()
Definition: nrnpy_utils.cpp:53
static void set_pyerr(PyObject *type, const char *message)
Definition: nrnpy_utils.cpp:41
char * release()
Releases ownership of the string.
Definition: unique_cstr.hpp:44
Symbol * hoc_table_lookup(const char *, Symlist *)
Definition: symbol.cpp:48
char * gargstr(int narg)
Definition: code2.cpp:227
void hoc_evalpointer()
Definition: code.cpp:1903
HocReturnType hoc_return_type_code
Definition: code.cpp:42
HocReturnType
Definition: code.h:2
#define key
Definition: tqueue.hpp:45
#define v
Definition: md1redef.h:11
#define data
Definition: md1redef.h:36
#define sec
Definition: md1redef.h:20
#define i
Definition: md1redef.h:19
#define prop
Definition: md1redef.h:38
DLFCN_EXPORT int dladdr(const void *addr, Dl_info *info)
Definition: dlfcn.c:731
static double interp(double frac, double x1, double x2)
Definition: functabl.cpp:67
char buf[512]
Definition: init.cpp:13
int hoc_is_object_arg(int narg)
Definition: code.cpp:876
double hoc_xpop()
Definition: code.cpp:903
void hoc_push_ndim(int d)
Definition: code.cpp:855
void hoc_pushstr(char **d)
Definition: code.cpp:800
void hoc_execerr_ext(const char *fmt,...)
printf style specification of hoc_execerror message.
Definition: fileio.cpp:828
double hoc_call_func(Symbol *s, int narg)
Definition: code.cpp:1477
void vector_resize(IvocVect *v, int n)
Definition: ivocvect.cpp:302
void hoc_pushpx(double *d)
Definition: code.cpp:834
void hoc_pushobj(Object **d)
Definition: code.cpp:784
void * hoc_sec_internal_name2ptr(const char *s, int eflag)
Definition: cabcode.cpp:764
int hoc_is_str_arg(int narg)
Definition: code.cpp:872
int hoc_is_temp_charptr(char **cpp)
Definition: code.cpp:722
double * hoc_pxpop()
Definition: code.cpp:922
Objectdata * hoc_objectdata
Definition: hoc_oop.cpp:122
void hoc_assign_str(char **cpp, const char *buf)
Definition: code.cpp:2263
int is_obj_type(Object *obj, const char *type_name)
Definition: hoc_oop.cpp:2110
int hoc_is_double_arg(int narg)
Definition: code.cpp:864
char ** hoc_temp_charptr(void)
Definition: code.cpp:717
double hoc_ac_
Definition: hoc_init.cpp:222
void hoc_obj_ref(Object *obj)
Definition: hoc_oop.cpp:1844
char * hoc_object_name(Object *ob)
Definition: hoc_oop.cpp:73
void * hoc_pysec_name2ptr(const char *s, int)
Definition: cabcode.cpp:806
Symbol * hoc_lookup(const char *)
Definition: symbol.cpp:59
int hoc_is_pdouble_arg(int narg)
Definition: code.cpp:868
void hoc_obj_unref(Object *obj)
Definition: hoc_oop.cpp:1881
void hoc_push_object(Object *d)
Definition: code.cpp:793
void hoc_push(neuron::container::generic_data_handle handle)
Definition: code.cpp:850
char ** hoc_pgargstr(int narg)
Definition: code.cpp:1623
int hoc_usegui
Definition: hoc.cpp:121
double(* func)(double)
Definition: hoc_init.cpp:85
Objectdata * hoc_objectdata_restore(Objectdata *obdsav)
Definition: hoc_oop.cpp:142
Objectdata * hoc_objectdata_save(void)
Definition: hoc_oop.cpp:132
#define assert(ex)
Definition: hocassrt.h:24
#define OBJECTALIAS
Definition: hocdec.h:94
#define USERPROPERTY
Definition: hocdec.h:85
#define JAVAOBJECT
Definition: hocdec.h:92
#define USERFLOAT
Definition: hocdec.h:86
#define USERDOUBLE
Definition: hocdec.h:84
#define getarg
Definition: hocdec.h:17
#define OBJECTTMP
Definition: hocdec.h:88
#define CPLUSOBJECT
Definition: hocdec.h:91
bool is_array(const Symbol &sym)
Definition: hocdec.h:136
#define USERINT
Definition: hocdec.h:83
#define OPSTR(sym)
Definition: hocdec.h:235
#define STOP
Definition: hocdec.h:57
#define VARALIAS
Definition: hocdec.h:95
#define OBJ(q)
Definition: hoclist.h:88
void hoc_l_delete(hoc_Item *)
Point_process * ob2pntproc_0(Object *ob)
Definition: hocmech.cpp:89
Object ** hoc_objgetarg(int)
Definition: code.cpp:1614
static double location(void *v)
Definition: impedanc.cpp:79
Object ** new_vect(Vect *v, ssize_t delta, ssize_t start, ssize_t step)
Definition: ivocvect.cpp:386
static int narg()
Definition: ivocvect.cpp:121
Object * hoc_thisobject
Definition: hoc_oop.cpp:121
void hoc_pushx(double)
Definition: code.cpp:779
#define NRNPOINTER
Definition: membfunc.hpp:83
#define CABLESECTION
Definition: membfunc.hpp:58
#define ITERATE(itm, lst)
Definition: model.h:18
#define RANGEOBJ
Definition: model.h:124
printf
Definition: extdef.h:5
step
Definition: extdef.h:7
const char * name
Definition: init.cpp:16
long subtype
Definition: init.cpp:107
void move(Item *q1, Item *q2, Item *q3)
Definition: list.cpp:200
Item * next(Item *item)
Definition: list.cpp:89
@ NextNotLast
Definition: nrnpython.h:71
@ Last
Definition: nrnpython.h:71
@ Begin
Definition: nrnpython.h:71
ObjectType
Definition: nrnpython.h:57
@ HocScalarPtr
Definition: nrnpython.h:67
@ HocRefObj
Definition: nrnpython.h:64
@ HocRefNum
Definition: nrnpython.h:62
@ HocForallSectionIterator
Definition: nrnpython.h:65
@ HocArrayIncomplete
Definition: nrnpython.h:68
@ HocRefStr
Definition: nrnpython.h:63
@ HocObject
Definition: nrnpython.h:59
@ HocArray
Definition: nrnpython.h:61
@ HocRefPStr
Definition: nrnpython.h:69
@ HocTopLevelInterpreter
Definition: nrnpython.h:58
@ HocSectionListIterator
Definition: nrnpython.h:66
@ HocFunction
Definition: nrnpython.h:60
double * vector_vec(IvocVect *v)
Definition: ivocvect.cpp:19
void hoc_execerror(const char *s1, const char *s2)
Definition: nrnoc_aux.cpp:39
int vector_capacity(IvocVect *v)
Definition: ivocvect.cpp:16
handle_interface< non_owning_identifier< storage > > handle
Non-owning handle to a Mechanism instance.
int Sprintf(char(&buf)[N], const char *fmt, Args &&... args)
Redirect sprintf to snprintf if the buffer size can be deduced.
Definition: wrap_sprintf.h:14
int * stride
Definition: cellorder.cpp:621
if(ncell==0)
Definition: cellorder.cpp:785
std::string to_string(const T &obj)
static convert_cxx_exceptions_trait< F, Args... >::return_type convert_cxx_exceptions(F f, Args &&... args)
static List * info
void section_unref(Section *)
Definition: solve.cpp:509
#define NRN_EXPORT
Definition: nrn_export.hpp:6
int const size_t const size_t n
Definition: nrngsl.h:10
#define FUNCTION(a, b)
Definition: nrngsl.h:5
size_t q
size_t p
size_t j
s
Definition: multisend.cpp:521
int ifarg(int)
Definition: code.cpp:1607
hoc_List * section_list
Definition: init.cpp:113
Symbol * nrnpy_pyobj_sym_
Definition: hoc_oop.cpp:25
_object PyObject
Definition: nrnpy.h:12
static PyType_Spec hocclass_spec
Definition: nrnpy_hoc.cpp:175
static PyObject * hocobj_baseattr_safe(PyObject *subself, PyObject *args)
Definition: nrnpy_hoc.cpp:1418
static void hocobj_dealloc(PyHocObject *self)
Definition: nrnpy_hoc.cpp:235
int nrnpy_numbercheck(PyObject *po)
Definition: nrnpy_hoc.cpp:542
static HocReturnType component(PyHocObject *po)
Definition: nrnpy_hoc.cpp:510
static PyObject * plotshape_plot
Definition: nrnpy_hoc.cpp:125
static char * double_array_interface(PyObject *po, long &stride)
Definition: nrnpy_hoc.cpp:2507
static Symbol * getsym(char *name, Object *ho, int fail)
Definition: nrnpy_hoc.cpp:483
double pyobj_to_double_or_fail(PyObject *obj, long obj_id)
Definition: nrnpy_hoc.cpp:2547
static PyObject * cpp2refstr(char **cpp)
Definition: nrnpy_hoc.cpp:2303
static PyObject * hocobj_name(PyObject *pself, PyObject *args)
Definition: nrnpy_hoc.cpp:346
static PyObject * hocobj_same(PyHocObject *pself, PyObject *args)
Definition: nrnpy_hoc.cpp:2494
static PyObject * hocobj_getattro(PyObject *subself, PyObject *name)
Definition: nrnpy_hoc.cpp:1423
static int hocobj_slice_setitem(PyObject *self, PyObject *slice, PyObject *arg)
Definition: nrnpy_hoc.cpp:2223
static PyType_Spec obj_spec_from_name(const char *name)
Definition: nrnpy_hoc.cpp:3339
static Symbol * sym_mat_x
Definition: nrnpy_hoc.cpp:1638
static PyObject * py_hocobj_add(PyObject *obj1, PyObject *obj2)
Definition: nrnpy_hoc.cpp:3165
static cTemplate * hoc_vec_template_
Definition: nrnpy_hoc.cpp:84
static cTemplate * hoc_list_template_
Definition: nrnpy_hoc.cpp:85
static cTemplate * hoc_sectionlist_template_
Definition: nrnpy_hoc.cpp:86
static IvocVect * nrnpy_vec_from_python(void *v)
Definition: nrnpy_hoc.cpp:2556
static int nrncore_file_mode_value()
return value of neuron.coreneuron.file_mode
Definition: nrnpy_hoc.cpp:3296
NRN_EXPORT PyObject * nrnpy_hoc()
Definition: nrnpy_hoc.cpp:3354
static PyObject * hocobj_iter(PyObject *raw_self)
Definition: nrnpy_hoc.cpp:1757
char ** hoc_strpop()
Definition: code.cpp:962
PyTypeObject * psection_type
Definition: nrnpy_nrn.cpp:111
static PyObject * curargs_
Definition: nrnpy_hoc.cpp:776
Object * hoc_obj_look_inside_stack(int)
Definition: code.cpp:885
static PyObject * py_hocobj_uabs(PyObject *obj)
Definition: nrnpy_hoc.cpp:3169
static PyObject * nrnexec(PyObject *self, PyObject *args)
Definition: nrnpy_hoc.cpp:205
void(* nrnpy_decref)(void *pyobj)
Definition: shapeplt.cpp:46
static int hocobj_init(PyObject *subself, PyObject *args, PyObject *kwds)
Definition: nrnpy_hoc.cpp:320
static PyObject * hocclass_getitem(PyObject *self, Py_ssize_t ix)
Definition: nrnpy_hoc.cpp:148
#define BYTEHEADER
Definition: nrnpy_hoc.cpp:2971
static PyObject * store_savestate_
Definition: nrnpy_hoc.cpp:2614
static PyObject * gui_helper_3_helper_(const char *name, Object *obj, int handle_strptr)
Definition: nrnpy_hoc.cpp:2696
static PyObject * hocobj_call(PyHocObject *self, PyObject *args, PyObject *kwrds)
Definition: nrnpy_hoc.cpp:782
static int hocclass_init(hocclass *cls, PyObject *args, PyObject *kwds)
Definition: nrnpy_hoc.cpp:140
static void * nrnpy_hoc_bool_pop()
Definition: nrnpy_hoc.cpp:712
static PyObject * hocobj_slice_getitem(PyObject *self, PyObject *slice)
Definition: nrnpy_hoc.cpp:2073
static PyObject * setpointer_safe(PyObject *self, PyObject *args)
Definition: nrnpy_hoc.cpp:2372
void hoc_objectvar()
Definition: hoc_oop.cpp:792
static PyObject * gui_callback
Definition: nrnpy_hoc.cpp:2669
static Inst * save_pc(Inst *newpc)
Definition: nrnpy_hoc.cpp:409
Symlist * hoc_top_level_symlist
Definition: symdir.cpp:16
void hoc_call()
Definition: code.cpp:1398
void(* nrnpy_sectionlist_helper_)(void *, Object *)
static PyObject * setpointer(PyObject *self, PyObject *args)
Definition: nrnpy_hoc.cpp:2325
static PyType_Slot hocclass_slots[]
Definition: nrnpy_hoc.cpp:168
int nrn_inpython_
Definition: hoc.cpp:52
int nrn_is_hocobj_ptr(PyObject *po, neuron::container::data_handle< double > &pd)
Definition: nrnpy_hoc.cpp:970
static Object ** vec_as_numpy_helper(int size, double *data)
Definition: nrnpy_hoc.cpp:2790
Object ** hoc_objpop()
Pop pointer to object pointer and return top elem from stack.
Definition: code.cpp:943
int ivoc_list_count(Object *)
Definition: oclist.cpp:396
static PyObject * hocobj_same_safe(PyHocObject *pself, PyObject *args)
Definition: nrnpy_hoc.cpp:2503
static void * nrnpy_hoc_int_pop()
Definition: nrnpy_hoc.cpp:707
static Object ** gui_helper_3_(const char *name, Object *obj, int handle_strptr)
Definition: nrnpy_hoc.cpp:2761
int hoc_max_builtin_class_id
Definition: hoc_oop.cpp:40
double cable_prop_eval(Symbol *sym)
Definition: cabcode.cpp:1447
static PyObject * hocobj_vptr_safe(PyObject *pself, PyObject *args)
Definition: nrnpy_hoc.cpp:2388
Symbol * ivoc_alias_lookup(const char *name, Object *ob)
Definition: strfun.cpp:144
static PyObject * hocpickle_reduce(PyObject *self, PyObject *args)
Definition: nrnpy_hoc.cpp:2926
void nrnpython_reg_real_nrnpy_hoc_cpp(neuron::python::impl_ptrs *ptrs)
Definition: nrnpy_hoc.cpp:3491
PyObject * nrnpy_nrn()
Definition: nrnpy_nrn.cpp:3005
static PyObject * py_hocobj_uneg(PyObject *obj)
Definition: nrnpy_hoc.cpp:3173
std::vector< const char * > py_exposed_classes
Definition: hoc_oop.cpp:37
static PyObject * hoc_ac(PyObject *self, PyObject *args)
Definition: nrnpy_hoc.cpp:218
static PyObject *(* vec_as_numpy)(int, double *)
Definition: nrnpy_hoc.cpp:2608
static void add2topdict(PyObject *)
Definition: nrnpy_hoc.cpp:3100
static PyObject * py_hocobj_upos(PyObject *obj)
Definition: nrnpy_hoc.cpp:3177
int hoc_stack_type()
Get the type of the top entry.
Definition: code.cpp:310
void hoc_pushs(Symbol *)
Definition: code.cpp:841
static void * fcall(void *vself, void *vargs)
Definition: nrnpy_hoc.cpp:717
static PyObject * hocpickle_reduce_safe(PyObject *self, PyObject *args)
Definition: nrnpy_hoc.cpp:2966
static void pyobject_in_objptr(Object **, PyObject *)
Definition: nrnpy_hoc.cpp:338
static int araylen(Arrayinfo *a, PyHocObject *po)
Definition: nrnpy_hoc.cpp:1641
static PyObject * libpython_path(PyObject *self, PyObject *args)
Definition: nrnpy_hoc.cpp:3047
void sec_access_push()
Definition: cabcode.cpp:749
PyObject * nrnpy_cas(PyObject *, PyObject *)
Definition: nrnpy_nrn.cpp:2967
static double object_to_double_(Object *obj)
Definition: nrnpy_hoc.cpp:2675
static hoc_Item * next_valid_secitem(hoc_Item *q, hoc_Item *ql)
Definition: nrnpy_hoc.cpp:1793
static void eval_component(PyHocObject *po, int ix)
Definition: nrnpy_hoc.cpp:949
void *(* nrnpy_get_pyobj)(Object *obj)
Definition: shapeplt.cpp:45
static int hocobj_setattro(PyObject *subself, PyObject *pyname, PyObject *value)
Definition: nrnpy_hoc.cpp:1455
double(* nrnpy_call_func)(Object *, double)
Definition: ivocvect.cpp:119
static char * nrncore_arg(double tstop)
Definition: nrnpy_hoc.cpp:3306
static PyObject * mkref(PyObject *self, PyObject *args)
Definition: nrnpy_hoc.cpp:2268
PyObject * nrnpy_newsecobj_safe(PyObject *, PyObject *, PyObject *)
Definition: nrnpy_nrn.cpp:516
static PyObject * pfunc_get_docstring
Definition: nrnpy_hoc.cpp:102
static PyObject * py_hocobj_div(PyObject *obj1, PyObject *obj2)
Definition: nrnpy_hoc.cpp:3189
static PyObject * hocobj_baseattr(PyObject *subself, PyObject *args)
Definition: nrnpy_hoc.cpp:1410
Object **(* nrnpy_vec_as_numpy_helper_)(int, double *)
Definition: ivocvect.cpp:118
static int hocobj_setitem(PyObject *self, Py_ssize_t i, PyObject *arg)
Definition: nrnpy_hoc.cpp:2102
NRN_EXPORT PyObject * nrn_hocobj_ptr(double *pd)
Definition: nrnpy_hoc.cpp:966
static int setup_doc_system()
Definition: nrnpy_hoc.cpp:998
int(* nrnpy_nrncore_file_mode_value_p_)()
value of neuron.coreneuron.file_mode as 0, 1 (-1 if error)
int hocobj_pushargs(PyObject *args, std::vector< neuron::unique_cstr > &s2free)
Definition: nrnpy_hoc.cpp:416
static PyObject * mkref_safe(PyObject *self, PyObject *args)
Definition: nrnpy_hoc.cpp:2298
void hoc_push_string()
Definition: code.cpp:805
void hoc_tobj_unref(Object **)
Definition: code.cpp:160
static PyMethodDef hocobj_methods[]
Definition: nrnpy_hoc.cpp:3072
static PyObject * hocobj_name_safe(PyObject *pself, PyObject *args)
Definition: nrnpy_hoc.cpp:401
static void nrnpy_restore_savestate_(int64_t size, char *data)
Definition: nrnpy_hoc.cpp:2637
static void hocobj_pushtop(PyHocObject *po, Symbol *sym, int ix)
Definition: nrnpy_hoc.cpp:903
static PyMethodDef toplevel_methods[]
Definition: nrnpy_hoc.cpp:3085
PyObject * nrn_type_from_metaclass(PyTypeObject *meta, PyObject *mod, PyType_Spec *spec, PyObject *bases)
void hoc_object_component()
Definition: hoc_oop.cpp:977
static PyObject * nrnpy_rvp_pyobj_callback
Definition: nrnpy_hoc.cpp:128
static PyObject * hocobj_richcmp(PyHocObject *self, PyObject *other, int op)
Definition: nrnpy_hoc.cpp:2422
static bool pyobj_is_vector(PyObject *obj)
Definition: nrnpy_hoc.cpp:3126
NRN_EXPORT int nrnpy_set_gui_callback(PyObject *new_gui_callback)
Definition: nrnpy_hoc.cpp:2670
PyObject * pmech_types
Definition: nrnpy_nrn.cpp:122
static PyObject * py_hocobj_math_unary(const char *op, PyObject *obj)
Definition: nrnpy_hoc.cpp:3156
void hoc_unref_defer()
Definition: code.cpp:181
static PyObject * hocobj_repr(PyObject *p)
Definition: nrnpy_hoc.cpp:405
static Symbol * sym_vec_x
Definition: nrnpy_hoc.cpp:1637
PyObject * nrnpy_cas_safe(PyObject *, PyObject *)
Definition: nrnpy_nrn.cpp:2977
static PyObject * hocobj_vptr(PyObject *pself, PyObject *args)
Definition: nrnpy_hoc.cpp:2376
static PyObject * hocpickle_setstate_safe(PyObject *self, PyObject *args)
Definition: nrnpy_hoc.cpp:3043
static int get_nrncore_opt_value(const char *option)
Definition: nrnpy_hoc.cpp:3269
static PyObject * libpython_path_safe(PyObject *self, PyObject *args)
Definition: nrnpy_hoc.cpp:3067
int section_object_seen
Definition: hoc_oop.cpp:28
Object **(* nrnpy_vec_to_python_p_)(void *)
Definition: ivocvect.cpp:117
static PyObject * rvp_plot
Definition: nrnpy_hoc.cpp:124
static PyObject * py_hocobj_sub(PyObject *obj1, PyObject *obj2)
Definition: nrnpy_hoc.cpp:3181
Object * hoc_newobj1(Symbol *, int)
Definition: hoc_oop.cpp:497
static PyHocObject * intermediate(PyHocObject *po, Symbol *sym, int ix)
Definition: nrnpy_hoc.cpp:876
PyObject * nrnpy_forall(PyObject *self, PyObject *args)
Definition: nrnpy_hoc.cpp:1743
NRN_EXPORT int nrnpy_vec_math_register(PyObject *callback)
Definition: nrnpy_hoc.cpp:3116
static PyObject * py_hocobj_math(const char *op, PyObject *obj1, PyObject *obj2)
Definition: nrnpy_hoc.cpp:3139
void(* nrnpy_restore_savestate)(int64_t, char *)
Definition: savstate.cpp:31
static int refuse_to_look
Definition: nrnpy_hoc.cpp:1422
static PyMethodDef HocMethods[]
Definition: nrnpy_hoc.cpp:227
NRN_EXPORT int nrnpy_set_vec_as_numpy(PyObject *(*p)(int, double *))
Definition: nrnpy_hoc.cpp:2609
int nrn_netcon_weight(NetCon *, double **)
Definition: netcvode.cpp:122
static PyObject * hocobj_getitem(PyObject *self, Py_ssize_t ix)
Definition: nrnpy_hoc.cpp:1933
static char array_interface_typestr[5]
Definition: nrnpy_hoc.cpp:98
char *(* nrnpy_nrncore_arg_p_)(double tstop)
Gets the python string returned by neuron.coreneuron.nrncore_arg(tstop) return a strdup() copy of the...
PyObject * hocobj_call_arg(int i)
Definition: nrnpy_hoc.cpp:778
PyObject * toplevel_get(PyObject *subself, const char *n)
Definition: nrnpy_hoc.cpp:1051
static int hocobj_nonzero(PyObject *self)
Definition: nrnpy_hoc.cpp:1718
static PyObject * restore_savestate_
Definition: nrnpy_hoc.cpp:2615
static PyObject * hocclass_getattro(PyObject *self, PyObject *pyname)
Definition: nrnpy_hoc.cpp:1019
NRN_EXPORT PyObject * get_plotshape_data(PyObject *sp)
Definition: nrnpy_hoc.cpp:2891
static PyObject * nrnpy_vec_math
Definition: nrnpy_hoc.cpp:3114
static Arrayinfo * hocobj_aray(Symbol *sym, Object *ho)
Definition: nrnpy_hoc.cpp:854
static Object ** nrnpy_vec_to_python(void *v)
Definition: nrnpy_hoc.cpp:2803
static std::unordered_map< Symbol *, PyTypeObject * > sym_to_type_map
Definition: nrnpy_hoc.cpp:88
static void nrnpy_decref_(void *pyobj)
Definition: nrnpy_hoc.cpp:2689
static Py_ssize_t hocobj_len(PyObject *self)
Definition: nrnpy_hoc.cpp:1692
static PyObject * hocobj_iternext(PyObject *self)
Definition: nrnpy_hoc.cpp:1906
static int set_final_from_stk(PyObject *po)
Definition: nrnpy_hoc.cpp:655
PyObject * rangevars_
Definition: nrnpy_nrn.cpp:123
static void symlist2dict(Symlist *sl, PyObject *dict)
Definition: nrnpy_hoc.cpp:982
static int hoc_evalpointer_err()
if hoc_evalpointer calls hoc_execerror, return 1
Definition: nrnpy_hoc.cpp:192
static int araychk(Arrayinfo *a, PyHocObject *po, int ix)
Definition: nrnpy_hoc.cpp:1669
static Py_ssize_t seclist_count(Object *ho)
Definition: nrnpy_hoc.cpp:1687
static PyObject * nrnexec_safe(PyObject *self, PyObject *args)
Definition: nrnpy_hoc.cpp:214
static long hocobj_hash(PyHocObject *self)
Definition: nrnpy_hoc.cpp:2392
static int nrncore_enable_value()
return value of neuron.coreneuron.enable
Definition: nrnpy_hoc.cpp:3291
static void nrnpy_store_savestate_(char **save_data, uint64_t *save_data_size)
Definition: nrnpy_hoc.cpp:2618
int(* nrnpy_nrncore_enable_value_p_)()
value of neuron.coreneuron.enable as 0, 1 (-1 if error)
static PyObject * hocobj_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
Definition: nrnpy_hoc.cpp:262
Symbol * nrn_child_sym
Definition: secref.cpp:25
static Object ** gui_helper_(const char *name, Object *obj)
Definition: nrnpy_hoc.cpp:2786
PyObject * nrnpy_forall_safe(PyObject *, PyObject *)
Definition: nrnpy_hoc.cpp:1752
Objectdata * hoc_top_level_data
Definition: hoc_oop.cpp:123
char get_endian_character()
Definition: nrnpy_hoc.cpp:3197
static PyObject * iternext_sl(PyHocObject *po, hoc_Item *ql)
Definition: nrnpy_hoc.cpp:1809
PyObject * nrn_hocobj_handle(neuron::container::data_handle< double > d)
Definition: nrnpy_hoc.cpp:957
int nrn_secref_nchild(Section *)
Definition: secref.cpp:234
static Symbol * sym_netcon_weight
Definition: nrnpy_hoc.cpp:1639
static std::unordered_map< PyTypeObject *, Symbol * > type_to_sym_map
Definition: nrnpy_hoc.cpp:89
Symlist * hoc_built_in_symlist
Definition: symbol.cpp:28
static PyObject * hoc_ac_safe(PyObject *self, PyObject *args)
Definition: nrnpy_hoc.cpp:223
Object * nrnpy_po2ho(PyObject *po)
Definition: nrnpy_hoc.cpp:592
PyObject * nrn_ptr_richcmp(void *self_ptr, void *other_ptr, int op)
Definition: nrnpy_hoc.cpp:2396
PyObject * nrnpy_ho2po(Object *o)
Definition: nrnpy_hoc.cpp:566
static void sectionlist_helper_(void *sl, Object *args)
Definition: nrnpy_hoc.cpp:3228
PyTypeObject * hocobject_type
Definition: nrnpy_hoc.cpp:130
static PyObject * py_hocobj_mul(PyObject *obj1, PyObject *obj2)
Definition: nrnpy_hoc.cpp:3185
static PyObject * hocobj_getsec(Symbol *sym)
Definition: nrnpy_hoc.cpp:937
static PyObject * hocobj_getattr(PyObject *subself, PyObject *pyname)
Definition: nrnpy_hoc.cpp:1065
static char ** gui_helper_3_str_(const char *name, Object *obj, int handle_strptr)
Definition: nrnpy_hoc.cpp:2774
static void * nrnpy_get_pyobj_(Object *obj)
Definition: nrnpy_hoc.cpp:2681
PyObject * nrnpy_hoc_pop(const char *mes)
Definition: nrnpy_hoc.cpp:620
static PyObject * hocpickle_setstate(PyObject *self, PyObject *args)
Definition: nrnpy_hoc.cpp:2985
static Object * rvp_rxd_to_callable_(Object *obj)
Definition: nrnpy_hoc.cpp:2879
bool nrn_chk_data_handle(const neuron::container::data_handle< double > &pd)
Definition: nrnpy_hoc.cpp:182
static std::vector< std::string > exposed_py_type_names
Definition: nrnpy_hoc.cpp:90
#define BYTESWAP(_X__, _TYPE__)
Definition: nrnpy_hoc.cpp:2976
static const char * hocobj_docstring
Definition: nrnpy_hoc.cpp:110
static PyObject * topmethdict
Definition: nrnpy_hoc.cpp:107
int nrn_matrix_dim(void *, int)
Definition: ocmatrix.cpp:15
static double nrnpy_call_func_(Object *, double)
Definition: nrnpy_hoc.cpp:3498
NRN_EXPORT int nrnpy_rvp_pyobj_callback_register(PyObject *callback)
Definition: nrnpy_hoc.cpp:3121
Object *(* nrnpy_rvp_rxd_to_callable)(Object *)
Definition: spaceplt.cpp:23
bool hoc_valid_stmt(const char *, Object *)
Definition: ocjump.cpp:21
static int hocobj_objectvar(Symbol *sym)
Definition: nrnpy_hoc.cpp:919
IvocVect *(* nrnpy_vec_from_python_p_)(void *)
Definition: ivocvect.cpp:116
NRN_EXPORT int nrnpy_set_toplevel_callbacks(PyObject *rvp_plot0, PyObject *plotshape_plot0, PyObject *get_mech_object_0, PyObject *store_savestate, PyObject *restore_savestate)
Definition: nrnpy_hoc.cpp:2654
Inst * hoc_pc
Definition: code.cpp:78
static PyObject * get_mech_object_
Definition: nrnpy_hoc.cpp:127
void(* nrnpy_store_savestate)(char **save_data, uint64_t *save_data_size)
Definition: savstate.cpp:32
static struct PyModuleDef hocmodule
Definition: nrnpy_hoc.h:33
static PyType_Slot nrnpy_HocObjectType_slots[]
Definition: nrnpy_hoc.h:3
neuron::container::generic_data_handle * nrnpy_setpointer_helper(PyObject *pyname, PyObject *mech)
Definition: nrnpy_nrn.cpp:2626
void nrnpy_sec_referr()
Definition: nrnpy_nrn.cpp:150
int nrn_pointer_assign(Prop *prop, Symbol *sym, PyObject *value)
Definition: nrnpy_nrn.cpp:2308
NPySecObj * newpysechelp(Section *sec)
Definition: nrnpy_nrn.cpp:1043
Object * nrnpy_pyobject_in_obj(PyObject *po)
Definition: nrnpy_p2h.cpp:91
PyObject * nrnpy_hoc2pyobject(Object *ho)
Definition: nrnpy_p2h.cpp:77
bool is_python_string(PyObject *python_string)
Definition: nrnpy_utils.h:9
#define castptr2long
Definition: nrnpython.h:39
#define PyInt_Check
Definition: nrnpython.h:24
#define PyString_FromString
Definition: nrnpython.h:23
#define PyInt_AS_LONG
Definition: nrnpython.h:26
#define lock
static double done(void *v)
Definition: ocbbs.cpp:251
static double ref(void *v)
Definition: ocbox.cpp:381
static uint32_t value
Definition: scoprand.cpp:25
double seclist_size(void *v)
Definition: seclist.cpp:231
void lvappendsec_and_ref(void *sl, Section *sec)
Definition: seclist.cpp:46
#define PROP_PY_INDEX
Definition: section.h:230
Object ** hoc_temp_objptr(Object *)
Definition: code.cpp:151
int nsub
Definition: hocdec.h:61
int sub[1]
Definition: hocdec.h:63
void * element
Definition: model.h:11
struct Item * next
Definition: model.h:12
PyObject_HEAD Section * sec_
Definition: nrnpython.h:79
Definition: hocdec.h:173
void * aliases
Definition: hocdec.h:181
void * this_pointer
Definition: hocdec.h:178
Objectdata * dataspace
Definition: hocdec.h:177
int index
Definition: hocdec.h:175
int refcount
Definition: hocdec.h:174
cTemplate * ctemplate
Definition: hocdec.h:180
union Object::@47 u
static void * fpycall(void *(*)(void *, void *), void *, void *)
Definition: ocjump.cpp:157
Definition: section.h:231
PyObject_HEAD Object * ho_
void * iteritem_
union PyHocObject::@37 u
Symbol * sym_
PyHoc::IteratorState its_
neuron::container::data_handle< double > px_
PyHoc::ObjectType type_
virtual const char * varname() const =0
virtual Object * neuron_section_list()=0
virtual float low()=0
virtual void * varobj() const =0
virtual float high()=0
Definition: model.h:47
union Symbol::@28 u
struct Symbol::@45::@46 rng
short type
Definition: model.h:48
int * pvalint
Definition: hocdec.h:116
long subtype
Definition: model.h:49
cTemplate * ctemplate
Definition: hocdec.h:126
char * name
Definition: model.h:61
int oboff
Definition: hocdec.h:111
Arrayinfo * arayinfo
Definition: hocdec.h:130
Definition: hocdec.h:75
Symbol * first
Definition: hocdec.h:76
Symbol * sym
Definition: hocdec.h:147
Symlist * symtable
Definition: hocdec.h:148
int id
Definition: hocdec.h:156
hoc_List * olist
Definition: hocdec.h:155
int count
Definition: hocdec.h:154
Definition: dlfcn.h:72
Section * sec
Definition: hoclist.h:40
union hoc_Item::@48 element
hoc_Item * next
Definition: hoclist.h:44
Symbol * sym
Definition: nrnpy_hoc.cpp:137
PyTypeObject head
Definition: nrnpy_hoc.cpp:136
data_handle next_array_element(int shift=1) const
Get a data handle to a different element of the same array variable.
Non-template stable handle to a generic value.
Collection of pointers to functions with python-version-specific implementations.
Definition: nrnpy.h:25
double(* object_to_double)(Object *)
Definition: nrnpy.h:45
Object **(* gui_helper)(const char *name, Object *obj)
Definition: nrnpy.h:33
Object **(* gui_helper3)(const char *name, Object *obj, int handle_strptr)
Definition: nrnpy.h:34
char **(* gui_helper3_str)(const char *, Object *, int)
Definition: nrnpy.h:35
Definition: hocdec.h:42
int i
Definition: hocdec.h:54
Symbol * sym
Definition: hocdec.h:52
Inst * in
Definition: hocdec.h:51
Arrayinfo * arayinfo
Definition: hocdec.h:169