From 1753a76267a4dda6858d117858f233c0ed662a7f Mon Sep 17 00:00:00 2001 From: Jonas Haag Date: Wed, 26 Aug 2015 09:12:05 +0200 Subject: [PATCH 1/1] Fixed #25322 -- Lazily compiled core.validators regular expressions. This speeds up import of 'django.core.validators' which can save a few hundred milliseconds when importing the module for the first time. It can be a significant speedup to the django-admin command. --- django/core/validators.py | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/django/core/validators.py b/django/core/validators.py index 89d184f..7719b40 100644 --- a/django/core/validators.py +++ b/django/core/validators.py @@ -6,6 +6,7 @@ from django.core.exceptions import ValidationError from django.utils import six from django.utils.deconstruct import deconstructible from django.utils.encoding import force_text +from django.utils.functional import SimpleLazyObject from django.utils.ipv6 import is_valid_ipv6_address from django.utils.six.moves.urllib.parse import urlsplit, urlunsplit from django.utils.translation import ugettext_lazy as _, ungettext_lazy @@ -14,6 +15,18 @@ from django.utils.translation import ugettext_lazy as _, ungettext_lazy EMPTY_VALUES = (None, '', [], (), {}) +def _lazy_re_compile(regex, flags=0): + """Lazily compile a regex with flags.""" + def _compile(): + # Compile the regex if it was not passed pre-compiled. + if isinstance(regex, six.string_types): + return re.compile(regex, flags) + else: + assert not flags, "flags must be empty if regex is passed pre-compiled" + return regex + return SimpleLazyObject(_compile) + + @deconstructible class RegexValidator(object): regex = '' @@ -36,9 +49,7 @@ class RegexValidator(object): if self.flags and not isinstance(self.regex, six.string_types): raise TypeError("If the flags are set, regex must be a regular expression string.") - # Compile the regex if it was not passed pre-compiled. - if isinstance(self.regex, six.string_types): - self.regex = re.compile(self.regex, self.flags) + self.regex = _lazy_re_compile(self.regex, self.flags) def __call__(self, value): """ @@ -77,7 +88,7 @@ class URLValidator(RegexValidator): tld_re = r'\.(?:[a-z' + ul + r']{2,}|xn--[a-z0-9]+)\.?' host_re = '(' + hostname_re + domain_re + tld_re + '|localhost)' - regex = re.compile( + regex = _lazy_re_compile( r'^(?:[a-z0-9\.\-]*)://' # scheme is validated separately r'(?:\S+(?::\S*)?@)?' # user:pass authentication r'(?:' + ipv4_re + '|' + ipv6_re + '|' + host_re + ')' @@ -126,7 +137,7 @@ class URLValidator(RegexValidator): url = value integer_validator = RegexValidator( - re.compile('^-?\d+\Z'), + _lazy_re_compile('^-?\d+\Z'), message=_('Enter a valid integer.'), code='invalid', ) @@ -140,16 +151,17 @@ def validate_integer(value): class EmailValidator(object): message = _('Enter a valid email address.') code = 'invalid' - user_regex = re.compile( + + user_regex = _lazy_re_compile( r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z" # dot-atom r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)', # quoted-string re.IGNORECASE) - domain_regex = re.compile( + domain_regex = _lazy_re_compile( # max length of the domain is 249: 254 (max email length) minus one # period, two characters for the TLD, @ sign, & one character before @. r'(?:[A-Z0-9](?:[A-Z0-9-]{0,247}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,}(?