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, + ), + ] + )