Skip to content

Commit

Permalink
Use HumanHandle to validate emails in the application
Browse files Browse the repository at this point in the history
PR #3556, issue #3555
  • Loading branch information
vxgmichel committed Nov 10, 2022
1 parent 0672100 commit ad928e0
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 3 deletions.
1 change: 1 addition & 0 deletions newsfragments/3555.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix email validation in the application for some special cases
25 changes: 22 additions & 3 deletions parsec/core/gui/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,16 +113,35 @@ def validate(self, string: str, pos: int) -> tuple[QValidator.State, str, int]:
# HumanHandle raises the same ValueError if either email or label are incorrect.
# We trick it by using an email we know will be valid, so that the only ValueError
# that can be raised will be because of an incorrect label.
HumanHandle(email="[email protected]", label=string)
HumanHandle(email="[email protected]", label=string)
return QValidator.Acceptable, string, pos
except ValueError:
return QValidator.Invalid, string, pos


class EmailValidator(QRegularExpressionValidator):
# We don't use the HumanHandle to validate the email because it's way too permissive.
def __init__(self) -> None:
super().__init__(QRegularExpression(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"))
# Note: this regex is very approximative, but it's used for two reasons:
# - to get a sensible `QValidator.Intermediate` status
# - to ban weird-but-valid email addresses such as `[email protected]#` and `[email protected]`
# However, it might not be able to accurately detect invalid email addresses.
# For instance, it will falsely report `[email protected]` as a valid email.
# It will also report email with excessively long domain names as valid, although they are not.
# Ultimately, the actual validation is performed by ANDing the results of the regex
# and the `HumanHandle` constructor, which in turn uses the `email_address_parser` crate.
email_regex = r"^([a-zA-Z0-9_%+-]+\.)*([a-zA-Z0-9_%+-]+)@([a-zA-Z0-9-]+\.)+([a-zA-Z]{2,})$"
super().__init__(QRegularExpression(email_regex))

def validate(self, input: str, pos: int) -> tuple[QValidator.State, str, int]:
status, string, pos = super().validate(input, pos)
if status != QValidator.Acceptable:
return status, string, pos
try:
HumanHandle(email=input, label="Some Label")
except ValueError:
return QValidator.Invalid, string, pos
else:
return QValidator.Acceptable, string, pos


class WorkspaceNameValidator(QValidator):
Expand Down
16 changes: 16 additions & 0 deletions tests/core/gui/test_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,22 @@ def test_email_validator(qtbot, core_config):
assert not le.is_input_valid()
assert le.property("validity") == QtGui.QValidator.Invalid

le.clear()
qtbot.keyClicks(le, "example.")
qtbot.wait_until(lambda: le.text() == "example.")
assert not le.is_input_valid()
assert le.property("validity") == QtGui.QValidator.Intermediate

qtbot.keyClicks(le, "@")
qtbot.wait_until(lambda: le.text() == "example.@")
assert not le.is_input_valid()
assert le.property("validity") == QtGui.QValidator.Invalid

qtbot.keyClicks(le, "example.com")
qtbot.wait_until(lambda: le.text() == "[email protected]")
assert not le.is_input_valid()
assert le.property("validity") == QtGui.QValidator.Invalid


@pytest.mark.gui
def test_organization_validator(qtbot, core_config):
Expand Down

0 comments on commit ad928e0

Please sign in to comment.