source: orange/source/orange/cls_orange.cpp @ 11065:d374411d8f22

Revision 11065:d374411d8f22, 36.6 KB checked in by Janez Demšar <janez.demsar@…>, 16 months ago (diff)

Added property 'name' that resolves to an attribute name, if it exists,
otherwise it returns the class name with the first letter in lowercase and
without the suffix 'Learner', 'Classifier' or 'Discretization'.

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    return PyString_FromString(name);
1075  PyCATCH
1076}
1077
1078
1079
1080int Orange_set_name(TPyOrange *self, PyObject *arg)
1081{
1082  PyTRY
1083    int res = Orange_setattr1(self, "name", arg);
1084    if (res == 1) {
1085        res = Orange_setattrDictionary(self, "name", arg, false);
1086    }
1087    return res;
1088  PyCATCH_1
1089}
1090
1091
1092int Orange_nonzero(PyObject *self)
1093{ PyTRY
1094    if (self->ob_type->tp_as_sequence && self->ob_type->tp_as_sequence->sq_length)
1095      return self->ob_type->tp_as_sequence->sq_length(self) ? 1 : 0;
1096     
1097    if (self->ob_type->tp_as_mapping && self->ob_type->tp_as_mapping->mp_length)
1098      return self->ob_type->tp_as_mapping->mp_length(self) ? 1 : 0;
1099     
1100    return PyOrange_AS_Orange(self) ? 1 : 0;
1101  PyCATCH_1
1102}
1103
1104 
1105int Orange_hash(TPyOrange *self)
1106{ return _Py_HashPointer(self); }
1107
1108
1109PyObject *Orange_setattr_force(TPyOrange *self, PyObject *args) PYARGS(METH_VARARGS, "(name, value) -> None") //>setattr
1110{ 
1111  PyObject *pyname, *pyvalue;
1112  if (!PyArg_ParseTuple(args, "OO:Orange.setattr", &pyname, &pyvalue))
1113    return PYNULL;
1114  if (!PyString_Check(pyname))
1115    PYERROR(PyExc_TypeError, "attribute name must be a string", PYNULL);
1116  if (Orange_setattrLow(self, pyname, pyvalue, false) == -1)
1117    return PYNULL;
1118  RETURN_NONE;
1119}
1120
1121
1122PyObject *Orange_clone(TPyOrange *self) PYARGS(METH_NOARGS, "() -> a sensibly deep copy of the object")
1123{
1124  return WrapOrange(POrange(CLONE(TOrange, ((TOrange *)self->ptr))));
1125}
1126
1127PyObject *Orange_reference(TPyOrange *self) PYARGS(METH_NOARGS, "() -> reference; Returns unique id for an object")
1128{ PyTRY
1129    return PyInt_FromLong(long(self->ptr));
1130  PyCATCH
1131}
1132
1133
1134PyObject *Orange_typeid(TPyOrange *self) PYARGS(METH_NOARGS, "() -> int; Returns unique id for object's type")
1135{ PyTRY
1136    return PyInt_FromLong(long(&typeid(*self->ptr))); 
1137  PyCATCH
1138}
1139
1140
1141PyObject *Orange_dump(PyObject *self, PyObject *args, PyObject *kwd) PYARGS(METH_VARARGS | METH_KEYWORDS, "(formatname, ...) -> string; Prints the object into string")
1142{ PyTRY
1143    if (!args || !PyTuple_Size(args)) {
1144      PyErr_Format(PyExc_AttributeError, "missing arguments for '%s'.output", self->ob_type->tp_name);
1145      return PYNULL;
1146    }
1147
1148    PyObject *stype = PyTuple_GetItem(args, 0);
1149    if (!PyString_Check(stype)) {
1150      PyErr_Format(PyExc_AttributeError, "invalid format argument for '%s'.output", self->ob_type->tp_name);
1151      return PYNULL;
1152    }
1153    char *formatname = PyString_AsString(stype);
1154   
1155    PyObject *margs = PyTuple_New(PyTuple_Size(args)-1);
1156    for (Py_ssize_t i = 1, e = PyTuple_Size(args); i<e; i++) {
1157      PyObject *t = PyTuple_GetItem(args, i);
1158      Py_INCREF(t);
1159      PyTuple_SetItem(margs, i-1, t);
1160    }
1161
1162    PyObject *result = callbackOutput(self, margs, kwd, formatname);
1163    if (!result && !PyErr_Occurred())
1164      PyErr_Format(PyExc_AttributeError, "Class '%s' cannot be dumped as '%s'", self->ob_type->tp_name, formatname);
1165   
1166    Py_DECREF(margs);
1167    return result;
1168  PyCATCH
1169}
1170
1171
1172PyObject *Orange_write(PyObject *self, PyObject *args, PyObject *kwd) PYARGS(METH_VARARGS | METH_KEYWORDS, "(formatname, file, ...) -> string; Writes the object to a file")
1173{ PyTRY
1174    if (!args || PyTuple_Size(args)<2) {
1175      PyErr_Format(PyExc_AttributeError, "missing arguments for '%s'.output", self->ob_type->tp_name);
1176      return PYNULL;
1177    }
1178
1179    PyObject *stype = PyTuple_GetItem(args, 0);
1180    if (!PyString_Check(stype)) {
1181      PyErr_Format(PyExc_AttributeError, "invalid format argument for '%s'.output", self->ob_type->tp_name);
1182      return PYNULL;
1183    }
1184    char *formatname = PyString_AsString(stype);
1185   
1186    PyObject *margs = PyTuple_New(PyTuple_Size(args)-2);
1187    for (Py_ssize_t i = 2, e = PyTuple_Size(args); i<e; i++) {
1188      PyObject *t = PyTuple_GetItem(args, i);
1189      Py_INCREF(t);
1190      PyTuple_SetItem(margs, i-2, t);
1191    }
1192
1193    PyObject *result = callbackOutput(self, margs, kwd, formatname);
1194    Py_DECREF(margs);
1195
1196    if (!result)
1197      return PYNULL;
1198
1199    PyObject *pfile = PyTuple_GetItem(args, 1);
1200    if (pfile)
1201      if (PyFile_Check(pfile))
1202        Py_INCREF(pfile);
1203      else
1204        if (PyString_Check(pfile))
1205          pfile = PyFile_FromString(PyString_AsString(pfile), "wb");
1206        else
1207          pfile = NULL;
1208   
1209    if (!pfile) {
1210      PyErr_Format(PyExc_AttributeError, "invalid format argument for '%s'.output", self->ob_type->tp_name);
1211      Py_DECREF(result);
1212      return PYNULL;
1213    }
1214
1215    int succ = PyFile_WriteObject(result, pfile, Py_PRINT_RAW);
1216    Py_DECREF(result);
1217    Py_DECREF(pfile);
1218
1219    if (succ<0) {
1220      if (!PyErr_Occurred())
1221        PyErr_Format(PyExc_AttributeError, "Class '%s' cannot be written as '%s'", self->ob_type->tp_name, formatname);
1222      return PYNULL;
1223    }
1224    else
1225      RETURN_NONE;
1226
1227  PyCATCH
1228}
1229
1230
1231#include <typeinfo>
1232#include <string>
1233
1234
1235bool convertFromPythonWithML(PyObject *obj, string &str, const TOrangeType &base)
1236{ if (PyString_Check(obj))
1237    str=PyString_AsString(obj);
1238  else if (PyObject_TypeCheck(obj, (PyTypeObject *)const_cast<TOrangeType *>(&base)))
1239    str = string(getName((TPyOrange *)obj));
1240  else
1241    PYERROR(PyExc_TypeError, "invalid argument type", false);
1242
1243  return true;
1244}
1245
1246
1247#include "cls_orange.px"
Note: See TracBrowser for help on using the repository browser.