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

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