We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Invalid HTML for select element
This html is generated by the Python form code.

template:
https://github.com/pypa/warehouse/blob/master/warehouse/templates/manage/roles.html

field:
`{{ form.role_name }}`

ERROR: The first child “option” element of a “select” element with a “required” attribute, and without a “multiple” attribute, and without a “size” attribute whose value is greater than “1”, must have either an empty “value” attribute, or must have no text content. Consider either adding a placeholder option label, or adding a “size” attribute with a value equal to the number of “option” elements. (433)

Reference:
https://maxdesign.com.au/articles/select-required/


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `warehouse/manage/forms.py`
Content:
```
1 # Licensed under the Apache License, Version 2.0 (the "License");
2 # you may not use this file except in compliance with the License.
3 # You may obtain a copy of the License at
4 #
5 # http://www.apache.org/licenses/LICENSE-2.0
6 #
7 # Unless required by applicable law or agreed to in writing, software
8 # distributed under the License is distributed on an "AS IS" BASIS,
9 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 # See the License for the specific language governing permissions and
11 # limitations under the License.
12 
13 import json
14 
15 import wtforms
16 
17 import warehouse.utils.otp as otp
18 import warehouse.utils.webauthn as webauthn
19 
20 from warehouse import forms
21 from warehouse.accounts.forms import (
22     NewEmailMixin,
23     NewPasswordMixin,
24     PasswordMixin,
25     TOTPValueMixin,
26     WebAuthnCredentialMixin,
27 )
28 
29 
30 class RoleNameMixin:
31 
32     role_name = wtforms.SelectField(
33         "Select role",
34         choices=[("Maintainer", "Maintainer"), ("Owner", "Owner")],
35         validators=[wtforms.validators.DataRequired(message="Select role")],
36     )
37 
38 
39 class UsernameMixin:
40 
41     username = wtforms.StringField(
42         validators=[wtforms.validators.DataRequired(message="Specify username")]
43     )
44 
45     def validate_username(self, field):
46         userid = self.user_service.find_userid(field.data)
47 
48         if userid is None:
49             raise wtforms.validators.ValidationError(
50                 "No user found with that username. Try again."
51             )
52 
53 
54 class CreateRoleForm(RoleNameMixin, UsernameMixin, forms.Form):
55     def __init__(self, *args, user_service, **kwargs):
56         super().__init__(*args, **kwargs)
57         self.user_service = user_service
58 
59 
60 class ChangeRoleForm(RoleNameMixin, forms.Form):
61     pass
62 
63 
64 class SaveAccountForm(forms.Form):
65 
66     __params__ = ["name"]
67 
68     name = wtforms.StringField()
69 
70 
71 class AddEmailForm(NewEmailMixin, forms.Form):
72 
73     __params__ = ["email"]
74 
75     def __init__(self, *args, user_service, user_id, **kwargs):
76         super().__init__(*args, **kwargs)
77         self.user_service = user_service
78         self.user_id = user_id
79 
80 
81 class ChangePasswordForm(PasswordMixin, NewPasswordMixin, forms.Form):
82 
83     __params__ = ["password", "new_password", "password_confirm"]
84 
85     def __init__(self, *args, user_service, **kwargs):
86         super().__init__(*args, **kwargs)
87         self.user_service = user_service
88 
89 
90 class DeleteTOTPForm(UsernameMixin, forms.Form):
91 
92     __params__ = ["confirm_username"]
93 
94     def __init__(self, *args, user_service, **kwargs):
95         super().__init__(*args, **kwargs)
96         self.user_service = user_service
97 
98 
99 class ProvisionTOTPForm(TOTPValueMixin, forms.Form):
100 
101     __params__ = ["totp_value"]
102 
103     def __init__(self, *args, totp_secret, **kwargs):
104         super().__init__(*args, **kwargs)
105         self.totp_secret = totp_secret
106 
107     def validate_totp_value(self, field):
108         totp_value = field.data.encode("utf8")
109         if not otp.verify_totp(self.totp_secret, totp_value):
110             raise wtforms.validators.ValidationError("Invalid TOTP code. Try again?")
111 
112 
113 class DeleteWebAuthnForm(forms.Form):
114     __params__ = ["confirm_device_name"]
115 
116     label = wtforms.StringField(
117         validators=[
118             wtforms.validators.DataRequired(message="Specify a device name"),
119             wtforms.validators.Length(
120                 max=64, message=("Label must be 64 characters or less")
121             ),
122         ]
123     )
124 
125     def __init__(self, *args, user_service, user_id, **kwargs):
126         super().__init__(*args, **kwargs)
127         self.user_service = user_service
128         self.user_id = user_id
129 
130     def validate_label(self, field):
131         label = field.data
132 
133         webauthn = self.user_service.get_webauthn_by_label(self.user_id, label)
134         if webauthn is None:
135             raise wtforms.validators.ValidationError("No WebAuthn key with given label")
136         self.webauthn = webauthn
137 
138 
139 class ProvisionWebAuthnForm(WebAuthnCredentialMixin, forms.Form):
140     __params__ = ["label", "credential"]
141 
142     label = wtforms.StringField(
143         validators=[
144             wtforms.validators.DataRequired(message="Specify a label"),
145             wtforms.validators.Length(
146                 max=64, message=("Label must be 64 characters or less")
147             ),
148         ]
149     )
150 
151     def __init__(
152         self, *args, user_service, user_id, challenge, rp_id, origin, **kwargs
153     ):
154         super().__init__(*args, **kwargs)
155         self.user_service = user_service
156         self.user_id = user_id
157         self.challenge = challenge
158         self.rp_id = rp_id
159         self.origin = origin
160 
161     def validate_credential(self, field):
162         try:
163             credential_dict = json.loads(field.data.encode("utf8"))
164         except json.JSONDecodeError:
165             raise wtforms.validators.ValidationError(
166                 "Invalid WebAuthn credential: Bad payload"
167             )
168 
169         try:
170             validated_credential = self.user_service.verify_webauthn_credential(
171                 credential_dict,
172                 challenge=self.challenge,
173                 rp_id=self.rp_id,
174                 origin=self.origin,
175             )
176         except webauthn.RegistrationRejectedException as e:
177             raise wtforms.validators.ValidationError(str(e))
178 
179         self.validated_credential = validated_credential
180 
181     def validate_label(self, field):
182         label = field.data
183 
184         if self.user_service.get_webauthn_by_label(self.user_id, label) is not None:
185             raise wtforms.validators.ValidationError(f"Label '{label}' already in use")
186 
187 
188 class CreateMacaroonForm(forms.Form):
189     __params__ = ["description", "token_scope"]
190 
191     def __init__(self, *args, user_id, macaroon_service, project_names, **kwargs):
192         super().__init__(*args, **kwargs)
193         self.user_id = user_id
194         self.macaroon_service = macaroon_service
195         self.project_names = project_names
196 
197     description = wtforms.StringField(
198         validators=[
199             wtforms.validators.DataRequired(message="Specify a token name"),
200             wtforms.validators.Length(
201                 max=100, message="Description must be 100 characters or less"
202             ),
203         ]
204     )
205 
206     token_scope = wtforms.StringField(
207         validators=[wtforms.validators.DataRequired(message="Specify the token scope")]
208     )
209 
210     def validate_description(self, field):
211         description = field.data
212 
213         if (
214             self.macaroon_service.get_macaroon_by_description(self.user_id, description)
215             is not None
216         ):
217             raise wtforms.validators.ValidationError("API token name already in use")
218 
219     def validate_token_scope(self, field):
220         scope = field.data
221 
222         try:
223             _, scope_kind = scope.split(":", 1)
224         except ValueError:
225             raise wtforms.ValidationError(f"Unknown token scope: {scope}")
226 
227         if scope_kind == "unspecified":
228             raise wtforms.ValidationError(f"Specify the token scope")
229 
230         if scope_kind == "user":
231             self.validated_scope = scope_kind
232             return
233 
234         try:
235             scope_kind, scope_value = scope_kind.split(":", 1)
236         except ValueError:
237             raise wtforms.ValidationError(f"Unknown token scope: {scope}")
238 
239         if scope_kind != "project":
240             raise wtforms.ValidationError(f"Unknown token scope: {scope}")
241         if scope_value not in self.project_names:
242             raise wtforms.ValidationError(
243                 f"Unknown or invalid project name: {scope_value}"
244             )
245 
246         self.validated_scope = {"projects": [scope_value]}
247 
248 
249 class DeleteMacaroonForm(forms.Form):
250     __params__ = ["macaroon_id"]
251 
252     macaroon_id = wtforms.StringField(
253         validators=[wtforms.validators.DataRequired(message="Identifier required")]
254     )
255 
256     def __init__(self, *args, macaroon_service, **kwargs):
257         super().__init__(*args, **kwargs)
258         self.macaroon_service = macaroon_service
259 
260     def validate_macaroon_id(self, field):
261         macaroon_id = field.data
262         if self.macaroon_service.find_macaroon(macaroon_id) is None:
263             raise wtforms.validators.ValidationError("No such macaroon")
264
```
--- 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()

```

diff --git a/warehouse/manage/forms.py b/warehouse/manage/forms.py
--- a/warehouse/manage/forms.py
+++ b/warehouse/manage/forms.py
@@ -31,7 +31,7 @@
 
     role_name = wtforms.SelectField(
         "Select role",
-        choices=[("Maintainer", "Maintainer"), ("Owner", "Owner")],
+        choices=[("", "Select role"), ("Maintainer", "Maintainer"), ("Owner", "Owner")],
         validators=[wtforms.validators.DataRequired(message="Select role")],
     )

{"golden_diff": "diff --git a/warehouse/manage/forms.py b/warehouse/manage/forms.py\n--- a/warehouse/manage/forms.py\n+++ b/warehouse/manage/forms.py\n@@ -31,7 +31,7 @@\n \n     role_name = wtforms.SelectField(\n         \"Select role\",\n-        choices=[(\"Maintainer\", \"Maintainer\"), (\"Owner\", \"Owner\")],\n+        choices=[(\"\", \"Select role\"), (\"Maintainer\", \"Maintainer\"), (\"Owner\", \"Owner\")],\n         validators=[wtforms.validators.DataRequired(message=\"Select role\")],\n     )\n", "issue": "Invalid HTML for select element\nThis html is generated by the Python form code.\r\n\r\ntemplate:\r\nhttps://github.com/pypa/warehouse/blob/master/warehouse/templates/manage/roles.html\r\n\r\nfield:\r\n`{{ form.role_name }}`\r\n\r\nERROR: The first child \u201coption\u201d element of a \u201cselect\u201d element with a \u201crequired\u201d attribute, and without a \u201cmultiple\u201d attribute, and without a \u201csize\u201d attribute whose value is greater than \u201c1\u201d, must have either an empty \u201cvalue\u201d attribute, or must have no text content. Consider either adding a placeholder option label, or adding a \u201csize\u201d attribute with a value equal to the number of \u201coption\u201d elements. (433)\r\n\r\nReference:\r\nhttps://maxdesign.com.au/articles/select-required/\r\n\n", "before_files": [{"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport json\n\nimport wtforms\n\nimport warehouse.utils.otp as otp\nimport warehouse.utils.webauthn as webauthn\n\nfrom warehouse import forms\nfrom warehouse.accounts.forms import (\n    NewEmailMixin,\n    NewPasswordMixin,\n    PasswordMixin,\n    TOTPValueMixin,\n    WebAuthnCredentialMixin,\n)\n\n\nclass RoleNameMixin:\n\n    role_name = wtforms.SelectField(\n        \"Select role\",\n        choices=[(\"Maintainer\", \"Maintainer\"), (\"Owner\", \"Owner\")],\n        validators=[wtforms.validators.DataRequired(message=\"Select role\")],\n    )\n\n\nclass UsernameMixin:\n\n    username = wtforms.StringField(\n        validators=[wtforms.validators.DataRequired(message=\"Specify username\")]\n    )\n\n    def validate_username(self, field):\n        userid = self.user_service.find_userid(field.data)\n\n        if userid is None:\n            raise wtforms.validators.ValidationError(\n                \"No user found with that username. Try again.\"\n            )\n\n\nclass CreateRoleForm(RoleNameMixin, UsernameMixin, forms.Form):\n    def __init__(self, *args, user_service, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.user_service = user_service\n\n\nclass ChangeRoleForm(RoleNameMixin, forms.Form):\n    pass\n\n\nclass SaveAccountForm(forms.Form):\n\n    __params__ = [\"name\"]\n\n    name = wtforms.StringField()\n\n\nclass AddEmailForm(NewEmailMixin, forms.Form):\n\n    __params__ = [\"email\"]\n\n    def __init__(self, *args, user_service, user_id, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.user_service = user_service\n        self.user_id = user_id\n\n\nclass ChangePasswordForm(PasswordMixin, NewPasswordMixin, forms.Form):\n\n    __params__ = [\"password\", \"new_password\", \"password_confirm\"]\n\n    def __init__(self, *args, user_service, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.user_service = user_service\n\n\nclass DeleteTOTPForm(UsernameMixin, forms.Form):\n\n    __params__ = [\"confirm_username\"]\n\n    def __init__(self, *args, user_service, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.user_service = user_service\n\n\nclass ProvisionTOTPForm(TOTPValueMixin, forms.Form):\n\n    __params__ = [\"totp_value\"]\n\n    def __init__(self, *args, totp_secret, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.totp_secret = totp_secret\n\n    def validate_totp_value(self, field):\n        totp_value = field.data.encode(\"utf8\")\n        if not otp.verify_totp(self.totp_secret, totp_value):\n            raise wtforms.validators.ValidationError(\"Invalid TOTP code. Try again?\")\n\n\nclass DeleteWebAuthnForm(forms.Form):\n    __params__ = [\"confirm_device_name\"]\n\n    label = wtforms.StringField(\n        validators=[\n            wtforms.validators.DataRequired(message=\"Specify a device name\"),\n            wtforms.validators.Length(\n                max=64, message=(\"Label must be 64 characters or less\")\n            ),\n        ]\n    )\n\n    def __init__(self, *args, user_service, user_id, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.user_service = user_service\n        self.user_id = user_id\n\n    def validate_label(self, field):\n        label = field.data\n\n        webauthn = self.user_service.get_webauthn_by_label(self.user_id, label)\n        if webauthn is None:\n            raise wtforms.validators.ValidationError(\"No WebAuthn key with given label\")\n        self.webauthn = webauthn\n\n\nclass ProvisionWebAuthnForm(WebAuthnCredentialMixin, forms.Form):\n    __params__ = [\"label\", \"credential\"]\n\n    label = wtforms.StringField(\n        validators=[\n            wtforms.validators.DataRequired(message=\"Specify a label\"),\n            wtforms.validators.Length(\n                max=64, message=(\"Label must be 64 characters or less\")\n            ),\n        ]\n    )\n\n    def __init__(\n        self, *args, user_service, user_id, challenge, rp_id, origin, **kwargs\n    ):\n        super().__init__(*args, **kwargs)\n        self.user_service = user_service\n        self.user_id = user_id\n        self.challenge = challenge\n        self.rp_id = rp_id\n        self.origin = origin\n\n    def validate_credential(self, field):\n        try:\n            credential_dict = json.loads(field.data.encode(\"utf8\"))\n        except json.JSONDecodeError:\n            raise wtforms.validators.ValidationError(\n                \"Invalid WebAuthn credential: Bad payload\"\n            )\n\n        try:\n            validated_credential = self.user_service.verify_webauthn_credential(\n                credential_dict,\n                challenge=self.challenge,\n                rp_id=self.rp_id,\n                origin=self.origin,\n            )\n        except webauthn.RegistrationRejectedException as e:\n            raise wtforms.validators.ValidationError(str(e))\n\n        self.validated_credential = validated_credential\n\n    def validate_label(self, field):\n        label = field.data\n\n        if self.user_service.get_webauthn_by_label(self.user_id, label) is not None:\n            raise wtforms.validators.ValidationError(f\"Label '{label}' already in use\")\n\n\nclass CreateMacaroonForm(forms.Form):\n    __params__ = [\"description\", \"token_scope\"]\n\n    def __init__(self, *args, user_id, macaroon_service, project_names, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.user_id = user_id\n        self.macaroon_service = macaroon_service\n        self.project_names = project_names\n\n    description = wtforms.StringField(\n        validators=[\n            wtforms.validators.DataRequired(message=\"Specify a token name\"),\n            wtforms.validators.Length(\n                max=100, message=\"Description must be 100 characters or less\"\n            ),\n        ]\n    )\n\n    token_scope = wtforms.StringField(\n        validators=[wtforms.validators.DataRequired(message=\"Specify the token scope\")]\n    )\n\n    def validate_description(self, field):\n        description = field.data\n\n        if (\n            self.macaroon_service.get_macaroon_by_description(self.user_id, description)\n            is not None\n        ):\n            raise wtforms.validators.ValidationError(\"API token name already in use\")\n\n    def validate_token_scope(self, field):\n        scope = field.data\n\n        try:\n            _, scope_kind = scope.split(\":\", 1)\n        except ValueError:\n            raise wtforms.ValidationError(f\"Unknown token scope: {scope}\")\n\n        if scope_kind == \"unspecified\":\n            raise wtforms.ValidationError(f\"Specify the token scope\")\n\n        if scope_kind == \"user\":\n            self.validated_scope = scope_kind\n            return\n\n        try:\n            scope_kind, scope_value = scope_kind.split(\":\", 1)\n        except ValueError:\n            raise wtforms.ValidationError(f\"Unknown token scope: {scope}\")\n\n        if scope_kind != \"project\":\n            raise wtforms.ValidationError(f\"Unknown token scope: {scope}\")\n        if scope_value not in self.project_names:\n            raise wtforms.ValidationError(\n                f\"Unknown or invalid project name: {scope_value}\"\n            )\n\n        self.validated_scope = {\"projects\": [scope_value]}\n\n\nclass DeleteMacaroonForm(forms.Form):\n    __params__ = [\"macaroon_id\"]\n\n    macaroon_id = wtforms.StringField(\n        validators=[wtforms.validators.DataRequired(message=\"Identifier required\")]\n    )\n\n    def __init__(self, *args, macaroon_service, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.macaroon_service = macaroon_service\n\n    def validate_macaroon_id(self, field):\n        macaroon_id = field.data\n        if self.macaroon_service.find_macaroon(macaroon_id) is None:\n            raise wtforms.validators.ValidationError(\"No such macaroon\")\n", "path": "warehouse/manage/forms.py"}], "after_files": [{"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport json\n\nimport wtforms\n\nimport warehouse.utils.otp as otp\nimport warehouse.utils.webauthn as webauthn\n\nfrom warehouse import forms\nfrom warehouse.accounts.forms import (\n    NewEmailMixin,\n    NewPasswordMixin,\n    PasswordMixin,\n    TOTPValueMixin,\n    WebAuthnCredentialMixin,\n)\n\n\nclass RoleNameMixin:\n\n    role_name = wtforms.SelectField(\n        \"Select role\",\n        choices=[(\"\", \"Select role\"), (\"Maintainer\", \"Maintainer\"), (\"Owner\", \"Owner\")],\n        validators=[wtforms.validators.DataRequired(message=\"Select role\")],\n    )\n\n\nclass UsernameMixin:\n\n    username = wtforms.StringField(\n        validators=[wtforms.validators.DataRequired(message=\"Specify username\")]\n    )\n\n    def validate_username(self, field):\n        userid = self.user_service.find_userid(field.data)\n\n        if userid is None:\n            raise wtforms.validators.ValidationError(\n                \"No user found with that username. Try again.\"\n            )\n\n\nclass CreateRoleForm(RoleNameMixin, UsernameMixin, forms.Form):\n    def __init__(self, *args, user_service, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.user_service = user_service\n\n\nclass ChangeRoleForm(RoleNameMixin, forms.Form):\n    pass\n\n\nclass SaveAccountForm(forms.Form):\n\n    __params__ = [\"name\"]\n\n    name = wtforms.StringField()\n\n\nclass AddEmailForm(NewEmailMixin, forms.Form):\n\n    __params__ = [\"email\"]\n\n    def __init__(self, *args, user_service, user_id, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.user_service = user_service\n        self.user_id = user_id\n\n\nclass ChangePasswordForm(PasswordMixin, NewPasswordMixin, forms.Form):\n\n    __params__ = [\"password\", \"new_password\", \"password_confirm\"]\n\n    def __init__(self, *args, user_service, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.user_service = user_service\n\n\nclass DeleteTOTPForm(UsernameMixin, forms.Form):\n\n    __params__ = [\"confirm_username\"]\n\n    def __init__(self, *args, user_service, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.user_service = user_service\n\n\nclass ProvisionTOTPForm(TOTPValueMixin, forms.Form):\n\n    __params__ = [\"totp_value\"]\n\n    def __init__(self, *args, totp_secret, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.totp_secret = totp_secret\n\n    def validate_totp_value(self, field):\n        totp_value = field.data.encode(\"utf8\")\n        if not otp.verify_totp(self.totp_secret, totp_value):\n            raise wtforms.validators.ValidationError(\"Invalid TOTP code. Try again?\")\n\n\nclass DeleteWebAuthnForm(forms.Form):\n    __params__ = [\"confirm_device_name\"]\n\n    label = wtforms.StringField(\n        validators=[\n            wtforms.validators.DataRequired(message=\"Specify a device name\"),\n            wtforms.validators.Length(\n                max=64, message=(\"Label must be 64 characters or less\")\n            ),\n        ]\n    )\n\n    def __init__(self, *args, user_service, user_id, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.user_service = user_service\n        self.user_id = user_id\n\n    def validate_label(self, field):\n        label = field.data\n\n        webauthn = self.user_service.get_webauthn_by_label(self.user_id, label)\n        if webauthn is None:\n            raise wtforms.validators.ValidationError(\"No WebAuthn key with given label\")\n        self.webauthn = webauthn\n\n\nclass ProvisionWebAuthnForm(WebAuthnCredentialMixin, forms.Form):\n    __params__ = [\"label\", \"credential\"]\n\n    label = wtforms.StringField(\n        validators=[\n            wtforms.validators.DataRequired(message=\"Specify a label\"),\n            wtforms.validators.Length(\n                max=64, message=(\"Label must be 64 characters or less\")\n            ),\n        ]\n    )\n\n    def __init__(\n        self, *args, user_service, user_id, challenge, rp_id, origin, **kwargs\n    ):\n        super().__init__(*args, **kwargs)\n        self.user_service = user_service\n        self.user_id = user_id\n        self.challenge = challenge\n        self.rp_id = rp_id\n        self.origin = origin\n\n    def validate_credential(self, field):\n        try:\n            credential_dict = json.loads(field.data.encode(\"utf8\"))\n        except json.JSONDecodeError:\n            raise wtforms.validators.ValidationError(\n                \"Invalid WebAuthn credential: Bad payload\"\n            )\n\n        try:\n            validated_credential = self.user_service.verify_webauthn_credential(\n                credential_dict,\n                challenge=self.challenge,\n                rp_id=self.rp_id,\n                origin=self.origin,\n            )\n        except webauthn.RegistrationRejectedException as e:\n            raise wtforms.validators.ValidationError(str(e))\n\n        self.validated_credential = validated_credential\n\n    def validate_label(self, field):\n        label = field.data\n\n        if self.user_service.get_webauthn_by_label(self.user_id, label) is not None:\n            raise wtforms.validators.ValidationError(f\"Label '{label}' already in use\")\n\n\nclass CreateMacaroonForm(forms.Form):\n    __params__ = [\"description\", \"token_scope\"]\n\n    def __init__(self, *args, user_id, macaroon_service, project_names, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.user_id = user_id\n        self.macaroon_service = macaroon_service\n        self.project_names = project_names\n\n    description = wtforms.StringField(\n        validators=[\n            wtforms.validators.DataRequired(message=\"Specify a token name\"),\n            wtforms.validators.Length(\n                max=100, message=\"Description must be 100 characters or less\"\n            ),\n        ]\n    )\n\n    token_scope = wtforms.StringField(\n        validators=[wtforms.validators.DataRequired(message=\"Specify the token scope\")]\n    )\n\n    def validate_description(self, field):\n        description = field.data\n\n        if (\n            self.macaroon_service.get_macaroon_by_description(self.user_id, description)\n            is not None\n        ):\n            raise wtforms.validators.ValidationError(\"API token name already in use\")\n\n    def validate_token_scope(self, field):\n        scope = field.data\n\n        try:\n            _, scope_kind = scope.split(\":\", 1)\n        except ValueError:\n            raise wtforms.ValidationError(f\"Unknown token scope: {scope}\")\n\n        if scope_kind == \"unspecified\":\n            raise wtforms.ValidationError(f\"Specify the token scope\")\n\n        if scope_kind == \"user\":\n            self.validated_scope = scope_kind\n            return\n\n        try:\n            scope_kind, scope_value = scope_kind.split(\":\", 1)\n        except ValueError:\n            raise wtforms.ValidationError(f\"Unknown token scope: {scope}\")\n\n        if scope_kind != \"project\":\n            raise wtforms.ValidationError(f\"Unknown token scope: {scope}\")\n        if scope_value not in self.project_names:\n            raise wtforms.ValidationError(\n                f\"Unknown or invalid project name: {scope_value}\"\n            )\n\n        self.validated_scope = {\"projects\": [scope_value]}\n\n\nclass DeleteMacaroonForm(forms.Form):\n    __params__ = [\"macaroon_id\"]\n\n    macaroon_id = wtforms.StringField(\n        validators=[wtforms.validators.DataRequired(message=\"Identifier required\")]\n    )\n\n    def __init__(self, *args, macaroon_service, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.macaroon_service = macaroon_service\n\n    def validate_macaroon_id(self, field):\n        macaroon_id = field.data\n        if self.macaroon_service.find_macaroon(macaroon_id) is None:\n            raise wtforms.validators.ValidationError(\"No such macaroon\")\n", "path": "warehouse/manage/forms.py"}]}