source: git/Singular/dyn_modules/pyobject/pyobject.cc @ 4cb8c8

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