NEURON
nrnpython.cpp
Go to the documentation of this file.
1 #include <nrnpython.h>
2 #include <nrnpy_utils.h>
3 #include <stdio.h>
4 #include <InterViews/resource.h>
5 #if HAVE_IV
6 #include <InterViews/session.h>
7 #endif
8 #include <nrnoc2iv.h>
9 #include <nrnpy_reg.h>
10 #include <hoccontext.h>
11 #include <string>
12 #include <ocfile.h> // bool isDirExist(const std::string& path);
13 
14 #include <hocstr.h>
15 extern "C" void nrnpython_real();
16 extern "C" int nrnpython_start(int);
17 extern int hoc_get_line();
18 extern HocStr* hoc_cbufstr;
19 extern int nrnpy_nositeflag;
20 extern char* nrnpy_pyhome;
21 extern char* hoc_ctp;
22 extern FILE* hoc_fin;
23 extern const char* hoc_promptstr;
24 extern char* neuronhome_forward();
25 #if DARWIN || defined(__linux__)
26 extern const char* path_prefix_to_libnrniv();
27 #endif
28 static char* nrnpython_getline(FILE*, FILE*, const char*);
29 extern int nrn_global_argc;
30 extern char** nrn_global_argv;
31 void nrnpy_augment_path();
32 int nrnpy_pyrun(const char*);
33 extern int (*p_nrnpy_pyrun)(const char*);
34 extern int nrn_global_argc;
35 extern char** nrn_global_argv;
36 #if NRNPYTHON_DYNAMICLOAD
37 int nrnpy_site_problem;
38 #endif
39 
40 extern "C" {
41 extern void rl_stuff_char(int);
42 } // extern "C"
43 
45  static int augmented = 0;
46  if (!augmented && strlen(neuronhome_forward()) > 0) {
47  augmented = 1;
48  int err = PyRun_SimpleString("import sys");
49  assert(err == 0);
50 #if defined(__linux__) || defined(DARWIN)
51  // If /where/installed/lib/python/neuron exists, then append to sys.path
52  std::string lib = std::string(path_prefix_to_libnrniv());
53 #else // not defined(__linux__) || defined(DARWIN)
54  std::string lib = std::string(neuronhome_forward()) + std::string("/lib/");
55 #endif // not defined(__linux__) || defined(DARWIN)
56  if (isDirExist(lib + std::string("python/neuron"))) {
57  std::string cmd = std::string("sys.path.append('") + lib + "python')";
58  err = PyRun_SimpleString(cmd.c_str());
59  assert(err == 0);
60  }
61  err = PyRun_SimpleString("sys.path.insert(0, '')");
62  assert(err == 0);
63  }
64 }
65 
66 /** @brief Execute a Python script.
67  * @return 0 on failure, 1 on success.
68  */
69 int nrnpy_pyrun(const char* fname) {
70 #ifdef MINGW
71  // perhaps this should be the generic implementation
72  char* cmd = new char[strlen(fname) + 40];
73  sprintf(cmd, "exec(open(\"%s\").read(), globals())", fname);
74  int err = PyRun_SimpleString(cmd);
75  delete[] cmd;
76  if (err != 0) {
77  PyErr_Print();
78  PyErr_Clear();
79  return 0;
80  }
81  return 1;
82 #else // MINGW not defined
83  FILE* fp = fopen(fname, "r");
84  if (fp) {
85  int const code = PyRun_AnyFile(fp, fname);
86  fclose(fp);
87  return !code;
88  } else {
89  std::cerr << "Could not open " << fname << std::endl;
90  return 0;
91  }
92 #endif // MINGW not defined
93 }
94 
95 /**
96  * @brief Like a PyRun_InteractiveLoop that does not need a FILE*
97  * Use InteractiveConsole to work around the issue of mingw FILE*
98  * not being compatible with Python via the CAPI on windows11.
99  * @return 0 on success, nonzero on failure.
100  */
102  int code{};
103  std::string lines[3]{
104  "import code as nrnmingw_code\n",
105  "nrnmingw_interpreter = nrnmingw_code.InteractiveConsole(locals=globals())\n",
106  "nrnmingw_interpreter.interact(\"\")\n"};
107  for (const auto& line: lines) {
108  if (PyRun_SimpleString(line.c_str())) {
109  PyErr_Print();
110  return -1;
111  }
112  }
113  return 0;
114 }
115 
116 static wchar_t** wcargv;
117 
118 static void del_wcargv(int argc) {
119  if (wcargv) {
120  for (int i = 0; i < argc; ++i) {
121  PyMem_Free(wcargv[i]);
122  }
123  PyMem_Free(wcargv);
124  wcargv = NULL;
125  }
126 }
127 
128 static void copy_argv_wcargv(int argc, char** argv) {
129  del_wcargv(argc);
130  // basically a copy of code from Modules/python.c
131  wcargv = (wchar_t**) PyMem_Malloc(sizeof(wchar_t*) * argc);
132  if (!wcargv) {
133  fprintf(stderr, "out of memory\n");
134  exit(1);
135  }
136  for (int i = 0; i < argc; ++i) {
137  wcargv[i] = Py_DecodeLocale(argv[i], NULL);
138  if (!wcargv[i]) {
139  fprintf(stderr, "out of memory\n");
140  exit(1);
141  }
142  }
143 }
144 
145 static wchar_t* mywstrdup(char* s) {
146  size_t sz = mbstowcs(NULL, s, 0);
147  wchar_t* ws = new wchar_t[sz + 1];
148  int count = mbstowcs(ws, s, sz + 1);
149  return ws;
150 }
151 
152 /** @brief Start the Python interpreter.
153  * @arg b Mode of operation, can be 0 (finalize), 1 (initialize),
154  * or 2 (execute commands/scripts)
155  * @return 0 on success, non-zero on error
156  *
157  * There is an internal state variable that stores whether or not Python has
158  * been initialized. Mode 1 only has an effect if Python is not initialized,
159  * while the other modes only take effect if Python is already initialized.
160  */
161 extern "C" int nrnpython_start(int b) {
162 #if USE_PYTHON
163  static int started = 0;
164  // printf("nrnpython_start %d started=%d\n", b, started);
165  if (b == 1 && !started) {
167  if (nrnpy_nositeflag) {
168  Py_NoSiteFlag = 1;
169  }
170  // nrnpy_pyhome hopefully holds the python base root and should
171  // work with virtual environments.
172  // But use only if not overridden by the PYTHONHOME environment variable.
173  char* _p_pyhome = getenv("PYTHONHOME");
174  if (_p_pyhome == NULL) {
175  _p_pyhome = nrnpy_pyhome;
176  }
177  if (_p_pyhome) {
178  Py_SetPythonHome(mywstrdup(_p_pyhome));
179  }
180  Py_Initialize();
181 #if NRNPYTHON_DYNAMICLOAD
182  // return from Py_Initialize means there was no site problem
183  nrnpy_site_problem = 0;
184 #endif
186  PySys_SetArgv(nrn_global_argc, wcargv);
187  started = 1;
188  // see nrnpy_reg.h
189  for (int i = 0; nrnpy_reg_[i]; ++i) {
190  (*nrnpy_reg_[i])();
191  }
193  }
194  if (b == 0 && started) {
195  PyGILState_STATE gilsav = PyGILState_Ensure();
197  Py_Finalize();
198  // because of finalize, no PyGILState_Release(gilsav);
199  started = 0;
200  }
201  if (b == 2 && started) {
202  int i;
204  PySys_SetArgv(nrn_global_argc, wcargv);
206 
207  // Used to crash with MINGW when assocated with a python gui thread e.g
208  // from neuron import h, gui
209  // g = h.Graph()
210  // del g
211  // Also, NEURONMainMenu/File/Quit did not work. The solution to both
212  // seems to be to just avoid gui threads if MINGW and launched nrniv
213  PyOS_ReadlineFunctionPointer = nrnpython_getline;
214 
215  // Is there a -c "command" or file.py arg.
216  bool python_error_encountered{false};
217  for (i = 1; i < nrn_global_argc; ++i) {
218  char* arg = nrn_global_argv[i];
219  if (strcmp(arg, "-c") == 0 && i + 1 < nrn_global_argc) {
220  if (PyRun_SimpleString(nrn_global_argv[i + 1])) {
221  python_error_encountered = true;
222  }
223  break;
224  } else if (strlen(arg) > 3 && strcmp(arg + strlen(arg) - 3, ".py") == 0) {
225  if (!nrnpy_pyrun(arg)) {
226  python_error_encountered = true;
227  }
228  break;
229  }
230  }
231  // python_error_encountered dictates whether NEURON will exit with a nonzero
232  // code. In noninteractive/batch mode that happens immediately, in
233  // interactive mode then we start a Python interpreter first.
234  if (nrn_istty_) {
235 #if !defined(MINGW)
236  PyRun_InteractiveLoop(hoc_fin, "stdin");
237 #else
238  // mingw FILE incompatible with windows11 Python FILE.
240  if (ret) {
241  python_error_encountered = ret;
242  }
243 #endif
244  }
245  return python_error_encountered;
246  }
247 #endif // USE_PYTHON
248  return 0;
249 }
250 
251 extern "C" void nrnpython_real() {
252  int retval = 0;
253 #if USE_PYTHON
255  PyLockGIL lock;
256  retval = PyRun_SimpleString(gargstr(1)) == 0;
257  }
259 #endif
260  hoc_retpushx(double(retval));
261 }
262 
263 static char* nrnpython_getline(FILE*, FILE*, const char* prompt) {
264  hoc_cbufstr->buf[0] = '\0';
265  hoc_promptstr = prompt;
266  int r = hoc_get_line();
267  // printf("r=%d c=%d\n", r, hoc_cbufstr->buf[0]);
268  if (r == 1) {
269  size_t n = strlen(hoc_cbufstr->buf) + 1;
270  hoc_ctp = hoc_cbufstr->buf + n - 1;
271  char* p = static_cast<char*>(PyMem_RawMalloc(n));
272  if (p == 0) {
273  return 0;
274  }
275  strcpy(p, hoc_cbufstr->buf);
276  return p;
277  } else if (r == EOF) {
278  char* p = static_cast<char*>(PyMem_RawMalloc(2));
279  if (p == 0) {
280  return 0;
281  }
282  p[0] = '\0';
283  return p;
284  }
285  return 0;
286 }
static Frame * fp
Definition: code.cpp:161
sprintf(buf, " if (secondorder) {\n" " int _i;\n" " for (_i = 0; _i < %d; ++_i) {\n" " _p[_slist%d[_i]] += dt*_p[_dlist%d[_i]];\n" " }}\n", numeqn, listnum, listnum)
void hoc_retpushx(double x)
Definition: hocusr.cpp:154
#define assert(ex)
Definition: hocassrt.h:32
#define HocContextRestore
Definition: hoccontext.h:17
#define HocTopContextSet
Definition: hoccontext.h:10
#define gargstr
Definition: hocdec.h:14
static int started
Definition: init.c:142
static int argc
Definition: inithoc.cpp:53
static char ** argv
Definition: inithoc.cpp:54
static char line[MAXLINE]
Definition: ivecop.c:35
int nrn_istty_
Definition: hoc.cpp:882
char * getenv(const char *s)
Definition: macprt.cpp:67
#define i
Definition: md1redef.h:12
#define fprintf
Definition: mwprefix.h:30
int const size_t const size_t n
Definition: nrngsl.h:11
size_t p
static PyObject *(* nrnpy_reg_[])()
Definition: nrnpy_reg.h:4
char * neuronhome_forward()
Definition: code2.cpp:190
char * nrnpy_pyhome
Definition: nrnpy.cpp:69
int nrnpy_nositeflag
Definition: ivocmain.cpp:183
static wchar_t ** wcargv
Definition: nrnpython.cpp:116
static wchar_t * mywstrdup(char *s)
Definition: nrnpython.cpp:145
int nrn_global_argc
Definition: nrnpython.cpp:34
HocStr * hoc_cbufstr
Definition: hoc.cpp:165
void nrnpy_augment_path()
Definition: nrnpython.cpp:44
static void del_wcargv(int argc)
Definition: nrnpython.cpp:118
static int nrnmingw_pyrun_interactiveloop()
Like a PyRun_InteractiveLoop that does not need a FILE* Use InteractiveConsole to work around the iss...
Definition: nrnpython.cpp:101
static void copy_argv_wcargv(int argc, char **argv)
Definition: nrnpython.cpp:128
int hoc_get_line()
Definition: hoc.cpp:1859
int nrnpy_pyrun(const char *)
Execute a Python script.
Definition: nrnpython.cpp:69
static char * nrnpython_getline(FILE *, FILE *, const char *)
Definition: nrnpython.cpp:263
const char * hoc_promptstr
Definition: hoc.cpp:166
FILE * hoc_fin
void nrnpython_real()
Definition: nrnpython.cpp:251
char ** nrn_global_argv
Definition: nrnpython.cpp:35
int nrnpython_start(int)
Start the Python interpreter.
Definition: nrnpython.cpp:161
char * hoc_ctp
int(* p_nrnpy_pyrun)(const char *)
Definition: hoc.cpp:38
void rl_stuff_char(int)
#define lock
static realtype retval
bool isDirExist(const std::string &path)
Definition: ocfile.cpp:545
#define arg
Definition: redef.h:28
#define ret
Definition: redef.h:123
FILE * fopen()
#define NULL
Definition: sptree.h:16
Definition: hocstr.h:7
char * buf
Definition: hocstr.h:8
static const char * fname(const char *name)
Definition: nrnbbs.cpp:113