瀏覽代碼

Fixed #35998 -- Added caching to django.utils.html.urlize().

Sarah Boyce 5 月之前
父節點
當前提交
b721f12760
共有 2 個文件被更改,包括 45 次插入6 次删除
  1. 8 6
      django/utils/html.py
  2. 37 0
      tests/template_tests/filter_tests/test_urlize.py

+ 8 - 6
django/utils/html.py

@@ -317,18 +317,20 @@ class Urlizer:
         safe_input = isinstance(text, SafeData)
 
         words = self.word_split_re.split(str(text))
-        return "".join(
-            [
-                self.handle_word(
+        local_cache = {}
+        urlized_words = []
+        for word in words:
+            if (urlized_word := local_cache.get(word)) is None:
+                urlized_word = self.handle_word(
                     word,
                     safe_input=safe_input,
                     trim_url_limit=trim_url_limit,
                     nofollow=nofollow,
                     autoescape=autoescape,
                 )
-                for word in words
-            ]
-        )
+                local_cache[word] = urlized_word
+            urlized_words.append(urlized_word)
+        return "".join(urlized_words)
 
     def handle_word(
         self,

+ 37 - 0
tests/template_tests/filter_tests/test_urlize.py

@@ -1,6 +1,9 @@
+from unittest import mock
+
 from django.template.defaultfilters import urlize
 from django.test import SimpleTestCase
 from django.utils.functional import lazy
+from django.utils.html import Urlizer
 from django.utils.safestring import mark_safe
 
 from ..utils import setup
@@ -467,3 +470,37 @@ class FunctionTests(SimpleTestCase):
             urlize(prepend_www("google.com")),
             '<a href="http://www.google.com" rel="nofollow">www.google.com</a>',
         )
+
+    @mock.patch.object(Urlizer, "handle_word", return_value="test")
+    def test_caching_repeated_words(self, mock_handle_word):
+        urlize("test test test test")
+        common_handle_word_args = {
+            "safe_input": False,
+            "trim_url_limit": None,
+            "nofollow": True,
+            "autoescape": True,
+        }
+        self.assertEqual(
+            mock_handle_word.mock_calls,
+            [
+                mock.call("test", **common_handle_word_args),
+                mock.call(" ", **common_handle_word_args),
+            ],
+        )
+
+    @mock.patch.object(Urlizer, "handle_word", return_value="test")
+    def test_caching_repeated_calls(self, mock_handle_word):
+        urlize("test")
+        handle_word_test = mock.call(
+            "test",
+            safe_input=False,
+            trim_url_limit=None,
+            nofollow=True,
+            autoescape=True,
+        )
+        self.assertEqual(mock_handle_word.mock_calls, [handle_word_test])
+
+        urlize("test")
+        self.assertEqual(
+            mock_handle_word.mock_calls, [handle_word_test, handle_word_test]
+        )