changeset 209:57807b5fa7bf

Change name of pyobj to pyobject
author genuinelucifer
date Thu, 26 May 2016 11:35:20 -0700
parents 4a369c94cab8
children 04f786b778fc
files @pyobject/dummy.m @pyobject/pyobject.m Makefile.am python_to_octave.cc
diffstat 4 files changed, 262 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/@pyobject/dummy.m	Thu May 26 11:35:20 2016 -0700
@@ -0,0 +1,124 @@
+%% Copyright (C) 2016 Colin B. Macdonald
+%%
+%% This file is part of PyTave.
+%%
+%% OctSymPy is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU General Public License as published
+%% by the Free Software Foundation; either version 3 of the License,
+%% or (at your option) any later version.
+%%
+%% This software is distributed in the hope that it will be useful,
+%% but WITHOUT ANY WARRANTY; without even the implied warranty
+%% of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+%% the GNU General Public License for more details.
+%%
+%% You should have received a copy of the GNU General Public
+%% License along with this software; see the file COPYING.
+%% If not, see <http://www.gnu.org/licenses/>.
+
+%% -*- texinfo -*-
+%% @documentencoding UTF-8
+%% @defmethod @@pyobject dummy (@var{x})
+%% Does nothing, stores doctests for now.
+%%
+%%
+%% Simple example:
+%% @example
+%% @group
+%% pyexec('g = 6')
+%% g = pyobject.fromPythonVarName('g');
+%%
+%% sort(whatmethods(g))
+%%   @result{} ans =
+%%     @{
+%%       [1,1] = bit_length
+%%       [1,2] = conjugate
+%%       [1,1] = denominator
+%%       [1,2] = imag
+%%       [1,3] = numerator
+%%       [1,4] = real
+%%      @}
+%%
+%% g.numerator
+%%   @result{} ans =  6
+%% g.denominator
+%%   @result{} ans =  1
+%% @end group
+%% @end example
+%%
+%%
+%% You can delete an object in Python and it will persist:
+%% @example
+%% @group
+%% pyexec('d = dict(one=1, two=2)')
+%% x = pyobject.fromPythonVarName('d')
+%%   @result{} x =
+%%       [PyObject id ...]
+%%       @{'two': 2, 'one': 1@}
+%%
+%% % oops, overwrote d in Python:
+%% pyexec('d = 42')
+%%
+%% % but have no fear, we still have a reference to it:
+%% x
+%%   @result{} x =
+%%       [PyObject id ...]
+%%       @{'two': 2, 'one': 1@}
+%% @end group
+%% @end example
+%%
+%% We can accesss ``callables'' (methods) of objects:
+%% @example
+%% @group
+%% x.keys()
+%%   @result{} ans =
+%%       @{
+%%         [1,1] = two
+%%         [1,2] = one
+%%       @}
+%% @end group
+%% @end example
+%%
+%% @code{pyeval} returns a @@pyobject for things it cannot convert to
+%% Octave-native objects:
+%% @example
+%% @group
+%% pyexec('import sys')
+%% sysmodule = pyeval('sys')
+%%   @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
+%%
+%%
+%% TODO: this should return a cell array with a double, a string,
+%% and an @@pyobject in it:
+%% @example
+%% @group
+%% pyeval('[42, "hello", sys]')         % doctest: +XFAIL
+%%   @result{} ans =
+%%       @{
+%%         [1,1] =  42
+%%         [1,2] = hello
+%%         [1,3] =
+%%           [PyObject id ...]
+%%           <module 'sys' (built-in)>
+%%       @}
+%% @end group
+%% @end example
+%%
+%% @seealso{pyobject}
+%% @end defmethod
+
+function dummy (x)
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/@pyobject/pyobject.m	Thu May 26 11:35:20 2016 -0700
@@ -0,0 +1,135 @@
+%% Copyright (C) 2016 Colin B. Macdonald
+%%
+%% This file is part of PyTave.
+%%
+%% OctSymPy is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU General Public License as published
+%% by the Free Software Foundation; either version 3 of the License,
+%% or (at your option) any later version.
+%%
+%% This software is distributed in the hope that it will be useful,
+%% but WITHOUT ANY WARRANTY; without even the implied warranty
+%% of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+%% the GNU General Public License for more details.
+%%
+%% You should have received a copy of the GNU General Public
+%% License along with this software; see the file COPYING.
+%% If not, see <http://www.gnu.org/licenses/>.
+
+%% -*- texinfo -*-
+%% @documentencoding UTF-8
+%% @defun  pyobject (@var{s})
+%% Wrap a Python object.
+%%
+%% TODO: where/how to document classdef classes?
+%%
+%% @seealso{pyexec, pyeval}
+%% @end defun
+
+classdef pyobject < handle
+  properties
+    id
+  end
+
+  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
+      % will keep track of the reference).
+      if (~ ischar(pyvarname))
+        error('pyobject: currently we only take variable names as input')
+      end
+      cmd = sprintf ([ ...
+        'if not ("__InOct__" in vars() or "__InOct__" in globals()):\n' ...
+        '  __InOct__ = dict()\n' ...
+        '__InOct__[hex(id(%s))] = %s' ], ...
+        pyvarname, pyvarname);
+      pyexec (cmd);
+      id = pyeval (['hex(id(' pyvarname '))']);
+      x = pyobject(id);
+    end
+  end
+
+
+  methods
+    function x = pyobject(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)
+      % called on clear of the last reference---for subclasses of
+      % handle; not called at all for "value classes".
+      % FIXME: #46497 this is never called!
+      %save('proof_of_delete', 6, x.id)
+      disp ('delete')
+      % throws KeyError if it wasn't in there for some reason
+      cmd = sprintf ('__InOct__.pop("%s")', x.id);
+      pyexec (cmd);
+    end
+
+    function force_delete (x)
+      % Manual workaround for #46497: call right before @code{clear x}.  But
+      % be careful, @code{x} needs to be the last reference: don't do this:
+      % @example
+      % d = pyobject (...);
+      % d2 = d;
+      % force_delete (d)
+      % clear d
+      % d2
+      %   @print{} ... KeyError ...
+      % @end example
+      delete(x)
+    end
+
+    dummy (x)
+
+    function r = getid (x)
+      r = x.id;
+    end
+
+    function disp(x)
+      printf ('[PyObject id %s]\n', x.id);
+      disp (pyeval (sprintf ('str(__InOct__["%s"])', x.id)))
+    end
+
+    function s = whatclass(x)
+      s = pyeval (sprintf ('str(__InOct__["%s"].__class__)', x.id));
+    end
+
+    function lst = whatmethods(x)
+      % filter the output of `dir(x)`
+      % properties only:
+      % [a for a in dir(x) if not callable(getattr(x, a)) and not a.startswith("__")]
+      cmd = sprintf ( ...
+        '[a for a in dir(__InOct__["%s"]) if not a.startswith("__")]', ...
+        x.id);
+      lst = pyeval (cmd);
+    end
+
+    function r = subsref(x, idx)
+      s = '';
+      for i=1:length(idx)
+        t = idx(i);
+        switch t.type
+          case '()'
+            if ( ! isempty (t.subs))
+              t
+              error('not implemented: function calls with arguments')
+            end
+            s = sprintf ('%s()', s);
+          case '.'
+            assert(ischar(t.subs))
+            s = sprintf ('%s.%s', s, t.subs);
+          otherwise
+            t
+            error('not implemented')
+        end
+      end
+      r = pyeval (sprintf ('__InOct__["%s"]%s', x.id, s));
+    end
+
+  end
+end
--- a/Makefile.am	Fri May 20 23:18:46 2016 -0700
+++ b/Makefile.am	Thu May 26 11:35:20 2016 -0700
@@ -38,7 +38,7 @@
   pyeval.oct \
   pyexec.oct
 
-OCT_SOURCES = $(patsubst %.oct, %.cc, $(OCT_FILES))
+ssources = $(patsubst %.oct, %.cc, $(OCT_FILES))
 
 PYTAVE_HEADER_FILES = \
   arrayobjectdefs.h \
@@ -53,7 +53,7 @@
   test/exceptions.py \
   test/test.py
 
-EXTRA_DIST = $(DOC_FILES) $(OCT_SOURCES) $(PY_FILES)
+EXTRA_DIST = $(DOC_FILES) $(ssources) $(PY_FILES)
 
 EXTRA_libdir = $(PYTAVE_MODULE_INSTALL_PATH)
 EXTRA_lib_LTLIBRARIES = _pytave.la
--- a/python_to_octave.cc	Fri May 20 23:18:46 2016 -0700
+++ b/python_to_octave.cc	Thu May 26 11:35:20 2016 -0700
@@ -477,6 +477,7 @@
         + (PyEval_GetFuncDesc (py_object.ptr ())
         + std::string (": Unsupported Python object type, "
                        "cannot convert to Octave value")));
+    
   }
 
   void pytuple_to_octlist (octave_value_list& octave_list,