diff --git a/scripts/param.py b/scripts/param.py deleted file mode 100644 index adce1d5e..00000000 --- a/scripts/param.py +++ /dev/null @@ -1,60 +0,0 @@ -from typing import Generic, get_args, get_origin, Literal - -from pydantic.fields import UndefinedType -from typing_extensions import TypeVar - -from omnipy import Model - -D = TypeVar('D', default=object) -S = TypeVar('S', default=object) -T = TypeVar('T', default=Literal[',']) - - -class Param(Model[UndefinedType | S | D], Generic[S, D]): - @classmethod - def _parse_data(cls, data: UndefinedType | S | D): - override, default = get_args(cls.outer_type(with_args=True))[1:3] - print(f'default: {repr(default)}, override: {repr(override)}, data: {repr(data)}') - assert get_origin(default) == Literal - target = override if get_origin(override) == Literal else default - print( - f'data: {repr(data)}, target: {repr(target)}, check: {type(data) is not UndefinedType and data != target}' - ) - if type(data) is not UndefinedType and data != target: - print('I am asserting!') - raise AssertionError( - f'Data other than the default or override literals are not allowed: {data}') - return target - - -DefaultDelim = Literal[','] - - -class DelimitParam(Param[S, DefaultDelim], Generic[S]): - ... - - -class Y(Model[list[str] | tuple[str, DelimitParam]]): - ... - - -class ValueWithParam(Model[tuple[DelimitParam[S], T]], Generic[T, S]): - @classmethod - def _parse_data(cls, data: tuple[T, DelimitParam[S]]): - print(f'data: {repr(data)}') - return data - - -class D(Model[T], Generic[T]): - ... - - -D[Literal['asd']]() - - -class Splitter(Model[list[str] | str | Model[T]], Generic[T]): - ... - - -class A(Model[tuple[T, S]], Generic[T, S]): - ... diff --git a/scripts/param_new.py b/scripts/param_new.py deleted file mode 100644 index 2a748bb6..00000000 --- a/scripts/param_new.py +++ /dev/null @@ -1,115 +0,0 @@ -from dataclasses import dataclass -from typing import Any, Callable, cast, Generic, get_args, ParamSpec, Protocol - -from typing_extensions import TypeVar - -from omnipy import Model - -ParamsT = TypeVar('ParamsT') -ParamsP = ParamSpec('ParamsP') - - -class Conf(Protocol[ParamsT]): - settings: ParamsT - - -def conf(param_cls: Callable[ParamsP, ParamsT]) -> Callable[ParamsP, type[Conf[ParamsT]]]: - def _conf(*args: ParamsP.args, **kwargs: ParamsP.kwargs) -> type[Conf[ParamsT]]: - # Factory for new _Conf classes. Needed to allow the classes to have individual settings - class _Conf(Conf[ParamsT]): - settings = param_cls(**kwargs) - - return _Conf - - return _conf - - -class ParamModelMixin(Generic[ParamsT]): - @dataclass - class Params: - ... - - @classmethod - def _get_conf_settings(cls) -> ParamsT: - conf_t = get_args(get_args(cls.full_type())[-1])[0] - if isinstance(conf_t, TypeVar): - conf_t = conf(cls.Params)() - return conf_t.settings - - -ConfT = TypeVar('ConfT', bound=type[Conf]) - - -class ConfHolder(Generic[ConfT]): - def __init__(self, conf: ConfT) -> None: - raise ValueError() - - -class MyModel(Model[int | ConfHolder[ConfT]], ParamModelMixin['MyModel.Params'], Generic[ConfT]): - @dataclass - class Params: - add: int = 123 - - @classmethod - def _parse_data(cls, data: int) -> int: - conf_settings = cls._get_conf_settings() - # reveal_type(settings) - # reveal_type(settings.add) - return data + conf_settings.add - - -my_conf = conf(MyModel.Params) -print('my_conf = conf(MyModel.Params):', my_conf, type(my_conf)) -# reveal_type(my_conf) - -MyConf = my_conf(add=23) -print('MyConf = my_conf(add=23):', MyConf, type(MyConf)) -# reveal_type(MyConf) - -settings = MyConf.settings -print('settings = MyConf.settings:', settings, type(settings)) -# reveal_type(settings) - -add = MyConf.settings.add -print('add = MyConf.settings.add:', add, type(add)) -# reveal_type(add) - -aa: MyModel[my_conf(add=33)] = MyModel[my_conf(add=33)](123) -print('MyModel[my_conf(add=33)](123):', aa, type(aa)) -# reveal_type(aa) - -MyNewConf = conf(MyModel.Params)(add=100) -bb = MyModel[MyNewConf](123) -print('MyNewConf = conf(MyModel.Params)(add=100); MyModel[MyNewConf](123):', bb, type(bb)) -# reveal_type(bb) - -abc = bb + 12 -print('MyModel[MyNewConf](123) + 12:', abc, type(abc)) -# reveal_type(abc) - -cc = MyModel(123) -print('MyModel(123):', cc, type(cc)) -# reveal_type(cc) - -config = conf(MyModel.Params) - - -class MyAddTenModel(MyModel[config(add=10)]): - ... - - -dd = MyAddTenModel(123) -print('MyAddTenModel(123):', dd, type(dd)) -# reveal_type(dd) - -# Variant of https://github.com/python/typing/issues/270#issuecomment-555966301 -# -# T = TypeVar("T") -# P = ParamSpec('P') -# -# class copy_paramspec(Generic[P]): -# def __init__(self, target: Callable[P, Any]) -> None: -# ... -# -# def __call__(self, wrapped: Callable[..., T]) -> Callable[P, T]: -# return wrapped diff --git a/src/omnipy/__init__.py b/src/omnipy/__init__.py index 98f89d3d..b95add18 100644 --- a/src/omnipy/__init__.py +++ b/src/omnipy/__init__.py @@ -9,8 +9,8 @@ LinearFlow, LinearFlowTemplate) from omnipy.compute.task import Task, TaskTemplate -from omnipy.data.dataset import Dataset, ListOfParamModelDataset, MultiModelDataset, ParamDataset -from omnipy.data.model import ListOfParamModel, Model, ParamModel +from omnipy.data.dataset import Dataset, MultiModelDataset +from omnipy.data.model import Model from omnipy.data.param import bind_adjust_dataset_func, bind_adjust_model_func, ParamsBase from omnipy.hub.runtime import runtime from omnipy.modules.general.models import (Chain2, @@ -119,9 +119,7 @@ JoinItemsModel, JoinLinesModel, SplitLinesToColumnsModel, - SplitLinesToColumnsModelNew, SplitToItemsModel, - SplitToItemsModelNew, SplitToLinesModel, StrModel) from omnipy.modules.raw.tasks import (concat_all, @@ -143,8 +141,6 @@ transpose_columns_with_data_files) from omnipy.util.contexts import print_exception -# if typing.TYPE_CHECKING: - ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) __all__ = [ @@ -158,12 +154,8 @@ 'Task', 'TaskTemplate', 'Dataset', - 'ParamDataset', - 'ListOfParamModelDataset', 'MultiModelDataset', 'Model', - 'ParamModel', - 'ListOfParamModel', 'FlattenedIsaJsonDataset', 'FlattenedIsaJsonModel', 'IsaJsonModel', @@ -250,10 +242,8 @@ 'SplitToLinesModel', 'JoinLinesModel', 'SplitToItemsModel', - 'SplitToItemsModelNew', 'JoinItemsModel', 'SplitLinesToColumnsModel', - 'SplitLinesToColumnsModelNew', 'JoinColumnsToLinesModel', 'StrModel', 'TableOfPydanticRecordsDataset', diff --git a/src/omnipy/_dynamic_all.py b/src/omnipy/_dynamic_all.py index f15ac5f6..39ac07ac 100644 --- a/src/omnipy/_dynamic_all.py +++ b/src/omnipy/_dynamic_all.py @@ -39,8 +39,8 @@ LinearFlowTemplate) from .compute.job import JobTemplateMixin from .compute.task import Task, TaskTemplate - from .data.dataset import Dataset, ListOfParamModelDataset, MultiModelDataset, ParamDataset - from .data.model import ListOfParamModel, Model, ParamModel + from .data.dataset import Dataset, MultiModelDataset + from .data.model import Model from .data.param import bind_adjust_dataset_func, bind_adjust_model_func, ParamsBase from .hub.runtime import runtime from .util.contexts import print_exception @@ -57,15 +57,11 @@ 'Task', 'TaskTemplate', 'Dataset', - 'ParamDataset', - 'ListOfParamModelDataset', 'MultiModelDataset', 'ParamsBase', 'bind_adjust_model_func', 'bind_adjust_dataset_func', 'Model', - 'ParamModel', - 'ListOfParamModel', 'print_exception', ] _all_element_names = set(__all__) diff --git a/src/omnipy/data/dataset.py b/src/omnipy/data/dataset.py index e234d36c..0f9a680b 100644 --- a/src/omnipy/data/dataset.py +++ b/src/omnipy/data/dataset.py @@ -4,7 +4,7 @@ import os import tarfile from tempfile import TemporaryDirectory -from typing import Any, Callable, cast, Generic, get_args, Iterator, Type, TypeAlias +from typing import Any, Callable, cast, Generic, Iterator from urllib.parse import ParseResult, urlparse import humanize @@ -20,16 +20,12 @@ from omnipy.data.data_class_creator import DataClassBase, DataClassBaseMeta from omnipy.data.model import (_cleanup_name_qualname_and_module, _waiting_for_terminal_repr, - DataWithParams, INTERACTIVE_MODULES, is_model_instance, - ListOfParamModel, - Model, - ParamModel) + Model) from omnipy.util.helpers import (get_calling_module_name, get_default_if_typevar, is_iterable, - is_union, remove_forward_ref_notation) from omnipy.util.tabulate import tabulate from omnipy.util.web import download_file_to_memory @@ -124,16 +120,9 @@ def __class_getitem__( orig_model = model if cls == Dataset: - args = get_args(model) - - if is_union(model) and len(args) == 2 and lenient_issubclass(args[1], DataWithParams): - model_to_check = args[0] - else: - model_to_check = model - - if not isinstance(model_to_check, TypeVar) \ - and not lenient_issubclass(model_to_check, Model): - raise TypeError('Invalid model: {}! '.format(model_to_check) + if not isinstance(model, TypeVar) \ + and not lenient_issubclass(model, Model): + raise TypeError('Invalid model: {}! '.format(model) + 'omnipy Dataset models must be a specialization of the omnipy ' 'Model class.') @@ -185,14 +174,8 @@ def __init__( # noqa: C901 "Not allowed to combine 'data' with other keyword arguments" super_kwargs[DATA_KEY] = data - # model_cls = self.get_model_class() if kwargs: if DATA_KEY not in super_kwargs: - # assert isinstance(model_cls, TypeVar) or not model_cls.is_param_model(), \ - # ('If any keyword arguments are defined, parametrized datasets require at least ' - # 'one positional argument in the __init__ method (typically providing the data ' - # 'in the form of a dict from name to content for each data file).') - # super_kwargs[DATA_KEY] = kwargs kwargs = {} @@ -615,81 +598,3 @@ def _to_data_if_model(data_obj: Any): if is_model_instance(data_obj): data_obj = data_obj.to_data() return data_obj - - -_KwargValT = TypeVar('_KwargValT', bound=object) -_ParamModelT = TypeVar('_ParamModelT', bound=ParamModel) -_ListOfParamModelT = TypeVar('_ListOfParamModelT', bound=ListOfParamModel) - -ParamModelSuperKwargsType: TypeAlias = \ - dict[str, dict[str, _ParamModelT | DataWithParams[_ParamModelT, _KwargValT]]] - - -class ParamDataset(Dataset[_ParamModelT | DataWithParams[_ParamModelT, _KwargValT]], - Generic[_ParamModelT, _KwargValT]): - def _init(self, super_kwargs: ParamModelSuperKwargsType, **kwargs: _KwargValT) -> None: - if kwargs and DATA_KEY in super_kwargs: - - def _convert_if_model(data: _ParamModelT) -> _ParamModelT: - if is_model_instance(data): - # Casting to _ParamModelT now, will be validated in super()__init__() - return cast(_ParamModelT, cast(Model, data).to_data()) - else: - return data - - data_in = cast(dict[str, _ParamModelT], super_kwargs[DATA_KEY]) - super_kwargs[DATA_KEY] = { - key: DataWithParams(data=_convert_if_model(val), params=kwargs) - for (key, val) in data_in.items() - } - - def from_data(self, - data: dict[str, Any] | Iterator[tuple[str, Any]], - update: bool = True, - **kwargs: _KwargValT) -> None: - if kwargs: - - def callback_func(model: ModelT, contents: Any): - model.from_data(contents, **kwargs) - - self._from_dict_with_callback(data, update, callback_func) - else: - super().from_data(data, update=update) - - def from_json(self, - data: Mapping[str, str] | Iterable[tuple[str, str]], - update: bool = True, - **kwargs: _KwargValT) -> None: - if kwargs: - - def callback_func(model: ModelT, contents: Any): - model.from_json(contents, **kwargs) - - self._from_dict_with_callback(data, update, callback_func) - else: - super().from_json(data, update=update) - - -class ListOfParamModelDataset(ParamDataset[_ListOfParamModelT, _KwargValT], - Generic[_ListOfParamModelT, _KwargValT]): - def _init(self, - super_kwargs: dict[str, - dict[str, - ListOfParamModel[_ParamModelT, _KwargValT] - | list[DataWithParams[_ParamModelT, _KwargValT]]]], - **kwargs: _KwargValT) -> None: - if kwargs and DATA_KEY in super_kwargs: - - def _convert_if_model(data: _ParamModelT) -> _ParamModelT: - if is_model_instance(data): - # Casting to _ParamModelT now, will be validated in super()__init__() - return cast(_ParamModelT, cast(Model, data).to_data()) - else: - return data - - data_in = cast(dict[str, list[_ParamModelT]], super_kwargs[DATA_KEY]) - - super_kwargs[DATA_KEY] = { - key: [DataWithParams(data=_convert_if_model(el), params=kwargs) for el in val] - for (key, val) in data_in.items() - } diff --git a/src/omnipy/data/model.py b/src/omnipy/data/model.py index 356d45a7..bca280e1 100644 --- a/src/omnipy/data/model.py +++ b/src/omnipy/data/model.py @@ -424,10 +424,9 @@ def __init__( num_root_vals += 1 if kwargs: - if num_root_vals == 0 or not self.is_param_model(): - super_kwargs[ROOT_KEY] = cast(_RootT, kwargs) - kwargs = {} - num_root_vals += 1 + super_kwargs[ROOT_KEY] = cast(_RootT, kwargs) + kwargs = {} + num_root_vals += 1 assert num_root_vals <= 1, 'Not allowed to provide root data in more than one argument' @@ -897,18 +896,6 @@ def full_type(cls) -> TypeForm: def is_nested_type(cls) -> bool: return not cls.inner_type(with_args=True) == cls.outer_type(with_args=True) - @classmethod - # Refactor: Remove is_param_model - def is_param_model(cls) -> bool: - if cls.outer_type() is list: - type_to_check = cls.inner_type(with_args=True) - else: - type_to_check = cls.outer_type(with_args=True) - args = get_args(type_to_check) - return is_union(type_to_check) \ - and len(args) >= 2 \ - and lenient_issubclass(args[-1], DataWithParams) - @classmethod def _get_root_field(cls) -> ModelField: return cast(ModelField, cls.__fields__.get(ROOT_KEY)) @@ -1464,101 +1451,3 @@ def _is_table(): _waiting_for_terminal_repr(False) return out - - -_ParamRootT = TypeVar('_ParamRootT', default=object | None) -_KwargValT = TypeVar('_KwargValT', default=object) - - -class DataWithParams(GenericModel, Generic[_ParamRootT, _KwargValT]): - data: _ParamRootT - params: dict[str, _KwargValT] - - -class ParamModel(Model[_ParamRootT | DataWithParams[_ParamRootT, _KwargValT]], - Generic[_ParamRootT, _KwargValT]): - def _init(self, - super_kwargs: dict[str, _ParamRootT | DataWithParams[_ParamRootT, _KwargValT]], - **kwargs: _KwargValT) -> None: - if kwargs and ROOT_KEY in super_kwargs: - assert not isinstance(super_kwargs[ROOT_KEY], DataWithParams) - super_kwargs[ROOT_KEY] = DataWithParams( - data=cast(_ParamRootT, super_kwargs[ROOT_KEY]), params=kwargs) - - @root_validator - def _parse_root_object( - cls, root_obj: dict[str, _ParamRootT | DataWithParams[_ParamRootT, _KwargValT]]) -> Any: - assert ROOT_KEY in root_obj - root_val = root_obj[ROOT_KEY] - - params: dict[str, _KwargValT] = {} - - if isinstance(root_val, DataWithParams): - data = root_val.data - params = root_val.params - else: - data = root_val - - try: - return {ROOT_KEY: cls._parse_data(data, **params)} - except TypeError as exc: - import inspect - for key in params.keys(): - if key not in inspect.signature(cls._parse_data).parameters: - raise ParamException(exc) from None - raise exc - - @classmethod - def _parse_data(cls, data: _ParamRootT, **params: _KwargValT) -> _ParamRootT: - return data - - def from_data(self, value: object, **kwargs: _KwargValT) -> None: - super().from_data(value) - if kwargs: - self._validate_and_set_contents_with_params(cast(_ParamRootT, self.contents), **kwargs) - - def from_json(self, json_contents: str, **kwargs: _KwargValT) -> None: - super().from_json(json_contents) - if kwargs: - self._validate_and_set_contents_with_params(cast(_ParamRootT, self.contents), **kwargs) - - def _validate_and_set_contents_with_params(self, contents: _ParamRootT, **kwargs: _KwargValT): - self._validate_and_set_value(DataWithParams(data=contents, params=kwargs)) - - -_ParamModelT = TypeVar('_ParamModelT', bound='ParamModel') - - -class ListOfParamModel(ParamModel[list[_ParamModelT - | DataWithParams[_ParamModelT, _KwargValT]], - _KwargValT], - Generic[_ParamModelT, _KwargValT]): - def _init( - self, - super_kwargs: dict[ # type: ignore[override] - str, - list[_ParamModelT - | DataWithParams[_ParamModelT, _KwargValT]]], - **kwargs: _KwargValT) -> None: - if kwargs and ROOT_KEY in super_kwargs: - - def _convert_if_model(data: _ParamModelT) -> _ParamModelT: - if is_model_instance(data): - # Casting to _ParamModelT now, will be validated in super()__init__() - return cast(_ParamModelT, cast(Model, data).to_data()) - else: - return data - - root_val = cast(list[_ParamModelT], super_kwargs[ROOT_KEY]) - super_kwargs[ROOT_KEY] = [ - DataWithParams(data=_convert_if_model(el), params=kwargs) for el in root_val - ] - - def _validate_and_set_contents_with_params( - self, - contents: list[_ParamModelT | DataWithParams[_ParamModelT, _KwargValT]], - **kwargs: _KwargValT): - self._validate_and_set_value([ - DataWithParams(data=cast(_ParamModelT, model).contents, params=kwargs) - for model in contents - ]) diff --git a/src/omnipy/modules/raw/datasets.py b/src/omnipy/modules/raw/datasets.py index 9d3ef217..95e8c53e 100644 --- a/src/omnipy/modules/raw/datasets.py +++ b/src/omnipy/modules/raw/datasets.py @@ -2,7 +2,7 @@ from typing_extensions import TypeVar -from omnipy.data.dataset import Dataset, ListOfParamModelDataset, ParamDataset +from omnipy.data.dataset import Dataset from omnipy.data.param import bind_adjust_dataset_func from .models import (BytesModel, @@ -16,15 +16,20 @@ BytesModelT = TypeVar('BytesModelT', default=BytesModel) StrModelT = TypeVar('StrModelT', default=StrModel) +SplitToLinesModelT = TypeVar('SplitToLinesModelT', default=SplitToLinesModel) +SplitToItemsModelT = TypeVar('SplitToItemsModelT', default=SplitToItemsModel) +JoinItemsModelT = TypeVar('JoinItemsModelT', default=JoinItemsModel) +SplitLinesToColumnsModelT = TypeVar('SplitLinesToColumnsModelT', default=SplitLinesToColumnsModel) +JoinColumnsToLinesModelT = TypeVar('JoinColumnsToLinesModelT', default=JoinColumnsToLinesModel) class _BytesDataset(Dataset[BytesModelT], Generic[BytesModelT]): ... -class BytesDataset(_BytesDataset[BytesModelT], Generic[BytesModelT]): +class BytesDataset(_BytesDataset[BytesModel]): adjust = bind_adjust_dataset_func( - _BytesDataset[BytesModelT].clone_dataset_cls, + _BytesDataset[BytesModel].clone_dataset_cls, BytesModel, BytesModel.Params, ) @@ -34,33 +39,75 @@ class _StrDataset(Dataset[StrModelT], Generic[StrModelT]): ... -class StrDataset(_StrDataset[StrModelT], Generic[StrModelT]): +class StrDataset(_StrDataset[StrModel]): adjust = bind_adjust_dataset_func( - _StrDataset[StrModelT].clone_dataset_cls, + _StrDataset[StrModel].clone_dataset_cls, StrModel, StrModel.Params, ) -class SplitToLinesDataset(ParamDataset[SplitToLinesModel, bool]): +class _SplitToLinesDataset(Dataset[SplitToLinesModelT], Generic[SplitToLinesModelT]): ... +class SplitToLinesDataset(_SplitToLinesDataset[SplitToLinesModel]): + adjust = bind_adjust_dataset_func( + _SplitToLinesDataset[SplitToLinesModel].clone_dataset_cls, + SplitToLinesModel, + SplitToLinesModel.Params, + ) + + class JoinLinesDataset(Dataset[JoinLinesModel]): ... -class SplitToItemsDataset(ParamDataset[SplitToItemsModel, bool | str]): +class _SplitToItemsDataset(Dataset[SplitToItemsModelT], Generic[SplitToItemsModelT]): ... -class JoinItemsDataset(ParamDataset[JoinItemsModel, str]): +class SplitToItemsDataset(_SplitToItemsDataset[SplitToItemsModel]): + adjust = bind_adjust_dataset_func( + _SplitToItemsDataset[SplitToItemsModel].clone_dataset_cls, + SplitToItemsModel, + SplitToItemsModel.Params, + ) + + +class _JoinItemsDataset(Dataset[JoinItemsModelT], Generic[JoinItemsModelT]): ... -class SplitLinesToColumnsDataset(ListOfParamModelDataset[SplitLinesToColumnsModel, bool | str]): +class JoinItemsDataset(_JoinItemsDataset[JoinItemsModel]): + adjust = bind_adjust_dataset_func( + _JoinItemsDataset[JoinItemsModel].clone_dataset_cls, + JoinItemsModel, + JoinItemsModel.Params, + ) + + +class _SplitLinesToColumnsDataset(Dataset[SplitLinesToColumnsModelT], + Generic[SplitLinesToColumnsModelT]): ... -class JoinColumnsToLinesDataset(ListOfParamModelDataset[JoinColumnsToLinesModel, str]): +class SplitLinesToColumnsDataset(_SplitLinesToColumnsDataset[SplitLinesToColumnsModel]): + adjust = bind_adjust_dataset_func( + _SplitLinesToColumnsDataset[SplitLinesToColumnsModel].clone_dataset_cls, + SplitLinesToColumnsModel, + SplitLinesToColumnsModel.Params, + ) + + +class _JoinColumnsToLinesDataset(Dataset[JoinColumnsToLinesModelT], + Generic[JoinColumnsToLinesModelT]): ... + + +class JoinColumnsToLinesDataset(_JoinColumnsToLinesDataset[JoinColumnsToLinesModel]): + adjust = bind_adjust_dataset_func( + _JoinColumnsToLinesDataset[JoinColumnsToLinesModel].clone_dataset_cls, + JoinColumnsToLinesModel, + JoinColumnsToLinesModel.Params, + ) diff --git a/src/omnipy/modules/raw/models.py b/src/omnipy/modules/raw/models.py index 324efe4b..35c6eefd 100644 --- a/src/omnipy/modules/raw/models.py +++ b/src/omnipy/modules/raw/models.py @@ -2,7 +2,7 @@ import os from typing import cast -from omnipy.data.model import ListOfParamModel, Model, ParamModel +from omnipy.data.model import Model from omnipy.data.param import bind_adjust_model_func, ParamsBase @@ -48,16 +48,27 @@ class StrModel(_StrModel): ) -class SplitToLinesModel(ParamModel[str | list[str], bool]): +class _SplitToLinesModel(Model[str | list[str]]): + @dataclass(kw_only=True) + class Params(ParamsBase): + strip: bool = True + @classmethod - def _parse_data(cls, data: str | list[str], strip: bool = True) -> list[str]: + def _parse_data(cls, data: str | list[str]) -> list[str]: if isinstance(data, list): return data - if strip: + if cls.Params.strip: data = data.strip() lines = data.split(os.linesep) - return [line.strip() for line in lines] if strip else lines + return [line.strip() for line in lines] if cls.Params.strip else lines + + +class SplitToLinesModel(_SplitToLinesModel): + adjust = bind_adjust_model_func( + _SplitToLinesModel.clone_model_cls, + _SplitToLinesModel.Params, + ) class JoinLinesModel(Model[list[str] | str]): @@ -68,7 +79,7 @@ def _parse_data(cls, data: list[str] | str) -> str: return os.linesep.join(data) -class SplitToItemsMixin: +class _SplitToItemsMixin: @dataclass(kw_only=True) class Params(ParamsBase): strip: bool = True @@ -84,69 +95,86 @@ def _split_line(cls, data: str) -> list[str]: return [item.strip(cls.Params.strip_chars) for item in items] if cls.Params.strip else items -class _SplitToItemsModelNew( +class _SplitToItemsModel( Model[list[str] | str], - SplitToItemsMixin, + _SplitToItemsMixin, ): @classmethod - def _parse_data(cls, data: list[str] | str) -> list[str]: + def _parse_data(cls, data: str | list[str]) -> list[str]: if isinstance(data, list): return data return cls._split_line(data) -class SplitToItemsModelNew(_SplitToItemsModelNew): +class SplitToItemsModel(_SplitToItemsModel): adjust = bind_adjust_model_func( - _SplitToItemsModelNew.clone_model_cls, - _SplitToItemsModelNew.Params, + _SplitToItemsModel.clone_model_cls, + _SplitToItemsModel.Params, ) -class SplitToItemsModel(ParamModel[str | list[str], bool | str]): +class _SplitLinesToColumnsModel( + Model[list[str] | list[StrModel] | list[list[str]]], + _SplitToItemsMixin, +): @classmethod - def _parse_data(cls, - data: str | list[str], - strip: bool = True, - delimiter: str = '\t') -> list[str]: - if isinstance(data, list): - return data + def _parse_data(cls, data: list[str] | list[StrModel] | list[list[str]]) -> list[list[str]]: + if isinstance(data, list) and (len(data) == 0 or isinstance(data[0], list)): + return cast(list[list[str]], data) - items = data.split(delimiter) - return [item.strip() for item in items] if strip else items + return [cls._split_line(cast(str, line)) for line in data] -class JoinItemsModel(ParamModel[list[str] | str, str]): - @classmethod - def _parse_data(cls, data: list[str] | str, delimiter: str = '\t') -> str: - if isinstance(data, str): - return data +class SplitLinesToColumnsModel(_SplitLinesToColumnsModel): + adjust = bind_adjust_model_func( + _SplitLinesToColumnsModel.clone_model_cls, + _SplitLinesToColumnsModel.Params, + ) + - return delimiter.join(data) +class _JoinItemsMixin: + @dataclass(kw_only=True) + class Params(ParamsBase): + delimiter: str = '\t' + @classmethod + def _join_items(cls, data: list[str]) -> str: + return cls.Params.delimiter.join(data) -class _SplitLinesToColumnsModelNew( - Model[list[list[str]] | list[str] | list[StrModel]], - SplitToItemsMixin, -): + +class _JoinItemsModel(Model[list[str] | str], _JoinItemsMixin): @classmethod - def _parse_data(cls, data: list[list[str]] | list[str] | list[StrModel]) -> list[list[str]]: - if isinstance(data, list) and (len(data) == 0 or isinstance(data[0], list)): - return cast(list[list[str]], data) + def _parse_data(cls, data: list[str] | str) -> str: + if isinstance(data, str): + return data - return [cls._split_line(cast(str, line)) for line in data] + return cls._join_items(data) + + ... -class SplitLinesToColumnsModelNew(_SplitLinesToColumnsModelNew): +class JoinItemsModel(_JoinItemsModel): adjust = bind_adjust_model_func( - _SplitLinesToColumnsModelNew.clone_model_cls, - _SplitLinesToColumnsModelNew.Params, + _JoinItemsModel.clone_model_cls, + _JoinItemsModel.Params, ) -class SplitLinesToColumnsModel(ListOfParamModel[SplitToItemsModel, bool | str]): - ... +class _JoinColumnsToLinesModel( + Model[list[list[str]] | list[str]], + _JoinItemsMixin, +): + @classmethod + def _parse_data(cls, data: list[list[str]] | list[str]) -> list[str]: + if isinstance(data, list) and (len(data) == 0 or not isinstance(data[0], list)): + return cast(list[str], data) + return [cls._join_items(cast(list[str], cols)) for cols in data] -class JoinColumnsToLinesModel(ListOfParamModel[JoinItemsModel, str]): - ... + +class JoinColumnsToLinesModel(_JoinColumnsToLinesModel): + adjust = bind_adjust_model_func( + _JoinColumnsToLinesModel.clone_model_cls, + _JoinColumnsToLinesModel.Params, + ) diff --git a/src/omnipy/modules/tables/models.py b/src/omnipy/modules/tables/models.py index fbb2d4e4..8748b6c9 100644 --- a/src/omnipy/modules/tables/models.py +++ b/src/omnipy/modules/tables/models.py @@ -7,7 +7,7 @@ from ..general.models import Chain3 from ..json.models import JsonListOfScalarsModel from ..json.typedefs import JsonScalar -from ..raw.models import SplitLinesToColumnsModelNew, SplitToLinesModel +from ..raw.models import SplitLinesToColumnsModel, SplitToLinesModel class TableListOfListsOfJsonScalarsModel(Model[list[list[JsonScalar]]]): @@ -77,7 +77,7 @@ def _parse_data(cls, data: _PydanticBaseModelT | JsonListOfScalarsModel) -> _Pyd class TableOfPydanticRecordsModel(Chain3[SplitToLinesModel, - SplitLinesToColumnsModelNew, + SplitLinesToColumnsModel, Model[list[PydanticRecordModel[_PydanticRecordT]]]], Generic[_PydanticRecordT]): ... diff --git a/tests/data/helpers/datasets.py b/tests/data/helpers/datasets.py index ed09fdf7..13c377cd 100644 --- a/tests/data/helpers/datasets.py +++ b/tests/data/helpers/datasets.py @@ -2,15 +2,11 @@ from typing_extensions import TypeVar -from omnipy.data.dataset import Dataset, ListOfParamModelDataset, ParamDataset +from omnipy.data.dataset import Dataset from omnipy.data.model import Model from omnipy.data.param import bind_adjust_dataset_func -from .models import (DefaultStrModel, - ListOfUpperStrModel, - MyFloatObjModel, - ParamUpperStrModel, - UpperStrModel) +from .models import DefaultStrModel, MyFloatObjModel, ParamUpperStrModel ChildT = TypeVar('ChildT', default=object) @@ -35,11 +31,6 @@ class NumberModel(Model[int]): MyFwdRefDataset.update_forward_refs(NumberModel=NumberModel) MyNestedFwdRefDataset.update_forward_refs(NumberModel=NumberModel) - -class UpperStrDataset(ParamDataset[UpperStrModel, bool]): - ... - - ParamUpperStrModelT = TypeVar('ParamUpperStrModelT', default=ParamUpperStrModel) @@ -47,18 +38,24 @@ class _ParamUpperStrDataset(Dataset[ParamUpperStrModelT], Generic[ParamUpperStrM ... -class ParamUpperStrDataset(_ParamUpperStrDataset[ParamUpperStrModelT], - Generic[ParamUpperStrModelT]): +class ParamUpperStrDataset(_ParamUpperStrDataset[ParamUpperStrModel]): adjust = bind_adjust_dataset_func( - _ParamUpperStrDataset[ParamUpperStrModelT].clone_dataset_cls, + _ParamUpperStrDataset[ParamUpperStrModel].clone_dataset_cls, ParamUpperStrModel, ParamUpperStrModel.Params, ) -class DefaultStrDataset(ParamDataset[DefaultStrModel, bool]): - ... +DefaultStrModelT = TypeVar('DefaultStrModelT', default=DefaultStrModel) -class ListOfUpperStrDataset(ListOfParamModelDataset[ListOfUpperStrModel, bool]): +class _DefaultStrDataset(Dataset[DefaultStrModelT], Generic[DefaultStrModelT]): ... + + +class DefaultStrDataset(_DefaultStrDataset[DefaultStrModel]): + adjust = bind_adjust_dataset_func( + _DefaultStrDataset[DefaultStrModel].clone_dataset_cls, + DefaultStrModel, + DefaultStrModel.Params, + ) diff --git a/tests/data/helpers/models.py b/tests/data/helpers/models.py index c5956774..36be21d2 100644 --- a/tests/data/helpers/models.py +++ b/tests/data/helpers/models.py @@ -1,13 +1,12 @@ from dataclasses import dataclass from math import floor -from types import NoneType from typing import Generic, Literal, Optional, TypeAlias from pydantic import BaseModel, Field from pydantic.generics import GenericModel from typing_extensions import TypeVar -from omnipy.data.model import ListOfParamModel, Model, ParamModel +from omnipy.data.model import Model from omnipy.data.param import bind_adjust_model_func, ParamsBase ChildT = TypeVar('ChildT', bound=object) @@ -83,20 +82,21 @@ class ParamUpperStrModel(_ParamUpperStrModel): ) -class UpperStrModel(ParamModel[str, bool]): - @classmethod - def _parse_data(cls, data: str, upper: bool = False) -> str: - return data.upper() if upper else data - +class _DefaultStrModel(Model[None | str]): + @dataclass(kw_only=True) + class Params(ParamsBase): + default: str = 'default' -class ListOfUpperStrModel(ListOfParamModel[UpperStrModel, bool]): - ... + @classmethod + def _parse_data(cls, data: None) -> str: + return cls.Params.default if data is None else data -class DefaultStrModel(ParamModel[NoneType | str, str]): - @classmethod - def _parse_data(cls, data: NoneType, default: str = 'default') -> str: - return default if data is None else data +class DefaultStrModel(_DefaultStrModel): + adjust = bind_adjust_model_func( + _DefaultStrModel.clone_model_cls, + _DefaultStrModel.Params, + ) class PydanticChildModel(BaseModel): diff --git a/tests/data/test_dataset.py b/tests/data/test_dataset.py index 6be8918b..4a730d5c 100644 --- a/tests/data/test_dataset.py +++ b/tests/data/test_dataset.py @@ -13,13 +13,11 @@ from omnipy.data.model import Model from .helpers.datasets import (DefaultStrDataset, - ListOfUpperStrDataset, MyFloatObjDataset, MyFwdRefDataset, MyNestedFwdRefDataset, - ParamUpperStrDataset, - UpperStrDataset) -from .helpers.models import MyFloatObject, NumberModel, StringToLength, UpperStrModel + ParamUpperStrDataset) +from .helpers.models import MyFloatObject, NumberModel, StringToLength def test_no_model(): @@ -907,34 +905,7 @@ def test_dataset_switch_models_issue(): # TODO: Add unit tests for MultiModelDataset -def test_parametrized_dataset(): - assert UpperStrDataset(dict(x='foo'))['x'].contents == 'foo' - assert UpperStrDataset(dict(x='bar'), upper=True)['x'].contents == 'BAR' - assert UpperStrDataset(dict(x='foo', y='bar'), upper=True).to_data() == dict(x='FOO', y='BAR') - - dataset = UpperStrDataset() - dataset['x'] = 'foo' - assert dataset['x'].contents == 'foo' - - dataset.from_data(dict(y='bar', z='foobar'), upper=True) - assert dataset.to_data() == dict(x='foo', y='BAR', z='FOOBAR') - - dataset.from_data(dict(y='bar', z='foobar'), update=False) - assert dataset.to_data() == dict(y='bar', z='foobar') - - dataset.from_json(dict(x='"foo"'), upper=True) - assert dataset.to_data() == dict(x='FOO', y='bar', z='foobar') - - dataset.from_json(dict(x='"foo"'), update=False) - assert dataset.to_data() == dict(x='foo') - - with pytest.raises(KeyError): - UpperStrDataset(dict(x='bar'), upper=True)['upper'] - with pytest.raises(AssertionError): - assert UpperStrDataset(x='bar', upper=True) - - -def test_parametrized_dataset_new() -> None: +def test_parametrized_dataset() -> None: assert ParamUpperStrDataset(x='foo')['x'].contents == 'foo' MyUpperStrDataset = ParamUpperStrDataset.adjust( @@ -969,74 +940,29 @@ def test_parametrized_dataset_new() -> None: def test_parametrized_dataset_wrong_keyword() -> None: - with pytest.raises(ParamException): - UpperStrDataset(dict(x='bar'), supper=True) - - dataset = UpperStrDataset() + with pytest.raises(KeyError): + ParamUpperStrDataset.adjust('ParamSupperStrDataset', 'ParamSupperStrModel', supper=True) - with pytest.raises(ParamException): - dataset.from_data(dict(x='bar'), supper=True) - with pytest.raises(ParamException): - dataset.from_json(dict(x='"foobar"'), supper=True) +def test_parametrized_dataset_with_none() -> None: + with pytest.raises(ValidationError): + ParamUpperStrDataset(dict(x=None)) + MyUpperStrDataset = ParamUpperStrDataset.adjust( + 'MyUpperStrDataset', + 'MyUpperStrModel', + upper=True, + ) -@pytest.mark.skipif( - os.getenv('OMNIPY_FORCE_SKIPPED_TEST') != '1', - reason='ParamDataset does not support None values yet. Wait until pydantic v2 removes the' - 'Annotated hack to simplify implementation') -def test_parametrized_dataset_with_none(): - with pytest.raises(ValidationError): - UpperStrDataset(dict(x=None)) with pytest.raises(ValidationError): - UpperStrDataset(dict(x=None), upper=True) + MyUpperStrDataset(dict(x=None)) assert DefaultStrDataset(dict(x=None))['x'].contents == 'default' - assert DefaultStrDataset(dict(x=None), default='other')['x'].contents == 'other' - - -def test_list_of_parametrized_model_dataset(): - assert ListOfUpperStrDataset(dict(x=['foo']))['x'].contents == [UpperStrModel('foo')] - assert ListOfUpperStrDataset(dict(x=['foo']))['x'].to_data() == ['foo'] - - dataset_1 = ListOfUpperStrDataset(dict(x=['foo', 'bar'], y=['foobar']), upper=True) - assert dataset_1['x'].contents \ - == [UpperStrModel('foo', upper=True), UpperStrModel('bar', upper=True)] - assert dataset_1.to_data() == {'x': ['FOO', 'BAR'], 'y': ['FOOBAR']} - - dataset_2 = ListOfUpperStrDataset() - dataset_2['x'] = ['foo'] - assert dataset_2['x'].contents == [UpperStrModel('foo')] - - dataset_2.from_data(dict(x=['foo'])) - assert dataset_2.to_data() == dict(x=['foo']) - - dataset_2.from_data(dict(y=['foo', 'bar'], z=['foobar']), upper=True) - assert dataset_2.to_data() == dict(x=['foo'], y=['FOO', 'BAR'], z=['FOOBAR']) - - dataset_2.from_data(dict(y=['foo', 'bar'], z=['foobar']), update=False) - assert dataset_2.to_data() == dict(y=['foo', 'bar'], z=['foobar']) - dataset_2.from_json(dict(x='["foo", "bar"]'), upper=True) - assert dataset_2.to_data() == dict(x=['FOO', 'BAR'], y=['foo', 'bar'], z=['foobar']) - - dataset_2.from_json(dict(x='["foo", "bar"]'), update=False) - assert dataset_2.to_data() == dict(x=['foo', 'bar']) - - with pytest.raises(KeyError): - ListOfUpperStrDataset(dict(x=['bar']), upper=True)['upper'] - with pytest.raises(AssertionError): - assert ListOfUpperStrDataset(x=['bar'], upper=True) - - -def test_list_of_parametrized_model_wrong_keyword() -> None: - with pytest.raises(ParamException): - ListOfUpperStrDataset(dict(x=['bar']), supper=True) - - dataset = ListOfUpperStrDataset() - - with pytest.raises(ParamException): - dataset.from_data(dict(x=['bar']), supper=True) + DefaultOtherStrDataset = DefaultStrDataset.adjust( + 'DefaultOtherStrDataset', + 'DefaultOtherStrModel', + default='other', + ) - with pytest.raises(ParamException): - dataset.from_json(dict(x='["foobar"]'), supper=True) + assert DefaultOtherStrDataset(dict(x=None))['x'].contents == 'other' diff --git a/tests/data/test_model.py b/tests/data/test_model.py index d769a995..57cff69f 100644 --- a/tests/data/test_model.py +++ b/tests/data/test_model.py @@ -23,7 +23,6 @@ import pytest_cases as pc from typing_extensions import TypeVar -from omnipy.api.exceptions import ParamException from omnipy.api.protocols.public.hub import IsRuntime from omnipy.data.helpers import TypeVarStore from omnipy.data.model import is_model_instance, Model, obj_or_model_contents_isinstance @@ -33,7 +32,6 @@ from ..helpers.protocols import AssertModelOrValFunc from .helpers.models import (CBA, DefaultStrModel, - ListOfUpperStrModel, LiteralFiveModel, LiteralFiveOrTextModel, LiteralTextModel, @@ -48,7 +46,6 @@ PydanticChildModel, PydanticParentModel, UppercaseModel, - UpperStrModel, WordSplitterModel) T = TypeVar('T', default=object) @@ -4093,29 +4090,7 @@ class PandasDataFrameModel(Model[pd.DataFrame]): def test_parametrized_model() -> None: - assert UpperStrModel().contents == '' - assert UpperStrModel().is_param_model() - assert UpperStrModel('foo').contents == 'foo' - assert UpperStrModel('bar', upper=True).contents == 'BAR' - - model = UpperStrModel() - - model.from_data('foo') - assert model.contents == 'foo' - - model.from_data('bar', upper=True) - assert model.contents == 'BAR' - - model.from_json('"foobar"') - assert model.contents == 'foobar' - - model.from_json('"foobar"', upper=True) - assert model.contents == 'FOOBAR' - - -def test_parametrized_model_new() -> None: assert ParamUpperStrModel().contents == '' - # assert ParamUpperStrModel().is_param_model() assert ParamUpperStrModel('foo').contents == 'foo' MyUpperStrModel = ParamUpperStrModel.adjust('MyUpperStrModel', upper=True) @@ -4137,72 +4112,28 @@ def test_parametrized_model_new() -> None: assert upper_model.contents == 'FOOBAR' with pytest.raises(AttributeError): - ParamUpperStrModel.adjust('MyUpperStrModel', True) # type: ignore[misc] + ParamUpperStrModel.adjust('MyUpperStrModel', True) def test_parametrized_model_wrong_keyword() -> None: - with pytest.raises(ParamException): - UpperStrModel('bar', supper=True) - - model = UpperStrModel() + with pytest.raises(KeyError): + ParamUpperStrModel.adjust('ParamSupperStrModel', supper=True) - with pytest.raises(ParamException): - model.from_data('bar', supper=True) - with pytest.raises(ParamException): - model.from_json('"foobar"', supper=True) - - -@pytest.mark.skipif( - os.getenv('OMNIPY_FORCE_SKIPPED_TEST') != '1', - reason='ParamModel does not support None values yet. Wait until pydantic v2 removes the' - 'Annotated hack to simplify implementation') def test_parametrized_model_with_none() -> None: with pytest.raises(ValidationError): - UpperStrModel(None) - with pytest.raises(ValidationError): - UpperStrModel(None, upper=True) - - assert DefaultStrModel(None).contents == 'default' - assert DefaultStrModel(None, default='other').contents == 'other' - - -def test_list_of_parametrized_model() -> None: - assert ListOfUpperStrModel().contents == [] - assert ListOfUpperStrModel().is_param_model() - assert ListOfUpperStrModel(['foo']).contents == [UpperStrModel('foo')] - assert ListOfUpperStrModel(['foo']).to_data() == ['foo'] - assert ListOfUpperStrModel(['foo', 'bar'], upper=True).contents \ - == [UpperStrModel('foo', upper=True), UpperStrModel('bar', upper=True)] - assert ListOfUpperStrModel(['foo', 'bar'], upper=True).to_data() == ['FOO', 'BAR'] - - model = ListOfUpperStrModel() - - model.from_data(['foo']) - assert model.contents == [UpperStrModel('foo', upper=False)] - - model.from_data(['foo'], upper=True) - model.append('bar') - assert model.contents == [UpperStrModel('foo', upper=True), UpperStrModel('bar', upper=False)] - - model.from_json('["foo", "bar"]') - assert model.contents == [UpperStrModel('foo', upper=False), UpperStrModel('bar', upper=False)] - - model.from_json('["foo", "bar"]', upper=True) - assert model.contents == [UpperStrModel('foo', upper=True), UpperStrModel('bar', upper=True)] + ParamUpperStrModel(None) + MyUpperStrModel = ParamUpperStrModel.adjust('MyUpperStrModel', upper=True) -def test_list_of_parametrized_model_wrong_keyword() -> None: - with pytest.raises(ParamException): - ListOfUpperStrModel(['bar'], supper=True) + with pytest.raises(ValidationError): + MyUpperStrModel(None) - model = ListOfUpperStrModel() + assert DefaultStrModel(None).contents == 'default' - with pytest.raises(ParamException): - model.from_data(['bar'], supper=True) + DefaultOtherStrModel = DefaultStrModel.adjust('DefaultOtherStrModel', default='other') - with pytest.raises(ParamException): - model.from_json('["foobar"]', supper=True) + assert DefaultOtherStrModel(None).contents == 'other' def test_is_model_instance() -> None: diff --git a/tests/modules/general/test_tasks.py b/tests/modules/general/test_tasks.py index 761ac1a1..7d673f3b 100644 --- a/tests/modules/general/test_tasks.py +++ b/tests/modules/general/test_tasks.py @@ -1,5 +1,11 @@ -from omnipy.data.dataset import Dataset, ParamDataset -from omnipy.data.model import Model, ParamModel +from dataclasses import dataclass +from typing import Generic + +from typing_extensions import TypeVar + +from omnipy.data.dataset import Dataset +from omnipy.data.model import Model +from omnipy.data.param import bind_adjust_dataset_func, bind_adjust_model_func, ParamsBase from omnipy.modules.general.tasks import convert_dataset @@ -11,15 +17,24 @@ class IntModel(Model[int]): ... -class RoundedIntModel(ParamModel[float | int, bool]): +class _RoundedIntModel(Model[float | int]): + @dataclass(kw_only=True) + class Params(ParamsBase): + round_to_nearest: bool = False + @classmethod - def _parse_data(cls, data: float | int, round_to_nearest: bool = False) -> int: + def _parse_data(cls, data: float | int) -> int: if isinstance(data, int): return data - return round(data) if round_to_nearest else int(data) + return round(data) if cls.Params.round_to_nearest else int(data) - ... + +class RoundedIntModel(_RoundedIntModel): + adjust = bind_adjust_model_func( + _RoundedIntModel.clone_model_cls, + _RoundedIntModel.Params, + ) class FloatDataset(Dataset[FloatModel]): @@ -30,10 +45,21 @@ class IntDataset(Dataset[IntModel]): ... -class RoundedIntDataset(ParamDataset[RoundedIntModel, bool]): +RoundedIntModelT = TypeVar('RoundedIntModelT', default=RoundedIntModel) + + +class _RoundedIntDataset(Dataset[RoundedIntModelT], Generic[RoundedIntModelT]): ... +class RoundedIntDataset(_RoundedIntDataset[RoundedIntModel]): + adjust = bind_adjust_dataset_func( + _RoundedIntDataset[RoundedIntModel].clone_dataset_cls, + RoundedIntModel, + RoundedIntModel.Params, + ) + + def test_convert_dataset(): floats = FloatDataset(a=1.23, b=3.6) ints = convert_dataset.run(floats, dataset_cls=IntDataset) @@ -48,8 +74,11 @@ def test_convert_dataset_with_default_params(): assert ints.to_data() == dict(a=1, b=3) -def test_convert_dataset_with_params(): +def test_convert_dataset_with_params() -> None: + RoundToNearestIntDataset = RoundedIntDataset.adjust( + 'RoundToNearestIntDataset', 'RoundToNearestIntModel', round_to_nearest=True) + floats = FloatDataset(a=1.23, b=3.6) - ints = convert_dataset.run(floats, dataset_cls=RoundedIntDataset, round_to_nearest=True) - assert isinstance(ints, RoundedIntDataset) + ints = convert_dataset.run(floats, dataset_cls=RoundToNearestIntDataset) + assert isinstance(ints, RoundToNearestIntDataset) assert ints.to_data() == dict(a=1, b=4) diff --git a/tests/modules/raw/test_datasets.py b/tests/modules/raw/test_datasets.py index 2f3e2387..faf7f386 100644 --- a/tests/modules/raw/test_datasets.py +++ b/tests/modules/raw/test_datasets.py @@ -6,7 +6,7 @@ import pytest from omnipy.api.protocols.public.hub import IsRuntime -from omnipy.data.model import DataWithParams, Model +from omnipy.data.model import Model from omnipy.modules.raw.datasets import (BytesDataset, JoinColumnsToLinesDataset, JoinItemsDataset, @@ -15,13 +15,12 @@ SplitToItemsDataset, SplitToLinesDataset, StrDataset) -from omnipy.modules.raw.models import JoinItemsModel, SplitToItemsModel from ...helpers.functions import assert_model # type: ignore[misc] from ...helpers.protocols import AssertModelOrValFunc -def test_bytes_dataset(): +def test_bytes_dataset() -> None: assert BytesDataset(dict(a=b''))['a'].contents == b'' assert BytesDataset( dict(a=b'\xc3\xa6\xc3\xb8\xc3\xa5'))['a'].contents == b'\xc3\xa6\xc3\xb8\xc3\xa5' @@ -39,7 +38,7 @@ def test_bytes_dataset(): BytesDatasetMyEncoding(dict(a='æøå')) -def test_str_dataset(): +def test_str_dataset() -> None: assert StrDataset(dict(a=''))['a'].contents == '' assert StrDataset(dict(a='æøå'))['a'].contents == 'æøå' assert StrDataset(dict(a=b''))['a'].contents == '' @@ -70,7 +69,6 @@ def test_split_to_and_join_lines_dataset( assert_model_if_dyn_conv_else_val: Annotated[AssertModelOrValFunc, pytest.fixture], ) -> None: - raw_data = """\ Alas, poor Yorick! I knew him, Horatio: a fellow @@ -90,7 +88,11 @@ def test_split_to_and_join_lines_dataset( str, 'Alas, poor Yorick! I knew him, Horatio: a fellow') - lines_unstripped = SplitToLinesDataset(dict(monologue=data), strip=False) + SplitToLinesNoStripDataset = SplitToLinesDataset.adjust( + 'SplitToLinesNoStripDataset', 'SplitToLinesNoStripModel', strip=False) + + lines_unstripped = SplitToLinesNoStripDataset(dict(monologue=data)) + assert_model_if_dyn_conv_else_val(lines_unstripped['monologue'][0], str, ' ') assert_model_if_dyn_conv_else_val(lines_unstripped['monologue'][1], str, @@ -111,6 +113,7 @@ def test_split_to_and_join_lines_dataset( ]) joined_lines = JoinLinesDataset(lines_stripped) + assert joined_lines['monologue'].contents == dedent("""\ Alas, poor Yorick! I knew him, Horatio: a fellow of infinite jest, of most excellent fancy: he hath""") @@ -122,8 +125,7 @@ def test_split_to_and_join_lines_dataset( assert JoinLinesDataset(SplitToLinesDataset(dict(monologue=data))).to_data() == { 'monologue': os.linesep.join([line.strip() for line in raw_data.strip().split(os.linesep)]) } - - assert JoinLinesDataset(SplitToLinesDataset(dict(monologue=data), strip=False)).to_data() == { + assert JoinLinesDataset(SplitToLinesNoStripDataset(dict(monologue=data))).to_data() == { 'monologue': raw_data } @@ -142,12 +144,19 @@ def test_split_to_and_join_items_dataset( data_tab_end = Model[str](raw_data_tab_end) if use_str_model else raw_data_tab_end items_stripped_tab = SplitToItemsDataset(dict(start=data_tab_start, end=data_tab_end)) + assert items_stripped_tab['start'].contents == ['abc', 'def', 'ghi', 'jkl'] assert_model_if_dyn_conv_else_val(items_stripped_tab['start'][1], str, 'def') assert items_stripped_tab['end'][-2:].contents == ['vwx', 'yz'] - items_unstripped_tab = SplitToItemsDataset( - dict(start=data_tab_start, end=data_tab_end), strip=False) + SplitToItemsNoStripDataset = SplitToItemsDataset.adjust( + 'SplitToItemsNoStripDataset', + 'SplitToItemsNoStripModel', + strip=False, + ) + + items_unstripped_tab = SplitToItemsNoStripDataset(dict(start=data_tab_start, end=data_tab_end)) + assert items_unstripped_tab['start'].contents == ['abc', ' def ', 'ghi', 'jkl'] assert_model_if_dyn_conv_else_val(items_unstripped_tab['start'][1], str, ' def ') assert items_unstripped_tab['end'][-2:].contents == ['vwx', 'yz '] @@ -155,8 +164,15 @@ def test_split_to_and_join_items_dataset( data_comma_start = 'abc, def, ghi, jkl' data_comma_end = 'mno, pqr, stu, vwx, yz' - items_stripped_comma = SplitToItemsDataset( - dict(start=data_comma_start, end=data_comma_end), delimiter=',') + SplitToItemsByCommaDataset = SplitToItemsDataset.adjust( + 'SplitToItemsByCommaDataset', + 'SplitToItemsByCommaModel', + delimiter=',', + ) + + items_stripped_comma = SplitToItemsByCommaDataset( + dict(start=data_comma_start, end=data_comma_end)) + assert items_stripped_comma['start'].contents == ['abc', 'def', 'ghi', 'jkl'] assert_model_if_dyn_conv_else_val(items_stripped_comma['start'][1], str, 'def') assert items_stripped_comma['end'][-2:].contents == ['vwx', 'yz'] @@ -171,7 +187,14 @@ def test_split_to_and_join_items_dataset( assert tab_joined_items['start'].contents == 'def\tghi' assert tab_joined_items['end'][1:-1].contents == 'qr\tst' - comma_space_joined_items = JoinItemsDataset(items_stripped_tab, delimiter=', ') + JoinItemsByCommaDataset = JoinItemsDataset.adjust( + 'JoinItemsByCommaDataset', + 'JoinItemsByCommaModel', + delimiter=', ', + ) + + comma_space_joined_items = JoinItemsByCommaDataset(items_stripped_tab) + assert comma_space_joined_items['start'].contents == 'def, ghi' assert comma_space_joined_items['end'][1:-1].contents == 'qr, st' @@ -190,19 +213,32 @@ def test_split_lines_to_columns_and_join_columns_to_lines_dataset( data_tab_rev = [Model[str](_) for _ in raw_data_tab_rev] if use_str_model else raw_data_tab_rev cols_stripped_tab = SplitLinesToColumnsDataset(dict(forward=data_tab_fw, reverse=data_tab_rev)) - assert cols_stripped_tab['forward'][0].contents == ['abc', 'def', 'ghi', 'jkl'] + + assert_model_if_dyn_conv_else_val( + cols_stripped_tab['forward'][0], + list[str], + ['abc', 'def', 'ghi', 'jkl'], + ) assert_model_if_dyn_conv_else_val(cols_stripped_tab['forward'][0][1], str, 'def') - assert cols_stripped_tab['reverse'][1:2].contents \ - == [SplitToItemsModel(' nml\t kji\thgf\t edc')] - assert cols_stripped_tab['reverse'][1:].to_data() \ - == [['nml', 'kji', 'hgf', 'edc'], ['ab']] - - cols_unstripped_tab = SplitLinesToColumnsDataset( - dict(forward=data_tab_fw, reverse=data_tab_rev), strip=False) - assert cols_unstripped_tab['forward'][0].contents == ['abc', ' def ', 'ghi', ' jkl'] + assert cols_stripped_tab['reverse'][1:2].contents == [['nml', 'kji', 'hgf', 'edc']] + assert cols_stripped_tab['reverse'][1:].to_data() == [['nml', 'kji', 'hgf', 'edc'], ['ab']] + + SplitLinesToColumnsNoStripDataset = SplitLinesToColumnsDataset.adjust( + 'SplitLinesToColumnsNoStripDataset', + 'SplitLinesToColumnsNoStripModel', + strip=False, + ) + + cols_unstripped_tab = SplitLinesToColumnsNoStripDataset( + dict(forward=data_tab_fw, reverse=data_tab_rev)) + + assert_model_if_dyn_conv_else_val( + cols_unstripped_tab['forward'][0], + list[str], + ['abc', ' def ', 'ghi', ' jkl'], + ) assert_model_if_dyn_conv_else_val(cols_unstripped_tab['forward'][0][1], str, ' def ') - assert cols_unstripped_tab['reverse'][1:2].contents \ - == [SplitToItemsModel(' nml\t kji\thgf\t edc', strip=False)] + assert cols_unstripped_tab['reverse'][1:2].contents == [[' nml', ' kji', 'hgf', ' edc']] assert cols_unstripped_tab['reverse'][1:].to_data() \ == [[' nml', ' kji', 'hgf', ' edc'], ['ab ']] @@ -214,13 +250,22 @@ def test_split_lines_to_columns_and_join_columns_to_lines_dataset( data_comma_rev = [Model[str](_) for _ in raw_data_comma_rev] \ if use_str_model else raw_data_comma_rev - cols_stripped_comma = SplitLinesToColumnsDataset( - dict(forward=data_comma_fw, reverse=data_comma_rev), delimiter=',') - assert cols_stripped_comma['forward'][0].contents == ['abc', 'def', 'ghi', 'jkl'] + SplitLinesToColumnsByCommaDataset = SplitLinesToColumnsDataset.adjust( + 'SplitLinesToColumnsByCommaDataset', + 'SplitLinesToColumnsByCommaModel', + delimiter=',', + ) + + cols_stripped_comma = SplitLinesToColumnsByCommaDataset( + dict(forward=data_comma_fw, reverse=data_comma_rev)) + + assert_model_if_dyn_conv_else_val( + cols_stripped_comma['forward'][0], + list[str], + ['abc', 'def', 'ghi', 'jkl'], + ) assert_model_if_dyn_conv_else_val(cols_stripped_comma['forward'][0][1], str, 'def') - assert cols_stripped_comma['reverse'][1:2].contents == [ - SplitToItemsModel('nml, kji, hgf, edc', delimiter=',') - ] + assert cols_stripped_comma['reverse'][1:2].contents == [['nml', 'kji', 'hgf', 'edc']] assert cols_stripped_comma['reverse'][1:].to_data() == [['nml', 'kji', 'hgf', 'edc'], ['ab']] for data_file, items in cols_stripped_comma.items(): @@ -230,13 +275,19 @@ def test_split_lines_to_columns_and_join_columns_to_lines_dataset( cols_stripped_tab[data_file] = items[1:] joined_cols = JoinColumnsToLinesDataset(cols_stripped_tab) - assert joined_cols['forward'].contents \ - == [JoinItemsModel('mno\tpqr\tstu\tvwx'), JoinItemsModel('yz')] - assert joined_cols['forward'][1:].contents == [JoinItemsModel('yz')] + + assert joined_cols['forward'].contents == ['mno\tpqr\tstu\tvwx', 'yz'] + assert joined_cols['forward'][1:].contents == ['yz'] assert joined_cols['reverse'].to_data() == ['nml\tkji\thgf\tedc', 'ab'] - joined_cols = JoinColumnsToLinesDataset(cols_stripped_comma, delimiter=', ') - assert joined_cols['forward'].contents \ - == [JoinItemsModel('mno, pqr, stu, vwx'), JoinItemsModel('yz')] - assert joined_cols['forward'][1:].contents == [JoinItemsModel('yz')] - assert joined_cols['reverse'].to_data() == ['nml, kji, hgf, edc', 'ab'] + JoinColumnsToLinesByCommaDataset = JoinColumnsToLinesDataset.adjust( + 'JoinColumnsToLinesByCommaDataset', + 'JoinColumnsToLinesByCommaModel', + delimiter=', ', + ) + + joined_cols_by_comma = JoinColumnsToLinesByCommaDataset(cols_stripped_comma) + + assert joined_cols_by_comma['forward'].contents == ['mno, pqr, stu, vwx', 'yz'] + assert joined_cols_by_comma['forward'][1:].contents == ['yz'] + assert joined_cols_by_comma['reverse'].to_data() == ['nml, kji, hgf, edc', 'ab'] diff --git a/tests/modules/raw/test_models.py b/tests/modules/raw/test_models.py index 58b53423..a1ff0042 100644 --- a/tests/modules/raw/test_models.py +++ b/tests/modules/raw/test_models.py @@ -12,16 +12,14 @@ JoinItemsModel, JoinLinesModel, SplitLinesToColumnsModel, - SplitLinesToColumnsModelNew, SplitToItemsModel, - SplitToItemsModelNew, SplitToLinesModel, StrModel) from ...helpers.protocols import AssertModelOrValFunc -def test_bytes_model(): +def test_bytes_model() -> None: assert BytesModel(b'').contents == b'' assert BytesModel(b'\xc3\xa6\xc3\xb8\xc3\xa5').contents == b'\xc3\xa6\xc3\xb8\xc3\xa5' assert BytesModel('').contents == b'' @@ -87,7 +85,9 @@ def test_split_to_and_join_lines_model( str, 'Alas, poor Yorick! I knew him, Horatio: a fellow') - lines_unstripped = SplitToLinesModel(data, strip=False) + SplitToLinesNoStripModel = SplitToLinesModel.adjust('SplitToLinesNoStripModel', strip=False) + + lines_unstripped = SplitToLinesNoStripModel(data) assert_model_if_dyn_conv_else_val(lines_unstripped[0], str, ' ') # type: ignore[index] assert_model_if_dyn_conv_else_val( lines_unstripped[1], # type: ignore[index] @@ -113,7 +113,7 @@ def test_split_to_and_join_lines_model( assert JoinLinesModel(SplitToLinesModel(data)).contents == os.linesep.join( [line.strip() for line in data.strip().split(os.linesep)]) # type: ignore[attr-defined] - assert JoinLinesModel(SplitToLinesModel(data, strip=False)).contents == raw_data + assert JoinLinesModel(SplitToLinesNoStripModel(data)).contents == raw_data @pytest.mark.parametrize('use_str_model', [False, True], ids=['str', 'Model[str]']) @@ -127,54 +127,13 @@ def test_split_to_and_join_items_model( data_tab = Model[str](raw_data_tab) if use_str_model else raw_data_tab - items_stripped_tab = SplitToItemsModel(data_tab) + items_stripped_tab: SplitToItemsModel = SplitToItemsModel(data_tab) assert items_stripped_tab.contents == ['abc', 'def', 'ghi', 'jkl'] assert_model_if_dyn_conv_else_val(items_stripped_tab[1], str, 'def') # type: ignore[index] assert items_stripped_tab[-2:].contents == ['ghi', 'jkl'] # type: ignore[index] - items_unstripped_tab = SplitToItemsModel(data_tab, strip=False) - assert items_unstripped_tab.contents == ['abc', ' def ', 'ghi', 'jkl'] - assert_model_if_dyn_conv_else_val(items_unstripped_tab[1], str, ' def ') # type: ignore[index] - assert items_unstripped_tab[-2:].contents == ['ghi', 'jkl'] # type: ignore[index] - - raw_data_comma = 'abc, def, ghi, jkl' - - data_comma = Model[str](raw_data_comma) if use_str_model else raw_data_comma - - items_stripped_comma = SplitToItemsModel(data_comma, delimiter=',') - assert items_stripped_comma.contents == ['abc', 'def', 'ghi', 'jkl'] - assert_model_if_dyn_conv_else_val(items_stripped_comma[1], str, 'def') # type: ignore[index] - assert items_stripped_comma[-2:].contents == ['ghi', 'jkl'] # type: ignore[index] - - tab_joined_items = JoinItemsModel(items_stripped_tab[1:3]) # type: ignore[index] - assert tab_joined_items.contents == 'def\tghi' - assert tab_joined_items[1:-1].contents == 'ef\tgh' # type: ignore[index] - - comma_space_joined_items = JoinItemsModel( - items_stripped_comma[1:3], delimiter=', ') # type: ignore[index] - assert comma_space_joined_items.contents == 'def, ghi' - assert comma_space_joined_items[1:-1].contents == 'ef, gh' # type: ignore[index] - - -@pytest.mark.parametrize('use_str_model', [False, True], ids=['str', 'Model[str]']) -def test_split_to_and_join_items_new_model( - use_str_model: bool, - runtime: Annotated[IsRuntime, pytest.fixture], - assert_model_if_dyn_conv_else_val: Annotated[AssertModelOrValFunc, pytest.fixture], -) -> None: - - raw_data_tab = 'abc\t def \tghi\tjkl' - - data_tab = Model[str](raw_data_tab) if use_str_model else raw_data_tab - - items_stripped_tab: SplitToItemsModelNew = SplitToItemsModelNew(data_tab) - assert items_stripped_tab.contents == ['abc', 'def', 'ghi', 'jkl'] - - assert_model_if_dyn_conv_else_val(items_stripped_tab[1], str, 'def') # type: ignore[index] - assert items_stripped_tab[-2:].contents == ['ghi', 'jkl'] # type: ignore[index] - - SplitToItemsNoStripModel = SplitToItemsModelNew.adjust('SplitToItemsNoStripModel', strip=False) + SplitToItemsNoStripModel = SplitToItemsModel.adjust('SplitToItemsNoStripModel', strip=False) items_unstripped_tab = SplitToItemsNoStripModel(data_tab) assert items_unstripped_tab.contents == ['abc', ' def ', 'ghi', 'jkl'] @@ -185,8 +144,7 @@ def test_split_to_and_join_items_new_model( data_comma = Model[str](raw_data_comma) if use_str_model else raw_data_comma - SplitToItemsByCommaModel = SplitToItemsModelNew.adjust( - 'SplitToItemsByCommaModel', delimiter=',') + SplitToItemsByCommaModel = SplitToItemsModel.adjust('SplitToItemsByCommaModel', delimiter=',') items_stripped_comma = SplitToItemsByCommaModel(data_comma) assert items_stripped_comma.contents == ['abc', 'def', 'ghi', 'jkl'] @@ -197,8 +155,10 @@ def test_split_to_and_join_items_new_model( assert tab_joined_items.contents == 'def\tghi' assert tab_joined_items[1:-1].contents == 'ef\tgh' # type: ignore[index] - comma_space_joined_items = JoinItemsModel( - items_stripped_comma[1:3], delimiter=', ') # type: ignore[index] + JoinItemsByCommaModel = JoinItemsModel.adjust('JoinItemsByCommaModel', delimiter=', ') + + comma_space_joined_items = JoinItemsByCommaModel( + items_stripped_comma[1:3]) # type: ignore[index] assert comma_space_joined_items.contents == 'def, ghi' assert comma_space_joined_items[1:-1].contents == 'ef, gh' # type: ignore[index] @@ -214,78 +174,39 @@ def test_split_lines_to_columns_and_join_columns_to_lines_model( data_tab = [Model[str](line) for line in raw_data_tab] if use_str_model else raw_data_tab cols_stripped_tab = SplitLinesToColumnsModel(data_tab) - assert cols_stripped_tab[0].contents == ['abc', 'def', 'ghi', 'jkl'] # type: ignore[index] + assert_model_if_dyn_conv_else_val( + cols_stripped_tab[0], # type: ignore[index] + list[str], + ['abc', 'def', 'ghi', 'jkl']) assert_model_if_dyn_conv_else_val(cols_stripped_tab[0][1], str, 'def') # type: ignore[index] assert cols_stripped_tab[1:2].contents == [ # type: ignore[index] - SplitToItemsModel('mno\t pqr\tstu\t vwx') + ['mno', 'pqr', 'stu', 'vwx'] ] assert cols_stripped_tab[1:].to_data() == [ # type: ignore[index] ['mno', 'pqr', 'stu', 'vwx'], ['yz'] ] - cols_unstripped_tab = SplitLinesToColumnsModel(data_tab, strip=False) - assert cols_unstripped_tab[0].contents == ['abc', ' def ', 'ghi', ' jkl'] # type: ignore[index] + SplitLinesToColumnsNoStripModel = SplitLinesToColumnsModel.adjust( + 'SplitLinesToColumnsNoStripModel', strip=False) + + cols_unstripped_tab = SplitLinesToColumnsNoStripModel(data_tab) + + assert_model_if_dyn_conv_else_val( + cols_unstripped_tab[0], # type: ignore[index] + list[str], + ['abc', ' def ', 'ghi', ' jkl']) assert_model_if_dyn_conv_else_val( cols_unstripped_tab[0][1], # type: ignore[index] str, ' def ') assert cols_unstripped_tab[1:2].contents == [ # type: ignore[index] - SplitToItemsModel('mno\t pqr\tstu\t vwx', strip=False) + ['mno', ' pqr', 'stu', ' vwx'] ] assert cols_unstripped_tab[1:].to_data() == [ # type: ignore[index] ['mno', ' pqr', 'stu', ' vwx'], ['yz'] ] - raw_data_comma = ['abc, def, ghi, jkl', 'mno, pqr, stu, vwx', 'yz'] - data_comma = [Model[str](line) for line in raw_data_comma] if use_str_model else raw_data_comma - - cols_stripped_comma = SplitLinesToColumnsModel(data_comma, delimiter=',') - assert cols_stripped_comma[0].contents == ['abc', 'def', 'ghi', 'jkl'] # type: ignore[index] - assert_model_if_dyn_conv_else_val(cols_stripped_comma[0][1], str, 'def') # type: ignore[index] - - assert cols_stripped_comma[1:2].contents == [ # type: ignore[index] - SplitToItemsModel('mno, pqr, stu, vwx', delimiter=',') - ] - assert cols_stripped_comma[1:].to_data() == [ # type: ignore[index] - ['mno', 'pqr', 'stu', 'vwx'], ['yz'] - ] - - joined_cols = JoinColumnsToLinesModel(cols_stripped_tab[1:]) # type: ignore[index] - assert joined_cols.contents == [JoinItemsModel('mno\tpqr\tstu\tvwx'), JoinItemsModel('yz')] - assert joined_cols[1:].contents == [JoinItemsModel('yz')] # type: ignore[index] - assert joined_cols.to_data() == ['mno\tpqr\tstu\tvwx', 'yz'] - - joined_cols = JoinColumnsToLinesModel( - cols_stripped_comma[1:], delimiter=', ') # type: ignore[index] - assert joined_cols.contents == [JoinItemsModel('mno, pqr, stu, vwx'), JoinItemsModel('yz')] - assert joined_cols[1:].contents == [JoinItemsModel('yz')] # type: ignore[index] - assert joined_cols.to_data() == ['mno, pqr, stu, vwx', 'yz'] - - -@pytest.mark.parametrize('use_str_model', [False, True], ids=['str', 'Model[str]']) -def test_split_lines_to_columns_new_model( - use_str_model: bool, - runtime: Annotated[IsRuntime, pytest.fixture], - assert_model_if_dyn_conv_else_val: Annotated[AssertModelOrValFunc, pytest.fixture], -) -> None: - - raw_data_tab = ['abc\t def \tghi\t jkl', 'mno\t pqr\tstu\t vwx', 'yz'] - data_tab = [Model[str](line) for line in raw_data_tab] if use_str_model else raw_data_tab - - cols_stripped_tab: SplitLinesToColumnsModelNew = SplitLinesToColumnsModelNew(data_tab) - assert_model_if_dyn_conv_else_val( - cols_stripped_tab[0], # type: ignore[index] - list[str], - ['abc', 'def', 'ghi', 'jkl']) - assert_model_if_dyn_conv_else_val(cols_stripped_tab[0][1], str, 'def') # type: ignore[index] - assert cols_stripped_tab[1:2].contents == [ # type: ignore[index] - ['mno', 'pqr', 'stu', 'vwx'] - ] - assert cols_stripped_tab[1:].to_data() == [ # type: ignore[index] - ['mno', 'pqr', 'stu', 'vwx'], ['yz'] - ] - - SplitLinesToColumnsNoStripModel = SplitLinesToColumnsModelNew.adjust( + SplitLinesToColumnsNoStripModel = SplitLinesToColumnsModel.adjust( 'SplitLinesToColumnsNoStripModel', strip=False) cols_unstripped_tab = SplitLinesToColumnsNoStripModel(data_tab) @@ -310,18 +231,38 @@ def test_split_lines_to_columns_new_model( raw_data_comma = ['abc, def, ghi, jkl', 'mno, pqr, stu, vwx', 'yz'] data_comma = [Model[str](line) for line in raw_data_comma] if use_str_model else raw_data_comma - SplitLinesToColumnsWithCommaModel = SplitLinesToColumnsModelNew.adjust( + SplitLinesToColumnsByCommaModel = SplitLinesToColumnsModel.adjust( 'SplitLinesToColumnsNoStripModel', delimiter=',') - cols_stripped_comma = SplitLinesToColumnsWithCommaModel(data_comma) + + cols_stripped_comma = SplitLinesToColumnsByCommaModel(data_comma) + assert_model_if_dyn_conv_else_val( - cols_stripped_comma[0], # type: ignore[index] + cols_stripped_comma[0], # type: ignore[index] list[str], ['abc', 'def', 'ghi', 'jkl']) - assert_model_if_dyn_conv_else_val(cols_stripped_comma[0][1], str, 'def') # type: ignore[index] - + assert_model_if_dyn_conv_else_val( + cols_stripped_comma[0][1], # type: ignore[index] + str, + 'def', + ) assert cols_stripped_comma[1:2].contents == [ # type: ignore[index] ['mno', 'pqr', 'stu', 'vwx'] ] assert cols_stripped_comma[1:].to_data() == [ # type: ignore[index] ['mno', 'pqr', 'stu', 'vwx'], ['yz'] ] + + joined_cols = JoinColumnsToLinesModel(cols_stripped_tab[1:]) # type: ignore[index] + + assert joined_cols.contents == ['mno\tpqr\tstu\tvwx', 'yz'] + assert joined_cols[1:].contents == ['yz'] # type: ignore[index] + assert joined_cols.to_data() == ['mno\tpqr\tstu\tvwx', 'yz'] + + JoinColumnsByCommaToLinesModel = JoinColumnsToLinesModel.adjust( + 'JoinColumnsByCommaToLinesModel', delimiter=', ') + + joined_cols_by_comma = JoinColumnsByCommaToLinesModel( + cols_stripped_comma[1:]) # type: ignore[index] + assert joined_cols_by_comma.contents == ['mno, pqr, stu, vwx', 'yz'] + assert joined_cols_by_comma[1:].contents == ['yz'] # type: ignore[index] + assert joined_cols_by_comma.to_data() == ['mno, pqr, stu, vwx', 'yz'] \ No newline at end of file