I got it to work (on Linux) doing the following:
// method to inspect PyObjects
static void reprint(PyObject *obj)
{
PyObject* repr = PyObject_Repr(obj);
PyObject* str = PyUnicode_AsEncodedString(repr, "utf-8", "~E~");
const char *bytes = PyBytes_AS_STRING(str);
printf("REPR: %s\n", bytes);
Py_XDECREF(repr);
Py_XDECREF(str);
}
.
int main()
{
// for our manually compiled and installed usr-local Python 3.6
//#define PATH L"/usr/local/bin:/usr/local/lib:/usr/local/lib/python3.6/lib-dynload"
//#define PREFIX L"/usr/local"
//#define EXEC_PREFIX L"/usr/local"
//#define FULL_PROG_PATH L"/usr/local/bin/python3.6"
// for apt installed Python 3.7
//#define PATH L"/usr/bin:/usr/lib:/usr/lib/python3.7/lib-dynload"
//#define PREFIX L"/usr"
//#define EXEC_PREFIX L"/usr"
//#define FULL_PROG_PATH L"/usr/bin/python3.7"
// for venv (which uses the /usr/local/lib python 3.6)
#define PATH L"/home/me/venv_dir/bin:/home/me/venv_dir/lib:/usr/local/lib/python3.6:/usr/local/lib/python3.6/lib-dynload"
#define PREFIX L"/home/me/venv_dir"
#define EXEC_PREFIX L"/usr/local"
#define FULL_PROG_PATH L"/usr/local/bin/python3.6"
// ------------------------------------------------
#define CHANGE_THE_INTERPRETER
#ifdef CHANGE_THE_INTERPRETER
// TODO : Look at using this: https://www.python.org/dev/peps/pep-0587/
Py_SetPath(PATH);
// change the built-in prefix/exec-prefix, in place.
wchar_t* wpPrefix = Py_GetPrefix();
wchar_t* wpExecPrefix = Py_GetExecPrefix();
wchar_t* wpProgramFullPath = Py_GetProgramFullPath();
wcscpy (wpPrefix, PREFIX);
wcscpy (wpExecPrefix, EXEC_PREFIX);
wcscpy (wpProgramFullPath, FULL_PROG_PATH);
#endif //CHANGE_THE_INTERPRETER
// inspect the environment variables. With the #define commented out above the "defaults" appear as indicated to the right hand side
wchar_t* xx; //defaults
xx = Py_GetPrefix(); //<prefix> L"/usr/local"
xx = Py_GetExecPrefix(); //<exec_prefix> L"/usr/local"
xx = Py_GetPath(); //L"/usr/local/lib/python36.zip:/usr/local/lib/python3.6:/usr/local/lib/python3.6:/usr/local/lib/python3.6/lib-dynload"
xx = Py_GetProgramName(); //L"python3"
xx = Py_GetPythonHome(); //null
xx = Py_GetProgramFullPath(); //<progpath> L"/usr/local/bin/python3"
Py_Initialize();
some extra bits
int x2 = PyRun_SimpleString ("import site; print (site.getsitepackages())");
int x3 = PyRun_SimpleString ("import datetime");
int x4 = PyRun_SimpleString ("import numpy as np");
//inspect sys info
PyObject* sys_executable = PySys_GetObject((char*)"executable"); reprint(sys_executable);
PyObject* sys_version = PySys_GetObject((char*)"version"); reprint(sys_version);
PyObject* sys_realPrefix = PySys_GetObject((char*)"real_prefix"); reprint(sys_realPrefix);
PyObject* sys_basePrefix = PySys_GetObject((char*)"base_prefix"); reprint(sys_basePrefix);
Some points to note:
If you look at the Python module getpath.c
you will see the following buffers:
static wchar_t prefix[MAXPATHLEN+1];
static wchar_t exec_prefix[MAXPATHLEN+1];
static wchar_t progpath[MAXPATHLEN+1];
Methods such as Py_GetProgramFullPath perform as follows:
if (!module_search_path)
calculate_path();
return progpath;
...so it is possible to use those methods to obtain the buffer pointers and wcscpy
the values directly into the buffers. Note this is currently only possible with getpath being implemented in this fashion!
lib-dynload
is needed to ensure that some modules (e.g. datetime) can be pulled in
Also note this approach ensures that the .../python3.x/encodings
directory can be found, which prevents a runtime error within Py_Initialize