Merge pull request #2881 from charettes/ticket-22943-compiled-regex-deconstruction
Fixed #22943 -- Correctly serialize compiled regexes.
This commit is contained in:
commit
f751109cb2
|
@ -6,6 +6,7 @@ import decimal
|
||||||
import collections
|
import collections
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import types
|
import types
|
||||||
|
|
||||||
|
@ -17,6 +18,9 @@ from django.utils.encoding import force_text
|
||||||
from django.utils.functional import Promise
|
from django.utils.functional import Promise
|
||||||
|
|
||||||
|
|
||||||
|
COMPILED_REGEX_TYPE = type(re.compile(''))
|
||||||
|
|
||||||
|
|
||||||
class SettingsReference(str):
|
class SettingsReference(str):
|
||||||
"""
|
"""
|
||||||
Special subclass of string which actually references a current settings
|
Special subclass of string which actually references a current settings
|
||||||
|
@ -344,6 +348,17 @@ class MigrationWriter(object):
|
||||||
# "()", not "(,)" because (,) is invalid Python syntax.
|
# "()", not "(,)" because (,) is invalid Python syntax.
|
||||||
format = "(%s)" if len(strings) != 1 else "(%s,)"
|
format = "(%s)" if len(strings) != 1 else "(%s,)"
|
||||||
return format % (", ".join(strings)), imports
|
return format % (", ".join(strings)), imports
|
||||||
|
# Compiled regex
|
||||||
|
elif isinstance(value, COMPILED_REGEX_TYPE):
|
||||||
|
imports = set(["import re"])
|
||||||
|
regex_pattern, pattern_imports = cls.serialize(value.pattern)
|
||||||
|
regex_flags, flag_imports = cls.serialize(value.flags)
|
||||||
|
imports.update(pattern_imports)
|
||||||
|
imports.update(flag_imports)
|
||||||
|
args = [regex_pattern]
|
||||||
|
if value.flags:
|
||||||
|
args.append(regex_flags)
|
||||||
|
return "re.compile(%s)" % ', '.join(args), imports
|
||||||
# Uh oh.
|
# Uh oh.
|
||||||
else:
|
else:
|
||||||
raise ValueError("Cannot serialize: %r\nThere are some values Django cannot serialize into migration files.\nFor more, see https://docs.djangoproject.com/en/dev/topics/migrations/#migration-serializing" % value)
|
raise ValueError("Cannot serialize: %r\nThere are some values Django cannot serialize into migration files.\nFor more, see https://docs.djangoproject.com/en/dev/topics/migrations/#migration-serializing" % value)
|
||||||
|
|
|
@ -1390,13 +1390,6 @@ Miscellaneous
|
||||||
a relation from the related object back to the content type for filtering,
|
a relation from the related object back to the content type for filtering,
|
||||||
ordering and other query operations.
|
ordering and other query operations.
|
||||||
|
|
||||||
* When a model field's :attr:`~django.db.models.Field.validators` contains
|
|
||||||
a :class:`~django.core.validators.RegexValidator`, the regular expression
|
|
||||||
must now be passed as a regular expression string. You can no longer use a
|
|
||||||
pre-compiled regular expression in this case, as it is not serializable.
|
|
||||||
The :attr:`~django.core.validators.RegexValidator.flags` attribute was added
|
|
||||||
to :class:`~django.core.validators.RegexValidator` to simplify this change.
|
|
||||||
|
|
||||||
* When running tests on PostgreSQL, the :setting:`USER` will need read access
|
* When running tests on PostgreSQL, the :setting:`USER` will need read access
|
||||||
to the built-in ``postgres`` database. This is in lieu of the previous
|
to the built-in ``postgres`` database. This is in lieu of the previous
|
||||||
behavior of connecting to the actual non-test database.
|
behavior of connecting to the actual non-test database.
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import tokenize
|
import tokenize
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
@ -103,18 +104,6 @@ class WriterTests(TestCase):
|
||||||
string, imports = MigrationWriter.serialize(safe_datetime)
|
string, imports = MigrationWriter.serialize(safe_datetime)
|
||||||
self.assertEqual(string, repr(datetime.datetime(2014, 3, 31, 16, 4, 31)))
|
self.assertEqual(string, repr(datetime.datetime(2014, 3, 31, 16, 4, 31)))
|
||||||
self.assertEqual(imports, {'import datetime'})
|
self.assertEqual(imports, {'import datetime'})
|
||||||
# Classes
|
|
||||||
validator = RegexValidator(message="hello")
|
|
||||||
string, imports = MigrationWriter.serialize(validator)
|
|
||||||
self.assertEqual(string, "django.core.validators.RegexValidator(message='hello')")
|
|
||||||
self.serialize_round_trip(validator)
|
|
||||||
validator = EmailValidator(message="hello") # Test with a subclass.
|
|
||||||
string, imports = MigrationWriter.serialize(validator)
|
|
||||||
self.assertEqual(string, "django.core.validators.EmailValidator(message='hello')")
|
|
||||||
self.serialize_round_trip(validator)
|
|
||||||
validator = deconstructible(path="custom.EmailValidator")(EmailValidator)(message="hello")
|
|
||||||
string, imports = MigrationWriter.serialize(validator)
|
|
||||||
self.assertEqual(string, "custom.EmailValidator(message='hello')")
|
|
||||||
# Django fields
|
# Django fields
|
||||||
self.assertSerializedFieldEqual(models.CharField(max_length=255))
|
self.assertSerializedFieldEqual(models.CharField(max_length=255))
|
||||||
self.assertSerializedFieldEqual(models.TextField(null=True, blank=True))
|
self.assertSerializedFieldEqual(models.TextField(null=True, blank=True))
|
||||||
|
@ -135,6 +124,51 @@ class WriterTests(TestCase):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_serialize_compiled_regex(self):
|
||||||
|
"""
|
||||||
|
Make sure compiled regex can be serialized.
|
||||||
|
"""
|
||||||
|
regex = re.compile(r'^\w+$', re.U)
|
||||||
|
self.assertSerializedEqual(regex)
|
||||||
|
|
||||||
|
def test_serialize_class_based_validators(self):
|
||||||
|
"""
|
||||||
|
Ticket #22943: Test serialization of class-based validators, including
|
||||||
|
compiled regexes.
|
||||||
|
"""
|
||||||
|
validator = RegexValidator(message="hello")
|
||||||
|
string = MigrationWriter.serialize(validator)[0]
|
||||||
|
self.assertEqual(string, "django.core.validators.RegexValidator(message='hello')")
|
||||||
|
self.serialize_round_trip(validator)
|
||||||
|
|
||||||
|
# Test with a compiled regex.
|
||||||
|
validator = RegexValidator(regex=re.compile(r'^\w+$', re.U))
|
||||||
|
string = MigrationWriter.serialize(validator)[0]
|
||||||
|
self.assertEqual(string, "django.core.validators.RegexValidator(regex=re.compile('^\\\\w+$', 32))")
|
||||||
|
self.serialize_round_trip(validator)
|
||||||
|
|
||||||
|
# Test a string regex with flag
|
||||||
|
validator = RegexValidator(r'^[0-9]+$', flags=re.U)
|
||||||
|
string = MigrationWriter.serialize(validator)[0]
|
||||||
|
self.assertEqual(string, "django.core.validators.RegexValidator('^[0-9]+$', flags=32)")
|
||||||
|
self.serialize_round_trip(validator)
|
||||||
|
|
||||||
|
# Test message and code
|
||||||
|
validator = RegexValidator('^[-a-zA-Z0-9_]+$', 'Invalid', 'invalid')
|
||||||
|
string = MigrationWriter.serialize(validator)[0]
|
||||||
|
self.assertEqual(string, "django.core.validators.RegexValidator('^[-a-zA-Z0-9_]+$', 'Invalid', 'invalid')")
|
||||||
|
self.serialize_round_trip(validator)
|
||||||
|
|
||||||
|
# Test with a subclass.
|
||||||
|
validator = EmailValidator(message="hello")
|
||||||
|
string = MigrationWriter.serialize(validator)[0]
|
||||||
|
self.assertEqual(string, "django.core.validators.EmailValidator(message='hello')")
|
||||||
|
self.serialize_round_trip(validator)
|
||||||
|
|
||||||
|
validator = deconstructible(path="custom.EmailValidator")(EmailValidator)(message="hello")
|
||||||
|
string = MigrationWriter.serialize(validator)[0]
|
||||||
|
self.assertEqual(string, "custom.EmailValidator(message='hello')")
|
||||||
|
|
||||||
def test_serialize_empty_nonempty_tuple(self):
|
def test_serialize_empty_nonempty_tuple(self):
|
||||||
"""
|
"""
|
||||||
Ticket #22679: makemigrations generates invalid code for (an empty
|
Ticket #22679: makemigrations generates invalid code for (an empty
|
||||||
|
|
Loading…
Reference in New Issue