Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion toolz/functoolz.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,13 @@ def __get__(self, obj, objtype=None):
@instanceproperty
def __signature__(self):
base = inspect.signature(self.first)
last = inspect.signature(self.funcs[-1])
try:
last = inspect.signature(self.funcs[-1])
except (TypeError, ValueError):
# The last function is only consulted for its return annotation,
# so a callable without an introspectable signature (e.g.
# ``max``) should not make the whole composition fail.
return base.replace(return_annotation=inspect.Signature.empty)
return base.replace(return_annotation=last.return_annotation)

__wrapped__ = instanceproperty(attrgetter('first'))
Expand Down
11 changes: 11 additions & 0 deletions toolz/tests/test_functoolz.py
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,17 @@ class MyClass:
assert len(inspect.signature(MyClass().method).parameters) == 4


def test_compose_signature_last_func_without_signature():
# ``max`` has no introspectable signature, but it is only used for the
# return annotation of the composition, so it must not break inspection.
def first(a: int, b: int) -> int:
return a + b

sig = inspect.signature(compose(max, first))
assert list(sig.parameters) == ['a', 'b']
assert sig.return_annotation is inspect.Signature.empty


def test_compose_annotations():
# Parameter annotations come from the first function applied and the
# return annotation from the last one: the same view as __signature__.
Expand Down