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