source: git/Singular/dyn_modules/pyobject/pyobject.cc @ 769a27

spielwiese
Last change on this file since 769a27 was 769a27, checked in by Hans Schoenemann <hannes@…>, 9 years ago
fix: user defined assign of newstruct objects
  • Property mode set to 100644
File size: 18.7 KB
Line 
1// -*- c++ -*-
2//*****************************************************************************
3/** @file pyobject.cc
4 *
5 * @author Alexander Dreyer
6 * @date 2010-12-15
7 *
8 * This file defines the @c blackbox operations for the pyobject type.
9 *
10 * @par Copyright:
11 *   (c) 2010 by The Singular Team, see LICENSE file
12**/
13//*****************************************************************************
14
15#include <kernel/mod2.h>
16#ifdef HAVE_PYTHON
17
18#include <omalloc/omalloc.h>
19#include <misc/intvec.h>
20
21#include <Singular/subexpr.h>
22#include <Singular/ipid.h>
23#include <Singular/blackbox.h>
24#include <Singular/lists.h>
25#include <Singular/ipid.h>
26#include <Singular/ipshell.h>
27#include <Singular/newstruct.h>
28#include <resources/feResource.h>
29
30#include <Singular/mod_lib.h>
31
32#include <Python.h>
33
34// #include <iterator>             // std::distance
35// #include <stdio.h>
36
37void sync_contexts();
38
39/** @class PythonInterpreter
40 * This class initializes and finalized the python interpreter.
41 *
42 * It also stores the Singular token number, which is assigned to this type on
43 * runtime.
44 **/
45class PythonInterpreter {
46public:
47  typedef int id_type;
48
49  ~PythonInterpreter()  { if(m_owns_python) Py_Finalize();  }
50
51  /// Initialize unique (singleton) python interpreter instance,
52  /// and set Singular type identifier
53  static void init(id_type num) { instance().m_id = num; }
54
55  /// Get Singular type identitfier
56  static id_type id() { return instance().m_id; }
57
58private:
59  /// Singleton: Only init() is allowed to construct an instance
60  PythonInterpreter():
61    m_id(0), m_owns_python(false)  { start_python(); }
62
63  /// Static initialization -
64  /// safely takes care of destruction on program termination
65  static PythonInterpreter& instance()
66  {
67    static PythonInterpreter init_interpreter;
68    return init_interpreter;
69  }
70
71  void start_python()
72  {
73    if (!Py_IsInitialized()) init_python();
74    set_python_defaults();
75  }
76
77  void init_python()
78  {
79    Py_Initialize();
80    m_owns_python = true;
81  }
82
83  void set_python_defaults()
84  {
85    // Sone python modules needs argc, argv set for some reason
86    char* argv = (char*)"";
87    PySys_SetArgv(1, &argv);
88    PyRun_SimpleString("from sys import path, modules");
89    PyRun_SimpleString("_SINGULAR_IMPORTED = dict()");
90
91    char cmd[MAXPATHLEN + 20];
92    sprintf(cmd, "path.insert(0, '%s')", feGetResource('b'));
93    PyRun_SimpleString(cmd);
94    PyRun_SimpleString("del path");  // cleanup
95  }
96
97  id_type m_id;
98  bool m_owns_python;
99};
100
101/** @class PythonObject
102 * This class defines an interface for calling PyObject from Singular.
103 *
104 * @note This class does not take care of the memory mangement, this is done in
105 * the blackbox routines.
106 **/
107class PythonObject
108{
109  typedef PythonObject self;
110
111public:
112  typedef PyObject* ptr_type;
113  struct sequence_tag{};
114
115  PythonObject(): m_ptr(Py_None) { }
116  PythonObject(ptr_type ptr): m_ptr(ptr) {
117    if (!ptr && handle_exception()) m_ptr = Py_None;
118  }
119
120  ptr_type check_context(ptr_type ptr) const {
121    if(ptr) sync_contexts();
122    return ptr;
123  }
124  /// Unary operations
125  self operator()(int op) const
126  {
127    switch(op)
128    {
129    case '(':  return check_context(PyObject_CallObject(*this, NULL));
130    case ATTRIB_CMD: return PyObject_Dir(*this);
131    case PROC_CMD: return *this;
132    }
133
134    if (op == PythonInterpreter::id())
135      return *this;
136
137    return self(NULL);
138  }
139
140  /// Binary and n-ary operations
141  self operator()(int op, const self& arg) const {
142
143    switch(op)
144    {
145      case '+':  return PyNumber_Add(*this, arg);
146      case '-':  return PyNumber_Subtract(*this, arg);
147      case '*':  return PyNumber_Multiply(*this, arg);
148      case '/':  return PyNumber_Divide(*this, arg);
149      case '^':  return PyNumber_Power(*this, arg, Py_None);
150      case '(':  return check_context(PyObject_CallObject(*this, arg));
151      case '[':  return operator[](arg);
152      case KILLATTR_CMD: return del_attr(arg);
153      case LIST_CMD:     return args2list(arg);
154      case '.': case COLONCOLON: case ATTRIB_CMD: return attr(arg);
155    }
156    return self(NULL);
157  }
158
159  /// Ternary operations
160  self operator()(int op, const self& arg1, const self& arg2) const
161  {
162    switch(op)
163    {
164      case ATTRIB_CMD:
165        if(PyObject_SetAttr(*this, arg1, arg2) == -1) handle_exception();
166        return self();
167    }
168    return self(NULL);
169  }
170
171  /// Get item
172  self operator[](const self& idx) const { return PyObject_GetItem(*this, idx); }
173  self operator[](long idx) const { return operator[](PyInt_FromLong(idx));  }
174
175  /// Get actual PyObject*
176  operator const ptr_type() const { return m_ptr; }
177
178  /// Get representative as C-style string
179  char* repr() const
180  {
181    return omStrDup(PyString_AsString(PyObject_Repr(*this)));
182  }
183
184  /// Extract C-style string
185  char* str() const { return omStrDup(PyString_AsString(*this)); }
186
187  Py_ssize_t size() const { return PyObject_Size(m_ptr); }
188
189  BOOLEAN assign_to(leftv result)
190  {
191    return (m_ptr? (m_ptr == Py_None? none_to(result): python_to(result)): TRUE);
192  }
193
194  void import_as(const char* name) const {
195    idhdl handle = enterid(omStrDup(name), 0, DEF_CMD,
196                           &IDROOT, FALSE);
197
198    if (handle)
199    {
200      IDDATA(handle) = (char*)m_ptr;
201      Py_XINCREF(m_ptr);
202      IDTYP(handle) =  PythonInterpreter::id();
203    }
204    else { Werror("Importing pyobject to Singular failed"); }
205  }
206
207  int compare(int op, const self& arg) const
208  { return PyObject_RichCompareBool(*this, arg, py_opid(op)); }
209
210
211  self attr(const self& arg) const { return PyObject_GetAttr(*this, arg); }
212
213  self del_attr(const self& arg) const
214  {
215    if (!PyObject_HasAttr(*this, arg))
216      Werror("Cannot delete attribute %s.", arg.repr());
217    else
218      PyObject_DelAttr(*this, arg);
219
220    return self();
221  }
222
223protected:
224  self args2list(const self& args) const
225  {
226    self pylist(PyList_New(0));
227    PyList_Append(pylist, *this);
228    if(PyTuple_Check(args))  pylist.append_iter(PyObject_GetIter(args));
229    else PyList_Append(pylist, args);
230
231    return pylist;
232  }
233
234  BOOLEAN handle_exception() const {
235
236    if(!PyErr_Occurred()) return FALSE;
237
238    PyObject *pType, *pMessage, *pTraceback;
239    PyErr_Fetch(&pType, &pMessage, &pTraceback);
240
241    WerrorS("pyobject error occured");
242    WerrorS(PyString_AsString(pMessage));
243
244    Py_XDECREF(pType);
245    Py_XDECREF(pMessage);
246    Py_XDECREF(pTraceback);
247
248    PyErr_Clear();
249    return TRUE;
250  }
251
252  void append_iter(self iterator) {
253    ptr_type item;
254    while ((item = PyIter_Next(iterator))) {
255      PyList_Append(*this, item);
256      Py_DECREF(item);
257    }
258  }
259
260  int py_opid(int op) const{
261    switch(op)
262    {
263      case '<':  return Py_LT;
264      case '>':  return Py_GT;
265      case EQUAL_EQUAL:  return Py_EQ;
266      case NOTEQUAL:  return Py_NE;
267      case GE:  return Py_GE;
268      case LE:  return Py_LE;
269    }
270    return -1;
271  }
272
273private:
274  BOOLEAN none_to(leftv result) const
275  {
276    Py_XDECREF(m_ptr);
277    result->data = NULL;
278    result->rtyp = NONE;
279    return FALSE;
280  }
281
282  BOOLEAN python_to(leftv result) const
283  {
284    result->data = m_ptr;
285    Py_XINCREF(m_ptr);
286    result->rtyp = PythonInterpreter::id();
287    return !m_ptr;
288  }
289
290  /// The actual pointer
291  ptr_type m_ptr;
292};
293
294
295
296/** @class PythonCastStatic
297 * This template class does conversion of Singular objects to python objects on
298 * compile-time.
299 *
300 * @note The Singular objects are assumed to be equivalent to the template argument.
301 **/
302template <class CastType = PythonObject::ptr_type>
303class PythonCastStatic:
304  public PythonObject {
305  typedef PythonCastStatic self;
306public:
307
308  PythonCastStatic(void* value):
309    PythonObject(get(reinterpret_cast<CastType>(value))) {}
310
311  PythonCastStatic(leftv value):
312    PythonObject(get(reinterpret_cast<CastType>(value->Data()))) {}
313
314private:
315  ptr_type get(ptr_type value)       { return value; }
316  ptr_type get(long value)           { return PyInt_FromLong(value); }
317  ptr_type get(int value)            { return PyInt_FromLong((long)value); }
318  ptr_type get(const char* value)    { return PyString_FromString(value); }
319  ptr_type get(char* value) { return get(const_cast<const char*>(value)); }
320  ptr_type get(intvec* value);       // inlined below
321  ptr_type get(lists value);         // inlined after PythonObjectDynamic
322};
323
324template <class CastType>
325inline PythonObject::ptr_type
326PythonCastStatic<CastType>::get(intvec* value)
327{
328  ptr_type pylist(PyList_New(0));
329  for (int idx = 0; idx < value->length(); ++idx)
330    PyList_Append(pylist, self::get((*value)[idx]));
331
332  return pylist;
333}
334
335/** @class PythonCastDynamic
336 * This class does conversion of Singular objects to python objects on runtime.
337 *
338 **/
339class PythonCastDynamic:
340  public PythonObject {
341  typedef PythonCastDynamic self;
342
343public:
344  PythonCastDynamic(leftv value): PythonObject(get(value, value->Typ())) {}
345
346private:
347  PythonObject get(leftv value, int typeId)
348  {
349    if (typeId == PythonInterpreter::id()) return PythonCastStatic<>(value);
350
351    switch (typeId)
352    {
353    case INT_CMD:    return PythonCastStatic<long>(value);
354    case STRING_CMD: return PythonCastStatic<const char*>(value);
355    case LIST_CMD:   return PythonCastStatic<lists>(value);
356    case INTVEC_CMD: return PythonCastStatic<intvec*>(value);
357    }
358
359    sleftv tmp;
360    BOOLEAN newstruct_Assign_user(int, leftv, leftv); // declaring overloaded '='
361    if (!newstruct_Assign_user(PythonInterpreter::id(), &tmp, value))
362      return PythonCastStatic<>(&tmp);
363
364    if (typeId > MAX_TOK)       // custom types
365    {
366      blackbox *bbx = getBlackboxStuff(typeId);
367      assume(bbx != NULL);
368      if (! bbx->blackbox_Op1(PythonInterpreter::id(), &tmp, value))
369        return PythonCastStatic<>(&tmp);
370    }
371
372    Werror("type '%s` incompatible with 'pyobject`", iiTwoOps(typeId));
373    return PythonObject();
374  }
375};
376
377template <class CastType>
378inline PythonObject::ptr_type
379PythonCastStatic<CastType>::get(lists value)
380{
381  ptr_type pylist(PyList_New(0));
382  for (int i = 0; i <= value->nr; ++i)
383    PyList_Append(pylist, PythonCastDynamic((value->m) + i));
384
385  return pylist;
386}
387
388/// Template specialization for getting handling sequence
389template <>
390class PythonCastStatic<PythonObject::sequence_tag>:
391public PythonObject
392{
393public:
394
395  PythonCastStatic(leftv value):
396    PythonObject(PyTuple_New(size(value)))  { append_to(value); }
397
398
399private:
400  size_t size(leftv iter, size_t distance = 0) const
401  {
402    if (iter) { do { ++distance; } while((iter = iter->next)); };
403    return distance;
404  }
405
406  void append_to(leftv iter) const
407  {
408    for(size_t idx = 0; iter != NULL; iter = iter->next)
409      PyTuple_SetItem(*this, idx++, PythonCastDynamic(iter));
410  }
411};
412
413
414PythonObject get_attrib_name(leftv arg)
415{
416  typedef PythonCastStatic<const char*> result_type;
417  if (arg->Typ() == STRING_CMD)
418    return result_type(arg);
419
420  return result_type((void*)arg->Name());
421}
422
423/// Evaluate string in python
424PythonObject python_eval(const char* arg) {
425
426  PyObject* globals = PyModule_GetDict(PyImport_Import(PyString_FromString("__main__")));
427  return PyRun_String(arg, Py_eval_input, globals, globals);
428}
429
430/// Evaluate string in python from Singular
431BOOLEAN python_eval(leftv result, leftv arg) {
432  if ( !arg || (arg->Typ() != STRING_CMD) ) {
433    Werror("expected python_eval('string')");
434    return TRUE;
435  }
436
437  return python_eval(reinterpret_cast<const char*>(arg->Data())).assign_to(result);
438}
439
440
441/// Execute string in python from Singular
442BOOLEAN python_run(leftv result, leftv arg)
443{
444  if ( !arg || (arg->Typ() != STRING_CMD) ) {
445    Werror("expected python_run('string')");
446    return TRUE;
447  }
448
449  PyRun_SimpleString(reinterpret_cast<const char*>(arg->Data()));
450  sync_contexts();
451
452  Py_INCREF(Py_None);
453  return PythonCastStatic<>(Py_None).assign_to(result);
454}
455
456PythonObject names_from_module(const char* module_name)
457{
458  char buffer[strlen(module_name) + 30];
459  sprintf (buffer, "SINGULAR_MODULE_NAME = '%s'", module_name);
460  PyRun_SimpleString(buffer);
461  PyRun_SimpleString("from sys import modules");
462  PyRun_SimpleString("exec('from ' + SINGULAR_MODULE_NAME + ' import *')");
463
464  return python_eval("[str for str in dir(modules[SINGULAR_MODULE_NAME]) if str[0] != '_']");
465}
466
467void from_module_import_all(const char* module_name)
468{
469  char buffer[strlen(module_name) + 20];
470  sprintf (buffer, "from %s import *", module_name);
471  PyRun_SimpleString(buffer);
472}
473
474/// import python module and export identifiers in Singular namespace
475BOOLEAN python_import(leftv result, leftv value) {
476
477  if ((value == NULL) || (value->Typ()!= STRING_CMD)) {
478    Werror("expected python_import('string')");
479    return TRUE;
480  }
481
482  from_module_import_all(reinterpret_cast<const char*>(value->Data()));
483  sync_contexts();
484
485  Py_INCREF(Py_None);
486  return PythonCastStatic<>(Py_None).assign_to(result);
487}
488
489/// blackbox support - initialization
490void* pyobject_Init(blackbox*)
491{
492  Py_INCREF(Py_None);
493  return Py_None;
494}
495
496/// blackbox support - convert to string representation
497char* pyobject_String(blackbox *b, void* ptr)
498{
499  return PythonCastStatic<>(ptr).repr();
500}
501
502/// blackbox support - copy element
503void* pyobject_Copy(blackbox*b, void* ptr)
504{
505    Py_XINCREF(ptr);
506    return ptr;
507}
508
509/// blackbox support - assign element
510BOOLEAN pyobject_Assign(leftv l, leftv r)
511{
512  Py_XDECREF(l->Data());
513  PyObject* result = PythonCastDynamic(r);
514  Py_XINCREF(result);
515
516  if (l->rtyp == IDHDL)
517    IDDATA((idhdl)l->data) = (char *)result;
518  else
519    l->data = (void *)result;
520
521  return !result;
522}
523
524
525/// blackbox support - unary operations
526BOOLEAN pyobject_Op1(int op, leftv res, leftv head)
527{
528  switch(op)
529  {
530    case INT_CMD:               // built-in return types first
531    {
532      long value = PyInt_AsLong(PythonCastStatic<>(head));
533      if( (value == -1) &&  PyErr_Occurred() ) {
534        Werror("'pyobject` cannot be converted to integer");
535        PyErr_Clear();
536        return TRUE;
537      }
538      res->data = (void*) value;
539      res->rtyp = INT_CMD;
540      return FALSE;
541    }
542    case TYPEOF_CMD:
543      res->data = (void*) omStrDup("pyobject");
544      res->rtyp = STRING_CMD;
545      return FALSE;
546  }
547
548  if (!PythonCastStatic<>(head)(op).assign_to(res))
549    return FALSE;
550
551  BOOLEAN newstruct_Op1(int, leftv, leftv); // forward declaration
552  return newstruct_Op1(op, res, head);
553}
554
555
556/// blackbox support - binary operations
557BOOLEAN pyobject_Op2(int op, leftv res, leftv arg1, leftv arg2)
558{
559  PythonCastStatic<> lhs(arg1);
560
561  switch(op)                    // built-in return types and special cases first
562  {
563    case '<': case '>': case EQUAL_EQUAL: case NOTEQUAL: case GE: case LE:
564    {
565      res->data = (void *)(long)(lhs.compare(op, PythonCastDynamic(arg2)));
566      res->rtyp = INT_CMD;
567      return FALSE;
568    }
569    case '.': case COLONCOLON: case ATTRIB_CMD:
570      return lhs.attr(get_attrib_name(arg2)).assign_to(res);
571  }
572
573  PythonCastDynamic rhs(arg2);
574  if (!lhs(op, rhs).assign_to(res))
575    return FALSE;
576
577  BOOLEAN newstruct_Op2(int, leftv, leftv, leftv); // forward declaration
578  return newstruct_Op2(op, res, arg1, arg2);
579
580}
581
582/// blackbox support - ternary operations
583BOOLEAN pyobject_Op3(int op, leftv res, leftv arg1, leftv arg2, leftv arg3)
584{
585  PythonCastStatic<> lhs(arg1);
586  PythonCastDynamic rhs1(arg2);
587  PythonCastDynamic rhs2(arg3);
588
589  if (!lhs(op, rhs1, rhs2).assign_to(res))
590    return FALSE;
591
592  return blackboxDefaultOp3(op, res, arg1, arg2, arg3);
593}
594
595
596/// blackbox support - n-ary operations
597BOOLEAN pyobject_OpM(int op, leftv res, leftv args)
598{
599  switch(op)                    // built-in return types first
600  {
601    case STRING_CMD:
602    {
603      blackbox* a = getBlackboxStuff(args->Typ());
604      res->data = (void *)a->blackbox_String(a, args->Data());
605      res->rtyp = STRING_CMD;
606      return FALSE;
607    }
608
609    case INTVEC_CMD:
610      PythonObject obj = PythonCastStatic<>(args->Data());
611      unsigned long len = obj.size();
612
613      intvec* vec = new intvec(len);
614      for(unsigned long idx = 0; idx != len; ++idx) {
615        long value = PyInt_AsLong(obj[idx]);
616        (*vec)[idx] = static_cast<int>(value);
617
618        if ((value == -1) &&  PyErr_Occurred()) {
619          value = 0;
620          PyErr_Clear();
621        }
622        if (value != long((*vec)[idx])) {
623          delete vec;
624          Werror("'pyobject` cannot be converted to intvec");
625          return TRUE;
626        }
627      }
628      res->data = (void *)vec;
629      res->rtyp = op;
630      return FALSE;
631  }
632  typedef PythonCastStatic<PythonObject::sequence_tag> seq_type;
633  if (! PythonCastStatic<>(args)(op, seq_type(args->next)).assign_to(res))
634    return FALSE;
635
636  BOOLEAN newstruct_OpM(int, leftv, leftv); // forward declaration
637  return newstruct_OpM(op, res, args);
638}
639
640/// blackbox support - destruction
641void pyobject_destroy(blackbox *b, void* ptr)
642{
643  Py_XDECREF(ptr);
644}
645
646PyObject* get_current_definition(const char* name) {
647  idhdl handle =  ggetid(name);
648  if (!handle || (IDTYP(handle) != PythonInterpreter::id()))  return NULL;
649  PythonCastStatic<PyObject*> value(IDDATA(handle));
650  return value;
651}
652
653/// getting stuff from python to Singular namespace
654void sync_contexts()
655{
656  PyRun_SimpleString("_SINGULAR_NEW = modules['__main__'].__dict__.copy()");
657
658  PythonObject newElts = python_eval("[(_k, _e)   \
659    for (_k, _e) in _SINGULAR_NEW.iteritems() \
660    if _k not in _SINGULAR_IMPORTED or not _SINGULAR_IMPORTED[_k] is _e]");
661
662  long len = newElts.size();
663  for (long idx = 0; idx < len; ++idx)
664  {
665    long i = 0;
666    char* name = newElts[idx][i].str();
667    if (name && (*name != '\0') && (*name != '_'))
668    {
669      Py_XDECREF(get_current_definition(name));
670      i = 1;
671      newElts[idx][i].import_as(name);
672    }
673
674  }
675
676  PythonObject deletedElts =
677    python_eval("list(set(_SINGULAR_IMPORTED.iterkeys()) - \
678     set(_SINGULAR_NEW.iterkeys()))");
679  len = deletedElts.size();
680
681  for (long idx = 0; idx < len; ++idx)
682  {
683    char* name = deletedElts[idx].str();
684    if (name && (*name != '\0') && (*name != '_'))
685      killid(name, &IDROOT);
686  }
687
688  PyRun_SimpleString("_SINGULAR_IMPORTED =_SINGULAR_NEW");
689  PyRun_SimpleString("del  _SINGULAR_NEW");
690}
691
692
693
694blackbox* pyobject_blackbox(int& tok) {
695  if(blackboxIsCmd("pyobject", tok) != ROOT_DECL)
696  {
697    tok = setBlackboxStuff((blackbox*)omAlloc0(sizeof(blackbox)),
698                           "pyobject");
699  }
700  return getBlackboxStuff(tok);
701}
702
703
704
705#define PYOBJECT_ADD_C_PROC(name) \
706  psModulFunctions->iiAddCproc((currPack->libname? currPack->libname: ""),\
707     (char*)#name, FALSE, name);
708
709extern "C" int SI_MOD_INIT(pyobject)(SModulFunctions* psModulFunctions)
710{
711  int tok = -1;
712  blackbox* bbx = pyobject_blackbox(tok);
713  if (bbx->blackbox_Init != pyobject_Init)
714  {
715    bbx->blackbox_destroy = pyobject_destroy;
716    bbx->blackbox_String  = pyobject_String;
717    bbx->blackbox_Init    = pyobject_Init;
718    bbx->blackbox_Copy    = pyobject_Copy;
719    bbx->blackbox_Assign  = pyobject_Assign;
720    bbx->blackbox_Op1     = pyobject_Op1;
721    bbx->blackbox_Op2     = pyobject_Op2;
722    bbx->blackbox_Op3     = pyobject_Op3;
723    bbx->blackbox_OpM     = pyobject_OpM;
724    bbx->data             = (void*)omAlloc0(newstruct_desc_size());
725
726    PythonInterpreter::init(tok);
727
728    PYOBJECT_ADD_C_PROC(python_import);
729    PYOBJECT_ADD_C_PROC(python_eval);
730    PYOBJECT_ADD_C_PROC(python_run);
731  }
732  return MAX_TOK;
733}
734#undef PYOBJECT_ADD_C_PROC
735
736#endif /* HAVE_PYTHON */
Note: See TracBrowser for help on using the repository browser.