11import warnings
22import numpy as np
3+ from functools import partial
34from collections .abc import (
45 MutableSequence , MutableMapping , KeysView , ValuesView , ItemsView
56)
@@ -222,7 +223,7 @@ class MatlabType(object):
222223 """Generic type for objects that have an exact matlab equivalent."""
223224
224225 @classmethod
225- def from_any (cls , other ):
226+ def from_any (cls , other , ** kwargs ):
226227 """
227228 Convert python/matlab objects to `MatlabType` objects
228229 (`Cell`, `Struct`, `Array`, `MatlabClass`).
@@ -234,6 +235,9 @@ def from_any(cls, other):
234235 # - we do not convert to types that can be passed directly to
235236 # the matlab runtime;
236237 # - instead, we convert to python types that mimic matlab types.
238+ _from_any = partial (cls .from_any , ** kwargs )
239+ _from_runtime = kwargs .pop ("_from_runtime" , False )
240+
237241 if isinstance (other , MatlabType ):
238242 if isinstance (other , AnyDelayedArray ):
239243 other ._error_is_not_finalized ()
@@ -278,15 +282,25 @@ def from_any(cls, other):
278282 raise ValueError ("Don't know what to do with type" , type__ )
279283
280284 else :
285+ other = type (other )(
286+ zip (other .keys (),
287+ map (_from_any , other .values ()))
288+ )
281289 return Struct .from_any (other )
282290
283291 if isinstance (other , (list , tuple , set )):
284292 # nested tuples are cells of cells, not cell arrays
285- return Cell .from_any (other )
293+ if _from_runtime :
294+ return Cell ._from_runtime (other )
295+ else :
296+ return Cell .from_any (other )
286297
287298 if isinstance (other , (np .ndarray , int , float , complex , bool )):
288299 # [array of] numbers -> Array
289- return Array .from_any (other )
300+ if _from_runtime :
301+ return Array ._from_runtime (other )
302+ else :
303+ return Array .from_any (other )
290304
291305 if isinstance (other , str ):
292306 return other
@@ -305,17 +319,20 @@ def from_any(cls, other):
305319 return MatlabFunction .from_any (other )
306320
307321 if type (other ) in _matlab_array_types ():
308- dtype = _matlab_array_types ()[type (other )]
309- return Array .from_any (other , dtype = dtype )
322+ return Array ._from_runtime (other )
310323
311324 if hasattr (other , "__iter__" ):
312325 # Iterable -> let's try to make it a cell
313- return cls .from_any (list (other ))
326+ return cls .from_any (list (other ), _from_runtime = _from_runtime )
314327
315328 raise TypeError (
316329 f"Cannot convert { type (other )} into a matlab object."
317330 )
318331
332+ @classmethod
333+ def _from_runtime (cls , obj ):
334+ return cls .from_any (obj , _from_runtime = True )
335+
319336 @classmethod
320337 def _to_runtime (cls , obj ):
321338 """
@@ -1157,7 +1174,10 @@ def _as_runtime(self) -> np.ndarray:
11571174
11581175 @classmethod
11591176 def _from_runtime (cls , other ) -> "Array" :
1160- return cls .from_any (other )
1177+ other = np .asarray (other )
1178+ if len (other .shape ) == 2 and other .shape [0 ] == 1 :
1179+ other = other .squeeze (0 )
1180+ return np .ndarray .view (other , cls )
11611181
11621182 @classmethod
11631183 def from_shape (cls , shape = tuple (), ** kwargs ) -> "Array" :
@@ -1855,9 +1875,19 @@ def _as_runtime(self) -> dict:
18551875
18561876 @classmethod
18571877 def _from_runtime (cls , objdict : dict ) -> "Cell" :
1878+ if isinstance (objdict , (list , tuple , set )):
1879+ shape = [len (objdict )]
1880+ objdict = dict (type__ = 'cell' , size__ = shape , data__ = objdict )
1881+
18581882 if objdict ['type__' ] != 'cell' :
18591883 raise TypeError ('objdict is not a cell' )
1884+
18601885 size = np .array (objdict ['size__' ], dtype = np .uint64 ).ravel ()
1886+ if len (size ) == 2 and size [0 ] == 1 :
1887+ # NOTE: should not be needed for Cell, as this should
1888+ # have been taken care of by MPython, but I am keeping it
1889+ # here for symmetry with Array and Struct.
1890+ size = size [1 :]
18611891 data = np .fromiter (objdict ['data__' ], dtype = object )
18621892 data = data .reshape (size [::- 1 ]).transpose ()
18631893 try :
@@ -1874,7 +1904,7 @@ def _from_runtime(cls, objdict: dict) -> "Cell":
18741904 op_flags = ['readwrite' , 'no_broadcast' ])
18751905 with np .nditer (data , ** opt ) as iter :
18761906 for elem in iter :
1877- elem [()] = MatlabType .from_any (elem .item ())
1907+ elem [()] = MatlabType ._from_runtime (elem .item ())
18781908
18791909 return obj
18801910
@@ -2485,6 +2515,11 @@ def _from_runtime(cls, objdict: dict) -> "Struct":
24852515 if objdict ['type__' ] != 'structarray' :
24862516 raise TypeError ('objdict is not a structarray' )
24872517 size = np .array (objdict ['size__' ], dtype = np .uint64 ).ravel ()
2518+ if len (size ) == 2 and size [0 ] == 1 :
2519+ # NOTE: should not be needed for Cell, as this should
2520+ # have been taken care of by MPython, but I am keeping it
2521+ # here for symmetry with Array and Struct.
2522+ size = size [1 :]
24882523 data = np .array (objdict ['data__' ], dtype = object )
24892524 data = data .reshape (size )
24902525 try :
@@ -2495,7 +2530,17 @@ def _from_runtime(cls, objdict: dict) -> "Struct":
24952530 f' data={ data } \n '
24962531 f' objdict={ objdict } '
24972532 )
2498- return MatlabType .from_any (obj )
2533+
2534+ # recurse
2535+ opt = dict (flags = ['refs_ok' , 'zerosize_ok' ],
2536+ op_flags = ['readonly' , 'no_broadcast' ])
2537+ with np .nditer (data , ** opt ) as iter :
2538+ for elem in iter :
2539+ item = elem .item ()
2540+ for key , val in item .items ():
2541+ item [key ] = MatlabType ._from_runtime (val )
2542+
2543+ return obj
24992544
25002545 @classmethod
25012546 def from_shape (cls , shape = tuple (), ** kwargs ) -> "Struct" :
0 commit comments