Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #22350 -- Consistently serialize bytes and text in migrations.

Thanks to @treyhunner and Loïc for their suggestions and review.
  • Loading branch information...
commit 72d3889db4eb3a14acb94f613edd79f0f27d26e3 1 parent b82f307
@charettes charettes authored
View
2  django/db/migrations/state.py
@@ -227,7 +227,7 @@ def render(self, apps):
body['__module__'] = "__fake__"
# Then, make a Model object
return type(
- self.name,
+ str(self.name),
bases,
body,
)
View
33 django/db/migrations/writer.py
@@ -53,8 +53,10 @@ def serialize(self):
self.feed('%s={' % arg_name)
self.indent()
for key, value in arg_value.items():
+ key_string, key_imports = MigrationWriter.serialize(key)
arg_string, arg_imports = MigrationWriter.serialize(value)
- self.feed('%s: %s,' % (repr(key), arg_string))
+ self.feed('%s: %s,' % (key_string, arg_string))
+ imports.update(key_imports)
imports.update(arg_imports)
self.unindent()
self.feed('},')
@@ -122,7 +124,7 @@ def as_string(self):
dependencies.append(" migrations.swappable_dependency(settings.%s)," % dependency[1])
imports.add("from django.conf import settings")
else:
- dependencies.append(" %s," % repr(dependency))
+ dependencies.append(" %s," % self.serialize(dependency)[0])
items["dependencies"] = "\n".join(dependencies) + "\n" if dependencies else ""
# Format imports nicely
@@ -131,7 +133,7 @@ def as_string(self):
# If there's a replaces, make a string for it
if self.migration.replaces:
- items['replaces_str'] = "\n replaces = %s\n" % repr(self.migration.replaces)
+ items['replaces_str'] = "\n replaces = %s\n" % self.serialize(self.migration.replaces)[0]
return (MIGRATION_TEMPLATE % items).encode("utf8")
@@ -185,6 +187,12 @@ def serialize(cls, value):
More advanced than repr() as it can encode things
like datetime.datetime.now.
"""
+ # FIXME: Ideally Promise would be reconstructible, but for now we
+ # use force_text on them and defer to the normal string serialization
+ # process.
+ if isinstance(value, Promise):
+ value = force_text(value)
+
# Sequences
if isinstance(value, (list, set, tuple)):
imports = set()
@@ -229,11 +237,20 @@ def serialize(cls, value):
elif isinstance(value, SettingsReference):
return "settings.%s" % value.setting_name, set(["from django.conf import settings"])
# Simple types
- elif isinstance(value, six.integer_types + (float, six.binary_type, six.text_type, bool, type(None))):
+ elif isinstance(value, six.integer_types + (float, bool, type(None))):
return repr(value), set()
- # Promise
- elif isinstance(value, Promise):
- return repr(force_text(value)), set()
+ elif isinstance(value, six.binary_type):
+ value_repr = repr(value)
+ if six.PY2:
+ # Prepend the `b` prefix since we're importing unicode_literals
+ value_repr = 'b' + value_repr
+ return value_repr, set()
+ elif isinstance(value, six.text_type):
+ value_repr = repr(value)
+ if six.PY2:
+ # Strip the `u` prefix since we're importing unicode_literals
+ value_repr = value_repr[1:]
+ return value_repr, set()
# Decimal
elif isinstance(value, decimal.Decimal):
return repr(value), set(["from decimal import Decimal"])
@@ -286,6 +303,8 @@ def serialize(cls, value):
MIGRATION_TEMPLATE = """\
# encoding: utf8
+from __future__ import unicode_literals
+
from django.db import models, migrations
%(imports)s
View
3  tests/migrations/test_migrations/0001_initial.py
@@ -1,3 +1,6 @@
+# encoding: utf8
+from __future__ import unicode_literals
+
from django.db import migrations, models
View
3  tests/migrations/test_migrations/0002_second.py
@@ -1,3 +1,6 @@
+# encoding: utf8
+from __future__ import unicode_literals
+
from django.db import migrations, models
View
3  tests/migrations/test_migrations_2/0001_initial.py
@@ -1,3 +1,6 @@
+# encoding: utf8
+from __future__ import unicode_literals
+
from django.db import migrations, models
View
3  tests/migrations/test_migrations_conflict/0001_initial.py
@@ -1,3 +1,6 @@
+# encoding: utf8
+from __future__ import unicode_literals
+
from django.db import migrations, models
View
3  tests/migrations/test_migrations_conflict/0002_conflicting_second.py
@@ -1,3 +1,6 @@
+# encoding: utf8
+from __future__ import unicode_literals
+
from django.db import migrations, models
View
3  tests/migrations/test_migrations_conflict/0002_second.py
@@ -1,3 +1,6 @@
+# encoding: utf8
+from __future__ import unicode_literals
+
from django.db import migrations, models
View
3  tests/migrations/test_migrations_squashed/0001_initial.py
@@ -1,3 +1,6 @@
+# encoding: utf8
+from __future__ import unicode_literals
+
from django.db import migrations, models
View
3  tests/migrations/test_migrations_squashed/0001_squashed_0002.py
@@ -1,3 +1,6 @@
+# encoding: utf8
+from __future__ import unicode_literals
+
from django.db import migrations, models
View
3  tests/migrations/test_migrations_squashed/0002_second.py
@@ -1,3 +1,6 @@
+# encoding: utf8
+from __future__ import unicode_literals
+
from django.db import migrations, models
View
3  tests/migrations/test_migrations_unmigdep/0001_initial.py
@@ -1,3 +1,6 @@
+# encoding: utf8
+from __future__ import unicode_literals
+
from django.db import migrations, models
View
23 tests/migrations/test_writer.py
@@ -1,9 +1,9 @@
# encoding: utf8
-
from __future__ import unicode_literals
import datetime
import os
+import tokenize
from django.core.validators import RegexValidator, EmailValidator
from django.db import models, migrations
@@ -59,7 +59,11 @@ def test_serialize(self):
self.assertSerializedEqual(1)
self.assertSerializedEqual(None)
self.assertSerializedEqual(b"foobar")
+ string, imports = MigrationWriter.serialize(b"foobar")
+ self.assertEqual(string, "b'foobar'")
self.assertSerializedEqual("föobár")
+ string, imports = MigrationWriter.serialize("foobar")
+ self.assertEqual(string, "'foobar'")
self.assertSerializedEqual({1: 2})
self.assertSerializedEqual(["a", 2, True, None])
self.assertSerializedEqual(set([2, 3, "eighty"]))
@@ -92,15 +96,15 @@ def test_serialize(self):
# Classes
validator = RegexValidator(message="hello")
string, imports = MigrationWriter.serialize(validator)
- self.assertEqual(string, "django.core.validators.RegexValidator(message=%s)" % repr("hello"))
+ 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=%s)" % repr("hello"))
+ 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=%s)" % repr("hello"))
+ self.assertEqual(string, "custom.EmailValidator(message='hello')")
# Django fields
self.assertSerializedFieldEqual(models.CharField(max_length=255))
self.assertSerializedFieldEqual(models.TextField(null=True, blank=True))
@@ -153,6 +157,17 @@ def test_simple_migration(self):
# Just make sure it runs for now, and that things look alright.
result = self.safe_exec(output)
self.assertIn("Migration", result)
+ # In order to preserve compatibility with Python 3.2 unicode literals
+ # prefix shouldn't be added to strings.
+ tokens = tokenize.generate_tokens(six.StringIO(str(output)).readline)
+ for token_type, token_source, (srow, scol), _, line in tokens:
+ if token_type == tokenize.STRING:
+ self.assertFalse(
+ token_source.startswith('u'),
+ "Unicode literal prefix found at %d:%d: %r" % (
+ srow, scol, line.strip()
+ )
+ )
def test_migration_path(self):
test_apps = [
Please sign in to comment.
Something went wrong with that request. Please try again.