NEURON
nrnpy.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 // For Linux and Max OS X,
3 // Solve the problem of not knowing what version of Python the user has by
4 // possibly deferring linking to libnrnpython.so to run time using the proper
5 // Python interface
6 
7 #include <../nrnpython/nrnpython_config.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <InterViews/resource.h>
11 #include "nrnoc2iv.h"
12 #include "classreg.h"
13 #include "nonvintblock.h"
14 #include "nrnmpi.h"
15 
16 #include <algorithm>
17 #include <cctype>
18 
19 extern int nrn_nopython;
20 extern int nrnpy_nositeflag;
21 extern char* nrnpy_pyexe;
22 extern int nrn_is_python_extension;
24 extern int (*p_nrnpython_start)(int);
25 void nrnpython();
26 static void (*p_nrnpython_real)();
28 extern "C" char* hoc_back2forward(char* s);
29 char* hoc_forward2back(char* s);
30 #if DARWIN
31 extern void nrn_possible_mismatched_arch(const char*);
32 #endif
33 
34 // following is undefined or else has the value of sys.api_version
35 // at time of configure (using the python first in the PATH).
36 #if defined(NRNPYTHON_DYNAMICLOAD)
37 
38 #include "nrnwrap_dlfcn.h"
39 #if !defined(RTLD_NOLOAD)
40 #define RTLD_NOLOAD 0
41 #endif // RTLD_NOLOAD
42 
43 extern char* neuron_home;
44 
45 #if NRNPYTHON_DYNAMICLOAD >= 30
46 
47 #ifdef MINGW
48 static const char* ver[] = {"3.7", 0};
49 #else
50 static const char* ver[] = {"3.10", "3.9", "3.8", "3.7", 0};
51 #endif // !MINGW
52 
53 #else
54 
55 static const char* ver[] = {0};
56 
57 #endif
58 
59 static int iver; // which python is loaded?
60 static void* python_already_loaded();
61 static void* load_python();
62 static void load_nrnpython(int, const char*);
63 #else //! defined(NRNPYTHON_DYNAMICLOAD)
64 extern "C" int nrnpython_start(int);
65 extern "C" void nrnpython_reg_real();
66 extern "C" void nrnpython_real();
67 #endif // defined(NRNPYTHON_DYNAMICLOAD)
68 
70 
71 void nrnpython() {
72 #if USE_PYTHON
73  if (p_nrnpython_real) {
74  (*p_nrnpython_real)();
75  return;
76  }
77 #endif
78  hoc_retpushx(0.);
79 }
80 
81 // Stub class for when Python does not exist
82 static void* p_cons(Object*) {
83  return 0;
84 }
85 static void p_destruct(void* v) {}
86 static Member_func p_members[] = {0, 0};
87 
88 #if NRNPYTHON_DYNAMICLOAD
89 static char* nrnpy_pylib;
90 
91 static void siteprob(void) {
93  printf("Py_Initialize exited. PYTHONHOME probably needs to be set correctly.\n");
94  if (nrnpy_pyhome) {
95  printf(
96  "The value of PYTHONHOME or our automatic guess based on the output of "
97  "nrnpyenv.sh:\n export PYTHONHOME=%s\ndid not work.\n",
98  nrnpy_pyhome);
99  }
100  printf(
101  "It will help to examine the output of:\nnrnpyenv.sh\n\
102 and set the indicated environment variables, or avoid python by adding\n\
103 nopython: on\n\
104 to %s/lib/nrn.defaults (or .nrn.defaults in your $HOME directory)\n",
105  neuron_home);
106  }
107 }
108 
109 static void set_nrnpylib() {
110  nrnpy_pylib = getenv("NRN_PYLIB");
111  nrnpy_pyhome = getenv("NRN_PYTHONHOME");
112  if (nrnpy_pylib && nrnpy_pyhome) {
113  return;
114  }
115  // copy allows free of the copy if needed
116  if (nrnpy_pylib) {
117  nrnpy_pylib = strdup(nrnpy_pylib);
118  }
119  if (nrnpy_pyhome) {
120  nrnpy_pyhome = strdup(nrnpy_pyhome);
121  }
122 
123  if (nrnmpi_myid_world == 0) {
124  int linesz = 1024 + (nrnpy_pyexe ? strlen(nrnpy_pyexe) : 0);
125 #ifdef MINGW
126  linesz += 3 * strlen(neuron_home);
127  char* line = new char[linesz + 1];
128  char* bnrnhome = strdup(neuron_home);
129  char* fnrnhome = strdup(neuron_home);
130  hoc_forward2back(bnrnhome);
131  hoc_back2forward(fnrnhome);
132  sprintf(line,
133  "%s\\mingw\\usr\\bin\\bash %s/bin/nrnpyenv.sh %s --NEURON_HOME=%s",
134  bnrnhome,
135  fnrnhome,
136  (nrnpy_pyexe && strlen(nrnpy_pyexe) > 0) ? nrnpy_pyexe : "",
137  fnrnhome);
138  free(fnrnhome);
139  free(bnrnhome);
140 #else
141  char* line = new char[linesz + 1];
142  sprintf(line,
143  "bash %s/../../bin/nrnpyenv.sh %s",
144  neuron_home,
145  (nrnpy_pyexe && strlen(nrnpy_pyexe) > 0) ? nrnpy_pyexe : "");
146 #endif
147  FILE* p = popen(line, "r");
148  if (!p) {
149  printf("could not popen '%s'\n", line);
150  } else {
151  if (!fgets(line, linesz, p)) {
152  printf("failed: %s\n", line);
153  }
154  while (fgets(line, linesz, p)) {
155  char* cp;
156  // must get rid of beginning '"' and trailing '"\n'
157  if (!nrnpy_pyhome && (cp = strstr(line, "export NRN_PYTHONHOME="))) {
158  cp += strlen("export NRN_PYTHONHOME=") + 1;
159  cp[strlen(cp) - 2] = '\0';
160  if (nrnpy_pyhome) {
161  free(nrnpy_pyhome);
162  }
163  nrnpy_pyhome = strdup(cp);
164  } else if (!nrnpy_pylib && (cp = strstr(line, "export NRN_PYLIB="))) {
165  cp += strlen("export NRN_PYLIB=") + 1;
166  cp[strlen(cp) - 2] = '\0';
167  if (nrnpy_pylib) {
168  free(nrnpy_pylib);
169  }
170  nrnpy_pylib = strdup(cp);
171  }
172  }
173  pclose(p);
174  }
175  delete[] line;
176  }
177 #if NRNMPI
178  if (nrnmpi_numprocs_world > 1) { // 0 broadcasts to everyone else.
179  nrnmpi_char_broadcast_world(&nrnpy_pylib, 0);
180  nrnmpi_char_broadcast_world(&nrnpy_pyhome, 0);
181  }
182 #endif
183 }
184 
185 #if 0
186 static void set_pythonhome(void* handle){
187  if (nrnmpi_myid == 0) {atexit(siteprob);}
188 #ifdef MINGW
189 #else
190  if (getenv("PYTHONHOME") || nrnpy_nositeflag) { return; }
191  if (nrnpy_pyhome) {
192  int res = setenv("PYTHONHOME", nrnpy_pyhome, 1);
193  assert(res == 0);
194  return;
195  }
196 
198  void* s = dlsym(handle, "Py_Initialize");
199  assert(s != NULL);
200  int success = dladdr(s, &dl_info);
201  if (success) {
202  //printf("%s\n", dl_info.dli_fname);
203  nrnpy_pyhome = strdup(dl_info.dli_fname);
204  char* p = nrnpy_pyhome;
205  int n = strlen(p);
206  int seen = 0;
207  for (int i = n-1; i > 0; --i) {
208  if (p[i] == '/') {
209  if (++seen >= 2) {
210  p[i] = '\0' ;
211  break;
212  }
213  }
214  }
215  int res = setenv("PYTHONHOME", p, 1);
216  assert(res == 0);
217  }
218 #endif
219 }
220 #endif // if 0
221 #endif
222 
224  // printf("nrnpython_reg in nrnpy.cpp\n");
225 #if USE_PYTHON
226  if (nrn_nopython) {
227  p_nrnpython_start = 0;
228  p_nrnpython_real = 0;
230  } else {
231 #if NRNPYTHON_DYNAMICLOAD
232  void* handle = NULL;
233 
235  // As last resort (or for python3) load $NRN_PYLIB
236  set_nrnpylib();
237  // printf("nrnpy_pylib %s\n", nrnpy_pylib);
238  // printf("nrnpy_pyhome %s\n", nrnpy_pyhome);
239  if (nrnpy_pylib) {
240  handle = dlopen(nrnpy_pylib, RTLD_NOW | RTLD_GLOBAL);
241  if (!handle) {
242  fprintf(stderr, "Could not dlopen NRN_PYLIB: %s\n", nrnpy_pylib);
243 #if DARWIN
244  nrn_possible_mismatched_arch(nrnpy_pylib);
245 #endif
246  exit(1);
247  }
248  }
249  if (!handle) {
250  python_already_loaded();
251  }
252  if (!handle) { // embed python
253  handle = load_python();
254  }
255 #if 0
256  // No longer do this as Py_SetPythonHome is used
257  if (handle) {
258  // need to worry about the site.py problem
259  // can fix with a proper PYTHONHOME but need to know
260  // what path was used to load the python library.
261  set_pythonhome(handle);
262  }
263 #endif
264  } else {
265  // printf("nrn_is_python_extension = %d\n", nrn_is_python_extension);
266  }
267  // for some mysterious reason on max osx 10.12
268  // (perhaps due to System Integrity Protection?) when python is
269  // launched, python_already_loaded() returns a NULL handle unless
270  // the full path to the dylib is used. Since we know it is loaded
271  // in these circumstances, it is sufficient to go ahead and dlopen
272  // the nrnpython interface library
273  if (handle || nrn_is_python_extension) {
274  load_nrnpython(nrn_is_python_extension, nrnpy_pylib);
275  }
276 #else
280 #endif
281  }
282  if (p_nrnpython_reg_real) {
283  (*p_nrnpython_reg_real)();
284  if (nrnpy_site_problem_p) {
286  }
287  return;
288  }
289 #endif
290  class2oc("PythonObject", p_cons, p_destruct, p_members, NULL, NULL, NULL);
291 }
292 
293 #if NRNPYTHON_DYNAMICLOAD // to end of file
294 
295 // important dlopen flags :
296 // RTLD_NOLOAD returns NULL if not open, or handle if it is resident.
297 
298 static void* ver_dlo(int flag) {
299  for (int i = 0; ver[i]; ++i) {
300  char name[100];
301 #ifdef MINGW
302  sprintf(name, "python%c%c.dll", ver[i][0], ver[i][2]);
303 #else
304 #if DARWIN
305  sprintf(name, "libpython%s.dylib", ver[i]);
306 #else
307  sprintf(name, "libpython%s.so", ver[i]);
308 #endif
309 #endif
310  void* handle = dlopen(name, flag);
311  iver = i;
312  if (handle) {
313  return handle;
314  }
315  }
316  iver = -1;
317  return NULL;
318 }
319 
320 static void* python_already_loaded() {
321  void* handle = ver_dlo(RTLD_NOW | RTLD_GLOBAL | RTLD_NOLOAD);
322  // printf("python_already_loaded %d\n", iver);
323  return handle;
324 }
325 
326 static void* load_python() {
327  void* handle = ver_dlo(RTLD_NOW | RTLD_GLOBAL);
328  // printf("load_python %d\n", iver);
329  return handle;
330 }
331 
332 static void* load_sym(void* handle, const char* name) {
333  void* p = dlsym(handle, name);
334  if (!p) {
335  printf("Could not load %s\n", name);
336  exit(1);
337  }
338  return p;
339 }
340 
341 static void* load_nrnpython_helper(const char* npylib) {
342  char name[2048];
343 #ifdef MINGW
344  sprintf(name, "%s.dll", npylib);
345 #else // !MINGW
346 #if DARWIN
347  sprintf(name, "%s/../../lib/%s.dylib", neuron_home, npylib);
348 #else // !DARWIN
349  sprintf(name, "%s/../../lib/%s.so", neuron_home, npylib);
350 #endif // DARWIN
351 #endif // MINGW
352  void* handle = dlopen(name, RTLD_NOW);
353  return handle;
354 }
355 
356 // Get python version as integer from pythonlib path
357 static int pylib2pyver10(std::string pylib) {
358  // skip past last \ or /
359  const auto pos = pylib.find_last_of("/\\");
360  if (pos != std::string::npos) {
361  pylib = pylib.substr(pos + 1);
362  }
363 
364  // erase nondigits
365  pylib.erase(std::remove_if(pylib.begin(), pylib.end(), [](char c) { return !std::isdigit(c); }),
366  pylib.end());
367 
368  // parse number. 0 is fine to return as error (no need for stoi)
369  return std::atoi(pylib.c_str());
370 }
371 
372 static void load_nrnpython(int pyver10, const char* pylib) {
373  void* handle = NULL;
374 #if (defined(__MINGW32__) || \
375  (defined(USE_LIBNRNPYTHON_MAJORMINOR) && USE_LIBNRNPYTHON_MAJORMINOR == 1))
376  char name[256];
377  int pv10 = pyver10;
378  if (pyver10 < 1 && pylib) {
379  pv10 = pylib2pyver10(pylib);
380  }
381  sprintf(name, "libnrnpython%d", pv10);
382  handle = load_nrnpython_helper(name);
383  if (!handle) {
384  printf("Could not load %s\n", name);
385  printf("pyver10=%d pylib=%s\n", pyver10, pylib ? pylib : "NULL");
386  return;
387  }
388 #else
389  handle = load_nrnpython_helper("libnrnpython3");
390  if (!handle) {
391  printf("Could not load libnrnpython3\n");
392  printf("pyver10=%d pylib=%s\n", pyver10, pylib ? pylib : "NULL");
393  return;
394  }
395 #endif
396  p_nrnpython_start = (int (*)(int)) load_sym(handle, "nrnpython_start");
397  p_nrnpython_real = (void (*)()) load_sym(handle, "nrnpython_real");
398  p_nrnpython_reg_real = (void (*)()) load_sym(handle, "nrnpython_reg_real");
399 }
400 
401 #endif
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)
DLFCN_EXPORT void * dlopen(const char *file, int mode)
Definition: dlfcn.c:331
DLFCN_EXPORT int dladdr(const void *addr, Dl_info *info)
Definition: dlfcn.c:731
DLFCN_NOINLINE DLFCN_EXPORT void * dlsym(void *handle, const char *name)
Definition: dlfcn.c:447
#define RTLD_NOW
Definition: dlfcn.h:47
#define RTLD_GLOBAL
Definition: dlfcn.h:56
#define c
void hoc_retpushx(double x)
Definition: hocusr.cpp:154
#define assert(ex)
Definition: hocassrt.h:32
static char line[MAXLINE]
Definition: ivecop.c:35
void
const char * neuron_home
Definition: hoc_init.cpp:404
pclose(FILE *p)
Definition: macprt.cpp:107
char * getenv(const char *s)
Definition: macprt.cpp:67
FILE * popen(char *s1, char *s2)
Definition: macprt.cpp:102
#define v
Definition: md1redef.h:4
#define i
Definition: md1redef.h:12
char * name
Definition: init.cpp:16
#define printf
Definition: mwprefix.h:26
#define fprintf
Definition: mwprefix.h:30
int const size_t const size_t n
Definition: nrngsl.h:11
size_t p
int nrnmpi_myid
int nrnmpi_numprocs_world
int nrnmpi_myid_world
void class2oc(const char *, void *(*cons)(Object *), void(*destruct)(void *), Member_func *, int(*checkpoint)(void **), Member_ret_obj_func *, Member_ret_str_func *)
Definition: hoc_oop.cpp:1560
char * nrnpy_pyhome
Definition: nrnpy.cpp:69
int nrnpy_nositeflag
Definition: ivocmain.cpp:183
int nrnpython_start(int)
defined(NRNPYTHON_DYNAMICLOAD)
Definition: nrnpython.cpp:161
int nrn_nopython
void nrnpython_reg()
Definition: nrnpy.cpp:223
int nrn_is_python_extension
Definition: fileio.cpp:842
int * nrnpy_site_problem_p
Definition: nrnpy.cpp:23
int(* p_nrnpython_start)(int)
static void p_destruct(void *v)
Definition: nrnpy.cpp:85
void nrnpython_real()
Definition: nrnpython.cpp:251
char * hoc_back2forward(char *s)
char * nrnpy_pyexe
void nrnpython_reg_real()
Definition: nrnpy_p2h.cpp:138
static void(* p_nrnpython_reg_real)()
Definition: nrnpy.cpp:27
static void(* p_nrnpython_real)()
Definition: nrnpy.cpp:26
static void * p_cons(Object *)
Definition: nrnpy.cpp:82
void nrnpython()
Definition: nrnpy.cpp:71
char * hoc_forward2back(char *s)
static Member_func p_members[]
Definition: nrnpy.cpp:86
static char * ver[10]
Definition: nrnversion.cpp:16
#define NULL
Definition: sptree.h:16
Definition: hocdec.h:227
Definition: dlfcn.h:72
const char * dli_fname
Definition: dlfcn.h:73
char * strstr(char *cs, char *ct)
Definition: xred.cpp:173