prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Validate username format before attempting to flush it to the db
  Validate a valid username prior to trying to save it to the database, using the same logic as is already in the database.
  
  ---
  
  https://sentry.io/python-software-foundation/warehouse-production/issues/258353802/
  
  ```
  IntegrityError: new row for relation "accounts_user" violates check constraint "accounts_user_valid_username"
  
    File "sqlalchemy/engine/base.py", line 1182, in _execute_context
      context)
    File "sqlalchemy/engine/default.py", line 470, in do_execute
      cursor.execute(statement, parameters)
  
  IntegrityError: (psycopg2.IntegrityError) new row for relation "accounts_user" violates check constraint "accounts_user_valid_username"
  (30 additional frame(s) were not displayed)
  ...
    File "warehouse/raven.py", line 41, in raven_tween
      return handler(request)
    File "warehouse/cache/http.py", line 69, in conditional_http_tween
      response = handler(request)
    File "warehouse/cache/http.py", line 33, in wrapped
      return view(context, request)
    File "warehouse/accounts/views.py", line 226, in register
      form.email.data
    File "warehouse/accounts/services.py", line 152, in create_user
      self.db.flush()
  
  IntegrityError: (psycopg2.IntegrityError) new row for relation "accounts_user" violates check constraint "accounts_user_valid_username"
  DETAIL:  Failing row contains 
  ```
  
  --- 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/accounts/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 disposable_email_domains
  14 import wtforms
  15 import wtforms.fields.html5
  16 
  17 from warehouse import forms, recaptcha
  18 from warehouse.accounts.interfaces import TooManyFailedLogins
  19 
  20 
  21 class CredentialsMixin:
  22     username = wtforms.StringField(
  23         validators=[
  24             wtforms.validators.DataRequired(),
  25             wtforms.validators.Length(
  26                 max=50,
  27                 message=(
  28                     "The username you have chosen is too long. Please choose "
  29                     "a username with under 50 characters."
  30                 )
  31             ),
  32         ],
  33     )
  34 
  35     password = wtforms.PasswordField(
  36         validators=[
  37             wtforms.validators.DataRequired(),
  38         ],
  39     )
  40 
  41     def __init__(self, *args, user_service, **kwargs):
  42         super().__init__(*args, **kwargs)
  43         self.user_service = user_service
  44 
  45 
  46 class RegistrationForm(CredentialsMixin, forms.Form):
  47     password = wtforms.PasswordField(
  48         validators=[
  49             wtforms.validators.DataRequired(),
  50             forms.PasswordStrengthValidator(
  51                 user_input_fields=["full_name", "username", "email"],
  52             ),
  53         ],
  54     )
  55     password_confirm = wtforms.PasswordField(
  56         validators=[
  57             wtforms.validators.DataRequired(),
  58             wtforms.validators.EqualTo(
  59                 "password", "Your passwords do not match. Please try again."
  60             ),
  61         ],
  62     )
  63 
  64     full_name = wtforms.StringField()
  65 
  66     email = wtforms.fields.html5.EmailField(
  67         validators=[
  68             wtforms.validators.DataRequired(),
  69             wtforms.validators.Email(
  70                 message=(
  71                     "The email address you have chosen is not a valid "
  72                     "format. Please try again."
  73                 )
  74             ),
  75         ],
  76     )
  77 
  78     g_recaptcha_response = wtforms.StringField()
  79 
  80     def __init__(self, *args, recaptcha_service, **kwargs):
  81         super().__init__(*args, **kwargs)
  82         self.recaptcha_service = recaptcha_service
  83 
  84     def validate_username(self, field):
  85         if self.user_service.find_userid(field.data) is not None:
  86             raise wtforms.validators.ValidationError(
  87                 "This username is already being used by another "
  88                 "account. Please choose a different username."
  89             )
  90 
  91     def validate_email(self, field):
  92         if self.user_service.find_userid_by_email(field.data) is not None:
  93             raise wtforms.validators.ValidationError(
  94                 "This email address is already being used by another account. "
  95                 "Please use a different email."
  96             )
  97         domain = field.data.split('@')[-1]
  98         if domain in disposable_email_domains.blacklist:
  99             raise wtforms.validators.ValidationError(
  100                 "Sorry, you cannot create an account with an email address "
  101                 "from this domain. Please use a different email."
  102             )
  103 
  104     def validate_g_recaptcha_response(self, field):
  105         # do required data validation here due to enabled flag being required
  106         if self.recaptcha_service.enabled and not field.data:
  107             raise wtforms.validators.ValidationError("Recaptcha error.")
  108         try:
  109             self.recaptcha_service.verify_response(field.data)
  110         except recaptcha.RecaptchaError:
  111             # TODO: log error
  112             # don't want to provide the user with any detail
  113             raise wtforms.validators.ValidationError("Recaptcha error.")
  114 
  115 
  116 class LoginForm(CredentialsMixin, forms.Form):
  117     def validate_username(self, field):
  118         userid = self.user_service.find_userid(field.data)
  119 
  120         if userid is None:
  121             raise wtforms.validators.ValidationError(
  122                 "No user found with that username. Please try again."
  123             )
  124 
  125     def validate_password(self, field):
  126         userid = self.user_service.find_userid(self.username.data)
  127         if userid is not None:
  128             try:
  129                 if not self.user_service.check_password(userid, field.data):
  130                     raise wtforms.validators.ValidationError(
  131                         "The username and password combination you have "
  132                         "provided is invalid. Please try again."
  133                     )
  134             except TooManyFailedLogins:
  135                 raise wtforms.validators.ValidationError(
  136                     "There have been too many unsuccessful login attempts, "
  137                     "please try again later."
  138                 ) from None
  139
  ```
  --- 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/warehouse/accounts/forms.py b/warehouse/accounts/forms.py
  --- a/warehouse/accounts/forms.py
  +++ b/warehouse/accounts/forms.py
  @@ -29,6 +29,18 @@
                       "a username with under 50 characters."
                   )
               ),
  +            # the regexp below must match the CheckConstraint
  +            # for the username field in accounts.models.User
  +            wtforms.validators.Regexp(
  +                r'^[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9]$',
  +                message=(
  +                    "The username is invalid. Usernames "
  +                    "must be composed of letters, numbers, "
  +                    "dots, hyphens and underscores. And must "
  +                    "also start and finish with a letter or number. "
  +                    "Please choose a different username."
  +                )
  +            )
           ],
       )
  
verification_info: {"golden_diff": "diff --git a/warehouse/accounts/forms.py b/warehouse/accounts/forms.py\n--- a/warehouse/accounts/forms.py\n+++ b/warehouse/accounts/forms.py\n@@ -29,6 +29,18 @@\n                     \"a username with under 50 characters.\"\n                 )\n             ),\n+            # the regexp below must match the CheckConstraint\n+            # for the username field in accounts.models.User\n+            wtforms.validators.Regexp(\n+                r'^[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9]$',\n+                message=(\n+                    \"The username is invalid. Usernames \"\n+                    \"must be composed of letters, numbers, \"\n+                    \"dots, hyphens and underscores. And must \"\n+                    \"also start and finish with a letter or number. \"\n+                    \"Please choose a different username.\"\n+                )\n+            )\n         ],\n     )\n", "issue": "Validate username format before attempting to flush it to the db\nValidate a valid username prior to trying to save it to the database, using the same logic as is already in the database.\n\n---\n\nhttps://sentry.io/python-software-foundation/warehouse-production/issues/258353802/\n\n```\nIntegrityError: new row for relation \"accounts_user\" violates check constraint \"accounts_user_valid_username\"\n\n  File \"sqlalchemy/engine/base.py\", line 1182, in _execute_context\n    context)\n  File \"sqlalchemy/engine/default.py\", line 470, in do_execute\n    cursor.execute(statement, parameters)\n\nIntegrityError: (psycopg2.IntegrityError) new row for relation \"accounts_user\" violates check constraint \"accounts_user_valid_username\"\n(30 additional frame(s) were not displayed)\n...\n  File \"warehouse/raven.py\", line 41, in raven_tween\n    return handler(request)\n  File \"warehouse/cache/http.py\", line 69, in conditional_http_tween\n    response = handler(request)\n  File \"warehouse/cache/http.py\", line 33, in wrapped\n    return view(context, request)\n  File \"warehouse/accounts/views.py\", line 226, in register\n    form.email.data\n  File \"warehouse/accounts/services.py\", line 152, in create_user\n    self.db.flush()\n\nIntegrityError: (psycopg2.IntegrityError) new row for relation \"accounts_user\" violates check constraint \"accounts_user_valid_username\"\nDETAIL:  Failing row contains \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 disposable_email_domains\nimport wtforms\nimport wtforms.fields.html5\n\nfrom warehouse import forms, recaptcha\nfrom warehouse.accounts.interfaces import TooManyFailedLogins\n\n\nclass CredentialsMixin:\n    username = wtforms.StringField(\n        validators=[\n            wtforms.validators.DataRequired(),\n            wtforms.validators.Length(\n                max=50,\n                message=(\n                    \"The username you have chosen is too long. Please choose \"\n                    \"a username with under 50 characters.\"\n                )\n            ),\n        ],\n    )\n\n    password = wtforms.PasswordField(\n        validators=[\n            wtforms.validators.DataRequired(),\n        ],\n    )\n\n    def __init__(self, *args, user_service, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.user_service = user_service\n\n\nclass RegistrationForm(CredentialsMixin, forms.Form):\n    password = wtforms.PasswordField(\n        validators=[\n            wtforms.validators.DataRequired(),\n            forms.PasswordStrengthValidator(\n                user_input_fields=[\"full_name\", \"username\", \"email\"],\n            ),\n        ],\n    )\n    password_confirm = wtforms.PasswordField(\n        validators=[\n            wtforms.validators.DataRequired(),\n            wtforms.validators.EqualTo(\n                \"password\", \"Your passwords do not match. Please try again.\"\n            ),\n        ],\n    )\n\n    full_name = wtforms.StringField()\n\n    email = wtforms.fields.html5.EmailField(\n        validators=[\n            wtforms.validators.DataRequired(),\n            wtforms.validators.Email(\n                message=(\n                    \"The email address you have chosen is not a valid \"\n                    \"format. Please try again.\"\n                )\n            ),\n        ],\n    )\n\n    g_recaptcha_response = wtforms.StringField()\n\n    def __init__(self, *args, recaptcha_service, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.recaptcha_service = recaptcha_service\n\n    def validate_username(self, field):\n        if self.user_service.find_userid(field.data) is not None:\n            raise wtforms.validators.ValidationError(\n                \"This username is already being used by another \"\n                \"account. Please choose a different username.\"\n            )\n\n    def validate_email(self, field):\n        if self.user_service.find_userid_by_email(field.data) is not None:\n            raise wtforms.validators.ValidationError(\n                \"This email address is already being used by another account. \"\n                \"Please use a different email.\"\n            )\n        domain = field.data.split('@')[-1]\n        if domain in disposable_email_domains.blacklist:\n            raise wtforms.validators.ValidationError(\n                \"Sorry, you cannot create an account with an email address \"\n                \"from this domain. Please use a different email.\"\n            )\n\n    def validate_g_recaptcha_response(self, field):\n        # do required data validation here due to enabled flag being required\n        if self.recaptcha_service.enabled and not field.data:\n            raise wtforms.validators.ValidationError(\"Recaptcha error.\")\n        try:\n            self.recaptcha_service.verify_response(field.data)\n        except recaptcha.RecaptchaError:\n            # TODO: log error\n            # don't want to provide the user with any detail\n            raise wtforms.validators.ValidationError(\"Recaptcha error.\")\n\n\nclass LoginForm(CredentialsMixin, forms.Form):\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. Please try again.\"\n            )\n\n    def validate_password(self, field):\n        userid = self.user_service.find_userid(self.username.data)\n        if userid is not None:\n            try:\n                if not self.user_service.check_password(userid, field.data):\n                    raise wtforms.validators.ValidationError(\n                        \"The username and password combination you have \"\n                        \"provided is invalid. Please try again.\"\n                    )\n            except TooManyFailedLogins:\n                raise wtforms.validators.ValidationError(\n                    \"There have been too many unsuccessful login attempts, \"\n                    \"please try again later.\"\n                ) from None\n", "path": "warehouse/accounts/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 disposable_email_domains\nimport wtforms\nimport wtforms.fields.html5\n\nfrom warehouse import forms, recaptcha\nfrom warehouse.accounts.interfaces import TooManyFailedLogins\n\n\nclass CredentialsMixin:\n    username = wtforms.StringField(\n        validators=[\n            wtforms.validators.DataRequired(),\n            wtforms.validators.Length(\n                max=50,\n                message=(\n                    \"The username you have chosen is too long. Please choose \"\n                    \"a username with under 50 characters.\"\n                )\n            ),\n            # the regexp below must match the CheckConstraint\n            # for the username field in accounts.models.User\n            wtforms.validators.Regexp(\n                r'^[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9]$',\n                message=(\n                    \"The username is invalid. Usernames \"\n                    \"must be composed of letters, numbers, \"\n                    \"dots, hyphens and underscores. And must \"\n                    \"also start and finish with a letter or number. \"\n                    \"Please choose a different username.\"\n                )\n            )\n        ],\n    )\n\n    password = wtforms.PasswordField(\n        validators=[\n            wtforms.validators.DataRequired(),\n        ],\n    )\n\n    def __init__(self, *args, user_service, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.user_service = user_service\n\n\nclass RegistrationForm(CredentialsMixin, forms.Form):\n    password = wtforms.PasswordField(\n        validators=[\n            wtforms.validators.DataRequired(),\n            forms.PasswordStrengthValidator(\n                user_input_fields=[\"full_name\", \"username\", \"email\"],\n            ),\n        ],\n    )\n    password_confirm = wtforms.PasswordField(\n        validators=[\n            wtforms.validators.DataRequired(),\n            wtforms.validators.EqualTo(\n                \"password\", \"Your passwords do not match. Please try again.\"\n            ),\n        ],\n    )\n\n    full_name = wtforms.StringField()\n\n    email = wtforms.fields.html5.EmailField(\n        validators=[\n            wtforms.validators.DataRequired(),\n            wtforms.validators.Email(\n                message=(\n                    \"The email address you have chosen is not a valid \"\n                    \"format. Please try again.\"\n                )\n            ),\n        ],\n    )\n\n    g_recaptcha_response = wtforms.StringField()\n\n    def __init__(self, *args, recaptcha_service, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.recaptcha_service = recaptcha_service\n\n    def validate_username(self, field):\n        if self.user_service.find_userid(field.data) is not None:\n            raise wtforms.validators.ValidationError(\n                \"This username is already being used by another \"\n                \"account. Please choose a different username.\"\n            )\n\n    def validate_email(self, field):\n        if self.user_service.find_userid_by_email(field.data) is not None:\n            raise wtforms.validators.ValidationError(\n                \"This email address is already being used by another account. \"\n                \"Please use a different email.\"\n            )\n        domain = field.data.split('@')[-1]\n        if domain in disposable_email_domains.blacklist:\n            raise wtforms.validators.ValidationError(\n                \"Sorry, you cannot create an account with an email address \"\n                \"from this domain. Please use a different email.\"\n            )\n\n    def validate_g_recaptcha_response(self, field):\n        # do required data validation here due to enabled flag being required\n        if self.recaptcha_service.enabled and not field.data:\n            raise wtforms.validators.ValidationError(\"Recaptcha error.\")\n        try:\n            self.recaptcha_service.verify_response(field.data)\n        except recaptcha.RecaptchaError:\n            # TODO: log error\n            # don't want to provide the user with any detail\n            raise wtforms.validators.ValidationError(\"Recaptcha error.\")\n\n\nclass LoginForm(CredentialsMixin, forms.Form):\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. Please try again.\"\n            )\n\n    def validate_password(self, field):\n        userid = self.user_service.find_userid(self.username.data)\n        if userid is not None:\n            try:\n                if not self.user_service.check_password(userid, field.data):\n                    raise wtforms.validators.ValidationError(\n                        \"The username and password combination you have \"\n                        \"provided is invalid. Please try again.\"\n                    )\n            except TooManyFailedLogins:\n                raise wtforms.validators.ValidationError(\n                    \"There have been too many unsuccessful login attempts, \"\n                    \"please try again later.\"\n                ) from None\n", "path": "warehouse/accounts/forms.py"}]}