Mercurial > pytave
view @pyobject/pyobject.m @ 295:116edbde7329
pyobject: Add overloaded ndims
By default, ndims doesn't respect the output of size. IIRC, there
is a related upstream Octave bug about this, forget which one.
* @pyobject/pyobject.m (@pyobject/ndims): New method.
author | Colin Macdonald <cbm@m.fsf.org> |
---|---|
date | Sun, 31 Jul 2016 11:06:12 -0700 |
parents | c7e1cb518372 |
children | fadb4475e75d |
line wrap: on
line source
## Copyright (C) 2016 Colin B. Macdonald ## ## This file is part of Pytave ## ## Pytave 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 endproperties methods (Static) function x = fromPythonVarName (pyvarname) # Warning: just for testing, may be removed without notice! # 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") endif cmd = sprintf ([ ... 'if not ("__InOct__" in vars() or "__InOct__" in globals()):\n' ... ' __InOct__ = dict()\n' ... ' # FIXME: make it accessible elsewhere?\n' ... ' import __main__\n' ... ' __main__.__InOct__ = __InOct__\n' ... '__InOct__[hex(id(%s))] = %s' ], ... pyvarname, pyvarname); pyexec (cmd); id = pyeval (["hex(id(" pyvarname "))"]); x = pyobject (0, id); endfunction endmethods methods function obj = pyobject (x, id) if (nargin == 0) obj = pyeval ("None"); return endif if (nargin == 1) ## Convert the input to a pyobject if (isa (x, "pyobject")) obj = x; else ## XXX: perhaps not the ideal implementation! vars = pyeval ("__import__('__main__').__dict__"); ## this is vars{"_temp"} = x idx = struct ("type", "{}", "subs", {{"_temp"}}); vars = subsasgn (vars, idx, x); cmd = [ ... "if not ('__InOct__' in vars() or '__InOct__' in globals()):\n" ... " __InOct__ = dict()\n" ... " # FIXME: make it accessible elsewhere?\n" ... " import __main__\n" ... " __main__.__InOct__ = __InOct__\n" ... "__InOct__[hex(id(_temp))] = _temp" ]; pyexec (cmd); id = pyeval (["hex(id(_temp))"]); obj = pyobject (0, id); endif return endif if (nargin == 2) ## The actual constructor. Nicer to split this off to static method ## like `pyobject.new` but I don't know how to call from pycall.cc. ## Warning: not intended for casual use: you must also insert the ## object into the Python `__InOct__` dict with key `id`. obj.id = id; return endif error ("pyobject: unexpected input to the constructor") endfunction 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! # Workaround: call @code{delete(x)} 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; # delete (d) # clear d # d2 # @print{} ... KeyError ... # @end example #disp ("delete") # throws KeyError if it wasn't in there for some reason cmd = sprintf ("__InOct__.pop('%s')", x.id); pyexec (cmd) endfunction # methods defined in external files dummy (x) display (x) subsref (x, idx) function r = getid (x) r = x.id; endfunction function s = char (x) s = pycall ("str", x); endfunction function varargout = disp (x) s = char (x); if (nargout == 0) disp (s) else varargout = {s}; endif endfunction function s = class (x) idx = struct ("type", ".", "subs", "__class__"); class_ref = subsref (x, idx); idx = struct ("type", ".", "subs", "__name__"); s = subsref (class_ref, idx); s = sprintf ("py.%s", char (s)); endfunction function vargout = help (x) idx = struct ("type", ".", "subs", "__doc__"); s = subsref (x, idx); if (nargout == 0) disp (s) else vargout = {s}; endif endfunction function len = length (x) try len = pycall ("len", x); catch len = 1; end_try_catch endfunction function sz = size (x, d) assert (nargin <= 2) try idx = struct ("type", ".", "subs", "shape"); sz = subsref (x, idx); sz = cell2mat (cell (sz)); catch ## if it had no shape, make it a row vector n = length (x); sz = [1 n]; end_try_catch if (nargin > 1) if (d > length (sz)) ## standard Octave behaviour: do we really want this? sz = 1; else sz = sz(d); endif endif endfunction function n = numel (x) assert (nargin == 1) sz = size (x); n = prod (sz); endfunction function n = ndims (x) assert (nargin == 1) n = length (size (x)); endfunction function r = end (x, index_pos, num_indices) assert (nargin == 3) assert (isscalar (index_pos)) if (num_indices == 1) r = numel (x); else r = size (x, index_pos); endif endfunction endmethods endclassdef %!test %! pyexec ("import sys") %! A = pyeval ("sys"); %! assert (isa (A, "pyobject")) %!test %! pyobj = pyeval ("{1:2, 2:3, 3:4}"); %! assert (isa (pyobj, "pyobject")) %! assert (length (pyobj), 3) %!test %! pyexec ("import sys"); %! pyobj = pyeval ("sys"); %! assert (length (pyobj), 1) %!assert (size (pyeval ("[10, 20, 30]")), [1 3]) %!assert (size (pyeval ("[10, 20, 30]"), 1), 1) %!assert (size (pyeval ("[10, 20, 30]"), 2), 3) %!assert (size (pyeval ("[10, 20, 30]"), 3), 1) %!assert (numel (pyeval ("[10, 20, 30]")), 3) %!test %! L = pyeval ("[10, 20, 30]"); %! assert (L{end}, 30) %! assert (L{end-1}, 20) %!test %! myrange = pyeval ( ... %! "range if __import__('sys').hexversion >= 0x03000000 else xrange"); %! R = pycall (myrange, int32 (5), int32 (10), int32 (2)); %! assert (R{end}, 9) %!shared a %! pyexec ("class _myclass(): shape = (3, 4, 5)") %! a = pyeval ("_myclass()"); %!assert (size (a), [3 4 5]) %!assert (size (a, 3), 5) %!assert (numel (a), 60) %!assert (ndims (a), 3) %!shared %!assert (char (pyeval ("None")), "None") %!assert (char (pyeval ("'this is a string'")), "this is a string") %!assert (char (pyeval ("[1, 2, 3, 4, 5]")), "[1, 2, 3, 4, 5]") %!assert (char (pyeval ("(1, 2, 3, 4, 5)")), "(1, 2, 3, 4, 5)") %!assert (char (pyeval ("__import__('sys')")), "<module 'sys' (built-in)>") %!assert (isa (pyobject (), "pyobject")) %!assert (isa (pyobject ("a string"), "pyobject")) %!assert (isa (pyobject (42.2), "pyobject")) %!assert (isa (pyobject (int32 (42)), "pyobject")) %!assert (isa (pyobject (pyobject ()), "pyobject")) %!assert (class (pyeval ("{}")), "py.dict") %!assert (class (pyeval ("[]")), "py.list") %!assert (class (pyeval ("()")), "py.tuple") %!assert (class (pyeval ("set()")), "py.set") %!assert (class (pyeval ("None")), "py.NoneType") %!assert (class (pyeval ("2.5")), "double")