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

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