source: git/Singular/pyobject.cc @ a04a05

jengelh-datetimespielwiese
Last change on this file since a04a05 was a04a05, checked in by Alexander Dreyer <alexander.dreyer@…>, 11 years ago
chg: encapsulating newstruct's overloaded '=' and use in pyobject
  • Property mode set to 100644
File size: 17.2 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
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) { 
113    if (!ptr && handle_exception()) m_ptr = Py_None;
114  }
115
116  ptr_type check_context(ptr_type ptr) const {
117    if(ptr) sync_contexts(); 
118    return ptr;
119  }
120  /// Unary operations
121  self operator()(int op) const
122  {
123    switch(op)
124    {
125    case '(':  return check_context(PyObject_CallObject(*this, NULL));
126    case ATTRIB_CMD: return PyObject_Dir(*this);
127    case PROC_CMD: return *this;
128    }
129
130    if (op == PythonInterpreter::id())
131      return *this;
132
133    return self(NULL);
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    return self(NULL);
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: 
161        if(PyObject_SetAttr(*this, arg1, arg2) == -1) handle_exception();
162        return self();
163    }
164    return self(NULL);
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? (m_ptr == Py_None? none_to(result): python_to(result)): TRUE);
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  BOOLEAN handle_exception() const {
231
232    if(!PyErr_Occurred()) return FALSE;
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    return TRUE;
246  }
247
248  void append_iter(self iterator) {
249    ptr_type item;
250    while (item = PyIter_Next(iterator)) {
251      PyList_Append(*this, item);
252      Py_DECREF(item);
253    }
254  }
255
256  int py_opid(int op) const{
257    switch(op)
258    { 
259      case '<':  return Py_LT;
260      case '>':  return Py_GT;
261      case EQUAL_EQUAL:  return Py_EQ;
262      case NOTEQUAL:  return Py_NE;
263      case GE:  return Py_GE; 
264      case LE:  return Py_LE; 
265    }
266    return -1;
267  }
268
269private:
270  BOOLEAN none_to(leftv result) const
271  {
272    result->data = NULL;
273    result->rtyp = NONE;
274    return FALSE;
275  }
276
277  BOOLEAN python_to(leftv result) const
278  {
279    result->data = m_ptr;
280    Py_XINCREF(m_ptr);
281    result->rtyp = PythonInterpreter::id();
282    return !m_ptr;
283  }
284
285  /// The actual pointer
286  ptr_type m_ptr;
287};
288
289
290
291/** @class PythonCastStatic
292 * This template class does conversion of Singular objects to python objects on
293 * compile-time.
294 *
295 * @note The Singular objects are assumed to be equivalent to the template argument.
296 **/
297template <class CastType = PythonObject::ptr_type>
298class PythonCastStatic:
299  public PythonObject {
300public:
301
302  PythonCastStatic(void* value):
303    PythonObject(get(reinterpret_cast<CastType>(value))) {}
304
305  PythonCastStatic(leftv value):
306    PythonObject(get(reinterpret_cast<CastType>(value->Data()))) {}
307
308private:
309  ptr_type get(ptr_type value)       { return value; }
310  ptr_type get(long value)           { return PyInt_FromLong(value); }
311  ptr_type get(const char* value)    { return PyString_FromString(value); }
312  ptr_type get(char* value) { return get(const_cast<const char*>(value)); }
313  ptr_type get(lists value);         // inlined after PythonObjectDynamic
314};
315
316
317
318/** @class PythonCastDynamic
319 * This class does conversion of Singular objects to python objects on runtime.
320 *
321 **/
322class PythonCastDynamic:
323  public PythonObject {
324  typedef PythonCastDynamic self;
325
326public:
327  PythonCastDynamic(leftv value): PythonObject(get(value, value->Typ())) {}
328
329private:
330  PythonObject get(leftv value, int typeId)
331  {
332    if (typeId == PythonInterpreter::id()) return PythonCastStatic<>(value);
333   
334    switch (typeId)
335    {
336    case INT_CMD:    return PythonCastStatic<long>(value);
337    case STRING_CMD: return PythonCastStatic<const char*>(value);
338    case LIST_CMD:   return PythonCastStatic<lists>(value);
339    }
340
341    sleftv tmp;
342    BOOLEAN newstruct_equal(int, leftv, leftv); // declaring overloaded '='
343    if (!newstruct_equal(PythonInterpreter::id(), &tmp, value)) 
344      return PythonCastStatic<>(&tmp);       
345
346    if (typeId > MAX_TOK)       // custom types
347    {
348      blackbox *bbx = getBlackboxStuff(typeId);
349      assume(bbx != NULL);
350      if (! bbx->blackbox_Op1(PythonInterpreter::id(), &tmp, value))
351        return PythonCastStatic<>(&tmp);       
352    }
353
354    Werror("type '%s` incompatible with 'pyobject`", iiTwoOps(typeId));
355    return PythonObject();
356  }
357};
358
359template <class CastType>
360inline PythonObject::ptr_type
361PythonCastStatic<CastType>::get(lists value)
362{
363  ptr_type pylist(PyList_New(0));
364  for (int i = 0; i <= value->nr; ++i)
365    PyList_Append(pylist, PythonCastDynamic((value->m) + i));
366
367  return pylist;
368}
369
370/// Template specialization for getting handling sequence
371template <>
372class PythonCastStatic<PythonObject::sequence_tag>:
373public PythonObject
374{
375public:
376
377  PythonCastStatic(leftv value):
378    PythonObject(PyTuple_New(size(value)))  { append_to(value); }
379
380 
381private:
382  size_t size(leftv iter, size_t distance = 0) const 
383  {
384    if (iter) { do { ++distance; } while(iter = iter->next); }; 
385    return distance;
386  }
387 
388  void append_to(leftv iter) const
389  {
390    for(size_t idx = 0; iter != NULL; iter = iter->next)
391      PyTuple_SetItem(*this, idx++, PythonCastDynamic(iter));
392  }
393};
394
395
396PythonObject get_attrib_name(leftv arg)
397{
398  typedef PythonCastStatic<const char*> result_type;
399  if (arg->Typ() == STRING_CMD)
400    return result_type(arg);
401
402  return result_type((void*)arg->Name());
403}
404
405/// Evaluate string in python
406PythonObject python_eval(const char* arg) {
407
408  PyObject* globals = PyModule_GetDict(PyImport_Import(PyString_FromString("__main__")));
409  return PyRun_String(arg, Py_eval_input, globals, globals);
410}
411
412/// Evaluate string in python from Singular
413BOOLEAN python_eval(leftv result, leftv arg) {
414  if ( !arg || (arg->Typ() != STRING_CMD) ) {
415    Werror("expected python_eval('string')");
416    return TRUE;
417  }
418
419  return python_eval(reinterpret_cast<const char*>(arg->Data())).assign_to(result);
420}
421
422
423/// Execute string in python from Singular
424BOOLEAN python_run(leftv result, leftv arg)
425{
426  if ( !arg || (arg->Typ() != STRING_CMD) ) {
427    Werror("expected python_run('string')");
428    return TRUE;
429  }
430
431  PyRun_SimpleString(reinterpret_cast<const char*>(arg->Data()));
432  sync_contexts();
433  return PythonCastStatic<>(Py_None).assign_to(result);
434}
435
436PythonObject names_from_module(const char* module_name)
437{
438  char buffer[strlen(module_name) + 30];
439  sprintf (buffer, "SINGULAR_MODULE_NAME = '%s'", module_name);
440  PyRun_SimpleString(buffer);
441  PyRun_SimpleString("from sys import modules");
442  PyRun_SimpleString("exec('from ' + SINGULAR_MODULE_NAME + ' import *')");
443
444  return python_eval("[str for str in dir(modules[SINGULAR_MODULE_NAME]) if str[0] != '_']");
445}
446
447void from_module_import_all(const char* module_name)
448{
449  char buffer[strlen(module_name) + 20];
450  sprintf (buffer, "from %s import *", module_name);
451  PyRun_SimpleString(buffer);
452}
453
454/// import python module and export identifiers in Singular namespace
455BOOLEAN python_import(leftv result, leftv value) {
456
457  if ((value == NULL) || (value->Typ()!= STRING_CMD)) {
458    Werror("expected python_import('string')");
459    return TRUE;
460  }
461
462  from_module_import_all(reinterpret_cast<const char*>(value->Data()));
463  sync_contexts();
464
465  return PythonCastStatic<>(Py_None).assign_to(result);
466}
467
468/// blackbox support - initialization
469void* pyobject_Init(blackbox*)
470{
471  return Py_None;
472}
473
474/// blackbox support - convert to string representation
475char* pyobject_String(blackbox *b, void* ptr)
476{
477  return PythonCastStatic<>(ptr).repr();
478}
479
480/// blackbox support - copy element
481void* pyobject_Copy(blackbox*b, void* ptr)
482{ 
483    Py_XINCREF(ptr);
484    return ptr;
485}
486
487/// blackbox support - assign element
488BOOLEAN pyobject_Assign(leftv l, leftv r)
489{
490  Py_XDECREF(l->Data());
491  PyObject* result = PythonCastDynamic(r);
492  Py_XINCREF(result);
493
494  if (l->rtyp == IDHDL)
495    IDDATA((idhdl)l->data) = (char *)result;
496  else
497    l->data = (void *)result;
498 
499  return !result;
500}
501                                                                     
502
503/// blackbox support - unary operations
504BOOLEAN pyobject_Op1(int op, leftv res, leftv head)
505{
506  switch(op)
507  {
508    case INT_CMD:               // built-in return types first
509    {
510      long value = PyInt_AsLong(PythonCastStatic<>(head));
511      if( (value == -1) &&  PyErr_Occurred() ) {
512        Werror("'pyobject` cannot be converted to integer");
513        PyErr_Clear();
514        return TRUE;
515      }
516      res->data = (void*) value;
517      res->rtyp = INT_CMD;
518      return FALSE;
519    }
520    case TYPEOF_CMD:
521      res->data = (void*) omStrDup("pyobject");
522      res->rtyp = STRING_CMD; 
523      return FALSE; 
524  }
525
526  if (!PythonCastStatic<>(head)(op).assign_to(res))
527    return FALSE;
528
529  BOOLEAN newstruct_Op1(int, leftv, leftv); // forward declaration
530  return newstruct_Op1(op, res, head);
531}
532
533
534/// blackbox support - binary operations
535BOOLEAN pyobject_Op2(int op, leftv res, leftv arg1, leftv arg2)
536{
537  PythonCastStatic<> lhs(arg1);
538
539  switch(op)                    // built-in return types and special cases first
540  { 
541    case '<': case '>': case EQUAL_EQUAL: case NOTEQUAL: case GE: case LE:
542    {
543      res->data = (void *)lhs.compare(op, PythonCastDynamic(arg2));
544      res->rtyp = INT_CMD;
545      return FALSE;
546    }
547    case '.': case COLONCOLON: case ATTRIB_CMD:
548      return lhs.attr(get_attrib_name(arg2)).assign_to(res);
549  }
550
551  PythonCastDynamic rhs(arg2);
552  if (!lhs(op, rhs).assign_to(res))
553    return FALSE;
554
555  BOOLEAN newstruct_Op2(int, leftv, leftv, leftv); // forward declaration
556  return newstruct_Op2(op, res, arg1, arg2);
557
558}
559
560/// blackbox support - ternary operations
561BOOLEAN pyobject_Op3(int op, leftv res, leftv arg1, leftv arg2, leftv arg3)
562{
563  PythonCastStatic<> lhs(arg1);
564  PythonCastDynamic rhs1(arg2);
565  PythonCastDynamic rhs2(arg3);
566
567  if (!lhs(op, rhs1, rhs2).assign_to(res))
568    return FALSE;
569
570  return blackboxDefaultOp3(op, res, arg1, arg2, arg3);
571}
572
573
574/// blackbox support - n-ary operations
575BOOLEAN pyobject_OpM(int op, leftv res, leftv args)
576{
577  typedef PythonCastStatic<PythonObject::sequence_tag> seq_type;
578
579  switch(op)                    // built-in return types first
580  {
581    case STRING_CMD:
582    {
583      blackbox* a = getBlackboxStuff(args->Typ());
584      res->data = (void *)a->blackbox_String(a, args->Data());
585      res->rtyp = STRING_CMD;
586      return FALSE;
587    }
588  }
589
590  typedef PythonCastStatic<PythonObject::sequence_tag> seq_type;
591  if (! PythonCastStatic<>(args)(op, seq_type(args->next)).assign_to(res))
592    return FALSE;
593
594  BOOLEAN newstruct_OpM(int, leftv, leftv); // forward declaration
595  return newstruct_OpM(op, res, args);
596}
597
598/// blackbox support - destruction
599void pyobject_destroy(blackbox *b, void* ptr)
600{
601  Py_XDECREF(ptr);
602}
603
604PyObject* get_current_definition(const char* name) {
605  idhdl handle =  ggetid(name);
606  if (!handle || (IDTYP(handle) != PythonInterpreter::id()))  return NULL;
607  PythonCastStatic<PyObject*> value(IDDATA(handle));
608  return value;
609}
610
611/// getting stuff from python to Singular namespace
612void sync_contexts()
613{
614  PyRun_SimpleString("_SINGULAR_NEW = modules['__main__'].__dict__.copy()");
615
616  PythonObject newElts = python_eval("[(_k, _e)   \
617    for (_k, _e) in _SINGULAR_NEW.iteritems() \
618    if _k not in _SINGULAR_IMPORTED or not _SINGULAR_IMPORTED[_k] is _e]");
619
620  long len = newElts.size();
621  for (long idx = 0; idx < len; ++idx)
622  {
623    char* name = newElts[idx][0].str();
624    if (name && (*name != '\0') && (*name != '_'))
625    {
626      Py_XDECREF(get_current_definition(name));
627      newElts[idx][1].import_as(name);
628    }
629
630  }
631
632  PythonObject deletedElts = 
633    python_eval("list(set(_SINGULAR_IMPORTED.iterkeys()) - \
634     set(_SINGULAR_NEW.iterkeys()))");
635  len = deletedElts.size();
636
637  for (long idx = 0; idx < len; ++idx)
638  {
639    char* name = deletedElts[idx].str();
640    if (name && (*name != '\0') && (*name != '_'))
641      killid(name, &IDROOT);
642  }
643
644  PyRun_SimpleString("_SINGULAR_IMPORTED =_SINGULAR_NEW");
645  PyRun_SimpleString("del  _SINGULAR_NEW");
646}
647
648
649// forward declaration
650int iiAddCproc(char *libname, char *procname, BOOLEAN pstatic,
651               BOOLEAN(*func)(leftv res, leftv v));
652
653#define ADD_C_PROC(name) iiAddCproc("", (char*)#name, FALSE, name);
654
655
656void pyobject_init() 
657{
658  blackbox *b = (blackbox*)omAlloc0(sizeof(blackbox));
659  b->blackbox_destroy = pyobject_destroy;
660  b->blackbox_String  = pyobject_String;
661  b->blackbox_Init    = pyobject_Init;
662  b->blackbox_Copy    = pyobject_Copy;
663  b->blackbox_Assign  = pyobject_Assign;
664  b->blackbox_Op1     = pyobject_Op1;
665  b->blackbox_Op2     = pyobject_Op2;
666  b->blackbox_Op3     = pyobject_Op3;
667  b->blackbox_OpM     = pyobject_OpM;
668  b->data             = omAlloc0(newstruct_desc_size());
669
670  PythonInterpreter::init(setBlackboxStuff(b,"pyobject"));
671
672  ADD_C_PROC(python_import);
673  ADD_C_PROC(python_eval);
674  ADD_C_PROC(python_run);
675}
676
677extern "C" { void mod_init() { pyobject_init(); } }
678
Note: See TracBrowser for help on using the repository browser.