1
0
mirror of https://github.com/django/django.git synced 2025-11-07 07:15:35 +00:00

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.
This commit is contained in:
Patrick Rauscher
2025-10-30 10:13:14 +01:00
committed by Jacob Walls
parent 340e4f832e
commit 6019147229
2 changed files with 30 additions and 1 deletions

View File

@@ -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

View File

@@ -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