1
0
mirror of https://github.com/django/django.git synced 2025-10-25 06:36:07 +00:00

Fixed #32565 -- Moved internal URLResolver view-strings mapping to admindocs.

Moved the functionality of URLResolver._is_callback(),
URLResolver._callback_strs, URLPattern.lookup_str() to
django.contrib.admindocs.
This commit is contained in:
Alokik Vijay
2022-05-17 09:46:26 +02:00
committed by Carlton Gibson
parent 2a5d2eefc7
commit 7f3cfaa12b
8 changed files with 87 additions and 50 deletions

View File

@@ -1,7 +1,15 @@
from django.apps import AppConfig
from django.urls import get_resolver, get_urlconf
from django.utils.translation import gettext_lazy as _
from .utils import _active, register_callback
class AdminDocsConfig(AppConfig):
name = "django.contrib.admindocs"
verbose_name = _("Administrative Documentation")
def ready(self):
urlconf = get_urlconf()
urlresolver = get_resolver(urlconf)
register_callback(urlresolver, _active.local_value)

View File

@@ -1,11 +1,15 @@
"Misc. utility functions/classes for admin documentation generator."
import functools
import re
from email.errors import HeaderParseError
from email.parser import HeaderParser
from inspect import cleandoc
from asgiref.local import Local
from django.urls import reverse
from django.urls.resolvers import URLPattern
from django.utils.regex_helper import _lazy_re_compile
from django.utils.safestring import mark_safe
@@ -239,3 +243,43 @@ def remove_non_capturing_groups(pattern):
final_pattern += pattern[prev_end:start]
prev_end = end
return final_pattern + pattern[prev_end:]
# Callback strings are cached in a dictionary for every urlconf.
# The active calback_strs are stored by thread id to make them thread local.
_callback_strs = set()
_active = Local()
_active.local_value = _callback_strs
def _is_callback(name, urlresolver=None):
if urlresolver and not urlresolver._populated:
register_callback(urlresolver, _active.local_value)
return name in _active.local_value
@functools.lru_cache(maxsize=None)
def lookup_str(urlpattern):
"""
A string that identifies the view (e.g. 'path.to.view_function' or
'path.to.ClassBasedView').
"""
callback = urlpattern.callback
if isinstance(callback, functools.partial):
callback = callback.func
if hasattr(callback, "view_class"):
callback = callback.view_class
elif not hasattr(callback, "__name__"):
return callback.__module__ + "." + callback.__class__.__name__
return callback.__module__ + "." + callback.__qualname__
def register_callback(urlresolver, thread):
for url_pattern in reversed(urlresolver.url_patterns):
if isinstance(url_pattern, URLPattern):
thread.add(lookup_str(url_pattern))
else: # url_pattern is a URLResolver.
_active.url_pattern_value = _callback_strs
register_callback(url_pattern, _active.url_pattern_value)
thread.update(_active.url_pattern_value)
urlresolver._populated = True

View File

@@ -30,7 +30,7 @@ from django.utils.inspect import (
from django.utils.translation import gettext as _
from django.views.generic import TemplateView
from .utils import get_view_name
from .utils import _is_callback, get_view_name
# Exclude methods starting with these strings from documentation
MODEL_METHODS_EXCLUDE = ("_", "add_", "delete", "save", "set_")
@@ -166,8 +166,7 @@ class ViewDetailView(BaseAdminDocsView):
@staticmethod
def _get_view_func(view):
urlconf = get_urlconf()
if get_resolver(urlconf)._is_callback(view):
if _is_callback(view):
mod, func = get_mod_func(view)
try:
# Separate the module and function, e.g.