Repository: pypi/warehouse

Issue:
DataError: No validator for user display name max length
https://sentry.io/organizations/python-software-foundation/issues/2847760761/?referrer=github_plugin

```
StringDataRightTruncation: value too long for type character varying(100)

  File "sqlalchemy/engine/base.py", line 1802, in _execute_context
    self.dialect.do_execute(
  File "sqlalchemy/engine/default.py", line 719, in do_execute
    cursor.execute(statement, parameters)

DataError: (raised as a result of Query-invoked autoflush; consider using a session.no_autoflush block if this flush is occurring prematurely)
(psycopg2.errors.StringDataRightTruncation) value too long for type character varying(100)

[SQL: UPDATE users SET name=%(name)s WHERE users.id = %(users_id)s]
[parameters: {'name': 'James is a marketing executive who excels in the development and execution of integrated marketing campaigns to drive brand awareness and drive customer loyalty. Links - https://www.amenify.com/...
(54 additional frame(s) were not displayed)
...
  File "sqlalchemy/engine/base.py", line 1845, in _execute_context
    self._handle_dbapi_exception(
  File "sqlalchemy/engine/base.py", line 2026, in _handle_dbapi_exception
    util.raise_(
  File "sqlalchemy/util/compat.py", line 207, in raise_
    raise exception
  File "sqlalchemy/engine/base.py", line 1802, in _execute_context
    self.dialect.do_execute(
  File "sqlalchemy/engine/default.py", line 719, in do_execute
    cursor.execute(statement, parameters)
```


Discussion:

Resolving patch:
diff --git a/warehouse/manage/forms.py b/warehouse/manage/forms.py
--- a/warehouse/manage/forms.py
+++ b/warehouse/manage/forms.py
@@ -25,6 +25,7 @@
     TOTPValueMixin,
     WebAuthnCredentialMixin,
 )
+from warehouse.i18n import localize as _
 
 
 class RoleNameMixin:
@@ -65,7 +66,17 @@ class SaveAccountForm(forms.Form):
 
     __params__ = ["name", "public_email"]
 
-    name = wtforms.StringField()
+    name = wtforms.StringField(
+        validators=[
+            wtforms.validators.Length(
+                max=100,
+                message=_(
+                    "The name is too long. "
+                    "Choose a name with 100 characters or less."
+                ),
+            )
+        ]
+    )
     public_email = wtforms.SelectField(choices=[("", "Not displayed")])
 
     def __init__(self, *args, user_service, user_id, **kwargs):


Test patch:
diff --git a/tests/unit/manage/test_forms.py b/tests/unit/manage/test_forms.py
--- a/tests/unit/manage/test_forms.py
+++ b/tests/unit/manage/test_forms.py
@@ -506,7 +506,7 @@ def test_public_email_verified(self):
         email = pretend.stub(verified=True, public=False, email="foo@example.com")
         user = pretend.stub(id=1, username=pretend.stub(), emails=[email])
         form = forms.SaveAccountForm(
-            name=pretend.stub(),
+            name="some name",
             public_email=email.email,
             user_service=pretend.stub(get_user=lambda _: user),
             user_id=user.id,
@@ -517,10 +517,26 @@ def test_public_email_unverified(self):
         email = pretend.stub(verified=False, public=False, email=pretend.stub())
         user = pretend.stub(id=1, username=pretend.stub(), emails=[email])
         form = forms.SaveAccountForm(
-            name=pretend.stub(),
+            name="some name",
             public_email=email.email,
             user_service=pretend.stub(get_user=lambda _: user),
             user_id=user.id,
         )
         assert not form.validate()
         assert "is not a verified email for" in form.public_email.errors.pop()
+
+    def test_name_too_long(self, pyramid_config):
+        email = pretend.stub(verified=True, public=False, email="foo@example.com")
+        user = pretend.stub(id=1, username=pretend.stub(), emails=[email])
+        form = forms.SaveAccountForm(
+            name="x" * 101,
+            public_email=email.email,
+            user_service=pretend.stub(get_user=lambda _: user),
+            user_id=user.id,
+        )
+
+        assert not form.validate()
+        assert (
+            str(form.name.errors.pop())
+            == "The name is too long. Choose a name with 100 characters or less."
+        )