/[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 914 - (show annotations) (download) (as text)
Sat Oct 28 05:27:58 2006 UTC (13 years, 9 months ago) by johnpye
File MIME type: text/x-csrc
File size: 9125 byte(s)
Fixed prototype for extpy_destroy
Dubious fix for the message about directory node in doc/SConscript (is $ expansion happening?)

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 #include <Python.h>
26
27 #include <stdio.h>
28 #include <string.h>
29
30 #include <utilities/ascConfig.h>
31 #include <utilities/error.h>
32 #include <general/ospath.h>
33
34 #include <compiler/importhandler.h>
35 #include <compiler/extfunc.h>
36
37
38 ImportHandlerCreateFilenameFn extpy_filename;
39 ImportHandlerImportFn extpy_import;
40
41 ExtMethodDestroyFn extpy_destroy;
42
43
44 #ifndef ASC_EXPORT
45 # error "Where is ASC_EXPORT?"
46 #endif
47
48 struct ExtPyData{
49 PyObject *fn;
50 char *name;
51 };
52
53 /**
54 This is the function called from "IMPORT extpy"
55
56 It sets up the functions in this external function library
57 */
58 extern ASC_EXPORT(int) extpy_register(){
59 int result = 0;
60
61 struct ImportHandler *handler;
62 handler = ASC_NEW(struct ImportHandler);
63
64 handler->name = "extpy";
65 handler->filenamefn = &extpy_filename;
66 handler->importfn = &extpy_import;
67
68 result = importhandler_add(handler);
69
70 if(result){
71 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed to register import handler (error = %d)",result);
72 }
73 return result;
74 }
75
76 /*------------------------------------------------------------------------------
77 PYTHON METHOD INVOKER
78 */
79
80 ExtMethodRun extpy_invokemethod;
81
82 /** Method invoker. extpy will supply a pointer to this function whenever it
83 registers a python function as an external script method. This function will
84 then dereference the user_data field into a python function, and execute that
85 python function.
86
87 One difficult aspect is the question of how to usefully pass the 'context'
88 argument to Python?
89 */
90 int extpy_invokemethod(struct Instance *context, struct gl_list_t *args, void *user_data){
91 PyObject *arglist, *result;
92 struct ExtPyData *extpydata;
93 /* cast user data to PyObject pointer */
94
95 /* CONSOLE_DEBUG("USER_DATA IS AT %p",user_data); */
96
97 extpydata = (struct ExtPyData *) user_data;
98
99 /* ERROR_REPORTER_HERE(ASC_USER_NOTE,"RUNNING PYTHON METHOD"); */
100 /* CONSOLE_DEBUG("RUNNING PYTHON METHOD..."); */
101
102 CONSOLE_DEBUG("Running python method '%s'",extpydata->name);
103
104 PyErr_Clear();
105
106 /* CONSOLE_DEBUG("PyObject 'fn' is at %p",extpydata->fn); */
107
108 if(!PyCallable_Check(extpydata->fn)){
109 ERROR_REPORTER_HERE(ASC_PROG_ERR,"user_data is not a PyCallable");
110 return 1;
111 }
112
113 /*
114 We need to be able to convert C 'struct Instance' pointers to Python 'Instance' objects.
115 This functionality is implemented in 'ascpy' but we're not going to link to that here,
116 so we will use the importhandler 'setsharedpointer' trick to pass the object to the
117 'registry' then write a routine in ascpy that will cast it into the appropriate
118 Python object.
119 */
120 importhandler_setsharedpointer("context",(void *)context);
121
122 /*
123 Eventually we'll work out how to pass the instance object directly into Python. For
124 the moment, just have a placeholder variable instead:
125 */
126 arglist = Py_BuildValue("(i)", 666);
127
128 /* CONSOLE_DEBUG("CALLING PYTHON"); */
129 result = PyEval_CallObject(extpydata->fn, arglist);
130 /* CONSOLE_DEBUG("DONE CALLING PYTHON"); */
131 Py_DECREF(arglist);
132
133 if(PyErr_Occurred()){
134 /** @TODO we really want to capture these error messages and output them to the GUI, instead of outputting them to the console */
135 PyErr_Print();
136 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed running python method (see console)");
137 return 1;
138 }
139
140 return 0;
141 }
142
143 /**
144 Free memory associated with a registered script method.
145 @return 0 on success
146 */
147 int extpy_destroy(void *user_data){
148 struct ExtPyData *extpydata;
149 extpydata = (struct ExtPyData *)user_data;
150 Py_DECREF(extpydata->fn);
151 ASC_FREE(extpydata->name);
152 ASC_FREE(extpydata);
153 return 0;
154 }
155
156 /*------------------------------------------------------------------------------
157 'EXTPY' PYTHON STATIC MODULE
158 */
159
160 static PyObject *extpy_getbrowser(PyObject *self, PyObject *args){
161 PyObject *browser;
162 if(args!=NULL){
163 ERROR_REPORTER_HERE(ASC_PROG_ERR,"args is not NULL?!");
164 }
165 browser = (PyObject *)importhandler_getsharedpointer("browser");
166 return Py_BuildValue("O",browser);
167 }
168
169 static PyObject *extpy_registermethod(PyObject *self, PyObject *args){
170 PyObject *fn, *name, *docstring;
171 const char *cname, *cdocstring;
172 int res;
173 int nargs = 1;
174 struct ExtPyData *extpydata;
175
176 PyArg_ParseTuple(args,"O:registermethod", &fn);
177 if(!PyCallable_Check(fn)){
178 PyErr_SetString(PyExc_TypeError,"parameter must be callable");
179 return NULL;
180 }
181
182 /* CONSOLE_DEBUG("FOUND FN=%p",fn); */
183
184 name = PyObject_GetAttr(fn,PyString_FromString("__name__"));
185 if(name==NULL){
186 CONSOLE_DEBUG("No __name__ attribute");
187 PyErr_SetString(PyExc_TypeError,"No __name__ attribute");
188 return NULL;
189 }
190 cname = PyString_AsString(name);
191
192 /* CONSOLE_DEBUG("REGISTERED METHOD '%s' HAS %d ARGS",cname,nargs); */
193
194 docstring = PyObject_GetAttr(fn,PyString_FromString("func_doc"));
195 cdocstring = "(no help)";
196 if(name!=NULL){
197 cdocstring = PyString_AsString(docstring);
198 CONSOLE_DEBUG("DOCSTRING: %s",cdocstring);
199 }
200
201 extpydata = ASC_NEW(struct ExtPyData);
202 extpydata->name = ASC_NEW_ARRAY(char,strlen(cname)+1);
203 extpydata->fn = fn;
204 strcpy(extpydata->name, cname);
205
206 res = CreateUserFunctionMethod(cname,&extpy_invokemethod,nargs,cdocstring,(void *)extpydata,&extpy_destroy);
207 Py_INCREF(fn);
208
209 /* CONSOLE_DEBUG("EXTPY 'fn' IS AT %p",fn); */
210
211 /* CONSOLE_DEBUG("EXTPY INVOKER IS AT %p",extpy_invokemethod); */
212
213 if(res){
214 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Problem registering external script method (%d)",res);
215 PyErr_SetString(PyExc_Exception,"unable to register script method");
216 return NULL;
217 }
218
219 ERROR_REPORTER_HERE(ASC_PROG_NOTE,"Registered python method '%s'",cname);
220
221 /* nothing gets returned (but possibly an exception) */
222 Py_INCREF(Py_None);
223 return Py_None;
224 }
225
226 static PyMethodDef extpymethods[] = {
227 {"getbrowser", extpy_getbrowser, METH_NOARGS,"Retrieve browser pointer"}
228 ,{"registermethod", extpy_registermethod, METH_VARARGS,"Register a python method as an ASCEND script method"}
229 ,{NULL,NULL,0,NULL}
230 };
231
232 PyMODINIT_FUNC initextpy(void){
233 PyObject *obj;
234 obj = Py_InitModule3("extpy", extpymethods,"Module for accessing shared ASCEND pointers from python");
235 }
236
237 /*------------------------------------------------------------------------------
238 STANDARD IMPORT HANDLER ROUTINES
239 */
240
241 /**
242 Create a filename base on a partial filename. In that case of python, this
243 just means adding '.py' to the end.
244
245 @param partialname the filename without suffix, as specified in the user's "IMPORT" command
246 @return new filename, or NULL on failure
247 */
248 char *extpy_filename(const char *partialname){
249 char *name;
250 int len;
251 if(partialname==NULL){
252 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Partial name is NULL, can't work out filename");
253 return NULL;
254 }
255
256 len = strlen(partialname);
257 name = ASC_NEW_ARRAY_CLEAR(char,len+4);
258 strcpy(name,partialname);
259 strcat(name,".py");
260 CONSOLE_DEBUG("New filename is '%s'",name);
261 return name;
262 }
263
264 /**
265 Import a python script located at the path indicated.
266
267 @return 0 on success, else error codes (TBD)
268 */
269 int extpy_import(const struct FilePath *fp, const char *initfunc, const char *partialpath){
270 char *name;
271 name = ospath_str(fp);
272 FILE *f;
273 PyObject *pyfile;
274
275 CONSOLE_DEBUG("Importing Python script %s",name);
276 if(Py_IsInitialized()){
277 CONSOLE_DEBUG("Python was already initialised");
278 }else{
279 CONSOLE_DEBUG("INITIALISING PYTHON");
280 Py_Initialize();
281 CONSOLE_DEBUG("COMPLETED ATTEMPT TO INITIALISE PYTHON");
282 }
283
284 if(!Py_IsInitialized()){
285 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to initialise Python");
286 CONSOLE_DEBUG("UNABLE TO INITIALIZE PYTHON");
287 ASC_FREE(name);
288 return 1;
289 }
290
291 initextpy();
292
293 if(PyRun_SimpleString("import ascpy")){
294 CONSOLE_DEBUG("Failed importing 'ascpy'");
295 return 1;
296 }
297
298 pyfile = PyFile_FromString(name,"r");
299 if(pyfile==NULL){
300 CONSOLE_DEBUG("Failed opening script");
301 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to open '%s' (%s)",partialpath,name);
302 ASC_FREE(name);
303 return 1;
304 }
305
306 f = PyFile_AsFile(pyfile);
307 if(f==NULL){
308 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to cast PyObject to FILE*");
309 ASC_FREE(name);
310 return 1;
311 }
312
313 PyRun_AnyFileEx(f,name,1);
314 /*if(PyErr_Occurred()){
315 ERROR_REPORTER_HERE(ASC_PROG_ERR,"An error occurred in the python script '%s'. Check the console for details");
316 PyErr_Print();
317 PyErr_Clear();
318 return 1;
319 }*/
320
321 ERROR_REPORTER_HERE(ASC_PROG_NOTE,"Imported python script '%s' (check console for errors)",partialpath);
322
323 ASC_FREE(name);
324 return 0;
325 }
326

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