Skip to content
Open
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
83 changes: 79 additions & 4 deletions src/rachis/core/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
# The full license is in the file LICENSE, distributed with this software.
# ----------------------------------------------------------------------------
import pathlib
from typing import get_origin, get_args, Union

from rachis import sdk
from rachis.plugin import model
from rachis.core import util
from rachis.core.exceptions import ValidationError


def identity_transformer(view):
Expand All @@ -19,7 +21,9 @@ def identity_transformer(view):
class ModelType:
@staticmethod
def from_view_type(view_type):
if issubclass(view_type, model.base.FormatBase):
if get_origin(view_type) is Union:
return UnionType(view_type)
elif issubclass(view_type, model.base.FormatBase):
if issubclass(view_type,
model.SingleFileDirectoryFormatBase):
# HACK: this is necessary because we need to be able to "act"
Expand Down Expand Up @@ -137,7 +141,7 @@ def coerce_view(self, view):

def validate(self, view, level='min'):
if not isinstance(view, self._view_type):
raise TypeError("%r is not an instance of %r."
raise ValidationError("%r is not an instance of %r."
% (view, self._view_type))
# Formats have a validate method, so defer to it
view.validate(level)
Expand Down Expand Up @@ -230,5 +234,76 @@ def wrapped(view):
class ObjectType(ModelType):
def validate(self, view, level=None):
if not isinstance(view, self._view_type):
raise TypeError("%r is not of type %r, cannot transform further."
% (view, self._view_type))
raise ValidationError(
"%r is not of type %r, cannot transform further."
% (view, self._view_type)
)


class UnionType(ModelType):
@classmethod
def _is_union_type(cls, type):
return get_origin(type) is Union

def make_transformation(self, other, recorder=None):
transformations = []
if (
self._is_union_type(self._view_type)
and self._is_union_type(other._view_type)
):
from_types = ', '.join(
[str(arg) for arg in get_args(self._view_type)]
)
to_types = ', '.join(
[str(arg) for arg in get_args(other._view_type)]
)
message = (
f'No transformation from any of {from_types} to any of '
f'{to_types}'
)
for from_arg in get_args(self._view_type):
for to_arg in get_args(self._view_type):
from_type = ModelType.from_view_type(from_arg)
to_type = ModelType.from_view_type(to_arg)
try:
transformations.append(
from_type.make_transformation(to_type, recorder)
)
except Exception:
pass
elif self._is_union_type(self._view_type):
from_types = ', '.join(
[str(arg) for arg in get_args(self._view_type)]
)
message = (
f'No transformation from any of {from_types} to '
f'{other._view_type}'
)
for arg in get_args(self._view_type):
type = ModelType.from_view_type(arg)
try:
transformations.append(
type.make_transformation(other, recorder)
)
except Exception:
pass
else:
to_types = ', '.join(
[str(arg) for arg in get_args(other._view_type)]
)
message = f'No transformation from {self._view_type} to {to_types}'
type = ModelType.from_view_type(self._view_type)
for arg in get_args(other._view_type):
arg = ModelType.from_view_type(arg)
try:
transformations.append(
type.make_transformation(arg, recorder)
)
except Exception as e:
print(e)
pass

if len(transformations) > 0:
return transformations

raise Exception(message)
71 changes: 45 additions & 26 deletions src/rachis/sdk/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
import distutils.dir_util
import pathlib
import json
from typing import Union, get_args, get_origin
from typing import Union, get_origin

from rachis.core.format import report
from rachis.core.exceptions import ValidationError
import rachis.plugin
import rachis.sdk
import rachis.core.type
Expand Down Expand Up @@ -437,13 +438,32 @@ def _from_view(cls, type, view, view_type, provenance_capture,
# lookup default format for the type
view_type = output_dir_fmt

from_type = transform.ModelType.from_view_type(view_type)
if get_origin(output_dir_fmt) is Union:
from_type = transform.UnionType(view_type)
else:
from_type = transform.ModelType.from_view_type(view_type)

to_type = transform.ModelType.from_view_type(output_dir_fmt)

recorder = provenance_capture.transformation_recorder('return')
transformation = from_type.make_transformation(to_type,
recorder=recorder)
result = transformation(view, validate_level)

result = None
if isinstance(transformation, list):
for trans in transformation:
try:
result = trans(view, validate_level)
break
except ValidationError:
pass
else:
result = transformation(view, validate_level)

if result is None:
raise Exception(
f'No valid transformation from {view_type} to {output_dir_fmt}'
)

if type_raw in pm.validators:
validation_object = pm.validators[type]
Expand All @@ -465,32 +485,31 @@ def _view(self, view_type, recorder=None):
raise TypeError(
"Artifact %r cannot be viewed as Rachis Metadata." % self)

from_type = transform.ModelType.from_view_type(self.format)
if get_origin(view_type) is Union:
from_type = transform.UnionType(self.format)
else:
from_type = transform.ModelType.from_view_type(self.format)

to_type = transform.ModelType.from_view_type(view_type)
transformation = from_type.make_transformation(
to_type, recorder=recorder
)

if isinstance(get_origin(view_type), type(Union)):
transformation = None
for arg in get_args(view_type):
to_type = transform.ModelType.from_view_type(arg)
result = None
if isinstance(transformation, list):
for trans in transformation:
try:
transformation = from_type.make_transformation(
to_type, recorder=recorder)
if transformation:
break
except Exception as e:
if str(e).startswith("No transformation from"):
continue
else:
raise e
if not transformation:
raise Exception(
"No transformation into either of %s was found" %
", ".join([str(x) for x in view_type.__args__])
)
result = trans(self._archiver.data_dir)
break
except ValidationError:
pass
else:
to_type = transform.ModelType.from_view_type(view_type)
transformation = from_type.make_transformation(to_type,
recorder=recorder)
result = transformation(self._archiver.data_dir)
result = transformation(self._archiver.data_dir)

if result is None:
raise Exception(
f'No valid transformation from {self.format} to {view_type}'
)

if view_type is rachis.Metadata:
result._add_artifacts([self])
Expand Down
21 changes: 20 additions & 1 deletion src/rachis/sdk/tests/test_artifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def test_from_view_union_not_valid(self):
self.assertIsInstance(artifact.uuid, uuid.UUID)
with self.assertRaisesRegex(
Exception,
'No transformation into either of'):
'No transformation from'):
self.assertEqual(artifact.view(Union[str, dict]), [-1, 42, 0, 43])

def test_from_view_different_type_with_multiple_view_types(self):
Expand Down Expand Up @@ -666,6 +666,25 @@ def test_cannot_be_viewed_as_metadata(self):
'as Rachis Metadata'):
A.view(Metadata)

def test_view_from_union(self):
artifact = Artifact._from_view(
IntSequence1,
[1, 2, 3, 4],
Union[IntSequenceFormat, list],
self.provenance_capture
)

self.assertEqual(artifact.type, IntSequence1)
self.assertIsInstance(artifact.uuid, uuid.UUID)
self.assertEqual(artifact.view(list), [1, 2, 3, 4])

def test_view_to_union(self):
artifact = Artifact.import_data(IntSequence1, [1, 2, 3, 4])
artifact = artifact.view(Union[dict, list])

self.assertEqual(type(artifact), list)
self.assertEqual(artifact, [1, 2, 3, 4])


if __name__ == '__main__':
unittest.main()
Loading