source: trunk/src/python/PyRpUnits.cc @ 78

Last change on this file since 78 was 78, checked in by cxsong, 19 years ago
  1. fixed the return value, within PyRpUnits?.cc. it used to look for a i return value of 1 as a successful conversion, not it looks for return value of 0 for successful conversion.
  2. fixed the case where user requests a conversion with units="off" and expects a double back, now they really get a double back.
File size: 15.8 KB
Line 
1#include <Python.h>
2#include <RpUnits.h>
3
4static PyObject *ErrorObject;
5
6typedef struct {
7    PyObject_HEAD
8    RpUnits* rp_unit;
9} RpUnitsObject;
10
11static void
12RpUnitsObject_dealloc(RpUnitsObject *self)
13{
14    if (self) {
15        if (self->rp_unit){
16            // cant call this right now because destructor is private
17            // delete(self->rp_unit);
18        }
19        PyObject_Del(self);
20    }
21}
22
23static PyObject*
24RpUnitsObject_getUnits(RpUnitsObject *self)
25{
26    PyObject* rv = NULL;
27
28    if (self->rp_unit){
29        rv = PyString_FromString(self->rp_unit->getUnits().c_str());
30    }
31
32    return rv;
33}
34
35static PyObject*
36RpUnitsObject_getUnitsName(RpUnitsObject *self)
37{
38    PyObject* rv = NULL;
39
40    if (self->rp_unit){
41        rv = PyString_FromString(self->rp_unit->getUnitsName().c_str());
42    }
43
44    return rv;
45}
46
47static PyObject*
48RpUnitsObject_getExponent(RpUnitsObject *self)
49{
50    PyObject* rv = NULL;
51
52    if (self->rp_unit){
53        rv = PyFloat_FromDouble(self->rp_unit->getExponent());
54    }
55
56    return rv;
57}
58
59static PyObject * RpUnitsObject_convert(RpUnitsObject *self, PyObject *args);
60
61static PyObject *
62RpUnitsObject_makeBasis(RpUnitsObject *self, PyObject *args)
63{
64    PyObject* rv = NULL;
65    double inVal = 0;
66    double outVal = 0;
67    int result = 0;
68
69    if (PyTuple_Size(args) > 0) {
70        PyArg_ParseTuple(args, "d", &inVal);
71    }
72    else {
73        PyErr_SetString(PyExc_AttributeError, "incorrect input arguments");
74        return NULL;
75    }
76
77    if (self->rp_unit){
78        outVal = self->rp_unit->makeBasis(inVal, &result);
79        if (result) {
80            rv = PyFloat_FromDouble(outVal);
81        }
82        else {
83            PyErr_SetString(PyExc_StandardError, "could not convert to basis");
84        }
85    }
86    else {
87        PyErr_SetString(PyExc_AttributeError, "rp_unit is NULL");
88    }
89
90    return rv;
91}
92
93static PyMethodDef RpUnitsObject_methods[] = {
94    {"getUnits", (PyCFunction)RpUnitsObject_getUnits, METH_NOARGS,
95     "Return the base name of the RpUnitsObject" },
96    {"getUnitsName", (PyCFunction)RpUnitsObject_getUnitsName, METH_NOARGS,
97     "Return the whole (base and exponent) name of the RpUnitsObject" },
98    {"getExponent", (PyCFunction)RpUnitsObject_getExponent, METH_NOARGS,
99     "Return the exponent of the RpUnitsObject" },
100    {"convert", (PyCFunction)RpUnitsObject_convert, METH_VARARGS,
101     "convert a value from one RpUnits Object to another" },
102    {"makeBasis", (PyCFunction)RpUnitsObject_makeBasis, METH_VARARGS,
103     "return the basis value of the value provided" },
104   
105    {NULL}  /* Sentinel */
106};
107
108static PyTypeObject RpUnitsObjectType = {
109    /* The ob_type field must be initialized in the module init function
110     * to be portable to Windows without using C++. */
111    PyObject_HEAD_INIT(NULL)
112    0,                                    /*ob_size*/
113    "RpUnits.RpUnitsObject",              /*tp_name*/
114    sizeof(RpUnitsObject),                /*tp_basicsize*/
115    0,                                    /*tp_itemsize*/
116    /* methods */
117    (destructor)RpUnitsObject_dealloc,    /*tp_dealloc*/
118    0,                                    /*tp_print*/
119    0,                                    /*tp_getattr*/
120    0,                                    /*tp_setattr*/
121    0,                                    /*tp_compare*/
122    0,                                    /*tp_repr*/
123    0,                                    /*tp_as_number*/
124    0,                                    /*tp_as_sequence*/
125    0,                                    /*tp_as_mapping*/
126    0,                                    /*tp_hash*/
127    0,                                    /*tp_call*/
128    0,                                    /*tp_str*/
129    0,                                    /*tp_getattro*/
130    0,                                    /*tp_setattro*/
131    0,                                    /*tp_as_buffer*/
132    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,   /*tp_flags*/
133    "RpUnits Object",                     /*tp_doc*/
134    0,                                    /*tp_traverse*/
135    0,                                    /*tp_clear*/
136    0,                                    /*tp_richcompare*/
137    0,                                    /*tp_weaklistoffset*/
138    0,                                    /*tp_iter*/
139    0,                                    /*tp_iternext*/
140    RpUnitsObject_methods,                /* tp_methods */
141    0,                                    /*tp_members*/
142    0,                                    /*tp_getset*/
143    0,                                    /*tp_base*/
144    0,                                    /*tp_dict*/
145    0,                                    /*tp_descr_get*/
146    0,                                    /*tp_descr_set*/
147    0,                                    /*tp_dictoffset*/
148    0,                                    /*tp_init*/
149    0,                                    /*tp_alloc*/
150    // RpUnitsObject_new,                 /*tp_new*/
151    0,                                    /*tp_new*/
152    0,                                    /*tp_free*/
153    0,                                    /*tp_is_gc*/
154};
155
156static RpUnitsObject*
157newRpUnitsObject(PyObject *arg)
158{
159    RpUnitsObject* self;
160    self = PyObject_New(RpUnitsObject, &RpUnitsObjectType);
161    if (self == NULL)
162        return NULL;
163    self->rp_unit = NULL;
164    return self;
165}
166
167static PyObject *
168RpUnitsObject_convert(RpUnitsObject *self, PyObject *args)
169{
170    PyObject* rv = NULL;
171    // PyObject* toUnits = NULL;
172    RpUnitsObject* toUnits = NULL;
173    PyObject* inVal = NULL;
174    PyObject* argList = NULL;
175    PyObject* outVal = NULL;
176    int result = 0;
177    int argTupleSize = PyTuple_Size(args);
178
179    if (argTupleSize > 0) {
180        PyArg_ParseTuple(args, "O!O", &RpUnitsObjectType, &toUnits, &inVal);
181        argList = PyTuple_New(1);
182        PyTuple_SetItem(argList, 0, inVal);
183        /*
184         * need to make it so user can give any number of variables in arglist
185         * because the new argList is sent to the python conversion fxn where it
186         * will be parsed in python when c++ calls the conv fxn.
187        PyArg_ParseTuple(args, "O!", &RpUnitsObjectType, &toUnits);
188        if (argTupleSize > 1) {
189            argTupleSize--;
190            inVal = PyTuple_New(argTupleSize);
191            while (argTupleSize)
192            inVal = PyTuple_GetSlice(args,0,argTupleSize);
193        }
194        else {
195            // convertion function needs no arguments???
196            inVal = PyTuple_New(0);
197        }
198        */
199    }
200    else {
201        PyErr_SetString(PyExc_AttributeError, "Not enough arguments");
202        return Py_None;
203    }
204
205    if (self->rp_unit){
206        outVal = (PyObject*) self->rp_unit->convert(toUnits->rp_unit,
207                                                    // (void*)&inVal,
208                                                    (void*)argList,
209                                                    &result );
210        if (result) {
211            rv = outVal;
212        }
213    }
214
215    return rv;
216}
217
218/* --------------------------------------------------------------------- */
219
220PyDoc_STRVAR(RpUnits_define_doc,
221"define(name, basis) -> RpUnitsObject \n\
222\n\
223define RpUnits Object where 'name' is the string name of the units \n\
224and 'basis' is an RpUnitsObject pointer for the basis of the unit \n\
225being created");
226
227static PyObject *
228RpUnits_define(PyObject *self, PyObject *args)
229{
230    RpUnitsObject* newRpUnit;
231    RpUnitsObject* basis = NULL;
232    char* unitsName;
233
234    if (PyTuple_Size(args) > 0) {
235        PyArg_ParseTuple(args, "s|O!", &unitsName, &RpUnitsObjectType,&basis);
236    }
237    else {
238        PyErr_SetString(PyExc_AttributeError, "Not enough arguments");
239        return Py_None;
240    }
241
242    newRpUnit = newRpUnitsObject(args);
243
244    if (newRpUnit == NULL)
245        return NULL;
246
247
248    if (basis && basis->rp_unit) {
249        newRpUnit->rp_unit = RpUnits::define(unitsName, basis->rp_unit);
250    }
251    else {
252        newRpUnit->rp_unit = RpUnits::define(unitsName, NULL);
253    }
254
255    if (newRpUnit->rp_unit == NULL) {
256        // rp_unit was not allocated.
257        RpUnitsObject_dealloc(newRpUnit);
258        PyErr_SetString(PyExc_AttributeError, "allocating rp_unit failed");
259        return NULL;
260    }
261
262    return (PyObject *)newRpUnit;
263}
264
265void* PyCallback (void* fxnPtr, void* args)
266{
267    PyObject* retVal = NULL;
268   
269    if ((PyObject*)fxnPtr != NULL) {
270        retVal = PyObject_CallObject((PyObject*)fxnPtr,(PyObject*)args);
271    }
272
273    return (void*) retVal;
274
275}
276
277PyDoc_STRVAR(RpUnits_defineConv_doc,
278"defineConv(fromUnits,toUnits, forwConvFxn, backConvFxn) -> RpUnitsObject\n\
279\n\
280define RpUnits Object where 'name' is the string name of the units \n\
281and 'basis' is an RpUnitsObject pointer for the basis of the unit \n\
282being created");
283
284static PyObject *
285RpUnits_defineConv(PyObject *self, PyObject *args)
286{
287    RpUnitsObject* fromUnit = NULL;
288    RpUnitsObject* toUnit = NULL;
289    PyObject* forwConvFxnStr = NULL;
290    PyObject* backConvFxnStr = NULL;
291
292    RpUnitsObject* newRpUnit = NULL;
293    RpUnits* newConv = NULL;
294
295    if (PyTuple_Size(args) > 0) {
296        PyArg_ParseTuple(args, "O!O!O!O!",&RpUnitsObjectType, &fromUnit,
297                                          &RpUnitsObjectType, &toUnit,
298                                          &PyFunction_Type, &forwConvFxnStr,
299                                          &PyFunction_Type, &backConvFxnStr);
300    }
301    else {
302        PyErr_SetString(PyExc_AttributeError, "incorrect input arguments");
303        return NULL;
304    }
305
306    // check to make sure fromUnit and toUnit are populated
307    if ( (fromUnit == NULL) ||
308         (toUnit == NULL) ) {
309        PyErr_SetString(PyExc_AttributeError,
310                "could not retrieve fromUnit or toUnit from argument list");
311    }
312
313    // check to make sure forwConvFxnStr and backConvFxnStr are populated
314    if ( (forwConvFxnStr == NULL) || (backConvFxnStr == NULL) ) {
315        PyErr_SetString(PyExc_AttributeError,
316                "could not retrieve conversion function argument");
317        return NULL;
318    }
319
320    // make sure we get callable functions and non-null RpUnit Objects
321    if ( PyCallable_Check(forwConvFxnStr) &&
322         PyCallable_Check(backConvFxnStr) &&
323         fromUnit->rp_unit &&
324         toUnit->rp_unit) {
325
326        Py_INCREF(forwConvFxnStr);
327        Py_INCREF(backConvFxnStr);
328        newConv = RpUnits::define(  fromUnit->rp_unit,
329                                    toUnit->rp_unit,
330                                    PyCallback,
331                                    (void*)forwConvFxnStr,
332                                    PyCallback,
333                                    (void*)backConvFxnStr );
334    }
335    else {
336        PyErr_SetString(PyExc_AttributeError,
337                "could not retrieve conversion function argument");
338        return NULL;
339    }
340
341    if (newConv) {
342        newRpUnit = newRpUnitsObject(args);
343
344        if (newRpUnit) {
345            newRpUnit->rp_unit = newConv;
346        }
347    }
348
349    return (PyObject *)newRpUnit;
350}
351
352PyDoc_STRVAR(RpUnits_find_doc,
353"find(name) -> RpUnitsObject \n\
354\n\
355search the dictionary of created RpUnits for a unit matching \n\
356the string 'name'");
357
358static PyObject *
359RpUnits_find(PyObject *self, PyObject *args)
360{
361    char* searchUnits = NULL;
362    RpUnits* foundUnits = NULL;
363    RpUnitsObject* returnUnits = NULL;
364
365    if (PyTuple_Size(args) > 0) {
366        PyArg_ParseTuple(args, "s", &searchUnits);
367    }
368    else {
369        PyErr_SetString(PyExc_AttributeError, "incorrect input arguments");
370        return NULL;
371    }
372   
373    foundUnits = RpUnits::find(searchUnits);
374
375    if (foundUnits) {
376        returnUnits = newRpUnitsObject(args);
377
378        if (returnUnits == NULL)
379            return NULL;
380       
381        returnUnits->rp_unit = foundUnits;
382    }
383
384    return (PyObject*) returnUnits;
385   
386}
387
388PyDoc_STRVAR(RpUnits_makeMetric_doc,
389"makeMetric (newBasis) -> PyInt \n\
390\n\
391Create the metric extentions and conversion functions for \n\
392the unit 'newBasis', thus making it a new basis.");
393
394static PyObject*
395RpUnits_makeMetric(PyObject *self, PyObject *args)
396{
397    RpUnitsObject* units = NULL;
398    int result = 0;
399
400    if (PyTuple_Size(args) > 0) {
401        PyArg_ParseTuple(args, "O!", &RpUnitsObjectType, &units);
402    }
403    else {
404        return NULL;
405    }
406   
407    if (units->rp_unit) {
408        result = RpUnits::makeMetric(units->rp_unit);
409    }
410
411    return PyInt_FromLong((long)result);
412}
413
414PyDoc_STRVAR(RpUnits_convert_doc,
415"convert (fromVal, to, units) -> PyString \n\
416\n\
417Convert the value 'fromVal', to the units listed in 'to', \n\
418and return the value as a string. The string 'fromVal' must have a \n\
419numeric value with units attached to the end. If 'units' is set to \n\
420'off' then the returned string will not have units. The default \n\
421behavior is to show units.");
422
423static PyObject*
424RpUnits_convert(PyObject *self, PyObject *args, PyObject *keywds)
425{
426    // RpUnitsObject* units = NULL;
427    char* fromVal = NULL;
428    char* to = NULL;
429    char* units = NULL;
430    // char* tmpRetStr = NULL;
431    std::string fromVal_S = "";
432    std::string to_S = "";
433    std::string tmpUnits_S = "";
434    int unitsVal = 1;
435    int result = 0;
436    std::string retStr = "";
437    PyObject* retVal = NULL;
438    PyObject* tmpPyStr = NULL;
439
440    static char *kwlist[] = {"fromVal", "to", "units", NULL};
441   
442    if (PyTuple_Size(args) > 0) {
443        // PyArg_ParseTuple(args, "ss|s", &fromVal, &to, &units);
444        if (!PyArg_ParseTupleAndKeywords(args, keywds, "ss|s",
445                    kwlist, &fromVal, &to, &units)) {
446            return NULL;
447        }
448    }
449    else {
450        return NULL;
451    }
452
453    fromVal_S = std::string(fromVal);
454    to_S = std::string(to);
455    if (units) {
456        tmpUnits_S = std::string(units);
457        if(tmpUnits_S.compare("off") == 0) {
458            unitsVal = 0;
459        }
460        else {
461            unitsVal = 1;
462        }
463    }
464
465    retStr = RpUnits::convert(fromVal_S,to_S,unitsVal,&result);
466
467    if ( (!retStr.empty()) && (result == 0) ) {
468        if (unitsVal) {
469            retVal = PyString_FromString(retStr.c_str());   
470        }
471        else {
472            // convert to a double and return that if
473            // the units were turned off
474            tmpPyStr = PyString_FromString(retStr.c_str());
475            if (tmpPyStr) {
476                Py_INCREF(tmpPyStr);
477                retVal = PyFloat_FromString(tmpPyStr,NULL);
478                Py_DECREF(tmpPyStr);
479            }
480        }
481    }
482    else {
483        //keeping this around in case you want string returned instead of None
484        //if (fromVal) {
485        //    retVal = PyString_FromString(fromVal);   
486        //}
487        //else {
488        retVal = Py_None;
489        Py_INCREF(retVal);
490        //}
491    }
492   
493    return retVal;
494}
495
496/* ---------- */
497
498
499/* List of functions defined in the module */
500
501static PyMethodDef RpUnits_Methods[] = {
502
503    {"define", RpUnits_define, METH_VARARGS,
504        RpUnits_define_doc},
505
506    {"defineConv", RpUnits_defineConv, METH_VARARGS,
507        RpUnits_defineConv_doc},
508   
509    {"find", RpUnits_find, METH_VARARGS,
510        RpUnits_find_doc},
511
512    {"makeMetric", RpUnits_makeMetric, METH_VARARGS,
513        RpUnits_makeMetric_doc},
514   
515    {"convert", (PyCFunction)RpUnits_convert, METH_VARARGS|METH_KEYWORDS,
516        RpUnits_convert_doc},
517   
518    {NULL,        NULL}        /* sentinel */
519};
520
521PyDoc_STRVAR(module_doc, "RpUnits Module for Python.");
522
523/* Initialization function for the module */
524
525PyMODINIT_FUNC
526initUnits(void)
527{
528    PyObject *m;
529
530    /* Finalize the type object including setting type of the new type
531     * object; doing it here is required for portability to Windows
532     * without requiring C++. */
533    if (PyType_Ready(&RpUnitsObjectType) < 0)
534        return;
535
536    /* Create the module and add the functions */
537    m = Py_InitModule3("Units", RpUnits_Methods, module_doc);
538
539    /* Add some symbolic constants to the module */
540    if (ErrorObject == NULL) {
541        ErrorObject = PyErr_NewException("RpUnits.error", NULL, NULL);
542        if (ErrorObject == NULL)
543            return;
544    }
545    Py_INCREF(ErrorObject);
546    PyModule_AddObject(m, "error", ErrorObject);
547
548    // add some standard units definitions and conversions.
549
550    RpUnits::addPresets("all");
551}
Note: See TracBrowser for help on using the repository browser.