prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Login template does not respect non_fields_errors display
  When using a custom `WAGTAILADMIN_USER_LOGIN_FORM`, you can't set form-wide errors as they will always be displayed as `Your {{ username_field }} and password didn't match. Please try again.` from `"wagtailadmin/login.html"`
  
  As the default LoginForm (`wagtail.admin.forms.auth.LoginForm`) subclasses `django.contrib.auth.forms.AuthenticationForm` which already has an `'invalid_login'` error message (that is usename_field-aware) we could just use that, but if we want this particular message we could just override this error message"
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `wagtail/admin/forms/auth.py`
  Content:
  ```
  1 from django import forms
  2 from django.contrib.auth.forms import AuthenticationForm
  3 from django.contrib.auth.forms import PasswordChangeForm as DjangoPasswordChangeForm
  4 from django.contrib.auth.forms import PasswordResetForm as DjangoPasswordResetForm
  5 from django.utils.translation import gettext_lazy
  6 
  7 
  8 class LoginForm(AuthenticationForm):
  9     username = forms.CharField(max_length=254, widget=forms.TextInput())
  10 
  11     password = forms.CharField(
  12         widget=forms.PasswordInput(
  13             attrs={
  14                 "placeholder": gettext_lazy("Enter password"),
  15             }
  16         )
  17     )
  18 
  19     remember = forms.BooleanField(required=False)
  20 
  21     def __init__(self, request=None, *args, **kwargs):
  22         super().__init__(request=request, *args, **kwargs)
  23         self.fields["username"].widget.attrs["placeholder"] = gettext_lazy(
  24             "Enter your %(username_field_name)s"
  25         ) % {"username_field_name": self.username_field.verbose_name}
  26         self.fields["username"].widget.attrs["autofocus"] = ""
  27 
  28     @property
  29     def extra_fields(self):
  30         for field_name in self.fields.keys():
  31             if field_name not in ["username", "password", "remember"]:
  32                 yield field_name, self[field_name]
  33 
  34 
  35 class PasswordResetForm(DjangoPasswordResetForm):
  36     email = forms.EmailField(
  37         label=gettext_lazy("Enter your email address to reset your password"),
  38         max_length=254,
  39         required=True,
  40     )
  41 
  42     @property
  43     def extra_fields(self):
  44         for field_name in self.fields.keys():
  45             if field_name not in ["email"]:
  46                 yield field_name, self[field_name]
  47 
  48 
  49 class PasswordChangeForm(DjangoPasswordChangeForm):
  50     """
  51     Since this is displayed as part of a larger form, this differs from the vanilla Django
  52     PasswordChangeForm as follows:
  53     * the old-password field is not auto-focused
  54     * Fields are not marked as required
  55     """
  56 
  57     def __init__(self, *args, **kwargs):
  58         super().__init__(*args, **kwargs)
  59         try:
  60             del self.fields["old_password"].widget.attrs["autofocus"]
  61         except KeyError:
  62             pass
  63 
  64         self.fields["old_password"].required = False
  65         self.fields["new_password1"].required = False
  66         self.fields["new_password2"].required = False
  67
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/wagtail/admin/forms/auth.py b/wagtail/admin/forms/auth.py
  --- a/wagtail/admin/forms/auth.py
  +++ b/wagtail/admin/forms/auth.py
  @@ -18,6 +18,13 @@
   
       remember = forms.BooleanField(required=False)
   
  +    error_messages = {
  +        **AuthenticationForm.error_messages,
  +        "invalid_login": gettext_lazy(
  +            "Your %(username_field)s and password didn't match. Please try again."
  +        ),
  +    }
  +
       def __init__(self, request=None, *args, **kwargs):
           super().__init__(request=request, *args, **kwargs)
           self.fields["username"].widget.attrs["placeholder"] = gettext_lazy(
  @@ -31,6 +38,13 @@
               if field_name not in ["username", "password", "remember"]:
                   yield field_name, self[field_name]
   
  +    def get_invalid_login_error(self):
  +        return forms.ValidationError(
  +            self.error_messages["invalid_login"],
  +            code="invalid_login",
  +            params={"username_field": self.username_field.verbose_name},
  +        )
  +
   
   class PasswordResetForm(DjangoPasswordResetForm):
       email = forms.EmailField(
  
verification_info: {"golden_diff": "diff --git a/wagtail/admin/forms/auth.py b/wagtail/admin/forms/auth.py\n--- a/wagtail/admin/forms/auth.py\n+++ b/wagtail/admin/forms/auth.py\n@@ -18,6 +18,13 @@\n \n     remember = forms.BooleanField(required=False)\n \n+    error_messages = {\n+        **AuthenticationForm.error_messages,\n+        \"invalid_login\": gettext_lazy(\n+            \"Your %(username_field)s and password didn't match. Please try again.\"\n+        ),\n+    }\n+\n     def __init__(self, request=None, *args, **kwargs):\n         super().__init__(request=request, *args, **kwargs)\n         self.fields[\"username\"].widget.attrs[\"placeholder\"] = gettext_lazy(\n@@ -31,6 +38,13 @@\n             if field_name not in [\"username\", \"password\", \"remember\"]:\n                 yield field_name, self[field_name]\n \n+    def get_invalid_login_error(self):\n+        return forms.ValidationError(\n+            self.error_messages[\"invalid_login\"],\n+            code=\"invalid_login\",\n+            params={\"username_field\": self.username_field.verbose_name},\n+        )\n+\n \n class PasswordResetForm(DjangoPasswordResetForm):\n     email = forms.EmailField(\n", "issue": "Login template does not respect non_fields_errors display\nWhen using a custom `WAGTAILADMIN_USER_LOGIN_FORM`, you can't set form-wide errors as they will always be displayed as `Your {{ username_field }} and password didn't match. Please try again.` from `\"wagtailadmin/login.html\"`\r\n\r\nAs the default LoginForm (`wagtail.admin.forms.auth.LoginForm`) subclasses `django.contrib.auth.forms.AuthenticationForm` which already has an `'invalid_login'` error message (that is usename_field-aware) we could just use that, but if we want this particular message we could just override this error message\"\n", "before_files": [{"content": "from django import forms\nfrom django.contrib.auth.forms import AuthenticationForm\nfrom django.contrib.auth.forms import PasswordChangeForm as DjangoPasswordChangeForm\nfrom django.contrib.auth.forms import PasswordResetForm as DjangoPasswordResetForm\nfrom django.utils.translation import gettext_lazy\n\n\nclass LoginForm(AuthenticationForm):\n    username = forms.CharField(max_length=254, widget=forms.TextInput())\n\n    password = forms.CharField(\n        widget=forms.PasswordInput(\n            attrs={\n                \"placeholder\": gettext_lazy(\"Enter password\"),\n            }\n        )\n    )\n\n    remember = forms.BooleanField(required=False)\n\n    def __init__(self, request=None, *args, **kwargs):\n        super().__init__(request=request, *args, **kwargs)\n        self.fields[\"username\"].widget.attrs[\"placeholder\"] = gettext_lazy(\n            \"Enter your %(username_field_name)s\"\n        ) % {\"username_field_name\": self.username_field.verbose_name}\n        self.fields[\"username\"].widget.attrs[\"autofocus\"] = \"\"\n\n    @property\n    def extra_fields(self):\n        for field_name in self.fields.keys():\n            if field_name not in [\"username\", \"password\", \"remember\"]:\n                yield field_name, self[field_name]\n\n\nclass PasswordResetForm(DjangoPasswordResetForm):\n    email = forms.EmailField(\n        label=gettext_lazy(\"Enter your email address to reset your password\"),\n        max_length=254,\n        required=True,\n    )\n\n    @property\n    def extra_fields(self):\n        for field_name in self.fields.keys():\n            if field_name not in [\"email\"]:\n                yield field_name, self[field_name]\n\n\nclass PasswordChangeForm(DjangoPasswordChangeForm):\n    \"\"\"\n    Since this is displayed as part of a larger form, this differs from the vanilla Django\n    PasswordChangeForm as follows:\n    * the old-password field is not auto-focused\n    * Fields are not marked as required\n    \"\"\"\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        try:\n            del self.fields[\"old_password\"].widget.attrs[\"autofocus\"]\n        except KeyError:\n            pass\n\n        self.fields[\"old_password\"].required = False\n        self.fields[\"new_password1\"].required = False\n        self.fields[\"new_password2\"].required = False\n", "path": "wagtail/admin/forms/auth.py"}], "after_files": [{"content": "from django import forms\nfrom django.contrib.auth.forms import AuthenticationForm\nfrom django.contrib.auth.forms import PasswordChangeForm as DjangoPasswordChangeForm\nfrom django.contrib.auth.forms import PasswordResetForm as DjangoPasswordResetForm\nfrom django.utils.translation import gettext_lazy\n\n\nclass LoginForm(AuthenticationForm):\n    username = forms.CharField(max_length=254, widget=forms.TextInput())\n\n    password = forms.CharField(\n        widget=forms.PasswordInput(\n            attrs={\n                \"placeholder\": gettext_lazy(\"Enter password\"),\n            }\n        )\n    )\n\n    remember = forms.BooleanField(required=False)\n\n    error_messages = {\n        **AuthenticationForm.error_messages,\n        \"invalid_login\": gettext_lazy(\n            \"Your %(username_field)s and password didn't match. Please try again.\"\n        ),\n    }\n\n    def __init__(self, request=None, *args, **kwargs):\n        super().__init__(request=request, *args, **kwargs)\n        self.fields[\"username\"].widget.attrs[\"placeholder\"] = gettext_lazy(\n            \"Enter your %(username_field_name)s\"\n        ) % {\"username_field_name\": self.username_field.verbose_name}\n        self.fields[\"username\"].widget.attrs[\"autofocus\"] = \"\"\n\n    @property\n    def extra_fields(self):\n        for field_name in self.fields.keys():\n            if field_name not in [\"username\", \"password\", \"remember\"]:\n                yield field_name, self[field_name]\n\n    def get_invalid_login_error(self):\n        return forms.ValidationError(\n            self.error_messages[\"invalid_login\"],\n            code=\"invalid_login\",\n            params={\"username_field\": self.username_field.verbose_name},\n        )\n\n\nclass PasswordResetForm(DjangoPasswordResetForm):\n    email = forms.EmailField(\n        label=gettext_lazy(\"Enter your email address to reset your password\"),\n        max_length=254,\n        required=True,\n    )\n\n    @property\n    def extra_fields(self):\n        for field_name in self.fields.keys():\n            if field_name not in [\"email\"]:\n                yield field_name, self[field_name]\n\n\nclass PasswordChangeForm(DjangoPasswordChangeForm):\n    \"\"\"\n    Since this is displayed as part of a larger form, this differs from the vanilla Django\n    PasswordChangeForm as follows:\n    * the old-password field is not auto-focused\n    * Fields are not marked as required\n    \"\"\"\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        try:\n            del self.fields[\"old_password\"].widget.attrs[\"autofocus\"]\n        except KeyError:\n            pass\n\n        self.fields[\"old_password\"].required = False\n        self.fields[\"new_password1\"].required = False\n        self.fields[\"new_password2\"].required = False\n", "path": "wagtail/admin/forms/auth.py"}]}