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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3258 - (show 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 /* 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 MSG("errtypestring = \"%s\"",PyString_AsString(errtypestring));
183
184 errstring = NULL;
185 if(perrvalue != NULL
186 && (errstring = PyObject_Str(perrvalue)) != NULL
187 && PyString_Check(errstring)
188 ){
189 MSG("errstring = \"%s\"",PyString_AsString(errstring));
190 error_reporter(ASC_PROG_ERR
191 ,extpydata->name,0
192 ,PyString_AsString(errtypestring)
193 ,"Error in extpy call: %s",PyString_AsString(errstring)
194 );
195 }else{
196 error_reporter(ASC_PROG_ERR,extpydata->name,0,extpydata->name,"(unknown python error)");
197 }
198 PyErr_Print();
199 ret = 1;
200 goto cleanup_extpy_invokemethod;
201 }
202
203 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 }
216
217 /**
218 Free memory associated with a registered script method.
219 @return 0 on success
220 */
221 int extpy_destroy(void *user_data){
222 struct ExtPyData *extpydata;
223 extpydata = (struct ExtPyData *)user_data;
224 Py_DECREF(extpydata->fn);
225 ASC_FREE(extpydata->name);
226 ASC_FREE(extpydata);
227 return 0;
228 }
229
230 /*------------------------------------------------------------------------------
231 'EXTPY' PYTHON STATIC MODULE
232 */
233
234 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 if(browser==NULL){
241 return Py_BuildValue("");
242 }
243 return browser;
244 /* return Py_BuildValue("O",browser);*/
245 }
246
247 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 struct ExtPyData *extpydata;
253
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 /* MSG("FOUND FN=%p",fn); */
261
262 name = PyObject_GetAttr(fn,PyString_FromString("__name__"));
263 if(name==NULL){
264 MSG("No __name__ attribute");
265 PyErr_SetString(PyExc_TypeError,"No __name__ attribute");
266 return NULL;
267 }
268 cname = PyString_AsString(name);
269
270 /* MSG("REGISTERED METHOD '%s' HAS %d ARGS",cname,nargs); */
271
272 docstring = PyObject_GetAttr(fn,PyString_FromString("func_doc"));
273 cdocstring = "(no help)";
274 if(name!=NULL){
275 cdocstring = PyString_AsString(docstring);
276 //MSG("DOCSTRING: %s",cdocstring);
277 }
278
279 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 Py_INCREF(fn);
286
287 /* MSG("EXTPY 'fn' IS AT %p",fn); */
288
289 /* MSG("EXTPY INVOKER IS AT %p",extpy_invokemethod); */
290
291 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 MSG("Registered python method '%s'",cname);
298
299 /* nothing gets returned (but possibly an exception) */
300 return Py_BuildValue("");
301 }
302
303 static PyMethodDef extpymethods[] = {
304 {"getbrowser", extpy_getbrowser, METH_NOARGS,"Retrieve browser pointer"}
305 ,{"registermethod", extpy_registermethod, METH_VARARGS,"Register a python method as an ASCEND script method"}
306 ,{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 /**
319 Create a filename base on a partial filename. In that case of python, this
320 just means adding '.py' to the end.
321
322 @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
333 len = strlen(partialname);
334 name = ASC_NEW_ARRAY_CLEAR(char,len+4);
335 strcpy(name,partialname);
336 strcat(name,".py");
337 MSG("New filename is '%s'",name);
338 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 int extpy_import(const struct FilePath *fp, const char *initfunc, const char *partialpath){
347 char *name;
348 name = ospath_str(fp);
349 FILE *f;
350 PyObject *pyfile;
351 int iserr;
352
353 MSG("Importing Python script %s",name);
354
355 if(Py_IsInitialized()){
356 MSG("Python was already initialised");
357 }else{
358 MSG("INITIALISING PYTHON");
359 Py_Initialize();
360 MSG("COMPLETED ATTEMPT TO INITIALISE PYTHON");
361 }
362
363 if(!Py_IsInitialized()){
364 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to initialise Python");
365 MSG("UNABLE TO INITIALIZE PYTHON");
366 ASC_FREE(name);
367 return 1;
368 }
369
370 initextpy();
371
372 if(PyRun_SimpleString("import ascpy")){
373 MSG("Failed importing 'ascpy'");
374 return 1;
375 }
376
377 pyfile = PyFile_FromString(name,"r");
378 if(pyfile==NULL){
379 MSG("Failed opening script");
380 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to open '%s' (%s)",partialpath,name);
381 ASC_FREE(name);
382 return 1;
383 }
384
385 f = PyFile_AsFile(pyfile);
386 if(f==NULL){
387 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to cast PyObject to FILE*");
388 ASC_FREE(name);
389 return 1;
390 }
391
392 PyErr_Clear();
393
394 iserr = PyRun_AnyFileEx(f,name,1);
395
396 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 return 1;
400 }
401
402 MSG("Imported python script '%s'",partialpath);
403
404 ASC_FREE(name);
405 return 0;
406 }
407
408 #endif /* WITH_PYTHON */
409

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