From c72dde41e603093ab0bb12fa24fa69cfda0d35f9 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Thu, 26 Jul 2018 15:04:58 -0400 Subject: [PATCH] Fixed #29595 -- Allowed using timedelta in migrations questioner. Refs #29600 -- Removed usage of django.utils.datetime_safe in migrations. --- django/db/migrations/questioner.py | 5 ++-- django/db/migrations/serializer.py | 46 ++++++++--------------------- tests/migrations/test_questioner.py | 16 ++++++++-- tests/migrations/test_writer.py | 15 ---------- 4 files changed, 29 insertions(+), 53 deletions(-) diff --git a/django/db/migrations/questioner.py b/django/db/migrations/questioner.py index d482de6388..08e7719dc1 100644 --- a/django/db/migrations/questioner.py +++ b/django/db/migrations/questioner.py @@ -1,10 +1,11 @@ +import datetime import importlib import os import sys from django.apps import apps from django.db.models.fields import NOT_PROVIDED -from django.utils import datetime_safe, timezone +from django.utils import timezone from .loader import MigrationLoader @@ -135,7 +136,7 @@ class InteractiveMigrationQuestioner(MigrationQuestioner): sys.exit(1) else: try: - return eval(code, {}, {"datetime": datetime_safe, "timezone": timezone}) + return eval(code, {}, {'datetime': datetime, 'timezone': timezone}) except (SyntaxError, NameError) as e: print("Invalid input: %s" % e) diff --git a/django/db/migrations/serializer.py b/django/db/migrations/serializer.py index 9b251e54cd..911cf0f359 100644 --- a/django/db/migrations/serializer.py +++ b/django/db/migrations/serializer.py @@ -12,7 +12,6 @@ import uuid 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 import datetime_safe from django.utils.functional import LazyObject, Promise from django.utils.timezone import utc from django.utils.version import get_docs_version @@ -46,25 +45,21 @@ class BaseSimpleSerializer(BaseSerializer): return repr(self.value), set() -class DatetimeSerializer(BaseSerializer): +class DateTimeSerializer(BaseSerializer): + """For datetime.*, except datetime.datetime.""" + def serialize(self): + return repr(self.value), {'import datetime'} + + +class DatetimeDatetimeSerializer(BaseSerializer): + """For datetime.datetime.""" def serialize(self): if self.value.tzinfo is not None and self.value.tzinfo != utc: self.value = self.value.astimezone(utc) - value_repr = repr(self.value).replace("", "utc") - if isinstance(self.value, datetime_safe.datetime): - value_repr = "datetime.%s" % value_repr imports = ["import datetime"] if self.value.tzinfo is not None: imports.append("from django.utils.timezone import utc") - return value_repr, set(imports) - - -class DateSerializer(BaseSerializer): - def serialize(self): - value_repr = repr(self.value) - if isinstance(self.value, datetime_safe.date): - value_repr = "datetime.%s" % value_repr - return value_repr, {"import datetime"} + return repr(self.value).replace('', 'utc'), set(imports) class DecimalSerializer(BaseSerializer): @@ -246,19 +241,6 @@ class SettingsReferenceSerializer(BaseSerializer): return "settings.%s" % self.value.setting_name, {"from django.conf import settings"} -class TimedeltaSerializer(BaseSerializer): - def serialize(self): - return repr(self.value), {"import datetime"} - - -class TimeSerializer(BaseSerializer): - def serialize(self): - value_repr = repr(self.value) - if isinstance(self.value, datetime_safe.time): - value_repr = "datetime.%s" % value_repr - return value_repr, {"import datetime"} - - class TupleSerializer(BaseSequenceSerializer): def _format(self): # When len(value)==0, the empty tuple should be serialized as "()", @@ -322,13 +304,9 @@ def serializer_factory(value): if isinstance(value, enum.Enum): return EnumSerializer(value) if isinstance(value, datetime.datetime): - return DatetimeSerializer(value) - if isinstance(value, datetime.date): - return DateSerializer(value) - if isinstance(value, datetime.time): - return TimeSerializer(value) - if isinstance(value, datetime.timedelta): - return TimedeltaSerializer(value) + return DatetimeDatetimeSerializer(value) + if isinstance(value, (datetime.date, datetime.timedelta, datetime.time)): + return DateTimeSerializer(value) if isinstance(value, SettingsReference): return SettingsReferenceSerializer(value) if isinstance(value, float): diff --git a/tests/migrations/test_questioner.py b/tests/migrations/test_questioner.py index 45536410bb..e17dd04ab6 100644 --- a/tests/migrations/test_questioner.py +++ b/tests/migrations/test_questioner.py @@ -1,6 +1,11 @@ -from django.db.migrations.questioner import MigrationQuestioner +import datetime +from unittest import mock + +from django.db.migrations.questioner import ( + InteractiveMigrationQuestioner, MigrationQuestioner, +) from django.test import SimpleTestCase -from django.test.utils import override_settings +from django.test.utils import captured_stdout, override_settings class QuestionerTests(SimpleTestCase): @@ -11,3 +16,10 @@ class QuestionerTests(SimpleTestCase): def test_ask_initial_with_disabled_migrations(self): questioner = MigrationQuestioner() self.assertIs(False, questioner.ask_initial('migrations')) + + @mock.patch('builtins.input', return_value='datetime.timedelta(days=1)') + def test_timedelta_default(self, mock): + questioner = InteractiveMigrationQuestioner() + with captured_stdout(): + value = questioner._ask_default() + self.assertEqual(value, datetime.timedelta(days=1)) diff --git a/tests/migrations/test_writer.py b/tests/migrations/test_writer.py index 946306c325..c656f95666 100644 --- a/tests/migrations/test_writer.py +++ b/tests/migrations/test_writer.py @@ -19,7 +19,6 @@ from django.db.migrations.writer import ( MigrationWriter, OperationWriter, SettingsReference, ) from django.test import SimpleTestCase -from django.utils import datetime_safe from django.utils.deconstruct import deconstructible from django.utils.functional import SimpleLazyObject from django.utils.timezone import get_default_timezone, get_fixed_timezone, utc @@ -364,20 +363,6 @@ class WriterTests(SimpleTestCase): ) ) - def test_serialize_datetime_safe(self): - self.assertSerializedResultEqual( - datetime_safe.date(2014, 3, 31), - ("datetime.date(2014, 3, 31)", {'import datetime'}) - ) - self.assertSerializedResultEqual( - datetime_safe.time(10, 25), - ("datetime.time(10, 25)", {'import datetime'}) - ) - self.assertSerializedResultEqual( - datetime_safe.datetime(2014, 3, 31, 16, 4, 31), - ("datetime.datetime(2014, 3, 31, 16, 4, 31)", {'import datetime'}) - ) - def test_serialize_fields(self): self.assertSerializedFieldEqual(models.CharField(max_length=255)) self.assertSerializedResultEqual(