source: orange/source/orange/cls_orange.cpp @ 10673:587b5c331cb6

Revision 10673:587b5c331cb6, 35.2 KB checked in by Janez Demšar <janez.demsar@…>, 2 years ago (diff)

Changed the warning type for obsolete attributes to DeprecationWarning (fixes #1157)

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    if (!strcmp(name, "name") || !strcmp(name, "shortDescription") || !strcmp(name, "description"))
396      return PyString_FromString("");
397
398    PyErr_Format(PyExc_AttributeError, "'%s' has no attribute '%s'", self->ob_type->tp_name, name);
399    return PYNULL;
400  PyCATCH;
401}
402
403
404
405
406PyObject *Orange_getattr1(TPyOrange *self, PyObject *pyname)
407// This is a complete getattr, but without translation of obsolete names.
408{ PyTRY
409    if (!self)
410      PYERROR(PyExc_SystemError, "NULL Orange object", PYNULL);
411
412    if (self->orange_dict) {
413      PyObject *res = PyDict_GetItem(self->orange_dict, pyname);
414      if (res) {
415        Py_INCREF(res);
416        return res;
417      }
418    }
419     
420    PyObject *res = PyObject_GenericGetAttr((PyObject *)self, pyname);
421    if (res)
422      return res;
423
424    PyErr_Clear();
425
426    if (!PyString_Check(pyname))
427      PYERROR(PyExc_TypeError, "object's attribute name must be a string", PYNULL);
428    char *name=PyString_AsString(pyname);
429
430    if (strcmp(name, "__dict__") == 0)
431      return PyOrange__dict__(self);
432
433    if (strcmp(name, "__members__") == 0)
434      return PyOrange__members__(self);
435
436    if (strcmp(name, "__class__") == 0) {
437      Py_INCREF(self->ob_type);
438      return (PyObject *)self->ob_type;
439    }
440
441    return Orange_getattr1(self, name);
442  PyCATCH;
443}
444
445
446inline void PyDict_SIS_Steal(PyObject *dict, const char *name, PyObject *obj) {
447  PyDict_SetItemString(dict, name, obj);
448  Py_DECREF(obj);
449}
450
451PyObject *packOrangeDictionary(PyObject *self)
452{
453  PyTRY
454    PyObject *packed = ((TPyOrange *)self)->orange_dict ? PyDict_Copy(((TPyOrange *)self)->orange_dict) : PyDict_New();
455
456    TOrange *me = (TOrange *)((TPyOrange *)self)->ptr;
457
458    for (const TPropertyDescription *pd = me->classDescription()->properties; pd->name; pd++) {
459      if (!pd->readOnly) {
460 
461  //      const type_info &propertyType = pd->type;
462
463        if (pd->type == &typeid(bool))
464          PyDict_SIS_Steal(packed, pd->name, PyInt_FromLong(me->getProperty_bool(pd) ? 1 : 0));
465
466        else if (pd->type == &typeid(int))
467          PyDict_SIS_Steal(packed, pd->name, PyInt_FromLong(me->getProperty_int(pd)));
468
469        else if (pd->type == &typeid(float))
470          PyDict_SIS_Steal(packed, pd->name, PyFloat_FromDouble(me->getProperty_float(pd)));
471
472        else if (pd->type == &typeid(string)) {
473          string value;
474          me->getProperty_string(pd, value);
475          PyDict_SIS_Steal(packed, pd->name, PyString_FromString(value.c_str()));
476        }
477
478        else if (pd->type == &typeid(TValue)) {
479          TValue value;
480          me->getProperty_TValue(pd, value);
481          PyDict_SIS_Steal(packed, pd->name, Value_FromValue(value));
482        }
483
484        else if (pd->type == &typeid(TExample)) {
485          POrange mlobj;
486          me->getProperty_POrange(pd, mlobj);
487          if (mlobj)
488            PyDict_SIS_Steal(packed, pd->name, Example_FromWrappedExample(PExample(mlobj)));
489          else
490            PyDict_SetItemString(packed, pd->name, Py_None);
491        }
492   
493        else {
494          POrange mlobj;
495          me->getProperty_POrange(pd, mlobj);
496          PyDict_SIS_Steal(packed, pd->name, (PyObject *)WrapOrange(mlobj));
497        }
498      }
499    }
500
501    return packed;
502  PyCATCH
503}
504
505
506int Orange_setattr(TPyOrange *self, PyObject *pyname, PyObject *args);
507
508int unpackOrangeDictionary(PyObject *self, PyObject *dict)
509{
510  PyObject *d_key, *d_value;
511  Py_ssize_t i = 0;
512  while (PyDict_Next(dict, &i, &d_key, &d_value)) {
513//    if (Orange_setattr1((TPyOrange *)self, d_key, d_value) == -1)
514      if (Orange_setattrLow((TPyOrange *)self, d_key, d_value, false) == -1)
515        return -1;
516    }
517  return 0;
518}
519
520ORANGE_API PyObject *Orange__reduce__(PyObject *self, PyObject *, PyObject *)
521{
522    if (!((TOrangeType *)(self->ob_type))->ot_constructorAllowsEmptyArgs) {
523      PyErr_Format(PyExc_TypeError, "instances of type '%s' cannot be pickled", self->ob_type->tp_name);
524      return NULL;
525    }
526
527    return Py_BuildValue("O()N", self->ob_type, packOrangeDictionary(self));
528}
529
530
531
532PyObject *objectOnTheFly(PyObject *args, PyTypeObject *objectType)
533{
534  PyObject *emptyDict = PyDict_New();
535  PyObject *targs;
536  if (PyTuple_Check(args)) {
537    targs = args;
538    Py_INCREF(targs);
539  }
540  else
541    targs = Py_BuildValue("(O)", args);
542
543  PyObject *obj = NULL;
544  try {
545    obj = objectType->tp_new(objectType, targs, emptyDict);
546  }
547  catch (...) {
548    // do nothing; if it failed, the user probably didn't mean it
549  }
550
551  // If this failed, maybe the constructor actually expected a tuple...
552  if (!obj && PyTuple_Check(args)) {
553     PyErr_Clear();
554     Py_DECREF(targs);
555     targs = Py_BuildValue("(O)", args);
556     try {
557       obj = objectType->tp_new(objectType, targs, emptyDict);
558     }
559     catch (...) 
560     {}
561  }
562
563  if (obj) {
564    if (   objectType->tp_init != NULL
565        && objectType->tp_init(obj, targs, emptyDict) < 0) {
566          Py_DECREF(obj);
567          obj = NULL;
568    }
569  }
570
571  Py_DECREF(emptyDict);
572  Py_DECREF(targs);
573
574  return obj;
575}
576
577
578int Orange_setattr1(TPyOrange *self, char *name, PyObject *args)
579{
580  TOrange *me = (TOrange *)self->ptr;
581
582  const TPropertyDescription *propertyDescription = me->propertyDescription(name, true);
583  if (!propertyDescription)
584    return 1;
585
586  PyTRY
587    if (propertyDescription->readOnly) {
588      /* Property might be marked as readOnly, but have a specialized set function.
589         The following code is pasted from PyObject_GenericSetAttr.
590         If I'd call it here and the attribute is really read-only, PyObject_GenericSetAttr
591         would blatantly store it in the dictionary. */
592      PyObject *pyname = PyString_FromString(name);
593      PyObject *descr = _PyType_Lookup(self->ob_type, pyname);
594        PyObject *f = PYNULL;
595        if (descr != NULL && PyType_HasFeature(descr->ob_type, Py_TPFLAGS_HAVE_CLASS)) {
596            descrsetfunc f = descr->ob_type->tp_descr_set;
597            if (f != NULL && PyDescr_IsData(descr))
598                return f(descr, (PyObject *)self, args);
599      }
600
601      PyErr_Format(PyExc_TypeError, "%s.%s: read-only attribute", self->ob_type->tp_name, name);
602      return -1;
603    }
604 
605    try {
606      const type_info &propertyType = *propertyDescription->type;
607
608      if ((propertyType==typeid(bool)) || (propertyType==typeid(int))) {
609        int value;
610        if (!PyArg_Parse(args, "i", &value)) {
611          PyErr_Format(PyExc_TypeError, "invalid parameter type for %s.%s', (int expected)", self->ob_type->tp_name, name);
612          return -1;
613        }
614        if (propertyType==typeid(bool))
615          me->setProperty(name, value!=0);
616        else
617          me->setProperty(name, value);
618        return 0;
619      }
620
621      if (propertyType==typeid(float)) {
622        float value;
623        if (!PyArg_Parse(args, "f", &value)) {
624          PyErr_Format(PyExc_TypeError, "invalid parameter type for %s.%s', (float expected)", self->ob_type->tp_name, name);
625          return -1;
626        }
627        me->setProperty(name, value);
628        return 0;
629      }
630
631      if (propertyType==typeid(string)) {
632        char *value;
633        if (!PyArg_Parse(args, "s", &value)) {
634          PyErr_Format(PyExc_TypeError, "invalid parameter type for %s.%s', (string expected)", self->ob_type->tp_name, name);
635          return -1;
636        }
637        me->setProperty(name, string(value));
638        return 0;
639      }
640
641      if (propertyType==typeid(TValue)) {
642        TValue value;
643        if (!convertFromPython(args, value))
644          return -1;
645        me->setProperty(name, value);
646        return 0;
647      }
648
649      if (propertyType==typeid(TExample)) {
650        if (args==Py_None) {
651          me->wr_setProperty(name, POrange());
652          return 0;
653        }
654        else {
655          if (!PyOrExample_Check(args)) {
656            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);
657            return -1;
658          }
659          me->wr_setProperty(name, POrange(PyExample_AS_Example(args)));
660          return 0;
661        }
662      }
663
664      if (1/*propertyType==typeid(POrange)*/) {
665        const type_info *wrappedType = propertyDescription->classDescription->type;
666
667        PyTypeObject *propertyPyType=(PyTypeObject *)FindOrangeType(*wrappedType);
668        if (!propertyPyType) {
669          PyErr_Format(PyExc_SystemError, "Orange class %s, needed for '%s.%s' not exported to Python", TYPENAME(*wrappedType), self->ob_type->tp_name, name);
670          return -1;
671        }
672
673        if (args==Py_None) {
674          me->wr_setProperty(name, POrange());
675          return 0;
676        }
677
678        // User might have supplied the correct object
679        if (PyObject_TypeCheck(args, propertyPyType)) {
680          me->wr_setProperty(name, PyOrange_AS_Orange((TPyOrange *)args));
681          return 0;
682        }
683
684        // User might have supplied parameters from which we can construct the object
685        if (propertyPyType->tp_new) {
686          PyObject *obj = objectOnTheFly(args, propertyPyType);
687          if (obj) {
688            bool success = true;
689            try {
690              me->wr_setProperty(name, PyOrange_AS_Orange((TPyOrange *)obj));
691            }
692            catch (...) {
693              success = false;
694            }
695            Py_DECREF(obj);
696            if (success)
697              return 0;
698          }
699        }
700
701        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);
702        return -1;
703      }
704
705      PyErr_Format(PyExc_TypeError, "internal Orange error: unrecognized type '%s.%s'", self->ob_type->tp_name, name);
706      return -1;
707    } catch (exception err)
708    {
709      PyErr_Format(PyExc_TypeError, "error setting '%s.%s'", self->ob_type->tp_name, name);
710      return -1;
711    }
712  PyCATCH_1
713}
714
715
716int Orange_setattr1(TPyOrange *self, PyObject *pyname, PyObject *args)
717// This is a complete setattr, but without translation of obsolete names.
718{ 
719  if (!self)
720    PYERROR(PyExc_SystemError, "NULL Orange object", -1);
721
722  /* We first have to check for a specific handler.
723     The following code is pasted from PyObject_GenericSetAttr, but we can't
724     call it since it would store *all* attributes in the dictionary. */
725  PyObject *descr = _PyType_Lookup(self->ob_type, pyname);
726  PyObject *f = PYNULL;
727  if (descr != NULL && PyType_HasFeature(descr->ob_type, Py_TPFLAGS_HAVE_CLASS)) {
728    descrsetfunc f = descr->ob_type->tp_descr_set;
729    if (f != NULL && PyDescr_IsData(descr))
730      return f(descr, (PyObject *)self, args);
731  }
732 
733  char *name=PyString_AsString(pyname);
734  int res = Orange_setattr1(self, name, args);
735  if (res != 1)
736    return res;
737
738  return 1; // attribute not set (not even attempted to), try something else
739}
740
741
742PyObject *Orange_new(PyTypeObject *type, PyObject *args, PyObject *keywords)  BASED_ON(ROOT, "()")
743{ return WrapNewOrange(mlnew TOrange(), type); }
744
745int Orange_init(PyObject *self, PyObject *, PyObject *keywords)
746{ PyTRY
747    return ((TPyOrange *)self)->call_constructed || SetAttr_FromDict(self, keywords, true) ? 0 : -1;
748  PyCATCH_1
749}
750
751
752void Orange_dealloc(TPyOrange *self)
753{
754  if (!self->is_reference) {
755    PyObject_GC_UnTrack((PyObject *)self);
756    mldelete self->ptr;
757  }
758
759  // This may cause troubles in multithread use
760  if (self->orange_dict) {
761    ((TPyOrange_DictProxy *)self->orange_dict)->backlink = NULL;
762    Py_DECREF(self->orange_dict);
763  }
764
765  self->ob_type->tp_free((PyObject *)self);
766}
767
768
769
770PyObject *Orange_getattr(TPyOrange *self, PyObject *name)
771// This calls getattr1; first with the given, than with the translated name
772{ 
773  PyTRY
774    PyObject *res = Orange_getattr1(self, name);
775    char *underscored = NULL;
776    if (!res) {
777        char *camel = PyString_AsString(name);
778        underscored = camel2underscore(camel);
779        if (underscored) {
780            PyObject *translation = PyString_FromString(underscored);
781            PyErr_Clear();
782            res = Orange_getattr1(self, translation);
783            Py_DECREF(translation);
784        }
785    }
786    if (!res) {
787        PyObject *translation = PyOrange_translateObsolete((PyObject *)self, name);
788        if (translation) {
789            PyErr_Clear();
790            res = Orange_getattr1(self, translation);
791            Py_DECREF(translation);
792        }
793    }
794
795    if (!res && underscored) {
796        PyMethodDef *mi = self->ob_type->tp_methods;
797        if (mi) {
798            for(; mi->ml_name; mi++) {
799                if (!strcmp(underscored, mi->ml_name)) {
800                    res = PyMethod_New((PyObject *)mi->ml_meth, (PyObject *)self, (PyObject *)(self->ob_type));
801                    break;
802                }
803            }
804        }
805    }
806
807    if (underscored) {
808        free(underscored);
809    }
810    return res;
811  PyCATCH
812}
813
814
815int Orange_setattrDictionary(TPyOrange *self, const char *name, PyObject *args, bool warn)
816{
817  PyObject *pyname = PyString_FromString(name);
818  int res = Orange_setattrDictionary(self, pyname, args, warn);
819  Py_DECREF(pyname);
820  return res;
821}
822
823int Orange_setattrDictionary(TPyOrange *self, PyObject* pyname, PyObject *args, bool warn)
824{ PyTRY
825    char *name = PyString_AsString(pyname);
826    if (args) {
827      /* Issue a warning unless name the name is in 'recognized_list' in some of the ancestors
828         or the instance's class only derived from some Orange's class, but is written in Python */
829      if (warn && PyOrange_CheckType(self->ob_type)) {
830        char **recognized = NULL;
831        for(PyTypeObject *otype = self->ob_type; otype && (!recognized || !*recognized); otype = otype->tp_base) {
832          recognized = PyOrange_CheckType(otype) ? ((TOrangeType *)otype)->ot_recognizedattributes : NULL;
833          if (recognized)
834            for(; *recognized && strcmp(*recognized, name); recognized++);
835        }
836
837        if (!recognized || !*recognized) {
838          char sbuf[255];
839          sprintf(sbuf, "'%s' is not a builtin attribute of '%s'", name, self->ob_type->tp_name);
840          if (PyErr_Warn(PyExc_OrangeAttributeWarning, sbuf))
841            return -1;
842        }
843      }
844
845      if (!self->orange_dict)
846        self->orange_dict = PyOrange_DictProxy_New(self);
847
848      return PyDict_SetItem(self->orange_dict, pyname, args);
849    }
850    else {
851      if (self->orange_dict)
852        return PyDict_DelItem(self->orange_dict, pyname);
853      else {
854        PyErr_Format(PyExc_AttributeError, "instance of '%s' has no attribute '%s'", self->ob_type->tp_name, name);
855        return -1;
856      }
857    }
858  PyCATCH_1
859}
860
861int Orange_setattrLow(TPyOrange *self, PyObject *pyname, PyObject *args, bool warn)
862// This calls setattr1; first with the given, than with the translated name
863{ PyTRY
864    if (!PyString_Check(pyname))
865      PYERROR(PyExc_AttributeError, "object's attribute name must be string", -1);
866
867    // Try to set it as C++ class member
868    int res = Orange_setattr1(self, pyname, args);
869    if (res!=1)
870      return res;
871   
872    PyErr_Clear();
873    char *camel = PyString_AsString(pyname);
874    char *underscored = camel2underscore(camel);
875    if (underscored) {
876        PyObject *translation = PyString_FromString(underscored);
877        free(underscored);
878        res = Orange_setattr1(self, translation, args);
879        Py_DECREF(translation);
880    }
881    if (res!=1)
882      return res;
883
884    PyErr_Clear();
885    // Try to translate it as an obsolete alias for C++ class member
886    PyObject *translation = PyOrange_translateObsolete((PyObject *)self, pyname);
887    if (translation) {   
888      char sbuf[255];
889      char *name = PyString_AsString(pyname);
890      char *transname = PyString_AsString(translation);
891      sprintf(sbuf, "'%s' is an (obsolete) alias for '%s'", name, transname);
892      if (PyErr_Warn(PyExc_DeprecationWarning, sbuf))
893        return -1;
894       
895      res = Orange_setattr1(self, translation, args);
896      Py_DECREF(translation);
897      return res;
898    }
899   
900    // Use instance's dictionary
901    return Orange_setattrDictionary(self, pyname, args, warn);
902   
903  PyCATCH_1
904}
905
906
907int Orange_setattr(TPyOrange *self, PyObject *pyname, PyObject *args)
908{ return Orange_setattrLow(self, pyname, args, true); }
909
910
911PyObject *callbackOutput(PyObject *self, PyObject *args, PyObject *kwds,
912                         char *formatname1, char *formatname2, PyTypeObject *toBase)
913{ 
914  PyObject *output;
915
916  char os1[256] = "__output_";
917  strcat(os1, formatname1);
918
919  char os2[256] = "__output_";
920  if (formatname2)
921    strcat(os2, formatname2);
922
923  for(PyTypeObject *type = self->ob_type;;type = type->tp_base) {
924    PyObject *type_py = (PyObject *)type;
925
926    if (PyObject_HasAttrString(type_py, os1)) {
927      output = PyObject_GetAttrString(type_py, os1);
928      break;
929    }
930
931    char os2[256] = "__output_";
932    if (formatname2 && PyObject_HasAttrString(type_py, os2)) {
933      output = PyObject_GetAttrString(type_py, os2);
934      break;
935    }
936
937    if (type==toBase)
938      return PYNULL;
939  }
940
941  PyObject *function = PyMethod_Function(output);
942  PyObject *result;
943  if (!args)
944    result = PyObject_CallFunction(function, "O", self);
945  else {
946    PyObject *margs = PyTuple_New(1+PyTuple_Size(args));
947   
948    Py_INCREF(self);
949    PyTuple_SetItem(margs, 0, self);
950    for(Py_ssize_t i = 0, e = PyTuple_Size(args); i<e; i++) {
951      PyObject *t = PyTuple_GetItem(args, i);
952      Py_INCREF(t);
953      PyTuple_SetItem(margs, i+1, t);
954    }
955
956    result = PyObject_Call(function, margs, kwds);
957    Py_DECREF(margs);
958  }
959
960  Py_DECREF(output);
961  return result;
962}
963 
964
965char const *getName(TPyOrange *self)
966{ static char *namebuf = NULL;
967
968  if (namebuf) {
969    delete namebuf;
970    namebuf = NULL;
971  }
972   
973  PyObject *pystr = PyString_FromString("name");
974  PyObject *pyname = Orange_getattr(self, pystr);
975  if (!pyname) {
976    PyErr_Clear();
977    return NULL;
978  }
979
980  Py_DECREF(pystr);
981
982  if (!PyString_Check(pyname)) {
983    pystr = PyObject_Repr(pyname);
984    Py_DECREF(pyname);
985    pyname = pystr;
986  }
987
988  const Py_ssize_t sze = PyString_Size(pyname);
989  if (sze) {
990    namebuf = mlnew char[sze+1];
991    strcpy(namebuf, PyString_AsString(pyname));
992  }
993  Py_DECREF(pyname);
994
995  return namebuf;
996}
997
998
999PyObject *Orange_repr(TPyOrange *self)
1000{ PyTRY
1001    PyObject *result = callbackOutput((PyObject *)self, NULL, NULL, "repr", "str");
1002    if (result)
1003      return result;
1004
1005    const char *tp_name = self->ob_type->tp_name + (strncmp(self->ob_type->tp_name, "orange.", 7) ? 0 : 7);
1006    const char *name = getName(self);
1007    return name ? PyString_FromFormat("%s '%s'", tp_name, name)
1008                : PyString_FromFormat("<%s instance at %p>", tp_name, self->ptr);
1009  PyCATCH
1010}
1011
1012
1013PyObject *Orange_str(TPyOrange *self)
1014{ PyTRY
1015    PyObject *result = callbackOutput((PyObject *)self, NULL, NULL, "str", "repr");
1016    if (result)
1017      return result;
1018
1019    const char *tp_name = self->ob_type->tp_name + (strncmp(self->ob_type->tp_name, "orange.", 7) ? 0 : 7);
1020    const char *name = getName(self);
1021    return name ? PyString_FromFormat("%s '%s'", tp_name, name)
1022                : PyString_FromFormat("<%s instance at %p>", tp_name, self->ptr);
1023  PyCATCH
1024}
1025
1026
1027int Orange_nonzero(PyObject *self)
1028{ PyTRY
1029    if (self->ob_type->tp_as_sequence && self->ob_type->tp_as_sequence->sq_length)
1030      return self->ob_type->tp_as_sequence->sq_length(self) ? 1 : 0;
1031     
1032    if (self->ob_type->tp_as_mapping && self->ob_type->tp_as_mapping->mp_length)
1033      return self->ob_type->tp_as_mapping->mp_length(self) ? 1 : 0;
1034     
1035    return PyOrange_AS_Orange(self) ? 1 : 0;
1036  PyCATCH_1
1037}
1038
1039 
1040int Orange_hash(TPyOrange *self)
1041{ return _Py_HashPointer(self); }
1042
1043
1044PyObject *Orange_setattr_force(TPyOrange *self, PyObject *args) PYARGS(METH_VARARGS, "(name, value) -> None") //>setattr
1045{ 
1046  PyObject *pyname, *pyvalue;
1047  if (!PyArg_ParseTuple(args, "OO:Orange.setattr", &pyname, &pyvalue))
1048    return PYNULL;
1049  if (!PyString_Check(pyname))
1050    PYERROR(PyExc_TypeError, "attribute name must be a string", PYNULL);
1051  if (Orange_setattrLow(self, pyname, pyvalue, false) == -1)
1052    return PYNULL;
1053  RETURN_NONE;
1054}
1055
1056
1057PyObject *Orange_clone(TPyOrange *self) PYARGS(METH_NOARGS, "() -> a sensibly deep copy of the object")
1058{
1059  return WrapOrange(POrange(CLONE(TOrange, ((TOrange *)self->ptr))));
1060}
1061
1062PyObject *Orange_reference(TPyOrange *self) PYARGS(METH_NOARGS, "() -> reference; Returns unique id for an object")
1063{ PyTRY
1064    return PyInt_FromLong(long(self->ptr));
1065  PyCATCH
1066}
1067
1068
1069PyObject *Orange_typeid(TPyOrange *self) PYARGS(METH_NOARGS, "() -> int; Returns unique id for object's type")
1070{ PyTRY
1071    return PyInt_FromLong(long(&typeid(*self->ptr))); 
1072  PyCATCH
1073}
1074
1075
1076PyObject *Orange_dump(PyObject *self, PyObject *args, PyObject *kwd) PYARGS(METH_VARARGS | METH_KEYWORDS, "(formatname, ...) -> string; Prints the object into string")
1077{ PyTRY
1078    if (!args || !PyTuple_Size(args)) {
1079      PyErr_Format(PyExc_AttributeError, "missing arguments for '%s'.output", self->ob_type->tp_name);
1080      return PYNULL;
1081    }
1082
1083    PyObject *stype = PyTuple_GetItem(args, 0);
1084    if (!PyString_Check(stype)) {
1085      PyErr_Format(PyExc_AttributeError, "invalid format argument for '%s'.output", self->ob_type->tp_name);
1086      return PYNULL;
1087    }
1088    char *formatname = PyString_AsString(stype);
1089   
1090    PyObject *margs = PyTuple_New(PyTuple_Size(args)-1);
1091    for (Py_ssize_t i = 1, e = PyTuple_Size(args); i<e; i++) {
1092      PyObject *t = PyTuple_GetItem(args, i);
1093      Py_INCREF(t);
1094      PyTuple_SetItem(margs, i-1, t);
1095    }
1096
1097    PyObject *result = callbackOutput(self, margs, kwd, formatname);
1098    if (!result && !PyErr_Occurred())
1099      PyErr_Format(PyExc_AttributeError, "Class '%s' cannot be dumped as '%s'", self->ob_type->tp_name, formatname);
1100   
1101    Py_DECREF(margs);
1102    return result;
1103  PyCATCH
1104}
1105
1106
1107PyObject *Orange_write(PyObject *self, PyObject *args, PyObject *kwd) PYARGS(METH_VARARGS | METH_KEYWORDS, "(formatname, file, ...) -> string; Writes the object to a file")
1108{ PyTRY
1109    if (!args || PyTuple_Size(args)<2) {
1110      PyErr_Format(PyExc_AttributeError, "missing arguments for '%s'.output", self->ob_type->tp_name);
1111      return PYNULL;
1112    }
1113
1114    PyObject *stype = PyTuple_GetItem(args, 0);
1115    if (!PyString_Check(stype)) {
1116      PyErr_Format(PyExc_AttributeError, "invalid format argument for '%s'.output", self->ob_type->tp_name);
1117      return PYNULL;
1118    }
1119    char *formatname = PyString_AsString(stype);
1120   
1121    PyObject *margs = PyTuple_New(PyTuple_Size(args)-2);
1122    for (Py_ssize_t i = 2, e = PyTuple_Size(args); i<e; i++) {
1123      PyObject *t = PyTuple_GetItem(args, i);
1124      Py_INCREF(t);
1125      PyTuple_SetItem(margs, i-2, t);
1126    }
1127
1128    PyObject *result = callbackOutput(self, margs, kwd, formatname);
1129    Py_DECREF(margs);
1130
1131    if (!result)
1132      return PYNULL;
1133
1134    PyObject *pfile = PyTuple_GetItem(args, 1);
1135    if (pfile)
1136      if (PyFile_Check(pfile))
1137        Py_INCREF(pfile);
1138      else
1139        if (PyString_Check(pfile))
1140          pfile = PyFile_FromString(PyString_AsString(pfile), "wb");
1141        else
1142          pfile = NULL;
1143   
1144    if (!pfile) {
1145      PyErr_Format(PyExc_AttributeError, "invalid format argument for '%s'.output", self->ob_type->tp_name);
1146      Py_DECREF(result);
1147      return PYNULL;
1148    }
1149
1150    int succ = PyFile_WriteObject(result, pfile, Py_PRINT_RAW);
1151    Py_DECREF(result);
1152    Py_DECREF(pfile);
1153
1154    if (succ<0) {
1155      if (!PyErr_Occurred())
1156        PyErr_Format(PyExc_AttributeError, "Class '%s' cannot be written as '%s'", self->ob_type->tp_name, formatname);
1157      return PYNULL;
1158    }
1159    else
1160      RETURN_NONE;
1161
1162  PyCATCH
1163}
1164
1165
1166#include <typeinfo>
1167#include <string>
1168
1169
1170bool convertFromPythonWithML(PyObject *obj, string &str, const TOrangeType &base)
1171{ if (PyString_Check(obj))
1172    str=PyString_AsString(obj);
1173  else if (PyObject_TypeCheck(obj, (PyTypeObject *)const_cast<TOrangeType *>(&base)))
1174    str = string(getName((TPyOrange *)obj));
1175  else
1176    PYERROR(PyExc_TypeError, "invalid argument type", false);
1177
1178  return true;
1179}
1180
1181
1182#include "cls_orange.px"
Note: See TracBrowser for help on using the repository browser.