Python 3, Django 1.7+ compatilibity, some tests.
authorRadek Czajka <radekczajka@nowoczesnapolska.org.pl>
Tue, 30 Sep 2014 11:40:31 +0000 (13:40 +0200)
committerRadek Czajka <radekczajka@nowoczesnapolska.org.pl>
Tue, 30 Sep 2014 11:41:31 +0000 (13:41 +0200)
Removed slughifi, use python-slugify instead.

19 files changed:
.gitignore
fnpdjango/management/commands/makecontribmessages.py
fnpdjango/pipeline_storage.py [new file with mode: 0644]
fnpdjango/templatetags/fnp_annoy.py
fnpdjango/templatetags/fnp_prevnext.py
fnpdjango/templatetags/macros.py
fnpdjango/utils/pipeline_storage.py [deleted file]
fnpdjango/utils/settings.py
fnpdjango/utils/text/slughifi.py [deleted file]
fnpdjango/utils/text/textilepl.py
runtests.py [new file with mode: 0644]
setup.py
tests/__init__.py [new file with mode: 0644]
tests/models.py [new file with mode: 0644]
tests/tests/__init__.py [new file with mode: 0644]
tests/tests/test_storage.py [new file with mode: 0644]
tests/tests/test_templatetags_fnp_markup.py [new file with mode: 0644]
tests/tests/test_utils_settings.py [new file with mode: 0644]
tox.ini [new file with mode: 0644]

index ea2e8e1..39ece65 100644 (file)
@@ -1,6 +1,7 @@
 *~
 *.orig
 *.log
+*.swp
 
 # Python garbage
 *.pyc
@@ -10,6 +11,8 @@ nosetests.xml
 build
 dist
 *.egg-info
+.tox
+/htmlcov
 
 # Mac OS X garbage
 .DS_Store
index 5aa8fa1..966988c 100755 (executable)
@@ -2,6 +2,8 @@
 # This file is part of FNPDjango, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
+from __future__ import print_function
+
 import os
 from optparse import make_option
 from django.core.management.base import BaseCommand
@@ -13,19 +15,19 @@ class Command(BaseCommand):
         from django.conf import settings
 
         if not hasattr(settings, 'CONTRIB_LOCALE_APPS') or not settings.CONTRIB_LOCALE_APPS:
-            print "CONTRIB_LOCALE_APPS not set, no contrib locale needed."
+            print("CONTRIB_LOCALE_APPS not set, no contrib locale needed.")
             return
 
         from subprocess import call
         import babel
 
         app_names = settings.CONTRIB_LOCALE_APPS
-        print 'L10n for:', ", ".join(app_names)
+        print('L10n for:', ", ".join(app_names))
         app_dirs = [os.path.dirname(__import__(app).__file__)
                         for app in app_names]
         assert settings.LOCALE_PATHS
         locale_path = settings.LOCALE_PATHS[0]
-        print 'Using:', locale_path
+        print('Using:', locale_path)
 
         # Create the POT file.
         babel_cfg = os.path.join(locale_path,  "babel.cfg")
diff --git a/fnpdjango/pipeline_storage.py b/fnpdjango/pipeline_storage.py
new file mode 100644 (file)
index 0000000..eb12f07
--- /dev/null
@@ -0,0 +1,6 @@
+from pipeline.storage import GZIPMixin
+from pipeline.storage import PipelineCachedStorage
+
+class GzipPipelineCachedStorage(GZIPMixin, PipelineCachedStorage):
+    pass
+
index c4a7dec..c999cec 100644 (file)
@@ -13,4 +13,4 @@ def annoy():
     if getattr(settings, 'FNP_ANNOY', False):
         return template.loader.render_to_string('fnpdjango/annoy.html')
     else:
-        return u""
+        return ""
index 17c8188..cb4efe2 100644 (file)
@@ -1,7 +1,12 @@
 from copy import copy
-from urllib import urlencode
 from django.template import Library
 
+try:
+    from urllib.parse import urlencode
+except ImportError:
+    from urllib import urlencode
+
+
 register = Library()
 
 
index b03e665..61b007d 100644 (file)
@@ -84,12 +84,12 @@ def do_macro(parser, token):
         args = token.split_contents()
         tag_name, macro_name, args = args[0], args[1], args[2:]
     except IndexError:
-        raise template.TemplateSyntaxError, "'%s' tag requires at least one argument (macro name)" % token.contents.split()[0]
+        raise template.TemplateSyntaxError("'%s' tag requires at least one argument (macro name)" % token.contents.split()[0])
     # TODO: check that 'args' are all simple strings ([a-zA-Z0-9_]+)
     r_valid_arg_name = re.compile(r'^[a-zA-Z0-9_]+$')
     for arg in args:
         if not r_valid_arg_name.match(arg):
-            raise template.TemplateSyntaxError, "Argument '%s' to macro '%s' contains illegal characters. Only alphanumeric characters and '_' are allowed." % (arg, macro_name)
+            raise template.TemplateSyntaxError("Argument '%s' to macro '%s' contains illegal characters. Only alphanumeric characters and '_' are allowed." % (arg, macro_name))
     nodelist = parser.parse(('endmacro', ))
     parser.delete_first_token()
 
@@ -111,7 +111,7 @@ def do_loadmacros(parser, token):
     try:
         tag_name, filename = token.split_contents()
     except IndexError:
-        raise template.TemplateSyntaxError, "'%s' tag requires at least one argument (macro name)" % token.contents.split()[0]
+        raise template.TemplateSyntaxError("'%s' tag requires at least one argument (macro name)" % token.contents.split()[0])
     if filename[0] in ('"', "'") and filename[-1] == filename[0]:
         filename = filename[1:-1]
     t = get_template(filename)
@@ -140,20 +140,20 @@ def do_usemacro(parser, token):
         args = token.split_contents()
         tag_name, macro_name, values = args[0], args[1], args[2:]
     except IndexError:
-        raise template.TemplateSyntaxError, "'%s' tag requires at least one argument (macro name)" % token.contents.split()[0]
+        raise template.TemplateSyntaxError("'%s' tag requires at least one argument (macro name)" % token.contents.split()[0])
     try:
         macro = parser._macros[macro_name]
     except (AttributeError, KeyError):
-        raise template.TemplateSyntaxError, "Macro '%s' is not defined" % macro_name
+        raise template.TemplateSyntaxError("Macro '%s' is not defined" % macro_name)
 
     if (len(values) != len(macro.args)):
-        raise template.TemplateSyntaxError"Macro '%s' was declared with %d parameters and used with %d parameter" % (
+        raise template.TemplateSyntaxError("Macro '%s' was declared with %d parameters and used with %d parameter" % (
             macro_name,
             len(macro.args),
-            len(values))
+            len(values)))
     filter_expressions = []
     for val in values:
         if (val[0] == "'" or val[0] == '"') and (val[0] != val[-1]):
-            raise template.TemplateSyntaxError, "Non-terminated string argument: %s" % val[1:]
+            raise template.TemplateSyntaxError("Non-terminated string argument: %s" % val[1:])
         filter_expressions.append(FilterExpression(val, parser))
     return UseMacroNode(macro, filter_expressions)
\ No newline at end of file
diff --git a/fnpdjango/utils/pipeline_storage.py b/fnpdjango/utils/pipeline_storage.py
deleted file mode 100644 (file)
index eb12f07..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-from pipeline.storage import GZIPMixin
-from pipeline.storage import PipelineCachedStorage
-
-class GzipPipelineCachedStorage(GZIPMixin, PipelineCachedStorage):
-    pass
-
index 39b0a25..43f49ec 100644 (file)
@@ -1,8 +1,18 @@
 """
 Utilities for global settings.
 """
+from django.utils.encoding import python_2_unicode_compatible
 
+# Use Python3 str.
+try:
+    unicode
+except NameError:
+    pass
+else:
+    str = unicode
 
+
+@python_2_unicode_compatible
 class LazyUGettextLazy(object):
     """You can use it to internationalize strings in settings.
 
@@ -14,11 +24,11 @@ class LazyUGettextLazy(object):
     def __init__(self, text):
         self.text = text
 
-    def __unicode__(self):
+    def __str__(self):
         if not self.real:
             from django.utils.translation import ugettext_lazy
             LazyUGettextLazy._ = staticmethod(ugettext_lazy)
             LazyUGettextLazy.real = True
-        return unicode(self._(self.text))
+        return str(self._(self.text))
 
 
diff --git a/fnpdjango/utils/text/slughifi.py b/fnpdjango/utils/text/slughifi.py
deleted file mode 100644 (file)
index 938049e..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- coding: utf-8 -*-
-import re
-from types import UnicodeType
-
-from django.template.defaultfilters import slugify
-
-# default unicode character mapping ( you may not see some chars, leave as is )
-char_map = {u'À': 'A', u'Á': 'A', u'Â': 'A', u'Ã': 'A', u'Ä': 'Ae', u'Å': 'A', u'Æ': 'A', u'Ā': 'A', u'Ą': 'A', u'Ă': 'A', u'Ç': 'C', u'Ć': 'C', u'Č': 'C', u'Ĉ': 'C', u'Ċ': 'C', u'Ď': 'D', u'Đ': 'D', u'È': 'E', u'É': 'E', u'Ê': 'E', u'Ë': 'E', u'Ē': 'E', u'Ę': 'E', u'Ě': 'E', u'Ĕ': 'E', u'Ė': 'E', u'Ĝ': 'G', u'Ğ': 'G', u'Ġ': 'G', u'Ģ': 'G', u'Ĥ': 'H', u'Ħ': 'H', u'Ì': 'I', u'Í': 'I', u'Î': 'I', u'Ï': 'I', u'Ī': 'I', u'Ĩ': 'I', u'Ĭ': 'I', u'Į': 'I', u'İ': 'I', u'IJ': 'IJ', u'Ĵ': 'J', u'Ķ': 'K', u'Ľ': 'K', u'Ĺ': 'K', u'Ļ': 'K', u'Ŀ': 'K', u'Ł': 'L', u'Ñ': 'N', u'Ń': 'N', u'Ň': 'N', u'Ņ': 'N', u'Ŋ': 'N', u'Ò': 'O', u'Ó': 'O', u'Ô': 'O', u'Õ': 'O', u'Ö': 'Oe', u'Ø': 'O', u'Ō': 'O', u'Ő': 'O', u'Ŏ': 'O', u'Œ': 'OE', u'Ŕ': 'R', u'Ř': 'R', u'Ŗ': 'R', u'Ś': 'S', u'Ş': 'S', u'Ŝ': 'S', u'Ș': 'S', u'Š': 'S', u'Ť': 'T', u'Ţ': 'T', u'Ŧ': 'T', u'Ț': 'T', u'Ù': 'U', u'Ú': 'U', u'Û': 'U', u'Ü': 'Ue', u'Ū': 'U', u'Ů': 'U', u'Ű': 'U', u'Ŭ': 'U', u'Ũ': 'U', u'Ų': 'U', u'Ŵ': 'W', u'Ŷ': 'Y', u'Ÿ': 'Y', u'Ý': 'Y', u'Ź': 'Z', u'Ż': 'Z', u'Ž': 'Z', u'à': 'a', u'á': 'a', u'â': 'a', u'ã': 'a', u'ä': 'ae', u'ā': 'a', u'ą': 'a', u'ă': 'a', u'å': 'a', u'æ': 'ae', u'ç': 'c', u'ć': 'c', u'č': 'c', u'ĉ': 'c', u'ċ': 'c', u'ď': 'd', u'đ': 'd', u'è': 'e', u'é': 'e', u'ê': 'e', u'ë': 'e', u'ē': 'e', u'ę': 'e', u'ě': 'e', u'ĕ': 'e', u'ė': 'e', u'ƒ': 'f', u'ĝ': 'g', u'ğ': 'g', u'ġ': 'g', u'ģ': 'g', u'ĥ': 'h', u'ħ': 'h', u'ì': 'i', u'í': 'i', u'î': 'i', u'ï': 'i', u'ī': 'i', u'ĩ': 'i', u'ĭ': 'i', u'į': 'i', u'ı': 'i', u'ij': 'ij', u'ĵ': 'j', u'ķ': 'k', u'ĸ': 'k', u'ł': 'l', u'ľ': 'l', u'ĺ': 'l', u'ļ': 'l', u'ŀ': 'l', u'ñ': 'n', u'ń': 'n', u'ň': 'n', u'ņ': 'n', u'ʼn': 'n', u'ŋ': 'n', u'ò': 'o', u'ó': 'o', u'ô': 'o', u'õ': 'o', u'ö': 'oe', u'ø': 'o', u'ō': 'o', u'ő': 'o', u'ŏ': 'o', u'œ': 'oe', u'ŕ': 'r', u'ř': 'r', u'ŗ': 'r', u'ś': 's', u'š': 's', u'ť': 't', u'ù': 'u', u'ú': 'u', u'û': 'u', u'ü': 'ue', u'ū': 'u', u'ů': 'u', u'ű': 'u', u'ŭ': 'u', u'ũ': 'u', u'ų': 'u', u'ŵ': 'w', u'ÿ': 'y', u'ý': 'y', u'ŷ': 'y', u'ż': 'z', u'ź': 'z', u'ž': 'z', u'ß': 'ss', u'ſ': 'ss', u'Α': 'A', u'Ά': 'A', u'Ἀ': 'A', u'Ἁ': 'A', u'Ἂ': 'A', u'Ἃ': 'A', u'Ἄ': 'A', u'Ἅ': 'A', u'Ἆ': 'A', u'Ἇ': 'A', u'ᾈ': 'A', u'ᾉ': 'A', u'ᾊ': 'A', u'ᾋ': 'A', u'ᾌ': 'A', u'ᾍ': 'A', u'ᾎ': 'A', u'ᾏ': 'A', u'Ᾰ': 'A', u'Ᾱ': 'A', u'Ὰ': 'A', u'Ά': 'A', u'ᾼ': 'A', u'Β': 'B', u'Γ': 'G', u'Δ': 'D', u'Ε': 'E', u'Έ': 'E', u'Ἐ': 'E', u'Ἑ': 'E', u'Ἒ': 'E', u'Ἓ': 'E', u'Ἔ': 'E', u'Ἕ': 'E', u'Έ': 'E', u'Ὲ': 'E', u'Ζ': 'Z', u'Η': 'I', u'Ή': 'I', u'Ἠ': 'I', u'Ἡ': 'I', u'Ἢ': 'I', u'Ἣ': 'I', u'Ἤ': 'I', u'Ἥ': 'I', u'Ἦ': 'I', u'Ἧ': 'I', u'ᾘ': 'I', u'ᾙ': 'I', u'ᾚ': 'I', u'ᾛ': 'I', u'ᾜ': 'I', u'ᾝ': 'I', u'ᾞ': 'I', u'ᾟ': 'I', u'Ὴ': 'I', u'Ή': 'I', u'ῌ': 'I', u'Θ': 'TH', u'Ι': 'I', u'Ί': 'I', u'Ϊ': 'I', u'Ἰ': 'I', u'Ἱ': 'I', u'Ἲ': 'I', u'Ἳ': 'I', u'Ἴ': 'I', u'Ἵ': 'I', u'Ἶ': 'I', u'Ἷ': 'I', u'Ῐ': 'I', u'Ῑ': 'I', u'Ὶ': 'I', u'Ί': 'I', u'Κ': 'K', u'Λ': 'L', u'Μ': 'M', u'Ν': 'N', u'Ξ': 'KS', u'Ο': 'O', u'Ό': 'O', u'Ὀ': 'O', u'Ὁ': 'O', u'Ὂ': 'O', u'Ὃ': 'O', u'Ὄ': 'O', u'Ὅ': 'O', u'Ὸ': 'O', u'Ό': 'O', u'Π': 'P', u'Ρ': 'R', u'Ῥ': 'R', u'Σ': 'S', u'Τ': 'T', u'Υ': 'Y', u'Ύ': 'Y', u'Ϋ': 'Y', u'Ὑ': 'Y', u'Ὓ': 'Y', u'Ὕ': 'Y', u'Ὗ': 'Y', u'Ῠ': 'Y', u'Ῡ': 'Y', u'Ὺ': 'Y', u'Ύ': 'Y', u'Φ': 'F', u'Χ': 'X', u'Ψ': 'PS', u'Ω': 'O', u'Ώ': 'O', u'Ὠ': 'O', u'Ὡ': 'O', u'Ὢ': 'O', u'Ὣ': 'O', u'Ὤ': 'O', u'Ὥ': 'O', u'Ὦ': 'O', u'Ὧ': 'O', u'ᾨ': 'O', u'ᾩ': 'O', u'ᾪ': 'O', u'ᾫ': 'O', u'ᾬ': 'O', u'ᾭ': 'O', u'ᾮ': 'O', u'ᾯ': 'O', u'Ὼ': 'O', u'Ώ': 'O', u'ῼ': 'O', u'α': 'a', u'ά': 'a', u'ἀ': 'a', u'ἁ': 'a', u'ἂ': 'a', u'ἃ': 'a', u'ἄ': 'a', u'ἅ': 'a', u'ἆ': 'a', u'ἇ': 'a', u'ᾀ': 'a', u'ᾁ': 'a', u'ᾂ': 'a', u'ᾃ': 'a', u'ᾄ': 'a', u'ᾅ': 'a', u'ᾆ': 'a', u'ᾇ': 'a', u'ὰ': 'a', u'ά': 'a', u'ᾰ': 'a', u'ᾱ': 'a', u'ᾲ': 'a', u'ᾳ': 'a', u'ᾴ': 'a', u'ᾶ': 'a', u'ᾷ': 'a', u'β': 'b', u'γ': 'g', u'δ': 'd', u'ε': 'e', u'έ': 'e', u'ἐ': 'e', u'ἑ': 'e', u'ἒ': 'e', u'ἓ': 'e', u'ἔ': 'e', u'ἕ': 'e', u'ὲ': 'e', u'έ': 'e', u'ζ': 'z', u'η': 'i', u'ή': 'i', u'ἠ': 'i', u'ἡ': 'i', u'ἢ': 'i', u'ἣ': 'i', u'ἤ': 'i', u'ἥ': 'i', u'ἦ': 'i', u'ἧ': 'i', u'ᾐ': 'i', u'ᾑ': 'i', u'ᾒ': 'i', u'ᾓ': 'i', u'ᾔ': 'i', u'ᾕ': 'i', u'ᾖ': 'i', u'ᾗ': 'i', u'ὴ': 'i', u'ή': 'i', u'ῂ': 'i', u'ῃ': 'i', u'ῄ': 'i', u'ῆ': 'i', u'ῇ': 'i', u'θ': 'th', u'ι': 'i', u'ί': 'i', u'ϊ': 'i', u'ΐ': 'i', u'ἰ': 'i', u'ἱ': 'i', u'ἲ': 'i', u'ἳ': 'i', u'ἴ': 'i', u'ἵ': 'i', u'ἶ': 'i', u'ἷ': 'i', u'ὶ': 'i', u'ί': 'i', u'ῐ': 'i', u'ῑ': 'i', u'ῒ': 'i', u'ΐ': 'i', u'ῖ': 'i', u'ῗ': 'i', u'κ': 'k', u'λ': 'l', u'μ': 'm', u'ν': 'n', u'ξ': 'ks', u'ο': 'o', u'ό': 'o', u'ὀ': 'o', u'ὁ': 'o', u'ὂ': 'o', u'ὃ': 'o', u'ὄ': 'o', u'ὅ': 'o', u'ὸ': 'o', u'ό': 'o', u'π': 'p', u'ρ': 'r', u'ῤ': 'r', u'ῥ': 'r', u'σ': 's', u'ς': 's', u'τ': 't', u'υ': 'y', u'ύ': 'y', u'ϋ': 'y', u'ΰ': 'y', u'ὐ': 'y', u'ὑ': 'y', u'ὒ': 'y', u'ὓ': 'y', u'ὔ': 'y', u'ὕ': 'y', u'ὖ': 'y', u'ὗ': 'y', u'ὺ': 'y', u'ύ': 'y', u'ῠ': 'y', u'ῡ': 'y', u'ῢ': 'y', u'ΰ': 'y', u'ῦ': 'y', u'ῧ': 'y', u'φ': 'f', u'χ': 'x', u'ψ': 'ps', u'ω': 'o', u'ώ': 'o', u'ὠ': 'o', u'ὡ': 'o', u'ὢ': 'o', u'ὣ': 'o', u'ὤ': 'o', u'ὥ': 'o', u'ὦ': 'o', u'ὧ': 'o', u'ᾠ': 'o', u'ᾡ': 'o', u'ᾢ': 'o', u'ᾣ': 'o', u'ᾤ': 'o', u'ᾥ': 'o', u'ᾦ': 'o', u'ᾧ': 'o', u'ὼ': 'o', u'ώ': 'o', u'ῲ': 'o', u'ῳ': 'o', u'ῴ': 'o', u'ῶ': 'o', u'ῷ': 'o', u'¨': '', u'΅': '', u'᾿': '', u'῾': '', u'῍': '', u'῝': '', u'῎': '', u'῞': '', u'῏': '', u'῟': '', u'῀': '', u'῁': '', u'΄': '', u'΅': '', u'`': '', u'῭': '', u'ͺ': '', u'᾽': '', u'А': 'A', u'Б': 'B', u'В': 'V', u'Г': 'G', u'Д': 'D', u'Е': 'E', u'Ё': 'E', u'Ж': 'ZH', u'З': 'Z', u'И': 'I', u'Й': 'I', u'К': 'K', u'Л': 'L', u'М': 'M', u'Н': 'N', u'О': 'O', u'П': 'P', u'Р': 'R', u'С': 'S', u'Т': 'T', u'У': 'U', u'Ф': 'F', u'Х': 'KH', u'Ц': 'TS', u'Ч': 'CH', u'Ш': 'SH', u'Щ': 'SHCH', u'Ы': 'Y', u'Э': 'E', u'Ю': 'YU', u'Я': 'YA', u'а': 'A', u'б': 'B', u'в': 'V', u'г': 'G', u'д': 'D', u'е': 'E', u'ё': 'E', u'ж': 'ZH', u'з': 'Z', u'и': 'I', u'й': 'I', u'к': 'K', u'л': 'L', u'м': 'M', u'н': 'N', u'о': 'O', u'п': 'P', u'р': 'R', u'с': 'S', u'т': 'T', u'у': 'U', u'ф': 'F', u'х': 'KH', u'ц': 'TS', u'ч': 'CH', u'ш': 'SH', u'щ': 'SHCH', u'ы': 'Y', u'э': 'E', u'ю': 'YU', u'я': 'YA', u'Ъ': '', u'ъ': '', u'Ь': '', u'ь': '', u'ð': 'd', u'Ð': 'D', u'þ': 'th', u'Þ': 'TH',
-            u'ა': 'a', u'ბ': 'b', u'გ': 'g', u'დ': 'd', u'ე': 'e', u'ვ': 'v', u'ზ': 'z', u'თ': 't', u'ი': 'i', u'კ': 'k', u'ლ': 'l', u'მ': 'm', u'ნ': 'n', u'ო': 'o', u'პ': 'p', u'ჟ': 'zh', u'რ': 'r', u'ს': 's', u'ტ': 't', u'უ': 'u', u'ფ': 'p', u'ქ': 'k', u'ღ': 'gh', u'ყ': 'q', u'შ': 'sh', u'ჩ': 'ch', u'ც': 'ts', u'ძ': 'dz', u'წ': 'ts', u'ჭ': 'ch', u'ხ': 'kh', u'ჯ': 'j', u'ჰ': 'h' }
-
-def replace_char(m):
-    char = m.group()
-    if char_map.has_key(char):
-        return char_map[char]
-    else:
-        return char
-
-def slughifi(value, do_slugify=True, overwrite_char_map={}):
-    """
-        High Fidelity slugify - slughifi.py, v 0.1
-
-        Examples :
-
-        >>> text = 'C\'est déjà l\'été.'
-
-        >>> slughifi(text)
-        'cest-deja-lete'
-
-        >>> slughifi(text, overwrite_char_map={u'\'': '-',})
-        'c-est-deja-l-ete'
-
-        >>> slughifi(text, do_slugify=False)
-        "C'est deja l'ete."
-
-        # Normal slugify removes accented characters
-        >>> slugify(text)
-        'cest-dj-lt'
-
-    """
-
-    # unicodification
-    if not isinstance(value, unicode):
-        value = unicode(value, 'utf-8', 'ignore')
-
-    # overwrite chararcter mapping
-    char_map.update(overwrite_char_map)
-
-    # try to replace chars
-    value = re.sub('[^a-zA-Z0-9\\s\\-]{1}', replace_char, value)
-
-    # apply django default slugify
-    if do_slugify:
-        value = slugify(value)
-
-    return value.encode('ascii', 'ignore')
-
index 2efde25..21db9ac 100644 (file)
@@ -6,17 +6,16 @@ class TextilePL(Textile):
 
     Changes opening quote to Polish lower-double.
     """
-    glyph_defaults = [(name, repl) 
-        for (name, repl) in Textile.glyph_defaults
-        if name != 'txt_quote_double_open']
-    glyph_defaults.append(('txt_quote_double_open', '&#8222;'))
+    glyph_definitions = dict(Textile.glyph_definitions,
+        quote_double_open = '&#8222;'
+    )
 
 
 def textile_pl(text):
-    return TextilePL().textile(text)
+    return TextilePL().parse(text)
 
 
 def textile_restricted_pl(text):
     return TextilePL(restricted=True, lite=True,
-                   noimage=True, auto_link=False).textile(
+                   noimage=True, auto_link=False).parse(
                         text, rel='nofollow')
diff --git a/runtests.py b/runtests.py
new file mode 100644 (file)
index 0000000..fd4e24a
--- /dev/null
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+# -*- coding: utf-8
+# This file is part of FNPDjango, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See README.md for more information.
+#
+"""
+Creates a simple Django configuration and runs tests for fnpdjango.
+"""
+from __future__ import unicode_literals
+
+import sys
+import os
+from os.path import dirname, abspath
+from optparse import OptionParser
+from shutil import rmtree
+
+from django.conf import settings
+from fnpdjango.utils.settings import LazyUGettextLazy as _
+
+
+# For convenience configure settings if they are not pre-configured or if we
+# haven't been provided settings to use by environment variable.
+if not settings.configured and not os.environ.get('DJANGO_SETTINGS_MODULE'):
+    import tempfile
+    media_root = tempfile.mkdtemp(prefix='djangotest_')
+
+    settings.configure(
+        DATABASES={
+            'default': {
+                'ENGINE': 'django.db.backends.sqlite3',
+            }
+        },
+        INSTALLED_APPS=[
+            'django.contrib.auth',
+            'django.contrib.contenttypes',
+            'django.contrib.sessions',
+            'django.contrib.sites',
+
+            'fnpdjango',
+            'tests',
+        ],
+        LANGUAGE_CODE='pl',
+        MEDIA_ROOT=media_root,
+        TEST_LAZY_UGETTEXT_LAZY=_("Lazy setting."),
+    )
+else:
+    media_root = None
+
+try:
+    from django.test.runner import DiscoverRunner
+except ImportError:
+    # Django < 1.6
+    from django.test.simple import DjangoTestSuiteRunner as DiscoverRunner
+
+
+def runtests(*test_args, **kwargs):
+    """Actual test suite entry point."""
+    if not test_args:
+        test_args = ['tests']
+    parent = dirname(abspath(__file__))
+    sys.path.insert(0, parent)
+
+    # For Django 1.7+
+    try:
+        from django import setup
+    except ImportError:
+        pass
+    else:
+        setup()
+
+    test_runner = DiscoverRunner(
+        verbosity=kwargs.get('verbosity', 1),
+        interactive=kwargs.get('interactive', False),
+        failfast=kwargs.get('failfast'))
+    failures = test_runner.run_tests(test_args)
+    if media_root:
+        rmtree(media_root)
+    sys.exit(failures)
+
+if __name__ == '__main__':
+    parser = OptionParser()
+    parser.add_option('--failfast', action='store_true',
+                      default=False, dest='failfast')
+
+    (options, args) = parser.parse_args()
+
+    runtests(failfast=options.failfast, *args)
index 59f2ffa..5f037f8 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -32,10 +32,14 @@ setup(
         'fnpdjango.management.commands': ['babel.cfg'],
     },
     install_requires=[
-        'Django>=1.4,<1.7',
-        'textile==2.1.5',
+        'Django>=1.4,<1.8',
+        'textile==2.2a',
+    ],
+    dependency_links=[
+        'https://github.com/rczajka/python-textile/archive/f7a262f2a8409c8a8adb817b6a9f15f91187a527.zip#egg=textile-2.2a',
     ],
     license='LICENSE',
     description='.',
     long_description="",
+    test_suite="runtests.runtests",
 )
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/models.py b/tests/models.py
new file mode 100644 (file)
index 0000000..11102ef
--- /dev/null
@@ -0,0 +1,9 @@
+from django.db import models
+
+from fnpdjango.storage import BofhFileSystemStorage
+
+bofh_storage = BofhFileSystemStorage()
+
+
+class SomeModel(models.Model):
+    attachment = models.FileField(null=True, upload_to="test", storage=bofh_storage)
diff --git a/tests/tests/__init__.py b/tests/tests/__init__.py
new file mode 100644 (file)
index 0000000..ec449c4
--- /dev/null
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+# This file is part of FNPDjango, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See README.md for more information.
+#
+"""
+This file works only for django.test.simple.DjangoTestSuiteRunner
+in Django<1.6.  The newer django.test.runner.DiscoverRunner finds
+test_* modules by itself.
+
+"""
+from .test_storage import *
+from .test_templatetags_fnp_markup import *
+from .test_utils_settings import *
diff --git a/tests/tests/test_storage.py b/tests/tests/test_storage.py
new file mode 100644 (file)
index 0000000..2cfe79c
--- /dev/null
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# This file is part of FNPDjango, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See README.md for more information.
+#
+from __future__ import unicode_literals
+
+from tempfile import NamedTemporaryFile
+from django.core.files.base import ContentFile
+from django.test import TestCase
+from tests.models import SomeModel
+
+
+class StorageTestCase(TestCase):
+    def test_save(self):
+        obj = SomeModel.objects.create()
+        obj.attachment.save("text.txt", ContentFile(b"A"))
+        path = obj.attachment.path
+        self.assertEqual(open(path, 'rb').read(), b"A")
+        # Now save again under the same filename.
+        obj.attachment.save("text.txt", ContentFile(b"B"))
+        self.assertEqual(open(path, 'rb').read(), b"B")
diff --git a/tests/tests/test_templatetags_fnp_markup.py b/tests/tests/test_templatetags_fnp_markup.py
new file mode 100644 (file)
index 0000000..5c8e07c
--- /dev/null
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+# This file is part of FNPDjango, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See README.md for more information.
+#
+from __future__ import unicode_literals
+
+import sys
+from django.test import TestCase
+
+try:
+    from unittest import skipIf
+except ImportError:
+    # Don't need skipping for Python2.6.
+    skipIf = lambda expr, comment: lambda test: test
+
+
+@skipIf(sys.version_info[:2] == (3, 2),
+    "No usable python-textile for Python 3.2.")
+class TemplatetagsFNPMarkupTestCase(TestCase):
+
+    def test_textile_en(self):
+        from fnpdjango.templatetags import fnp_markup
+        self.assertEqual(
+            fnp_markup.textile_en('Test "Textile".'),
+            '\t<p>Test &#8220;Textile&#8221;.</p>')
+        self.assertEqual(
+            fnp_markup.textile_restricted_en('Test "Textile".'),
+            '\t<p>Test &#8220;Textile&#8221;.</p>')
+
+    def test_textile_pl(self):
+        from fnpdjango.templatetags import fnp_markup
+        self.assertEqual(
+            fnp_markup.textile_pl('Test "Textile".'),
+            '\t<p>Test &#8222;Textile&#8221;.</p>')
+        self.assertEqual(
+            fnp_markup.textile_restricted_pl('Test "Textile".'),
+            '\t<p>Test &#8222;Textile&#8221;.</p>')
diff --git a/tests/tests/test_utils_settings.py b/tests/tests/test_utils_settings.py
new file mode 100644 (file)
index 0000000..7dc0f7d
--- /dev/null
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+# This file is part of FNPDjango, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See README.md for more information.
+#
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.test import TestCase
+
+try:
+    unicode
+except NameError:
+    pass
+else:
+    str = unicode
+
+
+class UtilsSettingsTestCase(TestCase):
+    def test_lazy_ugettext_lazy(self):
+        self.assertEqual(str(settings.TEST_LAZY_UGETTEXT_LAZY),
+            "Lazy setting.")
+
+
diff --git a/tox.ini b/tox.ini
new file mode 100644 (file)
index 0000000..981f570
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,31 @@
+# This file is part of FNPDjango, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See README.md for more information.
+#
+[tox]
+envlist=clear,
+    d14-py{26,27},
+    d1{5,6}-py{26,27,32,33},
+    d{17,d}-py{27,32,33,34},
+    stats
+
+[testenv]
+indexserver=https://py.mdrn.pl:8443
+commands=coverage run --source=fnpdjango --append --branch runtests.py
+deps=
+    d14: Django>=1.4,<1.5
+    d15: Django>=1.5,<1.6
+    d16: Django>=1.6,<1.7
+    d17: Django>=1.7,<1.8
+    dd: https://github.com/django/django/zipball/master
+    coverage
+install_command =
+    pip install --process-dependency-links {opts} {packages}
+
+[testenv:clear]
+basepython=python3.4
+commands=coverage erase
+
+[testenv:stats]
+basepython=python3.4
+commands=coverage html
+