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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 877 - (hide 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 johnpye 862 /* 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 johnpye 869 *//**
19     @file
20     Import handler to provide external python script functionality for ASCEND.
21 johnpye 862 */
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 johnpye 873 #include <compiler/extfunc.h>
32 johnpye 862
33 johnpye 865 #include <Python.h>
34    
35 johnpye 862 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 johnpye 869 /*------------------------------------------------------------------------------
66 johnpye 873 PYTHON METHOD INVOKER
67 johnpye 869 */
68    
69 johnpye 873 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 johnpye 874 PyObject *fn, *arglist, *result;
81 johnpye 875 PyObject *pycontext;
82 johnpye 873 /* cast user data to PyObject pointer */
83 johnpye 874
84     CONSOLE_DEBUG("USER_DATA IS AT %p",user_data);
85    
86 johnpye 873 fn = (PyObject *) user_data;
87    
88     ERROR_REPORTER_HERE(ASC_USER_NOTE,"RUNNING PYTHON METHOD");
89     CONSOLE_DEBUG("RUNNING PYTHON METHOD...");
90 johnpye 874
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 johnpye 875 /*
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 johnpye 877 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 johnpye 875 */
105     importhandler_setsharedpointer("context",(void *)context);
106    
107 johnpye 877 /*
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 johnpye 874 arglist = Py_BuildValue("(i)", 666);
112 johnpye 875
113     CONSOLE_DEBUG("CALLING PYTHON");
114 johnpye 874 result = PyEval_CallObject(fn, arglist);
115 johnpye 875 CONSOLE_DEBUG("DONE CALLING PYTHON");
116 johnpye 874 Py_DECREF(arglist);
117 johnpye 875
118 johnpye 874 if(PyErr_Occurred()){
119 johnpye 877 /** @TODO we really want to capture these error messages and output them to the GUI, instead of outputting them to the console */
120 johnpye 875 PyErr_Print();
121     ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed running python method (see console)");
122 johnpye 874 return 1;
123     }
124    
125     return 0;
126 johnpye 873 }
127    
128     /*------------------------------------------------------------------------------
129     'EXTPY' PYTHON STATIC MODULE
130     */
131    
132 johnpye 869 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 johnpye 873 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 johnpye 874 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 johnpye 873
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 johnpye 869 static PyMethodDef extpymethods[] = {
193     {"getbrowser", extpy_getbrowser, METH_NOARGS,"Retrieve browser pointer"}
194 johnpye 873 ,{"registermethod", extpy_registermethod, METH_VARARGS,"Register a python method as an ASCEND script method"}
195 johnpye 869 ,{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 johnpye 862 /**
208     Create a filename base on a partial filename. In that case of python, this
209     just means adding '.py' to the end.
210 johnpye 867
211 johnpye 862 @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 johnpye 867
222 johnpye 862 len = strlen(partialname);
223     name = ASC_NEW_ARRAY_CLEAR(char,len+4);
224     strcpy(name,partialname);
225     strcat(name,".py");
226 johnpye 865 CONSOLE_DEBUG("New filename is '%s'",name);
227 johnpye 862 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 johnpye 864 int extpy_import(const struct FilePath *fp, const char *initfunc, const char *partialpath){
236 johnpye 862 char *name;
237     name = ospath_str(fp);
238 johnpye 865 FILE *f;
239 johnpye 872 PyObject *pyfile;
240 johnpye 865
241 johnpye 873 CONSOLE_DEBUG("Importing Python script %s",name);
242 johnpye 865 if(Py_IsInitialized()){
243 johnpye 873 CONSOLE_DEBUG("Python was already initialised");
244 johnpye 865 }else{
245     CONSOLE_DEBUG("INITIALISING PYTHON");
246     Py_Initialize();
247 johnpye 867 CONSOLE_DEBUG("COMPLETED ATTEMPT TO INITIALISE PYTHON");
248 johnpye 865 }
249    
250 johnpye 867 if(!Py_IsInitialized()){
251 johnpye 873 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to initialise Python");
252 johnpye 867 CONSOLE_DEBUG("UNABLE TO INITIALIZE PYTHON");
253 johnpye 873 ASC_FREE(name);
254 johnpye 867 return 1;
255     }
256    
257 johnpye 869 initextpy();
258    
259 johnpye 875 if(PyRun_SimpleString("import ascpy")){
260     CONSOLE_DEBUG("Failed importing 'ascpy'");
261     return 1;
262     }
263    
264 johnpye 872 pyfile = PyFile_FromString(name,"r");
265     if(pyfile==NULL){
266 johnpye 871 CONSOLE_DEBUG("Failed opening script");
267 johnpye 873 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to open '%s' (%s)",partialpath,name);
268     ASC_FREE(name);
269 johnpye 871 return 1;
270     }
271 johnpye 872
272     f = PyFile_AsFile(pyfile);
273     if(f==NULL){
274 johnpye 873 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to cast PyObject to FILE*");
275     ASC_FREE(name);
276 johnpye 872 return 1;
277     }
278 johnpye 873
279 johnpye 872 PyRun_AnyFileEx(f,name,1);
280 johnpye 873 /*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 johnpye 871
287 johnpye 873 ERROR_REPORTER_HERE(ASC_PROG_NOTE,"Imported python script '%s' (check console for errors)",partialpath);
288    
289 johnpye 862 ASC_FREE(name);
290 johnpye 873 return 0;
291 johnpye 862 }
292    

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