xref: /petsc/src/sys/python/pythonsys.c (revision 84b215ba083d64dd11b38aaaf62a67d02759a903)
1 #include <petsc/private/petscimpl.h>       /*I "petscsys.h" I*/
2 
3 /* ---------------------------------------------------------------- */
4 
5 #if !defined(PETSC_PYTHON_EXE)
6 #define PETSC_PYTHON_EXE "python"
7 #endif
8 
9 static PetscErrorCode PetscPythonFindExecutable(char pythonexe[PETSC_MAX_PATH_LEN])
10 {
11   PetscBool      flag;
12   PetscErrorCode ierr;
13 
14   PetscFunctionBegin;
15   /* get the path for the Python interpreter executable */
16   ierr = PetscStrncpy(pythonexe,PETSC_PYTHON_EXE,PETSC_MAX_PATH_LEN);CHKERRQ(ierr);
17   ierr = PetscOptionsGetString(NULL,NULL,"-python",pythonexe,PETSC_MAX_PATH_LEN,&flag);CHKERRQ(ierr);
18   if (!flag || pythonexe[0]==0) {
19     ierr = PetscStrncpy(pythonexe,PETSC_PYTHON_EXE,PETSC_MAX_PATH_LEN);CHKERRQ(ierr);
20   }
21   PetscFunctionReturn(0);
22 }
23 
24 static PetscErrorCode PetscPythonFindLibrary(const char pythonexe[PETSC_MAX_PATH_LEN],char pythonlib[PETSC_MAX_PATH_LEN])
25 {
26   const char     cmdline1[] = "-c 'from distutils import sysconfig; print(sysconfig.get_config_var(\"LIBPYTHON\"))'";
27   const char     cmdline2[] = "-c 'import os;from distutils import sysconfig; print(os.path.join(sysconfig.get_config_var(\"LIBPL\"),sysconfig.get_config_var(\"LDLIBRARY\")))'";
28   char           command[PETSC_MAX_PATH_LEN+1+sizeof(cmdline2)+1],*eol;
29   FILE           *fp = NULL;
30   PetscBool      found = PETSC_FALSE;
31   PetscErrorCode ierr;
32 
33   PetscFunctionBegin;
34 #if defined(PETSC_PYTHON_LIB)
35   ierr = PetscStrcpy(pythonlib,PETSC_PYTHON_LIB);CHKERRQ(ierr);
36   PetscFunctionReturn(0);
37 #endif
38 
39   /* call Python to find out the name of the Python dynamic library */
40   ierr = PetscStrncpy(command,pythonexe,PETSC_MAX_PATH_LEN);CHKERRQ(ierr);
41   ierr = PetscStrcat(command," ");CHKERRQ(ierr);
42   ierr = PetscStrcat(command,cmdline1);CHKERRQ(ierr);
43 #if defined(PETSC_HAVE_POPEN)
44   ierr = PetscPOpen(PETSC_COMM_SELF,NULL,command,"r",&fp);CHKERRQ(ierr);
45   if (!fgets(pythonlib,PETSC_MAX_PATH_LEN,fp)) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Python: bad output from executable: %s",pythonexe);
46   ierr = PetscPClose(PETSC_COMM_SELF,fp);CHKERRQ(ierr);
47 #else
48   SETERRQ(PETSC_COMM_SELF,1,"Python: Aborted due to missing popen()");
49 #endif
50   /* remove newlines */
51   ierr = PetscStrchr(pythonlib,'\n',&eol);CHKERRQ(ierr);
52   if (eol) eol[0] = 0;
53   ierr = PetscTestFile(pythonlib,'r',&found);CHKERRQ(ierr);
54   if (!found) {
55     ierr = PetscStrncpy(command,pythonexe,PETSC_MAX_PATH_LEN);CHKERRQ(ierr);
56     ierr = PetscStrcat(command," ");CHKERRQ(ierr);
57     ierr = PetscStrcat(command,cmdline2);CHKERRQ(ierr);
58 #if defined(PETSC_HAVE_POPEN)
59     ierr = PetscPOpen(PETSC_COMM_SELF,NULL,command,"r",&fp);CHKERRQ(ierr);
60     if (!fgets(pythonlib,PETSC_MAX_PATH_LEN,fp)) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Python: bad output from executable: %s",pythonexe);
61     ierr = PetscPClose(PETSC_COMM_SELF,fp);CHKERRQ(ierr);
62 #else
63     SETERRQ(PETSC_COMM_SELF,1,"Python: Aborted due to missing popen()");
64 #endif
65     /* remove newlines */
66     ierr = PetscStrchr(pythonlib,'\n',&eol);CHKERRQ(ierr);
67     if (eol) eol[0] = 0;
68     ierr = PetscTestFile(pythonlib,'r',&found);CHKERRQ(ierr);
69   }
70   ierr = PetscInfo2(0,"Python library  %s found %d\n",pythonlib,found);CHKERRQ(ierr);
71   PetscFunctionReturn(0);
72 }
73 
74 /* ---------------------------------------------------------------- */
75 
76 typedef struct _Py_object_t PyObject; /* fake definition */
77 
78 static PyObject* Py_None = 0;
79 
80 static const char* (*Py_GetVersion)(void);
81 
82 static int       (*Py_IsInitialized)(void);
83 static void      (*Py_InitializeEx)(int);
84 static void      (*Py_Finalize)(void);
85 
86 static void      (*PySys_SetArgv)(int,char**);
87 static PyObject* (*PySys_GetObject)(const char*);
88 static PyObject* (*PyObject_CallMethod)(PyObject*,const char*, const char*, ...);
89 static PyObject* (*PyImport_ImportModule)(const char*);
90 
91 static void      (*Py_IncRef)(PyObject*);
92 static void      (*Py_DecRef)(PyObject*);
93 
94 static void      (*PyErr_Clear)(void);
95 static PyObject* (*PyErr_Occurred)(void);
96 static void      (*PyErr_Fetch)(PyObject**,PyObject**,PyObject**);
97 static void      (*PyErr_NormalizeException)(PyObject**,PyObject**, PyObject**);
98 static void      (*PyErr_Display)(PyObject*,PyObject*,PyObject*);
99 static void      (*PyErr_Restore)(PyObject*,PyObject*,PyObject*);
100 
101 
102 #define PetscDLPyLibOpen(libname) \
103   PetscDLLibraryAppend(PETSC_COMM_SELF,&PetscDLLibrariesLoaded,libname)
104 #define PetscDLPyLibSym(symbol, value) \
105   PetscDLLibrarySym(PETSC_COMM_SELF,&PetscDLLibrariesLoaded,NULL,symbol,(void**)value)
106 #define PetscDLPyLibClose(comm) \
107   do { } while (0)
108 
109 static PetscErrorCode PetscPythonLoadLibrary(const char pythonlib[])
110 {
111   PetscErrorCode ierr;
112 
113   PetscFunctionBegin;
114   /* open the Python dynamic library */
115   ierr = PetscDLPyLibOpen(pythonlib);CHKERRQ(ierr);
116   ierr = PetscInfo1(0,"Python: loaded dynamic library %s\n", pythonlib);CHKERRQ(ierr);
117   /* look required symbols from the Python C-API */
118   ierr = PetscDLPyLibSym("_Py_NoneStruct"        , &Py_None               );CHKERRQ(ierr);
119   ierr = PetscDLPyLibSym("Py_GetVersion"         , &Py_GetVersion         );CHKERRQ(ierr);
120   ierr = PetscDLPyLibSym("Py_IsInitialized"      , &Py_IsInitialized      );CHKERRQ(ierr);
121   ierr = PetscDLPyLibSym("Py_InitializeEx"       , &Py_InitializeEx       );CHKERRQ(ierr);
122   ierr = PetscDLPyLibSym("Py_Finalize"           , &Py_Finalize           );CHKERRQ(ierr);
123   ierr = PetscDLPyLibSym("PySys_GetObject"       , &PySys_GetObject       );CHKERRQ(ierr);
124   ierr = PetscDLPyLibSym("PySys_SetArgv"         , &PySys_SetArgv         );CHKERRQ(ierr);
125   ierr = PetscDLPyLibSym("PyObject_CallMethod"   , &PyObject_CallMethod   );CHKERRQ(ierr);
126   ierr = PetscDLPyLibSym("PyImport_ImportModule" , &PyImport_ImportModule );CHKERRQ(ierr);
127   ierr = PetscDLPyLibSym("Py_IncRef"             , &Py_IncRef             );CHKERRQ(ierr);
128   ierr = PetscDLPyLibSym("Py_DecRef"             , &Py_DecRef             );CHKERRQ(ierr);
129   ierr = PetscDLPyLibSym("PyErr_Clear"           , &PyErr_Clear           );CHKERRQ(ierr);
130   ierr = PetscDLPyLibSym("PyErr_Occurred"        , &PyErr_Occurred        );CHKERRQ(ierr);
131   ierr = PetscDLPyLibSym("PyErr_Fetch"             , &PyErr_Fetch             );CHKERRQ(ierr);
132   ierr = PetscDLPyLibSym("PyErr_NormalizeException", &PyErr_NormalizeException);CHKERRQ(ierr);
133   ierr = PetscDLPyLibSym("PyErr_Display",            &PyErr_Display           );CHKERRQ(ierr);
134   ierr = PetscDLPyLibSym("PyErr_Restore",            &PyErr_Restore           );CHKERRQ(ierr);
135   /* XXX TODO: check that ALL symbols were there !!! */
136   if (!Py_None)          SETERRQ(PETSC_COMM_SELF,1,"Python: failed to load symbols from dynamic library");
137   if (!Py_GetVersion)    SETERRQ(PETSC_COMM_SELF,1,"Python: failed to load symbols from dynamic library");
138   if (!Py_IsInitialized) SETERRQ(PETSC_COMM_SELF,1,"Python: failed to load symbols from dynamic library");
139   if (!Py_InitializeEx)  SETERRQ(PETSC_COMM_SELF,1,"Python: failed to load symbols from dynamic library");
140   if (!Py_Finalize)      SETERRQ(PETSC_COMM_SELF,1,"Python: failed to load symbols from dynamic library");
141   ierr = PetscInfo(0,"Python: all required symbols loaded from Python dynamic library\n");CHKERRQ(ierr);
142   PetscFunctionReturn(0);
143 }
144 
145 /* ---------------------------------------------------------------- */
146 
147 static char      PetscPythonExe[PETSC_MAX_PATH_LEN] = { 0 };
148 static char      PetscPythonLib[PETSC_MAX_PATH_LEN] = { 0 };
149 static PetscBool PetscBeganPython = PETSC_FALSE;
150 
151 /*@C
152   PetscPythonFinalize - Finalize Python.
153 
154   Level: intermediate
155 
156 .keywords: Python
157 @*/
158 PetscErrorCode  PetscPythonFinalize(void)
159 {
160   PetscFunctionBegin;
161   if (PetscBeganPython) { if (Py_IsInitialized()) Py_Finalize(); }
162   PetscBeganPython = PETSC_FALSE;
163   PetscFunctionReturn(0);
164 }
165 
166 /*@C
167   PetscPythonInitialize - Initialize Python and import petsc4py.
168 
169    Input Parameter:
170 +  pyexe - path to the Python interpreter executable, or NULL.
171 -  pylib - full path to the Python dynamic library, or NULL.
172 
173   Level: intermediate
174 
175 .keywords: Python
176 
177 @*/
178 PetscErrorCode  PetscPythonInitialize(const char pyexe[],const char pylib[])
179 {
180   PyObject       *module = 0;
181   PetscErrorCode ierr;
182 
183   PetscFunctionBegin;
184   if (PetscBeganPython) PetscFunctionReturn(0);
185   /* Python executable */
186   if (pyexe && pyexe[0] != 0) {
187     ierr = PetscStrncpy(PetscPythonExe,pyexe,sizeof(PetscPythonExe));CHKERRQ(ierr);
188   } else {
189     ierr = PetscPythonFindExecutable(PetscPythonExe);CHKERRQ(ierr);
190   }
191   /* Python dynamic library */
192   if (pylib && pylib[0] != 0) {
193     ierr = PetscStrncpy(PetscPythonLib,pylib,sizeof(PetscPythonLib));CHKERRQ(ierr);
194   } else {
195     ierr = PetscPythonFindLibrary(PetscPythonExe,PetscPythonLib);CHKERRQ(ierr);
196   }
197   /* dynamically load Python library */
198   ierr = PetscPythonLoadLibrary(PetscPythonLib);CHKERRQ(ierr);
199   /* initialize Python */
200   PetscBeganPython = PETSC_FALSE;
201   if (!Py_IsInitialized()) {
202     static PetscBool registered = PETSC_FALSE;
203     const char       *py_version;
204     PyObject         *sys_path;
205     char             path[PETSC_MAX_PATH_LEN] = { 0 };
206 
207     /* initialize Python */
208     Py_InitializeEx(0); /* 0: do not install signal handlers */
209     /*  build 'sys.argv' list */
210     py_version = Py_GetVersion();
211     if (py_version[0] == '2') {
212       int argc = 0; char **argv = 0;
213       ierr = PetscGetArgs(&argc,&argv);CHKERRQ(ierr);
214       PySys_SetArgv(argc,argv);
215     }
216     if (py_version[0] == '3') {
217       /* XXX 'argv' is type 'wchar_t**' */
218       PySys_SetArgv(0,NULL);
219     }
220     /* add PETSC_LIB_DIR in front of 'sys.path' */
221     sys_path = PySys_GetObject("path");
222     if (sys_path) {
223       ierr = PetscStrreplace(PETSC_COMM_SELF,"${PETSC_LIB_DIR}",path,sizeof(path));CHKERRQ(ierr);
224       Py_DecRef(PyObject_CallMethod(sys_path,"insert","is",(int)0,(char*)path));
225     }
226     /* register finalizer */
227     if (!registered) {
228       ierr = PetscRegisterFinalize(PetscPythonFinalize);CHKERRQ(ierr);
229 
230       registered = PETSC_TRUE;
231     }
232     PetscBeganPython = PETSC_TRUE;
233   }
234   /* import 'petsc4py.PETSc' module */
235   module = PyImport_ImportModule("petsc4py.PETSc");
236   if (module) {
237     ierr = PetscInfo(0,"Python: successfully imported  module 'petsc4py.PETSc'\n");CHKERRQ(ierr);
238 
239     Py_DecRef(module); module = 0;
240   } else {
241     PetscPythonPrintError();
242     SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Python: could not import module 'petsc4py.PETSc', perhaps your PYTHONPATH does not contain it\n");
243   }
244   PetscFunctionReturn(0);
245 }
246 
247 /*@C
248   PetscPythonPrintError - Print Python errors.
249 
250   Level: developer
251 
252 .keywords: Python
253 
254 @*/
255 PetscErrorCode  PetscPythonPrintError(void)
256 {
257   PyObject *exc=0, *val=0, *tb=0;
258 
259   PetscFunctionBegin;
260   if (!PetscBeganPython) PetscFunctionReturn(0);
261   if (!PyErr_Occurred()) PetscFunctionReturn(0);
262   PyErr_Fetch(&exc,&val,&tb);
263   PyErr_NormalizeException(&exc,&val,&tb);
264   PyErr_Display(exc ? exc : Py_None,
265                 val ? val : Py_None,
266                 tb  ? tb  : Py_None);
267   PyErr_Restore(exc,val,tb);
268   PetscFunctionReturn(0);
269 }
270 
271 /* ---------------------------------------------------------------- */
272 
273 PETSC_EXTERN PetscErrorCode (*PetscPythonMonitorSet_C)(PetscObject,const char[]);
274 PetscErrorCode (*PetscPythonMonitorSet_C)(PetscObject,const char[]) = NULL;
275 
276 /*@C
277   PetscPythonMonitorSet - Set Python monitor
278 
279   Level: developer
280 
281 .keywords: Python
282 
283 @*/
284 PetscErrorCode PetscPythonMonitorSet(PetscObject obj, const char url[])
285 {
286   PetscErrorCode ierr;
287 
288   PetscFunctionBegin;
289   PetscValidHeader(obj,1);
290   PetscValidCharPointer(url,2);
291   if (!PetscPythonMonitorSet_C) {
292     ierr = PetscPythonInitialize(NULL,NULL);CHKERRQ(ierr);
293     if (!PetscPythonMonitorSet_C) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Couldn't initialize Python support for monitors");
294   }
295   ierr = PetscPythonMonitorSet_C(obj,url);CHKERRQ(ierr);
296   PetscFunctionReturn(0);
297 }
298 
299 /* ---------------------------------------------------------------- */
300