Skip to content

Commit

Permalink
fix(common): support optional keyword-only parameters when validating…
Browse files Browse the repository at this point in the history
… callables
  • Loading branch information
jcrist committed Sep 8, 2023
1 parent c42a798 commit 519a9e0
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 10 deletions.
14 changes: 6 additions & 8 deletions ibis/common/patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -1520,20 +1520,18 @@ def match(self, value, context):
fn = annotated(self.args, self.return_, value)

has_varargs = False
positional, keyword_only = [], []
positional = []
for p in fn.__signature__.parameters.values():
if p.kind in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD):
positional.append(p)
elif p.kind is Parameter.KEYWORD_ONLY:
keyword_only.append(p)
elif p.kind is Parameter.KEYWORD_ONLY and p.default is Parameter.empty:
raise MatchError(
"Callable has mandatory keyword-only arguments which cannot be specified"
)
elif p.kind is Parameter.VAR_POSITIONAL:
has_varargs = True

if keyword_only:
raise MatchError(
"Callable has mandatory keyword-only arguments which cannot be specified"
)
elif len(positional) > len(self.args):
if len(positional) > len(self.args):
# Callable has more positional arguments than expected")
return NoMatch
elif len(positional) < len(self.args) and not has_varargs:
Expand Down
11 changes: 9 additions & 2 deletions ibis/common/tests/test_patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -527,15 +527,18 @@ def func_with_args(a, b, *args):
def func_with_kwargs(a, b, c=1, **kwargs):
return str(a) + b + str(c)

def func_with_mandatory_kwargs(*, c):
def func_with_optional_keyword_only_kwargs(a, *, c=1):
return a + c

def func_with_required_keyword_only_kwargs(*, c):
return c

p = CallableWith([InstanceOf(int), InstanceOf(str)])
assert p.match(10, context={}) is NoMatch

msg = "Callable has mandatory keyword-only arguments which cannot be specified"
with pytest.raises(MatchError, match=msg):
p.match(func_with_mandatory_kwargs, context={})
p.match(func_with_required_keyword_only_kwargs, context={})

# Callable has more positional arguments than expected
p = CallableWith([InstanceOf(int)] * 2)
Expand All @@ -556,6 +559,10 @@ def func_with_mandatory_kwargs(*, c):
with pytest.raises(ValidationError, match="2 doesn't match InstanceOf"):
wrapped(1, 2)

p = CallableWith([InstanceOf(int)])
wrapped = p.match(func_with_optional_keyword_only_kwargs, context={})
assert wrapped(1) == 2


def test_pattern_list():
p = PatternSequence([1, 2, InstanceOf(int), ...])
Expand Down

0 comments on commit 519a9e0

Please sign in to comment.