/[ascend]/branches/relerrorlist/models/johnpye/extpy/extpy.c
ViewVC logotype

Contents of /branches/relerrorlist/models/johnpye/extpy/extpy.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3208 - (show annotations) (download) (as text)
Thu Sep 14 03:04:52 2017 UTC (20 months, 1 week ago) by jpye
File MIME type: text/x-csrc
File size: 10666 byte(s)
minor changes to suppress unneeded output.

1 /* 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 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *//**
17 @file
18 Import handler to provide external python script functionality for ASCEND.
19 *//*
20 by John Pye, Oct 2006
21 */
22
23 #ifndef WITH_PYTHON
24 # error "Can't build 'extpy' without WITH_PYTHON set"
25 #else
26
27 #include <Python.h>
28
29 #include <stdio.h>
30 #include <string.h>
31
32 #include <ascend/general/platform.h>
33 #include <ascend/utilities/error.h>
34 #include <ascend/general/ospath.h>
35
36 #include <ascend/compiler/importhandler.h>
37 #include <ascend/compiler/extfunc.h>
38
39 //#define EXTPY_DEBUG
40 #ifdef EXTPY_DEBUG
41 # define MSG CONSOLE_DEBUG
42 #else
43 # define MSG(ARGS...) ((void)0)
44 #endif
45
46
47 ImportHandlerCreateFilenameFn extpy_filename;
48 ImportHandlerImportFn extpy_import;
49 ImportHandlerDestroyFn extpy_handler_destroy;
50
51 ExtMethodDestroyFn extpy_destroy;
52
53
54 #ifndef ASC_EXPORT
55 # error "Where is ASC_EXPORT?"
56 #endif
57
58 struct ExtPyData{
59 PyObject *fn;
60 char *name;
61 };
62
63 /**
64 This is the function called from "IMPORT extpy"
65
66 It sets up the functions in this external function library
67 */
68 extern ASC_EXPORT int extpy_register(){
69 int result = 0;
70
71 struct ImportHandler *handler;
72 handler = ASC_NEW(struct ImportHandler);
73
74 handler->name = "extpy";
75 handler->filenamefn = &extpy_filename;
76 handler->importfn = &extpy_import;
77 handler->destroyfn = &extpy_handler_destroy;
78
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
85 ERROR_REPORTER_HERE(ASC_USER_WARNING,"'extpy' import handler is still EXPERIMENTAL.");
86
87 return result;
88 }
89
90 int extpy_handler_destroy(struct ImportHandler *handler){
91 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Not implemented");
92 return 0;
93 }
94
95 /*------------------------------------------------------------------------------
96 PYTHON METHOD INVOKER
97 */
98
99 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 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 struct ExtPyData *extpydata;
116
117 /* cast user data to PyObject pointer */
118 extpydata = (struct ExtPyData *)user_data;
119
120 mainmodule = PyImport_AddModule("__main__");
121 if(mainmodule==NULL){
122 MSG("Unable to retrieve __main__ module");
123 ret = 1;
124 goto cleanup_extpy_invokemethod;
125 }
126
127 dict = PyModule_GetDict(mainmodule);
128 if(dict==NULL){
129 MSG("Unable to retrieve __main__ dict");
130 ret = 1;
131 goto cleanup_extpy_invokemethod;
132 }
133
134 MSG("Running python method '%s'",extpydata->name);
135
136 if(!PyCallable_Check(extpydata->fn)){
137 ERROR_REPORTER_HERE(ASC_PROG_ERR,"user_data is not a PyCallable");
138 ret = 1;
139 goto cleanup_extpy_invokemethod;
140 }
141
142 /*
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 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 */
149 importhandler_setsharedpointer("context",(void *)context);
150
151 PyErr_Clear();
152 pyinstance = PyRun_String("ascpy.Registry().getInstance('context')",Py_eval_input,dict,dict);
153 if(PyErr_Occurred()){
154 MSG("Failed retrieving instance");
155 ret = 1;
156 goto cleanup_extpy_invokemethod;
157 }
158
159 arglist = Py_BuildValue("(O)", pyinstance);
160
161
162 PyErr_Clear();
163 result = PyEval_CallObject(extpydata->fn, arglist);
164
165 if(PyErr_Occurred()){
166 MSG("Error occured in PyEval_CallObject");
167
168 /* get the content of the error message */
169 PyErr_Fetch(&perrtype, &perrvalue, &perrtrace);
170
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 MSG("errtypestring = \"%s\"",PyString_AsString(errtypestring));
181
182 errstring = NULL;
183 if(perrvalue != NULL
184 && (errstring = PyObject_Str(perrvalue)) != NULL
185 && PyString_Check(errstring)
186 ){
187 MSG("errstring = \"%s\"",PyString_AsString(errstring));
188 error_reporter(ASC_PROG_ERR
189 ,extpydata->name,0
190 ,PyString_AsString(errtypestring)
191 ,"%s",PyString_AsString(errstring)
192 );
193 }else{
194 error_reporter(ASC_PROG_ERR,extpydata->name,0,extpydata->name,"(unknown python error)");
195 }
196 PyErr_Print();
197 ret = 1;
198 goto cleanup_extpy_invokemethod;
199 }
200
201 ret=0;
202
203 cleanup_extpy_invokemethod:
204 Py_XDECREF(dict);
205 Py_XDECREF(arglist);
206 Py_XDECREF(pyinstance);
207 Py_XDECREF(errstring);
208 Py_XDECREF(errtypestring);
209 Py_XDECREF(perrtype);
210 Py_XDECREF(perrvalue);
211 Py_XDECREF(perrtrace);
212 return ret;
213 }
214
215 /**
216 Free memory associated with a registered script method.
217 @return 0 on success
218 */
219 int extpy_destroy(void *user_data){
220 struct ExtPyData *extpydata;
221 extpydata = (struct ExtPyData *)user_data;
222 Py_DECREF(extpydata->fn);
223 ASC_FREE(extpydata->name);
224 ASC_FREE(extpydata);
225 return 0;
226 }
227
228 /*------------------------------------------------------------------------------
229 'EXTPY' PYTHON STATIC MODULE
230 */
231
232 static PyObject *extpy_getbrowser(PyObject *self, PyObject *args){
233 PyObject *browser;
234 if(args!=NULL){
235 ERROR_REPORTER_HERE(ASC_PROG_ERR,"args is not NULL?!");
236 }
237 browser = (PyObject *)importhandler_getsharedpointer("browser");
238 if(browser==NULL){
239 return Py_BuildValue("");
240 }
241 return browser;
242 /* return Py_BuildValue("O",browser);*/
243 }
244
245 static PyObject *extpy_registermethod(PyObject *self, PyObject *args){
246 PyObject *fn, *name, *docstring;
247 const char *cname, *cdocstring;
248 int res;
249 int nargs = 1;
250 struct ExtPyData *extpydata;
251
252 PyArg_ParseTuple(args,"O:registermethod", &fn);
253 if(!PyCallable_Check(fn)){
254 PyErr_SetString(PyExc_TypeError,"parameter must be callable");
255 return NULL;
256 }
257
258 /* MSG("FOUND FN=%p",fn); */
259
260 name = PyObject_GetAttr(fn,PyString_FromString("__name__"));
261 if(name==NULL){
262 MSG("No __name__ attribute");
263 PyErr_SetString(PyExc_TypeError,"No __name__ attribute");
264 return NULL;
265 }
266 cname = PyString_AsString(name);
267
268 /* MSG("REGISTERED METHOD '%s' HAS %d ARGS",cname,nargs); */
269
270 docstring = PyObject_GetAttr(fn,PyString_FromString("func_doc"));
271 cdocstring = "(no help)";
272 if(name!=NULL){
273 cdocstring = PyString_AsString(docstring);
274 //MSG("DOCSTRING: %s",cdocstring);
275 }
276
277 extpydata = ASC_NEW(struct ExtPyData);
278 extpydata->name = ASC_NEW_ARRAY(char,strlen(cname)+1);
279 extpydata->fn = fn;
280 strcpy(extpydata->name, cname);
281
282 res = CreateUserFunctionMethod(cname,&extpy_invokemethod,nargs,cdocstring,(void *)extpydata,&extpy_destroy);
283 Py_INCREF(fn);
284
285 /* MSG("EXTPY 'fn' IS AT %p",fn); */
286
287 /* MSG("EXTPY INVOKER IS AT %p",extpy_invokemethod); */
288
289 if(res){
290 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Problem registering external script method (%d)",res);
291 PyErr_SetString(PyExc_Exception,"unable to register script method");
292 return NULL;
293 }
294
295 MSG("Registered python method '%s'",cname);
296
297 /* nothing gets returned (but possibly an exception) */
298 return Py_BuildValue("");
299 }
300
301 static PyMethodDef extpymethods[] = {
302 {"getbrowser", extpy_getbrowser, METH_NOARGS,"Retrieve browser pointer"}
303 ,{"registermethod", extpy_registermethod, METH_VARARGS,"Register a python method as an ASCEND script method"}
304 ,{NULL,NULL,0,NULL}
305 };
306
307 PyMODINIT_FUNC initextpy(void){
308 PyObject *obj;
309 obj = Py_InitModule3("extpy", extpymethods,"Module for accessing shared ASCEND pointers from python");
310 }
311
312 /*------------------------------------------------------------------------------
313 STANDARD IMPORT HANDLER ROUTINES
314 */
315
316 /**
317 Create a filename base on a partial filename. In that case of python, this
318 just means adding '.py' to the end.
319
320 @param partialname the filename without suffix, as specified in the user's "IMPORT" command
321 @return new filename, or NULL on failure
322 */
323 char *extpy_filename(const char *partialname){
324 char *name;
325 int len;
326 if(partialname==NULL){
327 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Partial name is NULL, can't work out filename");
328 return NULL;
329 }
330
331 len = strlen(partialname);
332 name = ASC_NEW_ARRAY_CLEAR(char,len+4);
333 strcpy(name,partialname);
334 strcat(name,".py");
335 MSG("New filename is '%s'",name);
336 return name;
337 }
338
339 /**
340 Import a python script located at the path indicated.
341
342 @return 0 on success, else error codes (TBD)
343 */
344 int extpy_import(const struct FilePath *fp, const char *initfunc, const char *partialpath){
345 char *name;
346 name = ospath_str(fp);
347 FILE *f;
348 PyObject *pyfile;
349 int iserr;
350
351 MSG("Importing Python script %s",name);
352
353 if(Py_IsInitialized()){
354 MSG("Python was already initialised");
355 }else{
356 MSG("INITIALISING PYTHON");
357 Py_Initialize();
358 MSG("COMPLETED ATTEMPT TO INITIALISE PYTHON");
359 }
360
361 if(!Py_IsInitialized()){
362 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to initialise Python");
363 MSG("UNABLE TO INITIALIZE PYTHON");
364 ASC_FREE(name);
365 return 1;
366 }
367
368 initextpy();
369
370 if(PyRun_SimpleString("import ascpy")){
371 MSG("Failed importing 'ascpy'");
372 return 1;
373 }
374
375 pyfile = PyFile_FromString(name,"r");
376 if(pyfile==NULL){
377 MSG("Failed opening script");
378 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to open '%s' (%s)",partialpath,name);
379 ASC_FREE(name);
380 return 1;
381 }
382
383 f = PyFile_AsFile(pyfile);
384 if(f==NULL){
385 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to cast PyObject to FILE*");
386 ASC_FREE(name);
387 return 1;
388 }
389
390 PyErr_Clear();
391
392 iserr = PyRun_AnyFileEx(f,name,1);
393
394 if(iserr){
395 /* according to the manual, there is no way of determining anything more about the error. */
396 ERROR_REPORTER_HERE(ASC_PROG_ERR,"An error occurring in importing the script '%s'",name);
397 return 1;
398 }
399
400 MSG("Imported python script '%s'",partialpath);
401
402 ASC_FREE(name);
403 return 0;
404 }
405
406 #endif /* WITH_PYTHON */
407

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