source: git/Singular/pyobject.cc @ 92d684

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