You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

561 lines
23 KiB

  1. from __future__ import unicode_literals
  2. import collections
  3. import datetime
  4. import decimal
  5. import functools
  6. import math
  7. import os
  8. import re
  9. import types
  10. from importlib import import_module
  11. from django import get_version
  12. from django.apps import apps
  13. from django.db import migrations, models
  14. from django.db.migrations.loader import MigrationLoader
  15. from django.db.migrations.operations.base import Operation
  16. from django.db.migrations.utils import COMPILED_REGEX_TYPE, RegexObject
  17. from django.utils import datetime_safe, six
  18. from django.utils._os import upath
  19. from django.utils.encoding import force_text
  20. from django.utils.functional import LazyObject, Promise
  21. from django.utils.inspect import get_func_args
  22. from django.utils.module_loading import module_dir
  23. from django.utils.timezone import now, utc
  24. from django.utils.version import get_docs_version
  25. class SettingsReference(str):
  26. """
  27. Special subclass of string which actually references a current settings
  28. value. It's treated as the value in memory, but serializes out to a
  29. settings.NAME attribute reference.
  30. """
  31. def __new__(self, value, setting_name):
  32. return str.__new__(self, value)
  33. def __init__(self, value, setting_name):
  34. self.setting_name = setting_name
  35. class OperationWriter(object):
  36. def __init__(self, operation, indentation=2):
  37. self.operation = operation
  38. self.buff = []
  39. self.indentation = indentation
  40. def serialize(self):
  41. def _write(_arg_name, _arg_value):
  42. if (_arg_name in self.operation.serialization_expand_args and
  43. isinstance(_arg_value, (list, tuple, dict))):
  44. if isinstance(_arg_value, dict):
  45. self.feed('%s={' % _arg_name)
  46. self.indent()
  47. for key, value in _arg_value.items():
  48. key_string, key_imports = MigrationWriter.serialize(key)
  49. arg_string, arg_imports = MigrationWriter.serialize(value)
  50. args = arg_string.splitlines()
  51. if len(args) > 1:
  52. self.feed('%s: %s' % (key_string, args[0]))
  53. for arg in args[1:-1]:
  54. self.feed(arg)
  55. self.feed('%s,' % args[-1])
  56. else:
  57. self.feed('%s: %s,' % (key_string, arg_string))
  58. imports.update(key_imports)
  59. imports.update(arg_imports)
  60. self.unindent()
  61. self.feed('},')
  62. else:
  63. self.feed('%s=[' % _arg_name)
  64. self.indent()
  65. for item in _arg_value:
  66. arg_string, arg_imports = MigrationWriter.serialize(item)
  67. args = arg_string.splitlines()
  68. if len(args) > 1:
  69. for arg in args[:-1]:
  70. self.feed(arg)
  71. self.feed('%s,' % args[-1])
  72. else:
  73. self.feed('%s,' % arg_string)
  74. imports.update(arg_imports)
  75. self.unindent()
  76. self.feed('],')
  77. else:
  78. arg_string, arg_imports = MigrationWriter.serialize(_arg_value)
  79. args = arg_string.splitlines()
  80. if len(args) > 1:
  81. self.feed('%s=%s' % (_arg_name, args[0]))
  82. for arg in args[1:-1]:
  83. self.feed(arg)
  84. self.feed('%s,' % args[-1])
  85. else:
  86. self.feed('%s=%s,' % (_arg_name, arg_string))
  87. imports.update(arg_imports)
  88. imports = set()
  89. name, args, kwargs = self.operation.deconstruct()
  90. operation_args = get_func_args(self.operation.__init__)
  91. # See if this operation is in django.db.migrations. If it is,
  92. # We can just use the fact we already have that imported,
  93. # otherwise, we need to add an import for the operation class.
  94. if getattr(migrations, name, None) == self.operation.__class__:
  95. self.feed('migrations.%s(' % name)
  96. else:
  97. imports.add('import %s' % (self.operation.__class__.__module__))
  98. self.feed('%s.%s(' % (self.operation.__class__.__module__, name))
  99. self.indent()
  100. for i, arg in enumerate(args):
  101. arg_value = arg
  102. arg_name = operation_args[i]
  103. _write(arg_name, arg_value)
  104. i = len(args)
  105. # Only iterate over remaining arguments
  106. for arg_name in operation_args[i:]:
  107. if arg_name in kwargs: # Don't sort to maintain signature order
  108. arg_value = kwargs[arg_name]
  109. _write(arg_name, arg_value)
  110. self.unindent()
  111. self.feed('),')
  112. return self.render(), imports
  113. def indent(self):
  114. self.indentation += 1
  115. def unindent(self):
  116. self.indentation -= 1
  117. def feed(self, line):
  118. self.buff.append(' ' * (self.indentation * 4) + line)
  119. def render(self):
  120. return '\n'.join(self.buff)
  121. class MigrationWriter(object):
  122. """
  123. Takes a Migration instance and is able to produce the contents
  124. of the migration file from it.
  125. """
  126. def __init__(self, migration):
  127. self.migration = migration
  128. self.needs_manual_porting = False
  129. def as_string(self):
  130. """
  131. Returns a string of the file contents.
  132. """
  133. items = {
  134. "replaces_str": "",
  135. "initial_str": "",
  136. }
  137. imports = set()
  138. # Deconstruct operations
  139. operations = []
  140. for operation in self.migration.operations:
  141. operation_string, operation_imports = OperationWriter(operation).serialize()
  142. imports.update(operation_imports)
  143. operations.append(operation_string)
  144. items["operations"] = "\n".join(operations) + "\n" if operations else ""
  145. # Format dependencies and write out swappable dependencies right
  146. dependencies = []
  147. for dependency in self.migration.dependencies:
  148. if dependency[0] == "__setting__":
  149. dependencies.append(" migrations.swappable_dependency(settings.%s)," % dependency[1])
  150. imports.add("from django.conf import settings")
  151. else:
  152. # No need to output bytestrings for dependencies
  153. dependency = tuple(force_text(s) for s in dependency)
  154. dependencies.append(" %s," % self.serialize(dependency)[0])
  155. items["dependencies"] = "\n".join(dependencies) + "\n" if dependencies else ""
  156. # Format imports nicely, swapping imports of functions from migration files
  157. # for comments
  158. migration_imports = set()
  159. for line in list(imports):
  160. if re.match("^import (.*)\.\d+[^\s]*$", line):
  161. migration_imports.add(line.split("import")[1].strip())
  162. imports.remove(line)
  163. self.needs_manual_porting = True
  164. # django.db.migrations is always used, but models import may not be.
  165. # If models import exists, merge it with migrations import.
  166. if "from django.db import models" in imports:
  167. imports.discard("from django.db import models")
  168. imports.add("from django.db import migrations, models")
  169. else:
  170. imports.add("from django.db import migrations")
  171. # Sort imports by the package / module to be imported (the part after
  172. # "from" in "from ... import ..." or after "import" in "import ...").
  173. sorted_imports = sorted(imports, key=lambda i: i.split()[1])
  174. items["imports"] = "\n".join(sorted_imports) + "\n" if imports else ""
  175. if migration_imports:
  176. items["imports"] += (
  177. "\n\n# Functions from the following migrations need manual "
  178. "copying.\n# Move them and any dependencies into this file, "
  179. "then update the\n# RunPython operations to refer to the local "
  180. "versions:\n# %s"
  181. ) % "\n# ".join(sorted(migration_imports))
  182. # If there's a replaces, make a string for it
  183. if self.migration.replaces:
  184. items['replaces_str'] = "\n replaces = %s\n" % self.serialize(self.migration.replaces)[0]
  185. # Hinting that goes into comment
  186. items.update(
  187. version=get_version(),
  188. timestamp=now().strftime("%Y-%m-%d %H:%M"),
  189. )
  190. if self.migration.initial:
  191. items['initial_str'] = "\n initial = True\n"
  192. return (MIGRATION_TEMPLATE % items).encode("utf8")
  193. @staticmethod
  194. def serialize_datetime(value):
  195. """
  196. Returns a serialized version of a datetime object that is valid,
  197. executable python code. It converts timezone-aware values to utc with
  198. an 'executable' utc representation of tzinfo.
  199. """
  200. if value.tzinfo is not None and value.tzinfo != utc:
  201. value = value.astimezone(utc)
  202. value_repr = repr(value).replace("<UTC>", "utc")
  203. if isinstance(value, datetime_safe.datetime):
  204. value_repr = "datetime.%s" % value_repr
  205. return value_repr
  206. @property
  207. def basedir(self):
  208. migrations_package_name = MigrationLoader.migrations_module(self.migration.app_label)
  209. if migrations_package_name is None:
  210. raise ValueError(
  211. "Django can't create migrations for app '%s' because "
  212. "migrations have been disabled via the MIGRATION_MODULES "
  213. "setting." % self.migration.app_label
  214. )
  215. # See if we can import the migrations module directly
  216. try:
  217. migrations_module = import_module(migrations_package_name)
  218. except ImportError:
  219. pass
  220. else:
  221. try:
  222. return upath(module_dir(migrations_module))
  223. except ValueError:
  224. pass
  225. # Alright, see if it's a direct submodule of the app
  226. app_config = apps.get_app_config(self.migration.app_label)
  227. maybe_app_name, _, migrations_package_basename = migrations_package_name.rpartition(".")
  228. if app_config.name == maybe_app_name:
  229. return os.path.join(app_config.path, migrations_package_basename)
  230. # In case of using MIGRATION_MODULES setting and the custom package
  231. # doesn't exist, create one, starting from an existing package
  232. existing_dirs, missing_dirs = migrations_package_name.split("."), []
  233. while existing_dirs:
  234. missing_dirs.insert(0, existing_dirs.pop(-1))
  235. try:
  236. base_module = import_module(".".join(existing_dirs))
  237. except ImportError:
  238. continue
  239. else:
  240. try:
  241. base_dir = upath(module_dir(base_module))
  242. except ValueError:
  243. continue
  244. else:
  245. break
  246. else:
  247. raise ValueError(
  248. "Could not locate an appropriate location to create "
  249. "migrations package %s. Make sure the toplevel "
  250. "package exists and can be imported." %
  251. migrations_package_name)
  252. final_dir = os.path.join(base_dir, *missing_dirs)
  253. if not os.path.isdir(final_dir):
  254. os.makedirs(final_dir)
  255. for missing_dir in missing_dirs:
  256. base_dir = os.path.join(base_dir, missing_dir)
  257. with open(os.path.join(base_dir, "__init__.py"), "w"):
  258. pass
  259. return final_dir
  260. @property
  261. def filename(self):
  262. return "%s.py" % self.migration.name
  263. @property
  264. def path(self):
  265. return os.path.join(self.basedir, self.filename)
  266. @classmethod
  267. def serialize_deconstructed(cls, path, args, kwargs):
  268. name, imports = cls._serialize_path(path)
  269. strings = []
  270. for arg in args:
  271. arg_string, arg_imports = cls.serialize(arg)
  272. strings.append(arg_string)
  273. imports.update(arg_imports)
  274. for kw, arg in sorted(kwargs.items()):
  275. arg_string, arg_imports = cls.serialize(arg)
  276. imports.update(arg_imports)
  277. strings.append("%s=%s" % (kw, arg_string))
  278. return "%s(%s)" % (name, ", ".join(strings)), imports
  279. @classmethod
  280. def _serialize_path(cls, path):
  281. module, name = path.rsplit(".", 1)
  282. if module == "django.db.models":
  283. imports = {"from django.db import models"}
  284. name = "models.%s" % name
  285. else:
  286. imports = {"import %s" % module}
  287. name = path
  288. return name, imports
  289. @classmethod
  290. def serialize(cls, value):
  291. """
  292. Serializes the value to a string that's parsable by Python, along
  293. with any needed imports to make that string work.
  294. More advanced than repr() as it can encode things
  295. like datetime.datetime.now.
  296. """
  297. # FIXME: Ideally Promise would be reconstructible, but for now we
  298. # use force_text on them and defer to the normal string serialization
  299. # process.
  300. if isinstance(value, Promise):
  301. value = force_text(value)
  302. elif isinstance(value, LazyObject):
  303. # The unwrapped value is returned as the first item of the
  304. # arguments tuple.
  305. value = value.__reduce__()[1][0]
  306. # Sequences
  307. if isinstance(value, (frozenset, list, set, tuple)):
  308. imports = set()
  309. strings = []
  310. for item in value:
  311. item_string, item_imports = cls.serialize(item)
  312. imports.update(item_imports)
  313. strings.append(item_string)
  314. if isinstance(value, set):
  315. # Don't use the literal "{%s}" as it doesn't support empty set
  316. format = "set([%s])"
  317. elif isinstance(value, frozenset):
  318. format = "frozenset([%s])"
  319. elif isinstance(value, tuple):
  320. # When len(value)==0, the empty tuple should be serialized as
  321. # "()", not "(,)" because (,) is invalid Python syntax.
  322. format = "(%s)" if len(value) != 1 else "(%s,)"
  323. else:
  324. format = "[%s]"
  325. return format % (", ".join(strings)), imports
  326. # Dictionaries
  327. elif isinstance(value, dict):
  328. imports = set()
  329. strings = []
  330. for k, v in sorted(value.items()):
  331. k_string, k_imports = cls.serialize(k)
  332. v_string, v_imports = cls.serialize(v)
  333. imports.update(k_imports)
  334. imports.update(v_imports)
  335. strings.append((k_string, v_string))
  336. return "{%s}" % (", ".join("%s: %s" % (k, v) for k, v in strings)), imports
  337. # Datetimes
  338. elif isinstance(value, datetime.datetime):
  339. value_repr = cls.serialize_datetime(value)
  340. imports = ["import datetime"]
  341. if value.tzinfo is not None:
  342. imports.append("from django.utils.timezone import utc")
  343. return value_repr, set(imports)
  344. # Dates
  345. elif isinstance(value, datetime.date):
  346. value_repr = repr(value)
  347. if isinstance(value, datetime_safe.date):
  348. value_repr = "datetime.%s" % value_repr
  349. return value_repr, {"import datetime"}
  350. # Times
  351. elif isinstance(value, datetime.time):
  352. value_repr = repr(value)
  353. if isinstance(value, datetime_safe.time):
  354. value_repr = "datetime.%s" % value_repr
  355. return value_repr, {"import datetime"}
  356. # Timedeltas
  357. elif isinstance(value, datetime.timedelta):
  358. return repr(value), {"import datetime"}
  359. # Settings references
  360. elif isinstance(value, SettingsReference):
  361. return "settings.%s" % value.setting_name, {"from django.conf import settings"}
  362. # Simple types
  363. elif isinstance(value, float):
  364. if math.isnan(value) or math.isinf(value):
  365. return 'float("{}")'.format(value), set()
  366. return repr(value), set()
  367. elif isinstance(value, six.integer_types + (bool, type(None))):
  368. return repr(value), set()
  369. elif isinstance(value, six.binary_type):
  370. value_repr = repr(value)
  371. if six.PY2:
  372. # Prepend the `b` prefix since we're importing unicode_literals
  373. value_repr = 'b' + value_repr
  374. return value_repr, set()
  375. elif isinstance(value, six.text_type):
  376. value_repr = repr(value)
  377. if six.PY2:
  378. # Strip the `u` prefix since we're importing unicode_literals
  379. value_repr = value_repr[1:]
  380. return value_repr, set()
  381. # Decimal
  382. elif isinstance(value, decimal.Decimal):
  383. return repr(value), {"from decimal import Decimal"}
  384. # Django fields
  385. elif isinstance(value, models.Field):
  386. attr_name, path, args, kwargs = value.deconstruct()
  387. return cls.serialize_deconstructed(path, args, kwargs)
  388. # Classes
  389. elif isinstance(value, type):
  390. special_cases = [
  391. (models.Model, "models.Model", []),
  392. ]
  393. for case, string, imports in special_cases:
  394. if case is value:
  395. return string, set(imports)
  396. if hasattr(value, "__module__"):
  397. module = value.__module__
  398. if module == six.moves.builtins.__name__:
  399. return value.__name__, set()
  400. else:
  401. return "%s.%s" % (module, value.__name__), {"import %s" % module}
  402. elif isinstance(value, models.manager.BaseManager):
  403. as_manager, manager_path, qs_path, args, kwargs = value.deconstruct()
  404. if as_manager:
  405. name, imports = cls._serialize_path(qs_path)
  406. return "%s.as_manager()" % name, imports
  407. else:
  408. return cls.serialize_deconstructed(manager_path, args, kwargs)
  409. elif isinstance(value, Operation):
  410. string, imports = OperationWriter(value, indentation=0).serialize()
  411. # Nested operation, trailing comma is handled in upper OperationWriter._write()
  412. return string.rstrip(','), imports
  413. elif isinstance(value, functools.partial):
  414. imports = {'import functools'}
  415. # Serialize functools.partial() arguments
  416. func_string, func_imports = cls.serialize(value.func)
  417. args_string, args_imports = cls.serialize(value.args)
  418. keywords_string, keywords_imports = cls.serialize(value.keywords)
  419. # Add any imports needed by arguments
  420. imports.update(func_imports)
  421. imports.update(args_imports)
  422. imports.update(keywords_imports)
  423. return (
  424. "functools.partial(%s, *%s, **%s)" % (
  425. func_string, args_string, keywords_string,
  426. ),
  427. imports,
  428. )
  429. # Anything that knows how to deconstruct itself.
  430. elif hasattr(value, 'deconstruct'):
  431. return cls.serialize_deconstructed(*value.deconstruct())
  432. # Functions
  433. elif isinstance(value, (types.FunctionType, types.BuiltinFunctionType)):
  434. # @classmethod?
  435. if getattr(value, "__self__", None) and isinstance(value.__self__, type):
  436. klass = value.__self__
  437. module = klass.__module__
  438. return "%s.%s.%s" % (module, klass.__name__, value.__name__), {"import %s" % module}
  439. # Further error checking
  440. if value.__name__ == '<lambda>':
  441. raise ValueError("Cannot serialize function: lambda")
  442. if value.__module__ is None:
  443. raise ValueError("Cannot serialize function %r: No module" % value)
  444. # Python 3 is a lot easier, and only uses this branch if it's not local.
  445. if getattr(value, "__qualname__", None) and getattr(value, "__module__", None):
  446. if "<" not in value.__qualname__: # Qualname can include <locals>
  447. return "%s.%s" % (value.__module__, value.__qualname__), {"import %s" % value.__module__}
  448. # Python 2/fallback version
  449. module_name = value.__module__
  450. # Make sure it's actually there and not an unbound method
  451. module = import_module(module_name)
  452. if not hasattr(module, value.__name__):
  453. raise ValueError(
  454. "Could not find function %s in %s.\n"
  455. "Please note that due to Python 2 limitations, you cannot "
  456. "serialize unbound method functions (e.g. a method "
  457. "declared and used in the same class body). Please move "
  458. "the function into the main module body to use migrations.\n"
  459. "For more information, see "
  460. "https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values"
  461. % (value.__name__, module_name, get_docs_version()))
  462. # Needed on Python 2 only
  463. if module_name == '__builtin__':
  464. return value.__name__, set()
  465. return "%s.%s" % (module_name, value.__name__), {"import %s" % module_name}
  466. # Other iterables
  467. elif isinstance(value, collections.Iterable):
  468. imports = set()
  469. strings = []
  470. for item in value:
  471. item_string, item_imports = cls.serialize(item)
  472. imports.update(item_imports)
  473. strings.append(item_string)
  474. # When len(strings)==0, the empty iterable should be serialized as
  475. # "()", not "(,)" because (,) is invalid Python syntax.
  476. format = "(%s)" if len(strings) != 1 else "(%s,)"
  477. return format % (", ".join(strings)), imports
  478. # Compiled regex
  479. elif isinstance(value, (COMPILED_REGEX_TYPE, RegexObject)):
  480. imports = {"import re"}
  481. regex_pattern, pattern_imports = cls.serialize(value.pattern)
  482. regex_flags, flag_imports = cls.serialize(value.flags)
  483. imports.update(pattern_imports)
  484. imports.update(flag_imports)
  485. args = [regex_pattern]
  486. if value.flags:
  487. args.append(regex_flags)
  488. return "re.compile(%s)" % ', '.join(args), imports
  489. # Uh oh.
  490. else:
  491. raise ValueError(
  492. "Cannot serialize: %r\nThere are some values Django cannot serialize into "
  493. "migration files.\nFor more, see https://docs.djangoproject.com/en/%s/"
  494. "topics/migrations/#migration-serializing" % (value, get_docs_version())
  495. )
  496. MIGRATION_TEMPLATE = """\
  497. # -*- coding: utf-8 -*-
  498. # Generated by Django %(version)s on %(timestamp)s
  499. from __future__ import unicode_literals
  500. %(imports)s
  501. class Migration(migrations.Migration):
  502. %(replaces_str)s%(initial_str)s
  503. dependencies = [
  504. %(dependencies)s\
  505. ]
  506. operations = [
  507. %(operations)s\
  508. ]
  509. """