/[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 930 - (show annotations) (download) (as text)
Wed Nov 22 13:09:09 2006 UTC (17 years, 9 months ago) by johnpye
File MIME type: text/x-csrc
File size: 9204 byte(s)
solve.py raises ImportError if 'browser' object is not available (right approach?)
Freeing some variables in dsgsat2.a4c.
Returing Py_None from extpy routine in 'browser' object not defined (eg during non-GUI unit testing)
Error reporting from extpy import handler (ongoing)
Timeout in versioncheck (when server unavailable)
A little more tinkering with IDA.
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 if(browser==NULL){
167 return Py_None;
168 }
169 return Py_BuildValue("O",browser);
170 }
171
172 static PyObject *extpy_registermethod(PyObject *self, PyObject *args){
173 PyObject *fn, *name, *docstring;
174 const char *cname, *cdocstring;
175 int res;
176 int nargs = 1;
177 struct ExtPyData *extpydata;
178
179 PyArg_ParseTuple(args,"O:registermethod", &fn);
180 if(!PyCallable_Check(fn)){
181 PyErr_SetString(PyExc_TypeError,"parameter must be callable");
182 return NULL;
183 }
184
185 /* CONSOLE_DEBUG("FOUND FN=%p",fn); */
186
187 name = PyObject_GetAttr(fn,PyString_FromString("__name__"));
188 if(name==NULL){
189 CONSOLE_DEBUG("No __name__ attribute");
190 PyErr_SetString(PyExc_TypeError,"No __name__ attribute");
191 return NULL;
192 }
193 cname = PyString_AsString(name);
194
195 /* CONSOLE_DEBUG("REGISTERED METHOD '%s' HAS %d ARGS",cname,nargs); */
196
197 docstring = PyObject_GetAttr(fn,PyString_FromString("func_doc"));
198 cdocstring = "(no help)";
199 if(name!=NULL){
200 cdocstring = PyString_AsString(docstring);
201 CONSOLE_DEBUG("DOCSTRING: %s",cdocstring);
202 }
203
204 extpydata = ASC_NEW(struct ExtPyData);
205 extpydata->name = ASC_NEW_ARRAY(char,strlen(cname)+1);
206 extpydata->fn = fn;
207 strcpy(extpydata->name, cname);
208
209 res = CreateUserFunctionMethod(cname,&extpy_invokemethod,nargs,cdocstring,(void *)extpydata,&extpy_destroy);
210 Py_INCREF(fn);
211
212 /* CONSOLE_DEBUG("EXTPY 'fn' IS AT %p",fn); */
213
214 /* CONSOLE_DEBUG("EXTPY INVOKER IS AT %p",extpy_invokemethod); */
215
216 if(res){
217 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Problem registering external script method (%d)",res);
218 PyErr_SetString(PyExc_Exception,"unable to register script method");
219 return NULL;
220 }
221
222 ERROR_REPORTER_HERE(ASC_PROG_NOTE,"Registered python method '%s'\n",cname);
223
224 /* nothing gets returned (but possibly an exception) */
225 Py_INCREF(Py_None);
226 return Py_None;
227 }
228
229 static PyMethodDef extpymethods[] = {
230 {"getbrowser", extpy_getbrowser, METH_NOARGS,"Retrieve browser pointer"}
231 ,{"registermethod", extpy_registermethod, METH_VARARGS,"Register a python method as an ASCEND script method"}
232 ,{NULL,NULL,0,NULL}
233 };
234
235 PyMODINIT_FUNC initextpy(void){
236 PyObject *obj;
237 obj = Py_InitModule3("extpy", extpymethods,"Module for accessing shared ASCEND pointers from python");
238 }
239
240 /*------------------------------------------------------------------------------
241 STANDARD IMPORT HANDLER ROUTINES
242 */
243
244 /**
245 Create a filename base on a partial filename. In that case of python, this
246 just means adding '.py' to the end.
247
248 @param partialname the filename without suffix, as specified in the user's "IMPORT" command
249 @return new filename, or NULL on failure
250 */
251 char *extpy_filename(const char *partialname){
252 char *name;
253 int len;
254 if(partialname==NULL){
255 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Partial name is NULL, can't work out filename");
256 return NULL;
257 }
258
259 len = strlen(partialname);
260 name = ASC_NEW_ARRAY_CLEAR(char,len+4);
261 strcpy(name,partialname);
262 strcat(name,".py");
263 CONSOLE_DEBUG("New filename is '%s'",name);
264 return name;
265 }
266
267 /**
268 Import a python script located at the path indicated.
269
270 @return 0 on success, else error codes (TBD)
271 */
272 int extpy_import(const struct FilePath *fp, const char *initfunc, const char *partialpath){
273 char *name;
274 name = ospath_str(fp);
275 FILE *f;
276 PyObject *pyfile;
277 int iserr;
278
279 CONSOLE_DEBUG("Importing Python script %s",name);
280 if(Py_IsInitialized()){
281 CONSOLE_DEBUG("Python was already initialised");
282 }else{
283 CONSOLE_DEBUG("INITIALISING PYTHON");
284 Py_Initialize();
285 CONSOLE_DEBUG("COMPLETED ATTEMPT TO INITIALISE PYTHON");
286 }
287
288 if(!Py_IsInitialized()){
289 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to initialise Python");
290 CONSOLE_DEBUG("UNABLE TO INITIALIZE PYTHON");
291 ASC_FREE(name);
292 return 1;
293 }
294
295 initextpy();
296
297 if(PyRun_SimpleString("import ascpy")){
298 CONSOLE_DEBUG("Failed importing 'ascpy'");
299 return 1;
300 }
301
302 pyfile = PyFile_FromString(name,"r");
303 if(pyfile==NULL){
304 CONSOLE_DEBUG("Failed opening script");
305 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to open '%s' (%s)",partialpath,name);
306 ASC_FREE(name);
307 return 1;
308 }
309
310 f = PyFile_AsFile(pyfile);
311 if(f==NULL){
312 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to cast PyObject to FILE*");
313 ASC_FREE(name);
314 return 1;
315 }
316
317 PyErr_Clear();
318
319 iserr = PyRun_AnyFileEx(f,name,1);
320
321 if(iserr){
322 /* according to the manual, there is no way of determining anything more about the error. */
323 ERROR_REPORTER_HERE(ASC_PROG_ERR,"An error occurring in importing the script '%s'",name);
324 return 1;
325 }
326
327 ERROR_REPORTER_HERE(ASC_PROG_NOTE,"Imported python script '%s'",partialpath);
328
329 ASC_FREE(name);
330 return 0;
331 }
332

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