#36696: django.utils.inspect leads to NameError with Python 3.14
-------------------------------------+-------------------------------------
Reporter: Patrick Rauscher | Type:
| Uncategorized
Status: new | Component: Utilities
Version: 5.2 | Severity: Normal
Keywords: typing,inspect | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Starting with Python 3.14, deferred evaluation of annotations (PEP-649) is
default now. This would suggest that using annotations in templatetags or
signals should be as easy as:
{{{
from typing import TYPE_CHECKING
from django.contrib.auth.signals import user_logged_in
from django.dispatch import receiver
if TYPE_CHECKING:
from django.http import HttpRequest
@receiver(user_logged_in)
def handle_successful_login(request: HttpRequest, *args, **kwargs) ->
None:
print("Someone logged in successful - this would contain useful code")
}}}
Weirdly, as long as DEBUG is set to False, this code will run without
problems. However, if DEBUG is turned to True, Signal.connect will use
func_accepts_kwargs from django.utils.inspect to check if
handle_successful_login accepts kwargs. func_accepts_kwargs will
ultimatively use inspect.signature on the function, which triggers
evaluation of the annotations and leads to NameError, as TYPE_CHECKING is
False here.
This can be resolved in various ways:
1. One can add from __future__ import annotations to switch back to
Stringified annotations. However, this is called to be deprecated in the
future
2. One can switch to always include HttpRequest. This would work, but code
linters will fight this, argueing that HttpRequest is only used as an type
annotation here (which is correct)
3. As django.utils.inspect does not actually care for the annotations,
inspect.inspect(..., annotation_format=annotationlib.Format.STRING) can be
used (sadly this works only in Python 3.14).
My suggestion would be to something around
{{{
@functools.lru_cache(maxsize=512)
def _get_func_parameters(func, remove_first):
if sys.version_info[0:2] >= (3, 14):
import annotationlib
signature = inspect.signature(func,
annotation_format=annotationlib.Format.STRING)
else:
signature = inspect.signature(func)
parameters = tuple(signature.parameters.values())
if remove_first:
parameters = parameters[1:]
return parameters
}}}
But I agree that it might not be the nicest solution.
--
Ticket URL: <https://code.djangoproject.com/ticket/36696>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/django-updates/0107019a306e9940-e028f7e1-e2dd-47ff-bb02-c8db16fa0615-000000%40eu-central-1.amazonses.com.