29
29
# the beginning or end of a *dot-atom component* of a hostname either.
30
30
ATEXT_HOSTNAME = r'(?:(?:[a-zA-Z0-9][a-zA-Z0-9\-]*)?[a-zA-Z0-9])'
31
31
32
+ # Length constants
33
+ # RFC 3696 + errata 1003 + errata 1690 (https://www.rfc-editor.org/errata_search.php?rfc=3696&eid=1690)
34
+ # explains the maximum length of an email address is 254 octets.
35
+ EMAIL_MAX_LENGTH = 254
36
+ LOCAL_PART_MAX_LENGTH = 64
37
+ DOMAIN_MAX_LENGTH = 255
38
+
32
39
# ease compatibility in type checking
33
40
if sys .version_info >= (3 ,):
34
41
unicode_class = str
@@ -161,6 +168,14 @@ def as_dict(self):
161
168
return self .__dict__
162
169
163
170
171
+ def __get_length_reason (addr , utf8 = False , limit = EMAIL_MAX_LENGTH ):
172
+ diff = len (addr ) - limit
173
+ reason = "({}{} character{} too many)"
174
+ prefix = "at least " if utf8 else ""
175
+ suffix = "s" if diff > 1 else ""
176
+ return reason .format (prefix , diff , suffix )
177
+
178
+
164
179
def validate_email (
165
180
email ,
166
181
allow_smtputf8 = True ,
@@ -212,9 +227,6 @@ def validate_email(
212
227
if not ret .smtputf8 :
213
228
ret .ascii_email = ret .ascii_local_part + "@" + ret .ascii_domain
214
229
215
- # RFC 3696 + errata 1003 + errata 1690 (https://www.rfc-editor.org/errata_search.php?rfc=3696&eid=1690)
216
- # explains the maximum length of an email address is 254 octets.
217
- #
218
230
# If the email address has an ASCII representation, then we assume it may be
219
231
# transmitted in ASCII (we can't assume SMTPUTF8 will be used on all hops to
220
232
# the destination) and the length limit applies to ASCII characters (which is
@@ -235,33 +247,24 @@ def validate_email(
235
247
# longer than the number of characters.
236
248
#
237
249
# See the length checks on the local part and the domain.
238
- if ret .ascii_email and len (ret .ascii_email ) > 254 :
250
+ if ret .ascii_email and len (ret .ascii_email ) > EMAIL_MAX_LENGTH :
239
251
if ret .ascii_email == ret .email :
240
- reason = " ({} character{} too many)" .format (
241
- len (ret .ascii_email ) - 254 ,
242
- "s" if (len (ret .ascii_email ) - 254 != 1 ) else ""
243
- )
244
- elif len (ret .email ) > 254 :
252
+ reason = __get_length_reason (ret .ascii_email )
253
+ elif len (ret .email ) > EMAIL_MAX_LENGTH :
245
254
# If there are more than 254 characters, then the ASCII
246
255
# form is definitely going to be too long.
247
- reason = " (at least {} character{} too many)" .format (
248
- len (ret .email ) - 254 ,
249
- "s" if (len (ret .email ) - 254 != 1 ) else ""
250
- )
256
+ reason = __get_length_reason (ret .email , utf8 = True )
251
257
else :
252
- reason = " (when converted to IDNA ASCII)"
253
- raise EmailSyntaxError ("The email address is too long{}." .format (reason ))
254
- if len (ret .email .encode ("utf8" )) > 254 :
255
- if len (ret .email ) > 254 :
258
+ reason = "(when converted to IDNA ASCII)"
259
+ raise EmailSyntaxError ("The email address is too long {}." .format (reason ))
260
+ if len (ret .email .encode ("utf8" )) > EMAIL_MAX_LENGTH :
261
+ if len (ret .email ) > EMAIL_MAX_LENGTH :
256
262
# If there are more than 254 characters, then the UTF-8
257
263
# encoding is definitely going to be too long.
258
- reason = " (at least {} character{} too many)" .format (
259
- len (ret .email ) - 254 ,
260
- "s" if (len (ret .email ) - 254 != 1 ) else ""
261
- )
264
+ reason = __get_length_reason (ret .email , utf8 = True )
262
265
else :
263
- reason = " (when encoded in bytes)"
264
- raise EmailSyntaxError ("The email address is too long{}." .format (reason ))
266
+ reason = "(when encoded in bytes)"
267
+ raise EmailSyntaxError ("The email address is too long {}." .format (reason ))
265
268
266
269
if check_deliverability :
267
270
# Validate the email address's deliverability and update the
@@ -295,11 +298,9 @@ def validate_email_local_part(local, allow_smtputf8=True, allow_empty_local=Fals
295
298
# internationalized, then the UTF-8 encoding may be longer, but
296
299
# that may not be relevant. We will check the total address length
297
300
# instead.
298
- if len (local ) > 64 :
299
- raise EmailSyntaxError ("The email address is too long before the @-sign ({} character{} too many)." .format (
300
- len (local ) - 64 ,
301
- "s" if (len (local ) - 64 != 1 ) else ""
302
- ))
301
+ if len (local ) > LOCAL_PART_MAX_LENGTH :
302
+ reason = __get_length_reason (local , limit = LOCAL_PART_MAX_LENGTH )
303
+ raise EmailSyntaxError ("The email address is too long before the @-sign {}." .format (reason ))
303
304
304
305
# Check the local part against the regular expression for the older ASCII requirements.
305
306
m = re .match (DOT_ATOM_TEXT + "\\ Z" , local )
@@ -404,7 +405,7 @@ def validate_email_domain_part(domain):
404
405
# on the assumption that the domain may be transmitted without SMTPUTF8
405
406
# as IDNA ASCII. This is also checked by idna.encode, so this exception
406
407
# is never reached.
407
- if len (ascii_domain ) > 255 :
408
+ if len (ascii_domain ) > DOMAIN_MAX_LENGTH :
408
409
raise EmailSyntaxError ("The email address is too long after the @-sign." )
409
410
410
411
# A "dot atom text", per RFC 2822 3.2.4, but using the restricted
0 commit comments