Skip to content

Commit

Permalink
Add is_iterable and use in test_asset_check_decorator
Browse files Browse the repository at this point in the history
  • Loading branch information
schrockn committed Jun 9, 2024
1 parent d7d3123 commit 18706f1
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 4 deletions.
28 changes: 28 additions & 0 deletions python_modules/dagster/dagster/_check/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,34 @@ def opt_nullable_iterable_param(
return iterable_param(obj, param_name, of_type, additional_message)


def old_is_iterable(
obj: object,
of_type: Optional[TypeOrTupleOfTypes] = None,
additional_message: Optional[str] = None,
) -> Iterable:
if not isinstance(obj, Iterable):
raise _type_mismatch_error(obj, list, additional_message)

if not of_type:
return obj

return _check_iterable_items(obj, of_type, "list")


def is_iterable(
obj: object,
of_type: Optional[TTypeOrTupleOfTTypes[T]] = None,
additional_message: Optional[str] = None,
) -> Iterable[T]:
if not isinstance(obj, Iterable):
raise _type_mismatch_error(obj, list, additional_message)

if not of_type:
return obj

return _check_iterable_items(obj, of_type, "list")


# ########################
# ##### SET
# ########################
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -848,9 +848,7 @@ def checks() -> Iterable[AssetCheckResult]:
yield AssetCheckResult(passed=False, asset_key="asset1", check_name="check2")
yield AssetCheckResult(passed=True, asset_key="asset2")

checks_ret_obj = checks()
assert isinstance(checks_ret_obj, Iterable)
results = check.is_list(list(checks_ret_obj), of_type=AssetCheckResult)
results = check.is_list(check.is_iterable(checks()), of_type=AssetCheckResult)
assert len(results) == 3
assert all(isinstance(result, AssetCheckResult) for result in results)
assert results[0].passed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import sys
from collections import defaultdict
from contextlib import contextmanager
from typing import Dict, Iterable, List, Mapping, Optional, Sequence, Set, Union
from typing import Any, Dict, Iterable, List, Mapping, Optional, Sequence, Set, Union

import dagster._check as check
import pytest
Expand Down Expand Up @@ -1514,6 +1514,42 @@ def test_opt_iterable():
check.opt_iterable_param(["atr", None], "nonedoesntcount", of_type=str)


def test_is_iterable() -> None:
assert check.is_iterable([]) == []
assert check.is_iterable((1, 2)) == tuple([1, 2])
assert check.is_iterable("foo") == "foo" # str is iterable
assert check.is_iterable({"a": 1}) == {"a": 1} # dict is iterable

assert check.is_iterable([1, "str"]) == [1, "str"]

with pytest.raises(CheckError):
check.is_iterable([1, "str"], of_type=int)

with pytest.raises(CheckError):
check.is_iterable([1, "str"], of_type=str)

with pytest.raises(CheckError):
check.is_iterable(None)

with pytest.raises(CheckError):
check.is_iterable(1)


def test_is_iterable_typing() -> None:
def returns_iterable_of_int_but_typed_any() -> Any:
return [1, 2]

def returns_iterable_of_t() -> Iterable[int]:
any_typed = returns_iterable_of_int_but_typed_any()
retval = check.is_iterable(any_typed, of_type=str)
# That the type: ignore is necessary is proof that
# is_iterable flows type information correctly
return retval # type: ignore

# meaningless assert. The test is show the typechecker working
assert returns_iterable_of_t


# ###################################################################################################
# ##### CHECK BUILDER
# ###################################################################################################
Expand Down

0 comments on commit 18706f1

Please sign in to comment.