source: git/Singular/pyobject.cc @ 16f511

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