source: git/Singular/pyobject.cc @ 121d2ae

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