[Python-modules-commits] [django-simple-captcha] 01/03: New upstream version 0.5.6

Brian May bam at debian.org
Tue Dec 12 07:45:45 UTC 2017


This is an automated email from the git hooks/post-receive script.

bam pushed a commit to branch debian/master
in repository django-simple-captcha.

commit 4cc2e2169920ed22c87bf23d7721a2bb84d1a059
Author: Brian May <bam at debian.org>
Date:   Tue Dec 12 18:41:35 2017 +1100

    New upstream version 0.5.6
---
 CHANGES                                            |  12 +
 PKG-INFO                                           |   3 +-
 README.rst                                         |   2 +-
 captcha/__init__.py                                |  24 +-
 captcha/conf/settings.py                           |   1 +
 captcha/fields.py                                  |  15 +-
 captcha/helpers.py                                 |  18 +-
 captcha/models.py                                  |   8 +-
 captcha/tests/tests.py                             |  17 +-
 captcha/urls.py                                    |   2 +-
 captcha/views.py                                   |  48 +-
 django_simple_captcha.egg-info/PKG-INFO            |   3 +-
 django_simple_captcha.egg-info/SOURCES.txt         |  22 +-
 django_simple_captcha.egg-info/requires.txt        |   7 +-
 docs/advanced.rst                                  |  14 +
 docs/conf.py                                       |   4 +-
 docs/usage.rst                                     |  16 +-
 setup.cfg                                          |   1 -
 setup.py                                           |   3 +-
 testproject/coverage.sh                            |   1 +
 ..._django-simple-captcha_captcha___init___py.html | 103 ++++
 ...go-simple-captcha_captcha_conf___init___py.html |  91 ++++
 ...go-simple-captcha_captcha_conf_settings_py.html | 207 ++++++++
 ...de_django-simple-captcha_captcha_fields_py.html | 473 +++++++++++++++++
 ...e_django-simple-captcha_captcha_helpers_py.html | 273 ++++++++++
 ...de_django-simple-captcha_captcha_models_py.html | 247 +++++++++
 ...Code_django-simple-captcha_captcha_urls_py.html | 109 ++++
 ...ode_django-simple-captcha_captcha_views_py.html | 415 +++++++++++++++
 testproject/htmlcov/coverage_html.js               | 584 +++++++++++++++++++++
 testproject/htmlcov/index.html                     | 200 +++++++
 .../htmlcov/jquery.ba-throttle-debounce.min.js     |   9 +
 testproject/htmlcov/jquery.hotkeys.js              |  99 ++++
 testproject/htmlcov/jquery.isonscreen.js           |  53 ++
 testproject/htmlcov/jquery.min.js                  |   4 +
 testproject/htmlcov/jquery.tablesorter.min.js      |   2 +
 testproject/htmlcov/keybd_closed.png               | Bin 0 -> 112 bytes
 testproject/htmlcov/keybd_open.png                 | Bin 0 -> 112 bytes
 testproject/htmlcov/status.json                    |   1 +
 testproject/htmlcov/style.css                      | 375 +++++++++++++
 testproject/settings.py                            |   9 +-
 .../templates/captcha_test/image_html5_audio.html  |  22 +
 tox.ini                                            |   8 +-
 42 files changed, 3407 insertions(+), 98 deletions(-)

diff --git a/CHANGES b/CHANGES
index 2ce446d..ae24819 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,15 @@
+Version 0.5.6
+-------------
+* Updated render method to adapt for Django 2.1 (PR #120, thanks @skozan)
+* Improved compatibility with Django 2.0, tests against Django 2.0a1 (PR #121, thanks @Kondou-ger)
+* Dropped support for PIL (use Pillow instead)
+* Updated documentation (Fixes #122, thanks @claudep)
+* Test against Django 2.0b1
+* Return a Ranged Response when returning WAV audio to support Safari (Fixes #123, thanks @po5i)
+* Optionally inject brown noise into the generated WAV audio file, to avoid rainbow-table attacks (Fixes #124, thanks @appleorange1)
+* Test against Django 2.0
+
+
 Version 0.5.5
 -------------
 * I messed the 0.5.4 release, re-releasing as 0.5.5
diff --git a/PKG-INFO b/PKG-INFO
index 48fb03c..7669864 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,11 +1,12 @@
 Metadata-Version: 1.1
 Name: django-simple-captcha
-Version: 0.5.5
+Version: 0.5.6
 Summary: A very simple, yet powerful, Django captcha application
 Home-page: https://github.com/mbi/django-simple-captcha
 Author: Marco Bonetti
 Author-email: mbonetti at gmail.com
 License: MIT
+Description-Content-Type: UNKNOWN
 Description: UNKNOWN
 Platform: UNKNOWN
 Classifier: Development Status :: 4 - Beta
diff --git a/README.rst b/README.rst
index 03a74c8..b5d1af7 100644
--- a/README.rst
+++ b/README.rst
@@ -22,7 +22,7 @@ Features
 Requirements
 ++++++++++++
 
-* Django 1.7+
+* Django 1.8+
 * A recent version of the Pillow compiled with FreeType support
 * Flite is required for text-to-speech (audio) output, but not mandatory
 
diff --git a/captcha/__init__.py b/captcha/__init__.py
index e0acc65..3739629 100644
--- a/captcha/__init__.py
+++ b/captcha/__init__.py
@@ -1,28 +1,6 @@
-import re
-
-VERSION = (0, 5, 5)
+VERSION = (0, 5, 6)
 
 
 def get_version(svn=False):
     "Return the version as a human-format string."
     return '.'.join([str(i) for i in VERSION])
-
-
-def pillow_required():
-    def pil_version(version):
-        try:
-            return int(re.compile('[^\d]').sub('', version))
-        except:
-            return 116
-
-    try:
-        from PIL import Image, ImageDraw, ImageFont
-    except ImportError:
-        try:
-            import Image
-            import ImageDraw  # NOQA
-            import ImageFont  # NOQA
-        except ImportError:
-            return True
-
-    return pil_version(Image.VERSION) < 116
diff --git a/captcha/conf/settings.py b/captcha/conf/settings.py
index c8ab588..3d8f69d 100644
--- a/captcha/conf/settings.py
+++ b/captcha/conf/settings.py
@@ -12,6 +12,7 @@ CAPTCHA_FILTER_FUNCTIONS = getattr(settings, 'CAPTCHA_FILTER_FUNCTIONS', ('captc
 CAPTCHA_WORDS_DICTIONARY = getattr(settings, 'CAPTCHA_WORDS_DICTIONARY', '/usr/share/dict/words')
 CAPTCHA_PUNCTUATION = getattr(settings, 'CAPTCHA_PUNCTUATION', '''_"',.;:-''')
 CAPTCHA_FLITE_PATH = getattr(settings, 'CAPTCHA_FLITE_PATH', None)
+CAPTCHA_SOX_PATH = getattr(settings, 'CAPTCHA_SOX_PATH', None)
 CAPTCHA_TIMEOUT = getattr(settings, 'CAPTCHA_TIMEOUT', 5)  # Minutes
 CAPTCHA_LENGTH = int(getattr(settings, 'CAPTCHA_LENGTH', 4))  # Chars
 # CAPTCHA_IMAGE_BEFORE_FIELD = getattr(settings, 'CAPTCHA_IMAGE_BEFORE_FIELD', True)
diff --git a/captcha/fields.py b/captcha/fields.py
index f442a2e..5273b7a 100644
--- a/captcha/fields.py
+++ b/captcha/fields.py
@@ -1,7 +1,11 @@
 from captcha.conf import settings
 from captcha.models import CaptchaStore
+import django
 from django.core.exceptions import ImproperlyConfigured
-from django.core.urlresolvers import reverse, NoReverseMatch
+if django.VERSION < (1, 10):  # NOQA
+    from django.core.urlresolvers import reverse, NoReverseMatch  # NOQA
+else:  # NOQA
+    from django.urls import reverse, NoReverseMatch  # NOQA
 from django.forms import ValidationError
 from django.forms.fields import CharField, MultiValueField
 from django.forms.widgets import TextInput, MultiWidget, HiddenInput
@@ -113,7 +117,7 @@ class CaptchaTextInput(BaseCaptchaTextInput):
             }
             return render_to_string(settings.CAPTCHA_FIELD_TEMPLATE, context)
 
-    def render(self, name, value, attrs=None):
+    def render(self, name, value, attrs=None, renderer=None):
         self.fetch_captcha_store(name, value, attrs, self._args.get('generator'))
 
         context = {
@@ -129,7 +133,12 @@ class CaptchaTextInput(BaseCaptchaTextInput):
         self.hidden_field = render_to_string(settings.CAPTCHA_HIDDEN_FIELD_TEMPLATE, context)
         self.text_field = render_to_string(settings.CAPTCHA_TEXT_FIELD_TEMPLATE, context)
 
-        return super(CaptchaTextInput, self).render(name, self._value, attrs=attrs)
+        extra_kwargs = {}
+        if django.VERSION >= (1, 11):
+            # https://docs.djangoproject.com/en/1.11/ref/forms/widgets/#django.forms.Widget.render
+            extra_kwargs['renderer'] = renderer
+
+        return super(CaptchaTextInput, self).render(name, self._value, attrs=attrs, **extra_kwargs)
 
     def _render(self, template_name, context, renderer=None):
         return self.format_output(None)
diff --git a/captcha/helpers.py b/captcha/helpers.py
index f4a5ce1..c98aba6 100644
--- a/captcha/helpers.py
+++ b/captcha/helpers.py
@@ -1,7 +1,11 @@
 # -*- coding: utf-8 -*-
 import random
 from captcha.conf import settings
-from django.core.urlresolvers import reverse
+import django
+if django.VERSION < (1, 10):  # NOQA
+    from django.core.urlresolvers import reverse  # NOQA
+else:  # NOQA
+    from django.urls import reverse  # NOQA
 from six import u, text_type
 
 
@@ -78,13 +82,15 @@ def noise_null(draw, image):
 
 
 def post_smooth(image):
-    try:
-        import ImageFilter
-    except ImportError:
-        from PIL import ImageFilter
+    from PIL import ImageFilter
     return image.filter(ImageFilter.SMOOTH)
 
 
 def captcha_image_url(key):
-    """ Return url to image. Need for ajax refresh and, etc"""
+    """Return url to image. Need for ajax refresh and, etc"""
     return reverse('captcha-image', args=[key])
+
+
+def captcha_audio_url(key):
+    """Return url to image. Need for ajax refresh and, etc"""
+    return reverse('captcha-audio', args=[key])
diff --git a/captcha/models.py b/captcha/models.py
index dd329fe..c2a5d6a 100644
--- a/captcha/models.py
+++ b/captcha/models.py
@@ -1,12 +1,13 @@
 from captcha.conf import settings as captcha_settings
 from django.db import models
 from django.utils import timezone
+from django.utils.encoding import python_2_unicode_compatible
 from django.utils.encoding import smart_text
 import datetime
-import random
-import time
 import hashlib
 import logging
+import random
+import time
 
 
 # Heavily based on session key generation in Django
@@ -20,6 +21,7 @@ MAX_RANDOM_KEY = 18446744073709551616     # 2 << 63
 logger = logging.getLogger(__name__)
 
 
+ at python_2_unicode_compatible
 class CaptchaStore(models.Model):
     challenge = models.CharField(blank=False, max_length=32)
     response = models.CharField(blank=False, max_length=32)
@@ -41,7 +43,7 @@ class CaptchaStore(models.Model):
             del(key_)
         super(CaptchaStore, self).save(*args, **kwargs)
 
-    def __unicode__(self):
+    def __str__(self):
         return self.challenge
 
     def remove_expired(cls):
diff --git a/captcha/tests/tests.py b/captcha/tests/tests.py
index e2dbc8f..0426872 100644
--- a/captcha/tests/tests.py
+++ b/captcha/tests/tests.py
@@ -2,9 +2,13 @@
 from captcha.conf import settings
 from captcha.fields import CaptchaField, CaptchaTextInput
 from captcha.models import CaptchaStore
+import django
 from django.core import management
 from django.core.exceptions import ImproperlyConfigured
-from django.core.urlresolvers import reverse
+if django.VERSION < (1, 10):  # NOQA
+    from django.core.urlresolvers import reverse  # NOQA
+else:  # NOQA
+    from django.urls import reverse  # NOQA
 from django.test import TestCase, override_settings
 from django.utils.translation import ugettext_lazy
 from django.utils import timezone
@@ -20,11 +24,7 @@ except ImportError:
     from io import BytesIO as StringIO
 
 from six import u, text_type
-
-try:
-    from PIL import Image
-except ImportError:
-    import Image  # NOQA
+from PIL import Image
 
 
 @override_settings(ROOT_URLCONF='captcha.tests.urls')
@@ -73,9 +73,9 @@ class CaptchaCase(TestCase):
         for key in (self.stores.get('math_store').hashkey, self.stores.get('math_store').hashkey, self.default_store.hashkey):
             response = self.client.get(reverse('captcha-audio', kwargs=dict(key=key)))
             self.assertEqual(response.status_code, 200)
-            self.assertTrue(len(response.content) > 1024)
+            self.assertTrue(response.ranged_file.size > 1024)
             self.assertTrue(response.has_header('content-type'))
-            self.assertEqual(response._headers.get('content-type'), ('Content-Type', 'audio/x-wav'))
+            self.assertEqual(response._headers.get('content-type'), ('Content-Type', 'audio/wav'))
 
     def test_form_submit(self):
         r = self.client.get(reverse('captcha-test'))
@@ -223,6 +223,7 @@ class CaptchaCase(TestCase):
         try:
             new_data = json.loads(six.text_type(r.content, encoding='ascii'))
             self.assertTrue('image_url' in new_data)
+            self.assertTrue('audio_url' in new_data)
         except:
             self.fail()
 
diff --git a/captcha/urls.py b/captcha/urls.py
index ba09c02..b80d263 100644
--- a/captcha/urls.py
+++ b/captcha/urls.py
@@ -4,6 +4,6 @@ from captcha import views
 urlpatterns = [
     url(r'image/(?P<key>\w+)/$', views.captcha_image, name='captcha-image', kwargs={'scale': 1}),
     url(r'image/(?P<key>\w+)@2/$', views.captcha_image, name='captcha-image-2x', kwargs={'scale': 2}),
-    url(r'audio/(?P<key>\w+)/$', views.captcha_audio, name='captcha-audio'),
+    url(r'audio/(?P<key>\w+).wav$', views.captcha_audio, name='captcha-audio'),
     url(r'refresh/$', views.captcha_refresh, name='captcha-refresh'),
 ]
diff --git a/captcha/views.py b/captcha/views.py
index 28a8249..3b3b727 100644
--- a/captcha/views.py
+++ b/captcha/views.py
@@ -1,10 +1,10 @@
 from captcha.conf import settings
-from captcha.helpers import captcha_image_url
+from captcha.helpers import captcha_image_url, captcha_audio_url
 from captcha.models import CaptchaStore
 from django.http import HttpResponse, Http404
 from django.core.exceptions import ImproperlyConfigured
+from ranged_response import RangedFileResponse
 import random
-import re
 import tempfile
 import os
 import subprocess
@@ -15,21 +15,15 @@ try:
 except ImportError:
     from io import BytesIO as StringIO
 
-try:
-    from PIL import Image, ImageDraw, ImageFont
-except ImportError:
-    import Image
-    import ImageDraw
-    import ImageFont
+from PIL import Image, ImageDraw, ImageFont
 
 try:
     import json
 except ImportError:
     from django.utils import simplejson as json
 
-NON_DIGITS_RX = re.compile('[^\d]')
 # Distance of the drawn text from the top of the captcha image
-from_top = 4
+DISTNACE_FROM_TOP = 4
 
 
 def getsize(font, text):
@@ -75,11 +69,6 @@ def captcha_image(request, key, scale=1):
         size = (size[0] * 2, int(size[1] * 1.4))
 
     image = makeimg(size)
-
-    try:
-        PIL_VERSION = int(NON_DIGITS_RX.sub('', Image.VERSION))
-    except:
-        PIL_VERSION = 116
     xpos = 2
 
     charlist = []
@@ -94,14 +83,11 @@ def captcha_image(request, key, scale=1):
         chardraw = ImageDraw.Draw(charimage)
         chardraw.text((0, 0), ' %s ' % char, font=font, fill='#ffffff')
         if settings.CAPTCHA_LETTER_ROTATION:
-            if PIL_VERSION >= 116:
-                charimage = charimage.rotate(random.randrange(*settings.CAPTCHA_LETTER_ROTATION), expand=0, resample=Image.BICUBIC)
-            else:
-                charimage = charimage.rotate(random.randrange(*settings.CAPTCHA_LETTER_ROTATION), resample=Image.BICUBIC)
+            charimage = charimage.rotate(random.randrange(*settings.CAPTCHA_LETTER_ROTATION), expand=0, resample=Image.BICUBIC)
         charimage = charimage.crop(charimage.getbbox())
         maskimage = Image.new('L', size)
 
-        maskimage.paste(charimage, (xpos, from_top, xpos + charimage.size[0], from_top + charimage.size[1]))
+        maskimage.paste(charimage, (xpos, DISTNACE_FROM_TOP, xpos + charimage.size[0], DISTNACE_FROM_TOP + charimage.size[1]))
         size = maskimage.size
         image = Image.composite(fgimage, image, maskimage)
         xpos = xpos + 2 + charimage.size[0]
@@ -109,7 +95,7 @@ def captcha_image(request, key, scale=1):
     if settings.CAPTCHA_IMAGE_SIZE:
         # centering captcha on the image
         tmpimg = makeimg(size)
-        tmpimg.paste(image, (int((size[0] - xpos) / 2), int((size[1] - charimage.size[1]) / 2 - from_top)))
+        tmpimg.paste(image, (int((size[0] - xpos) / 2), int((size[1] - charimage.size[1]) / 2 - DISTNACE_FROM_TOP)))
         image = tmpimg.crop((0, 0, size[0], size[1]))
     else:
         image = image.crop((0, 0, xpos + 1, size[1]))
@@ -146,13 +132,20 @@ def captcha_audio(request, key):
             text = ', '.join(list(text))
         path = str(os.path.join(tempfile.gettempdir(), '%s.wav' % key))
         subprocess.call([settings.CAPTCHA_FLITE_PATH, "-t", text, "-o", path])
+
+        # Add arbitrary noise if sox is installed
+        if settings.CAPTCHA_SOX_PATH:
+            arbnoisepath = str(os.path.join(tempfile.gettempdir(), '%s_arbitrary.wav') % key)
+            mergedpath = str(os.path.join(tempfile.gettempdir(), '%s_merged.wav') % key)
+            subprocess.call([settings.CAPTCHA_SOX_PATH, '-r', '8000', '-n', arbnoisepath, 'synth', '2', 'brownnoise', 'gain', '-15'])
+            subprocess.call([settings.CAPTCHA_SOX_PATH, '-m', arbnoisepath, path, '-t', 'wavpcm', '-b', '16', mergedpath])
+            os.remove(arbnoisepath)
+            os.remove(path)
+            os.rename(mergedpath, path)
+
         if os.path.isfile(path):
-            response = HttpResponse()
-            f = open(path, 'rb')
-            response['Content-Type'] = 'audio/x-wav'
-            response.write(f.read())
-            f.close()
-            os.unlink(path)
+            response = RangedFileResponse(request, open(path, 'rb'), content_type='audio/wav')
+            response['Content-Disposition'] = 'attachment; filename="{}.wav"'.format(key)
             return response
     raise Http404
 
@@ -166,5 +159,6 @@ def captcha_refresh(request):
     to_json_response = {
         'key': new_key,
         'image_url': captcha_image_url(new_key),
+        'audio_url': captcha_audio_url(new_key) if settings.CAPTCHA_FLITE_PATH else None
     }
     return HttpResponse(json.dumps(to_json_response), content_type='application/json')
diff --git a/django_simple_captcha.egg-info/PKG-INFO b/django_simple_captcha.egg-info/PKG-INFO
index 48fb03c..7669864 100644
--- a/django_simple_captcha.egg-info/PKG-INFO
+++ b/django_simple_captcha.egg-info/PKG-INFO
@@ -1,11 +1,12 @@
 Metadata-Version: 1.1
 Name: django-simple-captcha
-Version: 0.5.5
+Version: 0.5.6
 Summary: A very simple, yet powerful, Django captcha application
 Home-page: https://github.com/mbi/django-simple-captcha
 Author: Marco Bonetti
 Author-email: mbonetti at gmail.com
 License: MIT
+Description-Content-Type: UNKNOWN
 Description: UNKNOWN
 Platform: UNKNOWN
 Classifier: Development Status :: 4 - Beta
diff --git a/django_simple_captcha.egg-info/SOURCES.txt b/django_simple_captcha.egg-info/SOURCES.txt
index cc4628d..9c92bb6 100644
--- a/django_simple_captcha.egg-info/SOURCES.txt
+++ b/django_simple_captcha.egg-info/SOURCES.txt
@@ -89,5 +89,25 @@ testproject/manage.py
 testproject/settings.py
 testproject/urls.py
 testproject/views.py
+testproject/htmlcov/_Users_marco_Code_django-simple-captcha_captcha___init___py.html
+testproject/htmlcov/_Users_marco_Code_django-simple-captcha_captcha_conf___init___py.html
+testproject/htmlcov/_Users_marco_Code_django-simple-captcha_captcha_conf_settings_py.html
+testproject/htmlcov/_Users_marco_Code_django-simple-captcha_captcha_fields_py.html
+testproject/htmlcov/_Users_marco_Code_django-simple-captcha_captcha_helpers_py.html
+testproject/htmlcov/_Users_marco_Code_django-simple-captcha_captcha_models_py.html
+testproject/htmlcov/_Users_marco_Code_django-simple-captcha_captcha_urls_py.html
+testproject/htmlcov/_Users_marco_Code_django-simple-captcha_captcha_views_py.html
+testproject/htmlcov/coverage_html.js
+testproject/htmlcov/index.html
+testproject/htmlcov/jquery.ba-throttle-debounce.min.js
+testproject/htmlcov/jquery.hotkeys.js
+testproject/htmlcov/jquery.isonscreen.js
+testproject/htmlcov/jquery.min.js
+testproject/htmlcov/jquery.tablesorter.min.js
+testproject/htmlcov/keybd_closed.png
+testproject/htmlcov/keybd_open.png
+testproject/htmlcov/status.json
+testproject/htmlcov/style.css
 testproject/templates/home.html
-testproject/templates/captcha_test/image.html
\ No newline at end of file
+testproject/templates/captcha_test/image.html
+testproject/templates/captcha_test/image_html5_audio.html
\ No newline at end of file
diff --git a/django_simple_captcha.egg-info/requires.txt b/django_simple_captcha.egg-info/requires.txt
index 8839fe1..da1642e 100644
--- a/django_simple_captcha.egg-info/requires.txt
+++ b/django_simple_captcha.egg-info/requires.txt
@@ -1,4 +1,5 @@
 setuptools
-six >=1.2.0
-Django >= 1.7
-Pillow >=2.2.2
+six>=1.2.0
+Django>=1.7
+Pillow>=2.2.2
+django-ranged-response==0.2.0
diff --git a/docs/advanced.rst b/docs/advanced.rst
index ca38124..8cba1cd 100644
--- a/docs/advanced.rst
+++ b/docs/advanced.rst
@@ -104,6 +104,20 @@ Full path to the ``flite`` executable. When defined, will automatically add audi
 
 Defaults to: ``None`` (no audio output)
 
+CAPTCHA_SOX_PATH
+------------------------
+
+Full path to the ``sox`` executable. If audio output is enabled via ``CAPTCHA_FLITE_PATH``, the generated output audio file is identical across multiple generations (unlike CAPTCHA images which get different random noise each time they are rendered). User appleorange1_ has shown_ that this could be used to pre-generate a "rainbow-table" of all possible input strings and a hash of the generated output soundfile, thus rendering an attack on audio CAPTCHAs trivial.
+
+If sox_ is installed and used via this settings, random brown noise is injected into the generated audio file, rendering attacks via a rainbow table impossible.
+
+Defaults to: ``None`` (no audio output)
+
+.. _appleorange1: https://github.com/appleorange1
+.. _shown: https://github.com/appleorange1/django-simple-captcha-cracker-poc
+.. _sox: http://sox.sourceforge.net/
+
+
 CAPTCHA_TIMEOUT
 ---------------
 
diff --git a/docs/conf.py b/docs/conf.py
index 6b62f62..45fe322 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -44,14 +44,14 @@ master_doc = 'index'
 
 # General information about the project.
 project = u('Django Simple Captcha')
-copyright = u('2011-2016 Marco Bonetti')
+copyright = u('2011-2017 Marco Bonetti')
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = '0.5.5'
+version = '0.5.6'
 # The full version, including alpha/beta/rc tags.
 release = version
 
diff --git a/docs/usage.rst b/docs/usage.rst
index b5cc306..9cad3a1 100644
--- a/docs/usage.rst
+++ b/docs/usage.rst
@@ -4,22 +4,14 @@ Using django-simple-captcha
 Installation
 +++++++++++++
 
-1. Download ``django-simple-captcha`` using pip_ by running: ``pip install  django-simple-captcha``
+1. Install ``django-simple-captcha`` via pip_: ``pip install  django-simple-captcha``
 2. Add ``captcha`` to the ``INSTALLED_APPS`` in your ``settings.py``
-3. Run ``python manage.py syncdb`` (or ``python manage.py migrate`` if you are managing database migrations via South) to create the required database tables
+3. Run ``python manage.py migrate``
 4. Add an entry to your ``urls.py``::
 
-        urlpatterns += patterns('',
+        urlpatterns += [
             url(r'^captcha/', include('captcha.urls')),
-        )
-
-
-Django-simple-captcha 0.4.3 and later supports both Django 1.7's new migrations and South migrations: if you are using South and Django < 1.7, you must define the following in your settings::
-
-        SOUTH_MIGRATION_MODULES = {
-            'captcha': 'captcha.south_migrations',
-        }
-
+        ]
 
 .. _pip: http://pypi.python.org/pypi/pip
 
diff --git a/setup.cfg b/setup.cfg
index 6f08d0e..adf5ed7 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -4,5 +4,4 @@ universal = 1
 [egg_info]
 tag_build = 
 tag_date = 0
-tag_svn_revision = 0
 
diff --git a/setup.py b/setup.py
index 05e9953..46662fe 100644
--- a/setup.py
+++ b/setup.py
@@ -31,7 +31,8 @@ install_requires = [
     'setuptools',
     'six >=1.2.0',
     'Django >= 1.7',
-    'Pillow >=2.2.2'
+    'Pillow >=2.2.2',
+    'django-ranged-response == 0.2.0'
 ]
 
 setup(
diff --git a/testproject/coverage.sh b/testproject/coverage.sh
index 52422e1..be54f03 100644
--- a/testproject/coverage.sh
+++ b/testproject/coverage.sh
@@ -1,5 +1,6 @@
 #!/bin/bash
 export CAPTCHA_FLITE_PATH=`which flite`
+export CAPTCHA_SOX_PATH=`which sox`
 coverage run --rcfile .coveragerc  manage.py test --failfast captcha
 coverage xml
 coverage html
diff --git a/testproject/htmlcov/_Users_marco_Code_django-simple-captcha_captcha___init___py.html b/testproject/htmlcov/_Users_marco_Code_django-simple-captcha_captcha___init___py.html
new file mode 100644
index 0000000..640f8ab
--- /dev/null
+++ b/testproject/htmlcov/_Users_marco_Code_django-simple-captcha_captcha___init___py.html
@@ -0,0 +1,103 @@
+
+
+
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    
+    
+    <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+    <title>Coverage for /Users/marco/Code/django-simple-captcha/captcha/__init__.py: 100.00%</title>
+    <link rel="stylesheet" href="style.css" type="text/css">
+    
+    <script type="text/javascript" src="jquery.min.js"></script>
+    <script type="text/javascript" src="jquery.hotkeys.js"></script>
+    <script type="text/javascript" src="jquery.isonscreen.js"></script>
+    <script type="text/javascript" src="coverage_html.js"></script>
+    <script type="text/javascript">
+        jQuery(document).ready(coverage.pyfile_ready);
+    </script>
+</head>
+<body class="pyfile">
+
+<div id="header">
+    <div class="content">
+        <h1>Coverage for <b>/Users/marco/Code/django-simple-captcha/captcha/__init__.py</b> :
+            <span class="pc_cov">100.00%</span>
+        </h1>
+
+        <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
+
+        <h2 class="stats">
+            3 statements  
+            <span class="run hide_run shortkey_r button_toggle_run">3 run</span>
+            <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+            <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
+
+            
+                <span class="par run hide_run shortkey_p button_toggle_par">0 partial</span>
+            
+        </h2>
+    </div>
+</div>
+
+<div class="help_panel">
+    <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+    <p class="legend">Hot-keys on this page</p>
+    <div>
+    <p class="keyhelp">
+        <span class="key">r</span>
+        <span class="key">m</span>
+        <span class="key">x</span>
+        <span class="key">p</span>   toggle line displays
+    </p>
+    <p class="keyhelp">
+        <span class="key">j</span>
+        <span class="key">k</span>   next/prev highlighted chunk
+    </p>
+    <p class="keyhelp">
+        <span class="key">0</span>   (zero) top of page
+    </p>
+    <p class="keyhelp">
+        <span class="key">1</span>   (one) first highlighted chunk
+    </p>
+    </div>
+</div>
+
+<div id="source">
+    <table>
+        <tr>
+            <td class="linenos">
+<p id="n1" class="stm run hide_run"><a href="#n1">1</a></p>
+<p id="n2" class="pln"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="stm run hide_run"><a href="#n4">4</a></p>
+<p id="n5" class="pln"><a href="#n5">5</a></p>
+<p id="n6" class="stm run hide_run"><a href="#n6">6</a></p>
+
+            </td>
+            <td class="text">
+<p id="t1" class="stm run hide_run"><span class="nam">VERSION</span> <span class="op">=</span> <span class="op">(</span><span class="num">0</span><span class="op">,</span> <span class="num">5</span><span class="op">,</span> <span class="num">6</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t2" class="pln"><span class="strut"> </span></p>
+<p id="t3" class="pln"><span class="strut"> </span></p>
+<p id="t4" class="stm run hide_run"><span class="key">def</span> <span class="nam">get_version</span><span class="op">(</span><span class="nam">svn</span><span class="op">=</span><span class="key">False</span><span class="op">)</span><span class="op">:</span><span class="strut"> </span></p>
+<p id="t5" class="pln">    <span class="str">"Return the version as a human-format string."</span><span class="strut"> </span></p>
+<p id="t6" class="stm run hide_run">    <span class="key">return</span> <span class="str">'.'</span><span class="op">.</span><span class="nam">join</span><span class="op">(</span><span class="op">[</span><span class="nam">str</span><span class="op">(</span><span class="nam">i</span><span class="op">)</span> <span class="key">for</span> <span class="nam">i</span> <span class="key">in</span> <span class="nam">VERSION</span><span class="op">]</span><span class="op">)</span><span class="stru [...]
+
+            </td>
+        </tr>
+    </table>
+</div>
+
+<div id="footer">
+    <div class="content">
+        <p>
+            <a class="nav" href="index.html">&#xab; index</a>     <a class="nav" href="https://coverage.readthedocs.io">coverage.py v4.4.2</a>,
+            created at 2017-11-08 13:41
+        </p>
+    </div>
+</div>
+
+</body>
+</html>
diff --git a/testproject/htmlcov/_Users_marco_Code_django-simple-captcha_captcha_conf___init___py.html b/testproject/htmlcov/_Users_marco_Code_django-simple-captcha_captcha_conf___init___py.html
new file mode 100644
index 0000000..10f7d48
--- /dev/null
+++ b/testproject/htmlcov/_Users_marco_Code_django-simple-captcha_captcha_conf___init___py.html
@@ -0,0 +1,91 @@
+
+
+
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    
+    
+    <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+    <title>Coverage for /Users/marco/Code/django-simple-captcha/captcha/conf/__init__.py: 100.00%</title>
+    <link rel="stylesheet" href="style.css" type="text/css">
+    
+    <script type="text/javascript" src="jquery.min.js"></script>
+    <script type="text/javascript" src="jquery.hotkeys.js"></script>
+    <script type="text/javascript" src="jquery.isonscreen.js"></script>
+    <script type="text/javascript" src="coverage_html.js"></script>
+    <script type="text/javascript">
+        jQuery(document).ready(coverage.pyfile_ready);
+    </script>
+</head>
+<body class="pyfile">
+
+<div id="header">
+    <div class="content">
+        <h1>Coverage for <b>/Users/marco/Code/django-simple-captcha/captcha/conf/__init__.py</b> :
+            <span class="pc_cov">100.00%</span>
+        </h1>
+
+        <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
+
+        <h2 class="stats">
+            0 statements  
+            <span class="run hide_run shortkey_r button_toggle_run">0 run</span>
+            <span class="mis shortkey_m button_toggle_mis">0 missing</span>
+            <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
+
+            
+                <span class="par run hide_run shortkey_p button_toggle_par">0 partial</span>
+            
+        </h2>
+    </div>
+</div>
+
+<div class="help_panel">
+    <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+    <p class="legend">Hot-keys on this page</p>
+    <div>
+    <p class="keyhelp">
+        <span class="key">r</span>
+        <span class="key">m</span>
+        <span class="key">x</span>
+        <span class="key">p</span>   toggle line displays
+    </p>
+    <p class="keyhelp">
+        <span class="key">j</span>
+        <span class="key">k</span>   next/prev highlighted chunk
+    </p>
+    <p class="keyhelp">
+        <span class="key">0</span>   (zero) top of page
+    </p>
+    <p class="keyhelp">
+        <span class="key">1</span>   (one) first highlighted chunk
+    </p>
+    </div>
+</div>
+
+<div id="source">
+    <table>
+        <tr>
+            <td class="linenos">
+
+            </td>
+            <td class="text">
+
+            </td>
+        </tr>
+    </table>
+</div>
+
+<div id="footer">
+    <div class="content">
+        <p>
+            <a class="nav" href="index.html">&#xab; index</a>     <a class="nav" href="https://coverage.readthedocs.io">coverage.py v4.4.2</a>,
+            created at 2017-11-08 13:41
+        </p>
+    </div>
+</div>
+
+</body>
+</html>
diff --git a/testproject/htmlcov/_Users_marco_Code_django-simple-captcha_captcha_conf_settings_py.html b/testproject/htmlcov/_Users_marco_Code_django-simple-captcha_captcha_conf_settings_py.html
new file mode 100644
index 0000000..1cbccae
--- /dev/null
+++ b/testproject/htmlcov/_Users_marco_Code_django-simple-captcha_captcha_conf_settings_py.html
@@ -0,0 +1,207 @@
+
+
+
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    
+    
+    <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+    <title>Coverage for /Users/marco/Code/django-simple-captcha/captcha/conf/settings.py: 88.46%</title>
+    <link rel="stylesheet" href="style.css" type="text/css">
+    
+    <script type="text/javascript" src="jquery.min.js"></script>
+    <script type="text/javascript" src="jquery.hotkeys.js"></script>
+    <script type="text/javascript" src="jquery.isonscreen.js"></script>
+    <script type="text/javascript" src="coverage_html.js"></script>
+    <script type="text/javascript">
+        jQuery(document).ready(coverage.pyfile_ready);
+    </script>
+</head>
+<body class="pyfile">
+
+<div id="header">
+    <div class="content">
+        <h1>Coverage for <b>/Users/marco/Code/django-simple-captcha/captcha/conf/settings.py</b> :
+            <span class="pc_cov">88.46%</span>
+        </h1>
+
+        <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
+
+        <h2 class="stats">
+            44 statements  
+            <span class="run hide_run shortkey_r button_toggle_run">41 run</span>
+            <span class="mis shortkey_m button_toggle_mis">3 missing</span>
+            <span class="exc shortkey_x button_toggle_exc">0 excluded</span>
+
+            
+                <span class="par run hide_run shortkey_p button_toggle_par">3 partial</span>
+            
+        </h2>
+    </div>
+</div>
+
+<div class="help_panel">
+    <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+    <p class="legend">Hot-keys on this page</p>
+    <div>
+    <p class="keyhelp">
+        <span class="key">r</span>
+        <span class="key">m</span>
+        <span class="key">x</span>
+        <span class="key">p</span>   toggle line displays
+    </p>
+    <p class="keyhelp">
+        <span class="key">j</span>
+        <span class="key">k</span>   next/prev highlighted chunk
+    </p>
+    <p class="keyhelp">
+        <span class="key">0</span>   (zero) top of page
+    </p>
+    <p class="keyhelp">
+        <span class="key">1</span>   (one) first highlighted chunk
+    </p>
+    </div>
+</div>
+
+<div id="source">
+    <table>
+        <tr>
+            <td class="linenos">
+<p id="n1" class="stm run hide_run"><a href="#n1">1</a></p>
+<p id="n2" class="stm run hide_run"><a href="#n2">2</a></p>
+<p id="n3" class="pln"><a href="#n3">3</a></p>
+<p id="n4" class="stm run hide_run"><a href="#n4">4</a></p>
+<p id="n5" class="stm run hide_run"><a href="#n5">5</a></p>
+<p id="n6" class="stm run hide_run"><a href="#n6">6</a></p>
+<p id="n7" class="stm run hide_run"><a href="#n7">7</a></p>
+<p id="n8" class="stm run hide_run"><a href="#n8">8</a></p>
+<p id="n9" class="stm run hide_run"><a href="#n9">9</a></p>
+<p id="n10" class="stm run hide_run"><a href="#n10">10</a></p>
+<p id="n11" class="stm run hide_run"><a href="#n11">11</a></p>
+<p id="n12" class="stm run hide_run"><a href="#n12">12</a></p>
+<p id="n13" class="stm run hide_run"><a href="#n13">13</a></p>
+<p id="n14" class="stm run hide_run"><a href="#n14">14</a></p>
+<p id="n15" class="stm run hide_run"><a href="#n15">15</a></p>
+<p id="n16" class="stm run hide_run"><a href="#n16">16</a></p>
+<p id="n17" class="stm run hide_run"><a href="#n17">17</a></p>
+<p id="n18" class="pln"><a href="#n18">18</a></p>
+<p id="n19" class="stm run hide_run"><a href="#n19">19</a></p>
+<p id="n20" class="stm run hide_run"><a href="#n20">20</a></p>
+<p id="n21" class="stm run hide_run"><a href="#n21">21</a></p>
+<p id="n22" class="stm run hide_run"><a href="#n22">22</a></p>
+<p id="n23" class="stm run hide_run"><a href="#n23">23</a></p>
+<p id="n24" class="stm run hide_run"><a href="#n24">24</a></p>
+<p id="n25" class="stm run hide_run"><a href="#n25">25</a></p>
+<p id="n26" class="stm run hide_run"><a href="#n26">26</a></p>
+<p id="n27" class="stm run hide_run"><a href="#n27">27</a></p>
+<p id="n28" class="stm run hide_run"><a href="#n28">28</a></p>
+<p id="n29" class="stm run hide_run"><a href="#n29">29</a></p>
+<p id="n30" class="pln"><a href="#n30">30</a></p>
+<p id="n31" class="stm run hide_run"><a href="#n31">31</a></p>
+<p id="n32" class="pln"><a href="#n32">32</a></p>
+<p id="n33" class="pln"><a href="#n33">33</a></p>
+<p id="n34" class="stm par run hide_run"><a href="#n34">34</a></p>
+<p id="n35" class="stm mis"><a href="#n35">35</a></p>
+<p id="n36" class="pln"><a href="#n36">36</a></p>
+<p id="n37" class="pln"><a href="#n37">37</a></p>
+<p id="n38" class="stm run hide_run"><a href="#n38">38</a></p>
+<p id="n39" class="stm run hide_run"><a href="#n39">39</a></p>
+<p id="n40" class="stm run hide_run"><a href="#n40">40</a></p>
+<p id="n41" class="pln"><a href="#n41">41</a></p>
+<p id="n42" class="stm run hide_run"><a href="#n42">42</a></p>
+<p id="n43" class="pln"><a href="#n43">43</a></p>
+<p id="n44" class="pln"><a href="#n44">44</a></p>
+<p id="n45" class="stm run hide_run"><a href="#n45">45</a></p>
+<p id="n46" class="stm run hide_run"><a href="#n46">46</a></p>
+<p id="n47" class="pln"><a href="#n47">47</a></p>
+<p id="n48" class="pln"><a href="#n48">48</a></p>
+<p id="n49" class="stm run hide_run"><a href="#n49">49</a></p>
+<p id="n50" class="stm par run hide_run"><a href="#n50">50</a></p>
+<p id="n51" class="stm run hide_run"><a href="#n51">51</a></p>
+<p id="n52" class="stm mis"><a href="#n52">52</a></p>
+<p id="n53" class="pln"><a href="#n53">53</a></p>
+<p id="n54" class="pln"><a href="#n54">54</a></p>
+<p id="n55" class="stm run hide_run"><a href="#n55">55</a></p>
+<p id="n56" class="stm par run hide_run"><a href="#n56">56</a></p>
+<p id="n57" class="stm run hide_run"><a href="#n57">57</a></p>
+<p id="n58" class="stm mis"><a href="#n58">58</a></p>
+
+            </td>
+            <td class="text">
+<p id="t1" class="stm run hide_run"><span class="key">import</span> <span class="nam">os</span><span class="strut"> </span></p>
+<p id="t2" class="stm run hide_run"><span class="key">from</span> <span class="nam">django</span><span class="op">.</span><span class="nam">conf</span> <span class="key">import</span> <span class="nam">settings</span><span class="strut"> </span></p>
+<p id="t3" class="pln"><span class="strut"> </span></p>
+<p id="t4" class="stm run hide_run"><span class="nam">CAPTCHA_FONT_PATH</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_FONT_PATH'</span><span class="op">,</span> <span class="nam">os</span><span class="op">.</span><span class="nam">path</span><span class="op">.</span><span class="nam">normpath</span><span class="op">(</span><span class="nam">os</span><span clas [...]
+<p id="t5" class="stm run hide_run"><span class="nam">CAPTCHA_FONT_SIZE</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_FONT_SIZE'</span><span class="op">,</span> <span class="num">22</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t6" class="stm run hide_run"><span class="nam">CAPTCHA_LETTER_ROTATION</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_LETTER_ROTATION'</span><span class="op">,</span> <span class="op">(</span><span class="op">-</span><span class="num">35</span><span class="op">,</span> <span class="num">35</span><span class="op">)</span><span class="op">)</span><span cla [...]
+<p id="t7" class="stm run hide_run"><span class="nam">CAPTCHA_BACKGROUND_COLOR</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_BACKGROUND_COLOR'</span><span class="op">,</span> <span class="str">'#ffffff'</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t8" class="stm run hide_run"><span class="nam">CAPTCHA_FOREGROUND_COLOR</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_FOREGROUND_COLOR'</span><span class="op">,</span> <span class="str">'#001100'</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t9" class="stm run hide_run"><span class="nam">CAPTCHA_CHALLENGE_FUNCT</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_CHALLENGE_FUNCT'</span><span class="op">,</span> <span class="str">'captcha.helpers.random_char_challenge'</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t10" class="stm run hide_run"><span class="nam">CAPTCHA_NOISE_FUNCTIONS</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_NOISE_FUNCTIONS'</span><span class="op">,</span> <span class="op">(</span><span class="str">'captcha.helpers.noise_arcs'</span><span class="op">,</span> <span class="str">'captcha.helpers.noise_dots'</span><span class="op">,</span><span  [...]
+<p id="t11" class="stm run hide_run"><span class="nam">CAPTCHA_FILTER_FUNCTIONS</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_FILTER_FUNCTIONS'</span><span class="op">,</span> <span class="op">(</span><span class="str">'captcha.helpers.post_smooth'</span><span class="op">,</span><span class="op">)</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t12" class="stm run hide_run"><span class="nam">CAPTCHA_WORDS_DICTIONARY</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_WORDS_DICTIONARY'</span><span class="op">,</span> <span class="str">'/usr/share/dict/words'</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t13" class="stm run hide_run"><span class="nam">CAPTCHA_PUNCTUATION</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_PUNCTUATION'</span><span class="op">,</span> <span class="str">'''_"',.;:-'''</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t14" class="stm run hide_run"><span class="nam">CAPTCHA_FLITE_PATH</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_FLITE_PATH'</span><span class="op">,</span> <span class="key">None</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t15" class="stm run hide_run"><span class="nam">CAPTCHA_SOX_PATH</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_SOX_PATH'</span><span class="op">,</span> <span class="key">None</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t16" class="stm run hide_run"><span class="nam">CAPTCHA_TIMEOUT</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_TIMEOUT'</span><span class="op">,</span> <span class="num">5</span><span class="op">)</span>  <span class="com"># Minutes</span><span class="strut"> </span></p>
+<p id="t17" class="stm run hide_run"><span class="nam">CAPTCHA_LENGTH</span> <span class="op">=</span> <span class="nam">int</span><span class="op">(</span><span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_LENGTH'</span><span class="op">,</span> <span class="num">4</span><span class="op">)</span><span class="op">)</span>  <span class="com"># Chars</span><span class="strut"> </span></p>
+<p id="t18" class="pln"><span class="com"># CAPTCHA_IMAGE_BEFORE_FIELD = getattr(settings, 'CAPTCHA_IMAGE_BEFORE_FIELD', True)</span><span class="strut"> </span></p>
+<p id="t19" class="stm run hide_run"><span class="nam">CAPTCHA_DICTIONARY_MIN_LENGTH</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_DICTIONARY_MIN_LENGTH'</span><span class="op">,</span> <span class="num">0</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t20" class="stm run hide_run"><span class="nam">CAPTCHA_DICTIONARY_MAX_LENGTH</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_DICTIONARY_MAX_LENGTH'</span><span class="op">,</span> <span class="num">99</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t21" class="stm run hide_run"><span class="nam">CAPTCHA_IMAGE_SIZE</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_IMAGE_SIZE'</span><span class="op">,</span> <span class="key">None</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t22" class="stm run hide_run"><span class="nam">CAPTCHA_IMAGE_TEMPLATE</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_IMAGE_TEMPLATE'</span><span class="op">,</span> <span class="str">'captcha/image.html'</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t23" class="stm run hide_run"><span class="nam">CAPTCHA_HIDDEN_FIELD_TEMPLATE</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_HIDDEN_FIELD_TEMPLATE'</span><span class="op">,</span> <span class="str">'captcha/hidden_field.html'</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t24" class="stm run hide_run"><span class="nam">CAPTCHA_TEXT_FIELD_TEMPLATE</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_TEXT_FIELD_TEMPLATE'</span><span class="op">,</span> <span class="str">'captcha/text_field.html'</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t25" class="stm run hide_run"><span class="nam">CAPTCHA_FIELD_TEMPLATE</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_FIELD_TEMPLATE'</span><span class="op">,</span> <span class="str">'captcha/field.html'</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t26" class="stm run hide_run"><span class="nam">CAPTCHA_OUTPUT_FORMAT</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_OUTPUT_FORMAT'</span><span class="op">,</span> <span class="key">None</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t27" class="stm run hide_run"><span class="nam">CAPTCHA_MATH_CHALLENGE_OPERATOR</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_MATH_CHALLENGE_OPERATOR'</span><span class="op">,</span> <span class="str">'*'</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t28" class="stm run hide_run"><span class="nam">CAPTCHA_GET_FROM_POOL</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_GET_FROM_POOL'</span><span class="op">,</span> <span class="key">False</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t29" class="stm run hide_run"><span class="nam">CAPTCHA_GET_FROM_POOL_TIMEOUT</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_GET_FROM_POOL_TIMEOUT'</span><span class="op">,</span> <span class="num">5</span><span class="op">)</span><span class="strut"> </span></p>
+<p id="t30" class="pln"><span class="strut"> </span></p>
+<p id="t31" class="stm run hide_run"><span class="nam">CAPTCHA_TEST_MODE</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CAPTCHA_TEST_MODE'</span><span class="op">,</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">settings</span><span class="op">,</span> <span class="str">'CATPCHA_TEST_MODE'</span><span class="op">,</span> <span class="k [...]
+<p id="t32" class="pln"><span class="strut"> </span></p>
+<p id="t33" class="pln"><span class="com"># Failsafe</span><span class="strut"> </span></p>
+<p id="t34" class="stm par run hide_run"><span class="annotate short">34&#x202F;&#x219B;&#x202F;35</span><span class="annotate long">line 34 didn't jump to line 35, because the condition on line 34 was never true</span><span class="key">if</span> <span class="nam">CAPTCHA_DICTIONARY_MIN_LENGTH</span> <span class="op">></span> <span class="nam">CAPTCHA_DICTIONARY_MAX_LENGTH</span><span class="op">:</span><span class="strut"> </span></p>
+<p id="t35" class="stm mis">    <span class="nam">CAPTCHA_DICTIONARY_MIN_LENGTH</span><span class="op">,</span> <span class="nam">CAPTCHA_DICTIONARY_MAX_LENGTH</span> <span class="op">=</span> <span class="nam">CAPTCHA_DICTIONARY_MAX_LENGTH</span><span class="op">,</span> <span class="nam">CAPTCHA_DICTIONARY_MIN_LENGTH</span><span class="strut"> </span></p>
+<p id="t36" class="pln"><span class="strut"> </span></p>
+<p id="t37" class="pln"><span class="strut"> </span></p>
+<p id="t38" class="stm run hide_run"><span class="key">def</span> <span class="nam">_callable_from_string</span><span class="op">(</span><span class="nam">string_or_callable</span><span class="op">)</span><span class="op">:</span><span class="strut"> </span></p>
+<p id="t39" class="stm run hide_run">    <span class="key">if</span> <span class="nam">callable</span><span class="op">(</span><span class="nam">string_or_callable</span><span class="op">)</span><span class="op">:</span><span class="strut"> </span></p>
+<p id="t40" class="stm run hide_run">        <span class="key">return</span> <span class="nam">string_or_callable</span><span class="strut"> </span></p>
+<p id="t41" class="pln">    <span class="key">else</span><span class="op">:</span><span class="strut"> </span></p>
... 3071 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/django-simple-captcha.git



More information about the Python-modules-commits mailing list