7 #include <InterViews/session.h>
21 #include <nanobind/nanobind.h>
30 #if DARWIN || defined(__linux__)
31 extern const char* path_prefix_to_libnrniv();
44 #if defined(__linux__) || defined(DARWIN)
46 path = path_prefix_to_libnrniv();
58 struct PythonConfigWrapper {
59 PythonConfigWrapper() {
60 PyConfig_InitPythonConfig(&config);
62 ~PythonConfigWrapper() {
63 PyConfig_Clear(&config);
65 operator PyConfig*() {
68 PyConfig* operator->() {
73 struct PyMem_RawFree_Deleter {
74 void operator()(
wchar_t* ptr)
const {
84 void reset_sys_path(std::string_view new_first) {
85 nanobind::gil_scoped_acquire _{};
86 auto*
const path = PySys_GetObject(
"path");
89 nrn_assert(PyList_SetSlice(path, 0, PyList_Size(path),
nullptr) != -1);
91 auto*
const ustr = PyUnicode_DecodeFSDefaultAndSize(new_first.data(), new_first.size());
96 assert(basic_sys_path && PyTuple_Check(basic_sys_path));
97 nrn_assert(PySequence_SetSlice(path, 1, 1 + PyTuple_Size(basic_sys_path), basic_sys_path) == 0);
110 reset_sys_path(fname);
114 auto const realpath = std::filesystem::canonical(fname);
116 auto const dirname = realpath.parent_path().string();
117 reset_sys_path(dirname);
126 auto*
fp = fopen(fname,
"r");
130 Fprintf(stderr, fmt::format(
"Could not open {}\n", fname).c_str());
135 fp = fopen(fname,
"r");
137 int const code = PyRun_AnyFile(
fp, fname);
145 std::string exec{
"with open('"};
148 "', 'rb') as nrnmingw_file:"
149 " exec(nrnmingw_file.read(), globals())\n";
150 int const code = PyRun_SimpleString(exec.c_str());
155 PyRun_SimpleString(
"del nrnmingw_file\n");
167 std::string lines[3]{
168 "import code as nrnmingw_code\n",
169 "nrnmingw_interpreter = nrnmingw_code.InteractiveConsole(locals=globals())\n",
170 "nrnmingw_interpreter.interact(\"\")\n"};
171 for (
const auto& line: lines) {
172 if (PyRun_SimpleString(line.c_str())) {
194 static int started = 0;
195 if (b == 1 && !started) {
203 PythonConfigWrapper config;
205 config->site_import = 0;
207 auto const check = [](
const char* desc, PyStatus
status) {
208 if (PyStatus_Exception(
status)) {
209 std::ostringstream oss;
212 oss <<
": " <<
status.err_msg;
214 oss <<
" in " <<
status.func;
217 throw std::runtime_error(oss.str());
227 #ifndef NRNPYTHON_DYNAMICLOAD
232 auto const& default_python = neuron::config::default_python_executable;
233 if (pyexe.empty() && !default_python.empty()) {
235 pyexe = default_python;
239 throw std::runtime_error(
"Do not know what to set PyConfig.program_name to");
244 if (
auto p = std::filesystem::path{pyexe}; !
p.is_absolute()) {
245 std::ostringstream oss;
246 oss <<
"Setting PyConfig.program_name to a non-absolute path (" << pyexe
247 <<
") is not portable; try passing an absolute path to -pyexe or NRN_PYTHONEXE";
248 throw std::runtime_error(oss.str());
253 check(
"Could not set PyConfig.program_name",
254 PyConfig_SetBytesString(config, &config->program_name, pyexe.c_str()));
259 config->parse_argv = 0;
260 check(
"Could not set PyConfig.argv",
263 check(
"Could not initialise Python", Py_InitializeFromConfig(config));
266 nanobind::gil_scoped_acquire _{};
267 auto*
const sys_path = PySys_GetObject(
"path");
269 throw std::runtime_error(
"Could not get sys.path from C++");
275 auto* ustr = PyUnicode_DecodeFSDefaultAndSize(path.c_str(), path.size());
277 auto const already_there = PySequence_Contains(sys_path, ustr);
278 assert(already_there != -1);
279 if (already_there == 0 && PyList_Append(sys_path, ustr)) {
281 throw std::runtime_error(
"Could not append " + path +
" to sys.path");
294 assert(PyList_Check(sys_path) && !basic_sys_path);
295 basic_sys_path = PyList_AsTuple(sys_path);
301 if (b == 0 && started) {
302 PyGILState_STATE gilsav = PyGILState_Ensure();
304 Py_DECREF(basic_sys_path);
305 basic_sys_path =
nullptr;
310 if (b == 2 && started) {
326 PyRun_SimpleString(
"import readline as nrn_readline");
331 bool python_error_encountered{
false}, have_reset_sys_path{
false};
337 have_reset_sys_path =
true;
339 python_error_encountered =
true;
342 }
else if (strlen(arg) > 3 && strcmp(arg + strlen(arg) - 3,
".py") == 0) {
344 python_error_encountered =
true;
346 have_reset_sys_path =
true;
354 if (!have_reset_sys_path) {
361 PyRun_InteractiveLoop(
hoc_fin,
"stdin");
366 python_error_encountered = ret;
370 return python_error_encountered;
388 nanobind::gil_scoped_acquire
lock{};
403 auto*
const p =
static_cast<char*
>(PyMem_RawMalloc(
n));
409 }
else if (r == EOF) {
410 return static_cast<char*
>(PyMem_RawCalloc(1,
sizeof(
char)));
static double interp(double frac, double x1, double x2)
void hoc_retpushx(double x)
static void check(VecTNode &)
#define nrn_assert(x)
assert()-like macro, independent of NDEBUG status
int const size_t const size_t n
char * neuronhome_forward()
static void nrnpython_real()
Backend to nrnpython(...) in HOC/Python code.
static std::string python_sys_path_to_append()
static int nrnmingw_pyrun_interactiveloop()
Like a PyRun_InteractiveLoop that does not need a FILE* Use InteractiveConsole to work around the iss...
int nrnpy_pyrun(const char *)
Execute a Python script.
static char * nrnpython_getline(FILE *, FILE *, const char *)
const char * hoc_promptstr
void nrnpython_reg_real_nrnpython_cpp(neuron::python::impl_ptrs *ptrs)
static int nrnpython_start(int b)
Start the Python interpreter.
static void nrnpython_set_path(std::string_view fname)
Reset sys.path to its value at initialisation and prepend fname.
int(* p_nrnpy_pyrun)(const char *)
HOC interpreter function declarations (included by hocdec.h)
bool isDirExist(const std::string &path)
Collection of pointers to functions with python-version-specific implementations.
void(* interpreter_set_path)(std::string_view)
int(* interpreter_start)(int)
int Fprintf(FILE *stream, const char *fmt, Args... args)