/[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 935 - (show annotations) (download) (as text)
Fri Nov 24 00:37:11 2006 UTC (13 years, 3 months ago) by johnpye
File MIME type: text/x-csrc
File size: 9205 byte(s)
Adopted suggestions wrt Py_None on Windows platform (extpy)
Fixed up default values of CUNIT_PREFIX (needs testing for case where missing)
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_BuildValue("");
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 return Py_BuildValue("");
226 }
227
228 static PyMethodDef extpymethods[] = {
229 {"getbrowser", extpy_getbrowser, METH_NOARGS,"Retrieve browser pointer"}
230 ,{"registermethod", extpy_registermethod, METH_VARARGS,"Register a python method as an ASCEND script method"}
231 ,{NULL,NULL,0,NULL}
232 };
233
234 PyMODINIT_FUNC initextpy(void){
235 PyObject *obj;
236 obj = Py_InitModule3("extpy", extpymethods,"Module for accessing shared ASCEND pointers from python");
237 }
238
239 /*------------------------------------------------------------------------------
240 STANDARD IMPORT HANDLER ROUTINES
241 */
242
243 /**
244 Create a filename base on a partial filename. In that case of python, this
245 just means adding '.py' to the end.
246
247 @param partialname the filename without suffix, as specified in the user's "IMPORT" command
248 @return new filename, or NULL on failure
249 */
250 char *extpy_filename(const char *partialname){
251 char *name;
252 int len;
253 if(partialname==NULL){
254 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Partial name is NULL, can't work out filename");
255 return NULL;
256 }
257
258 len = strlen(partialname);
259 name = ASC_NEW_ARRAY_CLEAR(char,len+4);
260 strcpy(name,partialname);
261 strcat(name,".py");
262 CONSOLE_DEBUG("New filename is '%s'",name);
263 return name;
264 }
265
266 /**
267 Import a python script located at the path indicated.
268
269 @return 0 on success, else error codes (TBD)
270 */
271 int extpy_import(const struct FilePath *fp, const char *initfunc, const char *partialpath){
272 char *name;
273 name = ospath_str(fp);
274 FILE *f;
275 PyObject *pyfile;
276 int iserr;
277
278 CONSOLE_DEBUG("Importing Python script %s",name);
279 if(Py_IsInitialized()){
280 CONSOLE_DEBUG("Python was already initialised");
281 }else{
282 CONSOLE_DEBUG("INITIALISING PYTHON");
283 Py_Initialize();
284 CONSOLE_DEBUG("COMPLETED ATTEMPT TO INITIALISE PYTHON");
285 }
286
287 if(!Py_IsInitialized()){
288 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to initialise Python");
289 CONSOLE_DEBUG("UNABLE TO INITIALIZE PYTHON");
290 ASC_FREE(name);
291 return 1;
292 }
293
294 initextpy();
295
296 if(PyRun_SimpleString("import ascpy")){
297 CONSOLE_DEBUG("Failed importing 'ascpy'");
298 return 1;
299 }
300
301 pyfile = PyFile_FromString(name,"r");
302 if(pyfile==NULL){
303 CONSOLE_DEBUG("Failed opening script");
304 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to open '%s' (%s)",partialpath,name);
305 ASC_FREE(name);
306 return 1;
307 }
308
309 f = PyFile_AsFile(pyfile);
310 if(f==NULL){
311 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to cast PyObject to FILE*");
312 ASC_FREE(name);
313 return 1;
314 }
315
316 PyErr_Clear();
317
318 iserr = PyRun_AnyFileEx(f,name,1);
319
320 if(iserr){
321 /* according to the manual, there is no way of determining anything more about the error. */
322 ERROR_REPORTER_HERE(ASC_PROG_ERR,"An error occurring in importing the script '%s'",name);
323 return 1;
324 }
325
326 ERROR_REPORTER_HERE(ASC_PROG_NOTE,"Imported python script '%s'\n",partialpath);
327
328 ASC_FREE(name);
329 return 0;
330 }
331

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