Skip to content

Commit

Permalink
Add the NoneType hook in type_def (#8)
Browse files Browse the repository at this point in the history
* add support for expanding signiture list to its base classes when **kwargs is contained in a construction method

* remove fixed FIXME comment

* add a switch to turn off the inherent signature function

* disable the inherent function by default and fix a bug in register.get(), where an unexpected exception may be raised

* add more tests for better code coverage

* fix typos

* add typedef hook for NoneType

* enable full test in test_config_type_def

* remove redundant test call

---------

Co-authored-by: Yansen Wang <[email protected]>
  • Loading branch information
victorywys and Yansen Wang committed Jul 17, 2023
1 parent bc4697c commit 673b161
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 0 deletions.
34 changes: 34 additions & 0 deletions tests/test_config_type_def.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ def test_any():
assert TypeDef.dump(typing.Any, '456') == '456'


def test_none():
assert TypeDef.load(type(None), None) == None
assert TypeDef.dump(type(None), None) == None

with pytest.raises(ValidationError, match='must be NoneType'):
TypeDef.load(type(None), 1)
with pytest.raises(ValidationError, match='Expected None'):
TypeDef.dump(type(None), "123")


def test_unsupported_type():
with pytest.raises(TypeError, match=r'.*Callable\[\[\], str\].*'):
TypeDef.load(typing.Callable[[], str], lambda x: x)
Expand Down Expand Up @@ -197,6 +207,29 @@ class Foo:
assert TypeDef.dump(typing.Union[pathlib.Path, None], None) == None


def test_complex_optional_union():
@dataclass
class Foo:
bar: int = 1

assert TypeDef.load(typing.Optional[typing.Union[pathlib.Path, Foo]], '/bin') == pathlib.Path('/bin')
assert TypeDef.load(typing.Optional[typing.Union[pathlib.Path, Foo]], {'bar': 2}).bar == 2
assert TypeDef.load(typing.Optional[typing.Union[pathlib.Path, Foo]], None) == None

assert TypeDef.load(typing.Union[pathlib.Path, Foo, type(None)], None) == None
with pytest.raises(ValidationError, match='are exhausted'):
TypeDef.load(typing.Union[pathlib.Path, Foo, type(None)], [1, 2.5, '3'])

with pytest.raises(ValidationError, match='are exhausted'):
TypeDef.dump(typing.Union[pathlib.Path, Foo, type(None)], "/bin")

assert TypeDef.dump(typing.Optional[typing.Union[pathlib.Path, str]], '/bin') == '/bin'
assert TypeDef.dump(typing.Optional[typing.Union[pathlib.Path, Foo]], Foo(bar=2))['bar'] == 2
assert TypeDef.dump(typing.Optional[typing.Union[pathlib.Path, Foo]], None) == None

assert TypeDef.dump(typing.Union[pathlib.Path, Foo, type(None)], None) == None


def test_class_config():
class module:
def __init__(self, a, b, c=1):
Expand Down Expand Up @@ -259,3 +292,4 @@ def __init__(self, a: typing.Union[submodule, ClassConfig[submodule]],
{'a': {'a': 1, 'b': 2}, 'b': {'a': 3, 'b': 4, 'c': 5}}).b.c == 5
assert TypeDef.load(ClassConfig[module],
{'a': {'a': 1, 'b': 2}, 'b': {'a': 3, 'b': 4, 'c': 5}}).build().b._c == 5

19 changes: 19 additions & 0 deletions utilsd/config/type_def.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,24 @@ def to_plain(self, obj, ctx):
return obj


class NoneTypeDef(TypeDef):
@classmethod
def new(cls, type_):
if type_ is type(None):
return cls(type_)
return None

def from_plain(self, plain, ctx):
ctx.mark_cli_anchor_point(type(None))
return plain

def to_plain(self, obj, ctx):
if obj is None:
return None
else:
raise TypeError(f'Expected None, got {obj}')


class OptionalDef(TypeDef):
@classmethod
def new(cls, type_):
Expand Down Expand Up @@ -660,6 +678,7 @@ def to_plain(self, obj, ctx):

# register all the modules in this file
TypeDefRegistry.register_module(module=AnyDef)
TypeDefRegistry.register_module(module=NoneTypeDef)
TypeDefRegistry.register_module(module=OptionalDef)
TypeDefRegistry.register_module(module=PathDef)
TypeDefRegistry.register_module(module=ListDef)
Expand Down

0 comments on commit 673b161

Please sign in to comment.