Skip to content

Commit a7fbf26

Browse files
committed
added support for None to be implicitly interpreted as NoneType in type-checking/coercing
1 parent e52e32b commit a7fbf26

File tree

2 files changed

+78
-1
lines changed

2 files changed

+78
-1
lines changed

pydra/utils/tests/test_typing.py

+72
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,46 @@ def test_generic_is_subclass3():
737737
assert not TypeParser.is_subclass(ty.List[float], ty.List[int])
738738

739739

740+
def test_none_is_subclass1():
741+
assert TypeParser.is_subclass(None, ty.Union[int, None])
742+
743+
744+
def test_none_is_subclass2():
745+
assert not TypeParser.is_subclass(None, ty.Union[int, float])
746+
747+
748+
def test_none_is_subclass3():
749+
assert TypeParser.is_subclass(ty.Tuple[int, None], ty.Tuple[int, None])
750+
751+
752+
def test_none_is_subclass4():
753+
assert TypeParser.is_subclass(None, None)
754+
755+
756+
def test_none_is_subclass5():
757+
assert not TypeParser.is_subclass(None, int)
758+
759+
760+
def test_none_is_subclass6():
761+
assert not TypeParser.is_subclass(int, None)
762+
763+
764+
def test_none_is_subclass7():
765+
assert TypeParser.is_subclass(None, type(None))
766+
767+
768+
def test_none_is_subclass8():
769+
assert TypeParser.is_subclass(type(None), None)
770+
771+
772+
def test_none_is_subclass9():
773+
assert TypeParser.is_subclass(type(None), type(None))
774+
775+
776+
def test_none_is_subclass10():
777+
assert TypeParser.is_subclass(type(None), type(None))
778+
779+
740780
@pytest.mark.skipif(
741781
sys.version_info < (3, 9), reason="Cannot subscript tuple in < Py3.9"
742782
)
@@ -780,3 +820,35 @@ def test_type_is_instance3():
780820

781821
def test_type_is_instance4():
782822
assert TypeParser.is_instance(Json, type)
823+
824+
825+
def test_type_is_instance5():
826+
assert TypeParser.is_instance(None, None)
827+
828+
829+
def test_type_is_instance6():
830+
assert TypeParser.is_instance(None, type(None))
831+
832+
833+
def test_type_is_instance7():
834+
assert not TypeParser.is_instance(None, int)
835+
836+
837+
def test_type_is_instance8():
838+
assert not TypeParser.is_instance(1, None)
839+
840+
841+
def test_type_is_instance9():
842+
assert TypeParser.is_instance(None, ty.Union[int, None])
843+
844+
845+
def test_type_is_instance10():
846+
assert TypeParser.is_instance(1, ty.Union[int, None])
847+
848+
849+
def test_type_is_instance11():
850+
assert not TypeParser.is_instance(None, ty.Union[int, str])
851+
852+
853+
def test_type_is_instance12():
854+
assert not TypeParser.is_instance((1, None), ty.Tuple[int, None])

pydra/utils/typing.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,7 @@ def matches_type(
603603
def is_instance(
604604
cls,
605605
obj: object,
606-
candidates: ty.Union[ty.Type[ty.Any], ty.Sequence[ty.Type[ty.Any]]],
606+
candidates: ty.Union[ty.Type[ty.Any], ty.Sequence[ty.Type[ty.Any]], None],
607607
) -> bool:
608608
"""Checks whether the object is an instance of cls or that cls is typing.Any,
609609
extending the built-in isinstance to check nested type args
@@ -615,6 +615,8 @@ def is_instance(
615615
candidates : type or ty.Iterable[type]
616616
the candidate types to check the object against
617617
"""
618+
if candidates is None:
619+
candidates = [type(None)]
618620
if not isinstance(candidates, ty.Sequence):
619621
candidates = [candidates]
620622
for candidate in candidates:
@@ -656,6 +658,9 @@ def is_subclass(
656658
any_ok : bool
657659
whether klass=typing.Any should return True or False
658660
"""
661+
if klass is None:
662+
# Implicitly convert None to NoneType, like in other typing
663+
klass = type(None)
659664
if not isinstance(candidates, ty.Sequence):
660665
candidates = [candidates]
661666
if ty.Any in candidates:

0 commit comments

Comments
 (0)