diff --git a/django/utils/html.py b/django/utils/html.py
index 0d107a0da9..5b795accd6 100644
--- a/django/utils/html.py
+++ b/django/utils/html.py
@@ -317,18 +317,22 @@ class Urlizer:
safe_input = isinstance(text, SafeData)
words = self.word_split_re.split(str(text))
- return "".join(
- [
- self.handle_word(
+ mapping = {}
+ urlized_words = []
+ for word in words:
+ if word in mapping:
+ urlized_words.append(mapping[word])
+ else:
+ urlized_word = self.handle_word(
word,
safe_input=safe_input,
trim_url_limit=trim_url_limit,
nofollow=nofollow,
autoescape=autoescape,
)
- for word in words
- ]
- )
+ urlized_words.append(urlized_word)
+ mapping[word] = urlized_word
+ return "".join(urlized_words)
def handle_word(
self,
diff --git a/tests/template_tests/filter_tests/test_urlize.py b/tests/template_tests/filter_tests/test_urlize.py
index 546bd6c7d6..a415c59328 100644
--- a/tests/template_tests/filter_tests/test_urlize.py
+++ b/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,25 @@ class FunctionTests(SimpleTestCase):
urlize(prepend_www("google.com")),
'www.google.com',
)
+
+ @mock.patch.object(Urlizer, "handle_word", return_value="test")
+ def test_caching(self, mock_handle_word):
+ urlize("test test test test")
+ mock_handle_word.assert_has_calls(
+ [
+ mock.call(
+ "test",
+ safe_input=False,
+ trim_url_limit=None,
+ nofollow=True,
+ autoescape=True,
+ ),
+ mock.call(
+ " ",
+ safe_input=False,
+ trim_url_limit=None,
+ nofollow=True,
+ autoescape=True,
+ ),
+ ]
+ )