From eceb3c239a26977368fda8c14e36e4ae6883d91f Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Fri, 25 Jul 2014 16:52:37 +0200 Subject: [PATCH 01/21] Comparison operators added on Version object. --- versionfield/tests/__init__.py | 53 ++++++++++++++++++++++++++++++++++ versionfield/version.py | 47 +++++++++++++++++++++++++----- 2 files changed, 92 insertions(+), 8 deletions(-) diff --git a/versionfield/tests/__init__.py b/versionfield/tests/__init__.py index 1214ff1..749c586 100644 --- a/versionfield/tests/__init__.py +++ b/versionfield/tests/__init__.py @@ -1,6 +1,10 @@ +import unittest + from django.test import TestCase from django.db import models from .. import VersionField +from versionfield.constants import DEFAULT_NUMBER_BITS +from versionfield.version import Version class DummyModel(models.Model): version = VersionField() @@ -54,3 +58,52 @@ def setUp(self): def test_get_by_exact_version(self): thing = DummyModelCustomBit.objects.get(version="1.999.1") self.assertEqual(thing.version,"1.999.1") + + +class VersionObjectTestCase(unittest.TestCase): + + def test_equal_operator(self): + self.assertEqual( + Version("1.2.3", DEFAULT_NUMBER_BITS), + Version("1.2.3", DEFAULT_NUMBER_BITS), + ) + + def test_lt_operator(self): + self.assertTrue( + Version("1.2.3", DEFAULT_NUMBER_BITS) < + Version("1.2.4", DEFAULT_NUMBER_BITS) + ) + self.assertFalse( + Version("1.2.4", DEFAULT_NUMBER_BITS) < + Version("1.2.3", DEFAULT_NUMBER_BITS) + ) + + def test_le_operator(self): + self.assertTrue( + Version("1.2.3", DEFAULT_NUMBER_BITS) <= + Version("1.2.4", DEFAULT_NUMBER_BITS) + ) + self.assertTrue( + Version("1.2.4", DEFAULT_NUMBER_BITS) <= + Version("1.2.4", DEFAULT_NUMBER_BITS) + ) + + def test_gt_operator(self): + self.assertTrue( + Version("1.2.4", DEFAULT_NUMBER_BITS) > + Version("1.2.3", DEFAULT_NUMBER_BITS) + ) + self.assertFalse( + Version("2.3.4", DEFAULT_NUMBER_BITS) > + Version("3.2.1", DEFAULT_NUMBER_BITS) + ) + + def test_ge_operator(self): + self.assertTrue( + Version("1.2.4", DEFAULT_NUMBER_BITS) >= + Version("1.2.3", DEFAULT_NUMBER_BITS) + ) + self.assertTrue( + Version("3.3.3", DEFAULT_NUMBER_BITS) <= + Version("3.3.3", DEFAULT_NUMBER_BITS) + ) diff --git a/versionfield/version.py b/versionfield/version.py index ce7a766..7e29fd7 100644 --- a/versionfield/version.py +++ b/versionfield/version.py @@ -1,16 +1,20 @@ from .utils import convert_version_string_to_int, convert_version_int_to_string from .constants import DEFAULT_NUMBER_BITS + + class Version(object): - def __init__(self,string,number_bits): + def __init__(self, string, number_bits): """ Take in a verison string e.g. '3.0.1' Store it as a converted int """ self.number_bits = number_bits - self.internal_integer = convert_version_string_to_int(string,number_bits) + self.internal_integer = convert_version_string_to_int( + string, number_bits) def __unicode__(self): - return unicode(convert_version_int_to_string(self.internal_integer,self.number_bits)) + return unicode( + convert_version_int_to_string(self.internal_integer, self.number_bits)) def __str__(self): return self.__unicode__() @@ -21,10 +25,37 @@ def __repr__(self): def __int__(self): return self.internal_integer - def __eq__(self,other): + def __eq__(self, other): if not other: return False # we are obviously a valid Version, but 'other' isn't - if isinstance(other,basestring): - return self == Version(other,self.number_bits) - else: - return int(self) == int(other) \ No newline at end of file + if isinstance(other, basestring): + other = Version(other, self.number_bits) + return int(self) == int(other) + + def __lt__(self, other): + if not other: + return False + if isinstance(other, basestring): + other = Version(other, self.number_bits) + return int(self) < int(other) + + def __le__(self, other): + if not other: + return False + if isinstance(other, basestring): + other = Version(other, self.number_bits) + return int(self) <= int(other) + + def __gt__(self, other): + if not other: + return False + if isinstance(other, basestring): + other = Version(other, self.number_bits) + return int(self) > int(other) + + def __ge__(self, other): + if not other: + return False + if isinstance(other, basestring): + other = Version(other, self.number_bits) + return int(self) >= int(other) From 277e350e42a075a7373eaf6b787482cac3186fe6 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Sat, 20 Dec 2014 14:50:18 +0100 Subject: [PATCH 02/21] PEP8 fixes --- versionfield/__init__.py | 97 ++++++++++--------- versionfield/constants.py | 2 +- versionfield/forms.py | 40 +++++--- versionfield/tests/__init__.py | 172 +++++++++++++++++---------------- versionfield/utils.py | 107 +++++++++++--------- 5 files changed, 224 insertions(+), 194 deletions(-) diff --git a/versionfield/__init__.py b/versionfield/__init__.py index f589268..3c17c19 100644 --- a/versionfield/__init__.py +++ b/versionfield/__init__.py @@ -1,69 +1,72 @@ from django.db import models + +from . import forms from .constants import DEFAULT_NUMBER_BITS from .version import Version -from utils import convert_version_string_to_int, convert_version_int_to_string -import forms +from .utils import convert_version_string_to_int, convert_version_int_to_string class VersionField(models.PositiveIntegerField): - """ - A Field where version numbers are input/output as strings (e.g. 3.0.1) - but stored in the db as converted integers for fast indexing - """ - description = "A version number (e.g. 3.0.1)" - __metaclass__ = models.SubfieldBase + """ + A Field where version numbers are input/output as strings (e.g. 3.0.1) + but stored in the db as converted integers for fast indexing + """ + + description = "A version number (e.g. 3.0.1)" - def __init__(self, number_bits=DEFAULT_NUMBER_BITS, *args, **kwargs): - self.number_bits = number_bits - super(VersionField, self).__init__(*args, **kwargs) + __metaclass__ = models.SubfieldBase - def to_python(self,value): - if isinstance(value, Version): - return value + def __init__(self, number_bits=DEFAULT_NUMBER_BITS, *args, **kwargs): + self.number_bits = number_bits + super(VersionField, self).__init__(*args, **kwargs) - if isinstance(value,basestring): - return Version(value,self.number_bits) + def to_python(self, value): + if isinstance(value, Version): + return value - if value is None: - return None + if isinstance(value, basestring): + return Version(value, self.number_bits) - return Version(convert_version_int_to_string(value,self.number_bits),self.number_bits) + if value is None: + return None - def get_prep_value(self,value): - if isinstance(value,basestring): - return int(Version(value,self.number_bits)) + return Version( + convert_version_int_to_string(value, self.number_bits), + self.number_bits + ) - if value is None: - return None + def get_prep_value(self, value): + if isinstance(value, basestring): + return int(Version(value, self.number_bits)) - return int(value) + if value is None: + return None - def formfield(self, **kwargs): - defaults = { - 'form_class': forms.VersionField, - 'number_bits': self.number_bits - } - defaults.update(kwargs) - return super(VersionField, self).formfield(**defaults) + return int(value) - def __unicode__(self, value): - return unicode(value) + def formfield(self, **kwargs): + defaults = { + 'form_class': forms.VersionField, + 'number_bits': self.number_bits + } + defaults.update(kwargs) + return super(VersionField, self).formfield(**defaults) + def __unicode__(self, value): + return unicode(value) try: - from south.modelsinspector import add_introspection_rules - rules = [ - ( - (VersionField,), - [], - { - "number_bits": ["number_bits", {"default": DEFAULT_NUMBER_BITS}], - }, - ) - ] - add_introspection_rules(rules, ["^versionfield"]) + from south.modelsinspector import add_introspection_rules + rules = [( + (VersionField,), + [], + { + "number_bits": ["number_bits", {"default": DEFAULT_NUMBER_BITS}], + }, + )] + add_introspection_rules(rules, ["^versionfield"]) except ImportError: - # looks like we aren't using south - pass + # looks like we aren't using south + pass diff --git a/versionfield/constants.py b/versionfield/constants.py index 6f44d59..e8ad689 100644 --- a/versionfield/constants.py +++ b/versionfield/constants.py @@ -1 +1 @@ -DEFAULT_NUMBER_BITS = (8,8,16) \ No newline at end of file +DEFAULT_NUMBER_BITS = (8, 8, 16) diff --git a/versionfield/forms.py b/versionfield/forms.py index a64be58..59950f7 100644 --- a/versionfield/forms.py +++ b/versionfield/forms.py @@ -1,21 +1,31 @@ +"""Custom form fields.""" + from django import forms -from version import Version -from constants import DEFAULT_NUMBER_BITS -from utils import convert_version_string_to_int + +from .version import Version +from .constants import DEFAULT_NUMBER_BITS +from .utils import convert_version_int_to_string + class VersionField(forms.IntegerField): - def __init__(self,number_bits=DEFAULT_NUMBER_BITS,**kwargs): - self.number_bits = number_bits - return super(VersionField, self).__init__(**kwargs) - def to_python(self,value): - """ - Verifies that value can be converted to a Version object - """ - if not value: - return None + """A form field dedicated to version numbers.""" + + def __init__(self, number_bits=DEFAULT_NUMBER_BITS, **kwargs): + self.number_bits = number_bits + return super(VersionField, self).__init__(**kwargs) + + def to_python(self, value): + """ + Verifies that value can be converted to a Version object + """ + if not value: + return None - if isinstance(value,basestring): - return Version(value,self.number_bits) + if isinstance(value, basestring): + return Version(value, self.number_bits) - return Version(convert_version_int_to_string(value,self.number_bits),self.number_bits) \ No newline at end of file + return Version( + convert_version_int_to_string(value, self.number_bits), + self.number_bits + ) diff --git a/versionfield/tests/__init__.py b/versionfield/tests/__init__.py index 749c586..f0df3f8 100644 --- a/versionfield/tests/__init__.py +++ b/versionfield/tests/__init__.py @@ -2,108 +2,114 @@ from django.test import TestCase from django.db import models + from .. import VersionField -from versionfield.constants import DEFAULT_NUMBER_BITS -from versionfield.version import Version +from ..constants import DEFAULT_NUMBER_BITS +from ..version import Version + class DummyModel(models.Model): - version = VersionField() + version = VersionField() + class VersionFieldTest(TestCase): - def setUp(self): - DummyModel.objects.create(version="0.1") - DummyModel.objects.create(version="1.0") - DummyModel.objects.create(version="1.0.1") - def test_get_by_exact_version(self): - thing = DummyModel.objects.get(version="0.1") - self.assertEqual(thing.version,"0.1") - self.assertEqual(thing.version,"0.1.0") + def setUp(self): + DummyModel.objects.create(version="0.1") + DummyModel.objects.create(version="1.0") + DummyModel.objects.create(version="1.0.1") - def test_filter_by_greater_than_version(self): - things = DummyModel.objects.filter(version__gt="0.1") - self.assertEqual(len(things),2) + def test_get_by_exact_version(self): + thing = DummyModel.objects.get(version="0.1") + self.assertEqual(thing.version,"0.1") + self.assertEqual(thing.version,"0.1.0") - things = DummyModel.objects.filter(version__gt="1.0") - self.assertEqual(len(things),1) + def test_filter_by_greater_than_version(self): + things = DummyModel.objects.filter(version__gt="0.1") + self.assertEqual(len(things),2) - things = DummyModel.objects.filter(version__gt="1.0.1") - self.assertEqual(len(things),0) + things = DummyModel.objects.filter(version__gt="1.0") + self.assertEqual(len(things),1) - def test_filter_by_less_than_version(self): - things = DummyModel.objects.filter(version__lt="0.1") - self.assertEqual(len(things),0) + things = DummyModel.objects.filter(version__gt="1.0.1") + self.assertEqual(len(things),0) - things = DummyModel.objects.filter(version__lt="1.0") - self.assertEqual(len(things),1) + def test_filter_by_less_than_version(self): + things = DummyModel.objects.filter(version__lt="0.1") + self.assertEqual(len(things),0) - things = DummyModel.objects.filter(version__lt="1.0.1") - self.assertEqual(len(things),2) + things = DummyModel.objects.filter(version__lt="1.0") + self.assertEqual(len(things),1) + + things = DummyModel.objects.filter(version__lt="1.0.1") + self.assertEqual(len(things),2) + + def test_overflow_number(self): + error_occured = False + try: + overflow = DummyModel.objects.create(version="1.999.1") + except ValueError: + error_occured = True + self.assertTrue(error_occured) - def test_overflow_number(self): - error_occured = False - try: - overflow = DummyModel.objects.create(version="1.999.1") - except ValueError: - error_occured = True - self.assertTrue(error_occured) class DummyModelCustomBit(models.Model): - version = VersionField(number_bits=(8,16,8)) + version = VersionField(number_bits=(8, 16, 8)) + class VersionFieldCustomBitsTest(TestCase): - def setUp(self): - DummyModelCustomBit.objects.create(version="1.999.1") + def setUp(self): + DummyModelCustomBit.objects.create(version="1.999.1") - def test_get_by_exact_version(self): - thing = DummyModelCustomBit.objects.get(version="1.999.1") - self.assertEqual(thing.version,"1.999.1") + def test_get_by_exact_version(self): + thing = DummyModelCustomBit.objects.get(version="1.999.1") + self.assertEqual(thing.version, "1.999.1") class VersionObjectTestCase(unittest.TestCase): - def test_equal_operator(self): - self.assertEqual( - Version("1.2.3", DEFAULT_NUMBER_BITS), - Version("1.2.3", DEFAULT_NUMBER_BITS), - ) - - def test_lt_operator(self): - self.assertTrue( - Version("1.2.3", DEFAULT_NUMBER_BITS) < - Version("1.2.4", DEFAULT_NUMBER_BITS) - ) - self.assertFalse( - Version("1.2.4", DEFAULT_NUMBER_BITS) < - Version("1.2.3", DEFAULT_NUMBER_BITS) - ) - - def test_le_operator(self): - self.assertTrue( - Version("1.2.3", DEFAULT_NUMBER_BITS) <= - Version("1.2.4", DEFAULT_NUMBER_BITS) - ) - self.assertTrue( - Version("1.2.4", DEFAULT_NUMBER_BITS) <= - Version("1.2.4", DEFAULT_NUMBER_BITS) - ) - - def test_gt_operator(self): - self.assertTrue( - Version("1.2.4", DEFAULT_NUMBER_BITS) > - Version("1.2.3", DEFAULT_NUMBER_BITS) - ) - self.assertFalse( - Version("2.3.4", DEFAULT_NUMBER_BITS) > - Version("3.2.1", DEFAULT_NUMBER_BITS) - ) - - def test_ge_operator(self): - self.assertTrue( - Version("1.2.4", DEFAULT_NUMBER_BITS) >= - Version("1.2.3", DEFAULT_NUMBER_BITS) - ) - self.assertTrue( - Version("3.3.3", DEFAULT_NUMBER_BITS) <= - Version("3.3.3", DEFAULT_NUMBER_BITS) - ) + def test_equal_operator(self): + self.assertEqual( + Version("1.2.3", DEFAULT_NUMBER_BITS), + Version("1.2.3", DEFAULT_NUMBER_BITS), + ) + + def test_lt_operator(self): + self.assertTrue( + Version("1.2.3", DEFAULT_NUMBER_BITS) < + Version("1.2.4", DEFAULT_NUMBER_BITS) + ) + self.assertFalse( + Version("1.2.4", DEFAULT_NUMBER_BITS) < + Version("1.2.3", DEFAULT_NUMBER_BITS) + ) + + def test_le_operator(self): + self.assertTrue( + Version("1.2.3", DEFAULT_NUMBER_BITS) <= + Version("1.2.4", DEFAULT_NUMBER_BITS) + ) + self.assertTrue( + Version("1.2.4", DEFAULT_NUMBER_BITS) <= + Version("1.2.4", DEFAULT_NUMBER_BITS) + ) + + def test_gt_operator(self): + self.assertTrue( + Version("1.2.4", DEFAULT_NUMBER_BITS) > + Version("1.2.3", DEFAULT_NUMBER_BITS) + ) + self.assertFalse( + Version("2.3.4", DEFAULT_NUMBER_BITS) > + Version("3.2.1", DEFAULT_NUMBER_BITS) + ) + + def test_ge_operator(self): + self.assertTrue( + Version("1.2.4", DEFAULT_NUMBER_BITS) >= + Version("1.2.3", DEFAULT_NUMBER_BITS) + ) + self.assertTrue( + Version("3.3.3", DEFAULT_NUMBER_BITS) <= + Version("3.3.3", DEFAULT_NUMBER_BITS) + ) diff --git a/versionfield/utils.py b/versionfield/utils.py index 52332df..d30c54f 100644 --- a/versionfield/utils.py +++ b/versionfield/utils.py @@ -1,50 +1,61 @@ -def convert_version_string_to_int(string,number_bits): - """ - Take in a verison string e.g. '3.0.1' - Store it as a converted int: 3*(2**number_bits[0])+0*(2**number_bits[1])+1*(2**number_bits[2]) - - >>> convert_version_string_to_int('3.0.1',[8,8,16]) - 50331649 - """ - numbers = [int(number_string) for number_string in string.split(".")] - - if len(numbers) > len(number_bits): - raise NotImplementedError("Versions with more than {0} decimal places are not supported".format(len(number_bits)-1)) - - #add 0s for missing numbers - numbers.extend([0]*(len(number_bits) - len(numbers))) - - #convert to single int and return - number = 0 - total_bits = 0 - for num,bits in reversed(zip(numbers,number_bits)): - max_num = (bits+1)-1 - if num >= 1 << max_num: - raise ValueError("Number {0} cannot be stored with only {1} bits. Max is {2}".format(num,bits,max_num)) - number += num << total_bits - total_bits += bits - - return number - -def convert_version_int_to_string(number,number_bits): - """ - Take in a verison string e.g. '3.0.1' - Store it as a converted int: 3*(2**number_bits[0])+0*(2**number_bits[1])+1*(2**number_bits[2]) - - >>> convert_version_int_to_string(50331649,[8,8,16]) - '3.0.1' - """ - number_strings = [] - total_bits = sum(number_bits) - for bits in number_bits: - shift_amount = (total_bits-bits) - number_segment = number >> shift_amount - number_strings.append(str(number_segment)) - total_bits = total_bits - bits - number = number - (number_segment << shift_amount) - - return ".".join(number_strings) +"""Conversion functions.""" + + +def convert_version_string_to_int(string, number_bits): + """ + Take in a verison string e.g. '3.0.1' + Store it as a converted int: + 3 * (2**number_bits[0]) + 0 * (2**number_bits[1]) + 1 * (2**number_bits[2]) + + >>> convert_version_string_to_int('3.0.1',[8,8,16]) + 50331649 + """ + numbers = [int(number_string) for number_string in string.split(".")] + + if len(numbers) > len(number_bits): + raise NotImplementedError( + "Versions with more than {0} decimal places are not supported" + .format(len(number_bits) - 1) + ) + + #add 0s for missing numbers + numbers.extend([0] * (len(number_bits) - len(numbers))) + + #convert to single int and return + number = 0 + total_bits = 0 + for num, bits in reversed(zip(numbers, number_bits)): + max_num = (bits + 1) - 1 + if num >= 1 << max_num: + raise ValueError( + "Number {0} cannot be stored with only {1} bits. Max is {2}" + .format(num, bits, max_num) + ) + number += num << total_bits + total_bits += bits + + return number + + +def convert_version_int_to_string(number, number_bits): + """ + Take in a verison string e.g. '3.0.1' + Store it as a converted int: + 3 * ( 2**number_bits[0]) + 0 * (2**number_bits[1]) + 1 * (2**number_bits[2]) + + >>> convert_version_int_to_string(50331649,[8,8,16]) + '3.0.1' + """ + number_strings = [] + total_bits = sum(number_bits) + for bits in number_bits: + shift_amount = (total_bits - bits) + number_segment = number >> shift_amount + number_strings.append(str(number_segment)) + total_bits = total_bits - bits + number = number - (number_segment << shift_amount) + return ".".join(number_strings) if __name__ == "__main__": - import doctest - doctest.testmod() \ No newline at end of file + import doctest + doctest.testmod() From b70bce7abc913da6bd7e81d6bc848b38b47120e4 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Sun, 21 Dec 2014 14:32:29 +0100 Subject: [PATCH 03/21] Change package name to django-versionfield2. --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 19bdb09..124ecef 100644 --- a/setup.py +++ b/setup.py @@ -11,9 +11,9 @@ import os setup( - name = "django-versionfield", - version = "0.3.2", - url = 'https://github.com/mindsnacks/django-versionfield', + name = "django-versionfield2", + version = "0.3.3", + url = 'https://github.com/tonioo/django-versionfield', license = 'BSD', description = "A DB Independent Custom Django Field for storing Version numbers for fast indexing", author = 'Tom Hoddes', From 6f3e987504909a1f4ef025b6c9aca5889a6ff441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Domen=20Blenku=C5=A1?= Date: Mon, 5 Oct 2015 13:50:24 +0200 Subject: [PATCH 04/21] Translated tabs to spaces and fixed indentation. --- versionfield/version.py | 112 ++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/versionfield/version.py b/versionfield/version.py index 7e29fd7..18ea331 100644 --- a/versionfield/version.py +++ b/versionfield/version.py @@ -3,59 +3,59 @@ class Version(object): - def __init__(self, string, number_bits): - """ - Take in a verison string e.g. '3.0.1' - Store it as a converted int - """ - self.number_bits = number_bits - self.internal_integer = convert_version_string_to_int( - string, number_bits) - - def __unicode__(self): - return unicode( - convert_version_int_to_string(self.internal_integer, self.number_bits)) - - def __str__(self): - return self.__unicode__() - - def __repr__(self): - return self.__unicode__() - - def __int__(self): - return self.internal_integer - - def __eq__(self, other): - if not other: - return False # we are obviously a valid Version, but 'other' isn't - if isinstance(other, basestring): - other = Version(other, self.number_bits) - return int(self) == int(other) - - def __lt__(self, other): - if not other: - return False - if isinstance(other, basestring): - other = Version(other, self.number_bits) - return int(self) < int(other) - - def __le__(self, other): - if not other: - return False - if isinstance(other, basestring): - other = Version(other, self.number_bits) - return int(self) <= int(other) - - def __gt__(self, other): - if not other: - return False - if isinstance(other, basestring): - other = Version(other, self.number_bits) - return int(self) > int(other) - - def __ge__(self, other): - if not other: - return False - if isinstance(other, basestring): - other = Version(other, self.number_bits) - return int(self) >= int(other) + def __init__(self, string, number_bits): + """ + Take in a verison string e.g. '3.0.1' + Store it as a converted int + """ + self.number_bits = number_bits + self.internal_integer = convert_version_string_to_int( + string, number_bits) + + def __unicode__(self): + return unicode( + convert_version_int_to_string(self.internal_integer, self.number_bits)) + + def __str__(self): + return self.__unicode__() + + def __repr__(self): + return self.__unicode__() + + def __int__(self): + return self.internal_integer + + def __eq__(self, other): + if not other: + return False # we are obviously a valid Version, but 'other' isn't + if isinstance(other, basestring): + other = Version(other, self.number_bits) + return int(self) == int(other) + + def __lt__(self, other): + if not other: + return False + if isinstance(other, basestring): + other = Version(other, self.number_bits) + return int(self) < int(other) + + def __le__(self, other): + if not other: + return False + if isinstance(other, basestring): + other = Version(other, self.number_bits) + return int(self) <= int(other) + + def __gt__(self, other): + if not other: + return False + if isinstance(other, basestring): + other = Version(other, self.number_bits) + return int(self) > int(other) + + def __ge__(self, other): + if not other: + return False + if isinstance(other, basestring): + other = Version(other, self.number_bits) + return int(self) >= int(other) From 380436aede8ce240a3700d498bef015760022969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Domen=20Blenku=C5=A1?= Date: Mon, 5 Oct 2015 13:52:08 +0200 Subject: [PATCH 05/21] Removed not used imports. --- versionfield/__init__.py | 2 +- versionfield/version.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/versionfield/__init__.py b/versionfield/__init__.py index 3c17c19..363e44d 100644 --- a/versionfield/__init__.py +++ b/versionfield/__init__.py @@ -3,7 +3,7 @@ from . import forms from .constants import DEFAULT_NUMBER_BITS from .version import Version -from .utils import convert_version_string_to_int, convert_version_int_to_string +from .utils import convert_version_int_to_string class VersionField(models.PositiveIntegerField): diff --git a/versionfield/version.py b/versionfield/version.py index 18ea331..8ce721d 100644 --- a/versionfield/version.py +++ b/versionfield/version.py @@ -1,5 +1,4 @@ from .utils import convert_version_string_to_int, convert_version_int_to_string -from .constants import DEFAULT_NUMBER_BITS class Version(object): From 8ea0e39eb31fdfc7d23ef2603bc269270fd5aa9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Domen=20Blenku=C5=A1?= Date: Mon, 5 Oct 2015 14:00:48 +0200 Subject: [PATCH 06/21] Fixed pylint errors. --- setup.py | 20 +++++++++----------- versionfield/tests/__init__.py | 18 +++++++++--------- versionfield/utils.py | 6 +++--- versionfield/version.py | 6 +++--- 4 files changed, 24 insertions(+), 26 deletions(-) diff --git a/setup.py b/setup.py index 124ecef..a659e04 100644 --- a/setup.py +++ b/setup.py @@ -8,18 +8,16 @@ ez_setup.use_setuptools() from setuptools import setup, find_packages -import os - setup( - name = "django-versionfield2", - version = "0.3.3", - url = 'https://github.com/tonioo/django-versionfield', - license = 'BSD', - description = "A DB Independent Custom Django Field for storing Version numbers for fast indexing", - author = 'Tom Hoddes', - packages = find_packages(), - include_package_data = True, - classifiers = [ + name="django-versionfield2", + version="0.3.3", + url='https://github.com/tonioo/django-versionfield', + license='BSD', + description="A DB Independent Custom Django Field for storing Version numbers for fast indexing", + author='Tom Hoddes', + packages=find_packages(), + include_package_data=True, + classifiers=[ 'Development Status :: 3 - Alpha', 'Framework :: Django', 'Intended Audience :: Developers', diff --git a/versionfield/tests/__init__.py b/versionfield/tests/__init__.py index f0df3f8..d4cd8ef 100644 --- a/versionfield/tests/__init__.py +++ b/versionfield/tests/__init__.py @@ -21,33 +21,33 @@ def setUp(self): def test_get_by_exact_version(self): thing = DummyModel.objects.get(version="0.1") - self.assertEqual(thing.version,"0.1") - self.assertEqual(thing.version,"0.1.0") + self.assertEqual(thing.version, "0.1") + self.assertEqual(thing.version, "0.1.0") def test_filter_by_greater_than_version(self): things = DummyModel.objects.filter(version__gt="0.1") - self.assertEqual(len(things),2) + self.assertEqual(len(things), 2) things = DummyModel.objects.filter(version__gt="1.0") - self.assertEqual(len(things),1) + self.assertEqual(len(things), 1) things = DummyModel.objects.filter(version__gt="1.0.1") - self.assertEqual(len(things),0) + self.assertEqual(len(things), 0) def test_filter_by_less_than_version(self): things = DummyModel.objects.filter(version__lt="0.1") - self.assertEqual(len(things),0) + self.assertEqual(len(things), 0) things = DummyModel.objects.filter(version__lt="1.0") - self.assertEqual(len(things),1) + self.assertEqual(len(things), 1) things = DummyModel.objects.filter(version__lt="1.0.1") - self.assertEqual(len(things),2) + self.assertEqual(len(things), 2) def test_overflow_number(self): error_occured = False try: - overflow = DummyModel.objects.create(version="1.999.1") + DummyModel.objects.create(version="1.999.1") except ValueError: error_occured = True self.assertTrue(error_occured) diff --git a/versionfield/utils.py b/versionfield/utils.py index d30c54f..f8ea4e8 100644 --- a/versionfield/utils.py +++ b/versionfield/utils.py @@ -18,10 +18,10 @@ def convert_version_string_to_int(string, number_bits): .format(len(number_bits) - 1) ) - #add 0s for missing numbers + # add 0s for missing numbers numbers.extend([0] * (len(number_bits) - len(numbers))) - #convert to single int and return + # convert to single int and return number = 0 total_bits = 0 for num, bits in reversed(zip(numbers, number_bits)): @@ -41,7 +41,7 @@ def convert_version_int_to_string(number, number_bits): """ Take in a verison string e.g. '3.0.1' Store it as a converted int: - 3 * ( 2**number_bits[0]) + 0 * (2**number_bits[1]) + 1 * (2**number_bits[2]) + 3 * (2**number_bits[0]) + 0 * (2**number_bits[1]) + 1 * (2**number_bits[2]) >>> convert_version_int_to_string(50331649,[8,8,16]) '3.0.1' diff --git a/versionfield/version.py b/versionfield/version.py index 8ce721d..c3a82fb 100644 --- a/versionfield/version.py +++ b/versionfield/version.py @@ -12,8 +12,8 @@ def __init__(self, string, number_bits): string, number_bits) def __unicode__(self): - return unicode( - convert_version_int_to_string(self.internal_integer, self.number_bits)) + return unicode(convert_version_int_to_string( + self.internal_integer, self.number_bits)) def __str__(self): return self.__unicode__() @@ -26,7 +26,7 @@ def __int__(self): def __eq__(self, other): if not other: - return False # we are obviously a valid Version, but 'other' isn't + return False # we are obviously a valid Version, but 'other' isn't if isinstance(other, basestring): other = Version(other, self.number_bits) return int(self) == int(other) From b9786595546cd3b2dbace40cccd651b6553b1021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Domen=20Blenku=C5=A1?= Date: Mon, 5 Oct 2015 22:10:24 +0200 Subject: [PATCH 07/21] Moved tests from tests/__init__.py to tests.py. --- versionfield/{tests/__init__.py => tests.py} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename versionfield/{tests/__init__.py => tests.py} (97%) diff --git a/versionfield/tests/__init__.py b/versionfield/tests.py similarity index 97% rename from versionfield/tests/__init__.py rename to versionfield/tests.py index d4cd8ef..fe76ba3 100644 --- a/versionfield/tests/__init__.py +++ b/versionfield/tests.py @@ -3,9 +3,9 @@ from django.test import TestCase from django.db import models -from .. import VersionField -from ..constants import DEFAULT_NUMBER_BITS -from ..version import Version +from . import VersionField +from .constants import DEFAULT_NUMBER_BITS +from .version import Version class DummyModel(models.Model): From f930fc6eb40456294ff91384bbeb8b37e7cb89a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Domen=20Blenku=C5=A1?= Date: Mon, 5 Oct 2015 22:35:53 +0200 Subject: [PATCH 08/21] Added files generated by setup.py to gitignore. --- .gitignore | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 26209e9..6b7a409 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,8 @@ .DS_Store -*.pyc \ No newline at end of file +*.pyc + +# Setuptools distribution folder. +/dist/ + +# Python egg metadata, regenerated from source files by setuptools. +/*.egg-info From fb117c4fcbf4d6ca4261c08663fb8011d13358d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Domen=20Blenku=C5=A1?= Date: Mon, 5 Oct 2015 22:36:09 +0200 Subject: [PATCH 09/21] Python3 support. --- setup.py | 3 +++ versionfield/__init__.py | 17 +++++++++++------ versionfield/forms.py | 4 +++- versionfield/tests.py | 2 ++ versionfield/utils.py | 7 +++++-- versionfield/version.py | 24 +++++++++++++----------- 6 files changed, 37 insertions(+), 20 deletions(-) diff --git a/setup.py b/setup.py index a659e04..3e79794 100644 --- a/setup.py +++ b/setup.py @@ -17,6 +17,9 @@ author='Tom Hoddes', packages=find_packages(), include_package_data=True, + install_requires=[ + 'six>=1.9.0', + ], classifiers=[ 'Development Status :: 3 - Alpha', 'Framework :: Django', diff --git a/versionfield/__init__.py b/versionfield/__init__.py index 363e44d..5e72043 100644 --- a/versionfield/__init__.py +++ b/versionfield/__init__.py @@ -1,4 +1,9 @@ +from __future__ import unicode_literals + +import six + from django.db import models +from django.utils.encoding import python_2_unicode_compatible from . import forms from .constants import DEFAULT_NUMBER_BITS @@ -6,6 +11,8 @@ from .utils import convert_version_int_to_string +@python_2_unicode_compatible +@six.add_metaclass(models.SubfieldBase) class VersionField(models.PositiveIntegerField): """ @@ -15,8 +22,6 @@ class VersionField(models.PositiveIntegerField): description = "A version number (e.g. 3.0.1)" - __metaclass__ = models.SubfieldBase - def __init__(self, number_bits=DEFAULT_NUMBER_BITS, *args, **kwargs): self.number_bits = number_bits super(VersionField, self).__init__(*args, **kwargs) @@ -25,7 +30,7 @@ def to_python(self, value): if isinstance(value, Version): return value - if isinstance(value, basestring): + if isinstance(value, six.string_types): return Version(value, self.number_bits) if value is None: @@ -37,7 +42,7 @@ def to_python(self, value): ) def get_prep_value(self, value): - if isinstance(value, basestring): + if isinstance(value, six.string_types): return int(Version(value, self.number_bits)) if value is None: @@ -53,8 +58,8 @@ def formfield(self, **kwargs): defaults.update(kwargs) return super(VersionField, self).formfield(**defaults) - def __unicode__(self, value): - return unicode(value) + def __str__(self, value): + return six.text_type(value) try: diff --git a/versionfield/forms.py b/versionfield/forms.py index 59950f7..cb85782 100644 --- a/versionfield/forms.py +++ b/versionfield/forms.py @@ -1,5 +1,7 @@ """Custom form fields.""" +import six + from django import forms from .version import Version @@ -22,7 +24,7 @@ def to_python(self, value): if not value: return None - if isinstance(value, basestring): + if isinstance(value, six.string_types): return Version(value, self.number_bits) return Version( diff --git a/versionfield/tests.py b/versionfield/tests.py index fe76ba3..c7ea1ec 100644 --- a/versionfield/tests.py +++ b/versionfield/tests.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import unittest from django.test import TestCase diff --git a/versionfield/utils.py b/versionfield/utils.py index f8ea4e8..16162e0 100644 --- a/versionfield/utils.py +++ b/versionfield/utils.py @@ -1,6 +1,9 @@ """Conversion functions.""" +import six + + def convert_version_string_to_int(string, number_bits): """ Take in a verison string e.g. '3.0.1' @@ -24,7 +27,7 @@ def convert_version_string_to_int(string, number_bits): # convert to single int and return number = 0 total_bits = 0 - for num, bits in reversed(zip(numbers, number_bits)): + for num, bits in reversed(list(zip(numbers, number_bits))): max_num = (bits + 1) - 1 if num >= 1 << max_num: raise ValueError( @@ -51,7 +54,7 @@ def convert_version_int_to_string(number, number_bits): for bits in number_bits: shift_amount = (total_bits - bits) number_segment = number >> shift_amount - number_strings.append(str(number_segment)) + number_strings.append(six.text_type(number_segment)) total_bits = total_bits - bits number = number - (number_segment << shift_amount) return ".".join(number_strings) diff --git a/versionfield/version.py b/versionfield/version.py index c3a82fb..e0e2da4 100644 --- a/versionfield/version.py +++ b/versionfield/version.py @@ -1,6 +1,11 @@ +import six + +from django.utils.encoding import python_2_unicode_compatible + from .utils import convert_version_string_to_int, convert_version_int_to_string +@python_2_unicode_compatible class Version(object): def __init__(self, string, number_bits): """ @@ -11,15 +16,12 @@ def __init__(self, string, number_bits): self.internal_integer = convert_version_string_to_int( string, number_bits) - def __unicode__(self): - return unicode(convert_version_int_to_string( - self.internal_integer, self.number_bits)) - def __str__(self): - return self.__unicode__() + return six.text_type(convert_version_int_to_string( + self.internal_integer, self.number_bits)) def __repr__(self): - return self.__unicode__() + return self.__str__() def __int__(self): return self.internal_integer @@ -27,34 +29,34 @@ def __int__(self): def __eq__(self, other): if not other: return False # we are obviously a valid Version, but 'other' isn't - if isinstance(other, basestring): + if isinstance(other, six.string_types): other = Version(other, self.number_bits) return int(self) == int(other) def __lt__(self, other): if not other: return False - if isinstance(other, basestring): + if isinstance(other, six.string_types): other = Version(other, self.number_bits) return int(self) < int(other) def __le__(self, other): if not other: return False - if isinstance(other, basestring): + if isinstance(other, six.string_types): other = Version(other, self.number_bits) return int(self) <= int(other) def __gt__(self, other): if not other: return False - if isinstance(other, basestring): + if isinstance(other, six.string_types): other = Version(other, self.number_bits) return int(self) > int(other) def __ge__(self, other): if not other: return False - if isinstance(other, basestring): + if isinstance(other, six.string_types): other = Version(other, self.number_bits) return int(self) >= int(other) From 6238e0a96b6ed5f2823d990461e2951fc1a06898 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 7 Oct 2015 09:29:53 +0200 Subject: [PATCH 10/21] Release 0.4 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3e79794..cff6f3b 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ setup( name="django-versionfield2", - version="0.3.3", + version="0.4", url='https://github.com/tonioo/django-versionfield', license='BSD', description="A DB Independent Custom Django Field for storing Version numbers for fast indexing", From 07ba2c898933b809ffa92097e9b38e9bd7e29f10 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 7 Oct 2015 09:31:56 +0200 Subject: [PATCH 11/21] Release 0.4.0 --- setup.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index cff6f3b..187f5b5 100644 --- a/setup.py +++ b/setup.py @@ -10,11 +10,12 @@ setup( name="django-versionfield2", - version="0.4", + version="0.4.0", url='https://github.com/tonioo/django-versionfield', license='BSD', description="A DB Independent Custom Django Field for storing Version numbers for fast indexing", - author='Tom Hoddes', + author='Antoine Nguyen', + author_email='tonio@ngyn.org', packages=find_packages(), include_package_data=True, install_requires=[ From 53e7d682ecc96a7327d27b3003808f116db251bb Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Tue, 14 Jun 2016 19:58:17 +0200 Subject: [PATCH 12/21] Django 1.9 compat. --- versionfield/__init__.py | 21 ++++++++++++++++----- versionfield/forms.py | 6 ++---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/versionfield/__init__.py b/versionfield/__init__.py index 5e72043..ce0d739 100644 --- a/versionfield/__init__.py +++ b/versionfield/__init__.py @@ -12,8 +12,7 @@ @python_2_unicode_compatible -@six.add_metaclass(models.SubfieldBase) -class VersionField(models.PositiveIntegerField): +class VersionField(models.Field): """ A Field where version numbers are input/output as strings (e.g. 3.0.1) @@ -26,21 +25,33 @@ def __init__(self, number_bits=DEFAULT_NUMBER_BITS, *args, **kwargs): self.number_bits = number_bits super(VersionField, self).__init__(*args, **kwargs) + def db_type(self, connection): + """Use integer as internal representation.""" + return "integer" + def to_python(self, value): + if not value: + return None + if isinstance(value, Version): return value if isinstance(value, six.string_types): return Version(value, self.number_bits) - if value is None: - return None - return Version( convert_version_int_to_string(value, self.number_bits), self.number_bits ) + def from_db_value(self, value, expression, connection, context): + """Convert data from database.""" + if value is None: + return value + return Version( + convert_version_int_to_string(value, self.number_bits), + self.number_bits) + def get_prep_value(self, value): if isinstance(value, six.string_types): return int(Version(value, self.number_bits)) diff --git a/versionfield/forms.py b/versionfield/forms.py index cb85782..1388b92 100644 --- a/versionfield/forms.py +++ b/versionfield/forms.py @@ -9,7 +9,7 @@ from .utils import convert_version_int_to_string -class VersionField(forms.IntegerField): +class VersionField(forms.CharField): """A form field dedicated to version numbers.""" @@ -18,9 +18,7 @@ def __init__(self, number_bits=DEFAULT_NUMBER_BITS, **kwargs): return super(VersionField, self).__init__(**kwargs) def to_python(self, value): - """ - Verifies that value can be converted to a Version object - """ + """Verifies that value can be converted to a Version object.""" if not value: return None From 1b0f88e74c616ecf302563f20d11b7af69fc4325 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 15 Jun 2016 15:03:57 +0200 Subject: [PATCH 13/21] Release 0.5.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 187f5b5..4df3d8e 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ setup( name="django-versionfield2", - version="0.4.0", + version="0.5.0", url='https://github.com/tonioo/django-versionfield', license='BSD', description="A DB Independent Custom Django Field for storing Version numbers for fast indexing", From e76a120a76c721f272665a37d288504cdba650e9 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Thu, 5 Jan 2017 21:50:44 +0500 Subject: [PATCH 14/21] Implement validation for the field. The validation method now checks the following: * If version string has too many components (1.2.3.4.5 with only 3 components allowed); * If any component is not numeric (1.x is not supported due to storage implementation); * If any component is too big (one can't save number greater than 255 with 8 bits, for example). Raising ValidationError in validate() allows us to avoid ValueError later. --- versionfield/forms.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/versionfield/forms.py b/versionfield/forms.py index 1388b92..67309d3 100644 --- a/versionfield/forms.py +++ b/versionfield/forms.py @@ -17,11 +17,35 @@ def __init__(self, number_bits=DEFAULT_NUMBER_BITS, **kwargs): self.number_bits = number_bits return super(VersionField, self).__init__(**kwargs) + def check_format(self, string): + """Check that value contains no more than N decimal numbers.""" + parts = string.split(".") + actual_len = len(parts) + allowed_len = len(self.number_bits) + if actual_len > allowed_len: + raise forms.ValidationError("Version has %(actual)d components; only %(allowed)d components are allowed", + code = 'too_long_version', + params = dict(actual=actual_len, allowed=allowed_len)) + for i, (part, bits) in enumerate(zip(parts, self.number_bits), 1): + if not part.isdigit(): + raise forms.ValidationError("Version's %(index)d component (%(part)s) is not numeric; only numeric values are allowed", + code = 'not_numeric_version', + params = dict(index=i, part=part)) + num = int(part) + max_allowed = (1 << bits) - 1 + if num > max_allowed: + raise forms.ValidationError("Version's %(index)d component (%(part)s) is too big; maximum allowed value for this component is %(allowed)d", + code = 'version_component_too_big', + params = dict(index=i, part=part, allowed=max_allowed)) + + def to_python(self, value): """Verifies that value can be converted to a Version object.""" if not value: return None + self.check_format(value) + if isinstance(value, six.string_types): return Version(value, self.number_bits) @@ -29,3 +53,4 @@ def to_python(self, value): convert_version_int_to_string(value, self.number_bits), self.number_bits ) + From 8dc3735e26ddd0f0928d251937d5c8f64ce98c42 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 22 Jan 2017 15:42:58 +0500 Subject: [PATCH 15/21] Add unit tests for validation. --- versionfield/tests.py | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/versionfield/tests.py b/versionfield/tests.py index c7ea1ec..0bad4ac 100644 --- a/versionfield/tests.py +++ b/versionfield/tests.py @@ -4,10 +4,12 @@ from django.test import TestCase from django.db import models +from django.forms import ValidationError from . import VersionField from .constants import DEFAULT_NUMBER_BITS from .version import Version +from . import forms class DummyModel(models.Model): @@ -54,6 +56,49 @@ def test_overflow_number(self): error_occured = True self.assertTrue(error_occured) + def test_validate_positive(self): + field = forms.VersionField() + field.check_format("10.11.12") + + def test_validate_too_long(self): + error_occured = False + correct_error = False + field = forms.VersionField() + try: + field.check_format("10.11.12.13") + except ValidationError as e: + error_occured = True + if e.code == "too_long_version": + correct_error = True + self.assertTrue(error_occured) + self.assertTrue(correct_error) + + def test_validate_not_numeric(self): + error_occured = False + correct_error = False + field = forms.VersionField() + try: + field.check_format("10.x.1") + except ValidationError as e: + error_occured = True + if e.code == "not_numeric_version": + correct_error = True + self.assertTrue(error_occured) + self.assertTrue(correct_error) + + def test_validate_too_big(self): + error_occured = False + correct_error = False + field = forms.VersionField() + try: + field.check_format("10.999.1") + except ValidationError as e: + error_occured = True + if e.code == "version_component_too_big": + correct_error = True + self.assertTrue(error_occured) + self.assertTrue(correct_error) + class DummyModelCustomBit(models.Model): version = VersionField(number_bits=(8, 16, 8)) From 0bd7b25a09d34148cbf8b910a0a5a82ddc7585f9 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Mon, 23 Jan 2017 14:42:02 +0100 Subject: [PATCH 16/21] Added CI instructions. --- .travis.yml | 14 +++ test_project/manage.py | 10 +++ test_project/test_project/__init__.py | 0 test_project/test_project/settings.py | 122 ++++++++++++++++++++++++++ test_project/test_project/urls.py | 21 +++++ test_project/test_project/wsgi.py | 16 ++++ 6 files changed, 183 insertions(+) create mode 100644 .travis.yml create mode 100755 test_project/manage.py create mode 100644 test_project/test_project/__init__.py create mode 100644 test_project/test_project/settings.py create mode 100644 test_project/test_project/urls.py create mode 100644 test_project/test_project/wsgi.py diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..09a53de --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +sudo: false +language: python +python: + - "2.7" + +before_install: + - pip install codecov + +script: + - cd test_project + - coverage run --source ../versionfield manage.py test versionfield + +after_success: + - codecov diff --git a/test_project/manage.py b/test_project/manage.py new file mode 100755 index 0000000..0fc36a3 --- /dev/null +++ b/test_project/manage.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/test_project/test_project/__init__.py b/test_project/test_project/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test_project/test_project/settings.py b/test_project/test_project/settings.py new file mode 100644 index 0000000..b60d23f --- /dev/null +++ b/test_project/test_project/settings.py @@ -0,0 +1,122 @@ +""" +Django settings for test_project project. + +Generated by 'django-admin startproject' using Django 1.9.7. + +For more information on this file, see +https://docs.djangoproject.com/en/1.9/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.9/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = '33%!r(6)*y1@f0=(2(!*fwoz5@=mi#$6&cc-ze@2#ws3(gx(5p' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'versionfield', +] + +MIDDLEWARE_CLASSES = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'test_project.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'test_project.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.9/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/1.9/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.9/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/test_project/test_project/urls.py b/test_project/test_project/urls.py new file mode 100644 index 0000000..b03753d --- /dev/null +++ b/test_project/test_project/urls.py @@ -0,0 +1,21 @@ +"""test_project URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.9/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.conf.urls import url, include + 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) +""" +from django.conf.urls import url +from django.contrib import admin + +urlpatterns = [ + url(r'^admin/', admin.site.urls), +] diff --git a/test_project/test_project/wsgi.py b/test_project/test_project/wsgi.py new file mode 100644 index 0000000..fc2e14e --- /dev/null +++ b/test_project/test_project/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for test_project project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings") + +application = get_wsgi_application() From 1be83ed4d7dce2a4d1d60bebba62c46e3c6e2af5 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Mon, 23 Jan 2017 14:44:18 +0100 Subject: [PATCH 17/21] Updated CI config. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 09a53de..bac0c58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ python: - "2.7" before_install: + - pip install Django<=1.10.99 - pip install codecov script: From 0036c48fc634e21ac42a21b86bea129b1667ec05 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Mon, 23 Jan 2017 14:46:35 +0100 Subject: [PATCH 18/21] Updated CI config. --- .travis.yml | 2 +- setup.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bac0c58..c644035 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,8 @@ python: - "2.7" before_install: - - pip install Django<=1.10.99 - pip install codecov + - python setup.py -q install script: - cd test_project diff --git a/setup.py b/setup.py index 4df3d8e..c01121d 100644 --- a/setup.py +++ b/setup.py @@ -19,6 +19,7 @@ packages=find_packages(), include_package_data=True, install_requires=[ + 'Django<=1.10.99', 'six>=1.9.0', ], classifiers=[ From d601f3819ca3e77bd4abac7e59073c65af746901 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Mon, 23 Jan 2017 14:50:07 +0100 Subject: [PATCH 19/21] Fixed CI config. --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c644035..82155de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,9 @@ python: before_install: - pip install codecov - - python setup.py -q install + +install: + - python setup.py -q develop script: - cd test_project From d634fc36b8c89b517fd60caa1b6a4df354050f3c Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Mon, 23 Jan 2017 14:54:19 +0100 Subject: [PATCH 20/21] Updated README. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index f1d6d13..db6efd2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ django-versionfield ==== +[![codecov](https://codecov.io/gh/tonioo/django-versionfield/branch/master/graph/badge.svg)](https://codecov.io/gh/tonioo/django-versionfield) + Usage: from versionfield import VersionField @@ -16,6 +18,7 @@ License django-versionfield is distributed under a BSD-style license. + Copyright (c) 2014-2017 Antoine Nguyen Copyright (c) 2011-2013 MindSnacks (http://mindsnacks.com/) Permission is hereby granted, free of charge, to any person obtaining a copy From 95626fd2d830b359e8d2e80388aed0d596076260 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Mon, 23 Jan 2017 15:05:40 +0100 Subject: [PATCH 21/21] Fixed pep8 warnings. --- README.md | 1 + versionfield/forms.py | 26 +++++++++++++++----------- versionfield/utils.py | 1 + 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index db6efd2..cef8340 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ django-versionfield ==== +[![Build Status](https://travis-ci.org/tonioo/django-versionfield.svg?branch=master)](https://travis-ci.org/tonioo/django-versionfield) [![codecov](https://codecov.io/gh/tonioo/django-versionfield/branch/master/graph/badge.svg)](https://codecov.io/gh/tonioo/django-versionfield) Usage: diff --git a/versionfield/forms.py b/versionfield/forms.py index 67309d3..cff57d5 100644 --- a/versionfield/forms.py +++ b/versionfield/forms.py @@ -23,21 +23,26 @@ def check_format(self, string): actual_len = len(parts) allowed_len = len(self.number_bits) if actual_len > allowed_len: - raise forms.ValidationError("Version has %(actual)d components; only %(allowed)d components are allowed", - code = 'too_long_version', - params = dict(actual=actual_len, allowed=allowed_len)) + raise forms.ValidationError( + "Version has %(actual)d components; only %(allowed)d " + "components are allowed", + code="too_long_version", + params=dict(actual=actual_len, allowed=allowed_len)) for i, (part, bits) in enumerate(zip(parts, self.number_bits), 1): if not part.isdigit(): - raise forms.ValidationError("Version's %(index)d component (%(part)s) is not numeric; only numeric values are allowed", - code = 'not_numeric_version', - params = dict(index=i, part=part)) + raise forms.ValidationError( + "Version's %(index)d component (%(part)s) is not numeric; " + "only numeric values are allowed", + code="not_numeric_version", + params=dict(index=i, part=part)) num = int(part) max_allowed = (1 << bits) - 1 if num > max_allowed: - raise forms.ValidationError("Version's %(index)d component (%(part)s) is too big; maximum allowed value for this component is %(allowed)d", - code = 'version_component_too_big', - params = dict(index=i, part=part, allowed=max_allowed)) - + raise forms.ValidationError( + "Version's %(index)d component (%(part)s) is too big; " + "maximum allowed value for this component is %(allowed)d", + code="version_component_too_big", + params=dict(index=i, part=part, allowed=max_allowed)) def to_python(self, value): """Verifies that value can be converted to a Version object.""" @@ -53,4 +58,3 @@ def to_python(self, value): convert_version_int_to_string(value, self.number_bits), self.number_bits ) - diff --git a/versionfield/utils.py b/versionfield/utils.py index 16162e0..5dd1f4d 100644 --- a/versionfield/utils.py +++ b/versionfield/utils.py @@ -59,6 +59,7 @@ def convert_version_int_to_string(number, number_bits): number = number - (number_segment << shift_amount) return ".".join(number_strings) + if __name__ == "__main__": import doctest doctest.testmod()