source: git/Singular/pyobject.cc @ 762407

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