Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mpython/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "25.04alpha3"
__version__ = "25.04rc1"
10 changes: 7 additions & 3 deletions mpython/array.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import numpy as np

from .core import WrappedArray, _ListishMixin
from .utils import _copy_if_needed
from .utils import _copy_if_needed, DelayedImport


class _imports(DelayedImport):
Cell = 'mpython.cell.Cell'


class Array(_ListishMixin, WrappedArray):
Expand Down Expand Up @@ -48,7 +52,7 @@ def _as_runtime(self) -> np.ndarray:
return np.ndarray.view(self, np.ndarray)

@classmethod
def _from_runtime(cls, other) -> "Array":
def _from_runtime(cls, other, runtime=None) -> "Array":
other = np.asarray(other)
if len(other.shape) == 2 and other.shape[0] == 1:
other = other.squeeze(0)
Expand Down Expand Up @@ -176,7 +180,7 @@ def from_cell(cls, other, **kwargs) -> "Array":
array : Array
Converted array.
"""
from .cell import Cell # FIXME: avoid circular import
Cell = _imports.Cell

if not isinstance(other, Cell):
raise TypeError(f"Expected a {Cell} but got a {type(other)}")
Expand Down
34 changes: 18 additions & 16 deletions mpython/cell.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import numpy as np

from .core import AnyDelayedArray, DelayedCell, MatlabType, WrappedArray, _ListMixin
from .utils import _copy_if_needed, _empty_array, _import_matlab, _matlab_array_types

global matlab
from .core import (
AnyDelayedArray, DelayedCell, MatlabType, WrappedArray, _ListMixin
)
from .utils import _copy_if_needed, _empty_array, _matlab_array_types


class Cell(_ListMixin, WrappedArray):
Expand Down Expand Up @@ -67,7 +67,8 @@ class Cell(_ListMixin, WrappedArray):
def _DEFAULT(cls, shape: list = ()) -> np.ndarray:
data = np.empty(shape, dtype=object)
opt = dict(
flags=["refs_ok", "zerosize_ok"], op_flags=["writeonly", "no_broadcast"]
flags=["refs_ok", "zerosize_ok"],
op_flags=["writeonly", "no_broadcast"]
)
with np.nditer(data, **opt) as iter:
for elem in iter:
Expand All @@ -77,7 +78,8 @@ def _DEFAULT(cls, shape: list = ()) -> np.ndarray:
def _fill_default(self):
arr = np.ndarray.view(self, np.ndarray)
opt = dict(
flags=["refs_ok", "zerosize_ok"], op_flags=["writeonly", "no_broadcast"]
flags=["refs_ok", "zerosize_ok"],
op_flags=["writeonly", "no_broadcast"]
)
with np.nditer(arr, **opt) as iter:
for elem in iter:
Expand Down Expand Up @@ -106,7 +108,7 @@ def _as_runtime(self) -> dict:
return dict(type__="cell", size__=size, data__=data)

@classmethod
def _from_runtime(cls, objdict: dict) -> "Cell":
def _from_runtime(cls, objdict: dict, runtime=None) -> "Cell":
if isinstance(objdict, (list, tuple, set)):
shape = [len(objdict)]
objdict = dict(type__="cell", size__=shape, data__=objdict)
Expand All @@ -126,16 +128,18 @@ def _from_runtime(cls, objdict: dict) -> "Cell":
obj = data.view(cls)
except Exception:
raise RuntimeError(
f"Failed to construct Cell data:\n data={data}\n objdict={objdict}"
f"Failed to construct Cell data:\n"
f" data={data}\n objdict={objdict}"
)

# recurse
opt = dict(
flags=["refs_ok", "zerosize_ok"], op_flags=["readwrite", "no_broadcast"]
flags=["refs_ok", "zerosize_ok"],
op_flags=["readwrite", "no_broadcast"]
)
with np.nditer(data, **opt) as iter:
for elem in iter:
elem[()] = MatlabType._from_runtime(elem.item())
elem[()] = MatlabType._from_runtime(elem.item(), runtime)

return obj

Expand Down Expand Up @@ -217,10 +221,6 @@ def from_any(cls, other, **kwargs) -> "Cell":

# recursive shallow conversion
if not deepcat:
# make sure matlab is imported so that we can detect
# matlab arrays.
_import_matlab()

# This is so list[list] are converted to Cell[Cell] and
# not to a 2D Cell array.
def asrecursive(other):
Expand Down Expand Up @@ -266,7 +266,8 @@ def asrecursive(other):

# recurse
opt = dict(
flags=["refs_ok", "zerosize_ok"], op_flags=["readwrite", "no_broadcast"]
flags=["refs_ok", "zerosize_ok"],
op_flags=["readwrite", "no_broadcast"]
)
with np.nditer(other, **opt) as iter:
for elem in iter:
Expand All @@ -286,7 +287,8 @@ def _unroll_build(cls, arr):
rebuild = False
arr = np.asarray(arr)
opt = dict(
flags=["refs_ok", "zerosize_ok"], op_flags=["readwrite", "no_broadcast"]
flags=["refs_ok", "zerosize_ok"],
op_flags=["readwrite", "no_broadcast"]
)
with np.nditer(arr, **opt) as iter:
for elem in iter:
Expand Down
79 changes: 47 additions & 32 deletions mpython/core/base_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@

import numpy as np

from ..utils import _import_matlab, _matlab_array_types
from ..utils import _import_matlab, _matlab_array_types, DelayedImport


class _imports(DelayedImport):
Array = 'mpython.array.Array'
Cell = 'mpython.cell.Cell'
Struct = 'mpython.struct.Struct'
SparseArray = 'mpython.sparse_array.SparseArray'
MatlabClass = 'mpython.matlab_class.MatlabClass'
MatlabFunction = 'mpython.matlab_function.MatlabFunction'
AnyDelayedArray = 'mpython.core.delayed_types.AnyDelayedArray'


class MatlabType:
Expand All @@ -16,16 +26,14 @@ def from_any(cls, other, **kwargs):

!!! warning "Conversion is performed in-place when possible."
"""
# FIXME: Circular import
from ..array import Array

# FIXME: Circular import
from ..cell import Cell
from ..matlab_class import MatlabClass
from ..matlab_function import MatlabFunction
from ..sparse_array import SparseArray
from ..struct import Struct
from .delayed_types import AnyDelayedArray
# Circular import
Array = _imports.Array
Cell = _imports.Cell
MatlabClass = _imports.MatlabClass
MatlabFunction = _imports.MatlabFunction
SparseArray = _imports.SparseArray
Struct = _imports.Struct
AnyDelayedArray = _imports.AnyDelayedArray

# Conversion rules:
# - we do not convert to matlab's own array types
Expand All @@ -34,7 +42,7 @@ def from_any(cls, other, **kwargs):
# the matlab runtime;
# - instead, we convert to python types that mimic matlab types.
_from_any = partial(cls.from_any, **kwargs)
_from_runtime = kwargs.pop("_from_runtime", False)
_runtime = kwargs.pop("_runtime", None)

if isinstance(other, MatlabType):
if isinstance(other, AnyDelayedArray):
Expand All @@ -56,21 +64,21 @@ def from_any(cls, other, **kwargs):
elif type__ == "structarray":
# MPython returns a list of dictionaries in data__
# and the array shape in size__.
return Struct._from_runtime(other)
return Struct._from_runtime(other, _runtime)

elif type__ == "cell":
# MPython returns a list of dictionaries in data__
# and the array shape in size__.
return Cell._from_runtime(other)
return Cell._from_runtime(other, _runtime)

elif type__ == "object":
# MPython returns the object's fields serialized
# in a dictionary.
return MatlabClass._from_runtime(other)
return MatlabClass._from_runtime(other, _runtime)

elif type__ == "sparse":
# MPython returns the coordinates and values in a dict.
return SparseArray._from_runtime(other)
return SparseArray._from_runtime(other, _runtime)

elif type__ == "char":
# Character array that is not a row vector
Expand All @@ -82,26 +90,28 @@ def from_any(cls, other, **kwargs):
size = size[:-1] + [1]
other["type__"] = "cell"
other["size__"] = np.asarray([size])
return Cell._from_runtime(other)
return Cell._from_runtime(other, _runtime)

else:
raise ValueError("Don't know what to do with type", type__)

else:
other = type(other)(zip(other.keys(), map(_from_any, other.values())))
other = type(other)(
zip(other.keys(), map(_from_any, other.values()))
)
return Struct.from_any(other)

if isinstance(other, (list, tuple, set)):
# nested tuples are cells of cells, not cell arrays
if _from_runtime:
return Cell._from_runtime(other)
if _runtime:
return Cell._from_runtime(other, _runtime)
else:
return Cell.from_any(other)

if isinstance(other, (np.ndarray, int, float, complex, bool)):
# [array of] numbers -> Array
if _from_runtime:
return Array._from_runtime(other)
if _runtime:
return Array._from_runtime(other, _runtime)
else:
return Array.from_any(other)

Expand All @@ -117,20 +127,20 @@ def from_any(cls, other, **kwargs):

matlab = _import_matlab()
if matlab and isinstance(other, matlab.object):
return MatlabFunction.from_any(other)
return MatlabFunction._from_runtime(other, _runtime)

if type(other) in _matlab_array_types():
return Array._from_runtime(other)
return Array._from_runtime(other, _runtime)

if hasattr(other, "__iter__"):
# Iterable -> let's try to make it a cell
return cls.from_any(list(other), _from_runtime=_from_runtime)
return cls.from_any(list(other), _runtime=_runtime)

raise TypeError(f"Cannot convert {type(other)} into a matlab object.")

@classmethod
def _from_runtime(cls, obj):
return cls.from_any(obj, _from_runtime=True)
def _from_runtime(cls, obj, _runtime):
return cls.from_any(obj, _runtime=_runtime)

@classmethod
def _to_runtime(cls, obj):
Expand Down Expand Up @@ -162,8 +172,7 @@ def _to_runtime(cls, obj):
return obj

elif sparse and isinstance(obj, sparse.sparray):
from .SparseArray import SparseArray

SparseArray = _imports.SparseArray
return SparseArray.from_any(obj)._as_runtime()

else:
Expand Down Expand Up @@ -192,14 +201,20 @@ class AnyMatlabArray(MatlabType):

@property
def as_num(self):
raise TypeError(f"Cannot interpret a {type(self).__name__} as a numeric array")
raise TypeError(
f"Cannot interpret a {type(self).__name__} as a numeric array"
)

@property
def as_cell(self):
raise TypeError(f"Cannot interpret a {type(self).__name__} as a cell")
raise TypeError(
f"Cannot interpret a {type(self).__name__} as a cell"
)

@property
def as_struct(self):
raise TypeError(f"Cannot interpret a {type(self).__name__} as a struct")
raise TypeError(
f"Cannot interpret a {type(self).__name__} as a struct"
)

# TODO: `as_obj` for object arrays?
Loading
Loading