source: git/Singular/pyobject.cc @ 702175

spielwiese
Last change on this file since 702175 was 011fb62, checked in by Hans Schoenemann <hannes@…>, 13 years ago
error messages for python support git-svn-id: file:///usr/local/Singular/svn/trunk@13808 2c84dea3-7e68-4137-9b89-c4e89433aadc
  • Property mode set to 100644
File size: 16.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
16
17
18
19#include <Singular/mod2.h>
20
21#include <Singular/ipid.h>
22#include <Singular/blackbox.h>
23#include <omalloc/omalloc.h>
24#include <kernel/febase.h>
25#include <kernel/longrat.h>
26#include <Singular/subexpr.h>
27
28#include <Singular/lists.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) {if (!ptr) handle_exception();}
114
115  ptr_type check_context(ptr_type ptr) const {
116    if(ptr) sync_contexts(); 
117    return ptr;
118  }
119  /// Unary operations
120  self operator()(int op) const
121  {
122    switch(op)
123    {
124    case '(':  return check_context(PyObject_CallObject(*this, NULL));
125    case ATTRIB_CMD: return PyObject_Dir(*this);
126    case PROC_CMD: return *this;
127    }
128
129    if (op == PythonInterpreter::id())
130      return *this;
131
132    Werror("unary op %d not implemented for pyobject", op);
133    return self();
134  }
135
136  /// Binary and n-ary operations
137  self operator()(int op, const self& arg) const {
138
139    switch(op)
140    {
141      case '+':  return PyNumber_Add(*this, arg);
142      case '-':  return PyNumber_Subtract(*this, arg);
143      case '*':  return PyNumber_Multiply(*this, arg);
144      case '/':  return PyNumber_Divide(*this, arg);
145      case '^':  return PyNumber_Power(*this, arg, Py_None);
146      case '(':  return check_context(PyObject_CallObject(*this, arg));
147      case '[':  return operator[](arg);
148      case KILLATTR_CMD: return del_attr(arg);
149      case LIST_CMD:     return args2list(arg);
150      case '.': case COLONCOLON: case ATTRIB_CMD: return attr(arg);
151    }
152    Werror("binary op %d not implemented for pyobject!", op);
153
154    return self();
155  }
156
157  /// Ternary operations
158  self operator()(int op, const self& arg1, const self& arg2) const 
159  {
160    switch(op)
161    {
162      case ATTRIB_CMD: PyObject_SetAttr(*this, arg1, arg2); return self();
163    }
164
165    Werror("ternary op %d not implemented for pyobject!", op);
166    return self();
167  }
168
169  /// Get item
170  self operator[](const self& idx) const { return PyObject_GetItem(*this, idx); }
171  self operator[](long idx) const { return operator[](PyInt_FromLong(idx));  }
172
173  /// Get actual PyObject*
174  operator const ptr_type() const { return m_ptr; }
175
176  /// Get representative as C-style string
177  char* repr() const
178  {
179    return omStrDup(PyString_AsString(PyObject_Repr(*this)));
180  }
181
182  /// Extract C-style string
183  char* str() const { return omStrDup(PyString_AsString(*this)); }
184
185  Py_ssize_t size() const { return PyObject_Size(m_ptr); }
186
187  BOOLEAN assign_to(leftv result)
188  {
189    return (m_ptr == Py_None? none_to(result): python_to(result));
190  }
191
192  void import_as(const char* name) const {
193    idhdl handle = enterid(omStrDup(name), 0, DEF_CMD, 
194                           &IDROOT, FALSE);
195
196    if (handle)
197    {
198      IDDATA(handle) = (char*)m_ptr;
199      Py_XINCREF(m_ptr); 
200      IDTYP(handle) =  PythonInterpreter::id();
201    }
202    else { Werror("Importing pyobject to Singular failed"); }
203  }
204
205  int compare(int op, const self& arg) const
206  { return PyObject_RichCompareBool(*this, arg, py_opid(op)); }
207
208
209  self attr(const self& arg) const { return PyObject_GetAttr(*this, arg); }
210
211  self del_attr(const self& arg) const 
212  {
213    if (!PyObject_HasAttr(*this, arg)) 
214      Werror("Cannot delete attribute %s.", arg.repr());
215    else
216      PyObject_DelAttr(*this, arg); 
217 
218    return self();
219  }
220
221protected:
222  self args2list(const self& args) const
223  {
224    self pylist(PyList_New(0));
225    PyList_Append(pylist, *this);
226    if(PyTuple_Check(args))  pylist.append_iter(PyObject_GetIter(args));
227    else PyList_Append(pylist, args);
228
229    return pylist;
230  }
231
232  void handle_exception() {
233   
234    PyObject *pType, *pMessage, *pTraceback;
235    PyErr_Fetch(&pType, &pMessage, &pTraceback);
236   
237    Werror("pyobject error occured");
238    Werror(PyString_AsString(pMessage));
239   
240    Py_XDECREF(pType);
241    Py_XDECREF(pMessage);
242    Py_XDECREF(pTraceback);
243   
244    PyErr_Clear();
245  }
246
247  void append_iter(self iterator) {
248    ptr_type item;
249    while (item = PyIter_Next(iterator)) {
250      PyList_Append(*this, item);
251      Py_DECREF(item);
252    }
253  }
254
255  int py_opid(int op) const{
256    switch(op)
257    { 
258      case '<':  return Py_LT;
259      case '>':  return Py_GT;
260      case EQUAL_EQUAL:  return Py_EQ;
261      case NOTEQUAL:  return Py_NE;
262      case GE:  return Py_GE; 
263      case LE:  return Py_LE; 
264    }
265    return -1;
266  }
267
268private:
269  BOOLEAN none_to(leftv result) const
270  {
271    result->data = NULL;
272    result->rtyp = NONE;
273    return FALSE;
274  }
275
276  BOOLEAN python_to(leftv result) const
277  {
278    result->data = m_ptr;
279    result->rtyp = PythonInterpreter::id();
280    return !m_ptr;
281  }
282
283  /// The actual pointer
284  ptr_type m_ptr;
285};
286
287
288
289/** @class PythonCastStatic
290 * This template class does conversion of Singular objects to python objects on
291 * compile-time.
292 *
293 * @note The Singular objects are assumed to be equivalent to the template argument.
294 **/
295template <class CastType = PythonObject::ptr_type>
296class PythonCastStatic:
297  public PythonObject {
298public:
299
300  PythonCastStatic(void* value):
301    PythonObject(get(reinterpret_cast<CastType>(value))) {}
302
303  PythonCastStatic(leftv value):
304    PythonObject(get(reinterpret_cast<CastType>(value->Data()))) {}
305
306private:
307  ptr_type get(ptr_type value)       { return value; }
308  ptr_type get(long value)           { return PyInt_FromLong(value); }
309  ptr_type get(const char* value)    { return PyString_FromString(value); }
310  ptr_type get(char* value) { return get(const_cast<const char*>(value)); }
311  ptr_type get(lists value);         // inlined after PythonObjectDynamic
312};
313
314
315
316/** @class PythonCastDynamic
317 * This class does conversion of Singular objects to python objects on runtime.
318 *
319 **/
320class PythonCastDynamic:
321  public PythonObject {
322  typedef PythonCastDynamic self;
323
324public:
325  PythonCastDynamic(leftv value): PythonObject(get(value, value->Typ())) {}
326
327private:
328  PythonObject get(leftv value, int typeId)
329  {
330    if (typeId == PythonInterpreter::id()) return PythonCastStatic<>(value);
331   
332    switch (typeId)
333    {
334    case INT_CMD:    return PythonCastStatic<long>(value);
335    case STRING_CMD: return PythonCastStatic<const char*>(value);
336    case LIST_CMD:   return PythonCastStatic<lists>(value);
337      //    case UNKNOWN:    return PythonCastStatic<const char*>((void*)value->Name());
338    }
339   
340    Werror("type # %d incompatible with pyobject", typeId);
341    return PythonObject();
342  }
343};
344
345template <class CastType>
346inline PythonObject::ptr_type
347PythonCastStatic<CastType>::get(lists value)
348{
349  ptr_type pylist(PyList_New(0));
350  for (std::size_t i = 0; i <= value->nr; ++i)
351    PyList_Append(pylist, PythonCastDynamic((value->m) + i));
352
353  return pylist;
354}
355
356/// Template specialization for getting handling sequence
357template <>
358class PythonCastStatic<PythonObject::sequence_tag>:
359public PythonObject
360{
361public:
362
363  PythonCastStatic(leftv value):
364    PythonObject(PyTuple_New(size(value)))  { append_to(value); }
365
366 
367private:
368  std::size_t size(leftv iter, std::size_t distance = 0) const 
369  {
370    if (iter) { do { ++distance; } while(iter = iter->next); }; 
371    return distance;
372  }
373 
374  void append_to(leftv iter) const
375  {
376    for(std::size_t idx = 0; iter != NULL; iter = iter->next)
377      PyTuple_SetItem(*this, idx++, PythonCastDynamic(iter));
378  }
379};
380
381
382PythonObject get_attrib_name(leftv arg)
383{
384  typedef PythonCastStatic<const char*> result_type;
385  if (arg->Typ() == STRING_CMD)
386    return result_type(arg);
387
388  return result_type((void*)arg->Name());
389}
390
391/// Evaluate string in python
392PythonObject python_eval(const char* arg) {
393
394  PyObject* globals = PyModule_GetDict(PyImport_Import(PyString_FromString("__main__")));
395  return PyRun_String(arg, Py_eval_input, globals, globals);
396}
397
398/// Evaluate string in python from Singular
399BOOLEAN python_eval(leftv result, leftv arg) {
400  if ( !arg || (arg->Typ() != STRING_CMD) ) {
401    Werror("expected python_eval('string')");
402    return TRUE;
403  }
404
405  return python_eval(reinterpret_cast<const char*>(arg->Data())).assign_to(result);
406}
407
408
409/// Execute string in python from Singular
410BOOLEAN python_run(leftv result, leftv arg)
411{
412  if ( !arg || (arg->Typ() != STRING_CMD) ) {
413    Werror("expected python_run('string')");
414    return TRUE;
415  }
416
417  PyRun_SimpleString(reinterpret_cast<const char*>(arg->Data()));
418  sync_contexts();
419  return PythonCastStatic<>(Py_None).assign_to(result);
420}
421
422PythonObject names_from_module(const char* module_name)
423{
424  char buffer[strlen(module_name) + 30];
425  sprintf (buffer, "SINGULAR_MODULE_NAME = '%s'", module_name);
426  PyRun_SimpleString(buffer);
427  PyRun_SimpleString("from sys import modules");
428  PyRun_SimpleString("exec('from ' + SINGULAR_MODULE_NAME + ' import *')");
429
430  return python_eval("[str for str in dir(modules[SINGULAR_MODULE_NAME]) if str[0] != '_']");
431}
432
433void from_module_import_all(const char* module_name)
434{
435  char buffer[strlen(module_name) + 20];
436  sprintf (buffer, "from %s import *", module_name);
437  PyRun_SimpleString(buffer);
438}
439
440/// import python module and export identifiers in Singular namespace
441BOOLEAN python_import(leftv result, leftv value) {
442
443  if ((value == NULL) || (value->Typ()!= STRING_CMD)) {
444    Werror("expected python_import('string')");
445    return TRUE;
446  }
447
448  from_module_import_all(reinterpret_cast<const char*>(value->Data()));
449  sync_contexts();
450
451  return PythonCastStatic<>(Py_None).assign_to(result);
452}
453
454/// blackbox support - initialization
455void* pyobject_Init(blackbox*)
456{
457  return Py_None;
458}
459
460/// blackbox support - convert to string representation
461char* pyobject_String(blackbox *b, void* ptr)
462{
463  return PythonCastStatic<>(ptr).repr();
464}
465
466/// blackbox support - copy element
467void* pyobject_Copy(blackbox*b, void* ptr)
468{ 
469    Py_XINCREF(ptr);
470    return ptr;
471}
472
473/// blackbox support - assign element
474BOOLEAN pyobject_Assign(leftv l, leftv r)
475{
476  Py_XDECREF(l->Data());
477  PyObject* result = PythonCastDynamic(r);
478  Py_XINCREF(result);
479
480  if (l->rtyp == IDHDL)
481    IDDATA((idhdl)l->data) = (char *)result;
482  else
483    l->data = (void *)result;
484 
485  return !result;
486}
487                                                                     
488
489/// blackbox support - unary operations
490BOOLEAN pyobject_Op1(int op, leftv res, leftv head)
491{
492  switch(op)
493  {
494    case INT_CMD:               // built-in return types first
495    {
496      long value = PyInt_AsLong(PythonCastStatic<>(head));
497      if( (value == -1) &&  PyErr_Occurred() ) {
498        Werror("pyobject cannot be converted to integer");
499        PyErr_Clear();
500        return TRUE;
501      }
502      res->data = (void*) value;
503      res->rtyp = INT_CMD;
504      return FALSE;
505    }
506    case TYPEOF_CMD:
507      res->data = (void*) omStrDup("pyobject");
508      res->rtyp = STRING_CMD; 
509      return FALSE; 
510
511  case LIST_CMD:
512    return FALSE;
513
514  }
515  return PythonCastStatic<>(head)(op).assign_to(res);
516}
517
518
519/// blackbox support - binary operations
520BOOLEAN pyobject_Op2(int op, leftv res, leftv arg1, leftv arg2)
521{
522  PythonCastStatic<> lhs(arg1);
523
524  switch(op)                    // built-in return types and special cases first
525  { 
526    case '<': case '>': case EQUAL_EQUAL: case NOTEQUAL: case GE: case LE:
527    {
528      res->data = (void *)lhs.compare(op, PythonCastDynamic(arg2));
529      res->rtyp = INT_CMD;
530      return FALSE;
531    }
532    case '.': case COLONCOLON: case ATTRIB_CMD:
533      return lhs.attr(get_attrib_name(arg2)).assign_to(res);
534  }
535  PythonCastDynamic rhs(arg2);
536  return lhs(op, rhs).assign_to(res);
537}
538
539/// blackbox support - ternary operations
540BOOLEAN pyobject_Op3(int op, leftv res, leftv arg1, leftv arg2, leftv arg3)
541{
542  PythonCastStatic<> lhs(arg1);
543  PythonCastDynamic rhs1(arg2);
544  PythonCastDynamic rhs2(arg3);
545
546  return lhs(op, rhs1, rhs2).assign_to(res);
547}
548
549
550/// blackbox support - n-ary operations
551BOOLEAN pyobject_OpM(int op, leftv res, leftv args)
552{
553  typedef PythonCastStatic<PythonObject::sequence_tag> seq_type;
554
555  switch(op)                    // built-in return types first
556  {
557    case STRING_CMD:
558    {
559      blackbox* a = getBlackboxStuff(args->Typ());
560      res->data = (void *)a->blackbox_String(a, args->Data());
561      res->rtyp = STRING_CMD;
562      return FALSE;
563    }
564  }
565
566  typedef PythonCastStatic<PythonObject::sequence_tag> seq_type;
567  return PythonCastStatic<>(args)(op, seq_type(args->next)).assign_to(res);
568}
569
570/// blackbox support - destruction
571void pyobject_destroy(blackbox *b, void* ptr)
572{
573  Py_XDECREF(ptr);
574}
575
576PyObject* get_current_definition(const char* name) {
577  idhdl handle =  ggetid(name);
578  if (!handle || (IDTYP(handle) != PythonInterpreter::id()))  return NULL;
579  PythonCastStatic<PyObject*> value(IDDATA(handle));
580  return value;
581}
582
583/// getting stuff from python to Singular namespace
584void sync_contexts()
585{
586  PyRun_SimpleString("_SINGULAR_NEW = modules['__main__'].__dict__.copy()");
587
588  PythonObject newElts = python_eval("[(_k, _e)   \
589    for (_k, _e) in _SINGULAR_NEW.iteritems() \
590    if _k not in _SINGULAR_IMPORTED or not _SINGULAR_IMPORTED[_k] is _e]");
591
592  long len = newElts.size();
593  for (long idx = 0; idx < len; ++idx)
594  {
595    char* name = newElts[idx][0].str();
596    if (name && (*name != '\0') && (*name != '_'))
597    {
598      Py_XDECREF(get_current_definition(name));
599      newElts[idx][1].import_as(name);
600    }
601
602  }
603
604  PythonObject deletedElts = 
605    python_eval("list(set(_SINGULAR_IMPORTED.iterkeys()) - \
606     set(_SINGULAR_NEW.iterkeys()))");
607  len = deletedElts.size();
608
609  for (long idx = 0; idx < len; ++idx)
610  {
611    char* name = deletedElts[idx].str();
612    if (name && (*name != '\0') && (*name != '_'))
613      killid(name, &IDROOT);
614  }
615
616  PyRun_SimpleString("_SINGULAR_IMPORTED =_SINGULAR_NEW");
617  PyRun_SimpleString("del  _SINGULAR_NEW");
618}
619
620
621// forward declaration
622int iiAddCproc(char *libname, char *procname, BOOLEAN pstatic,
623               BOOLEAN(*func)(leftv res, leftv v));
624
625#define ADD_C_PROC(name) iiAddCproc("", (char*)#name, FALSE, name);
626
627
628void pyobject_init() 
629{
630  blackbox *b = (blackbox*)omAlloc0(sizeof(blackbox));
631  b->blackbox_destroy = pyobject_destroy;
632  b->blackbox_String  = pyobject_String;
633  b->blackbox_Init    = pyobject_Init;
634  b->blackbox_Copy    = pyobject_Copy;
635  b->blackbox_Assign  = pyobject_Assign;
636  b->blackbox_Op1     = pyobject_Op1;
637  b->blackbox_Op2     = pyobject_Op2;
638  b->blackbox_Op3     = pyobject_Op3;
639  b->blackbox_OpM     = pyobject_OpM;
640
641  PythonInterpreter::init(setBlackboxStuff(b,"pyobject"));
642
643  ADD_C_PROC(python_import);
644  ADD_C_PROC(python_eval);
645  ADD_C_PROC(python_run);
646}
647
648extern "C" { void mod_init() { pyobject_init(); } }
649
Note: See TracBrowser for help on using the repository browser.