source: git/Singular/pyobject.cc @ ee7fda

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