Loading django/core/validators.py +42 −2 Original line number Diff line number Diff line import re import urlparse from django.core.exceptions import ValidationError from django.utils.translation import ugettext_lazy as _ Loading Loading @@ -52,7 +53,29 @@ class URLValidator(RegexValidator): self.user_agent = validator_user_agent def __call__(self, value): try: super(URLValidator, self).__call__(value) except ValidationError, e: # Trivial case failed. Try for possible IDN domain if value: original = value value = smart_unicode(value) splitted = urlparse.urlsplit(value) try: netloc_ace = splitted[1].encode('idna') # IDN -> ACE except UnicodeError: # invalid domain part raise e value = value.replace(splitted[1], netloc_ace) # If no URL path given, assume / if not splitted[2]: value += u'/' super(URLValidator, self).__call__(value) # After validation revert ACE encoded domain-part to # original (IDN) value as suggested by RFC 3490 value = original else: raise if self.verify_exists: import urllib2 headers = { Loading @@ -77,12 +100,29 @@ def validate_integer(value): except (ValueError, TypeError), e: raise ValidationError('') class EmailValidator(RegexValidator): def __call__(self, value): try: super(EmailValidator, self).__call__(value) except ValidationError, e: # Trivial case failed. Try for possible IDN domain-part if value and u'@' in value: parts = value.split(u'@') domain_part = parts[-1] try: parts[-1] = parts[-1].encode('idna') except UnicodeError: raise e super(EmailValidator, self).__call__(u'@'.join(parts)) else: raise email_re = re.compile( r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string r')@(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?$', re.IGNORECASE) # domain validate_email = RegexValidator(email_re, _(u'Enter a valid e-mail address.'), 'invalid') validate_email = EmailValidator(email_re, _(u'Enter a valid e-mail address.'), 'invalid') slug_re = re.compile(r'^[-\w]+$') validate_slug = RegexValidator(slug_re, _(u"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."), 'invalid') Loading docs/ref/forms/fields.txt +11 −0 Original line number Diff line number Diff line Loading @@ -482,6 +482,11 @@ Has two optional arguments for validation, ``max_length`` and ``min_length``. If provided, these arguments ensure that the string is at most or at least the given length. .. versionchanged:: 1.2 The EmailField previously did not recognize e-mail addresses as valid that contained an IDN (Internationalized Domain Name; a domain containing unicode characters) domain part. This has now been corrected. ``FileField`` ~~~~~~~~~~~~~ Loading Loading @@ -707,6 +712,12 @@ Takes the following optional arguments: String used as the user-agent used when checking for a URL's existence. Defaults to the value of the ``URL_VALIDATOR_USER_AGENT`` setting. .. versionchanged:: 1.2 The URLField previously did not recognize URLs as valid that contained an IDN (Internationalized Domain Name; a domain name containing unicode characters) domain name. This has now been corrected. Slightly complex built-in ``Field`` classes ------------------------------------------- Loading tests/regressiontests/forms/fields.py +3 −0 Original line number Diff line number Diff line Loading @@ -408,6 +408,8 @@ class FieldsTests(TestCase): self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid e-mail address.']", f.clean, 'example@inv-.-alid.com') self.assertEqual(u'example@valid-----hyphens.com', f.clean('example@valid-----hyphens.com')) self.assertEqual(u'example@valid-with-hyphens.com', f.clean('example@valid-with-hyphens.com')) self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid e-mail address.']", f.clean, 'example@.com') self.assertEqual(u'local@domain.with.idn.xyz\xe4\xf6\xfc\xdfabc.part.com', f.clean('local@domain.with.idn.xyzäöüßabc.part.com')) def test_email_regexp_for_performance(self): f = EmailField() Loading Loading @@ -489,6 +491,7 @@ class FieldsTests(TestCase): self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid URL.']", f.clean, 'http://inv-.alid-.com') self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid URL.']", f.clean, 'http://inv-.-alid.com') self.assertEqual(u'http://valid-----hyphens.com/', f.clean('http://valid-----hyphens.com')) self.assertEqual(u'http://some.idn.xyz\xe4\xf6\xfc\xdfabc.domain.com:123/blah', f.clean('http://some.idn.xyzäöüßabc.domain.com:123/blah')) def test_url_regex_ticket11198(self): f = URLField() Loading Loading
django/core/validators.py +42 −2 Original line number Diff line number Diff line import re import urlparse from django.core.exceptions import ValidationError from django.utils.translation import ugettext_lazy as _ Loading Loading @@ -52,7 +53,29 @@ class URLValidator(RegexValidator): self.user_agent = validator_user_agent def __call__(self, value): try: super(URLValidator, self).__call__(value) except ValidationError, e: # Trivial case failed. Try for possible IDN domain if value: original = value value = smart_unicode(value) splitted = urlparse.urlsplit(value) try: netloc_ace = splitted[1].encode('idna') # IDN -> ACE except UnicodeError: # invalid domain part raise e value = value.replace(splitted[1], netloc_ace) # If no URL path given, assume / if not splitted[2]: value += u'/' super(URLValidator, self).__call__(value) # After validation revert ACE encoded domain-part to # original (IDN) value as suggested by RFC 3490 value = original else: raise if self.verify_exists: import urllib2 headers = { Loading @@ -77,12 +100,29 @@ def validate_integer(value): except (ValueError, TypeError), e: raise ValidationError('') class EmailValidator(RegexValidator): def __call__(self, value): try: super(EmailValidator, self).__call__(value) except ValidationError, e: # Trivial case failed. Try for possible IDN domain-part if value and u'@' in value: parts = value.split(u'@') domain_part = parts[-1] try: parts[-1] = parts[-1].encode('idna') except UnicodeError: raise e super(EmailValidator, self).__call__(u'@'.join(parts)) else: raise email_re = re.compile( r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string r')@(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?$', re.IGNORECASE) # domain validate_email = RegexValidator(email_re, _(u'Enter a valid e-mail address.'), 'invalid') validate_email = EmailValidator(email_re, _(u'Enter a valid e-mail address.'), 'invalid') slug_re = re.compile(r'^[-\w]+$') validate_slug = RegexValidator(slug_re, _(u"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."), 'invalid') Loading
docs/ref/forms/fields.txt +11 −0 Original line number Diff line number Diff line Loading @@ -482,6 +482,11 @@ Has two optional arguments for validation, ``max_length`` and ``min_length``. If provided, these arguments ensure that the string is at most or at least the given length. .. versionchanged:: 1.2 The EmailField previously did not recognize e-mail addresses as valid that contained an IDN (Internationalized Domain Name; a domain containing unicode characters) domain part. This has now been corrected. ``FileField`` ~~~~~~~~~~~~~ Loading Loading @@ -707,6 +712,12 @@ Takes the following optional arguments: String used as the user-agent used when checking for a URL's existence. Defaults to the value of the ``URL_VALIDATOR_USER_AGENT`` setting. .. versionchanged:: 1.2 The URLField previously did not recognize URLs as valid that contained an IDN (Internationalized Domain Name; a domain name containing unicode characters) domain name. This has now been corrected. Slightly complex built-in ``Field`` classes ------------------------------------------- Loading
tests/regressiontests/forms/fields.py +3 −0 Original line number Diff line number Diff line Loading @@ -408,6 +408,8 @@ class FieldsTests(TestCase): self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid e-mail address.']", f.clean, 'example@inv-.-alid.com') self.assertEqual(u'example@valid-----hyphens.com', f.clean('example@valid-----hyphens.com')) self.assertEqual(u'example@valid-with-hyphens.com', f.clean('example@valid-with-hyphens.com')) self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid e-mail address.']", f.clean, 'example@.com') self.assertEqual(u'local@domain.with.idn.xyz\xe4\xf6\xfc\xdfabc.part.com', f.clean('local@domain.with.idn.xyzäöüßabc.part.com')) def test_email_regexp_for_performance(self): f = EmailField() Loading Loading @@ -489,6 +491,7 @@ class FieldsTests(TestCase): self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid URL.']", f.clean, 'http://inv-.alid-.com') self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid URL.']", f.clean, 'http://inv-.-alid.com') self.assertEqual(u'http://valid-----hyphens.com/', f.clean('http://valid-----hyphens.com')) self.assertEqual(u'http://some.idn.xyz\xe4\xf6\xfc\xdfabc.domain.com:123/blah', f.clean('http://some.idn.xyzäöüßabc.domain.com:123/blah')) def test_url_regex_ticket11198(self): f = URLField() Loading