Mercurial > pytave
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,