prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Enhance form rendering: Radio button list
  This is a follow-on to #322, to enhance how forms are rendered in the [`page` template](https://github.com/cal-itp/benefits/blob/dev/benefits/core/templates/core/page.html)
  
  @angela-tran [notes](https://github.com/cal-itp/benefits/pull/337#issuecomment-1076882758):
  
  > `RadioSelect` in Django 3.2 renders as `<li>` elements. We probably don't want that? Should we add something to `page.html` so that we have more granular control over how radio buttons are shown? (Django docs have an [example](https://docs.djangoproject.com/en/3.2/ref/forms/widgets/#radioselect) of looping over the widgets.)
  
  ![image](https://user-images.githubusercontent.com/1783439/160215029-07dadd9d-e422-49ea-91b3-533485236daa.png)
  
  The [State Template form components](https://template.webstandards.ca.gov/sample/form.html) has some example HTML for what might work here?
  
  ```html
  <!--Bootstrap4 Custom Radios-->
  <div class="form-check">
      <input class="form-check-input" type="radio" name="flexRadioDefault" id="customRadio1">
      <label class="form-check-label" for="customRadio1">
          Toggle this custom radio
      </label>
  </div>
  <div class="form-check">
      <input class="form-check-input" type="radio" name="flexRadioDefault" id="customRadio2" checked="">
      <label class="form-check-label" for="customRadio2">
          Or toggle this other custom radio
      </label>
  </div>
  ```
  
  The comment indicates this is just [Bootstrap 4 radios](https://getbootstrap.com/docs/4.0/components/forms/#radios)?
  
  @angela-tran [suggests](https://github.com/cal-itp/benefits/pull/337#issuecomment-1076882758):
  
  > Rendering the `selection_label_description` may involve adding some HTML to the display value in our `choices` tuple. My first step would be to try out the approach in https://stackoverflow.com/questions/2204696/setting-help-text-for-each-choice-in-a-radioselect.
  
  ![image](https://user-images.githubusercontent.com/1783439/160215069-abd2c18b-0ebf-4d78-a202-be7947e70ab5.png)
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `benefits/core/widgets.py`
  Content:
  ```
  1 """
  2 The core application: Helper form widgets.
  3 """
  4 from django.forms import widgets
  5 
  6 
  7 class FormControlTextInput(widgets.TextInput):
  8     """A styled text input."""
  9 
  10     def __init__(self, pattern=None, placeholder=None, **kwargs):
  11         super().__init__(**kwargs)
  12 
  13         self.attrs.update({"class": "form-control form-control-lg"})
  14         if pattern:
  15             self.attrs.update({"pattern": pattern})
  16         if placeholder:
  17             self.attrs.update({"placeholder": placeholder})
  18
  ```
  
  Path: `benefits/eligibility/forms.py`
  Content:
  ```
  1 """
  2 The eligibility application: Form definition for the eligibility verification flow.
  3 """
  4 import logging
  5 
  6 from django import forms
  7 from django.utils.translation import gettext_lazy as _
  8 
  9 from benefits.core import models, recaptcha, widgets
  10 
  11 
  12 logger = logging.getLogger(__name__)
  13 
  14 
  15 class EligibilityVerifierSelectionForm(forms.Form):
  16     """Form to capture eligibility verifier selection."""
  17 
  18     action_url = "eligibility:index"
  19     method = "POST"
  20 
  21     verifier = forms.ChoiceField(label="", widget=forms.RadioSelect)
  22 
  23     submit_value = _("eligibility.buttons.continue")
  24 
  25     def __init__(self, agency: models.TransitAgency, *args, **kwargs):
  26         super().__init__(*args, **kwargs)
  27         choices = [(v.id, _(v.selection_label)) for v in agency.eligibility_verifiers.all()]
  28         self.fields["verifier"].choices = choices
  29 
  30 
  31 class EligibilityVerificationForm(forms.Form):
  32     """Form to collect eligibility verification details."""
  33 
  34     action_url = "eligibility:confirm"
  35     method = "POST"
  36 
  37     submit_value = _("eligibility.forms.confirm.submit")
  38     submitting_value = _("eligibility.forms.confirm.submitting")
  39 
  40     _error_messages = {
  41         "invalid": _("eligibility.forms.confirm.errors.invalid"),
  42         "missing": _("eligibility.forms.confirm.errors.missing"),
  43     }
  44 
  45     def __init__(self, verifier: models.EligibilityVerifier, *args, **kwargs):
  46         super().__init__(*args, **kwargs)
  47 
  48         sub_widget = widgets.FormControlTextInput(placeholder=verifier.form_sub_placeholder)
  49         if verifier.form_sub_pattern:
  50             sub_widget.attrs.update({"pattern": verifier.form_sub_pattern})
  51 
  52         self.fields["sub"] = forms.CharField(label=_(verifier.form_sub_label), widget=sub_widget)
  53 
  54         name_widget = widgets.FormControlTextInput(placeholder=verifier.form_name_placeholder)
  55         if verifier.form_name_max_length:
  56             name_widget.attrs.update({"maxlength": verifier.form_name_max_length})
  57 
  58         self.fields["name"] = forms.CharField(label=_(verifier.form_name_label), widget=name_widget)
  59 
  60     def add_api_errors(self, form_errors):
  61         """Handle errors passed back from API server related to submitted form values."""
  62 
  63         validation_errors = {
  64             field: forms.ValidationError(self._error_messages.get(code, _("core.pages.error.title")), code=code)
  65             for (field, code) in form_errors.items()
  66             if field in self.fields
  67         }
  68 
  69         if len(validation_errors) > 0:
  70             logger.warning("Form fields are invalid")
  71 
  72         for (field, err) in validation_errors.items():
  73             self.add_error(field, err)
  74 
  75     def clean(self):
  76         if not recaptcha.verify(self.data):
  77             raise forms.ValidationError("reCAPTCHA failed")
  78
  ```
  --- 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/benefits/core/widgets.py b/benefits/core/widgets.py
  --- a/benefits/core/widgets.py
  +++ b/benefits/core/widgets.py
  @@ -1,6 +1,7 @@
   """
   The core application: Helper form widgets.
   """
  +import copy
   from django.forms import widgets
   
   
  @@ -15,3 +16,25 @@
               self.attrs.update({"pattern": pattern})
           if placeholder:
               self.attrs.update({"placeholder": placeholder})
  +
  +
  +class RadioSelect(widgets.RadioSelect):
  +    template_name = "core/widgets/radio_select.html"
  +    option_template_name = "core/widgets/radio_select_option.html"
  +
  +    def __init__(self, choice_descriptions=(), *args, **kwargs):
  +        super().__init__(*args, **kwargs)
  +        self.choice_descriptions = list(choice_descriptions)
  +
  +    def __deepcopy__(self, memo):
  +        obj = super().__deepcopy__(memo)
  +        obj.choice_descriptions = copy.copy(self.choice_descriptions)
  +        return obj
  +
  +    def create_option(self, name, value, label, selected, index, subindex, attrs):
  +        option = super().create_option(name, value, label, selected, index, subindex, attrs)
  +        # this implementation does not support groups from ChoiceWidget.optgroups
  +        if value in self.choice_descriptions:
  +            option.update({"description": self.choice_descriptions[value]})
  +
  +        return option
  diff --git a/benefits/eligibility/forms.py b/benefits/eligibility/forms.py
  --- a/benefits/eligibility/forms.py
  +++ b/benefits/eligibility/forms.py
  @@ -18,14 +18,18 @@
       action_url = "eligibility:index"
       method = "POST"
   
  -    verifier = forms.ChoiceField(label="", widget=forms.RadioSelect)
  +    verifier = forms.ChoiceField(label="", widget=widgets.RadioSelect)
   
       submit_value = _("eligibility.buttons.continue")
   
       def __init__(self, agency: models.TransitAgency, *args, **kwargs):
           super().__init__(*args, **kwargs)
  -        choices = [(v.id, _(v.selection_label)) for v in agency.eligibility_verifiers.all()]
  -        self.fields["verifier"].choices = choices
  +        verifiers = agency.eligibility_verifiers.all()
  +
  +        self.fields["verifier"].choices = [(v.id, _(v.selection_label)) for v in verifiers]
  +        self.fields["verifier"].widget.choice_descriptions = {
  +            v.id: _(v.selection_label_description) for v in verifiers if v.selection_label_description
  +        }
   
   
   class EligibilityVerificationForm(forms.Form):
  
verification_info: {"golden_diff": "diff --git a/benefits/core/widgets.py b/benefits/core/widgets.py\n--- a/benefits/core/widgets.py\n+++ b/benefits/core/widgets.py\n@@ -1,6 +1,7 @@\n \"\"\"\n The core application: Helper form widgets.\n \"\"\"\n+import copy\n from django.forms import widgets\n \n \n@@ -15,3 +16,25 @@\n             self.attrs.update({\"pattern\": pattern})\n         if placeholder:\n             self.attrs.update({\"placeholder\": placeholder})\n+\n+\n+class RadioSelect(widgets.RadioSelect):\n+    template_name = \"core/widgets/radio_select.html\"\n+    option_template_name = \"core/widgets/radio_select_option.html\"\n+\n+    def __init__(self, choice_descriptions=(), *args, **kwargs):\n+        super().__init__(*args, **kwargs)\n+        self.choice_descriptions = list(choice_descriptions)\n+\n+    def __deepcopy__(self, memo):\n+        obj = super().__deepcopy__(memo)\n+        obj.choice_descriptions = copy.copy(self.choice_descriptions)\n+        return obj\n+\n+    def create_option(self, name, value, label, selected, index, subindex, attrs):\n+        option = super().create_option(name, value, label, selected, index, subindex, attrs)\n+        # this implementation does not support groups from ChoiceWidget.optgroups\n+        if value in self.choice_descriptions:\n+            option.update({\"description\": self.choice_descriptions[value]})\n+\n+        return option\ndiff --git a/benefits/eligibility/forms.py b/benefits/eligibility/forms.py\n--- a/benefits/eligibility/forms.py\n+++ b/benefits/eligibility/forms.py\n@@ -18,14 +18,18 @@\n     action_url = \"eligibility:index\"\n     method = \"POST\"\n \n-    verifier = forms.ChoiceField(label=\"\", widget=forms.RadioSelect)\n+    verifier = forms.ChoiceField(label=\"\", widget=widgets.RadioSelect)\n \n     submit_value = _(\"eligibility.buttons.continue\")\n \n     def __init__(self, agency: models.TransitAgency, *args, **kwargs):\n         super().__init__(*args, **kwargs)\n-        choices = [(v.id, _(v.selection_label)) for v in agency.eligibility_verifiers.all()]\n-        self.fields[\"verifier\"].choices = choices\n+        verifiers = agency.eligibility_verifiers.all()\n+\n+        self.fields[\"verifier\"].choices = [(v.id, _(v.selection_label)) for v in verifiers]\n+        self.fields[\"verifier\"].widget.choice_descriptions = {\n+            v.id: _(v.selection_label_description) for v in verifiers if v.selection_label_description\n+        }\n \n \n class EligibilityVerificationForm(forms.Form):\n", "issue": "Enhance form rendering: Radio button list\nThis is a follow-on to #322, to enhance how forms are rendered in the [`page` template](https://github.com/cal-itp/benefits/blob/dev/benefits/core/templates/core/page.html)\r\n\r\n@angela-tran [notes](https://github.com/cal-itp/benefits/pull/337#issuecomment-1076882758):\r\n\r\n> `RadioSelect` in Django 3.2 renders as `<li>` elements. We probably don't want that? Should we add something to `page.html` so that we have more granular control over how radio buttons are shown? (Django docs have an [example](https://docs.djangoproject.com/en/3.2/ref/forms/widgets/#radioselect) of looping over the widgets.)\r\n\r\n![image](https://user-images.githubusercontent.com/1783439/160215029-07dadd9d-e422-49ea-91b3-533485236daa.png)\r\n\r\nThe [State Template form components](https://template.webstandards.ca.gov/sample/form.html) has some example HTML for what might work here?\r\n\r\n```html\r\n<!--Bootstrap4 Custom Radios-->\r\n<div class=\"form-check\">\r\n    <input class=\"form-check-input\" type=\"radio\" name=\"flexRadioDefault\" id=\"customRadio1\">\r\n    <label class=\"form-check-label\" for=\"customRadio1\">\r\n        Toggle this custom radio\r\n    </label>\r\n</div>\r\n<div class=\"form-check\">\r\n    <input class=\"form-check-input\" type=\"radio\" name=\"flexRadioDefault\" id=\"customRadio2\" checked=\"\">\r\n    <label class=\"form-check-label\" for=\"customRadio2\">\r\n        Or toggle this other custom radio\r\n    </label>\r\n</div>\r\n```\r\n\r\nThe comment indicates this is just [Bootstrap 4 radios](https://getbootstrap.com/docs/4.0/components/forms/#radios)?\r\n\r\n@angela-tran [suggests](https://github.com/cal-itp/benefits/pull/337#issuecomment-1076882758):\r\n\r\n> Rendering the `selection_label_description` may involve adding some HTML to the display value in our `choices` tuple. My first step would be to try out the approach in https://stackoverflow.com/questions/2204696/setting-help-text-for-each-choice-in-a-radioselect.\r\n\r\n![image](https://user-images.githubusercontent.com/1783439/160215069-abd2c18b-0ebf-4d78-a202-be7947e70ab5.png)\r\n\n", "before_files": [{"content": "\"\"\"\nThe core application: Helper form widgets.\n\"\"\"\nfrom django.forms import widgets\n\n\nclass FormControlTextInput(widgets.TextInput):\n    \"\"\"A styled text input.\"\"\"\n\n    def __init__(self, pattern=None, placeholder=None, **kwargs):\n        super().__init__(**kwargs)\n\n        self.attrs.update({\"class\": \"form-control form-control-lg\"})\n        if pattern:\n            self.attrs.update({\"pattern\": pattern})\n        if placeholder:\n            self.attrs.update({\"placeholder\": placeholder})\n", "path": "benefits/core/widgets.py"}, {"content": "\"\"\"\nThe eligibility application: Form definition for the eligibility verification flow.\n\"\"\"\nimport logging\n\nfrom django import forms\nfrom django.utils.translation import gettext_lazy as _\n\nfrom benefits.core import models, recaptcha, widgets\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass EligibilityVerifierSelectionForm(forms.Form):\n    \"\"\"Form to capture eligibility verifier selection.\"\"\"\n\n    action_url = \"eligibility:index\"\n    method = \"POST\"\n\n    verifier = forms.ChoiceField(label=\"\", widget=forms.RadioSelect)\n\n    submit_value = _(\"eligibility.buttons.continue\")\n\n    def __init__(self, agency: models.TransitAgency, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        choices = [(v.id, _(v.selection_label)) for v in agency.eligibility_verifiers.all()]\n        self.fields[\"verifier\"].choices = choices\n\n\nclass EligibilityVerificationForm(forms.Form):\n    \"\"\"Form to collect eligibility verification details.\"\"\"\n\n    action_url = \"eligibility:confirm\"\n    method = \"POST\"\n\n    submit_value = _(\"eligibility.forms.confirm.submit\")\n    submitting_value = _(\"eligibility.forms.confirm.submitting\")\n\n    _error_messages = {\n        \"invalid\": _(\"eligibility.forms.confirm.errors.invalid\"),\n        \"missing\": _(\"eligibility.forms.confirm.errors.missing\"),\n    }\n\n    def __init__(self, verifier: models.EligibilityVerifier, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n\n        sub_widget = widgets.FormControlTextInput(placeholder=verifier.form_sub_placeholder)\n        if verifier.form_sub_pattern:\n            sub_widget.attrs.update({\"pattern\": verifier.form_sub_pattern})\n\n        self.fields[\"sub\"] = forms.CharField(label=_(verifier.form_sub_label), widget=sub_widget)\n\n        name_widget = widgets.FormControlTextInput(placeholder=verifier.form_name_placeholder)\n        if verifier.form_name_max_length:\n            name_widget.attrs.update({\"maxlength\": verifier.form_name_max_length})\n\n        self.fields[\"name\"] = forms.CharField(label=_(verifier.form_name_label), widget=name_widget)\n\n    def add_api_errors(self, form_errors):\n        \"\"\"Handle errors passed back from API server related to submitted form values.\"\"\"\n\n        validation_errors = {\n            field: forms.ValidationError(self._error_messages.get(code, _(\"core.pages.error.title\")), code=code)\n            for (field, code) in form_errors.items()\n            if field in self.fields\n        }\n\n        if len(validation_errors) > 0:\n            logger.warning(\"Form fields are invalid\")\n\n        for (field, err) in validation_errors.items():\n            self.add_error(field, err)\n\n    def clean(self):\n        if not recaptcha.verify(self.data):\n            raise forms.ValidationError(\"reCAPTCHA failed\")\n", "path": "benefits/eligibility/forms.py"}], "after_files": [{"content": "\"\"\"\nThe core application: Helper form widgets.\n\"\"\"\nimport copy\nfrom django.forms import widgets\n\n\nclass FormControlTextInput(widgets.TextInput):\n    \"\"\"A styled text input.\"\"\"\n\n    def __init__(self, pattern=None, placeholder=None, **kwargs):\n        super().__init__(**kwargs)\n\n        self.attrs.update({\"class\": \"form-control form-control-lg\"})\n        if pattern:\n            self.attrs.update({\"pattern\": pattern})\n        if placeholder:\n            self.attrs.update({\"placeholder\": placeholder})\n\n\nclass RadioSelect(widgets.RadioSelect):\n    template_name = \"core/widgets/radio_select.html\"\n    option_template_name = \"core/widgets/radio_select_option.html\"\n\n    def __init__(self, choice_descriptions=(), *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.choice_descriptions = list(choice_descriptions)\n\n    def __deepcopy__(self, memo):\n        obj = super().__deepcopy__(memo)\n        obj.choice_descriptions = copy.copy(self.choice_descriptions)\n        return obj\n\n    def create_option(self, name, value, label, selected, index, subindex, attrs):\n        option = super().create_option(name, value, label, selected, index, subindex, attrs)\n        # this implementation does not support groups from ChoiceWidget.optgroups\n        if value in self.choice_descriptions:\n            option.update({\"description\": self.choice_descriptions[value]})\n\n        return option\n", "path": "benefits/core/widgets.py"}, {"content": "\"\"\"\nThe eligibility application: Form definition for the eligibility verification flow.\n\"\"\"\nimport logging\n\nfrom django import forms\nfrom django.utils.translation import gettext_lazy as _\n\nfrom benefits.core import models, recaptcha, widgets\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass EligibilityVerifierSelectionForm(forms.Form):\n    \"\"\"Form to capture eligibility verifier selection.\"\"\"\n\n    action_url = \"eligibility:index\"\n    method = \"POST\"\n\n    verifier = forms.ChoiceField(label=\"\", widget=widgets.RadioSelect)\n\n    submit_value = _(\"eligibility.buttons.continue\")\n\n    def __init__(self, agency: models.TransitAgency, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        verifiers = agency.eligibility_verifiers.all()\n\n        self.fields[\"verifier\"].choices = [(v.id, _(v.selection_label)) for v in verifiers]\n        self.fields[\"verifier\"].widget.choice_descriptions = {\n            v.id: _(v.selection_label_description) for v in verifiers if v.selection_label_description\n        }\n\n\nclass EligibilityVerificationForm(forms.Form):\n    \"\"\"Form to collect eligibility verification details.\"\"\"\n\n    action_url = \"eligibility:confirm\"\n    method = \"POST\"\n\n    submit_value = _(\"eligibility.forms.confirm.submit\")\n    submitting_value = _(\"eligibility.forms.confirm.submitting\")\n\n    _error_messages = {\n        \"invalid\": _(\"eligibility.forms.confirm.errors.invalid\"),\n        \"missing\": _(\"eligibility.forms.confirm.errors.missing\"),\n    }\n\n    def __init__(self, verifier: models.EligibilityVerifier, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n\n        sub_widget = widgets.FormControlTextInput(placeholder=verifier.form_sub_placeholder)\n        if verifier.form_sub_pattern:\n            sub_widget.attrs.update({\"pattern\": verifier.form_sub_pattern})\n\n        self.fields[\"sub\"] = forms.CharField(label=_(verifier.form_sub_label), widget=sub_widget)\n\n        name_widget = widgets.FormControlTextInput(placeholder=verifier.form_name_placeholder)\n        if verifier.form_name_max_length:\n            name_widget.attrs.update({\"maxlength\": verifier.form_name_max_length})\n\n        self.fields[\"name\"] = forms.CharField(label=_(verifier.form_name_label), widget=name_widget)\n\n    def add_api_errors(self, form_errors):\n        \"\"\"Handle errors passed back from API server related to submitted form values.\"\"\"\n\n        validation_errors = {\n            field: forms.ValidationError(self._error_messages.get(code, _(\"core.pages.error.title\")), code=code)\n            for (field, code) in form_errors.items()\n            if field in self.fields\n        }\n\n        if len(validation_errors) > 0:\n            logger.warning(\"Form fields are invalid\")\n\n        for (field, err) in validation_errors.items():\n            self.add_error(field, err)\n\n    def clean(self):\n        if not recaptcha.verify(self.data):\n            raise forms.ValidationError(\"reCAPTCHA failed\")\n", "path": "benefits/eligibility/forms.py"}]}