fix: preserve annotation metadata when cloning wrapped partials

Addresses Codex P1: when reconstructing a partial to strip __wrapped__,
copy over functools.WRAPPER_ASSIGNMENTS (__module__, __qualname__, etc.)
so deferred annotation resolution works correctly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
strawgate 2026-05-12 22:46:54 -05:00
commit 9fd60025f8

View file

@ -66,7 +66,21 @@ def prepare_callable(fn: Callable[..., Any]) -> Callable[..., Any]:
# Strip __wrapped__ from partials so Pydantic sees the partial's own
# signature with bound args removed, not the original function's signature.
if isinstance(fn, functools.partial) and hasattr(fn, "__wrapped__"):
old = fn
fn = functools.partial(fn.func, *fn.args, **fn.keywords)
# Preserve annotation metadata copied by functools.update_wrapper
# (e.g. __module__, __qualname__) needed for deferred annotation
# resolution when `from __future__ import annotations` is active.
for attr in functools.WRAPPER_ASSIGNMENTS:
try:
val = getattr(old, attr)
except AttributeError:
pass
else:
try:
setattr(fn, attr, val)
except AttributeError:
pass
# Callable classes (not routines, not partials) → unwrap to __call__
if not inspect.isroutine(fn) and not isinstance(fn, functools.partial):