2017-01-07 19:11:46 +08:00
|
|
|
import builtins
|
2018-02-06 00:42:47 +08:00
|
|
|
import collections.abc
|
2016-01-27 22:22:39 +08:00
|
|
|
import datetime
|
|
|
|
import decimal
|
2017-01-20 02:55:20 +08:00
|
|
|
import enum
|
2016-01-27 22:22:39 +08:00
|
|
|
import functools
|
|
|
|
import math
|
2017-01-19 05:52:25 +08:00
|
|
|
import re
|
2016-01-27 22:22:39 +08:00
|
|
|
import types
|
2016-11-06 20:53:00 +08:00
|
|
|
import uuid
|
2016-01-27 22:22:39 +08:00
|
|
|
|
2019-01-12 04:28:22 +08:00
|
|
|
from django.conf import SettingsReference
|
2016-01-27 22:22:39 +08:00
|
|
|
from django.db import models
|
|
|
|
from django.db.migrations.operations.base import Operation
|
|
|
|
from django.db.migrations.utils import COMPILED_REGEX_TYPE, RegexObject
|
|
|
|
from django.utils.functional import LazyObject, Promise
|
|
|
|
from django.utils.timezone import utc
|
|
|
|
from django.utils.version import get_docs_version
|
|
|
|
|
|
|
|
|
2017-01-19 15:39:46 +08:00
|
|
|
class BaseSerializer:
|
2016-01-27 22:22:39 +08:00
|
|
|
def __init__(self, value):
|
|
|
|
self.value = value
|
|
|
|
|
|
|
|
def serialize(self):
|
|
|
|
raise NotImplementedError('Subclasses of BaseSerializer must implement the serialize() method.')
|
|
|
|
|
|
|
|
|
|
|
|
class BaseSequenceSerializer(BaseSerializer):
|
|
|
|
def _format(self):
|
|
|
|
raise NotImplementedError('Subclasses of BaseSequenceSerializer must implement the _format() method.')
|
|
|
|
|
|
|
|
def serialize(self):
|
|
|
|
imports = set()
|
|
|
|
strings = []
|
|
|
|
for item in self.value:
|
|
|
|
item_string, item_imports = serializer_factory(item).serialize()
|
|
|
|
imports.update(item_imports)
|
|
|
|
strings.append(item_string)
|
|
|
|
value = self._format()
|
|
|
|
return value % (", ".join(strings)), imports
|
|
|
|
|
|
|
|
|
|
|
|
class BaseSimpleSerializer(BaseSerializer):
|
|
|
|
def serialize(self):
|
|
|
|
return repr(self.value), set()
|
|
|
|
|
|
|
|
|
2019-01-01 01:57:35 +08:00
|
|
|
class ChoicesSerializer(BaseSerializer):
|
|
|
|
def serialize(self):
|
|
|
|
return serializer_factory(self.value.value).serialize()
|
|
|
|
|
|
|
|
|
2018-07-27 03:04:58 +08:00
|
|
|
class DateTimeSerializer(BaseSerializer):
|
|
|
|
"""For datetime.*, except datetime.datetime."""
|
|
|
|
def serialize(self):
|
|
|
|
return repr(self.value), {'import datetime'}
|
|
|
|
|
|
|
|
|
|
|
|
class DatetimeDatetimeSerializer(BaseSerializer):
|
|
|
|
"""For datetime.datetime."""
|
2016-01-27 22:22:39 +08:00
|
|
|
def serialize(self):
|
|
|
|
if self.value.tzinfo is not None and self.value.tzinfo != utc:
|
|
|
|
self.value = self.value.astimezone(utc)
|
|
|
|
imports = ["import datetime"]
|
|
|
|
if self.value.tzinfo is not None:
|
|
|
|
imports.append("from django.utils.timezone import utc")
|
2018-07-27 03:04:58 +08:00
|
|
|
return repr(self.value).replace('<UTC>', 'utc'), set(imports)
|
2016-01-27 22:22:39 +08:00
|
|
|
|
|
|
|
|
|
|
|
class DecimalSerializer(BaseSerializer):
|
|
|
|
def serialize(self):
|
|
|
|
return repr(self.value), {"from decimal import Decimal"}
|
|
|
|
|
|
|
|
|
|
|
|
class DeconstructableSerializer(BaseSerializer):
|
|
|
|
@staticmethod
|
|
|
|
def serialize_deconstructed(path, args, kwargs):
|
|
|
|
name, imports = DeconstructableSerializer._serialize_path(path)
|
|
|
|
strings = []
|
|
|
|
for arg in args:
|
|
|
|
arg_string, arg_imports = serializer_factory(arg).serialize()
|
|
|
|
strings.append(arg_string)
|
|
|
|
imports.update(arg_imports)
|
|
|
|
for kw, arg in sorted(kwargs.items()):
|
|
|
|
arg_string, arg_imports = serializer_factory(arg).serialize()
|
|
|
|
imports.update(arg_imports)
|
|
|
|
strings.append("%s=%s" % (kw, arg_string))
|
|
|
|
return "%s(%s)" % (name, ", ".join(strings)), imports
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def _serialize_path(path):
|
|
|
|
module, name = path.rsplit(".", 1)
|
|
|
|
if module == "django.db.models":
|
|
|
|
imports = {"from django.db import models"}
|
|
|
|
name = "models.%s" % name
|
|
|
|
else:
|
|
|
|
imports = {"import %s" % module}
|
|
|
|
name = path
|
|
|
|
return name, imports
|
|
|
|
|
|
|
|
def serialize(self):
|
|
|
|
return self.serialize_deconstructed(*self.value.deconstruct())
|
|
|
|
|
|
|
|
|
|
|
|
class DictionarySerializer(BaseSerializer):
|
|
|
|
def serialize(self):
|
|
|
|
imports = set()
|
|
|
|
strings = []
|
|
|
|
for k, v in sorted(self.value.items()):
|
|
|
|
k_string, k_imports = serializer_factory(k).serialize()
|
|
|
|
v_string, v_imports = serializer_factory(v).serialize()
|
|
|
|
imports.update(k_imports)
|
|
|
|
imports.update(v_imports)
|
|
|
|
strings.append((k_string, v_string))
|
|
|
|
return "{%s}" % (", ".join("%s: %s" % (k, v) for k, v in strings)), imports
|
|
|
|
|
|
|
|
|
|
|
|
class EnumSerializer(BaseSerializer):
|
|
|
|
def serialize(self):
|
|
|
|
enum_class = self.value.__class__
|
|
|
|
module = enum_class.__module__
|
2019-09-25 05:42:35 +08:00
|
|
|
return (
|
2019-10-02 21:19:22 +08:00
|
|
|
'%s.%s[%r]' % (module, enum_class.__qualname__, self.value.name),
|
2019-09-25 05:42:35 +08:00
|
|
|
{'import %s' % module},
|
|
|
|
)
|
2016-01-27 22:22:39 +08:00
|
|
|
|
|
|
|
|
|
|
|
class FloatSerializer(BaseSimpleSerializer):
|
|
|
|
def serialize(self):
|
|
|
|
if math.isnan(self.value) or math.isinf(self.value):
|
|
|
|
return 'float("{}")'.format(self.value), set()
|
2017-01-21 21:13:44 +08:00
|
|
|
return super().serialize()
|
2016-01-27 22:22:39 +08:00
|
|
|
|
|
|
|
|
|
|
|
class FrozensetSerializer(BaseSequenceSerializer):
|
|
|
|
def _format(self):
|
|
|
|
return "frozenset([%s])"
|
|
|
|
|
|
|
|
|
|
|
|
class FunctionTypeSerializer(BaseSerializer):
|
|
|
|
def serialize(self):
|
|
|
|
if getattr(self.value, "__self__", None) and isinstance(self.value.__self__, type):
|
|
|
|
klass = self.value.__self__
|
|
|
|
module = klass.__module__
|
|
|
|
return "%s.%s.%s" % (module, klass.__name__, self.value.__name__), {"import %s" % module}
|
|
|
|
# Further error checking
|
|
|
|
if self.value.__name__ == '<lambda>':
|
|
|
|
raise ValueError("Cannot serialize function: lambda")
|
|
|
|
if self.value.__module__ is None:
|
|
|
|
raise ValueError("Cannot serialize function %r: No module" % self.value)
|
2017-01-26 02:59:25 +08:00
|
|
|
|
2016-01-27 22:22:39 +08:00
|
|
|
module_name = self.value.__module__
|
2017-01-26 02:59:25 +08:00
|
|
|
|
|
|
|
if '<' not in self.value.__qualname__: # Qualname can include <locals>
|
|
|
|
return '%s.%s' % (module_name, self.value.__qualname__), {'import %s' % self.value.__module__}
|
|
|
|
|
|
|
|
raise ValueError(
|
|
|
|
'Could not find function %s in %s.\n' % (self.value.__name__, module_name)
|
|
|
|
)
|
2016-01-27 22:22:39 +08:00
|
|
|
|
|
|
|
|
|
|
|
class FunctoolsPartialSerializer(BaseSerializer):
|
|
|
|
def serialize(self):
|
|
|
|
# Serialize functools.partial() arguments
|
|
|
|
func_string, func_imports = serializer_factory(self.value.func).serialize()
|
|
|
|
args_string, args_imports = serializer_factory(self.value.args).serialize()
|
|
|
|
keywords_string, keywords_imports = serializer_factory(self.value.keywords).serialize()
|
|
|
|
# Add any imports needed by arguments
|
2017-12-11 20:08:45 +08:00
|
|
|
imports = {'import functools', *func_imports, *args_imports, *keywords_imports}
|
2016-01-27 22:22:39 +08:00
|
|
|
return (
|
2017-12-01 22:24:56 +08:00
|
|
|
'functools.%s(%s, *%s, **%s)' % (
|
|
|
|
self.value.__class__.__name__,
|
|
|
|
func_string,
|
|
|
|
args_string,
|
|
|
|
keywords_string,
|
2016-01-27 22:22:39 +08:00
|
|
|
),
|
|
|
|
imports,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class IterableSerializer(BaseSerializer):
|
|
|
|
def serialize(self):
|
|
|
|
imports = set()
|
|
|
|
strings = []
|
|
|
|
for item in self.value:
|
|
|
|
item_string, item_imports = serializer_factory(item).serialize()
|
|
|
|
imports.update(item_imports)
|
|
|
|
strings.append(item_string)
|
|
|
|
# When len(strings)==0, the empty iterable should be serialized as
|
|
|
|
# "()", not "(,)" because (,) is invalid Python syntax.
|
|
|
|
value = "(%s)" if len(strings) != 1 else "(%s,)"
|
|
|
|
return value % (", ".join(strings)), imports
|
|
|
|
|
|
|
|
|
|
|
|
class ModelFieldSerializer(DeconstructableSerializer):
|
|
|
|
def serialize(self):
|
|
|
|
attr_name, path, args, kwargs = self.value.deconstruct()
|
|
|
|
return self.serialize_deconstructed(path, args, kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
class ModelManagerSerializer(DeconstructableSerializer):
|
|
|
|
def serialize(self):
|
|
|
|
as_manager, manager_path, qs_path, args, kwargs = self.value.deconstruct()
|
|
|
|
if as_manager:
|
|
|
|
name, imports = self._serialize_path(qs_path)
|
|
|
|
return "%s.as_manager()" % name, imports
|
|
|
|
else:
|
|
|
|
return self.serialize_deconstructed(manager_path, args, kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
class OperationSerializer(BaseSerializer):
|
|
|
|
def serialize(self):
|
|
|
|
from django.db.migrations.writer import OperationWriter
|
|
|
|
string, imports = OperationWriter(self.value, indentation=0).serialize()
|
|
|
|
# Nested operation, trailing comma is handled in upper OperationWriter._write()
|
|
|
|
return string.rstrip(','), imports
|
|
|
|
|
|
|
|
|
|
|
|
class RegexSerializer(BaseSerializer):
|
|
|
|
def serialize(self):
|
|
|
|
regex_pattern, pattern_imports = serializer_factory(self.value.pattern).serialize()
|
2017-01-19 05:52:25 +08:00
|
|
|
# Turn off default implicit flags (e.g. re.U) because regexes with the
|
|
|
|
# same implicit and explicit flags aren't equal.
|
|
|
|
flags = self.value.flags ^ re.compile('').flags
|
|
|
|
regex_flags, flag_imports = serializer_factory(flags).serialize()
|
2017-12-11 20:08:45 +08:00
|
|
|
imports = {'import re', *pattern_imports, *flag_imports}
|
2016-01-27 22:22:39 +08:00
|
|
|
args = [regex_pattern]
|
2017-01-19 05:52:25 +08:00
|
|
|
if flags:
|
2016-01-27 22:22:39 +08:00
|
|
|
args.append(regex_flags)
|
|
|
|
return "re.compile(%s)" % ', '.join(args), imports
|
|
|
|
|
|
|
|
|
|
|
|
class SequenceSerializer(BaseSequenceSerializer):
|
|
|
|
def _format(self):
|
|
|
|
return "[%s]"
|
|
|
|
|
|
|
|
|
|
|
|
class SetSerializer(BaseSequenceSerializer):
|
|
|
|
def _format(self):
|
2017-05-18 21:33:40 +08:00
|
|
|
# Serialize as a set literal except when value is empty because {}
|
|
|
|
# is an empty dict.
|
|
|
|
return '{%s}' if self.value else 'set(%s)'
|
2016-01-27 22:22:39 +08:00
|
|
|
|
|
|
|
|
|
|
|
class SettingsReferenceSerializer(BaseSerializer):
|
|
|
|
def serialize(self):
|
|
|
|
return "settings.%s" % self.value.setting_name, {"from django.conf import settings"}
|
|
|
|
|
|
|
|
|
|
|
|
class TupleSerializer(BaseSequenceSerializer):
|
|
|
|
def _format(self):
|
|
|
|
# When len(value)==0, the empty tuple should be serialized as "()",
|
|
|
|
# not "(,)" because (,) is invalid Python syntax.
|
|
|
|
return "(%s)" if len(self.value) != 1 else "(%s,)"
|
|
|
|
|
|
|
|
|
|
|
|
class TypeSerializer(BaseSerializer):
|
|
|
|
def serialize(self):
|
|
|
|
special_cases = [
|
|
|
|
(models.Model, "models.Model", []),
|
2018-10-11 19:54:37 +08:00
|
|
|
(type(None), 'type(None)', []),
|
2016-01-27 22:22:39 +08:00
|
|
|
]
|
|
|
|
for case, string, imports in special_cases:
|
|
|
|
if case is self.value:
|
|
|
|
return string, set(imports)
|
|
|
|
if hasattr(self.value, "__module__"):
|
|
|
|
module = self.value.__module__
|
2017-01-07 19:11:46 +08:00
|
|
|
if module == builtins.__name__:
|
2016-01-27 22:22:39 +08:00
|
|
|
return self.value.__name__, set()
|
|
|
|
else:
|
2019-11-22 20:43:03 +08:00
|
|
|
return "%s.%s" % (module, self.value.__qualname__), {"import %s" % module}
|
2016-01-27 22:22:39 +08:00
|
|
|
|
|
|
|
|
2016-11-06 20:53:00 +08:00
|
|
|
class UUIDSerializer(BaseSerializer):
|
|
|
|
def serialize(self):
|
|
|
|
return "uuid.%s" % repr(self.value), {"import uuid"}
|
|
|
|
|
|
|
|
|
2019-01-11 02:05:19 +08:00
|
|
|
class Serializer:
|
2019-02-05 19:22:08 +08:00
|
|
|
_registry = {
|
|
|
|
# Some of these are order-dependent.
|
|
|
|
frozenset: FrozensetSerializer,
|
|
|
|
list: SequenceSerializer,
|
|
|
|
set: SetSerializer,
|
|
|
|
tuple: TupleSerializer,
|
|
|
|
dict: DictionarySerializer,
|
2019-01-01 01:57:35 +08:00
|
|
|
models.Choices: ChoicesSerializer,
|
2019-02-05 19:22:08 +08:00
|
|
|
enum.Enum: EnumSerializer,
|
|
|
|
datetime.datetime: DatetimeDatetimeSerializer,
|
|
|
|
(datetime.date, datetime.timedelta, datetime.time): DateTimeSerializer,
|
|
|
|
SettingsReference: SettingsReferenceSerializer,
|
|
|
|
float: FloatSerializer,
|
2019-04-13 21:03:26 +08:00
|
|
|
(bool, int, type(None), bytes, str, range): BaseSimpleSerializer,
|
2019-02-05 19:22:08 +08:00
|
|
|
decimal.Decimal: DecimalSerializer,
|
|
|
|
(functools.partial, functools.partialmethod): FunctoolsPartialSerializer,
|
|
|
|
(types.FunctionType, types.BuiltinFunctionType, types.MethodType): FunctionTypeSerializer,
|
|
|
|
collections.abc.Iterable: IterableSerializer,
|
|
|
|
(COMPILED_REGEX_TYPE, RegexObject): RegexSerializer,
|
|
|
|
uuid.UUID: UUIDSerializer,
|
|
|
|
}
|
2019-01-11 02:05:19 +08:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def register(cls, type_, serializer):
|
|
|
|
if not issubclass(serializer, BaseSerializer):
|
|
|
|
raise ValueError("'%s' must inherit from 'BaseSerializer'." % serializer.__name__)
|
|
|
|
cls._registry[type_] = serializer
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def unregister(cls, type_):
|
|
|
|
cls._registry.pop(type_)
|
|
|
|
|
|
|
|
|
2016-01-27 22:22:39 +08:00
|
|
|
def serializer_factory(value):
|
|
|
|
if isinstance(value, Promise):
|
2017-04-22 01:52:26 +08:00
|
|
|
value = str(value)
|
2016-01-27 22:22:39 +08:00
|
|
|
elif isinstance(value, LazyObject):
|
|
|
|
# The unwrapped value is returned as the first item of the arguments
|
|
|
|
# tuple.
|
|
|
|
value = value.__reduce__()[1][0]
|
|
|
|
|
2016-05-05 04:05:11 +08:00
|
|
|
if isinstance(value, models.Field):
|
|
|
|
return ModelFieldSerializer(value)
|
|
|
|
if isinstance(value, models.manager.BaseManager):
|
|
|
|
return ModelManagerSerializer(value)
|
|
|
|
if isinstance(value, Operation):
|
|
|
|
return OperationSerializer(value)
|
|
|
|
if isinstance(value, type):
|
|
|
|
return TypeSerializer(value)
|
|
|
|
# Anything that knows how to deconstruct itself.
|
|
|
|
if hasattr(value, 'deconstruct'):
|
|
|
|
return DeconstructableSerializer(value)
|
2019-01-11 02:05:19 +08:00
|
|
|
for type_, serializer_cls in Serializer._registry.items():
|
|
|
|
if isinstance(value, type_):
|
|
|
|
return serializer_cls(value)
|
2016-01-27 22:22:39 +08:00
|
|
|
raise ValueError(
|
|
|
|
"Cannot serialize: %r\nThere are some values Django cannot serialize into "
|
|
|
|
"migration files.\nFor more, see https://docs.djangoproject.com/en/%s/"
|
|
|
|
"topics/migrations/#migration-serializing" % (value, get_docs_version())
|
|
|
|
)
|