/[ascend]/trunk/models/johnpye/extpy/extpy.c
ViewVC logotype

Annotation of /trunk/models/johnpye/extpy/extpy.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3258 - (hide annotations) (download) (as text)
Wed Nov 15 04:57:56 2017 UTC (2 years, 11 months ago) by jpye
File MIME type: text/x-csrc
File size: 10754 byte(s)
Merged relerrorlist branch back to trunk.

1 johnpye 862 /* ASCEND modelling environment
2     Copyright (C) 2006 Carnegie Mellon University
3    
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2, or (at your option)
7     any later version.
8    
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12     GNU General Public License for more details.
13    
14     You should have received a copy of the GNU General Public License
15 jpye 2649 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 johnpye 869 *//**
17     @file
18     Import handler to provide external python script functionality for ASCEND.
19 johnpye 880 *//*
20     by John Pye, Oct 2006
21 johnpye 862 */
22    
23 johnpye 982 #ifndef WITH_PYTHON
24     # error "Can't build 'extpy' without WITH_PYTHON set"
25     #else
26    
27 johnpye 912 #include <Python.h>
28    
29 johnpye 862 #include <stdio.h>
30     #include <string.h>
31    
32 jpye 2323 #include <ascend/general/platform.h>
33 jpye 2018 #include <ascend/utilities/error.h>
34     #include <ascend/general/ospath.h>
35 johnpye 862
36 jpye 2018 #include <ascend/compiler/importhandler.h>
37     #include <ascend/compiler/extfunc.h>
38 johnpye 862
39 jpye 3258 //#define EXTPY_DEBUG
40 jpye 2803 #ifdef EXTPY_DEBUG
41     # define MSG CONSOLE_DEBUG
42     #else
43     # define MSG(ARGS...) ((void)0)
44     #endif
45 johnpye 865
46 jpye 1332
47 johnpye 862 ImportHandlerCreateFilenameFn extpy_filename;
48     ImportHandlerImportFn extpy_import;
49 jpye 2334 ImportHandlerDestroyFn extpy_handler_destroy;
50 johnpye 862
51 johnpye 914 ExtMethodDestroyFn extpy_destroy;
52    
53    
54 johnpye 862 #ifndef ASC_EXPORT
55     # error "Where is ASC_EXPORT?"
56     #endif
57    
58 johnpye 912 struct ExtPyData{
59     PyObject *fn;
60     char *name;
61     };
62    
63 johnpye 862 /**
64     This is the function called from "IMPORT extpy"
65    
66     It sets up the functions in this external function library
67     */
68 johnpye 1063 extern ASC_EXPORT int extpy_register(){
69 johnpye 862 int result = 0;
70    
71     struct ImportHandler *handler;
72     handler = ASC_NEW(struct ImportHandler);
73    
74     handler->name = "extpy";
75 johnpye 912 handler->filenamefn = &extpy_filename;
76     handler->importfn = &extpy_import;
77 jpye 2334 handler->destroyfn = &extpy_handler_destroy;
78 johnpye 862
79     result = importhandler_add(handler);
80    
81     if(result){
82     ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed to register import handler (error = %d)",result);
83     }
84 johnpye 1059
85 jpye 3258 ERROR_REPORTER_HERE(ASC_USER_WARNING,"'extpy' import handler is still EXPERIMENTAL.");
86 jpye 2649
87 johnpye 862 return result;
88     }
89    
90 jpye 2334 int extpy_handler_destroy(struct ImportHandler *handler){
91     ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Not implemented");
92     return 0;
93     }
94    
95 johnpye 869 /*------------------------------------------------------------------------------
96 johnpye 873 PYTHON METHOD INVOKER
97 johnpye 869 */
98    
99 johnpye 873 ExtMethodRun extpy_invokemethod;
100    
101     /** Method invoker. extpy will supply a pointer to this function whenever it
102     registers a python function as an external script method. This function will
103     then dereference the user_data field into a python function, and execute that
104     python function.
105    
106     One difficult aspect is the question of how to usefully pass the 'context'
107     argument to Python?
108     */
109     int extpy_invokemethod(struct Instance *context, struct gl_list_t *args, void *user_data){
110 johnpye 937 PyObject *arglist=NULL, *result=NULL, *pyinstance=NULL, *dict=NULL
111     , *mainmodule=NULL, *errstring=NULL, *errtypestring=NULL;
112     PyObject *perrtype=NULL, *perrvalue=NULL, *perrtrace=NULL;
113    
114     int ret;
115 johnpye 912 struct ExtPyData *extpydata;
116 johnpye 937
117 johnpye 873 /* cast user data to PyObject pointer */
118 johnpye 937 extpydata = (struct ExtPyData *)user_data;
119 johnpye 874
120 johnpye 937 mainmodule = PyImport_AddModule("__main__");
121     if(mainmodule==NULL){
122 jpye 2803 MSG("Unable to retrieve __main__ module");
123 johnpye 937 ret = 1;
124     goto cleanup_extpy_invokemethod;
125     }
126 johnpye 874
127 johnpye 937 dict = PyModule_GetDict(mainmodule);
128     if(dict==NULL){
129 jpye 2803 MSG("Unable to retrieve __main__ dict");
130 johnpye 937 ret = 1;
131     goto cleanup_extpy_invokemethod;
132     }
133 johnpye 873
134 jpye 2803 MSG("Running python method '%s'",extpydata->name);
135 johnpye 912
136     if(!PyCallable_Check(extpydata->fn)){
137 johnpye 874 ERROR_REPORTER_HERE(ASC_PROG_ERR,"user_data is not a PyCallable");
138 johnpye 937 ret = 1;
139     goto cleanup_extpy_invokemethod;
140 johnpye 874 }
141    
142 johnpye 875 /*
143     We need to be able to convert C 'struct Instance' pointers to Python 'Instance' objects.
144     This functionality is implemented in 'ascpy' but we're not going to link to that here,
145 johnpye 877 so we will use the importhandler 'setsharedpointer' trick to pass the object to the
146     'registry' then write a routine in ascpy that will cast it into the appropriate
147     Python object.
148 johnpye 875 */
149     importhandler_setsharedpointer("context",(void *)context);
150    
151 johnpye 937 PyErr_Clear();
152     pyinstance = PyRun_String("ascpy.Registry().getInstance('context')",Py_eval_input,dict,dict);
153     if(PyErr_Occurred()){
154 jpye 2803 MSG("Failed retrieving instance");
155 johnpye 937 ret = 1;
156     goto cleanup_extpy_invokemethod;
157     }
158 johnpye 875
159 johnpye 937 arglist = Py_BuildValue("(O)", pyinstance);
160    
161 jpye 2649
162 johnpye 937 PyErr_Clear();
163 johnpye 912 result = PyEval_CallObject(extpydata->fn, arglist);
164 johnpye 875
165 johnpye 874 if(PyErr_Occurred()){
166 jpye 2803 MSG("Error occured in PyEval_CallObject");
167 johnpye 937
168     /* get the content of the error message */
169 jpye 2649 PyErr_Fetch(&perrtype, &perrvalue, &perrtrace);
170 johnpye 937
171     errtypestring = NULL;
172     if(perrtype != NULL
173     && (errtypestring = PyObject_Str(perrtype)) != NULL
174     && PyString_Check(errtypestring)
175     ){
176     // nothing
177     }else{
178     errtypestring = Py_BuildValue("");
179     }
180 jpye 3258 MSG("errtypestring = \"%s\"",PyString_AsString(errtypestring));
181 jpye 2649
182 jpye 3258 MSG("errtypestring = \"%s\"",PyString_AsString(errtypestring));
183    
184 johnpye 937 errstring = NULL;
185     if(perrvalue != NULL
186     && (errstring = PyObject_Str(perrvalue)) != NULL
187     && PyString_Check(errstring)
188     ){
189 jpye 3258 MSG("errstring = \"%s\"",PyString_AsString(errstring));
190 johnpye 937 error_reporter(ASC_PROG_ERR
191     ,extpydata->name,0
192     ,PyString_AsString(errtypestring)
193 jpye 3258 ,"Error in extpy call: %s",PyString_AsString(errstring)
194 johnpye 937 );
195     }else{
196     error_reporter(ASC_PROG_ERR,extpydata->name,0,extpydata->name,"(unknown python error)");
197     }
198 johnpye 875 PyErr_Print();
199 johnpye 937 ret = 1;
200     goto cleanup_extpy_invokemethod;
201 johnpye 874 }
202    
203 johnpye 937 ret=0;
204    
205     cleanup_extpy_invokemethod:
206     Py_XDECREF(dict);
207     Py_XDECREF(arglist);
208     Py_XDECREF(pyinstance);
209     Py_XDECREF(errstring);
210     Py_XDECREF(errtypestring);
211     Py_XDECREF(perrtype);
212     Py_XDECREF(perrvalue);
213     Py_XDECREF(perrtrace);
214     return ret;
215 johnpye 873 }
216    
217 johnpye 912 /**
218     Free memory associated with a registered script method.
219 johnpye 914 @return 0 on success
220 johnpye 912 */
221 johnpye 914 int extpy_destroy(void *user_data){
222 johnpye 912 struct ExtPyData *extpydata;
223     extpydata = (struct ExtPyData *)user_data;
224     Py_DECREF(extpydata->fn);
225     ASC_FREE(extpydata->name);
226     ASC_FREE(extpydata);
227 johnpye 914 return 0;
228 johnpye 912 }
229    
230 johnpye 873 /*------------------------------------------------------------------------------
231     'EXTPY' PYTHON STATIC MODULE
232     */
233    
234 johnpye 869 static PyObject *extpy_getbrowser(PyObject *self, PyObject *args){
235     PyObject *browser;
236     if(args!=NULL){
237     ERROR_REPORTER_HERE(ASC_PROG_ERR,"args is not NULL?!");
238     }
239     browser = (PyObject *)importhandler_getsharedpointer("browser");
240 johnpye 930 if(browser==NULL){
241 johnpye 935 return Py_BuildValue("");
242 johnpye 930 }
243 johnpye 1055 return browser;
244     /* return Py_BuildValue("O",browser);*/
245 johnpye 869 }
246    
247 johnpye 873 static PyObject *extpy_registermethod(PyObject *self, PyObject *args){
248     PyObject *fn, *name, *docstring;
249     const char *cname, *cdocstring;
250     int res;
251     int nargs = 1;
252 johnpye 912 struct ExtPyData *extpydata;
253 johnpye 873
254     PyArg_ParseTuple(args,"O:registermethod", &fn);
255     if(!PyCallable_Check(fn)){
256     PyErr_SetString(PyExc_TypeError,"parameter must be callable");
257     return NULL;
258     }
259    
260 jpye 2803 /* MSG("FOUND FN=%p",fn); */
261 johnpye 873
262     name = PyObject_GetAttr(fn,PyString_FromString("__name__"));
263     if(name==NULL){
264 jpye 2803 MSG("No __name__ attribute");
265 johnpye 873 PyErr_SetString(PyExc_TypeError,"No __name__ attribute");
266     return NULL;
267     }
268     cname = PyString_AsString(name);
269    
270 jpye 2803 /* MSG("REGISTERED METHOD '%s' HAS %d ARGS",cname,nargs); */
271 johnpye 873
272     docstring = PyObject_GetAttr(fn,PyString_FromString("func_doc"));
273     cdocstring = "(no help)";
274     if(name!=NULL){
275     cdocstring = PyString_AsString(docstring);
276 jpye 2803 //MSG("DOCSTRING: %s",cdocstring);
277 johnpye 873 }
278    
279 johnpye 912 extpydata = ASC_NEW(struct ExtPyData);
280     extpydata->name = ASC_NEW_ARRAY(char,strlen(cname)+1);
281     extpydata->fn = fn;
282     strcpy(extpydata->name, cname);
283    
284     res = CreateUserFunctionMethod(cname,&extpy_invokemethod,nargs,cdocstring,(void *)extpydata,&extpy_destroy);
285 johnpye 874 Py_INCREF(fn);
286 johnpye 873
287 jpye 2803 /* MSG("EXTPY 'fn' IS AT %p",fn); */
288 johnpye 873
289 jpye 2803 /* MSG("EXTPY INVOKER IS AT %p",extpy_invokemethod); */
290 johnpye 912
291 johnpye 873 if(res){
292     ERROR_REPORTER_HERE(ASC_PROG_ERR,"Problem registering external script method (%d)",res);
293     PyErr_SetString(PyExc_Exception,"unable to register script method");
294     return NULL;
295     }
296    
297 jpye 3258 MSG("Registered python method '%s'",cname);
298 johnpye 873
299     /* nothing gets returned (but possibly an exception) */
300 johnpye 935 return Py_BuildValue("");
301 johnpye 873 }
302    
303 johnpye 869 static PyMethodDef extpymethods[] = {
304     {"getbrowser", extpy_getbrowser, METH_NOARGS,"Retrieve browser pointer"}
305 johnpye 873 ,{"registermethod", extpy_registermethod, METH_VARARGS,"Register a python method as an ASCEND script method"}
306 johnpye 869 ,{NULL,NULL,0,NULL}
307     };
308    
309     PyMODINIT_FUNC initextpy(void){
310     PyObject *obj;
311     obj = Py_InitModule3("extpy", extpymethods,"Module for accessing shared ASCEND pointers from python");
312     }
313    
314     /*------------------------------------------------------------------------------
315     STANDARD IMPORT HANDLER ROUTINES
316     */
317    
318 johnpye 862 /**
319     Create a filename base on a partial filename. In that case of python, this
320     just means adding '.py' to the end.
321 johnpye 867
322 johnpye 862 @param partialname the filename without suffix, as specified in the user's "IMPORT" command
323     @return new filename, or NULL on failure
324     */
325     char *extpy_filename(const char *partialname){
326     char *name;
327     int len;
328     if(partialname==NULL){
329     ERROR_REPORTER_HERE(ASC_PROG_ERR,"Partial name is NULL, can't work out filename");
330     return NULL;
331     }
332 johnpye 867
333 johnpye 862 len = strlen(partialname);
334     name = ASC_NEW_ARRAY_CLEAR(char,len+4);
335     strcpy(name,partialname);
336     strcat(name,".py");
337 jpye 2803 MSG("New filename is '%s'",name);
338 johnpye 862 return name;
339     }
340    
341     /**
342     Import a python script located at the path indicated.
343    
344     @return 0 on success, else error codes (TBD)
345     */
346 johnpye 864 int extpy_import(const struct FilePath *fp, const char *initfunc, const char *partialpath){
347 johnpye 862 char *name;
348     name = ospath_str(fp);
349 johnpye 865 FILE *f;
350 johnpye 872 PyObject *pyfile;
351 johnpye 930 int iserr;
352 johnpye 865
353 jpye 2803 MSG("Importing Python script %s",name);
354 johnpye 1058
355 johnpye 865 if(Py_IsInitialized()){
356 jpye 2803 MSG("Python was already initialised");
357 johnpye 865 }else{
358 jpye 2803 MSG("INITIALISING PYTHON");
359 johnpye 865 Py_Initialize();
360 jpye 2803 MSG("COMPLETED ATTEMPT TO INITIALISE PYTHON");
361 johnpye 865 }
362    
363 johnpye 867 if(!Py_IsInitialized()){
364 johnpye 873 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to initialise Python");
365 jpye 2803 MSG("UNABLE TO INITIALIZE PYTHON");
366 johnpye 873 ASC_FREE(name);
367 johnpye 867 return 1;
368     }
369    
370 johnpye 869 initextpy();
371    
372 johnpye 875 if(PyRun_SimpleString("import ascpy")){
373 jpye 2803 MSG("Failed importing 'ascpy'");
374 johnpye 875 return 1;
375     }
376    
377 johnpye 872 pyfile = PyFile_FromString(name,"r");
378     if(pyfile==NULL){
379 jpye 2803 MSG("Failed opening script");
380 johnpye 873 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to open '%s' (%s)",partialpath,name);
381     ASC_FREE(name);
382 johnpye 871 return 1;
383     }
384 johnpye 880
385     f = PyFile_AsFile(pyfile);
386 johnpye 872 if(f==NULL){
387 johnpye 873 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to cast PyObject to FILE*");
388     ASC_FREE(name);
389 johnpye 872 return 1;
390     }
391 johnpye 873
392 johnpye 930 PyErr_Clear();
393    
394     iserr = PyRun_AnyFileEx(f,name,1);
395 jpye 2649
396 johnpye 930 if(iserr){
397     /* according to the manual, there is no way of determining anything more about the error. */
398     ERROR_REPORTER_HERE(ASC_PROG_ERR,"An error occurring in importing the script '%s'",name);
399 johnpye 873 return 1;
400 johnpye 930 }
401 johnpye 871
402 jpye 3258 MSG("Imported python script '%s'",partialpath);
403 johnpye 873
404 johnpye 862 ASC_FREE(name);
405 johnpye 873 return 0;
406 johnpye 862 }
407    
408 johnpye 982 #endif /* WITH_PYTHON */
409    

john.pye@anu.edu.au
ViewVC Help
Powered by ViewVC 1.1.22