Skip to content

Use of inspect.getfullargspec is problematic for PEP 649 robustness in Python 3.14 #3812

@Daverball

Description

@Daverball

Bug Report

Describe the bug
With PEP 649 evaluation of annotations is deferred, this replaces the commonly used from __future__ import annotations which achieved a similar result by storing all the annotations as plain strings.

When migrating from one approach to the other, the use of inspect.getfullargspec is problematic, since under the hood it uses inspect.signature with the default parameters, which will cause annotation_format to end up as Format.VALUE which will cause exceptions, if the annotation cannot be properly evaluated, because e.g. a typing-only import was moved into a if TYPE_CHECKING: block, in order to improve application startup times.

The two utility functions that make use of inspect.getfullargspec should switch to inspect.signature and pass annotation_format=Format.STRING or anotation_format=Format.FORWARDREF if sys.version_info >= (3, 14).

To Reproduce

from pyramid.config import Configurator
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from pyramid.interfaces import IRequest

def home_view(request: IRequest) -> None:
    pass

with Configurator() as config:
    config.add_route('home', '/')
    config.add_view(home_view, route_name='home')

On Python 3.14+ this will result in a TypeError in inspect.getfullargspec as a direct result of the NameError for IRequest emitted by inspect.signature. The same code will work if from __future__ import annotations is present.

Expected behavior
Since Pyramid doesn't do anything with the annotations, it shouldn't really care whether or not they can be evaluated using annotationlib.Format.VALUE, so it should switch to Format.FORWARDREF. This provides a clear upgrade path for people that previously relied on from __future__ import annotations and would like to switch to the new PEP 649 semantics in Python 3.14+ without any necessary code changes.

Additional context
Switching from inspect.getfullargspec to inspect.signature should simplify some of the code and make it more reliable too, since it properly respects __wrapped__ and automatically removes the first parameter from bound methods.

In order to ensure proper backwards compatibility I would add a new utility function get_signature to pyramid.util which is defined as follows (you could also put this inside a compat module):

import inspect
import sys

if sys.version_info < (3, 14):
    def get_signature(fn):
        return inspect.signature(fn)
else:
    from annotationlib import Format

    def get_signature(fn):
         return inspect.signature(fn, annotation_format=Format.FORWARDREF)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions