source: git/Singular/pyobject.cc @ c2ff413

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