diff --git a/src/pynguin/analyses/typesystem.py b/src/pynguin/analyses/typesystem.py index 24c715ec1..c0c759edc 100644 --- a/src/pynguin/analyses/typesystem.py +++ b/src/pynguin/analyses/typesystem.py @@ -35,6 +35,9 @@ import networkx as nx from networkx.drawing.nx_pydot import to_pydot +if sys.version_info >= (3, 14): + import annotationlib + import pynguin.configuration as config import pynguin.utils.typetracing as tt from pynguin.analyses.string_subtypes import infer_regex_from_methods @@ -2369,7 +2372,15 @@ def infer_signature( """ method_for_signature = get_method_for_signature(method) try: - method_signature = inspect.signature(method_for_signature) + if sys.version_info >= (3, 14): + # Deferred annotations (PEP 649) may name types imported only for + # type checking, which raises NameError in the default VALUE format. + method_signature = inspect.signature( + method_for_signature, + annotation_format=annotationlib.Format.FORWARDREF, + ) + else: + method_signature = inspect.signature(method_for_signature) except (ValueError, TypeError): method_signature = inspect.Signature( parameters=[ diff --git a/tests/analyses/test_typesystem.py b/tests/analyses/test_typesystem.py index 28505b426..6f8eedf65 100644 --- a/tests/analyses/test_typesystem.py +++ b/tests/analyses/test_typesystem.py @@ -9,6 +9,7 @@ import inspect import operator import re +import sys from typing import Any, ParamSpec, TypeVar, Union from unittest import mock @@ -192,6 +193,16 @@ def test_infer_type_info(func, infer_types, expected_parameters, expected_return assert result.return_type == expected_return +@pytest.mark.skipif(sys.version_info < (3, 14), reason="Annotations are deferred from Python 3.14") +def test_infer_type_info_annotation_imported_for_type_checking(): + from tests.fixtures.types.deferred_annotations import length # noqa: PLC0415 + + type_system = TypeSystem() + result = type_system.infer_type_info(length, type_inference_provider=HintInference()) + assert list(result.signature.parameters) == ["obj"] + assert result.original_parameters == {"obj": AnyType()} + + A = TypeVar("A") diff --git a/tests/fixtures/types/deferred_annotations.py b/tests/fixtures/types/deferred_annotations.py new file mode 100644 index 000000000..3ba9bab9c --- /dev/null +++ b/tests/fixtures/types/deferred_annotations.py @@ -0,0 +1,14 @@ +# This file is part of Pynguin. +# +# SPDX-FileCopyrightText: 2019–2026 Pynguin Contributors +# +# SPDX-License-Identifier: MIT +# +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Sized + + +def length(obj: Sized) -> int: + return len(obj)