Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nrnpyenv.sh determines PYTHONHOME more robustly. #589

Merged
merged 4 commits into from
Jun 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 98 additions & 25 deletions bin/nrnpyenv.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# environment as python

# May specify the python executable with explicit first argument.
# Without arg use python and if that does not exist then python3
# Without arg use python3 and if that does not exist then python

# Overcome environment issues when --with-nrnpython=dynamic .

Expand Down Expand Up @@ -112,40 +112,60 @@ fi
echo "# PYTHON=`$WHICH $PYTHON`"

# what is the python library for Darwin
z=''
nrnpylib_provenance=""
nrn_pylib=""
kernel_name=''
if type -P uname > /dev/null ; then
z=`uname`
kernel_name=`uname`
fi
if test "$z" = "Darwin" ; then
p=`$WHICH $PYTHON`
d=`dirname $p`
if test "$kernel_name" = "Darwin" ; then
python_path=`$WHICH $PYTHON`
pyexedir=`dirname $python_path`
# Get the python lib dir in an official way, working with virtualenv
PYLIB=$($p -c 'from distutils import sysconfig; print(sysconfig.get_config_var("LIBDIR"))')
PYLIB=$($python_path -c 'from distutils import sysconfig; print(sysconfig.get_config_var("LIBDIR"))')
for path in $PYLIB/libpython*.dylib; do
if test -f "$path"; then
l="$path"
nrn_pylib="$path"
break
fi
done
if test -f "$l" ; then
z="$l"
unset p
unset d
unset l
else
if test -f "$nrn_pylib" ; then
unset python_path
unset pyexedir
nrnpylib_provenance="sysconfig LIBDIR"
fi
if test "$nrn_pylib" = "" ; then
nrn_pylib=$($p -c '
try:
from neuron import h
shlib=h.libpython_path()
shlib = shlib if ".dylib" in shlib else ""
print(shlib)
except:
print("")
')
if test "$nrn_pylib" != "" ; then
nrnpylib_provenance="h.libpython_path()"
fi
fi
if test "$nrn_pylib" = "" ; then
DYLD_PRINT_LIBRARIES=1
export DYLD_PRINT_LIBRARIES
z=`$PYTHON -c 'quit()' 2>&1 | sed -n 's/^dyld: loaded: //p' | sed -n /libpython/p`
if test "$z" = "" ; then
z=`$PYTHON -c 'quit()' 2>&1 | sed -n 's/^dyld: loaded: //p' | sed -n 2p`
nrn_pylib=`$PYTHON -c 'quit()' 2>&1 | sed -n 's/^dyld: loaded: //p' | sed -n /libpython/p`
if test "$nrn_pylib" = "" ; then
nrn_pylib=`$PYTHON -c 'quit()' 2>&1 | sed -n 's/^dyld: loaded: //p' | sed -n 2p`
fi
unset DYLD_PRINT_LIBRARIES
if test "$nrn_pylib" != "" ; then
nrnpylib_provenance=DYLD_PRINT_LIBRARIES
fi
fi
if test -f "$z" ; then
PYLIB_DARWIN=$z
if test -f "$nrn_pylib" ; then
PYLIB_DARWIN=$nrn_pylib
else
PYLIB_DARWIN=""
fi
export nrnpylib_provenance
export PYLIB_DARWIN
fi

Expand All @@ -157,6 +177,9 @@ import sys, os, site
usep = "/"
upathsep = ":"

nrnpylib_provenance = "not found"
nrnpyhome_provenance = "not found"

def upath(path):
#return linux path
if path == None:
Expand Down Expand Up @@ -185,6 +208,7 @@ def u2d(p):

#a copy of nrnpylib_linux() but with some os x specific modifications
def nrnpylib_darwin_helper():
global nrnpylib_provenance
import os, sys, re, subprocess
#in case it was dynamically loaded by python
pid = os.getpid()
Expand All @@ -203,6 +227,7 @@ def nrnpylib_darwin_helper():
if re.search(r'libpython.*\.[ds]', line):
print ("# nrn_pylib from lsof: %s" % line)
nrn_pylib = line.strip()
nrnpylib_provenance = "lsof search for libpython..."
return nrn_pylib
if re.search(r'[Ll][Ii][Bb].*[Pp]ython', line):
cnt += 1
Expand All @@ -211,6 +236,7 @@ def nrnpylib_darwin_helper():
if re.search(r'[Pp]ython', line.split('/')[-1]):
print ("# nrn_pylib from lsof: %s" % line)
nrn_pylib = line.strip()
nrnpylib_provenance = 'lsof search for second occurrence of [Ll][Ii][Bb].*[Pp]ython'
return nrn_pylib
else: # figure it out from the os path
p = os.path.sep.join(os.__file__.split(os.path.sep)[:-1])
Expand All @@ -230,6 +256,7 @@ def nrnpylib_darwin_helper():
libs.append(line.strip())
print ('# %s'%str(libs))
if len(libs) == 1:
nrnpylib_provenance="search based on os.__file__, found unique"
print ("# nrn_pylib from os.path %s"%str(libs[0]))
return libs[0]
if len(libs) > 1:
Expand All @@ -256,19 +283,23 @@ def nrnpylib_darwin_helper():
for i in libs:
if name in i:
print ("# nrn_pylib from os.path %s" % i)
nrnpylib_provenance='search based on os.__file__, found one with version in name'
return i
print ("# nrn_pylib from os.path %s" % str(nrn_pylib))
return nrn_pylib

def nrnpylib_darwin():
global nrnpylib_provenance
import os
nrn_pylib = os.getenv("PYLIB_DARWIN")
if nrn_pylib != "":
print ("# nrn_pylib from PYLIB_DARWIN %s"%nrn_pylib)
nrnpylib_provenance = os.getenv("nrnpylib_provenance")
return nrn_pylib
return nrnpylib_darwin_helper()

def nrnpylib_mswin():
global nrnpylib_provenance
import os, sys, re
e = '/'.join(sys.executable.split(os.path.sep))
cmd = 'cygcheck "%s"' % e
Expand All @@ -277,11 +308,36 @@ def nrnpylib_mswin():
for line in f:
if re.search('ython[a-zA-Z0-9_.]*\.dll', line):
nrn_pylib = '/'.join(line.split(os.path.sep)).strip()
nrnpylib_provenance="cygcheck"
return nrn_pylib

def nrnpylib_linux():
global nrnpylib_provenance
import os, sys, re, subprocess

# Try the official way first
from distutils import sysconfig
libdir=sysconfig.get_config_var("LIBDIR")
try:
from os.path import isfile, join
for f in os.listdir(libdir):
if 'libpython' in f and '.so' in f:
nrn_pylib = join(libdir, f)
nrnpylib_provenance='sysconfig LIBDIR'
return nrn_pylib
except:
pass

#in case it was dynamically loaded by python
try:
from neuron import h
s=h.libpython_path()
s = s if ".so" in s else ""
if (s != ""):
nrnpylib_provenance="h.libpython_path()"
return s
except:
print("")
pid = os.getpid()
cmd = "lsof -p %d"%pid
f = []
Expand All @@ -297,6 +353,7 @@ def nrnpylib_linux():
if re.search(r'libpython.*\.so', line):
print ("# from lsof: %s" % line)
nrn_pylib = line.strip()
nrnpylib_provenance = 'lsof search for libpython.*\.so'
return nrn_pylib
else: # figure it out from the os path
p = os.path.sep.join(os.__file__.split(os.path.sep)[:-1])
Expand All @@ -316,6 +373,7 @@ def nrnpylib_linux():
libs.append(line.strip())
print ('# %s'%str(libs))
if len(libs) == 1:
nrnpylib_provenance="search based on os.__file__, found unique"
return libs[0]
if len(libs) > 1:
# which one do we want? Check the name of an imported shared object
Expand All @@ -335,6 +393,7 @@ def nrnpylib_linux():
pass
for i in libs:
if name in i:
nrnpylib_provenance='search based on os.__file__, found one with version in name'
return i
return nrn_pylib

Expand All @@ -346,23 +405,32 @@ elif 'win' in sys.platform:
elif 'linux' in sys.platform:
nrn_pylib = nrnpylib_linux()

#Use sys.base_prefix for PYTHONHOME if available, otherwise sys.prefix
try:
sp = upath(sys.base_prefix)
spname='sys.base_prefix'
base=True
except:
sp = upath(sys.prefix)
spname='sys.prefix'
base=False

#there is a question about whether to use sys.prefix for PYTHONHOME
#or whether to derive from site.__file__.
#to help answer, ask how many sys.path items begin with sys.prefix and
#how many begin with site.__file__ - 3
p = [upath(i) for i in sys.path]
print ("# items in sys.path = " + str(len(p)))
sp = upath(sys.prefix)
print ("# beginning with sys.prefix = " + str(len([i for i in p if sp in i])))
s = usep.join(upath(site.__file__).split(usep)[:-3])
if s == sp:
print ("# site-3 same as sys.prefix")
print ("# site-3 same as " + spname)
else:
print ("# beginning with site-3 = " + str(len([i for i in p if s in i])))
foo = [i for i in p if sp not in i]
foo = [i for i in foo if s not in i]
print ("# in neither location " + str(foo))
print ("# sys.prefix = " + sp)
print ("# " + spname + " = " + sp)
print ("# site-3 = " + s)

if "darwin" in sys.platform or "linux" in sys.platform or "win" in sys.platform:
Expand All @@ -374,7 +442,8 @@ if "darwin" in sys.platform or "linux" in sys.platform or "win" in sys.platform:
if i > 1:
path = newpath[:i]

pythonhome = upath(sys.prefix)
pythonhome = upath(sp)
print ("#pythonhome=" + pythonhome)
pythonpath = upath(os.getenv("PYTHONPATH"))

ldpath = ""
Expand Down Expand Up @@ -409,14 +478,18 @@ if "darwin" in sys.platform or "linux" in sys.platform or "win" in sys.platform:
if pythonpath:
print ("\n# if launch python, then need:")
print ("export PYTHONPATH=" + dq + pythonpath + dq)

if path:
print ("\n#PYTHON prepended the following to PATH")
print ("export PATH=" + dq + path + "$PATH" + dq)

print("\n#NRN_PYLIB provenance: " + str(nrnpylib_provenance))
print ("\n# if launch nrniv, then likely need:")
if pythonhome:
pythonhome=u2d(pythonhome)
print ("export PYTHONHOME=" + dq + pythonhome + dq)
if ldpath and nrn_pylib == None:
print ("export LD_LIBRARY_PATH=" + dq + ldpath + upathsep + "$LD_LIBRARY_PATH" + dq)
if path:
print ("export PATH=" + dq + path + "$PATH" + dq)
if nrn_pylib != None:
print ('export NRN_PYLIB="%s"' % nrn_pylib)

Expand Down
24 changes: 24 additions & 0 deletions src/nrnpython/nrnpy_hoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
#include "nrnpy_utils.h"
#include "../nrniv/shapeplt.h"
#include <vector>
#if defined(HAVE_DLFCN_H)
#include <dlfcn.h>
#endif

#if defined(NRNPYTHON_DYNAMICLOAD) && NRNPYTHON_DYNAMICLOAD > 0
// when compiled with different Python.h, force correct value
Expand Down Expand Up @@ -2671,6 +2674,25 @@ static PyObject* hocpickle_setstate(PyObject* self, PyObject* args) {
return Py_None;
}

static PyObject* libpython_path(PyObject* self, PyObject* args) {
#if defined(HAVE_DLFCN_H)
Dl_info info;
int rval = dladdr((const void*)Py_Initialize, &info);
if (!rval) {
PyErr_SetString(PyExc_Exception, "dladdr: Py_Initialize could not be matched to a shared object");
return NULL;
}
if (!info.dli_fname) {
PyErr_SetString(PyExc_Exception, "dladdr: No symbol matching Py_Initialize could be found.");
return NULL;
}
return Py_BuildValue("s", info.dli_fname);
#else
Py_INCREF(Py_None);
return Py_None;
#endif
}

// available for every HocObject
static PyMethodDef hocobj_methods[] = {
{"baseattr", hocobj_baseattr, METH_VARARGS,
Expand All @@ -2695,6 +2717,8 @@ static PyMethodDef toplevel_methods[] = {
"Return a new Section"},
{"setpointer", setpointer, METH_VARARGS,
"Assign hoc variable address to NMODL POINTER"},
{"libpython_path", libpython_path, METH_NOARGS,
"Return full path to file that contains Py_Initialize()"},
{NULL, NULL, 0, NULL}};

static void add2topdict(PyObject* dict) {
Expand Down