From 6d0d31fbd42edbfeb1ef188fefa311add2f83863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Dlouh=C3=BD?= Date: Thu, 1 Sep 2022 22:40:54 +0200 Subject: [PATCH 01/10] fixes for Django 4.0 and Python 3.0, drop Python 2 support --- timedelta/fields.py | 28 +++++++++++++++++++++++++--- timedelta/forms.py | 9 ++++----- timedelta/helpers.py | 6 ++---- timedelta/widgets.py | 7 +++---- 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/timedelta/fields.py b/timedelta/fields.py index 4e84481..bd8a962 100644 --- a/timedelta/fields.py +++ b/timedelta/fields.py @@ -1,6 +1,5 @@ from django.db import models from django.core.exceptions import ValidationError -from django.utils import six from collections import defaultdict import datetime @@ -10,7 +9,7 @@ # TODO: Figure out why django admin thinks fields of this type have changed every time an object is saved. -class TimedeltaField(six.with_metaclass(models.SubfieldBase, models.Field)): +class TimedeltaField(models.Field): """ Store a datetime.timedelta as an INTERVAL in postgres, or a CHAR(20) in other database backends. @@ -39,7 +38,7 @@ def to_python(self, value): def get_prep_value(self, value): if self.null and value == "": return None - if (value is None) or isinstance(value, six.string_types): + if (value is None) or isinstance(value, str): return value return str(value).replace(',', '') @@ -87,3 +86,26 @@ def deconstruct(self): if self._max_value is not None: kwargs['max_value'] = self._max_value return name, path, args, kwargs + + def contribute_to_class(self, cls, name): + super(TimedeltaField, self).contribute_to_class(cls, name) + setattr(cls, name, CastOnAssignDescriptor(self)) + + +class CastOnAssignDescriptor(object): + """ + A property descriptor which ensures that `field.to_python()` is called on _every_ assignment to the field. + This used to be provided by the `django.db.models.subclassing.Creator` class, which in turn + was used by the deprecated-in-Django-1.10 `SubfieldBase` class, hence the reimplementation here. + """ + + def __init__(self, field): + self.field = field + + def __get__(self, obj, type=None): + if obj is None: + return self + return obj.__dict__[self.field.name] + + def __set__(self, obj, value): + obj.__dict__[self.field.name] = self.field.to_python(value) diff --git a/timedelta/forms.py b/timedelta/forms.py index 3ec1f11..089fba1 100644 --- a/timedelta/forms.py +++ b/timedelta/forms.py @@ -1,6 +1,5 @@ from django import forms -from django.utils.translation import ugettext_lazy as _ -from django.utils import six +from django.utils.translation import gettext_lazy as _ import datetime from collections import defaultdict @@ -36,7 +35,7 @@ def clean(self, value): >>> try: ... t.clean('3 days, 8:42:42.3.42161') ... except forms.ValidationError as arg: - ... six.print_(arg.messages[0]) + ... print(arg.messages[0]) Enter a valid time span: e.g. "3 days, 4 hours, 2 minutes" >>> t.clean('5 day, 8:42:42') datetime.timedelta(5, 31362) @@ -77,9 +76,9 @@ def clean(self, value): >>> t.clean('2 weeks, 2 days') datetime.timedelta(16) >>> try: - ... t.clean(six.u('2 we\xe8k, 2 days')) + ... t.clean('2 we\xe8k, 2 days') ... except forms.ValidationError as arg: - ... six.print_(arg.messages[0]) + ... print(arg.messages[0]) Enter a valid time span: e.g. "3 days, 4 hours, 2 minutes" """ diff --git a/timedelta/helpers.py b/timedelta/helpers.py index b7215ec..6de411e 100644 --- a/timedelta/helpers.py +++ b/timedelta/helpers.py @@ -4,8 +4,6 @@ import datetime from decimal import Decimal -from django.utils import six - STRFDATETIME = re.compile('([dgGhHis])') STRFDATETIME_REPL = lambda x: '%%(%s)s' % x.group() @@ -276,7 +274,7 @@ def parse(string): # and from serialization d = re.match(r'^((?P[-+]?\d+) days?,? )?(?P[-+]?)(?P\d+):' r'(?P\d+)(:(?P\d+(\.\d+)?))?$', - six.text_type(string)) + str(string)) if d: d = d.groupdict(0) if d['sign'] == '-': @@ -291,7 +289,7 @@ def parse(string): r'((?P-?((\d*\.\d+)|\d+))\W*h(ou)?(r(s)?)?(,)?\W*)?' r'((?P-?((\d*\.\d+)|\d+))\W*m(in(ute)?(s)?)?(,)?\W*)?' r'((?P-?((\d*\.\d+)|\d+))\W*s(ec(ond)?(s)?)?)?\W*$', - six.text_type(string)) + str(string)) if not d: raise TypeError("'%s' is not a valid time interval" % string) d = d.groupdict(0) diff --git a/timedelta/widgets.py b/timedelta/widgets.py index c09843c..c757bab 100644 --- a/timedelta/widgets.py +++ b/timedelta/widgets.py @@ -1,25 +1,24 @@ import datetime from django import forms -from django.utils import six from .helpers import nice_repr, parse class TimedeltaWidget(forms.TextInput): def __init__(self, *args, **kwargs): return super(TimedeltaWidget, self).__init__(*args, **kwargs) - + def render(self, name, value, attrs=None): if value is None: value = "" - elif isinstance(value, six.string_types): + elif isinstance(value, str): pass else: if isinstance(value, int): value = datetime.timedelta(seconds=value) value = nice_repr(value) return super(TimedeltaWidget, self).render(name, value, attrs) - + def _has_changed(self, initial, data): """ We need to make sure the objects are of the canonical form, as a From 0e0d6019cb5c8d2909d60139139ab329aaf764bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Dlouh=C3=BD?= Date: Fri, 2 Sep 2022 23:13:47 +0200 Subject: [PATCH 02/10] update tox configuration --- tox.ini | 82 ++++++++++++++++++--------------------------------------- 1 file changed, 26 insertions(+), 56 deletions(-) diff --git a/tox.ini b/tox.ini index 0155060..4a73005 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,10 @@ [tox] envlist = - py26, py27, py33, pypy, - py27-django-1.4, py27-django-1.5, - py27-django-1.6, py27-django-1.7, py27-django-trunk, - py34-django-1.6, py34-django-1.7, py34-django-trunk + py{310,39,38}-django-{40,41}, + py{310,39,38,37}-django-32, + py{39,38,37}-django-{30,31}, + py{38,37}-django-{20,21,22}, + py{37}-django-{18,19,110,111}, [base] deps = @@ -12,55 +13,24 @@ deps = [testenv] deps = - django - {[base]deps} -commands = python setup.py test - -[testenv:py27-django-1.4] -basepython = python2.7 -deps = - django>=1.4,<1.5 - {[base]deps} - -[testenv:py27-django-1.5] -basepython = python2.7 -deps = - django>=1.5,<1.6 - {[base]deps} - -[testenv:py27-django-1.6] -basepython = python2.7 -deps = - django>=1.6,<1.7 - {[base]deps} - -[testenv:py27-django-1.7] -basepython = python2.7 -deps = - https://www.djangoproject.com/download/1.7b2/tarball/ - {[base]deps} - -[testenv:py27-django-trunk] -basepython = python2.7 -deps = - git+git://github.com/django/django.git - {[base]deps} - -[testenv:py34-django-1.6] -basepython = python3.4 -deps = - django>=1.5,<1.6 - {[base]deps} - -[testenv:py34-django-1.7] -basepython = python3.4 -deps = - https://www.djangoproject.com/download/1.7b2/tarball/ - {[base]deps} - -[testenv:py34-django-trunk] -basepython = python3.4 -deps = - git+git://github.com/django/django.git - {[base]deps} - + django-18: django==1.8.* + django-19: django==1.9.* + django-110: django==1.10.* + django-111: django==1.11.* + django-20: django==2.0.* + django-21: django==2.1.* + django-22: django==2.2.* + django-30: django==3.0.* + django-31: django==3.1.* + django-32: django==3.2.* + django-40: django==4.0.* + django-41: django==4.1.* + {[base]deps} + +commands = python manage.py test + +basepython = + py37: python3.7 + py38: python3.8 + py39: python3.9 + py310: python3.10 From 42aa4c7c15d5ef250d2f60713a4308d2ab3a1677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Dlouh=C3=BD?= Date: Fri, 2 Sep 2022 23:14:17 +0200 Subject: [PATCH 03/10] fix tests in Python 3.x --- timedelta/forms.py | 46 +++++++++---------- timedelta/helpers.py | 102 +++++++++++++++++++++---------------------- 2 files changed, 74 insertions(+), 74 deletions(-) diff --git a/timedelta/forms.py b/timedelta/forms.py index 089fba1..89cf802 100644 --- a/timedelta/forms.py +++ b/timedelta/forms.py @@ -25,56 +25,56 @@ def clean(self, value): >>> t = TimedeltaFormField() >>> t.clean('1 day') - datetime.timedelta(1) + datetime.timedelta(days=1) >>> t.clean('1 day, 0:00:00') - datetime.timedelta(1) + datetime.timedelta(days=1) >>> t.clean('1 day, 8:42:42.342') - datetime.timedelta(1, 31362, 342000) + datetime.timedelta(days=1, seconds=31362, microseconds=342000) >>> t.clean('3 days, 8:42:42.342161') - datetime.timedelta(3, 31362, 342161) + datetime.timedelta(days=3, seconds=31362, microseconds=342161) >>> try: ... t.clean('3 days, 8:42:42.3.42161') ... except forms.ValidationError as arg: ... print(arg.messages[0]) Enter a valid time span: e.g. "3 days, 4 hours, 2 minutes" >>> t.clean('5 day, 8:42:42') - datetime.timedelta(5, 31362) + datetime.timedelta(days=5, seconds=31362) >>> t.clean('1 days') - datetime.timedelta(1) + datetime.timedelta(days=1) >>> t.clean('1 second') - datetime.timedelta(0, 1) + datetime.timedelta(seconds=1) >>> t.clean('1 sec') - datetime.timedelta(0, 1) + datetime.timedelta(seconds=1) >>> t.clean('10 seconds') - datetime.timedelta(0, 10) + datetime.timedelta(seconds=10) >>> t.clean('30 seconds') - datetime.timedelta(0, 30) + datetime.timedelta(seconds=30) >>> t.clean('1 minute, 30 seconds') - datetime.timedelta(0, 90) + datetime.timedelta(seconds=90) >>> t.clean('2.5 minutes') - datetime.timedelta(0, 150) + datetime.timedelta(seconds=150) >>> t.clean('2 minutes, 30 seconds') - datetime.timedelta(0, 150) + datetime.timedelta(seconds=150) >>> t.clean('.5 hours') - datetime.timedelta(0, 1800) + datetime.timedelta(seconds=1800) >>> t.clean('30 minutes') - datetime.timedelta(0, 1800) + datetime.timedelta(seconds=1800) >>> t.clean('1 hour') - datetime.timedelta(0, 3600) + datetime.timedelta(seconds=3600) >>> t.clean('5.5 hours') - datetime.timedelta(0, 19800) + datetime.timedelta(seconds=19800) >>> t.clean('1 day, 1 hour, 30 mins') - datetime.timedelta(1, 5400) + datetime.timedelta(days=1, seconds=5400) >>> t.clean('8 min') - datetime.timedelta(0, 480) + datetime.timedelta(seconds=480) >>> t.clean('3 days, 12 hours') - datetime.timedelta(3, 43200) + datetime.timedelta(days=3, seconds=43200) >>> t.clean('3.5 day') - datetime.timedelta(3, 43200) + datetime.timedelta(days=3, seconds=43200) >>> t.clean('1 week') - datetime.timedelta(7) + datetime.timedelta(days=7) >>> t.clean('2 weeks, 2 days') - datetime.timedelta(16) + datetime.timedelta(days=16) >>> try: ... t.clean('2 we\xe8k, 2 days') ... except forms.ValidationError as arg: diff --git a/timedelta/helpers.py b/timedelta/helpers.py index 6de411e..2631d6c 100644 --- a/timedelta/helpers.py +++ b/timedelta/helpers.py @@ -158,49 +158,49 @@ def parse(string): Parse a string into a timedelta object. >>> parse("1 day") - datetime.timedelta(1) + datetime.timedelta(days=1) >>> parse("2 days") - datetime.timedelta(2) + datetime.timedelta(days=2) >>> parse("1 d") - datetime.timedelta(1) + datetime.timedelta(days=1) >>> parse("1 hour") - datetime.timedelta(0, 3600) + datetime.timedelta(seconds=3600) >>> parse("1 hours") - datetime.timedelta(0, 3600) + datetime.timedelta(seconds=3600) >>> parse("1 hr") - datetime.timedelta(0, 3600) + datetime.timedelta(seconds=3600) >>> parse("1 hrs") - datetime.timedelta(0, 3600) + datetime.timedelta(seconds=3600) >>> parse("1h") - datetime.timedelta(0, 3600) + datetime.timedelta(seconds=3600) >>> parse("1wk") - datetime.timedelta(7) + datetime.timedelta(days=7) >>> parse("1 week") - datetime.timedelta(7) + datetime.timedelta(days=7) >>> parse("1 weeks") - datetime.timedelta(7) + datetime.timedelta(days=7) >>> parse("2 wks") - datetime.timedelta(14) + datetime.timedelta(days=14) >>> parse("1 sec") - datetime.timedelta(0, 1) + datetime.timedelta(seconds=1) >>> parse("1 secs") - datetime.timedelta(0, 1) + datetime.timedelta(seconds=1) >>> parse("1 s") - datetime.timedelta(0, 1) + datetime.timedelta(seconds=1) >>> parse("1 second") - datetime.timedelta(0, 1) + datetime.timedelta(seconds=1) >>> parse("1 seconds") - datetime.timedelta(0, 1) + datetime.timedelta(seconds=1) >>> parse("1 minute") - datetime.timedelta(0, 60) + datetime.timedelta(seconds=60) >>> parse("1 min") - datetime.timedelta(0, 60) + datetime.timedelta(seconds=60) >>> parse("1 m") - datetime.timedelta(0, 60) + datetime.timedelta(seconds=60) >>> parse("1 minutes") - datetime.timedelta(0, 60) + datetime.timedelta(seconds=60) >>> parse("1 mins") - datetime.timedelta(0, 60) + datetime.timedelta(seconds=60) >>> parse("2 ws") Traceback (most recent call last): ... @@ -226,28 +226,28 @@ def parse(string): ... TypeError: '' is not a valid time interval >>> parse("1.5 days") - datetime.timedelta(1, 43200) + datetime.timedelta(days=1, seconds=43200) >>> parse("3 weeks") - datetime.timedelta(21) + datetime.timedelta(days=21) >>> parse("4.2 hours") - datetime.timedelta(0, 15120) + datetime.timedelta(seconds=15120) >>> parse(".5 hours") - datetime.timedelta(0, 1800) + datetime.timedelta(seconds=1800) >>> parse(" hours") Traceback (most recent call last): ... TypeError: 'hours' is not a valid time interval >>> parse("1 hour, 5 mins") - datetime.timedelta(0, 3900) + datetime.timedelta(seconds=3900) >>> parse("-2 days") - datetime.timedelta(-2) + datetime.timedelta(days=-2) >>> parse("-1 day 0:00:01") - datetime.timedelta(-1, 1) + datetime.timedelta(days=-1, seconds=1) >>> parse("-1 day, -1:01:01") - datetime.timedelta(-2, 82739) + datetime.timedelta(days=-2, seconds=82739) >>> parse("-1 weeks, 2 days, -3 hours, 4 minutes, -5 seconds") - datetime.timedelta(-5, 11045) + datetime.timedelta(days=-5, seconds=11045) >>> parse("0 seconds") datetime.timedelta(0) @@ -264,7 +264,7 @@ def parse(string): >>> parse(nice_repr(zero, 'short')) datetime.timedelta(0) >>> parse(' 50 days 00:00:00 ') - datetime.timedelta(50) + datetime.timedelta(days=50) """ string = string.strip() @@ -308,7 +308,7 @@ def divide(obj1, obj2, as_float=False): >>> divide(td(2), td(1)) 2 >>> divide(td(32), 16) - datetime.timedelta(2) + datetime.timedelta(days=2) >>> divide(datetime.timedelta(1), datetime.timedelta(hours=6)) 4 >>> divide(datetime.timedelta(2), datetime.timedelta(3)) @@ -316,7 +316,7 @@ def divide(obj1, obj2, as_float=False): >>> divide(datetime.timedelta(8), datetime.timedelta(3), as_float=True) 2.6666666666666665 >>> divide(datetime.timedelta(8), 2.0) - datetime.timedelta(4) + datetime.timedelta(days=4) >>> divide(datetime.timedelta(8), 2, as_float=True) Traceback (most recent call last): ... @@ -347,11 +347,11 @@ def modulo(obj1, obj2): >>> from datetime import timedelta as td >>> modulo(td(5), td(2)) - datetime.timedelta(1) + datetime.timedelta(days=1) >>> modulo(td(6), td(3)) datetime.timedelta(0) >>> modulo(td(15), 4 * 3600 * 24) - datetime.timedelta(3) + datetime.timedelta(days=3) >>> modulo(5, td(1)) Traceback (most recent call last): @@ -399,13 +399,13 @@ def multiply(obj, val): """ Allows for the multiplication of timedeltas by float values. >>> multiply(datetime.timedelta(seconds=20), 1.5) - datetime.timedelta(0, 30) + datetime.timedelta(seconds=30) >>> multiply(datetime.timedelta(1), 2.5) - datetime.timedelta(2, 43200) + datetime.timedelta(days=2, seconds=43200) >>> multiply(datetime.timedelta(1), 3) - datetime.timedelta(3) + datetime.timedelta(days=3) >>> multiply(datetime.timedelta(1), Decimal("5.5")) - datetime.timedelta(5, 43200) + datetime.timedelta(days=5, seconds=43200) >>> multiply(datetime.date.today(), 2.5) Traceback (most recent call last): ... @@ -447,31 +447,31 @@ def round_to_nearest(obj, timedelta): >>> round_to_nearest(datetime.timedelta(minutes=14), td) datetime.timedelta(0) >>> round_to_nearest(datetime.timedelta(minutes=15), td) - datetime.timedelta(0, 1800) + datetime.timedelta(seconds=1800) >>> round_to_nearest(datetime.timedelta(minutes=29), td) - datetime.timedelta(0, 1800) + datetime.timedelta(seconds=1800) >>> round_to_nearest(datetime.timedelta(minutes=30), td) - datetime.timedelta(0, 1800) + datetime.timedelta(seconds=1800) >>> round_to_nearest(datetime.timedelta(minutes=42), td) - datetime.timedelta(0, 1800) + datetime.timedelta(seconds=1800) >>> round_to_nearest(datetime.timedelta(hours=7, minutes=22), td) - datetime.timedelta(0, 27000) + datetime.timedelta(seconds=27000) >>> td = datetime.timedelta(minutes=15) >>> round_to_nearest(datetime.timedelta(minutes=0), td) datetime.timedelta(0) >>> round_to_nearest(datetime.timedelta(minutes=14), td) - datetime.timedelta(0, 900) + datetime.timedelta(seconds=900) >>> round_to_nearest(datetime.timedelta(minutes=15), td) - datetime.timedelta(0, 900) + datetime.timedelta(seconds=900) >>> round_to_nearest(datetime.timedelta(minutes=29), td) - datetime.timedelta(0, 1800) + datetime.timedelta(seconds=1800) >>> round_to_nearest(datetime.timedelta(minutes=30), td) - datetime.timedelta(0, 1800) + datetime.timedelta(seconds=1800) >>> round_to_nearest(datetime.timedelta(minutes=42), td) - datetime.timedelta(0, 2700) + datetime.timedelta(seconds=2700) >>> round_to_nearest(datetime.timedelta(hours=7, minutes=22), td) - datetime.timedelta(0, 26100) + datetime.timedelta(seconds=26100) >>> td = datetime.timedelta(minutes=30) >>> round_to_nearest(datetime.datetime(2010,1,1,9,22), td) From 62a61bab53541882d62ead42a26b1377367e00db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Dlouh=C3=BD?= Date: Fri, 2 Sep 2022 23:14:55 +0200 Subject: [PATCH 04/10] move test models to test_app, add migrations, fix tests --- manage.py | 11 +++ test_app/migrations/0001_initial.py | 48 +++++++++++++ test_app/migrations/__init__.py | 0 test_app/models.py | 10 +++ test_app/settings.py | 108 ++++++++++++++++++++++++++++ tests.py | 2 +- timedelta/tests.py | 12 ++-- 7 files changed, 182 insertions(+), 9 deletions(-) create mode 100755 manage.py create mode 100644 test_app/migrations/0001_initial.py create mode 100644 test_app/migrations/__init__.py create mode 100644 test_app/models.py create mode 100644 test_app/settings.py diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..0f59d8a --- /dev/null +++ b/manage.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python +import os +import sys + + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_app.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/test_app/migrations/0001_initial.py b/test_app/migrations/0001_initial.py new file mode 100644 index 0000000..45cd504 --- /dev/null +++ b/test_app/migrations/0001_initial.py @@ -0,0 +1,48 @@ +# Generated by Django 4.1 on 2022-09-02 20:04 + +import datetime +from django.db import migrations, models +import timedelta.fields + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="MinMaxTestModel", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "min", + timedelta.fields.TimedeltaField( + min_value=datetime.timedelta(days=1) + ), + ), + ( + "max", + timedelta.fields.TimedeltaField( + max_value=datetime.timedelta(days=1) + ), + ), + ( + "minmax", + timedelta.fields.TimedeltaField( + max_value=datetime.timedelta(days=7), + min_value=datetime.timedelta(days=1), + ), + ), + ], + ), + ] diff --git a/test_app/migrations/__init__.py b/test_app/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test_app/models.py b/test_app/models.py new file mode 100644 index 0000000..a3c4127 --- /dev/null +++ b/test_app/models.py @@ -0,0 +1,10 @@ + +from django.db import models +from timedelta.fields import TimedeltaField +import datetime + + +class MinMaxTestModel(models.Model): + min = TimedeltaField(min_value=datetime.timedelta(1)) + max = TimedeltaField(max_value=datetime.timedelta(1)) + minmax = TimedeltaField(min_value=datetime.timedelta(1), max_value=datetime.timedelta(7)) diff --git a/test_app/settings.py b/test_app/settings.py new file mode 100644 index 0000000..52a4bea --- /dev/null +++ b/test_app/settings.py @@ -0,0 +1,108 @@ +""" +Django settings for test_project project. + +For more information on this file, see +https://docs.djangoproject.com/en/1.7/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.7/ref/settings/ +""" + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +import os +from typing import List + + +BASE_DIR = os.path.dirname(os.path.dirname(__file__)) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = "f8fkupu8pa%%u$wgk6c!os39el41v7i7^u*8xf#g5p@+c&)b#^" + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "APP_DIRS": True, + "OPTIONS": { + "debug": True, + "context_processors": [ + "django.contrib.auth.context_processors.auth", + "django.template.context_processors.debug", + "django.template.context_processors.i18n", + "django.template.context_processors.media", + "django.template.context_processors.static", + "django.template.context_processors.tz", + "django.template.context_processors.request", + "django.contrib.messages.context_processors.messages", + ], + }, + }, +] + +ALLOWED_HOSTS: List[str] = [] + + +# Application definition + +INSTALLED_APPS = ( + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "timedelta", + "test_app", +) + +MIDDLEWARE = ( + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", +) +MIDDLEWARE_CLASSES = MIDDLEWARE + +# ROOT_URLCONF = "test_project.urls" + +# WSGI_APPLICATION = "test_project.wsgi.application" + +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + + +# Database +# https://docs.djangoproject.com/en/1.7/ref/settings/#databases + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.path.join(BASE_DIR, "db.sqlite3"), + } +} + +# Internationalization +# https://docs.djangoproject.com/en/1.7/topics/i18n/ + +LANGUAGE_CODE = "en-us" + +TIME_ZONE = "UTC" + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.7/howto/static-files/ + +STATIC_URL = "/static/" diff --git a/tests.py b/tests.py index 2d30a6d..08eea2b 100644 --- a/tests.py +++ b/tests.py @@ -12,7 +12,7 @@ def main(db_engine='sqlite3'): You can play with a django model without a complete django app installation. http://www.djangosnippets.org/snippets/1044/ """ - os.environ["DJANGO_SETTINGS_MODULE"] = "django.conf.global_settings" + os.environ["DJANGO_SETTINGS_MODULE"] = "test_app.settings" from django.conf import global_settings global_settings.INSTALLED_APPS = ( diff --git a/timedelta/tests.py b/timedelta/tests.py index ebdaf3a..b2833a2 100644 --- a/timedelta/tests.py +++ b/timedelta/tests.py @@ -3,18 +3,11 @@ import doctest from django.core.exceptions import ValidationError -from django.db import models -from django.utils import six -from .fields import TimedeltaField import timedelta.helpers import timedelta.forms import timedelta.widgets - -class MinMaxTestModel(models.Model): - min = TimedeltaField(min_value=datetime.timedelta(1)) - max = TimedeltaField(max_value=datetime.timedelta(1)) - minmax = TimedeltaField(min_value=datetime.timedelta(1), max_value=datetime.timedelta(7)) +from test_app.models import MinMaxTestModel class TimedeltaModelFieldTest(TestCase): def test_validate(self): @@ -55,6 +48,9 @@ def test_load_from_db(self): self.assertEquals(datetime.timedelta(0, 120), obj.max) self.assertEquals(datetime.timedelta(3), obj.minmax) + def tearDown(self): + MinMaxTestModel.objects.all().delete() + def load_tests(loader, tests, ignore): tests.addTests(doctest.DocTestSuite(timedelta.helpers)) tests.addTests(doctest.DocTestSuite(timedelta.forms)) From 0184cd3cd0877c6708375775481f0c56e5940d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Dlouh=C3=BD?= Date: Tue, 10 Jan 2023 13:20:44 +0100 Subject: [PATCH 05/10] add .gitignore --- .gitignore | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2374e69 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class +*~ + + +db.sqlite3 +.tox + +django_timedeltafield.egg-info/ From 3608657cd3e59740cd36226fa5ae9918e030f8b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Dlouh=C3=BD?= Date: Tue, 10 Jan 2023 13:13:20 +0100 Subject: [PATCH 06/10] fix tox --- tox.ini | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tox.ini b/tox.ini index 4a73005..33c05f1 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,10 @@ [tox] envlist = - py{310,39,38}-django-{40,41}, - py{310,39,38,37}-django-32, - py{39,38,37}-django-{30,31}, - py{38,37}-django-{20,21,22}, - py{37}-django-{18,19,110,111}, + py{310,39,38}-django{40,41}, + py{310,39,38,37}-django32, + py{39,38,37}-django{30,31}, + py{38,37}-django{20,21,22}, + py{37}-django{18,19,110,111} [base] deps = @@ -13,18 +13,18 @@ deps = [testenv] deps = - django-18: django==1.8.* - django-19: django==1.9.* - django-110: django==1.10.* - django-111: django==1.11.* - django-20: django==2.0.* - django-21: django==2.1.* - django-22: django==2.2.* - django-30: django==3.0.* - django-31: django==3.1.* - django-32: django==3.2.* - django-40: django==4.0.* - django-41: django==4.1.* + django18: django==1.8.* + django19: django==1.9.* + django110: django==1.10.* + django111: django==1.11.* + django20: django==2.0.* + django21: django==2.1.* + django22: django==2.2.* + django30: django==3.0.* + django31: django==3.1.* + django32: django==3.2.* + django40: django==4.0.* + django41: django==4.1.* {[base]deps} commands = python manage.py test From b1c91ba4b08d7cd9a9543e4aeeffa41a262874d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Dlouh=C3=BD?= Date: Tue, 10 Jan 2023 13:16:59 +0100 Subject: [PATCH 07/10] add github actions configuration --- .github/workflows/tests.yml | 49 +++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..d7b7bb9 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,49 @@ +name: Tests + +on: + push: + branches: + - main + pull_request: + +jobs: + tests: + name: Python ${{ matrix.python-version }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: + - "3.7" + - "3.8" + - "3.9" + - "3.10" + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel setuptools tox + - name: Run tox targets for ${{ matrix.python-version }} + run: | + ENV_PREFIX=$(tr -C -d "0-9" <<< "${{ matrix.python-version }}") + TOXENV=$(tox --listenvs | grep "^py$ENV_PREFIX" | tr '\n' ',') python -m tox + + # lint: + # name: Lint + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v3 + # - name: Set up Python + # uses: actions/setup-python@v3 + # with: + # python-version: "3.10" + # - name: Install dependencies + # run: | + # python -m pip install --upgrade pip tox + # - name: Run lint + # run: tox -e style From b9c37b58d7325d038857b7f8ae81d4f76fd6544f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Dlouh=C3=BD?= Date: Tue, 10 Jan 2023 13:55:00 +0100 Subject: [PATCH 08/10] fix tests running in Github actions --- timedelta/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/timedelta/tests.py b/timedelta/tests.py index b2833a2..216ffa3 100644 --- a/timedelta/tests.py +++ b/timedelta/tests.py @@ -1,4 +1,4 @@ -from unittest import TestCase +from django.test import TestCase import datetime import doctest From a4f64ec8feb2a0f979db613d8b43bb76f895da4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Dlouh=C3=BD?= Date: Tue, 10 Jan 2023 12:51:42 +0100 Subject: [PATCH 09/10] fix module name to be timedelta_field --- {timedelta => timedelta_field}/VERSION | 0 {timedelta => timedelta_field}/__init__.py | 0 {timedelta => timedelta_field}/fields.py | 0 {timedelta => timedelta_field}/forms.py | 0 {timedelta => timedelta_field}/helpers.py | 0 {timedelta => timedelta_field}/models.py | 0 {timedelta => timedelta_field}/templatetags/__init__.py | 0 {timedelta => timedelta_field}/templatetags/decimal_hours.py | 0 {timedelta => timedelta_field}/templatetags/timedelta.py | 0 {timedelta => timedelta_field}/tests.py | 0 {timedelta => timedelta_field}/widgets.py | 0 11 files changed, 0 insertions(+), 0 deletions(-) rename {timedelta => timedelta_field}/VERSION (100%) rename {timedelta => timedelta_field}/__init__.py (100%) rename {timedelta => timedelta_field}/fields.py (100%) rename {timedelta => timedelta_field}/forms.py (100%) rename {timedelta => timedelta_field}/helpers.py (100%) rename {timedelta => timedelta_field}/models.py (100%) rename {timedelta => timedelta_field}/templatetags/__init__.py (100%) rename {timedelta => timedelta_field}/templatetags/decimal_hours.py (100%) rename {timedelta => timedelta_field}/templatetags/timedelta.py (100%) rename {timedelta => timedelta_field}/tests.py (100%) rename {timedelta => timedelta_field}/widgets.py (100%) diff --git a/timedelta/VERSION b/timedelta_field/VERSION similarity index 100% rename from timedelta/VERSION rename to timedelta_field/VERSION diff --git a/timedelta/__init__.py b/timedelta_field/__init__.py similarity index 100% rename from timedelta/__init__.py rename to timedelta_field/__init__.py diff --git a/timedelta/fields.py b/timedelta_field/fields.py similarity index 100% rename from timedelta/fields.py rename to timedelta_field/fields.py diff --git a/timedelta/forms.py b/timedelta_field/forms.py similarity index 100% rename from timedelta/forms.py rename to timedelta_field/forms.py diff --git a/timedelta/helpers.py b/timedelta_field/helpers.py similarity index 100% rename from timedelta/helpers.py rename to timedelta_field/helpers.py diff --git a/timedelta/models.py b/timedelta_field/models.py similarity index 100% rename from timedelta/models.py rename to timedelta_field/models.py diff --git a/timedelta/templatetags/__init__.py b/timedelta_field/templatetags/__init__.py similarity index 100% rename from timedelta/templatetags/__init__.py rename to timedelta_field/templatetags/__init__.py diff --git a/timedelta/templatetags/decimal_hours.py b/timedelta_field/templatetags/decimal_hours.py similarity index 100% rename from timedelta/templatetags/decimal_hours.py rename to timedelta_field/templatetags/decimal_hours.py diff --git a/timedelta/templatetags/timedelta.py b/timedelta_field/templatetags/timedelta.py similarity index 100% rename from timedelta/templatetags/timedelta.py rename to timedelta_field/templatetags/timedelta.py diff --git a/timedelta/tests.py b/timedelta_field/tests.py similarity index 100% rename from timedelta/tests.py rename to timedelta_field/tests.py diff --git a/timedelta/widgets.py b/timedelta_field/widgets.py similarity index 100% rename from timedelta/widgets.py rename to timedelta_field/widgets.py From 33437f1f8638b6cae437d60907060ec2ed376566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Dlouh=C3=BD?= Date: Tue, 10 Jan 2023 13:14:01 +0100 Subject: [PATCH 10/10] fix tests after module rename --- setup.py | 8 ++++---- test_app/migrations/0001_initial.py | 8 ++++---- test_app/models.py | 2 +- test_app/settings.py | 2 +- timedelta_field/tests.py | 20 +++++++++++++++----- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/setup.py b/setup.py index 9ec0cec..f0a3097 100644 --- a/setup.py +++ b/setup.py @@ -3,17 +3,17 @@ setup( name = "django-timedeltafield", - version = open(os.path.join(os.path.dirname(__file__), 'timedelta', 'VERSION')).read().strip(), + version = open(os.path.join(os.path.dirname(__file__), 'timedelta_field', 'VERSION')).read().strip(), description = "TimedeltaField for django models", long_description = open("README").read(), url = "http://hg.schinckel.net/django-timedelta-field/", author = "Matthew Schinckel", author_email = "matt@schinckel.net", packages = [ - "timedelta", - "timedelta.templatetags", + "timedelta_field", + "timedelta_field.templatetags", ], - package_data = {'timedelta': ['VERSION']}, + package_data = {'timedelta_field': ['VERSION']}, classifiers = [ 'Programming Language :: Python', 'License :: OSI Approved :: BSD License', diff --git a/test_app/migrations/0001_initial.py b/test_app/migrations/0001_initial.py index 45cd504..d03299c 100644 --- a/test_app/migrations/0001_initial.py +++ b/test_app/migrations/0001_initial.py @@ -2,7 +2,7 @@ import datetime from django.db import migrations, models -import timedelta.fields +import timedelta_field.fields class Migration(migrations.Migration): @@ -26,19 +26,19 @@ class Migration(migrations.Migration): ), ( "min", - timedelta.fields.TimedeltaField( + timedelta_field.fields.TimedeltaField( min_value=datetime.timedelta(days=1) ), ), ( "max", - timedelta.fields.TimedeltaField( + timedelta_field.fields.TimedeltaField( max_value=datetime.timedelta(days=1) ), ), ( "minmax", - timedelta.fields.TimedeltaField( + timedelta_field.fields.TimedeltaField( max_value=datetime.timedelta(days=7), min_value=datetime.timedelta(days=1), ), diff --git a/test_app/models.py b/test_app/models.py index a3c4127..f1f6032 100644 --- a/test_app/models.py +++ b/test_app/models.py @@ -1,6 +1,6 @@ from django.db import models -from timedelta.fields import TimedeltaField +from timedelta_field.fields import TimedeltaField import datetime diff --git a/test_app/settings.py b/test_app/settings.py index 52a4bea..9cc2e24 100644 --- a/test_app/settings.py +++ b/test_app/settings.py @@ -57,7 +57,7 @@ "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", - "timedelta", + "timedelta_field", "test_app", ) diff --git a/timedelta_field/tests.py b/timedelta_field/tests.py index 216ffa3..a69d797 100644 --- a/timedelta_field/tests.py +++ b/timedelta_field/tests.py @@ -4,9 +4,9 @@ from django.core.exceptions import ValidationError -import timedelta.helpers -import timedelta.forms -import timedelta.widgets +import timedelta_field.helpers +import timedelta_field.forms +import timedelta_field.widgets from test_app.models import MinMaxTestModel class TimedeltaModelFieldTest(TestCase): @@ -48,10 +48,20 @@ def test_load_from_db(self): self.assertEquals(datetime.timedelta(0, 120), obj.max) self.assertEquals(datetime.timedelta(3), obj.minmax) + obj.min = datetime.timedelta(hours=23) + obj.save() + obj.refresh_from_db() + self.assertEquals(datetime.timedelta(hours=23), obj.min) + + obj.min = '25 days' + obj.save() + obj.refresh_from_db() + self.assertEquals(datetime.timedelta(days=25), obj.min) + def tearDown(self): MinMaxTestModel.objects.all().delete() def load_tests(loader, tests, ignore): - tests.addTests(doctest.DocTestSuite(timedelta.helpers)) - tests.addTests(doctest.DocTestSuite(timedelta.forms)) + tests.addTests(doctest.DocTestSuite(timedelta_field.helpers)) + tests.addTests(doctest.DocTestSuite(timedelta_field.forms)) return tests