changeset 202:3fa99babc7b5

pyeval: preliminary support for returning pyobj Introduces static method `pyobj.fromPythonVarName(s)`, whereas the constructor itself now no longer deals with `__InOct__` itself. * pyeval.cc: return the id() instead for now * @pyobj/pyobj.m: change constructor, add method * @pyobj/dummy.m: add doctests
author Colin Macdonald <cbm@m.fsf.org>
date Fri, 20 May 2016 00:20:03 -0700
parents b3c23054a9c2
children 7d03df51d6e8
files @pyobj/dummy.m @pyobj/pyobj.m pyeval.cc
diffstat 3 files changed, 70 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/@pyobj/dummy.m	Thu May 19 17:08:03 2016 -0700
+++ b/@pyobj/dummy.m	Fri May 20 00:20:03 2016 -0700
@@ -26,7 +26,7 @@
 %% @example
 %% @group
 %% pyexec('d = dict(one=1, two=2)')
-%% x = pyobj('d')
+%% x = pyobj.fromPythonVarName('d')
 %%   @result{} x =
 %%       [PyObject id ...]
 %%       @{'two': 2, 'one': 1@}
@@ -42,6 +42,39 @@
 %% @end group
 %% @end example
 %%
+%% @code{pyeval} should return a @@pyobj for things it cannot convert to
+%% Octave-native objects:
+%% @example
+%% @group
+%% pyexec('import sys')             % doctest: +XFAIL
+%% sysmodule = pyeval('sys')
+%%   @result{} sysmodule =
+%%       [PyObject id ...]
+%%       <module 'sys' (built-in)>
+%% @end group
+%% @end example
+%%
+%% But it doesn't work yet, for now you have to do:
+%% @example
+%% @group
+%% pyexec('import sys')
+%% key = pyeval('sys')
+%%   @result{} key = ...
+%% sysmodule = pyobj(key)
+%%   @result{} sysmodule =
+%%       [PyObject id ...]
+%%       <module 'sys' (built-in)>
+%% @end group
+%% @end example
+%%
+%% After you have the object, you can access its properties:
+%% @example
+%% @group
+%% sysmodule.version
+%%   @result{} ans = ...
+%% @end group
+%% @end example
+%%
 %% @seealso{pyobj}
 %% @end defmethod
 
--- a/@pyobj/pyobj.m	Thu May 19 17:08:03 2016 -0700
+++ b/@pyobj/pyobj.m	Fri May 20 00:20:03 2016 -0700
@@ -53,9 +53,9 @@
   properties
     id
   end
-  methods
 
-    function x = pyobj(pyvarname)
+  methods(Static)
+    function x = fromPythonVarName(pyvarname)
       % if @var{pyvarname} is a string, its assumed to be a variable
       % name, e.g., previously created with pyexec.  This must exist
       % at the time of construction but it can disappear later (we
@@ -63,16 +63,23 @@
       if (~ ischar(pyvarname))
         error('pyobj: currently we only take variable names as input')
       end
-      % FIXME: check __InOct__ exists
-      % FIXME: ensure id is not in the dict
       cmd = sprintf ([ ...
         'if not ("__InOct__" in vars() or "__InOct__" in globals()):\n' ...
         '  __InOct__ = dict()\n' ...
         '__InOct__[hex(id(%s))] = %s' ], ...
         pyvarname, pyvarname);
       pyexec (cmd);
-      x.id = pyeval (['hex(id(' pyvarname '))']);
-      %x.repr = pyeval (['repr(' x.varname ')']);
+      id = pyeval (['hex(id(' pyvarname '))']);
+      x = pyobj(id);
+    end
+  end
+
+
+  methods
+    function x = pyobj(id)
+      % warning: not intended for casual use: you must also insert
+      % the object into the Python `__InOct__` dict with key `id`.
+      x.id = id;
     end
 
     function delete(x)
@@ -100,6 +107,8 @@
       delete(x)
     end
 
+    dummy (x)
+
     function r = getid (x)
       r = x.id;
     end
--- a/pyeval.cc	Thu May 19 17:08:03 2016 -0700
+++ b/pyeval.cc	Fri May 20 00:20:03 2016 -0700
@@ -63,13 +63,22 @@
 
   std::string code = args(0).string_value ();
 
+  std::string id;
+  object res;
+
   Py_Initialize ();
 
+  object main_module = import ("__main__");
+  object main_namespace = main_module.attr ("__dict__");
+
   try
     {
-      object main_module = import ("__main__");
-      object main_namespace = main_module.attr ("__dict__");
-      object res = eval (code.c_str (), main_namespace, main_namespace);
+      res = eval (code.c_str (), main_namespace, main_namespace);
+      object builtins = main_module.attr ("__builtins__");
+      // hex(id(res))
+      object idtmp = builtins.attr("hex")(builtins.attr("id")(res));
+      id = extract<std::string> (idtmp);
+      //std::cerr << "got it: " << id << std::endl;
 
       // FIXME: currently, we cannot return the raw object to octave...
       if (! res.is_none ())
@@ -81,7 +90,15 @@
     }
   catch (pytave::object_convert_exception const &)
     {
-      error ("pyeval: error in return value type conversion");
+      printf ("pyeval: could not convert return value to Octave-native object, making pyobj...\n");
+      // Ensure we have a __InOct__ dict, and then put `res` into it
+      exec ("if not (\"__InOct__\" in vars() or \"__InOct__\" in globals()):\n"
+            "  __InOct__ = dict()\n",
+            main_namespace, main_namespace);
+      main_namespace["__InOct__"][id] = res;
+      //retval(0) = pyobj(id);
+      // FIXME: how to do the above?  For now, just return the string
+      retval(0) = id;
     }
   catch (error_already_set const &)
     {