diff --git a/django/utils/html.py b/django/utils/html.py
index d3b904a82237..734d7fbfb3d0 100644
--- a/django/utils/html.py
+++ b/django/utils/html.py
@@ -10,7 +10,7 @@
from django.conf import settings
from django.core.exceptions import SuspiciousOperation, ValidationError
-from django.core.validators import EmailValidator
+from django.core.validators import DomainNameValidator, EmailValidator
from django.utils.deprecation import RemovedInDjango70Warning
from django.utils.functional import Promise, cached_property, keep_lazy, keep_lazy_text
from django.utils.http import MAX_URL_LENGTH, RFC3986_GENDELIMS, RFC3986_SUBDELIMS
@@ -296,7 +296,9 @@ class Urlizer:
simple_url_re = _lazy_re_compile(r"^https?://\[?\w", re.IGNORECASE)
simple_url_2_re = _lazy_re_compile(
- r"^www\.|^(?!http)\w[^@]+\.(com|edu|gov|int|mil|net|org)($|/.*)$", re.IGNORECASE
+ rf"^www\.|^(?!http)(?:{DomainNameValidator.hostname_re})"
+ r"\.(com|edu|gov|int|mil|net|org)($|/.*)$",
+ re.IGNORECASE,
)
word_split_re = _lazy_re_compile(r"""([\s<>"']+)""")
diff --git a/docs/howto/custom-management-commands.txt b/docs/howto/custom-management-commands.txt
index 5ee82b3774ca..71c5fdac0cdc 100644
--- a/docs/howto/custom-management-commands.txt
+++ b/docs/howto/custom-management-commands.txt
@@ -120,7 +120,7 @@ this::
# ...
The option (``delete`` in our example) is available in the options dict
-parameter of the handle method. See the :py:mod:`argparse` Python documentation
+parameter of the handle method. See the :mod:`argparse` Python documentation
for more about ``add_argument`` usage.
In addition to being able to add custom command line options, all
@@ -208,7 +208,7 @@ All attributes can be set in your derived class and can be used in
If your command defines mandatory positional arguments, you can customize
the message error returned in the case of missing arguments. The default is
- output by :py:mod:`argparse` ("too few arguments").
+ output by :mod:`argparse` ("too few arguments").
.. attribute:: BaseCommand.output_transaction
diff --git a/docs/howto/logging.txt b/docs/howto/logging.txt
index 149b8bb83be3..7826eb38d945 100644
--- a/docs/howto/logging.txt
+++ b/docs/howto/logging.txt
@@ -25,7 +25,7 @@ To send a log message from within your code, you place a logging call into it.
logging, use a view function as suggested in the example below.
First, import the Python logging library, and then obtain a logger instance
-with :py:func:`logging.getLogger`. Provide the ``getLogger()`` method with a
+with :func:`logging.getLogger`. Provide the ``getLogger()`` method with a
name to identify it and the records it emits. A good option is to use
``__name__`` (see :ref:`naming-loggers` below for more on this) which will
provide the name of the current Python module as a dotted path::
@@ -43,7 +43,7 @@ And then in a function, for example in a view, send a record to the logger::
if some_risky_state:
logger.warning("Platform is running at risk")
-When this code is executed, a :py:class:`~logging.LogRecord` containing that
+When this code is executed, a :class:`~logging.LogRecord` containing that
message will be sent to the logger. If you're using Django's default logging
configuration, the message will appear in the console.
@@ -129,7 +129,7 @@ file ``general.log`` (at the project root):
Different handler classes take different configuration options. For more
information on available handler classes, see the
:class:`~django.utils.log.AdminEmailHandler` provided by Django and the various
-:py:mod:`handler classes ` provided by Python.
+:mod:`handler classes ` provided by Python.
Logging levels can also be set on the handlers (by default, they accept log
messages of all levels). Using the example above, adding:
@@ -238,7 +238,7 @@ application. A named logging configuration will capture logs only from loggers
with matching names.
The namespace of a logger instance is defined using
-:py:func:`~logging.getLogger`. For example in ``views.py`` of ``my_app``::
+:func:`~logging.getLogger`. For example in ``views.py`` of ``my_app``::
logger = logging.getLogger(__name__)
diff --git a/docs/internals/contributing/writing-code/coding-style.txt b/docs/internals/contributing/writing-code/coding-style.txt
index 64ef6c5a515f..883ea8b9c221 100644
--- a/docs/internals/contributing/writing-code/coding-style.txt
+++ b/docs/internals/contributing/writing-code/coding-style.txt
@@ -56,8 +56,8 @@ Python style
These limits are checked when ``flake8`` is run.
* String variable interpolation may use
- :py:ref:`%-formatting `, :py:ref:`f-strings
- `, or :py:meth:`str.format` as appropriate, with the goal of
+ :ref:`%-formatting `, :ref:`f-strings
+ `, or :meth:`str.format` as appropriate, with the goal of
maximizing code readability.
Final judgments of readability are left to the Merger's discretion. As a
diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt
index 22105090d2e2..99887005c3a3 100644
--- a/docs/internals/deprecation.txt
+++ b/docs/internals/deprecation.txt
@@ -742,8 +742,8 @@ details on these changes.
* The ability to use a dotted Python path for the ``LOGIN_URL`` and
``LOGIN_REDIRECT_URL`` settings will be removed.
-* Support for :py:mod:`optparse` will be dropped for custom management commands
- (replaced by :py:mod:`argparse`).
+* Support for :mod:`optparse` will be dropped for custom management commands
+ (replaced by :mod:`argparse`).
* The class ``django.core.management.NoArgsCommand`` will be removed. Use
:class:`~django.core.management.BaseCommand` instead, which takes no
diff --git a/docs/lint.py b/docs/lint.py
index cb129997b27c..5c7a168c97db 100644
--- a/docs/lint.py
+++ b/docs/lint.py
@@ -5,12 +5,14 @@
from unittest import mock
from sphinxlint.checkers import (
+ _ROLE_BODY,
_is_long_interpreted_text,
_is_very_long_string_literal,
_starts_with_anonymous_hyperlink,
_starts_with_directive_or_hyperlink,
)
from sphinxlint.checkers import checker as sphinxlint_checker
+from sphinxlint.rst import SIMPLENAME
from sphinxlint.sphinxlint import check_text
from sphinxlint.utils import PER_FILE_CACHES, hide_non_rst_blocks
@@ -116,6 +118,24 @@ def is_multiline_block_to_exclude(line):
yield lno + 1, f"Line too long ({len(line) - 1}/{options.max_line_length})"
+_PYTHON_DOMAIN = re.compile(f":py:{SIMPLENAME}:`{_ROLE_BODY}`")
+
+
+@sphinxlint_checker(".rst", enabled=False, rst_only=True)
+def check_python_domain_in_roles(file, lines, options=None):
+ """
+ :py: indicates the Python language domain. This means code writen in
+ Python, not Python built-ins in particular.
+
+ Bad: :py:class:`email.message.EmailMessage`
+ Good: :class:`email.message.EmailMessage`
+ """
+ for lno, line in enumerate(lines, start=1):
+ role = _PYTHON_DOMAIN.search(line)
+ if role:
+ yield lno, f":py domain is the default and can be omitted {role.group(0)!r}"
+
+
import sphinxlint # noqa: E402
sphinxlint.check_file = django_check_file
diff --git a/docs/ref/contrib/staticfiles.txt b/docs/ref/contrib/staticfiles.txt
index 2ca9c9a5192a..f5f078a2390f 100644
--- a/docs/ref/contrib/staticfiles.txt
+++ b/docs/ref/contrib/staticfiles.txt
@@ -492,7 +492,7 @@ This view function serves static files in development.
.. note::
To guess the served files' content types, this view relies on the
- :py:mod:`mimetypes` module from the Python standard library, which itself
+ :mod:`mimetypes` module from the Python standard library, which itself
relies on the underlying platform's map files. If you find that this view
doesn't return proper content types for certain files, it is most likely
that the platform's map files are incorrect or need to be updated. This can
diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt
index e54776359e98..fad6976b5ca6 100644
--- a/docs/ref/databases.txt
+++ b/docs/ref/databases.txt
@@ -951,7 +951,7 @@ Enabling JSON1 extension on SQLite
----------------------------------
To use :class:`~django.db.models.JSONField` on SQLite, you need to enable the
-`JSON1 extension`_ on Python's :py:mod:`sqlite3` library. If the extension is
+`JSON1 extension`_ on Python's :mod:`sqlite3` library. If the extension is
not enabled on your installation, a system error (``fields.E180``) will be
raised.
diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt
index a9d4a1b7b454..245a8f387cd1 100644
--- a/docs/ref/django-admin.txt
+++ b/docs/ref/django-admin.txt
@@ -1845,7 +1845,7 @@ allows for the following options by default:
.. django-admin-option:: --pythonpath PYTHONPATH
-Adds the given filesystem path to the Python :py:data:`sys.path` module
+Adds the given filesystem path to the Python :data:`sys.path` module
attribute. If this isn't provided, ``django-admin`` will use the
:envvar:`PYTHONPATH` environment variable.
diff --git a/docs/ref/files/file.txt b/docs/ref/files/file.txt
index 6fd56c227b92..701b2e5b371f 100644
--- a/docs/ref/files/file.txt
+++ b/docs/ref/files/file.txt
@@ -13,7 +13,7 @@ The ``File`` class
.. class:: File(file_object, name=None)
The :class:`File` class is a thin wrapper around a Python
- :py:term:`file object` with some Django-specific additions.
+ :term:`file object` with some Django-specific additions.
Internally, Django uses this class when it needs to represent a file.
:class:`File` objects have the following attributes and methods:
@@ -29,14 +29,14 @@ The ``File`` class
.. attribute:: file
- The underlying :py:term:`file object` that this class wraps.
+ The underlying :term:`file object` that this class wraps.
.. admonition:: Be careful with this attribute in subclasses.
Some subclasses of :class:`File`, including
:class:`~django.core.files.base.ContentFile` and
:class:`~django.db.models.fields.files.FieldFile`, may replace this
- attribute with an object other than a Python :py:term:`file
+ attribute with an object other than a Python :term:`file
object`. In these cases, this attribute may itself be a
:class:`File` subclass (and not necessarily the same subclass).
Whenever possible, use the attributes and methods of the subclass
diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt
index c786239c25c0..6108932d58e9 100644
--- a/docs/ref/forms/fields.txt
+++ b/docs/ref/forms/fields.txt
@@ -919,7 +919,7 @@ For each field, we describe the default widget used if you don't specify
.. attribute:: encoder
- A :py:class:`json.JSONEncoder` subclass to serialize data types not
+ A :class:`json.JSONEncoder` subclass to serialize data types not
supported by the standard JSON serializer (e.g. ``datetime.datetime``
or :class:`~python:uuid.UUID`). For example, you can use the
:class:`~django.core.serializers.json.DjangoJSONEncoder` class.
@@ -928,14 +928,14 @@ For each field, we describe the default widget used if you don't specify
.. attribute:: decoder
- A :py:class:`json.JSONDecoder` subclass to deserialize the input. Your
+ A :class:`json.JSONDecoder` subclass to deserialize the input. Your
deserialization may need to account for the fact that you can't be
certain of the input type. For example, you run the risk of returning a
``datetime`` that was actually a string that just happened to be in the
same format chosen for ``datetime``\s.
The ``decoder`` can be used to validate the input. If
- :py:class:`json.JSONDecodeError` is raised during the deserialization,
+ :class:`json.JSONDecodeError` is raised during the deserialization,
a ``ValidationError`` will be raised.
Defaults to ``json.JSONDecoder``.
diff --git a/docs/ref/logging.txt b/docs/ref/logging.txt
index 756ac6d8f936..686145b673b7 100644
--- a/docs/ref/logging.txt
+++ b/docs/ref/logging.txt
@@ -164,7 +164,7 @@ Messages to this logger have the following extra context:
* ``status_code``: The HTTP response code associated with the request.
-* ``request``: The request object (a :py:class:`socket.socket`) that generated
+* ``request``: The request object (a :class:`socket.socket`) that generated
the logging message.
.. _django-template-logger:
diff --git a/docs/ref/models/expressions.txt b/docs/ref/models/expressions.txt
index d4095a39007e..121ed0f30a69 100644
--- a/docs/ref/models/expressions.txt
+++ b/docs/ref/models/expressions.txt
@@ -563,7 +563,7 @@ values into their corresponding database type.
If no :ref:`output_field` is specified, it will be inferred from
the type of the provided ``value`` for many common types. For example, passing
-an instance of :py:class:`datetime.datetime` as ``value`` defaults
+an instance of :class:`datetime.datetime` as ``value`` defaults
``output_field`` to :class:`~django.db.models.DateTimeField`.
``ExpressionWrapper()`` expressions
@@ -1094,7 +1094,7 @@ calling the appropriate methods on the wrapped expression.
Tells Django which value should be returned when the expression is used
to apply a function over an empty result set. Defaults to
- :py:data:`NotImplemented` which forces the expression to be computed on
+ :data:`NotImplemented` which forces the expression to be computed on
the database.
.. attribute:: set_returning
diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt
index b08718879b9c..dd0862926aa0 100644
--- a/docs/ref/models/fields.txt
+++ b/docs/ref/models/fields.txt
@@ -1434,7 +1434,7 @@ Python native format: dictionaries, lists, strings, numbers, booleans and
.. attribute:: JSONField.encoder
- An optional :py:class:`json.JSONEncoder` subclass to serialize data types
+ An optional :class:`json.JSONEncoder` subclass to serialize data types
not supported by the standard JSON serializer (e.g. ``datetime.datetime``
or :class:`~python:uuid.UUID`). For example, you can use the
:class:`~django.core.serializers.json.DjangoJSONEncoder` class.
@@ -1443,7 +1443,7 @@ Python native format: dictionaries, lists, strings, numbers, booleans and
.. attribute:: JSONField.decoder
- An optional :py:class:`json.JSONDecoder` subclass to deserialize the value
+ An optional :class:`json.JSONDecoder` subclass to deserialize the value
retrieved from the database. The value will be in the format chosen by the
custom encoder (most often a string). Your deserialization may need to
account for the fact that you can't be certain of the input type. For
@@ -1458,7 +1458,7 @@ To query ``JSONField`` in the database, see :ref:`querying-jsonfield`.
.. admonition:: Default value
If you give the field a :attr:`~django.db.models.Field.default`, ensure
- it's a callable such as the :py:class:`dict` class or a function that
+ it's a callable such as the :class:`dict` class or a function that
returns a fresh object each time. Incorrectly using a mutable object like
``default={}`` or ``default=[]`` creates a mutable default that is shared
between all instances.
@@ -1483,8 +1483,8 @@ To query ``JSONField`` in the database, see :ref:`querying-jsonfield`.
.. admonition:: Oracle users
Oracle Database does not support storing JSON scalar values. Only JSON
- objects and arrays (represented in Python using :py:class:`dict` and
- :py:class:`list`) are supported.
+ objects and arrays (represented in Python using :class:`dict` and
+ :class:`list`) are supported.
``PositiveBigIntegerField``
---------------------------
@@ -2440,7 +2440,7 @@ Field API reference
.. attribute:: descriptor_class
- A class implementing the :py:ref:`descriptor protocol `
+ A class implementing the :ref:`descriptor protocol `
that is instantiated and assigned to the model instance attribute. The
constructor must accept a single argument, the ``Field`` instance.
Overriding this class attribute allows for customizing the get and set
diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt
index ab1f3575e89e..892dc24008ca 100644
--- a/docs/ref/request-response.txt
+++ b/docs/ref/request-response.txt
@@ -889,7 +889,7 @@ Attributes
.. attribute:: HttpResponse.cookies
- A :py:obj:`http.cookies.SimpleCookie` object holding the cookies included
+ A :obj:`http.cookies.SimpleCookie` object holding the cookies included
in the response.
.. attribute:: HttpResponse.headers
@@ -952,7 +952,7 @@ Methods
``"text/html; charset=utf-8"``.
``status`` is the :rfc:`HTTP status code <9110#section-15>` for the
- response. You can use Python's :py:class:`http.HTTPStatus` for meaningful
+ response. You can use Python's :class:`http.HTTPStatus` for meaningful
aliases, such as ``HTTPStatus.NO_CONTENT``.
``reason`` is the HTTP response phrase. If not provided, a default phrase
@@ -1187,7 +1187,7 @@ Custom response classes
~~~~~~~~~~~~~~~~~~~~~~~
If you find yourself needing a response class that Django doesn't provide, you
-can create it with the help of :py:class:`http.HTTPStatus`. For example::
+can create it with the help of :class:`http.HTTPStatus`. For example::
from http import HTTPStatus
from django.http import HttpResponse
diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt
index 432418b07f01..9eee51beba97 100644
--- a/docs/ref/templates/builtins.txt
+++ b/docs/ref/templates/builtins.txt
@@ -1225,14 +1225,14 @@ attribute and calling the result ``country_list``.
``{% regroup %}`` produces a list (in this case, ``country_list``) of
**group objects**. Group objects are instances of
-:py:func:`~collections.namedtuple` with two fields:
+:func:`~collections.namedtuple` with two fields:
* ``grouper`` -- the item that was grouped by (e.g., the string "India" or
"Japan").
* ``list`` -- a list of all items in this group (e.g., a list of all cities
with country='India').
-Because ``{% regroup %}`` produces :py:func:`~collections.namedtuple` objects,
+Because ``{% regroup %}`` produces :func:`~collections.namedtuple` objects,
you can also write the previous example as:
.. code-block:: html+django
@@ -1839,7 +1839,7 @@ For example:
{{ value|date:"D d M Y" }}
-If ``value`` is a :py:class:`~datetime.datetime` object (e.g., the result of
+If ``value`` is a :class:`~datetime.datetime` object (e.g., the result of
``datetime.datetime.now()``), the output will be the string
``'Wed 09 Jan 2008'``.
@@ -2736,7 +2736,7 @@ specifier for the ``de`` locale as shipped with Django is ``"H:i"``).
The ``time`` filter will only accept parameters in the format string that
relate to the time of day, not the date. If you need to format a ``date``
value, use the :tfilter:`date` filter instead (or along with :tfilter:`time` if
-you need to render a full :py:class:`~datetime.datetime` value).
+you need to render a full :class:`~datetime.datetime` value).
There is one exception the above rule: When passed a ``datetime`` value with
attached timezone information (a :ref:`time-zone-aware
diff --git a/docs/ref/unicode.txt b/docs/ref/unicode.txt
index f8ac528666ea..86cc42160381 100644
--- a/docs/ref/unicode.txt
+++ b/docs/ref/unicode.txt
@@ -268,7 +268,7 @@ Use strings when creating templates manually::
But the common case is to read templates from the filesystem. If your template
files are not stored with a UTF-8 encoding, adjust the :setting:`TEMPLATES`
-setting. The built-in :py:mod:`~django.template.backends.django` backend
+setting. The built-in :mod:`~django.template.backends.django` backend
provides the ``'file_charset'`` option to change the encoding used to read
files from disk.
diff --git a/docs/ref/urls.txt b/docs/ref/urls.txt
index 094fca992b93..3129f6ab3c65 100644
--- a/docs/ref/urls.txt
+++ b/docs/ref/urls.txt
@@ -95,7 +95,7 @@ Returns an element for inclusion in ``urlpatterns``. For example::
The ``route`` argument should be a string or
:func:`~django.utils.translation.gettext_lazy` (see
:ref:`translating-urlpatterns`) that contains a regular expression compatible
-with Python's :py:mod:`re` module. Strings typically use raw string syntax
+with Python's :mod:`re` module. Strings typically use raw string syntax
(``r''``) so that they can contain sequences like ``\d`` without the need to
escape the backslash with another backslash. When a match is made, captured
groups from the regular expression are passed to the view -- as named arguments
@@ -104,7 +104,7 @@ passed as strings, without any type conversion.
When a ``route`` ends with ``$`` the whole requested URL, matching against
:attr:`~django.http.HttpRequest.path_info`, must match the regular expression
-pattern (:py:func:`re.fullmatch` is used).
+pattern (:func:`re.fullmatch` is used).
The ``view``, ``kwargs`` and ``name`` arguments are the same as for
:func:`~django.urls.path`.
diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt
index fb053f689573..f8a58fcda106 100644
--- a/docs/ref/utils.txt
+++ b/docs/ref/utils.txt
@@ -352,7 +352,7 @@ compatibility
An optional string containing the MIME type of the stylesheet. If not
specified, Django will attempt to guess it by using Python's
- :py:func:`mimetypes.guess_type`. Use ``mimetype=None`` if you don't
+ :func:`mimetypes.guess_type`. Use ``mimetype=None`` if you don't
want your stylesheet to have a MIME type specified.
.. attribute:: media
@@ -532,7 +532,7 @@ compatibility
# set a value manually, that will persist on the instance until cleared
person.friends = ["Huckleberry Finn", "Tom Sawyer"]
- Because of the way the :py:ref:`descriptor protocol
+ Because of the way the :ref:`descriptor protocol
` works, using ``del`` (or ``delattr``) on a
``cached_property`` that hasn't been accessed raises ``AttributeError``.
@@ -560,7 +560,7 @@ compatibility
.. class:: classproperty(method=None)
- Similar to :py:func:`@classmethod `, the ``@classproperty``
+ Similar to :func:`@classmethod `, the ``@classproperty``
decorator converts the result of a method with a single ``cls`` argument
into a property that can be accessed directly from the class.
diff --git a/docs/releases/1.10.txt b/docs/releases/1.10.txt
index 0c0da896436b..4d04db521f83 100644
--- a/docs/releases/1.10.txt
+++ b/docs/releases/1.10.txt
@@ -448,7 +448,7 @@ Requests and Responses
* Added :class:`~django.http.HttpResponse` methods
:meth:`~django.http.HttpResponse.readable` and
:meth:`~django.http.HttpResponse.seekable` to make an instance a
- stream-like object and allow wrapping it with :py:class:`io.TextIOWrapper`.
+ stream-like object and allow wrapping it with :class:`io.TextIOWrapper`.
* Added the :attr:`HttpRequest.content_type
` and
diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt
index a63f14ca519c..33c2635c640a 100644
--- a/docs/releases/1.8.txt
+++ b/docs/releases/1.8.txt
@@ -275,7 +275,7 @@ Cryptography
* The ``max_age`` parameter of the
:meth:`django.core.signing.TimestampSigner.unsign` method now also accepts a
- :py:class:`datetime.timedelta` object.
+ :class:`datetime.timedelta` object.
Database backends
~~~~~~~~~~~~~~~~~
@@ -384,7 +384,7 @@ Generic Views
* Placeholders in :attr:`ModelFormMixin.success_url
` now support the
- Python :py:meth:`str.format` syntax. The legacy ``%()s`` syntax is still
+ Python :meth:`str.format` syntax. The legacy ``%()s`` syntax is still
supported but will be removed in Django 1.10.
Internationalization
@@ -753,7 +753,7 @@ Management commands that only accept positional arguments
If you have written a custom management command that only accepts positional
arguments and you didn't specify the ``args`` command variable, you might get
an error like ``Error: unrecognized arguments: ...``, as variable parsing is
-now based on :py:mod:`argparse` which doesn't implicitly accept positional
+now based on :mod:`argparse` which doesn't implicitly accept positional
arguments. You can make your command backwards compatible by simply setting the
``args`` class variable. However, if you don't have to keep compatibility with
older Django versions, it's better to implement the new
@@ -766,10 +766,10 @@ Custom test management command arguments through test runner
The method to add custom arguments to the ``test`` management command through
the test runner has changed. Previously, you could provide an ``option_list``
class variable on the test runner to add more arguments (à la
-:py:mod:`optparse`). Now to implement the same behavior, you have to create an
+:mod:`optparse`). Now to implement the same behavior, you have to create an
``add_arguments(cls, parser)`` class method on the test runner and call
``parser.add_argument`` to add any custom arguments, as parser is now an
-:py:class:`argparse.ArgumentParser` instance.
+:class:`argparse.ArgumentParser` instance.
Model check ensures auto-generated column names are within limits specified by database
---------------------------------------------------------------------------------------
@@ -1407,7 +1407,7 @@ in Django 1.10:
Extending management command arguments through ``Command.option_list``
----------------------------------------------------------------------
-Management commands now use :py:mod:`argparse` instead of :py:mod:`optparse` to
+Management commands now use :mod:`argparse` instead of :mod:`optparse` to
parse command-line arguments passed to commands. This also means that the way
to add custom arguments to commands has changed: instead of extending the
``option_list`` class list, you should now override the
diff --git a/docs/releases/3.0.txt b/docs/releases/3.0.txt
index 1332a646ae0a..3cf841b8b9a6 100644
--- a/docs/releases/3.0.txt
+++ b/docs/releases/3.0.txt
@@ -285,7 +285,7 @@ Models
* The new :attr:`.Field.descriptor_class` attribute allows model fields to
customize the get and set behavior by overriding their
- :py:ref:`descriptors `.
+ :ref:`descriptors `.
* :class:`~django.db.models.Avg` and :class:`~django.db.models.Sum` now support
the ``distinct`` argument.
diff --git a/docs/releases/3.1.3.txt b/docs/releases/3.1.3.txt
index c51e0dd4a8b4..153d49571feb 100644
--- a/docs/releases/3.1.3.txt
+++ b/docs/releases/3.1.3.txt
@@ -43,7 +43,7 @@ Bugfixes
* Fixed a regression in Django 3.1 where
:exc:`ProtectedError.protected_objects ` and
:exc:`RestrictedError.restricted_objects `
- attributes returned iterators instead of :py:class:`set` of objects
+ attributes returned iterators instead of :class:`set` of objects
(:ticket:`32107`).
* Fixed a regression in Django 3.1.2 that caused incorrect form input layout on
diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt
index 0c2041c5a2a3..7dc20f1b1b56 100644
--- a/docs/releases/3.1.txt
+++ b/docs/releases/3.1.txt
@@ -407,7 +407,7 @@ Models
SQL on MySQL by using ``DELETE`` instead of ``TRUNCATE`` statements for
tables which don't require resetting sequences.
-* SQLite functions are now marked as :py:meth:`deterministic
+* SQLite functions are now marked as :meth:`deterministic
` on Python 3.8+. This allows using them
in check constraints and partial indexes.
diff --git a/docs/releases/3.2.txt b/docs/releases/3.2.txt
index 9a49075d3725..681306316080 100644
--- a/docs/releases/3.2.txt
+++ b/docs/releases/3.2.txt
@@ -406,11 +406,11 @@ Models
* :class:`Value() ` expression now
automatically resolves its ``output_field`` to the appropriate
:class:`Field ` subclass based on the type of
- its provided ``value`` for :py:class:`bool`, :py:class:`bytes`,
- :py:class:`float`, :py:class:`int`, :py:class:`str`,
- :py:class:`datetime.date`, :py:class:`datetime.datetime`,
- :py:class:`datetime.time`, :py:class:`datetime.timedelta`,
- :py:class:`decimal.Decimal`, and :py:class:`uuid.UUID` instances. As a
+ its provided ``value`` for :class:`bool`, :class:`bytes`,
+ :class:`float`, :class:`int`, :class:`str`,
+ :class:`datetime.date`, :class:`datetime.datetime`,
+ :class:`datetime.time`, :class:`datetime.timedelta`,
+ :class:`decimal.Decimal`, and :class:`uuid.UUID` instances. As a
consequence, resolving an ``output_field`` for database functions and
combined expressions may now crash with mixed types when using ``Value()``.
You will need to explicitly set the ``output_field`` in such cases.
@@ -519,11 +519,11 @@ Tests
* Objects assigned to class attributes in :meth:`.TestCase.setUpTestData` are
now isolated for each test method. Such objects are now required to support
- creating deep copies with :py:func:`copy.deepcopy`. Assigning objects which
+ creating deep copies with :func:`copy.deepcopy`. Assigning objects which
don't support ``deepcopy()`` is deprecated and will be removed in Django 4.1.
* :class:`~django.test.runner.DiscoverRunner` now enables
- :py:mod:`faulthandler` by default. This can be disabled by using the
+ :mod:`faulthandler` by default. This can be disabled by using the
:option:`test --no-faulthandler` option.
* :class:`~django.test.runner.DiscoverRunner` and the
@@ -703,8 +703,8 @@ Miscellaneous
removed. Please use :func:`urllib.parse.parse_qsl` instead.
* ``django.test.utils.TestContextDecorator`` now uses
- :py:meth:`~unittest.TestCase.addCleanup` so that cleanups registered in the
- :py:meth:`~unittest.TestCase.setUp` method are called before
+ :meth:`~unittest.TestCase.addCleanup` so that cleanups registered in the
+ :meth:`~unittest.TestCase.setUp` method are called before
``TestContextDecorator.disable()``.
* ``SessionMiddleware`` now raises a
@@ -758,7 +758,7 @@ Miscellaneous
-------------
* Assigning objects which don't support creating deep copies with
- :py:func:`copy.deepcopy` to class attributes in
+ :func:`copy.deepcopy` to class attributes in
:meth:`.TestCase.setUpTestData` is deprecated.
* Using a boolean value in :attr:`.BaseCommand.requires_system_checks` is
diff --git a/docs/releases/4.0.txt b/docs/releases/4.0.txt
index c7d63ee2931b..69f7309b4471 100644
--- a/docs/releases/4.0.txt
+++ b/docs/releases/4.0.txt
@@ -289,7 +289,7 @@ Management Commands
* On PostgreSQL, :djadmin:`dbshell` now supports specifying a password file.
-* The :djadmin:`shell` command now respects :py:data:`sys.__interactivehook__`
+* The :djadmin:`shell` command now respects :data:`sys.__interactivehook__`
at startup. This allows loading shell history between interactive sessions.
As a consequence, ``readline`` is no longer loaded if running in *isolated*
mode.
@@ -349,7 +349,7 @@ Signals
* The new ``stdout`` argument for :func:`~django.db.models.signals.pre_migrate`
and :func:`~django.db.models.signals.post_migrate` signals allows redirecting
output to a stream-like object. It should be preferred over
- :py:data:`sys.stdout` and :py:func:`print` when emitting verbose output in
+ :data:`sys.stdout` and :func:`print` when emitting verbose output in
order to allow proper capture when testing.
Templates
@@ -370,7 +370,7 @@ Tests
* The :option:`test --buffer` option now supports parallel tests.
* The new ``logger`` argument to :class:`~django.test.runner.DiscoverRunner`
- allows a Python :py:ref:`logger ` to be used for logging.
+ allows a Python :ref:`logger ` to be used for logging.
* The new :meth:`.DiscoverRunner.log` method provides a way to log messages
that uses the ``DiscoverRunner.logger``, or prints to the console if not set.
diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt
index 10b9f3728a46..df614357a370 100644
--- a/docs/releases/4.1.txt
+++ b/docs/releases/4.1.txt
@@ -536,7 +536,7 @@ Miscellaneous
* :class:`~django.test.runner.DiscoverRunner` now returns a non-zero error code
for unexpected successes from tests marked with
- :py:func:`unittest.expectedFailure`.
+ :func:`unittest.expectedFailure`.
* :class:`~django.middleware.csrf.CsrfViewMiddleware` no longer masks the CSRF
cookie like it does the CSRF token in the DOM.
@@ -608,7 +608,7 @@ Features deprecated in 4.1
Log out via GET
---------------
-Logging out via ``GET`` requests to the :py:class:`built-in logout view
+Logging out via ``GET`` requests to the :class:`built-in logout view
` is deprecated. Use ``POST`` requests
instead.
diff --git a/docs/releases/4.2.txt b/docs/releases/4.2.txt
index 37cf228dc7cd..de6523691440 100644
--- a/docs/releases/4.2.txt
+++ b/docs/releases/4.2.txt
@@ -475,8 +475,8 @@ Miscellaneous
* Support for ``PROJ`` < 5 is removed.
* :class:`~django.core.mail.backends.smtp.EmailBackend` now verifies a
- :py:attr:`hostname ` and
- :py:attr:`certificates `. If you need the
+ :attr:`hostname ` and
+ :attr:`certificates `. If you need the
previous behavior that is less restrictive and not recommended, subclass
``EmailBackend`` and override the ``ssl_context`` property.
@@ -564,7 +564,7 @@ Miscellaneous
* The ``BaseUserManager.make_random_password()`` method is deprecated. See
`recipes and best practices
`_
- for using Python's :py:mod:`secrets` module to generate passwords.
+ for using Python's :mod:`secrets` module to generate passwords.
* The ``length_is`` template filter is deprecated in favor of :tfilter:`length`
and the ``==`` operator within an :ttag:`{% if %}` tag. For example
diff --git a/docs/releases/5.1.txt b/docs/releases/5.1.txt
index 0a5ececccafd..32d32cf9e769 100644
--- a/docs/releases/5.1.txt
+++ b/docs/releases/5.1.txt
@@ -382,7 +382,7 @@ Miscellaneous
* ``django.utils.text.Truncator`` used by :tfilter:`truncatechars_html` and
:tfilter:`truncatewords_html` template filters now uses
- :py:class:`html.parser.HTMLParser` subclasses. This results in a more robust
+ :class:`html.parser.HTMLParser` subclasses. This results in a more robust
and faster operation, but there may be small differences in the output.
* The undocumented ``django.urls.converters.get_converter()`` function is
diff --git a/docs/releases/5.2.txt b/docs/releases/5.2.txt
index d5ecce48d79e..fa005bd55098 100644
--- a/docs/releases/5.2.txt
+++ b/docs/releases/5.2.txt
@@ -380,8 +380,8 @@ Utilities
~~~~~~~~~
* :class:`~django.utils.safestring.SafeString` now returns
- :py:data:`NotImplemented` in ``__add__`` for non-string right-hand side
- values. This aligns with the :py:class:`str` addition behavior and allows
+ :data:`NotImplemented` in ``__add__`` for non-string right-hand side
+ values. This aligns with the :class:`str` addition behavior and allows
``__radd__`` to be used if available.
* :func:`~django.utils.html.format_html_join` now supports taking an iterable
diff --git a/docs/releases/6.0.txt b/docs/releases/6.0.txt
index e54b9788ce2b..94376b999415 100644
--- a/docs/releases/6.0.txt
+++ b/docs/releases/6.0.txt
@@ -79,15 +79,15 @@ Adoption of Python's modern email API
Email handling in Django now uses Python's modern email API, introduced in
Python 3.6. This API, centered around the
-:py:class:`email.message.EmailMessage` class, offers a cleaner and
+:class:`email.message.EmailMessage` class, offers a cleaner and
Unicode-friendly interface for composing and sending emails. It replaces use of
Python's older legacy (``Compat32``) API, which relied on lower-level MIME
-classes (from :py:mod:`email.mime`) and required more manual handling of
+classes (from :mod:`email.mime`) and required more manual handling of
message structure and encoding.
Notably, the return type of the :class:`EmailMessage.message()
` method is now an instance of Python's
-:py:class:`email.message.EmailMessage`. This supports the same API as the
+:class:`email.message.EmailMessage`. This supports the same API as the
previous ``SafeMIMEText`` and ``SafeMIMEMultipart`` return types, but is not an
instance of those now-deprecated classes.
@@ -228,7 +228,7 @@ Email
* The new ``policy`` argument for :class:`EmailMessage.message()
` allows specifying the email policy, the set
of rules for updating and serializing the representation of the message.
- Defaults to :py:data:`email.policy.default`.
+ Defaults to :data:`email.policy.default`.
* :class:`EmailMessage.attach() ` now accepts a
:class:`~email.message.MIMEPart` object from Python's modern email API.
@@ -439,7 +439,7 @@ Email
* The undocumented ``encoding`` property of
:class:`~django.core.mail.EmailMessage` no longer supports Python legacy
- :py:class:`email.charset.Charset` objects.
+ :class:`email.charset.Charset` objects.
* As the internal implementations of :class:`~django.core.mail.EmailMessage`
and :class:`~django.core.mail.EmailMultiAlternatives` have changed
@@ -471,7 +471,7 @@ Miscellaneous
at the end of the output, even without the ``indent`` option set.
* The undocumented ``django.utils.http.parse_header_parameters()`` function is
- refactored to use Python's :py:class:`email.message.Message` for parsing.
+ refactored to use Python's :class:`email.message.Message` for parsing.
Input headers exceeding 10000 characters will now raise :exc:`ValueError`.
* Widgets from :mod:`django.contrib.gis.forms.widgets` now render without
@@ -570,6 +570,9 @@ Miscellaneous
* The undocumented ``django.core.mail.forbid_multi_line_headers()`` and
``django.core.mail.message.sanitize_address()`` functions are deprecated.
+* The ``django.utils.crypto.constant_time_compare()`` function is deprecated
+ because it is merely an alias of :func:`hmac.compare_digest`.
+
Features removed in 6.0
=======================
diff --git a/docs/topics/async.txt b/docs/topics/async.txt
index 3c6758bd573d..8138103f927c 100644
--- a/docs/topics/async.txt
+++ b/docs/topics/async.txt
@@ -312,7 +312,7 @@ Threadlocals and contextvars values are preserved across the boundary in both
directions.
:func:`async_to_sync` is essentially a more powerful version of the
-:py:func:`asyncio.run` function in Python's standard library. As well
+:func:`asyncio.run` function in Python's standard library. As well
as ensuring threadlocals work, it also enables the ``thread_sensitive`` mode of
:func:`sync_to_async` when that wrapper is used below it.
diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt
index a2e6172f7a60..3451f71fbacc 100644
--- a/docs/topics/db/queries.txt
+++ b/docs/topics/db/queries.txt
@@ -1060,7 +1060,7 @@ representation of the JSON scalar ``null`` is the same as SQL ``NULL``, i.e.
``None``. Therefore, it can be hard to distinguish between them.
This only applies to ``None`` as the top-level value of the field. If ``None``
-is inside a :py:class:`list` or :py:class:`dict`, it will always be interpreted
+is inside a :class:`list` or :class:`dict`, it will always be interpreted
as JSON ``null``.
When querying, ``None`` value will always be interpreted as JSON ``null``. To
diff --git a/docs/topics/db/transactions.txt b/docs/topics/db/transactions.txt
index 3ffd7c37b58a..c44c8c6ee701 100644
--- a/docs/topics/db/transactions.txt
+++ b/docs/topics/db/transactions.txt
@@ -113,7 +113,7 @@ Django provides a single API to control database transactions.
durability and can be achieved by setting ``durable=True``. If the
``atomic`` block is nested within another it raises a ``RuntimeError``.
- ``atomic`` is usable both as a :py:term:`decorator`::
+ ``atomic`` is usable both as a :term:`decorator`::
from django.db import transaction
@@ -123,7 +123,7 @@ Django provides a single API to control database transactions.
# This code executes inside a transaction.
do_stuff()
- and as a :py:term:`context manager`::
+ and as a :term:`context manager`::
from django.db import transaction
diff --git a/docs/topics/email.txt b/docs/topics/email.txt
index bcb8737f4c07..0087a2655c22 100644
--- a/docs/topics/email.txt
+++ b/docs/topics/email.txt
@@ -418,11 +418,11 @@ The class has the following methods:
The keyword argument ``policy`` allows specifying the set of rules for
updating and serializing the representation of the message. It must be an
- :py:mod:`email.policy.Policy ` object. Defaults to
- :py:data:`email.policy.default`. In certain cases you may want to use
- :py:data:`~email.policy.SMTP`, :py:data:`~email.policy.SMTPUTF8` or a custom
+ :mod:`email.policy.Policy ` object. Defaults to
+ :data:`email.policy.default`. In certain cases you may want to use
+ :data:`~email.policy.SMTP`, :data:`~email.policy.SMTPUTF8` or a custom
policy. For example, :class:`django.core.mail.backends.smtp.EmailBackend`
- uses the :py:data:`~email.policy.SMTP` policy to ensure ``\r\n`` line endings
+ uses the :data:`~email.policy.SMTP` policy to ensure ``\r\n`` line endings
as required by the SMTP protocol.
If you ever need to extend Django's :class:`~django.core.mail.EmailMessage`
@@ -432,7 +432,7 @@ The class has the following methods:
.. versionchanged:: 6.0
The ``policy`` keyword argument was added and the return type was updated
- to an instance of :py:class:`~email.message.EmailMessage`.
+ to an instance of :class:`~email.message.EmailMessage`.
* ``recipients()`` returns a list of all the recipients of the message,
whether they're recorded in the ``to``, ``cc`` or ``bcc`` attributes. This
diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt
index d4441ce138e8..b3ea14dc270b 100644
--- a/docs/topics/forms/formsets.txt
+++ b/docs/topics/forms/formsets.txt
@@ -50,8 +50,8 @@ following example will create a formset class to display two blank forms:
Formsets can be iterated and indexed, accessing forms in the order they were
created. You can reorder the forms by overriding the default
-:py:meth:`iteration ` and
-:py:meth:`indexing ` behavior if needed.
+:meth:`iteration ` and
+:meth:`indexing ` behavior if needed.
.. _formsets-initial-data:
diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt
index dd2a9a2890cd..186362ca7eb5 100644
--- a/docs/topics/i18n/translation.txt
+++ b/docs/topics/i18n/translation.txt
@@ -129,7 +129,7 @@ have more than a single parameter. If you used positional interpolation,
translations wouldn't be able to reorder placeholder text.
Since string extraction is done by the ``xgettext`` command, only syntaxes
-supported by ``gettext`` are supported by Django. Python :py:ref:`f-strings
+supported by ``gettext`` are supported by Django. Python :ref:`f-strings
` cannot be used directly with ``gettext`` functions because
f-string expressions are evaluated before they reach ``gettext``. This means
``_(f"Welcome {name}")`` will not work as expected, as the variable is
diff --git a/docs/topics/performance.txt b/docs/topics/performance.txt
index 042c09f8d3ce..dcffb1a68317 100644
--- a/docs/topics/performance.txt
+++ b/docs/topics/performance.txt
@@ -204,7 +204,7 @@ therefore have performance implications, and the more expensive the work
concerned, the more there is to gain through laziness.
Python provides a number of tools for lazy evaluation, particularly through the
-:py:term:`generator` and :py:term:`generator expression` constructs. It's worth
+:term:`generator` and :term:`generator expression` constructs. It's worth
reading up on laziness in Python to discover opportunities for making use of
lazy patterns in your code.
diff --git a/docs/topics/settings.txt b/docs/topics/settings.txt
index 982668b4b124..fdf93116fbe6 100644
--- a/docs/topics/settings.txt
+++ b/docs/topics/settings.txt
@@ -44,7 +44,7 @@ by using an environment variable, :envvar:`DJANGO_SETTINGS_MODULE`.
The value of :envvar:`DJANGO_SETTINGS_MODULE` should be in Python path syntax,
e.g. ``mysite.settings``. Note that the settings module should be on the
-Python :py:data:`sys.path`.
+Python :data:`sys.path`.
The ``django-admin`` utility
diff --git a/docs/topics/signing.txt b/docs/topics/signing.txt
index 4e7da9eb14bf..57370fa4bbc9 100644
--- a/docs/topics/signing.txt
+++ b/docs/topics/signing.txt
@@ -116,7 +116,7 @@ generate signatures. You can use a different secret by passing it to the
separate values. ``sep`` cannot be in the :rfc:`URL safe base64 alphabet
<4648#section-5>`. This alphabet contains alphanumeric characters, hyphens,
and underscores. ``algorithm`` must be an algorithm supported by
- :py:mod:`hashlib`, it defaults to ``'sha256'``. ``fallback_keys`` is a list
+ :mod:`hashlib`, it defaults to ``'sha256'``. ``fallback_keys`` is a list
of additional values used to validate signed data, defaults to
:setting:`SECRET_KEY_FALLBACKS`.
@@ -192,7 +192,7 @@ created within a specified period of time:
Checks if ``value`` was signed less than ``max_age`` seconds ago,
otherwise raises ``SignatureExpired``. The ``max_age`` parameter can
- accept an integer or a :py:class:`datetime.timedelta` object.
+ accept an integer or a :class:`datetime.timedelta` object.
.. method:: sign_object(obj, serializer=JSONSerializer, compress=False)
@@ -203,7 +203,7 @@ created within a specified period of time:
Checks if ``signed_obj`` was signed less than ``max_age`` seconds ago,
otherwise raises ``SignatureExpired``. The ``max_age`` parameter can
- accept an integer or a :py:class:`datetime.timedelta` object.
+ accept an integer or a :class:`datetime.timedelta` object.
.. _signing-complex-data:
diff --git a/docs/topics/templates.txt b/docs/topics/templates.txt
index c2e2e15a7427..4ddb59d7c459 100644
--- a/docs/topics/templates.txt
+++ b/docs/topics/templates.txt
@@ -660,7 +660,7 @@ adds defaults that differ from Jinja2's for a few options:
is more in line with the design of Jinja2.
The default configuration is purposefully kept to a minimum. If a template is
-rendered with a request (e.g. when using :py:func:`~django.shortcuts.render`),
+rendered with a request (e.g. when using :func:`~django.shortcuts.render`),
the ``Jinja2`` backend adds the globals ``request``, ``csrf_input``, and
``csrf_token`` to the context. Apart from that, this backend doesn't create a
Django-flavored environment. It doesn't know about Django filters and tags.
diff --git a/docs/topics/testing/advanced.txt b/docs/topics/testing/advanced.txt
index fcc9c782c729..6a334f086247 100644
--- a/docs/topics/testing/advanced.txt
+++ b/docs/topics/testing/advanced.txt
@@ -587,7 +587,7 @@ and tear down the test suite.
If ``buffer`` is ``True``, outputs from passing tests will be discarded.
- If ``enable_faulthandler`` is ``True``, :py:mod:`faulthandler` will be
+ If ``enable_faulthandler`` is ``True``, :mod:`faulthandler` will be
enabled.
If ``timing`` is ``True``, test timings, including database setup and total
@@ -601,7 +601,7 @@ and tear down the test suite.
:ref:`Grouping by test class ` is preserved when using this
option.
- ``logger`` can be used to pass a Python :py:ref:`Logger object `.
+ ``logger`` can be used to pass a Python :ref:`Logger object `.
If provided, the logger will be used to log messages instead of printing to
the console. The logger object will respect its logging level rather than
the ``verbosity``.
@@ -661,7 +661,7 @@ Methods
Override this class method to add custom arguments accepted by the
:djadmin:`test` management command. See
- :py:meth:`argparse.ArgumentParser.add_argument` for details about adding
+ :meth:`argparse.ArgumentParser.add_argument` for details about adding
arguments to a parser.
.. method:: DiscoverRunner.setup_test_environment(**kwargs)
diff --git a/docs/topics/testing/overview.txt b/docs/topics/testing/overview.txt
index cafd9dd883d9..aa171a8caac2 100644
--- a/docs/topics/testing/overview.txt
+++ b/docs/topics/testing/overview.txt
@@ -83,7 +83,7 @@ your project's ``manage.py`` utility:
$ ./manage.py test
-Test discovery is based on the unittest module's :py:ref:`built-in test
+Test discovery is based on the unittest module's :ref:`built-in test
discovery `. By default, this will discover tests in
any file named ``test*.py`` under the current working directory.
diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt
index 4c93e762c784..bd41d6221bc6 100644
--- a/docs/topics/testing/tools.txt
+++ b/docs/topics/testing/tools.txt
@@ -965,7 +965,7 @@ It also provides an additional method:
called before each test, negating the speed benefits.
Objects assigned to class attributes in ``setUpTestData()`` must support
- creating deep copies with :py:func:`copy.deepcopy` in order to isolate them
+ creating deep copies with :func:`copy.deepcopy` in order to isolate them
from alterations performed by each test methods.
.. classmethod:: TestCase.captureOnCommitCallbacks(using=DEFAULT_DB_ALIAS, execute=False)
diff --git a/tests/template_tests/filter_tests/test_urlize.py b/tests/template_tests/filter_tests/test_urlize.py
index ca9f8fb7e1ee..692c3fc8306f 100644
--- a/tests/template_tests/filter_tests/test_urlize.py
+++ b/tests/template_tests/filter_tests/test_urlize.py
@@ -359,9 +359,8 @@ def test_brackets(self):
"www.example.com]",
)
self.assertEqual(
- urlize("see test[at[example.com"),
- 'see '
- "test[at[example.com",
+ urlize("see test[at[example.com"), # Invalid hostname.
+ "see test[at[example.com",
)
self.assertEqual(
urlize("[http://168.192.0.1](http://168.192.0.1)"),
diff --git a/tests/utils_tests/test_html.py b/tests/utils_tests/test_html.py
index f1a29977a743..7167383aef23 100644
--- a/tests/utils_tests/test_html.py
+++ b/tests/utils_tests/test_html.py
@@ -489,6 +489,7 @@ def test_urlize_unchanged_inputs(self):
"foo@localhost.",
"test@example?;+!.com",
"email me@example.com,then I'll respond",
+ "[a link](https://www.djangoproject.com/)",
# trim_punctuation catastrophic tests
"(" * 100_000 + ":" + ")" * 100_000,
"(" * 100_000 + "&:" + ")" * 100_000,