Skip to content

FIX: don't evaluate annotations when collecting signature parameters (Python 3.14 / PEP 649) - #700

Merged
larsoner merged 2 commits into
numpy:mainfrom
mscheltienne:fix
Jul 27, 2026
Merged

FIX: don't evaluate annotations when collecting signature parameters (Python 3.14 / PEP 649)#700
larsoner merged 2 commits into
numpy:mainfrom
mscheltienne:fix

Conversation

@mscheltienne

Copy link
Copy Markdown
Contributor

Fix suggested by Claude Opus 4.8 on failures observed today while modernizing our SW stack to 3.14+.

On Python 3.14, PEP 649 evaluates annotations lazily and inspect.signature() now resolves them (VALUE format) when building the signature. As a result, running numpydoc validation on a callable whose signature annotation references a name that's only importable under TYPE_CHECKING raises NameError — a common pattern for typing-only imports, especially in modules that can't use from __future__ import annotations. Validator.signature_parameters guards inspect.signature() only against (TypeError, ValueError), so the NameError propagates and aborts the Sphinx build (the autodoc-process-docstring handler raises).

signature_parameters only uses parameter names and kinds, never the annotation values, so this evaluation is unnecessary. On Python 3.14+ it now introspects with annotationlib.Format.FORWARDREF, which returns unresolved forward references as ForwardRef objects rather than evaluating (and crashing on) them. Behaviour on Python < 3.14 is unchanged.

Reproducer (Python 3.14):

from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from somewhere import Thing

def build() -> Thing: ...   # module does NOT use `from __future__ import annotations`

Validating build previously raised NameError: name 'Thing' is not defined; with this change it validates cleanly.

@mscheltienne

Copy link
Copy Markdown
Contributor Author

@jarrodmillman @larsoner It would be appreciated if that fix can fit in the 1.11 release :)
Let me know if you want additional changes.

@larsoner

Copy link
Copy Markdown
Collaborator

Looks reasonable to me! Could you add a regression test? Maybe something like (suggested by Claude Opus 5):

@pytest.mark.skipif(sys.version_info < (3, 14), reason="PEP 649 lazy annotations")
def test_signature_params_with_typechecking_only_annotation():
    ns = {}
    exec("def func(a, b=1, *args, **kwargs) -> OnlyUnderTypeChecking: ...", ns)
    assert Validator(get_doc_object(ns["func"])).signature_parameters == (
        "a", "b", "*args", "**kwargs",
    )

@mscheltienne

Copy link
Copy Markdown
Contributor Author

Done: 0d20d54

@larsoner
larsoner merged commit 9905ea9 into numpy:main Jul 27, 2026
26 checks passed
@larsoner

Copy link
Copy Markdown
Collaborator

Thanks @mscheltienne !

@stefanv stefanv added this to the 1.11.0 milestone Jul 27, 2026
@mscheltienne
mscheltienne deleted the fix branch July 28, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants