diff options
| author | Patrick Rauscher <Patrick.Rauscher@deutschebahn.com> | 2025-10-30 10:13:14 +0100 |
|---|---|---|
| committer | Jacob Walls <jacobtylerwalls@gmail.com> | 2025-10-31 08:45:13 -0400 |
| commit | 953d31028a48911777d932e3ea76bedb2daf6ad8 (patch) | |
| tree | 5b52752342ae43335bc06e5f5c5520cd435f50c1 | |
| parent | a36108094dd4479f37e5272921745eca7e7c992f (diff) | |
[6.0.x] Fixed #36696 -- Fixed NameError when inspecting functions with deferred annotations.
In Python 3.14, annotations are deferred by default, so we should not
assume that the names in them have been imported unconditionally.
Backport of 601914722956cc41f1f2c53972d669ddee6ffc04 from main.
| -rw-r--r-- | django/utils/inspect.py | 16 | ||||
| -rw-r--r-- | tests/utils_tests/test_inspect.py | 15 |
2 files changed, 30 insertions, 1 deletions
diff --git a/django/utils/inspect.py b/django/utils/inspect.py index 31f3cf994b..ac5e82ca14 100644 --- a/django/utils/inspect.py +++ b/django/utils/inspect.py @@ -1,10 +1,24 @@ import functools import inspect +from django.utils.version import PY314 + +if PY314: + import annotationlib + @functools.lru_cache(maxsize=512) def _get_func_parameters(func, remove_first): - parameters = tuple(inspect.signature(func).parameters.values()) + # As the annotations are not used in any case, inspect the signature with + # FORWARDREF to leave any deferred annotations unevaluated. + if PY314: + signature = inspect.signature( + func, annotation_format=annotationlib.Format.FORWARDREF + ) + else: + signature = inspect.signature(func) + + parameters = tuple(signature.parameters.values()) if remove_first: parameters = parameters[1:] return parameters diff --git a/tests/utils_tests/test_inspect.py b/tests/utils_tests/test_inspect.py index 38ea35ecfb..f6e82e5808 100644 --- a/tests/utils_tests/test_inspect.py +++ b/tests/utils_tests/test_inspect.py @@ -1,8 +1,13 @@ import subprocess import unittest +from typing import TYPE_CHECKING from django.shortcuts import aget_object_or_404 from django.utils import inspect +from django.utils.version import PY314 + +if TYPE_CHECKING: + from django.utils.safestring import SafeString class Person: @@ -103,6 +108,16 @@ class TestInspectMethods(unittest.TestCase): self.assertIs(inspect.func_accepts_kwargs(Person.all_kinds), True) self.assertIs(inspect.func_accepts_kwargs(Person().just_args), False) + @unittest.skipUnless(PY314, "Deferred annotations are Python 3.14+ only") + def test_func_accepts_kwargs_deferred_annotations(self): + + def func_with_annotations(self, name: str, complex: SafeString) -> None: + pass + + # Inspection fails with deferred annotations with python 3.14+. Earlier + # Python versions trigger the NameError on module initialization. + self.assertIs(inspect.func_accepts_kwargs(func_with_annotations), False) + class IsModuleLevelFunctionTestCase(unittest.TestCase): @classmethod |
