From 18e1f56a418290d358c5338b2d97d91256f6df5f Mon Sep 17 00:00:00 2001 From: Malcolm Tredinnick Date: Sat, 20 Oct 2007 13:40:20 +0000 Subject: [PATCH] Fixed #5475 -- Added the Luhn check algorithm to django.utils.checksums so that localflavors don't have to reimplement it each time. Thanks, __hawkeye__. git-svn-id: http://code.djangoproject.com/svn/django/trunk@6569 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/utils/checksums.py | 22 ++++++++++++++++++++++ tests/regressiontests/utils/tests.py | 28 +++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 django/utils/checksums.py diff --git a/django/utils/checksums.py b/django/utils/checksums.py new file mode 100644 index 0000000000..970f563f38 --- /dev/null +++ b/django/utils/checksums.py @@ -0,0 +1,22 @@ +""" +Common checksum routines (used in multiple localflavor/ cases, for example). +""" + +__all__ = ['luhn',] + +LUHN_ODD_LOOKUP = (0, 2, 4, 6, 8, 1, 3, 5, 7, 9) # sum_of_digits(index * 2) + +def luhn(candidate): + """ + Checks a candidate number for validity according to the Luhn + algorithm (used in validation of, for example, credit cards). + Both numeric and string candidates are accepted. + """ + if not isinstance(candidate, basestring): + candidate = str(candidate) + try: + evens = sum([int(c) for c in candidate[-1::-2]]) + odds = sum([LUHN_ODD_LOOKUP[int(c)] for c in candidate[-2::-2]]) + return ((evens + odds) % 10 == 0) + except ValueError: # Raised if an int conversion fails + return False diff --git a/tests/regressiontests/utils/tests.py b/tests/regressiontests/utils/tests.py index fe0b226adc..eb3a722888 100644 --- a/tests/regressiontests/utils/tests.py +++ b/tests/regressiontests/utils/tests.py @@ -4,7 +4,7 @@ Tests for django.utils. from unittest import TestCase -from django.utils import html +from django.utils import html, checksums from timesince import timesince_tests @@ -116,6 +116,32 @@ class TestUtilsHtml(TestCase): for value, output in items: self.check_output(f, value, output) +class TestUtilsChecksums(TestCase): + + def check_output(self, function, value, output=None): + """ + Check that function(value) equals output. If output is None, + check that function(value) equals value. + """ + if output is None: + output = value + self.assertEqual(function(value), output) + + def test_luhn(self): + f = checksums.luhn + items = ( + (4111111111111111, True), ('4111111111111111', True), + (4222222222222, True), (378734493671000, True), + (5424000000000015, True), (5555555555554444, True), + (1008, True), ('0000001008', True), ('000000001008', True), + (4012888888881881, True), (1234567890123456789012345678909, True), + (4111111111211111, False), (42222222222224, False), + (100, False), ('100', False), ('0000100', False), + ('abc', False), (None, False), (object(), False), + ) + for value, output in items: + self.check_output(f, value, output) + __test__ = { 'timesince_tests': timesince_tests, }