Repository: getpelican/pelican

Issue:
truncate_html_words not working with python 3.5
while preparing a pr to add py35 to tox and travis I discovered that `pelican.utils.truncate_html_words` is no longer working with python 3.5. No shorting is taking place.

it is possible a good idea to avoid python 3.5 for now till we have made sure it is working properly.

If you are suffering from this issue please have a look at the docs which explain how to use virtualenv to create a custom virtual environment that contains a python version that is supported (2.7, 3.3, 3.4)



Discussion:
Changed in version 3.5: The default value for argument convert_charrefs is now True.
If we overwrite it with False it works again, thoughts on the matter (nudge @avaris )?


Resolving patch:
diff --git a/pelican/log.py b/pelican/log.py
--- a/pelican/log.py
+++ b/pelican/log.py
@@ -172,21 +172,49 @@ def __init__(self, *args, **kwargs):
 logging.setLoggerClass(LimitLogger)
 
 
-def init(level=None, handler=logging.StreamHandler()):
+def supports_color():
+    """
+    Returns True if the running system's terminal supports color,
+    and False otherwise.
+
+    from django.core.management.color
+    """
+    plat = sys.platform
+    supported_platform = plat != 'Pocket PC' and \
+        (plat != 'win32' or 'ANSICON' in os.environ)
 
-    logger = logging.getLogger()
+    # isatty is not always implemented, #6223.
+    is_a_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
+    if not supported_platform or not is_a_tty:
+        return False
+    return True
 
-    if os.isatty(sys.stdout.fileno()) and not sys.platform.startswith('win'):
-        fmt = ANSIFormatter()
+
+def get_formatter():
+    if supports_color():
+        return ANSIFormatter()
     else:
-        fmt = TextFormatter()
-    handler.setFormatter(fmt)
+        return TextFormatter()
+
+
+def init(level=None, handler=logging.StreamHandler(), name=None):
+
+    logger = logging.getLogger(name)
+
+    handler.setFormatter(get_formatter())
     logger.addHandler(handler)
 
     if level:
         logger.setLevel(level)
 
 
+def log_warnings():
+    import warnings
+    logging.captureWarnings(True)
+    warnings.simplefilter("default", DeprecationWarning)
+    init(logging.DEBUG, name='py.warnings')
+
+
 if __name__ == '__main__':
     init(level=logging.DEBUG)
 
diff --git a/pelican/utils.py b/pelican/utils.py
--- a/pelican/utils.py
+++ b/pelican/utils.py
@@ -424,7 +424,11 @@ def __init__(self, truncate_at):
     def __init__(self, max_words):
         # In Python 2, HTMLParser is not a new-style class,
         # hence super() cannot be used.
-        HTMLParser.__init__(self)
+        try:
+            HTMLParser.__init__(self, convert_charrefs=False)
+        except TypeError:
+            # pre Python 3.3
+            HTMLParser.__init__(self)
 
         self.max_words = max_words
         self.words_found = 0
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -55,6 +55,7 @@
          'Programming Language :: Python :: 3',
          'Programming Language :: Python :: 3.3',
          'Programming Language :: Python :: 3.4',
+         'Programming Language :: Python :: 3.5',
          'Topic :: Internet :: WWW/HTTP',
          'Topic :: Software Development :: Libraries :: Python Modules',
     ],


Test patch:
diff --git a/pelican/tests/__init__.py b/pelican/tests/__init__.py
--- a/pelican/tests/__init__.py
+++ b/pelican/tests/__init__.py
@@ -1,2 +1,15 @@
 import logging
+import warnings
+
+from pelican.log import log_warnings
+
+# redirect warnings modulole to use logging instead
+log_warnings()
+
+# setup warnings to log DeprecationWarning's and error on
+# warnings in pelican's codebase
+warnings.simplefilter("default", DeprecationWarning)
+warnings.filterwarnings("error", ".*", Warning, "pelican")
+
+# Add a NullHandler to silence warning about no available handlers
 logging.getLogger().addHandler(logging.NullHandler())
diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py
--- a/pelican/tests/test_readers.py
+++ b/pelican/tests/test_readers.py
@@ -3,6 +3,8 @@
 
 import os
 
+import six
+
 from pelican import readers
 from pelican.tests.support import get_settings, unittest
 from pelican.utils import SafeDatetime
@@ -55,7 +57,8 @@ def test_equal(self):
         self.assertDictHasSubset(self.dictionary, self.dictionary)
 
     def test_fail_not_set(self):
-        self.assertRaisesRegexp(
+        six.assertRaisesRegex(
+            self,
             AssertionError,
             'Expected.*key-c.*to have value.*val-c.*but was not in Dict',
             self.assertDictHasSubset,
@@ -63,7 +66,8 @@ def test_fail_not_set(self):
             {'key-c': 'val-c'})
 
     def test_fail_wrong_val(self):
-        self.assertRaisesRegexp(
+        six.assertRaisesRegex(
+            self,
             AssertionError,
             'Expected .*key-a.* to have value .*val-b.* but was .*val-a.*',
             self.assertDictHasSubset,
diff --git a/pelican/tests/test_testsuite.py b/pelican/tests/test_testsuite.py
new file mode 100644
--- /dev/null
+++ b/pelican/tests/test_testsuite.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+from __future__ import print_function, unicode_literals
+
+import sys
+import warnings
+
+from pelican.tests.support import unittest
+
+
+class TestSuiteTest(unittest.TestCase):
+
+    @unittest.skipIf(sys.version_info[:2] == (3, 3),
+                     "does not throw an exception on python 3.3")
+    def test_error_on_warning(self):
+        with self.assertRaises(UserWarning):
+            warnings.warn('test warning')