[Draft] Support functools.partial as tools, prompts, and resources#3269
[Draft] Support functools.partial as tools, prompts, and resources#3269
functools.partial as tools, prompts, and resources#3269Conversation
Co-authored-by: Bill Easton <strawgate@users.noreply.github.com> 🤖 Generated with Claude Code
|
/marvin why do we need |
|
Claude finished @strawgate's task in 1m 34s —— View job Why the
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7ef84c0eab
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if isinstance(fn, functools.partial) and hasattr(fn, "__wrapped__"): | ||
| fn = functools.partial(fn.func, *fn.args, **fn.keywords) |
There was a problem hiding this comment.
Preserve partial metadata when stripping wrapped
Reconstructing the functools.partial object here removes attributes that functools.update_wrapper added (notably __annotations__ and module context), and downstream schema generation relies on that metadata for resolving postponed/forward type hints. For wrapped partial tools that use string or forward-referenced annotations, this can degrade or break input/output schema generation during registration. Prefer removing only __wrapped__ (or copying metadata onto the new partial) so bound-argument handling does not discard type information.
Useful? React with 👍 / 👎.
| if isinstance(fn, functools.partial) and hasattr(fn, "__wrapped__"): | ||
| fn = functools.partial(fn.func, *fn.args, **fn.keywords) |
There was a problem hiding this comment.
Keep resource docstring after partial normalization
This creates a fresh partial and drops the wrapped function metadata copied by update_wrapper, including __doc__; later description=metadata.description or inspect.getdoc(fn) will then use the generic functools.partial doc instead of the resource function's docstring. As a result, resources registered from wrapped partials expose incorrect descriptions unless callers always pass description explicitly.
Useful? React with 👍 / 👎.
Closes #3266
functools.partialobjects failed with aPydanticSchemaGenerationErrorbecauseinspect.isroutine()returnsFalsefor them, causing FastMCP to unwrap them via.__call__into amethod-wrapperthat Pydantic can't handle. Additionally,functools.update_wrapper()sets__wrapped__which causes Pydantic to follow it back to the original function, ignoring the partial's bound arguments.This PR teaches FastMCP to recognize
functools.partialas a first-class callable throughout the tool, prompt, and resource pipelines — skipping the__call__unwrap and stripping__wrapped__so Pydantic sees the partial's own signature.Generated with Claude Code