Skip to content

Commit

Permalink
fix: Handle partials as functions while inspecting
Browse files Browse the repository at this point in the history
This doesn't solve the issue that `functools.partial` does not set the `__module__` attribute of the resulting partial to be the module of the wrapped function, which means that the inspector always treats partials as aliases (since it finds out they were defined in the `functools` module).

This issue cannot be fixed on our side, because special-casing partials to decide that they are always defined in the current module would not work for partials imported from elsewhere.

Therefore, devs *must* set the `__module__` attribute of partials themselves.
  • Loading branch information
pawamoy committed Jun 16, 2024
1 parent e6d3bb6 commit be29c32
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 5 deletions.
11 changes: 6 additions & 5 deletions src/griffe/agents/nodes/_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,14 @@ def kind(self) -> ObjectKind:
return ObjectKind.BUILTIN_METHOD
if self.is_coroutine:
return ObjectKind.COROUTINE
if self.is_function:
return ObjectKind.FUNCTION
if self.is_builtin_function:
return ObjectKind.BUILTIN_FUNCTION
if self.is_property:
return ObjectKind.PROPERTY
if self.is_method_descriptor:
return ObjectKind.METHOD_DESCRIPTOR
if self.is_function:
return ObjectKind.FUNCTION
if self.is_property:
return ObjectKind.PROPERTY
return ObjectKind.ATTRIBUTE

@cached_property
Expand All @@ -152,7 +152,8 @@ def is_class(self) -> bool:
@cached_property
def is_function(self) -> bool:
"""Whether this node's object is a function."""
return inspect.isfunction(self.obj)
# `inspect.isfunction` returns `False` for partials.
return inspect.isfunction(self.obj) or (callable(self.obj) and not self.is_class)

@cached_property
def is_builtin_function(self) -> bool:
Expand Down
17 changes: 17 additions & 0 deletions tests/test_inspector.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,20 @@ def test_inspecting_objects_from_private_builtin_stdlib_moduless() -> None:
ast = inspect("_ast")
assert "Assign" in ast.members
assert not ast["Assign"].is_alias


def test_inspecting_partials_as_functions() -> None:
"""Assert partials are correctly inspected as functions."""
with temporary_inspected_module(
"""
from functools import partial
def func(a: int, b: int) -> int: pass
partial_func = partial(func, 1)
partial_func.__module__ = __name__
""",
) as module:
partial_func = module["partial_func"]
assert partial_func.is_function
assert partial_func.parameters[0].name == "b"
assert partial_func.parameters[0].annotation.name == "int"
assert partial_func.returns.name == "int"

0 comments on commit be29c32

Please sign in to comment.