source: orange/source/orange/cls_orange.cpp @ 11603:57399ad8c343

Revision 11603:57399ad8c343, 36.6 KB checked in by Ales Erjavec <ales.erjavec@…>, 10 months ago (diff)

Fixed two memory leaks.

Line 
1/*
2    This file is part of Orange.
3   
4    Copyright 1996-2010 Faculty of Computer and Information Science, University of Ljubljana
5    Contact: janez.demsar@fri.uni-lj.si
6
7    Orange is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11
12    Orange is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with Orange.  If not, see <http://www.gnu.org/licenses/>.
19*/
20
21
22#include <string.h>
23
24#include "stladdon.hpp"
25
26#include "vars.hpp"
27#include "domain.hpp"
28
29#include "cls_value.hpp"
30#include "cls_example.hpp"
31#include "cls_orange.hpp"
32
33#include "externs.px"
34
35
36BASED_ON(Orange, ROOT)
37DATASTRUCTURE(Orange - Orange.core.OrangeBase, TPyOrange, orange_dict)
38RECOGNIZED_ATTRIBUTES(Orange, "name shortDescription description")
39
40
41PyObject *PyOrType_GenericAbstract(PyTypeObject *thistype, PyTypeObject *type, PyObject *args, PyObject *kwds)
42{ PyTRY
43    // if the user wants to create an instance of abstract class, we stop him
44    if ((thistype == type) || !thistype->tp_base || !thistype->tp_base->tp_new) {
45      PyErr_Format(PyExc_TypeError,  "cannot create instances of abstract class '%s'", type->tp_name);
46      return NULL;
47    }
48
49    // if he derived a new class, we may let him
50    return thistype->tp_base->tp_new(type, args, kwds);
51  PyCATCH
52}
53
54
55PyObject *PyOrType_GenericNew(PyTypeObject *type, PyObject *args, PyObject *)
56{ PyTRY
57    PyObject *old = NULL;
58    if (args && !PyArg_ParseTuple(args, "|O", &old)) {
59      PyErr_Format(PyExc_TypeError, "%s: invalid arguments: nothing or an existing object expected", type->tp_name);
60      return NULL;
61    }
62
63    if (old)
64      if (PyType_IsSubtype(old->ob_type, type)) {
65        Py_INCREF(old);
66        return old;
67      }
68      else {
69        PyErr_Format(PyExc_TypeError, "%s: '%s' is not a subtype of '%s'", type->tp_name, old->ob_type->tp_name, type->tp_name);
70        return NULL;
71      }
72
73    // we assert PyOrange_OrangeBaseClass will succeed and that ot_defaultconstruct is defined.
74    // the latter is pyxtract responsibility and pyxtract shouldn't be doubted  ;)
75    POrange obj = PyOrange_OrangeBaseClass(type)->ot_defaultconstruct(type);
76    if (!obj) {
77      PyErr_Format(PyExc_SystemError, "constructor for '%s' failed", type->tp_name);
78      return NULL;
79    }
80     
81    return WrapOrange(obj);
82  PyCATCH
83}
84
85
86PyObject *PyOrType_GenericNamedNew(PyTypeObject *type, PyObject *args, PyObject *)
87{
88  PyTRY
89    PyObject *name=NULL;
90    if (args && !PyArg_ParseTuple(args, "|O", &name)) {
91      PyErr_Format(PyExc_TypeError, "%s: invalid arguments: nothing, a name or an existing object expected", type->tp_name);
92      return NULL;
93    }
94
95    if (name && !PyString_Check(name))
96      if (PyType_IsSubtype(name->ob_type, type)) {
97        Py_INCREF(name);
98        return name;
99      }
100      else {
101        PyErr_Format(PyExc_TypeError, "%s: '%s' is not a subtype of '%s'", type->tp_name, name->ob_type->tp_name, type->tp_name);
102        return NULL;
103      }
104
105    // we assert PyOrange_OrangeBaseClass will succeed and that ot_defaultconstruct is defined.
106    // the latter is pyxtract responsibility and pyxtract shouldn't be doubted
107    POrange obj = PyOrange_OrangeBaseClass(type)->ot_defaultconstruct(type);
108    if (!obj) {
109      PyErr_Format(PyExc_SystemError, "constructor for '%s' failed", type->tp_name);
110      return NULL;
111    }
112     
113    PyObject *self=WrapOrange(obj);
114
115    if (!name || (PyObject_SetAttrString(self, "name", name)==0))
116      return self;
117    else {
118      Py_DECREF(self);
119      return PYNULL;
120    }
121  PyCATCH
122}
123
124
125int Orange_init(PyObject *self, PyObject *args, PyObject *keywords);
126
127
128// Rewrapping: this is not a toy - use it cautiously
129void rewrap(TPyOrange *&obj, PyTypeObject *type)
130{ if (obj && (type!=obj->ob_type)) {
131    if (obj->ob_refcnt>1) {
132      #ifdef _MSC_VER
133        throw exception("cannot rewrap (refcnt>1)");
134      #else
135        throw exception();
136      #endif
137    }
138    PyObject_GC_UnTrack((PyObject *)obj);
139
140    TPyOrange *newobj = (TPyOrange *)type->tp_alloc(type, 0);
141    newobj->orange_dict = obj->orange_dict;
142    newobj->ptr = obj->ptr;
143    newobj->call_constructed = obj->call_constructed;
144    newobj->is_reference = obj->is_reference;
145
146    obj->orange_dict = NULL;
147    obj->ptr = NULL;
148    obj->freeRef();
149
150    obj = newobj;
151  }
152}
153
154
155PyObject *PyOrType_GenericCallableNew(PyTypeObject *type, PyObject *args, PyObject *kwds)
156{ PyObject *self1 = NULL,
157           *self2 = NULL,
158           *ccReturn = NULL;
159
160  PyTRY
161  // we assert PyOrange_OrangeBaseClass will succeed and that ot_defaultconstruct is defined.
162  // the latter is pyxtract responsibility and pyxtract shouldn't be doubted
163    POrange obj=PyOrange_OrangeBaseClass(type)->ot_defaultconstruct(PyTuple_Size(args) ? (PyTypeObject *)&PyOrOrange_Type : type);
164    if (!obj) {
165      PyErr_Format(PyExc_SystemError, "constructor for '%s' failed", type->tp_name);
166      goto err;
167    }
168
169    PyObject *self1 = WrapOrange(obj);
170
171    if (!PyTuple_Size(args))
172      return self1;
173
174    else {
175/*      if (self1->ob_type != type) {
176        PyErr_Format(PyExc_SystemError, "Subclassed orange classes are not call-constructable", type->tp_name);
177        Py_DECREF(self1);
178        return NULL;
179      }
180*/
181      if (!self1->ob_type->tp_call) {
182        PyErr_Format(PyExc_SystemError, "error in orange class structure ('%s' not callable)", type->tp_name);
183        goto err;
184      }
185
186      /* We should manually call init - Python will call init on self2, and it won't do anything.
187         It's important to call it prior to setting call_constructed (below) */
188      if (Orange_init(self1, args, kwds) < 0)
189        goto err;
190
191      /* this is to tell tp_call(self1) not to complain about keyword arguments;
192         self1 is disposed later in this function, so this should cause no problems */
193      ((TPyOrange *)self1)->call_constructed = true;
194      PyObject *self2=self1->ob_type->tp_call(self1, args, kwds);
195
196      if (self2) {
197        if (type!=self1->ob_type) {
198          char *oname = new char[30 + strlen(type->tp_name)];
199          sprintf(oname, "_%s__call_construction_type", type->tp_name);
200          PyObject *ccReturn = PyObject_GetAttrString((PyObject *)type, oname);
201          delete oname;
202
203          if (!ccReturn) {
204            PyErr_Format(PyExc_SystemError, "no return type specified for call-construction of '%s'", type->tp_name);
205            goto err;
206          }
207
208          if (!PyType_Check(ccReturn)) {
209            PyErr_Format(PyExc_SystemError, "no return type specified for call-construction of '%s'", type->tp_name);
210            goto err;
211          }
212
213          if (self2->ob_refcnt>1) { // Object might have a reference to itself...
214            PyErr_Format(PyExc_SystemError, "cannot rewrap the class '%s' - too many references", self2->ob_type->tp_name);
215            goto err;
216          }
217
218          rewrap((TPyOrange *&)self2, (PyTypeObject *)ccReturn);
219        }
220
221        Py_DECREF(self1);
222
223        if (PyOrOrange_Check(self2))
224          ((TPyOrange *)self2)->call_constructed = true;
225        return self2;
226      }
227   
228    }
229  PyCATCH
230
231err:
232  Py_XDECREF(self1);
233  Py_XDECREF(self2);
234  Py_XDECREF(ccReturn);
235  return NULL;
236}
237
238
239int Orange_traverse(TPyOrange *self, visitproc visit, void *arg)
240{
241  if (self->orange_dict) {
242    int err = visit(self->orange_dict, arg);
243    if (err)
244      return err;
245  }
246
247  return ((TOrange *)self->ptr)->traverse(visit, arg);
248}
249
250
251int Orange_clear(TPyOrange *self)
252{ 
253  return ((TOrange *)self->ptr)->dropReferences();
254}
255
256
257PyObject *PyOrange__dict__(TPyOrange *self)
258{ 
259  if (!self->orange_dict)
260    self->orange_dict = PyOrange_DictProxy_New(self);
261
262  Py_INCREF(self->orange_dict);
263  return (PyObject *)(self->orange_dict);
264}
265
266
267PyObject *PyOrange__members__(TPyOrange *self)
268{
269  const TPropertyDescription *ppd = PyOrange_AS_Orange(self)->classDescription()->properties;
270  const TPropertyDescription *pd;
271  for(pd = ppd; pd->name; pd++);
272
273  PyObject *res = PyList_New(pd-ppd);
274  for(pd = ppd; pd->name; pd++)
275    PyList_SetItem(res, pd-ppd, PyString_FromString(pd->name));
276
277  return res;
278}
279
280
281char *camel2underscore(const char *camel)
282{
283    const char *ci = camel;
284    if ((*ci >= 'A') && (*ci <= 'Z')) {
285        return NULL;
286    }
287
288    char *underscored = (char *)malloc(2*strlen(camel)+1);
289    char *ui = underscored;
290    bool changed = false;
291    *ui = *ci;
292    while(*ci) { // just copied
293        if (   (*ci >= 'a') && (*ci <= 'z')       // a small letter
294            && (ci[1] >= 'A') && (ci[1] <= 'Z')) {  // followed by capital
295            *++ui = '_';
296            const char of = (ci[2] < 'A') || (ci[2] > 'Z') ? 32 : 0; // 32, if not followed by capital
297            *++ui = *++ci + of;
298            changed = true;
299        }
300        else {
301            *++ui = *++ci;
302        }
303    }
304    if (!changed) {
305        free(underscored);
306        underscored = NULL;
307    }
308    return underscored;
309}
310
311
312PyObject *PyOrange_translateObsolete(PyObject *self, PyObject *pyname)
313{ 
314  char *name = PyString_AsString(pyname);
315  char *underscored = camel2underscore(name);
316  for(TOrangeType *selftype = PyOrange_OrangeBaseClass(self->ob_type); PyOrange_CheckType((PyTypeObject *)selftype); selftype=(TOrangeType *)(selftype->ot_inherited.tp_base)) {
317      if (selftype->ot_aliases) {
318          for(TAttributeAlias *aliases=selftype->ot_aliases; aliases->alias; aliases++) {
319              if (!strcmp(name, aliases->alias) || (underscored && !strcmp(underscored, aliases->alias))) {
320                  if (underscored) {
321                      free(underscored);
322                  }
323                  return PyString_FromString(aliases->realName);
324              }
325          }
326      }
327  }
328  if (underscored) {
329      free(underscored);
330  }
331  return NULL;
332}   
333
334
335PyObject *Orange_getattr1(TPyOrange *self, const char *name)
336// This is a getattr, without translation of obsolete names and without looking into the associated dictionary
337{ PyTRY
338    if (!self)
339      PYERROR(PyExc_SystemError, "NULL Orange object", PYNULL);
340
341    TOrange *me = (TOrange *)self->ptr;
342    if (me->hasProperty(name)) {
343      try {
344        const TPropertyDescription *propertyDescription = me->propertyDescription(name);
345        const type_info &propertyType = *propertyDescription->type;
346        TPropertyTransformer *transformer = propertyDescription->transformer;
347
348        if (propertyType==typeid(bool)) {
349          bool value;
350          me->getProperty(name, value);
351          return transformer ? (PyObject *)transformer(&value) : PyBool_FromLong(value ? 1 : 0);
352        }
353
354        if (propertyType==typeid(int)) {
355          int value;
356          me->getProperty(name, value);
357          return transformer ? (PyObject *)transformer(&value) : PyInt_FromLong(value);
358        }
359
360        if (propertyType==typeid(float)) {
361          float value;
362          me->getProperty(name, value);
363          return transformer ? (PyObject *)transformer(&value) : PyFloat_FromDouble(value);
364        }
365
366        if (propertyType==typeid(string)) {
367          string value;
368          me->getProperty(name, value);
369          return transformer ? (PyObject *)transformer(&value) : PyString_FromString(value.c_str());
370        }
371
372        if (propertyType==typeid(TValue)) {
373          TValue value;
374          me->getProperty(name, value);
375          return transformer ? (PyObject *)transformer(&value) : Value_FromValue(value);
376        }
377
378        if (propertyType==typeid(TExample)) {
379          POrange mlobj;
380          me->wr_getProperty(name, mlobj);
381          if (transformer)
382            return (PyObject *)transformer(&mlobj);
383          if (mlobj)
384            return Example_FromWrappedExample(PExample(mlobj));
385          RETURN_NONE;
386        }
387     
388        POrange mlobj;
389        me->wr_getProperty(name, mlobj);
390        return transformer ? (PyObject *)transformer(&mlobj) : (PyObject *)WrapOrange(mlobj);
391      } catch (exception err)
392      {}
393    }
394 
395    PyErr_Format(PyExc_AttributeError, "'%s' has no attribute '%s'", self->ob_type->tp_name, name);
396    return PYNULL;
397  PyCATCH;
398}
399
400
401
402
403PyObject *Orange_getattr1(TPyOrange *self, PyObject *pyname)
404// This is a complete getattr, but without translation of obsolete names.
405{ PyTRY
406    if (!self)
407      PYERROR(PyExc_SystemError, "NULL Orange object", PYNULL);
408
409    if (self->orange_dict) {
410      PyObject *res = PyDict_GetItem(self->orange_dict, pyname);
411      if (res) {
412        Py_INCREF(res);
413        return res;
414      }
415    }
416     
417    PyObject *res = PyObject_GenericGetAttr((PyObject *)self, pyname);
418    if (res)
419      return res;
420
421    PyErr_Clear();
422
423    if (!PyString_Check(pyname))
424      PYERROR(PyExc_TypeError, "object's attribute name must be a string", PYNULL);
425    char *name=PyString_AsString(pyname);
426
427    if (strcmp(name, "__dict__") == 0)
428      return PyOrange__dict__(self);
429
430    if (strcmp(name, "__members__") == 0)
431      return PyOrange__members__(self);
432
433    if (strcmp(name, "__class__") == 0) {
434      Py_INCREF(self->ob_type);
435      return (PyObject *)self->ob_type;
436    }
437
438    return Orange_getattr1(self, name);
439  PyCATCH;
440}
441
442
443inline void PyDict_SIS_Steal(PyObject *dict, const char *name, PyObject *obj) {
444  PyDict_SetItemString(dict, name, obj);
445  Py_DECREF(obj);
446}
447
448PyObject *packOrangeDictionary(PyObject *self)
449{
450  PyTRY
451    PyObject *packed = ((TPyOrange *)self)->orange_dict ? PyDict_Copy(((TPyOrange *)self)->orange_dict) : PyDict_New();
452
453    TOrange *me = (TOrange *)((TPyOrange *)self)->ptr;
454
455    for (const TPropertyDescription *pd = me->classDescription()->properties; pd->name; pd++) {
456      if (!pd->readOnly) {
457 
458  //      const type_info &propertyType = pd->type;
459
460        if (pd->type == &typeid(bool))
461          PyDict_SIS_Steal(packed, pd->name, PyInt_FromLong(me->getProperty_bool(pd) ? 1 : 0));
462
463        else if (pd->type == &typeid(int))
464          PyDict_SIS_Steal(packed, pd->name, PyInt_FromLong(me->getProperty_int(pd)));
465
466        else if (pd->type == &typeid(float))
467          PyDict_SIS_Steal(packed, pd->name, PyFloat_FromDouble(me->getProperty_float(pd)));
468
469        else if (pd->type == &typeid(string)) {
470          string value;
471          me->getProperty_string(pd, value);
472          PyDict_SIS_Steal(packed, pd->name, PyString_FromString(value.c_str()));
473        }
474
475        else if (pd->type == &typeid(TValue)) {
476          TValue value;
477          me->getProperty_TValue(pd, value);
478          PyDict_SIS_Steal(packed, pd->name, Value_FromValue(value));
479        }
480
481        else if (pd->type == &typeid(TExample)) {
482          POrange mlobj;
483          me->getProperty_POrange(pd, mlobj);
484          if (mlobj)
485            PyDict_SIS_Steal(packed, pd->name, Example_FromWrappedExample(PExample(mlobj)));
486          else
487            PyDict_SetItemString(packed, pd->name, Py_None);
488        }
489   
490        else {
491          POrange mlobj;
492          me->getProperty_POrange(pd, mlobj);
493          PyDict_SIS_Steal(packed, pd->name, (PyObject *)WrapOrange(mlobj));
494        }
495      }
496    }
497
498    return packed;
499  PyCATCH
500}
501
502
503int Orange_setattr(TPyOrange *self, PyObject *pyname, PyObject *args);
504
505int unpackOrangeDictionary(PyObject *self, PyObject *dict)
506{
507  PyObject *d_key, *d_value;
508  Py_ssize_t i = 0;
509  while (PyDict_Next(dict, &i, &d_key, &d_value)) {
510//    if (Orange_setattr1((TPyOrange *)self, d_key, d_value) == -1)
511      if (Orange_setattrLow((TPyOrange *)self, d_key, d_value, false) == -1)
512        return -1;
513    }
514  return 0;
515}
516
517ORANGE_API PyObject *Orange__reduce__(PyObject *self, PyObject *, PyObject *)
518{
519    if (!((TOrangeType *)(self->ob_type))->ot_constructorAllowsEmptyArgs) {
520      PyErr_Format(PyExc_TypeError, "instances of type '%s' cannot be pickled", self->ob_type->tp_name);
521      return NULL;
522    }
523
524    return Py_BuildValue("O()N", self->ob_type, packOrangeDictionary(self));
525}
526
527
528
529PyObject *objectOnTheFly(PyObject *args, PyTypeObject *objectType)
530{
531  PyObject *emptyDict = PyDict_New();
532  PyObject *targs;
533  if (PyTuple_Check(args)) {
534    targs = args;
535    Py_INCREF(targs);
536  }
537  else
538    targs = Py_BuildValue("(O)", args);
539
540  PyObject *obj = NULL;
541  try {
542    obj = objectType->tp_new(objectType, targs, emptyDict);
543  }
544  catch (...) {
545    // do nothing; if it failed, the user probably didn't mean it
546  }
547
548  // If this failed, maybe the constructor actually expected a tuple...
549  if (!obj && PyTuple_Check(args)) {
550     PyErr_Clear();
551     Py_DECREF(targs);
552     targs = Py_BuildValue("(O)", args);
553     try {
554       obj = objectType->tp_new(objectType, targs, emptyDict);
555     }
556     catch (...) 
557     {}
558  }
559
560  if (obj) {
561    if (   objectType->tp_init != NULL
562        && objectType->tp_init(obj, targs, emptyDict) < 0) {
563          Py_DECREF(obj);
564          obj = NULL;
565    }
566  }
567
568  Py_DECREF(emptyDict);
569  Py_DECREF(targs);
570
571  return obj;
572}
573
574
575int Orange_setattr1(TPyOrange *self, char *name, PyObject *args)
576{
577  TOrange *me = (TOrange *)self->ptr;
578
579  const TPropertyDescription *propertyDescription = me->propertyDescription(name, true);
580  if (!propertyDescription)
581    return 1;
582
583  PyTRY
584    if (propertyDescription->readOnly) {
585      /* Property might be marked as readOnly, but have a specialized set function.
586         The following code is pasted from PyObject_GenericSetAttr.
587         If I'd call it here and the attribute is really read-only, PyObject_GenericSetAttr
588         would blatantly store it in the dictionary. */
589      PyObject *pyname = PyString_FromString(name);
590      PyObject *descr = _PyType_Lookup(self->ob_type, pyname);
591        PyObject *f = PYNULL;
592        if (descr != NULL && PyType_HasFeature(descr->ob_type, Py_TPFLAGS_HAVE_CLASS)) {
593            descrsetfunc f = descr->ob_type->tp_descr_set;
594            if (f != NULL && PyDescr_IsData(descr))
595                return f(descr, (PyObject *)self, args);
596      }
597
598      PyErr_Format(PyExc_TypeError, "%s.%s: read-only attribute", self->ob_type->tp_name, name);
599      return -1;
600    }
601 
602    try {
603      const type_info &propertyType = *propertyDescription->type;
604
605      if ((propertyType==typeid(bool)) || (propertyType==typeid(int))) {
606        int value;
607        if (!PyArg_Parse(args, "i", &value)) {
608          PyErr_Format(PyExc_TypeError, "invalid parameter type for %s.%s', (int expected)", self->ob_type->tp_name, name);
609          return -1;
610        }
611        if (propertyType==typeid(bool))
612          me->setProperty(name, value!=0);
613        else
614          me->setProperty(name, value);
615        return 0;
616      }
617
618      if (propertyType==typeid(float)) {
619        float value;
620        if (!PyArg_Parse(args, "f", &value)) {
621          PyErr_Format(PyExc_TypeError, "invalid parameter type for %s.%s', (float expected)", self->ob_type->tp_name, name);
622          return -1;
623        }
624        me->setProperty(name, value);
625        return 0;
626      }
627
628      if (propertyType==typeid(string)) {
629        char *value;
630        if (!PyArg_Parse(args, "s", &value)) {
631          PyErr_Format(PyExc_TypeError, "invalid parameter type for %s.%s', (string expected)", self->ob_type->tp_name, name);
632          return -1;
633        }
634        me->setProperty(name, string(value));
635        return 0;
636      }
637
638      if (propertyType==typeid(TValue)) {
639        TValue value;
640        if (!convertFromPython(args, value))
641          return -1;
642        me->setProperty(name, value);
643        return 0;
644      }
645
646      if (propertyType==typeid(TExample)) {
647        if (args==Py_None) {
648          me->wr_setProperty(name, POrange());
649          return 0;
650        }
651        else {
652          if (!PyOrExample_Check(args)) {
653            PyErr_Format(PyExc_TypeError, "invalid parameter type for '%s.%s', (expected 'Example', got '%s')", self->ob_type->tp_name, name, args->ob_type->tp_name);
654            return -1;
655          }
656          me->wr_setProperty(name, POrange(PyExample_AS_Example(args)));
657          return 0;
658        }
659      }
660
661      if (1/*propertyType==typeid(POrange)*/) {
662        const type_info *wrappedType = propertyDescription->classDescription->type;
663
664        PyTypeObject *propertyPyType=(PyTypeObject *)FindOrangeType(*wrappedType);
665        if (!propertyPyType) {
666          PyErr_Format(PyExc_SystemError, "Orange class %s, needed for '%s.%s' not exported to Python", TYPENAME(*wrappedType), self->ob_type->tp_name, name);
667          return -1;
668        }
669
670        if (args==Py_None) {
671          me->wr_setProperty(name, POrange());
672          return 0;
673        }
674
675        // User might have supplied the correct object
676        if (PyObject_TypeCheck(args, propertyPyType)) {
677          me->wr_setProperty(name, PyOrange_AS_Orange((TPyOrange *)args));
678          return 0;
679        }
680
681        // User might have supplied parameters from which we can construct the object
682        if (propertyPyType->tp_new) {
683          PyObject *obj = objectOnTheFly(args, propertyPyType);
684          if (obj) {
685            bool success = true;
686            try {
687              me->wr_setProperty(name, PyOrange_AS_Orange((TPyOrange *)obj));
688            }
689            catch (...) {
690              success = false;
691            }
692            Py_DECREF(obj);
693            if (success)
694              return 0;
695          }
696        }
697
698        PyErr_Format(PyExc_TypeError, "invalid parameter type for '%s.%s', (expected '%s', got '%s')", self->ob_type->tp_name, name, propertyPyType->tp_name, args->ob_type->tp_name);
699        return -1;
700      }
701
702      PyErr_Format(PyExc_TypeError, "internal Orange error: unrecognized type '%s.%s'", self->ob_type->tp_name, name);
703      return -1;
704    } catch (exception err)
705    {
706      PyErr_Format(PyExc_TypeError, "error setting '%s.%s'", self->ob_type->tp_name, name);
707      return -1;
708    }
709  PyCATCH_1
710}
711
712
713int Orange_setattr1(TPyOrange *self, PyObject *pyname, PyObject *args)
714// This is a complete setattr, but without translation of obsolete names.
715{ 
716  if (!self)
717    PYERROR(PyExc_SystemError, "NULL Orange object", -1);
718
719  /* We first have to check for a specific handler.
720     The following code is pasted from PyObject_GenericSetAttr, but we can't
721     call it since it would store *all* attributes in the dictionary. */
722  PyObject *descr = _PyType_Lookup(self->ob_type, pyname);
723  PyObject *f = PYNULL;
724  if (descr != NULL && PyType_HasFeature(descr->ob_type, Py_TPFLAGS_HAVE_CLASS)) {
725    descrsetfunc f = descr->ob_type->tp_descr_set;
726    if (f != NULL && PyDescr_IsData(descr))
727      return f(descr, (PyObject *)self, args);
728  }
729 
730  char *name=PyString_AsString(pyname);
731  int res = Orange_setattr1(self, name, args);
732  if (res != 1)
733    return res;
734
735  return 1; // attribute not set (not even attempted to), try something else
736}
737
738
739PyObject *Orange_new(PyTypeObject *type, PyObject *args, PyObject *keywords)  BASED_ON(ROOT, "()")
740{ return WrapNewOrange(mlnew TOrange(), type); }
741
742int Orange_init(PyObject *self, PyObject *, PyObject *keywords)
743{ PyTRY
744    return ((TPyOrange *)self)->call_constructed || SetAttr_FromDict(self, keywords, true) ? 0 : -1;
745  PyCATCH_1
746}
747
748
749void Orange_dealloc(TPyOrange *self)
750{
751  if (!self->is_reference) {
752    PyObject_GC_UnTrack((PyObject *)self);
753    mldelete self->ptr;
754  }
755
756  // This may cause troubles in multithread use
757  if (self->orange_dict) {
758    ((TPyOrange_DictProxy *)self->orange_dict)->backlink = NULL;
759    Py_DECREF(self->orange_dict);
760  }
761
762  self->ob_type->tp_free((PyObject *)self);
763}
764
765
766
767PyObject *Orange_getattr(TPyOrange *self, PyObject *name)
768// This calls getattr1; first with the given, than with the translated name
769{ 
770  PyTRY
771    PyObject *res = Orange_getattr1(self, name);
772    char *underscored = NULL;
773    if (!res) {
774        char *camel = PyString_AsString(name);
775        underscored = camel2underscore(camel);
776        if (underscored) {
777            PyObject *translation = PyString_FromString(underscored);
778            PyErr_Clear();
779            res = Orange_getattr1(self, translation);
780            Py_DECREF(translation);
781        }
782    }
783    if (!res) {
784        PyObject *translation = PyOrange_translateObsolete((PyObject *)self, name);
785        if (translation) {
786            PyErr_Clear();
787            res = Orange_getattr1(self, translation);
788            Py_DECREF(translation);
789        }
790    }
791
792    if (!res && underscored) {
793        PyMethodDef *mi = self->ob_type->tp_methods;
794        if (mi) {
795            for(; mi->ml_name; mi++) {
796                if (!strcmp(underscored, mi->ml_name)) {
797                    res = PyMethod_New((PyObject *)mi->ml_meth, (PyObject *)self, (PyObject *)(self->ob_type));
798                    break;
799                }
800            }
801        }
802    }
803
804    if (underscored) {
805        free(underscored);
806    }
807    return res;
808  PyCATCH
809}
810
811
812int Orange_setattrDictionary(TPyOrange *self, const char *name, PyObject *args, bool warn)
813{
814  PyObject *pyname = PyString_FromString(name);
815  int res = Orange_setattrDictionary(self, pyname, args, warn);
816  Py_DECREF(pyname);
817  return res;
818}
819
820int Orange_setattrDictionary(TPyOrange *self, PyObject* pyname, PyObject *args, bool warn)
821{ PyTRY
822    char *name = PyString_AsString(pyname);
823    if (args) {
824      /* Issue a warning unless name the name is in 'recognized_list' in some of the ancestors
825         or the instance's class only derived from some Orange's class, but is written in Python */
826      if (warn && PyOrange_CheckType(self->ob_type)) {
827        char **recognized = NULL;
828        for(PyTypeObject *otype = self->ob_type; otype && (!recognized || !*recognized); otype = otype->tp_base) {
829          recognized = PyOrange_CheckType(otype) ? ((TOrangeType *)otype)->ot_recognizedattributes : NULL;
830          if (recognized)
831            for(; *recognized && strcmp(*recognized, name); recognized++);
832        }
833
834        if (!recognized || !*recognized) {
835          char sbuf[255];
836          sprintf(sbuf, "'%s' is not a builtin attribute of '%s'", name, self->ob_type->tp_name);
837          if (PyErr_Warn(PyExc_OrangeAttributeWarning, sbuf))
838            return -1;
839        }
840      }
841
842      if (!self->orange_dict)
843        self->orange_dict = PyOrange_DictProxy_New(self);
844
845      return PyDict_SetItem(self->orange_dict, pyname, args);
846    }
847    else {
848      if (self->orange_dict)
849        return PyDict_DelItem(self->orange_dict, pyname);
850      else {
851        PyErr_Format(PyExc_AttributeError, "instance of '%s' has no attribute '%s'", self->ob_type->tp_name, name);
852        return -1;
853      }
854    }
855  PyCATCH_1
856}
857
858int Orange_setattrLow(TPyOrange *self, PyObject *pyname, PyObject *args, bool warn)
859// This calls setattr1; first with the given, than with the translated name
860{ PyTRY
861    if (!PyString_Check(pyname))
862      PYERROR(PyExc_AttributeError, "object's attribute name must be string", -1);
863
864    // Try to set it as C++ class member
865    int res = Orange_setattr1(self, pyname, args);
866    if (res!=1)
867      return res;
868   
869    PyErr_Clear();
870    char *camel = PyString_AsString(pyname);
871    char *underscored = camel2underscore(camel);
872    if (underscored) {
873        PyObject *translation = PyString_FromString(underscored);
874        free(underscored);
875        res = Orange_setattr1(self, translation, args);
876        Py_DECREF(translation);
877    }
878    if (res!=1)
879      return res;
880
881    PyErr_Clear();
882    // Try to translate it as an obsolete alias for C++ class member
883    PyObject *translation = PyOrange_translateObsolete((PyObject *)self, pyname);
884    if (translation) {   
885      char sbuf[255];
886      char *name = PyString_AsString(pyname);
887      char *transname = PyString_AsString(translation);
888      sprintf(sbuf, "'%s' is an (obsolete) alias for '%s'", name, transname);
889      if (PyErr_Warn(PyExc_DeprecationWarning, sbuf))
890        return -1;
891       
892      res = Orange_setattr1(self, translation, args);
893      Py_DECREF(translation);
894      return res;
895    }
896   
897    // Use instance's dictionary
898    return Orange_setattrDictionary(self, pyname, args, warn);
899   
900  PyCATCH_1
901}
902
903
904int Orange_setattr(TPyOrange *self, PyObject *pyname, PyObject *args)
905{ return Orange_setattrLow(self, pyname, args, true); }
906
907
908PyObject *callbackOutput(PyObject *self, PyObject *args, PyObject *kwds,
909                         char *formatname1, char *formatname2, PyTypeObject *toBase)
910{ 
911  PyObject *output;
912
913  char os1[256] = "__output_";
914  strcat(os1, formatname1);
915
916  char os2[256] = "__output_";
917  if (formatname2)
918    strcat(os2, formatname2);
919
920  for(PyTypeObject *type = self->ob_type;;type = type->tp_base) {
921    PyObject *type_py = (PyObject *)type;
922
923    if (PyObject_HasAttrString(type_py, os1)) {
924      output = PyObject_GetAttrString(type_py, os1);
925      break;
926    }
927
928    char os2[256] = "__output_";
929    if (formatname2 && PyObject_HasAttrString(type_py, os2)) {
930      output = PyObject_GetAttrString(type_py, os2);
931      break;
932    }
933
934    if (type==toBase)
935      return PYNULL;
936  }
937
938  PyObject *function = PyMethod_Function(output);
939  PyObject *result;
940  if (!args)
941    result = PyObject_CallFunction(function, "O", self);
942  else {
943    PyObject *margs = PyTuple_New(1+PyTuple_Size(args));
944   
945    Py_INCREF(self);
946    PyTuple_SetItem(margs, 0, self);
947    for(Py_ssize_t i = 0, e = PyTuple_Size(args); i<e; i++) {
948      PyObject *t = PyTuple_GetItem(args, i);
949      Py_INCREF(t);
950      PyTuple_SetItem(margs, i+1, t);
951    }
952
953    result = PyObject_Call(function, margs, kwds);
954    Py_DECREF(margs);
955  }
956
957  Py_DECREF(output);
958  return result;
959}
960 
961
962char const *getName(TPyOrange *self)
963{ static char *namebuf = NULL;
964
965  if (namebuf) {
966    delete namebuf;
967    namebuf = NULL;
968  }
969   
970  PyObject *pystr = PyString_FromString("name");
971  PyObject *pyname = Orange_getattr(self, pystr);
972  if (!pyname) {
973    PyErr_Clear();
974    return NULL;
975  }
976
977  Py_DECREF(pystr);
978
979  if (!PyString_Check(pyname)) {
980    pystr = PyObject_Repr(pyname);
981    Py_DECREF(pyname);
982    pyname = pystr;
983  }
984
985  const Py_ssize_t sze = PyString_Size(pyname);
986  if (sze) {
987    namebuf = mlnew char[sze+1];
988    strcpy(namebuf, PyString_AsString(pyname));
989  }
990  Py_DECREF(pyname);
991
992  return namebuf;
993}
994
995
996PyObject *Orange_repr(TPyOrange *self)
997{ PyTRY
998    PyObject *result = callbackOutput((PyObject *)self, NULL, NULL, "repr", "str");
999    if (result)
1000      return result;
1001
1002    const char *tp_name = self->ob_type->tp_name + (strncmp(self->ob_type->tp_name, "orange.", 7) ? 0 : 7);
1003    const char *name = getName(self);
1004    return name ? PyString_FromFormat("%s '%s'", tp_name, name)
1005                : PyString_FromFormat("<%s instance at %p>", tp_name, self->ptr);
1006  PyCATCH
1007}
1008
1009
1010PyObject *Orange_str(TPyOrange *self)
1011{ PyTRY
1012    PyObject *result = callbackOutput((PyObject *)self, NULL, NULL, "str", "repr");
1013    if (result)
1014      return result;
1015
1016    const char *tp_name = self->ob_type->tp_name + (strncmp(self->ob_type->tp_name, "orange.", 7) ? 0 : 7);
1017    const char *name = getName(self);
1018    return name ? PyString_FromFormat("%s '%s'", tp_name, name)
1019                : PyString_FromFormat("<%s instance at %p>", tp_name, self->ptr);
1020  PyCATCH
1021}
1022
1023
1024char const *genericNames[] = {"Classifier", "Learner", "Discretizer", NULL};
1025
1026PyObject *Orange_get_name(TPyOrange *self)
1027{
1028  PyTRY
1029    PyObject *pyname = Orange_getattr1(self, "name");
1030    if (!pyname) { 
1031      PyErr_Clear();
1032      if (self->orange_dict) {
1033        pyname = PyDict_GetItemString(self->orange_dict, "name");
1034        Py_XINCREF(pyname);
1035      }
1036    }
1037    if (pyname) {
1038      if (PyString_Check(pyname)) {
1039        return pyname;
1040      }
1041      PyObject *pystr = PyObject_Repr(pyname);
1042      Py_DECREF(pyname);
1043      return pystr;
1044    }
1045
1046    char const *tp_name = self->ob_type->tp_name;
1047    char const *dotp = tp_name + strlen(tp_name);
1048    while((dotp != tp_name) && (*dotp != '.')) {
1049      dotp--;
1050    }
1051    if (*dotp == '.') {
1052      dotp++;
1053    }
1054    if (*dotp == '_') {
1055      dotp++;
1056    }
1057    char *name = (char *)malloc(strlen(dotp) + 1);
1058    strcpy(name, dotp);
1059
1060    const int lenname = strlen(name);
1061    char *nameend = name + lenname;
1062    for(char const *const *gen = genericNames; *gen; gen++) {
1063        if (strlen(*gen) < lenname) {
1064            char *cap = nameend - strlen(*gen);
1065            if (!strcmp(cap, *gen)) {
1066              *cap = 0;
1067              break;
1068            }
1069        }
1070    }
1071    if ((*name >= 'A') && (*name <= 'Z')) {
1072      *name ^= 32;
1073      }
1074
1075    PyObject * orangename = PyString_FromString(name);
1076    free(name);
1077
1078    return orangename;
1079
1080  PyCATCH
1081}
1082
1083
1084
1085int Orange_set_name(TPyOrange *self, PyObject *arg)
1086{
1087  PyTRY
1088    int res = Orange_setattr1(self, "name", arg);
1089    if (res == 1) {
1090        res = Orange_setattrDictionary(self, "name", arg, false);
1091    }
1092    return res;
1093  PyCATCH_1
1094}
1095
1096
1097int Orange_nonzero(PyObject *self)
1098{ PyTRY
1099    if (self->ob_type->tp_as_sequence && self->ob_type->tp_as_sequence->sq_length)
1100      return self->ob_type->tp_as_sequence->sq_length(self) ? 1 : 0;
1101     
1102    if (self->ob_type->tp_as_mapping && self->ob_type->tp_as_mapping->mp_length)
1103      return self->ob_type->tp_as_mapping->mp_length(self) ? 1 : 0;
1104     
1105    return PyOrange_AS_Orange(self) ? 1 : 0;
1106  PyCATCH_1
1107}
1108
1109 
1110int Orange_hash(TPyOrange *self)
1111{ return _Py_HashPointer(self); }
1112
1113
1114PyObject *Orange_setattr_force(TPyOrange *self, PyObject *args) PYARGS(METH_VARARGS, "(name, value) -> None") //>setattr
1115{ 
1116  PyObject *pyname, *pyvalue;
1117  if (!PyArg_ParseTuple(args, "OO:Orange.setattr", &pyname, &pyvalue))
1118    return PYNULL;
1119  if (!PyString_Check(pyname))
1120    PYERROR(PyExc_TypeError, "attribute name must be a string", PYNULL);
1121  if (Orange_setattrLow(self, pyname, pyvalue, false) == -1)
1122    return PYNULL;
1123  RETURN_NONE;
1124}
1125
1126
1127PyObject *Orange_clone(TPyOrange *self) PYARGS(METH_NOARGS, "() -> a sensibly deep copy of the object")
1128{
1129  return WrapOrange(POrange(CLONE(TOrange, ((TOrange *)self->ptr))));
1130}
1131
1132PyObject *Orange_reference(TPyOrange *self) PYARGS(METH_NOARGS, "() -> reference; Returns unique id for an object")
1133{ PyTRY
1134    return PyInt_FromLong(long(self->ptr));
1135  PyCATCH
1136}
1137
1138
1139PyObject *Orange_typeid(TPyOrange *self) PYARGS(METH_NOARGS, "() -> int; Returns unique id for object's type")
1140{ PyTRY
1141    return PyInt_FromLong(long(&typeid(*self->ptr))); 
1142  PyCATCH
1143}
1144
1145
1146PyObject *Orange_dump(PyObject *self, PyObject *args, PyObject *kwd) PYARGS(METH_VARARGS | METH_KEYWORDS, "(formatname, ...) -> string; Prints the object into string")
1147{ PyTRY
1148    if (!args || !PyTuple_Size(args)) {
1149      PyErr_Format(PyExc_AttributeError, "missing arguments for '%s'.output", self->ob_type->tp_name);
1150      return PYNULL;
1151    }
1152
1153    PyObject *stype = PyTuple_GetItem(args, 0);
1154    if (!PyString_Check(stype)) {
1155      PyErr_Format(PyExc_AttributeError, "invalid format argument for '%s'.output", self->ob_type->tp_name);
1156      return PYNULL;
1157    }
1158    char *formatname = PyString_AsString(stype);
1159   
1160    PyObject *margs = PyTuple_New(PyTuple_Size(args)-1);
1161    for (Py_ssize_t i = 1, e = PyTuple_Size(args); i<e; i++) {
1162      PyObject *t = PyTuple_GetItem(args, i);
1163      Py_INCREF(t);
1164      PyTuple_SetItem(margs, i-1, t);
1165    }
1166
1167    PyObject *result = callbackOutput(self, margs, kwd, formatname);
1168    if (!result && !PyErr_Occurred())
1169      PyErr_Format(PyExc_AttributeError, "Class '%s' cannot be dumped as '%s'", self->ob_type->tp_name, formatname);
1170   
1171    Py_DECREF(margs);
1172    return result;
1173  PyCATCH
1174}
1175
1176
1177PyObject *Orange_write(PyObject *self, PyObject *args, PyObject *kwd) PYARGS(METH_VARARGS | METH_KEYWORDS, "(formatname, file, ...) -> string; Writes the object to a file")
1178{ PyTRY
1179    if (!args || PyTuple_Size(args)<2) {
1180      PyErr_Format(PyExc_AttributeError, "missing arguments for '%s'.output", self->ob_type->tp_name);
1181      return PYNULL;
1182    }
1183
1184    PyObject *stype = PyTuple_GetItem(args, 0);
1185    if (!PyString_Check(stype)) {
1186      PyErr_Format(PyExc_AttributeError, "invalid format argument for '%s'.output", self->ob_type->tp_name);
1187      return PYNULL;
1188    }
1189    char *formatname = PyString_AsString(stype);
1190   
1191    PyObject *margs = PyTuple_New(PyTuple_Size(args)-2);
1192    for (Py_ssize_t i = 2, e = PyTuple_Size(args); i<e; i++) {
1193      PyObject *t = PyTuple_GetItem(args, i);
1194      Py_INCREF(t);
1195      PyTuple_SetItem(margs, i-2, t);
1196    }
1197
1198    PyObject *result = callbackOutput(self, margs, kwd, formatname);
1199    Py_DECREF(margs);
1200
1201    if (!result)
1202      return PYNULL;
1203
1204    PyObject *pfile = PyTuple_GetItem(args, 1);
1205    if (pfile)
1206      if (PyFile_Check(pfile))
1207        Py_INCREF(pfile);
1208      else
1209        if (PyString_Check(pfile))
1210          pfile = PyFile_FromString(PyString_AsString(pfile), "wb");
1211        else
1212          pfile = NULL;
1213   
1214    if (!pfile) {
1215      PyErr_Format(PyExc_AttributeError, "invalid format argument for '%s'.output", self->ob_type->tp_name);
1216      Py_DECREF(result);
1217      return PYNULL;
1218    }
1219
1220    int succ = PyFile_WriteObject(result, pfile, Py_PRINT_RAW);
1221    Py_DECREF(result);
1222    Py_DECREF(pfile);
1223
1224    if (succ<0) {
1225      if (!PyErr_Occurred())
1226        PyErr_Format(PyExc_AttributeError, "Class '%s' cannot be written as '%s'", self->ob_type->tp_name, formatname);
1227      return PYNULL;
1228    }
1229    else
1230      RETURN_NONE;
1231
1232  PyCATCH
1233}
1234
1235
1236#include <typeinfo>
1237#include <string>
1238
1239
1240bool convertFromPythonWithML(PyObject *obj, string &str, const TOrangeType &base)
1241{ if (PyString_Check(obj))
1242    str=PyString_AsString(obj);
1243  else if (PyObject_TypeCheck(obj, (PyTypeObject *)const_cast<TOrangeType *>(&base)))
1244    str = string(getName((TPyOrange *)obj));
1245  else
1246    PYERROR(PyExc_TypeError, "invalid argument type", false);
1247
1248  return true;
1249}
1250
1251
1252#include "cls_orange.px"
Note: See TracBrowser for help on using the repository browser.