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

Mechanisms loading cpp #2834

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
17 changes: 9 additions & 8 deletions share/lib/python/neuron/__init__.py
Expand Up @@ -290,12 +290,12 @@
# h.anyclass methods may be overridden. If so the base method can be called
# using the idiom self.basemethod = self.baseattr('methodname')
# ------------------------------------------------------------------------------

from pathlib import Path
import sys, types
from neuron.hclass3 import HocBaseObject, hclass

# global list of paths already loaded by load_mechanisms
nrn_dll_loaded = []
nrn_dll_loaded = set()


def load_mechanisms(path, warn_if_already_loaded=True):
Expand All @@ -313,9 +313,10 @@
import platform

global nrn_dll_loaded
if path in nrn_dll_loaded:
path_obj = Path(path).resolve()
if path_obj.as_posix() in nrn_dll_loaded:
if warn_if_already_loaded:
print("Mechanisms already loaded from path: %s. Aborting." % path)
print(f"Mechanisms already loaded from path: {path_obj}. Aborting.")

Check warning on line 319 in share/lib/python/neuron/__init__.py

View check run for this annotation

Codecov / codecov/patch

share/lib/python/neuron/__init__.py#L319

Added line #L319 was not covered by tests
return True

# in case NEURON is assuming a different architecture to Python,
Expand All @@ -332,10 +333,10 @@
arch_list = [""]

for arch in arch_list:
lib_path = os.path.join(path, arch, libsubdir, libname)
if os.path.exists(lib_path):
h.nrn_load_dll(lib_path)
nrn_dll_loaded.append(path)
lib_path = path_obj / arch / libsubdir / libname
if lib_path.exists():
h.nrn_load_dll(lib_path.as_posix())
nrn_dll_loaded.add(path_obj.as_posix())

Check warning on line 339 in share/lib/python/neuron/__init__.py

View check run for this annotation

Codecov / codecov/patch

share/lib/python/neuron/__init__.py#L338-L339

Added lines #L338 - L339 were not covered by tests
return True
print("NEURON mechanisms not found in %s." % path)
return False
Expand Down
28 changes: 28 additions & 0 deletions share/lib/python/neuron/tests/test_neuron.py
Expand Up @@ -5,7 +5,10 @@
$Id$
"""

from pathlib import Path
from tempfile import TemporaryDirectory
import unittest
from unittest.mock import patch
import neuron
from neuron import h

Expand Down Expand Up @@ -199,6 +202,31 @@
self.assertFalse(error)
return 0

def test_load_mechanisms_from_non_existent_path(self):
"""Assure mechanisms are not loaded from a non-existent directory."""
from neuron import load_mechanisms, nrn_dll_loaded

Check warning on line 207 in share/lib/python/neuron/tests/test_neuron.py

View check run for this annotation

Codecov / codecov/patch

share/lib/python/neuron/tests/test_neuron.py#L207

Added line #L207 was not covered by tests

mech_path1 = "fake_mechanisms_dir"
assert not load_mechanisms(mech_path1)
assert Path(mech_path1).resolve().as_posix() not in nrn_dll_loaded

Check warning on line 211 in share/lib/python/neuron/tests/test_neuron.py

View check run for this annotation

Codecov / codecov/patch

share/lib/python/neuron/tests/test_neuron.py#L209-L211

Added lines #L209 - L211 were not covered by tests

@patch("pathlib.Path.exists")
def test_load_mechanisms_reloading(self, mock_exists):
"""Assure the same directory is not loaded multiple times."""
from neuron import load_mechanisms, nrn_dll_loaded

Check warning on line 216 in share/lib/python/neuron/tests/test_neuron.py

View check run for this annotation

Codecov / codecov/patch

share/lib/python/neuron/tests/test_neuron.py#L216

Added line #L216 was not covered by tests

mock_exists.return_value = True # bypass path.exists()

Check warning on line 218 in share/lib/python/neuron/tests/test_neuron.py

View check run for this annotation

Codecov / codecov/patch

share/lib/python/neuron/tests/test_neuron.py#L218

Added line #L218 was not covered by tests

with TemporaryDirectory() as temp_dir:
mech_path1 = Path(temp_dir) / "mechanisms_dir"
mech_path1.mkdir()
assert load_mechanisms(mech_path1)
assert mech_path1.resolve().as_posix() in nrn_dll_loaded
assert len(nrn_dll_loaded) == 1
mech_path2 = mech_path1 / ".." / "mechanisms_dir"
assert load_mechanisms(mech_path2)
assert len(nrn_dll_loaded) == 1

Check warning on line 228 in share/lib/python/neuron/tests/test_neuron.py

View check run for this annotation

Codecov / codecov/patch

share/lib/python/neuron/tests/test_neuron.py#L220-L228

Added lines #L220 - L228 were not covered by tests

def testRxDexistence(self):
from neuron import config

Expand Down
93 changes: 42 additions & 51 deletions src/nrnoc/init.cpp
Expand Up @@ -22,6 +22,7 @@

#include <vector>
#include <unordered_map>
#include <unordered_set>

/* change this to correspond to the ../nmodl/nocpout nmodl_version_ string*/
static char nmodl_version_[] = "7.7.0";
Expand Down Expand Up @@ -224,71 +225,61 @@
return 1;
}

void* nrn_realpath_dlopen(const char* relpath, int flags) {
std::string nrn_realpath(const char* relpath) {
char* abspath = NULL;
void* handle = NULL;

/* use realpath or _fullpath even if is already a full path */

#if defined(HAVE_REALPATH)
abspath = realpath(relpath, NULL);
#else /* not HAVE_REALPATH */
#if defined(__MINGW32__)
#elif defined(__MINGW32__)
abspath = _fullpath(NULL, relpath, 0);
#else /* not __MINGW32__ */
#else
abspath = strdup(relpath);
#endif /* not __MINGW32__ */
#endif /* not HAVE_REALPATH */
if (abspath) {
handle = dlopen(abspath, flags);
#if DARWIN
if (!handle) {
nrn_possible_mismatched_arch(abspath);
}
#endif // DARWIN
free(abspath);
} else {
int patherr = errno;
handle = dlopen(relpath, flags);
if (!handle) {
Fprintf(stderr,
"realpath failed errno=%d (%s) and dlopen failed with %s\n",
patherr,
strerror(patherr),
relpath);
#if DARWIN
nrn_possible_mismatched_arch(abspath);
#endif // DARWIN
}
#endif
if (!abspath) {
Fprintf(stderr, "Failed to resolve path: %s, due to error: %s\n", relpath, strerror(errno));
return "";

Check warning on line 240 in src/nrnoc/init.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnoc/init.cpp#L239-L240

Added lines #L239 - L240 were not covered by tests
}
return handle;
std::string resolved_path(abspath);
free(abspath);
return resolved_path;
}

// Global set to keep track of loaded DLLs
static std::unordered_set<std::string> loaded_dlls;

int mswin_load_dll(const char* cp1) {
void* handle;
if (nrnmpi_myid < 1)
if (!nrn_nobanner_ && nrn_istty_) {
fprintf(stderr, "loading membrane mechanisms from %s\n", cp1);
}
std::string full_path = nrn_realpath(cp1);
if (full_path.empty()) {
return 0;

Check warning on line 253 in src/nrnoc/init.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnoc/init.cpp#L253

Added line #L253 was not covered by tests
}

// Check if the DLL has already been loaded
if (loaded_dlls.find(full_path) != loaded_dlls.end()) {
Fprintf(stderr, "DLL already loaded: %s\n", full_path.c_str());
return 1; // Return success as the DLL is already loaded

Check warning on line 259 in src/nrnoc/init.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnoc/init.cpp#L258-L259

Added lines #L258 - L259 were not covered by tests
}

// Load the DLL
void* handle = dlopen(full_path.c_str(), RTLD_NOW);
if (!handle) {
Fprintf(stderr, "dlopen failed: %s\n", dlerror());

Check warning on line 265 in src/nrnoc/init.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnoc/init.cpp#L265

Added line #L265 was not covered by tests
#if DARWIN
handle = nrn_realpath_dlopen(cp1, RTLD_NOW);
#else // not DARWIN
handle = dlopen(cp1, RTLD_NOW);
#endif // not DARWIN
if (handle) {
Pfrv mreg = (Pfrv) dlsym(handle, "modl_reg");
if (mreg) {
(*mreg)();
} else {
fprintf(stderr, "dlsym modl_reg failed\n%s\n", dlerror());
dlclose(handle);
return 0;
}
// Darwin-specific architecture mismatch handling
nrn_possible_mismatched_arch(full_path.c_str());
#endif
return 0;

Check warning on line 270 in src/nrnoc/init.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnoc/init.cpp#L270

Added line #L270 was not covered by tests
}

Pfrv mreg = (Pfrv) dlsym(handle, "modl_reg");
if (mreg) {
(*mreg)();
loaded_dlls.insert(full_path); // Insert the path into the set
return 1;
} else {
fprintf(stderr, "dlopen failed - \n%s\n", dlerror());
std::cerr << "dlsym modl_reg failed: " << dlerror() << std::endl;
dlclose(handle);
return 0;

Check warning on line 281 in src/nrnoc/init.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnoc/init.cpp#L279-L281

Added lines #L279 - L281 were not covered by tests
}
return 0;
}

void hoc_nrn_load_dll(void) {
Expand Down