Skip to content

Commit

Permalink
handle attribute now works without returns
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasborgen committed Dec 26, 2021
1 parent c07636c commit 2dbcf1c
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 23 deletions.
10 changes: 10 additions & 0 deletions kaiba/casting.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@
from kaiba.models.casting import CastToOptions


def unsafe_get_casting_function(cast_to: CastToOptions) -> Callable:
"""Return casting function depending on name."""
if cast_to == CastToOptions.INTEGER:
return CastToInteger()

elif cast_to == CastToOptions.DECIMAL:
return CastToDecimal()

return CastToDate()

@safe
def get_casting_function(cast_to: CastToOptions) -> Callable:
"""Return casting function depending on name."""
Expand Down
60 changes: 59 additions & 1 deletion kaiba/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from returns.pointfree import bind
from returns.result import Failure, ResultE, safe

from kaiba.casting import get_casting_function
from kaiba.casting import get_casting_function, unsafe_get_casting_function
from kaiba.models.base import AnyType
from kaiba.models.casting import Casting
from kaiba.models.if_statement import Conditions, IfStatement
Expand Down Expand Up @@ -168,6 +168,38 @@ def _apply_statement(
return statement.otherwise or if_value


def unsafe_apply_separator(
mapped_values: List[AnyType],
separator: str,
) -> AnyType:
"""Apply separator between the values of a List[Any].
:param mapped_values: The list of values to join with the separator
:type mapped_values: List[AnyType]
:param separator: :term:`separator` value to join mapped_values list with
:type separator: str
:return: Success/Failure containers
:rtype: AnyType
Example
>>> from returns.pipeline import is_successful
>>> apply_separator(['a', 'b', 'c'], ' ').unwrap()
'a b c'
>>> apply_separator([1, 'b', True], ' ').unwrap()
'1 b True'
>>> is_successful(apply_separator([], ' '))
False
"""

if len(mapped_values) == 1:
return mapped_values[0]

return separator.join([str(mapped) for mapped in mapped_values])


@safe
def apply_separator(
mapped_values: List[AnyType],
Expand Down Expand Up @@ -342,6 +374,32 @@ def apply_regex( # noqa: WPS212, WPS234
return matches[num_group]


def unsafe_apply_casting(
value_to_cast: AnyType,
casting: Casting,
) -> AnyType:
"""Casting one type of code to another.
:param casting: :term:`casting` object
:type casting: dict
:param value_to_cast: The value to cast to casting['to']
:type value_to_cast: AnyType
:return: Success/Failure containers
:rtype: AnyType
Example
>>> apply_casting('123', Casting(**{'to': 'integer'})).unwrap()
123
>>> apply_casting('123.12', Casting(**{'to': 'decimal'})).unwrap()
Decimal('123.12')
"""
function = unsafe_get_casting_function(casting.to)

return function(value_to_cast, casting.original_format).unwrap()


def apply_casting(
value_to_cast: Optional[AnyType],
casting: Casting,
Expand Down
95 changes: 73 additions & 22 deletions kaiba/handlers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Dict, List, Union
from typing import Any, Dict, List, Optional, Union

from returns.curry import partial
from returns.pipeline import flow, is_successful
Expand All @@ -13,9 +13,11 @@
apply_regex,
apply_separator,
apply_slicing,
unsafe_apply_casting,
unsafe_apply_default,
unsafe_apply_if_statements,
unsafe_apply_regex,
unsafe_apply_separator,
)
from kaiba.models.attribute import Attribute
from kaiba.models.base import AnyType
Expand Down Expand Up @@ -73,6 +75,51 @@ def handle_data_fetcher(
return Failure(ValueError('Failed to produce a value'))


def unsafe_handle_data_fetcher(
collection: Union[Dict[str, Any], List[Any]],
cfg: DataFetcher,
) -> Optional[AnyType]:
"""Find a data at path or produce a value.
return value can be:
- value found at path
- value found but sliced
- value found applied to regular expression
- conditional value depending on if statements
- default value if all the above still produces `None`
Flow description:
find data from path or None ->
apply regular expression ->
apply slicing ->
apply if statements ->
return default value if Failure else mapped value
"""

produced_value = None

produced_value = unsafe_fetch_data_by_keys(
collection, path=cfg.path,
)

if produced_value and cfg.regex:
produced_value = unsafe_apply_regex(produced_value, regex=cfg.regex)

if produced_value and cfg.slicing:
produced_value = apply_slicing(produced_value, cfg.slicing)

produced_value = unsafe_apply_if_statements(
produced_value,
cfg.if_statements,
)

return unsafe_apply_default(
produced_value,
cfg.default,
)


def handle_attribute(
collection: Union[Dict[str, Any], List[Any]],
cfg: Attribute,
Expand All @@ -90,29 +137,33 @@ def handle_attribute(
Return Result
"""

fetched_values = [
fetched.unwrap()
for fetched in # noqa: WPS361
[
handle_data_fetcher(collection, data_fetcher)
fetched
for fetched in [ # noqa: WPS361
unsafe_handle_data_fetcher(collection, data_fetcher)
for data_fetcher in cfg.data_fetchers
]
if is_successful(fetched)
if fetched is not None
]

# partially declare if statement and casting functions
ifs = partial(apply_if_statements, statements=cfg.if_statements)

cast = safe(lambda the_value: the_value)
if cfg.casting:
cast = partial(apply_casting, casting=cfg.casting)

return flow(
apply_separator(fetched_values, separator=cfg.separator),
fix(lambda _: None), # type: ignore
bind(ifs),
bind(cast),
rescue(
lambda _: apply_default(default=cfg.default),
),
)
attribute = None

if fetched_values:
attribute = unsafe_apply_separator(
fetched_values,
separator=cfg.separator,
)

attribute = unsafe_apply_if_statements(attribute, cfg.if_statements)

if attribute and cfg.casting:
attribute = unsafe_apply_casting(attribute, cfg.casting)

attribute = unsafe_apply_default(attribute, default=cfg.default)

# just return the modals so tests will still work.
if attribute:
return Success(attribute)

return Failure(ValueError('Failed to produce a value'))

0 comments on commit 2dbcf1c

Please sign in to comment.